// Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: BUSL-1.1 package oss_test import ( "context" "testing" "github.com/hashicorp/boundary/internal/db" "github.com/hashicorp/boundary/internal/db/common" "github.com/hashicorp/boundary/internal/db/schema" "github.com/hashicorp/boundary/internal/db/schema/migration" "github.com/hashicorp/boundary/internal/db/schema/migrations/oss/internal/hook97001" "github.com/hashicorp/boundary/testing/dbtest" "github.com/stretchr/testify/require" ) // cases to look at [NA = not-applicable | P = pass | F = fail] - 'FindProblem' query should find all `F` cases // [F] Global Role (r_globala_97001) - Grant This, Descendants, individual org, and individual project // - need to remove individual org // - need to remove individual proj // // [F] Global Role (r_globalb_97001) - Grant This, Children, individual org, and individual project // - need to remove individual org // // [P] Global Role (r_globalc_97001) - Grant role's self scope ID // - switch to `this` automagically // // [F] Org Role (r_orgaa___97001) - Grant This, Children, and individual project // - need to remove individual project // // [P] Org Role (r_orgab___97001) - Grant role self scope ID // - switch to `this` automagically // // [P] Project Role (r_prjaa___97001) - Grant self scope ID // - switch to `this` automagically // // [NA] Org Role - Grant Children AND individual org (different org) // - this cannot be created // // [NA] Org Role - Grant Children AND individual project (from different org) // - this cannot be created // // [NA] Project Role (p_pA___97001) - Grant individual project (different proj) // - this cannot be created // // [NA] Global Role - Grant Descendants and Children // - this cannot be created func TestMigrationHook97001(t *testing.T) { const ( priorMigration = 96001 ) dialect := dbtest.Postgres ctx := context.Background() c, u, _, err := dbtest.StartUsingTemplate(dialect, dbtest.WithTemplate(dbtest.Template1)) require.NoError(t, err) t.Cleanup(func() { require.NoError(t, c()) }) d, err := common.SqlOpen(dialect, u) require.NoError(t, err) // migration to the prior migration (before the one we want to test) m, err := schema.NewManager(ctx, schema.Dialect(dialect), d, schema.WithEditions( schema.TestCreatePartialEditions(schema.Dialect(dialect), schema.PartialEditions{"oss": priorMigration}), )) require.NoError(t, err) _, err = m.ApplyMigrations(ctx) require.NoError(t, err) state, err := m.CurrentState(ctx) require.NoError(t, err) want := &schema.State{ Initialized: true, Editions: []schema.EditionState{ { Name: "oss", BinarySchemaVersion: priorMigration, DatabaseSchemaVersion: priorMigration, DatabaseSchemaState: schema.Equal, }, }, } require.Equal(t, want, state) dbType, err := db.StringToDbType(dialect) require.NoError(t, err) conn, err := db.Open(ctx, dbType, u) require.NoError(t, err) rw := db.New(conn) t.Helper() query := ` insert into iam_scope (parent_id, type, public_id, name) values ('global', 'org', 'o_ta___97001', 'Org A Testing Invalid Role Grant Scope Associations'), ('global', 'org', 'o_tb___97001', 'Org B Testing Invalid Role Grant Scope Associations'), ('o_ta___97001', 'project', 'p_pA___97001', 'testing 97001 Project A'), ('o_ta___97001', 'project', 'p_pB___97001', 'testing 97001 Project B'), ('o_tb___97001', 'project', 'p_pC___97001', 'testing 97001 Project C')` _, err = rw.Exec(ctx, query, nil) require.NoError(t, err) query = ` insert into iam_role (public_id, scope_id, name ) values ('r_globala_97001', 'global', 'testing 97001 global role A'), ('r_globalb_97001', 'global', 'testing 97001 global role B'), ('r_globalc_97001', 'global', 'testing 97001 global role C'), ('r_globald_97001', 'global', 'testing 97001 global role D'), ('r_globale_97001', 'global', 'testing 97001 global role E'), ('r_globalf_97001', 'global', 'testing 97001 global role f'), ('r_orgaa___97001', 'o_ta___97001', 'testing 97001 org A role A'), ('r_orgab___97001', 'o_ta___97001', 'testing 97001 org A role B'), ('r_prjaa___97001', 'p_pA___97001', 'testing 97001 proj A role A'), ('r_prjab___97001', 'p_pA___97001', 'testing 97001 proj A role B') ` _, err = rw.Exec(ctx, query, nil) require.NoError(t, err) query = ` insert into iam_role_grant_scope (role_id, scope_id_or_special) values -- Global Role (r_globala_97001) - Grant This, Descendants, individual org, and individual project ('r_globala_97001', 'this'), ('r_globala_97001', 'descendants'), ('r_globala_97001', 'o_ta___97001'), ('r_globala_97001', 'p_pA___97001'), -- Global Role (r_globalb_97001) - Grant This, Children, individual org, and individual project ('r_globalb_97001', 'this'), ('r_globalb_97001', 'children'), ('r_globalb_97001', 'o_ta___97001'), ('r_globalb_97001', 'p_pA___97001'), -- Org Role (r_orgaa___97001) - Grant This, Children, and individual project ('r_orgaa___97001', 'this'), ('r_orgaa___97001', 'children'), ('r_orgaa___97001', 'p_pA___97001'), -- below this line are non-problematic roles that should not show up in the final list -- Global Role (r_globalc_97001) - Grant role's self scope ID ('r_globalc_97001', 'global'), -- Global Role (r_globald_97001) - Grant children and project ('r_globald_97001', 'children'), ('r_globald_97001', 'p_pA___97001'), -- Global Role (r_globale_97001) - Grant this and descendants ('r_globale_97001', 'this'), ('r_globale_97001', 'descendants'), -- Global Role (r_globalf_97001) - Grant this and children ('r_globalf_97001', 'this'), ('r_globalf_97001', 'children'), -- Org Role (r_orgab___97001) - Grant role self scope ID ('r_orgab___97001', 'o_ta___97001'), -- Project Role (p_pA___97001) - Grant self scope ID ('r_prjaa___97001', 'p_pA___97001') ` _, err = rw.Exec(ctx, query, nil) require.NoError(t, err) tx, err := d.BeginTx(ctx, nil) require.NoError(t, err) // Run hook check checkReport, err := hook97001.FindInvalidAssociations(ctx, tx) require.NoError(t, err) // Run hook repair repairReport, err := hook97001.RepairInvalidAssociations(ctx, tx) require.NoError(t, err) err = tx.Commit() require.NoError(t, err) hook97001validateCheckFunc(t, checkReport) hook97001validateRepairFunc(t, repairReport) } func hook97001validateCheckFunc(t *testing.T, checkReport migration.Problems) { t.Helper() require := require.New(t) require.ElementsMatch(checkReport, migration.Problems{ "Role 'r_globala_97001' in scope 'global' has the 'descendants' grant scope which covers 'o_ta___97001'", "Role 'r_globala_97001' in scope 'global' has the 'descendants' grant scope which covers 'p_pA___97001'", "Role 'r_globalb_97001' in scope 'global' has the 'children' grant scope which covers 'o_ta___97001'", "Role 'r_orgaa___97001' in scope 'o_ta___97001' has the 'children' grant scope which covers 'p_pA___97001'", }) } func hook97001validateRepairFunc(t *testing.T, checkReport migration.Repairs) { t.Helper() require := require.New(t) require.ElementsMatch(checkReport, migration.Problems{ "Remove redundant grant scopes 'o_ta___97001' association from role 'r_globala_97001' in scope 'global' because it overlaps with 'descendants'", "Remove redundant grant scopes 'p_pA___97001' association from role 'r_globala_97001' in scope 'global' because it overlaps with 'descendants'", "Remove redundant grant scopes 'o_ta___97001' association from role 'r_globalb_97001' in scope 'global' because it overlaps with 'children'", "Remove redundant grant scopes 'p_pA___97001' association from role 'r_orgaa___97001' in scope 'o_ta___97001' because it overlaps with 'children'", }) }