// Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: BUSL-1.1 package oss_test import ( "context" "database/sql" "fmt" "testing" "github.com/hashicorp/boundary/internal/db" "github.com/hashicorp/boundary/internal/db/common" "github.com/hashicorp/boundary/internal/db/schema/migration" "github.com/hashicorp/boundary/internal/db/schema/migrations/oss" "github.com/hashicorp/boundary/internal/db/schema/migrations/oss/internal/hook46001" "github.com/hashicorp/boundary/testing/dbtest" "github.com/stretchr/testify/require" ) func TestMigrationHook46001(t *testing.T) { t.Parallel() ctx := context.Background() dialect := dbtest.Postgres 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) // get a connection dbType, err := db.StringToDbType(dialect) require.NoError(t, err) conn, err := db.Open(ctx, dbType, u) require.NoError(t, err) rw := db.New(conn) // The reason why we are starting at 43001 is because one of the security vulnerabilities was a // bad trigger on table target_host_set that was suppose to validate scope_id integrity between // the target & host set. In migration 44002, this trigger is fixed. oss.ApplyMigration(t, ctx, d, 43001) // Insert test data into the database. populateScopes(t, rw) populateKMS(t, rw) // Belongs to PRJA___65001 populateTarget(t, rw) // Valid associations to target populateStaticCredential(t, rw, "PRJA___65001") populateCredentialLibrary(t, rw, "PRJA___65001") populateHostSet(t, rw, "PRJA___65001") // Illegal associations to target populateStaticCredential(t, rw, "PRJB___65001") populateCredentialLibrary(t, rw, "PRJB___65001") populateHostSet(t, rw, "PRJB___65001") // Apply migrations 4400x, 4500x migrationIds := []int{44001, 44002, 44003, 44004, 45001, 45002, 45003} for _, migrationId := range migrationIds { oss.ApplyMigration(t, ctx, d, migrationId) } tx, err := d.BeginTx(ctx, nil) require.NoError(t, err) // Run hook check checkReport, err := hook46001.FindIllegalAssociations(ctx, tx) require.NoError(t, err) // Run hook repair repairReport, err := hook46001.RepairIllegalAssociations(ctx, tx) require.NoError(t, err) err = tx.Commit() require.NoError(t, err) // Hook validations validateCheckFunc(t, checkReport) validateRepairFunc(t, rw, repairReport) } func populateScopes(t *testing.T, rw *db.Db) { t.Helper() require := require.New(t) ctx := context.Background() var ( err error query string ) query = ` insert into iam_scope (parent_id, type, public_id, name) values ('global', 'org', 'o_test___65001', 'Testing Invalid Target Associations')` _, err = rw.Exec(ctx, query, nil) require.NoError(err) query = ` insert into iam_scope (parent_id, type, public_id, name) values ('o_test___65001', 'project', 'p_PRJA___65001', 'testing 65001 Project A'), ('o_test___65001', 'project', 'p_PRJB___65001', 'testing 65001 Project B')` _, err = rw.Exec(ctx, query, nil) require.NoError(err) } func populateKMS(t *testing.T, rw *db.Db) { t.Helper() require := require.New(t) ctx := context.Background() var ( err error query string ) query = ` insert into kms_root_key (private_id, scope_id) values ('krk________65001', 'o_test___65001')` _, err = rw.Exec(ctx, query, nil) require.NoError(err) query = ` insert into kms_root_key_version (private_id, root_key_id, key) values ('krkv________65001', 'krk________65001', '________65001'::bytea)` _, err = rw.Exec(ctx, query, nil) require.NoError(err) query = ` insert into kms_data_key (private_id, root_key_id, purpose) values ('kdk________65001', 'krk________65001', 'database')` _, err = rw.Exec(ctx, query, nil) require.NoError(err) query = ` insert into kms_data_key_version (private_id, data_key_id, root_key_version_id, key) values ('kdkv_PRJA___65001', 'kdk________65001', 'krkv________65001', '________65001'::bytea), ('kdkv_PRJB___65001', 'kdk________65001', 'krkv________65001', '________65001'::bytea)` _, err = rw.Exec(ctx, query, nil) require.NoError(err) } func populateTarget(t *testing.T, rw *db.Db) { t.Helper() require := require.New(t) ctx := context.Background() var ( err error query string ) query = ` insert into target_tcp (scope_id, public_id, name) values ('p_PRJA___65001', 'ttcp_PRJA___65001', 'Test Target Project A')` _, err = rw.Exec(ctx, query, nil) require.NoError(err) } func populateStaticCredential(t *testing.T, rw *db.Db, projectId string) { t.Helper() require := require.New(t) ctx := context.Background() var ( err error query string ) query = fmt.Sprintf(` insert into credential_static_store (scope_id, public_id, name, description) values ('p_%[1]s', 'csst_%[1]s', '%[1]s', 'None')`, projectId) _, err = rw.Exec(ctx, query, nil) require.NoError(err) query = fmt.Sprintf(` insert into credential_static_username_password_credential (store_id, public_id, name, description, username, password_encrypted, password_hmac, key_id) values ('csst_%[1]s', 'credup_%[1]s', 'test %[1]s static credential', 'None', 'admin', 'encrypted_password', 'hmac-value', 'kdkv_%[1]s')`, projectId) _, err = rw.Exec(ctx, query, nil) require.NoError(err) query = fmt.Sprintf(` insert into target_static_credential (target_id, credential_static_id, credential_purpose) values ('ttcp_PRJA___65001', 'credup_%s', 'brokered')`, projectId) _, err = rw.Exec(ctx, query, nil) require.NoError(err) } func populateCredentialLibrary(t *testing.T, rw *db.Db, projectId string) { t.Helper() require := require.New(t) ctx := context.Background() var ( err error query string ) query = fmt.Sprintf(` insert into credential_vault_store (scope_id, public_id, name, description, vault_address, namespace) values ('p_%[1]s', 'csvlt_%[1]s', 'test credentail library', 'None', 'https://vault.widget', 'default')`, projectId) _, err = rw.Exec(ctx, query, nil) require.NoError(err) query = fmt.Sprintf(` insert into credential_vault_token (store_id, key_id, status, token_hmac, token, last_renewal_time, expiration_time) values ('csvlt_%[1]s', 'kdkv_%[1]s', 'current', '%[1]s-hmac-value', 'token-value', now(), now() + interval '1 hour')`, projectId) _, err = rw.Exec(ctx, query, nil) require.NoError(err) query = fmt.Sprintf(` insert into credential_vault_library (store_id, public_id, name, description, vault_path, http_method, credential_type) values ('csvlt_%[1]s', 'clvlt_%[1]s', 'test credential', 'None', '/secrets/kv/one', 'GET', 'username_password')`, projectId) _, err = rw.Exec(ctx, query, nil) require.NoError(err) query = fmt.Sprintf(` insert into target_credential_library (target_id, credential_library_id, credential_purpose) values ('ttcp_PRJA___65001', 'clvlt_%s', 'brokered')`, projectId) _, err = rw.Exec(ctx, query, nil) require.NoError(err) } func populateHostSet(t *testing.T, rw *db.Db, projectId string) { t.Helper() require := require.New(t) ctx := context.Background() var ( err error query string ) query = fmt.Sprintf(` insert into static_host_catalog (scope_id, public_id, name) values ('p_%[1]s', 'hcst_%[1]s', '%[1]s HostCatalog')`, projectId) _, err = rw.Exec(ctx, query, nil) require.NoError(err) query = fmt.Sprintf(` insert into static_host (catalog_id, public_id, address) values ('hcst_%[1]s', 'hst_%[1]s', '%[1]s Host')`, projectId) _, err = rw.Exec(ctx, query, nil) require.NoError(err) query = fmt.Sprintf(` insert into static_host_set (catalog_id, public_id, name) values ('hcst_%[1]s', 'hsst_%[1]s', '%[1]s HostSet')`, projectId) _, err = rw.Exec(ctx, query, nil) require.NoError(err) query = fmt.Sprintf(` insert into static_host_set_member ( host_id, set_id, catalog_id) values ('hst_%[1]s', 'hsst_%[1]s', 'hcst_%[1]s')`, projectId) _, err = rw.Exec(ctx, query, nil) require.NoError(err) query = fmt.Sprintf(` insert into target_host_set (target_id, host_set_id) values ('ttcp_PRJA___65001', 'hsst_%s')`, projectId) _, err = rw.Exec(ctx, query, nil) require.NoError(err) } func validateCheckFunc(t *testing.T, checkReport migration.Problems) { t.Helper() require := require.New(t) require.ElementsMatch(checkReport, migration.Problems{ "Target ttcp_PRJA___65001 in project p_PRJA___65001 in org o_test___65001 has an illegal association to credential library clvlt_PRJB___65001 in credential store csvlt_PRJB___65001 in project p_PRJB___65001 in org o_test___65001", "Target ttcp_PRJA___65001 in project p_PRJA___65001 in org o_test___65001 has an illegal association to host set hsst_PRJB___65001 in host catalog hcst_PRJB___65001 in project p_PRJB___65001 in org o_test___65001", "Target ttcp_PRJA___65001 in project p_PRJA___65001 in org o_test___65001 has an illegal association to static credential credup_PRJB___65001 in credential store csst_PRJB___65001 in project p_PRJB___65001 in org o_test___65001", }) } func validateRepairFunc(t *testing.T, rw *db.Db, repairReport migration.Repairs) { t.Helper() require := require.New(t) ctx := context.Background() require.ElementsMatch(repairReport, migration.Repairs{ "Removed illegal association from target ttcp_PRJA___65001 in project p_PRJA___65001 in org o_test___65001 to credential library clvlt_PRJB___65001 in credential store csvlt_PRJB___65001 in project p_PRJB___65001 in org o_test___65001", "Removed illegal association from target ttcp_PRJA___65001 in project p_PRJA___65001 in org o_test___65001 to host set hsst_PRJB___65001 in host catalog hcst_PRJB___65001 in project p_PRJB___65001 in org o_test___65001", "Removed illegal association from target ttcp_PRJA___65001 in project p_PRJA___65001 in org o_test___65001 to static credential credup_PRJB___65001 in credential store csst_PRJB___65001 in project p_PRJB___65001 in org o_test___65001", }) var ( err error query string rows *sql.Rows ) type targetAssociation struct { targetId string resourceId string } query = ` with ths (target_id, host_set_id) as ( select ths.target_id, ths.host_set_id from target_host_set as ths where ths.target_id = 'ttcp_PRJA___65001' ), tsc (target_id, credential_static_id) as ( select tsc.target_id, tsc.credential_static_id from target_static_credential as tsc where tsc.target_id = 'ttcp_PRJA___65001' ), tcl (target_id, credential_library_id) as ( select tcl.target_id, tcl.credential_library_id from target_credential_library as tcl where tcl.target_id = 'ttcp_PRJA___65001' ), resources (target_id, resource_id) as ( select target_id, host_set_id as resource_id from ths union select target_id, credential_static_id as resource_id from tsc union select target_id, credential_library_id as resource_id from tcl ) select * from resources;` rows, err = rw.Query(ctx, query, nil) require.NoError(err) associations := []targetAssociation{} for rows.Next() { var targetId, resourceId string if err := rows.Scan(&targetId, &resourceId); err != nil { require.NoError(err) } associations = append(associations, targetAssociation{ targetId: targetId, resourceId: resourceId, }) } require.NoError(rows.Err()) require.Equal([]targetAssociation{ { targetId: "ttcp_PRJA___65001", resourceId: "clvlt_PRJA___65001", }, { targetId: "ttcp_PRJA___65001", resourceId: "hsst_PRJA___65001", }, { targetId: "ttcp_PRJA___65001", resourceId: "credup_PRJA___65001", }, }, associations) }