// Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: BUSL-1.1 package oss_test import ( "database/sql" "fmt" "math/rand/v2" "strconv" "testing" "time" "github.com/hashicorp/boundary/internal/db/common" "github.com/hashicorp/boundary/internal/db/schema" "github.com/hashicorp/boundary/testing/dbtest" "github.com/hashicorp/go-secure-stdlib/base62" "github.com/stretchr/testify/require" ) // TestVaultCredLibRefactor tests the credential_vault_library refactor. It // inserts state in the changed tables prior to the refactor migration, executes // the refactor migration and then asserts that this state is still correct. // // For structural tests, see // sqltest/tests/credential/vault/credential_vault_library_refactor.sql. // // For context, see 99/README.md. func TestVaultCredLibRefactor(t *testing.T) { t.Parallel() require := require.New(t) c := testVaultCredLibRefactorDbSetupCounts{ orgs: 2, projectsPerOrg: 4, vaultGenericCredLibs: 20, vaultGenericCredLibsDeleted: 5, vaultSshCertCredLibs: 20, vaultSshCertCredLibsDeleted: 5, } dataBeforeMigration, dataAfterMigration := testVaultCredLibRefactorDbSetup(t, c) require.NotEmpty(dataBeforeMigration) require.NotEmpty(dataAfterMigration) // Validate that `testVaultCredLibRefactorDbSetup` seeded data correctly // according to the count variables above. require.Len(dataBeforeMigration.orgs, c.orgs) require.Len(dataBeforeMigration.projects, c.orgs*c.projectsPerOrg) require.Len(dataBeforeMigration.credentialStores, c.orgs*c.projectsPerOrg) require.Len(dataBeforeMigration.credLibBase, (c.orgs*c.projectsPerOrg*c.vaultGenericCredLibs)+(c.orgs*c.projectsPerOrg*c.vaultSshCertCredLibs)) require.NotEmpty(dataBeforeMigration.credLibHistoryBase) // Some credential libraries have been updated so they'll have more than 1 history entry. require.Len(dataBeforeMigration.postRefactorVaultCredLibBase, 0) require.Len(dataBeforeMigration.vaultGenericCredLibs, c.orgs*c.projectsPerOrg*c.vaultGenericCredLibs) require.Len(dataBeforeMigration.vaultSshCertCredLibs, c.orgs*c.projectsPerOrg*c.vaultSshCertCredLibs) require.Len(dataBeforeMigration.deletedVaultGenericCredLibs, c.orgs*c.projectsPerOrg*c.vaultGenericCredLibsDeleted) require.Len(dataBeforeMigration.deletedVaultSshCertCredLibs, c.orgs*c.projectsPerOrg*c.vaultSshCertCredLibsDeleted) // Validate migrated data. Given that we asserted the data before migration // is correctly seeded, we can just assert that post-migration and // pre-migration elements match. require.ElementsMatch(dataBeforeMigration.orgs, dataAfterMigration.orgs) require.ElementsMatch(dataBeforeMigration.projects, dataAfterMigration.projects) require.ElementsMatch(dataBeforeMigration.credentialStores, dataAfterMigration.credentialStores) require.ElementsMatch(dataBeforeMigration.credLibBase, dataAfterMigration.credLibBase) require.ElementsMatch(dataBeforeMigration.credLibHistoryBase, dataAfterMigration.credLibHistoryBase) require.ElementsMatch(dataBeforeMigration.credLibBase, dataAfterMigration.postRefactorVaultCredLibBase) // We fill new credential_vault_library with data from credential_library. require.ElementsMatch(dataBeforeMigration.vaultGenericCredLibs, dataAfterMigration.vaultGenericCredLibs) require.ElementsMatch(dataBeforeMigration.vaultSshCertCredLibs, dataAfterMigration.vaultSshCertCredLibs) require.ElementsMatch(dataBeforeMigration.deletedVaultGenericCredLibs, dataAfterMigration.deletedVaultGenericCredLibs) require.ElementsMatch(dataBeforeMigration.deletedVaultSshCertCredLibs, dataAfterMigration.deletedVaultSshCertCredLibs) // Finally, check table and view row counts. require.EqualValues(dataBeforeMigration.rowCounts.credLib, dataAfterMigration.rowCounts.credLib) require.EqualValues(dataBeforeMigration.rowCounts.credLibHistory, dataAfterMigration.rowCounts.credLibHistory) require.EqualValues(dataBeforeMigration.rowCounts.credLib, dataAfterMigration.rowCounts.credVaultLibPostRefactor) // We fill new credential_vault_library from credential_library. require.EqualValues(dataBeforeMigration.rowCounts.credVaultGenericLibMappingOvrd, dataAfterMigration.rowCounts.credVaultGenericLibMappingOvrd) require.EqualValues(dataBeforeMigration.rowCounts.credVaultGenericLib, dataAfterMigration.rowCounts.credVaultGenericLib) require.EqualValues(dataBeforeMigration.rowCounts.credVaultGenericLibHistory, dataAfterMigration.rowCounts.credVaultGenericLibHistory) require.EqualValues(dataBeforeMigration.rowCounts.credVaultGenericLibDeleted, dataAfterMigration.rowCounts.credVaultGenericLibDeleted) require.EqualValues(dataBeforeMigration.rowCounts.credVaultSshCertLib, dataAfterMigration.rowCounts.credVaultSshCertLib) require.EqualValues(dataBeforeMigration.rowCounts.credVaultSshCertLibHistory, dataAfterMigration.rowCounts.credVaultSshCertLibHistory) require.EqualValues(dataBeforeMigration.rowCounts.credVaultSshCertLibDeleted, dataAfterMigration.rowCounts.credVaultSshCertLibDeleted) require.EqualValues(dataBeforeMigration.rowCounts.credVaultGenericLibMappingOvrdUserPass, dataAfterMigration.rowCounts.credVaultGenericLibMappingOvrdUserPass) require.EqualValues(dataBeforeMigration.rowCounts.credVaultGenericLibMappingOvrdUserPassDomain, dataAfterMigration.rowCounts.credVaultGenericLibMappingOvrdUserPassDomain) require.EqualValues(dataBeforeMigration.rowCounts.credVaultGenericLibMappingOvrdSshPk, dataAfterMigration.rowCounts.credVaultGenericLibMappingOvrdSshPk) require.EqualValues(dataBeforeMigration.rowCounts.credVaultLibIssueCredentials, dataAfterMigration.rowCounts.credVaultLibIssueCredentials) require.EqualValues(dataBeforeMigration.rowCounts.credVaultGenericLibListLookup, dataAfterMigration.rowCounts.credVaultGenericLibListLookup) require.EqualValues(dataBeforeMigration.rowCounts.credVaultGenericLibHistoryAgg, dataAfterMigration.rowCounts.credVaultGenericLibHistoryAgg) require.EqualValues(dataBeforeMigration.rowCounts.credDimensionSource, dataAfterMigration.rowCounts.credDimensionSource) } // testVaultCredLibRefactorDbSetup sets up a Boundary database running a prior // migration with credential library data, then migrates to the refactored // version. Not designed to be called outside of TestVaultCredLibRefactor. func testVaultCredLibRefactorDbSetup(t testing.TB, counts testVaultCredLibRefactorDbSetupCounts) (beforeMigration, afterMigration dbData) { require := require.New(t) const priorMigration = 98006 const credlibRefactorMigration = 99001 dialect := dbtest.Postgres c, u, _, err := dbtest.StartUsingTemplate(dialect, dbtest.WithTemplate(dbtest.Template1)) require.NoError(err) t.Cleanup(func() { require.NoError(c()) }) db, err := common.SqlOpen(dialect, u) require.NoError(err) // Migrate to the prior migration (before the one we want to test). m, err := schema.NewManager(t.Context(), schema.Dialect(dialect), db, schema.WithEditions( schema.TestCreatePartialEditions(schema.Dialect(dialect), schema.PartialEditions{"oss": priorMigration}), )) require.NoError(err) _, err = m.ApplyMigrations(t.Context()) require.NoError(err) state, err := m.CurrentState(t.Context()) require.NoError(err) require.Equal(&schema.State{ Initialized: true, Editions: []schema.EditionState{ { Name: "oss", BinarySchemaVersion: priorMigration, DatabaseSchemaVersion: priorMigration, DatabaseSchemaState: schema.Equal, }, }, }, state) tx, err := db.BeginTx(t.Context(), &sql.TxOptions{}) require.NoError(err) require.NotNil(tx) // Queries to seed all the required data. createOrgScopeQuery := ` insert into iam_scope (parent_id, type, public_id, name) values ('global', 'org', $1, $2); ` createProjectScopeQuery := ` insert into iam_scope (parent_id, type, public_id, name) values ( $1, 'project', $2, $3); ` createCredStoreQuery := ` insert into credential_vault_store (public_id, project_id, name, vault_address) values ( $1, $2, $3, $4); ` createVaultGenericCredLibQuery := ` insert into credential_vault_library (public_id, store_id, project_id, name, description, credential_type, vault_path, http_method, http_request_body) values ( $1, $2, $3, $4, $5, $6, $7, $8, $9); ` updateVaultGenericCredLibQuery := ` update credential_vault_library set name = $1, description = $2, vault_path = $3 where public_id = $4; ` deleteVaultGenericCredLibQuery := ` delete from credential_vault_library where public_id = $1; ` createUpMappingOvrdQuery := ` insert into credential_vault_library_username_password_mapping_override (library_id, username_attribute, password_attribute) values ( $1, $2, $3); ` createUpdMappingOvrdQuery := ` insert into credential_vault_library_username_password_domain_mapping_ovrd (library_id, username_attribute, password_attribute, domain_attribute) values ( $1, $2, $3, $4); ` createSshPkMappingOvrdQuery := ` insert into credential_vault_library_ssh_private_key_mapping_override (library_id, username_attribute, private_key_attribute, private_key_passphrase_attribute) values ( $1, $2, $3, $4); ` createVaultSshCertCredLibQuery := ` insert into credential_vault_ssh_cert_library (public_id, store_id, project_id, name, description, credential_type, vault_path, username, key_type, key_bits, ttl, key_id, critical_options, extensions, additional_valid_principals) values ( $1, $2, $3, $4, $5, 'ssh_certificate', $6, $7, $8, $9, $10, $11, $12, $13, $14); ` updateVaultSshCertCredLibQuery := ` update credential_vault_ssh_cert_library set name = $1, description = $2, vault_path = $3, username = $4 where public_id = $5; ` deleteVaultSshCertCredLibQuery := ` delete from credential_vault_ssh_cert_library where public_id = $1; ` execOk := func(res sql.Result, err error) { // Exec returned no error. require.NoError(err) require.NotNil(res) // Exec affected at least 1 row. ra, err := res.RowsAffected() require.NoError(err) require.Greater(ra, int64(0)) } // Create org scopes. for range counts.orgs { orgId := fmt.Sprintf("o_%s", base62.MustRandom(10)) orgName := fmt.Sprintf("Test %q Org Scope", orgId) execOk(tx.ExecContext(t.Context(), createOrgScopeQuery, orgId, orgName)) // For each org scope, create `projectsPerOrgCount` project scopes and a // Vault credential store. for range counts.projectsPerOrg { projectId := fmt.Sprintf("p_%s", base62.MustRandom(10)) projectName := fmt.Sprintf("Test %q Project Scope", projectId) execOk(tx.ExecContext(t.Context(), createProjectScopeQuery, orgId, projectId, projectName)) credStoreId := fmt.Sprintf("csst_%s", base62.MustRandom(10)) credStoreName := fmt.Sprintf("Test %q Vault Credential Store", credStoreId) execOk(tx.ExecContext(t.Context(), createCredStoreQuery, credStoreId, projectId, credStoreName, "http://127.0.0.1:8200")) // For each credential store, create `vaultGenericCredLibs` Vault // generic credential libraries with differing configurations and // mapping overrides. Add `vaultGenericCredLibsDeleted` to account // for the ones we have to create to delete. deletedVaultGenericCredLibs := 0 for range counts.vaultGenericCredLibs + counts.vaultGenericCredLibsDeleted { vgCredLibId := fmt.Sprintf("clvlt_%s", base62.MustRandom(10)) vgCredLibName := fmt.Sprintf("Test %q Vault Generic Credential Library", base62.MustRandom(10)) var vgCredType string switch rand.IntN(4) { case 0: vgCredType = "unspecified" case 1: vgCredType = "username_password" case 2: vgCredType = "username_password_domain" case 3: vgCredType = "ssh_private_key" } var vgHttpMethod string var vgHttpReqBody []byte switch rand.IntN(2) { case 0: vgHttpMethod = "GET" case 1: vgHttpMethod = "POST" vgHttpReqBody = []byte(`{"my_custom_request_body":true, "data": "this_is_some_data"}`) } execOk(tx.ExecContext(t.Context(), createVaultGenericCredLibQuery, vgCredLibId, credStoreId, projectId, vgCredLibName, vgCredLibName, vgCredType, "/my/vg/vault/path", vgHttpMethod, vgHttpReqBody, )) var vgMappingOverrideQuery string vgMappingOverrideArgs := []any{vgCredLibId} switch vgCredType { case "unspecified": // No mapping override. case "username_password": vgMappingOverrideQuery = createUpMappingOvrdQuery vgMappingOverrideArgs = append(vgMappingOverrideArgs, "username_ovrd", "password_ovrd") case "username_password_domain": vgMappingOverrideQuery = createUpdMappingOvrdQuery vgMappingOverrideArgs = append(vgMappingOverrideArgs, "username_ovrd", "password_ovrd", "domain_ovrd") case "ssh_private_key": vgMappingOverrideQuery = createSshPkMappingOvrdQuery vgMappingOverrideArgs = append(vgMappingOverrideArgs, "username_ovrd", "ssh_pk_ovrd", "ssh_pk_passphrase_ovrd") } if vgMappingOverrideQuery != "" { execOk(tx.ExecContext(t.Context(), vgMappingOverrideQuery, vgMappingOverrideArgs...)) } // Chance of updating the credential library. switch rand.IntN(10) { case 0: execOk(tx.ExecContext(t.Context(), updateVaultGenericCredLibQuery, "Updated"+vgCredLibName, "Updated"+vgCredLibName, "/my/updated/vault/path", vgCredLibId)) } if counts.vaultGenericCredLibsDeleted > deletedVaultGenericCredLibs { execOk(tx.ExecContext(t.Context(), deleteVaultGenericCredLibQuery, vgCredLibId)) deletedVaultGenericCredLibs++ } } // For each credential store, create `vaultSshCertCredLibs` Vault // SSH Certificate credential libraries with different // configurations. Add `vaultSshCertCredLibsDeleted` to account for // the ones we have to create to delete. deletedSshCertCredLibs := 0 for range counts.vaultSshCertCredLibs + counts.vaultSshCertCredLibsDeleted { vSshCertCredLibId := fmt.Sprintf("clvsclt_%s", base62.MustRandom(10)) vSshCertCredLibName := fmt.Sprintf("Test %q Vault SSH Certificate Credential Library", base62.MustRandom(10)) var keyType string var keyBits int switch rand.IntN(3) { case 0: keyType = "ed25519" keyBits = 0 case 1: keyType = "ecdsa" keyBits = 521 case 2: keyType = "rsa" keyBits = 4096 } var ttl string switch rand.IntN(2) { case 0: // No TTL. case 1: ttl = strconv.FormatInt(rand.Int64N(65536), 10) } var keyId string switch rand.IntN(2) { case 0: // No key id. case 1: keyId = "my_key_id" } var criticalOptions []byte switch rand.IntN(2) { case 0: // No Critical options. case 1: criticalOptions = []byte(`critical_option1=yes, critical_option2=alsoyes, critical_option3=yep`) } var extensions []byte switch rand.IntN(2) { case 0: // No extensions. case 1: extensions = []byte(`extension1=yes, extension2=alsoyes, extension3=yep`) } var additionalPrincipals string switch rand.IntN(2) { case 0: // No additional principals. case 1: additionalPrincipals = "principal1, principal2, principal3" } execOk(tx.ExecContext(t.Context(), createVaultSshCertCredLibQuery, vSshCertCredLibId, credStoreId, projectId, vSshCertCredLibName, vSshCertCredLibName, "/ssh/sign/cert1", "username", keyType, keyBits, ttl, keyId, criticalOptions, extensions, additionalPrincipals, )) require.NoError(err) // Chance of updating credential library. switch rand.IntN(10) { case 0: execOk(tx.ExecContext(t.Context(), updateVaultSshCertCredLibQuery, "Updated"+vSshCertCredLibName, "Updated"+vSshCertCredLibName, "/ssh/issue/updated_cert1", "updated_username", vSshCertCredLibId)) } if counts.vaultSshCertCredLibsDeleted > deletedSshCertCredLibs { execOk(tx.ExecContext(t.Context(), deleteVaultSshCertCredLibQuery, vSshCertCredLibId)) deletedSshCertCredLibs++ } } } } err = tx.Commit() require.NoError(err) // Get a snapshot of what the data looks like before the migration. beforeMigration = testBuildTestVaultCredLibRefactorDbData(t, db, false) // Now we're ready for the migration we want to test. m, err = schema.NewManager(t.Context(), schema.Dialect(dialect), db, schema.WithEditions( schema.TestCreatePartialEditions(schema.Dialect(dialect), schema.PartialEditions{"oss": credlibRefactorMigration}), )) require.NoError(err) _, err = m.ApplyMigrations(t.Context()) require.NoError(err) state, err = m.CurrentState(t.Context()) require.NoError(err) require.Equal(&schema.State{ Initialized: true, Editions: []schema.EditionState{ { Name: "oss", BinarySchemaVersion: credlibRefactorMigration, DatabaseSchemaVersion: credlibRefactorMigration, DatabaseSchemaState: schema.Equal, }, }, }, state) // Get a snapshot of the data after the migration. afterMigration = testBuildTestVaultCredLibRefactorDbData(t, db, true) return beforeMigration, afterMigration } // testBuildTestVaultCredLibRefactorDbData will build a 'dbData' object from // database queries. Accepts a boolean that denotes if any given call is // post-refactor. 'false' means assume before refactor migration; 'true' means // assume after refactor migration. This boolean controls table names on various // queries that retrieve data. func testBuildTestVaultCredLibRefactorDbData(t testing.TB, db *sql.DB, afterRefactor bool) dbData { require := require.New(t) queryContext := func(tx *sql.Tx, query string, callbackFn func(scanFn func(dest ...any) error)) { rows, err := tx.QueryContext(t.Context(), query) require.NoError(err) require.NotNil(rows) for rows.Next() { callbackFn(rows.Scan) } require.NoError(rows.Err()) } data := dbData{ orgs: make([]orgProj, 0), projects: make([]orgProj, 0), credentialStores: make([]vaultCredStore, 0), credLibBase: make([]credLibBase, 0), credLibHistoryBase: make([]credLibHistoryBase, 0), postRefactorVaultCredLibBase: make([]credLibBase, 0), vaultGenericCredLibs: make([]vaultGenericCredLib, 0), vaultSshCertCredLibs: make([]vaultSshCertCredLib, 0), deletedVaultGenericCredLibs: make([]credLibDeleted, 0), deletedVaultSshCertCredLibs: make([]credLibDeleted, 0), rowCounts: rowCounts{}, } tx, err := db.BeginTx(t.Context(), &sql.TxOptions{}) require.NoError(err) require.NotNil(tx) defer func() { _ = tx.Rollback() }() // Get org and project scopes. queryContext(tx, "select * from iam_scope", func(scanFn func(dest ...any) error) { var publicId, name, typ, description, parentId, primaryAuthMethodId *string var createTime, updateTime *time.Time var version *int require.NoError(scanFn(&publicId, &createTime, &updateTime, &name, &typ, &description, &parentId, &version, &primaryAuthMethodId), ) op := orgProj{ publicId: testDeref(t, publicId), name: testDeref(t, name), description: testDeref(t, description), createTime: testDeref(t, createTime), updateTime: testDeref(t, updateTime), typ: testDeref(t, typ), parentId: testDeref(t, parentId), version: testDeref(t, version), } switch op.typ { case "global": case "org": data.orgs = append(data.orgs, op) case "project": data.projects = append(data.projects, op) default: require.Failf("failed to get org/project scopes", "unknown scope type %s", op.typ) } }) // Get credential stores. queryContext(tx, "select * from credential_vault_store", func(scanFn func(dest ...any) error) { var publicId, projectId, name, description, vaultAddr, namespace, tlsServerName, workerFilter *string var createTime, updateTime, deleteTime *time.Time var version *int var caCert *[]byte var tlsSkipVerify *bool require.NoError(scanFn(&publicId, &projectId, &name, &description, &createTime, &updateTime, &deleteTime, &version, &vaultAddr, &namespace, &caCert, &tlsServerName, &tlsSkipVerify, &workerFilter), ) data.credentialStores = append(data.credentialStores, vaultCredStore{ publicId: testDeref(t, publicId), projectId: testDeref(t, projectId), name: testDeref(t, name), description: testDeref(t, description), createTime: testDeref(t, createTime), updateTime: testDeref(t, updateTime), deleteTime: testDeref(t, deleteTime), version: testDeref(t, version), vaultAddr: testDeref(t, vaultAddr), namespace: testDeref(t, namespace), caCert: testDeref(t, caCert), tlsServerName: testDeref(t, tlsServerName), tlsSkipVerify: testDeref(t, tlsSkipVerify), workerFilter: testDeref(t, workerFilter), }) }) // Get credential library base table. queryContext(tx, "select * from credential_library", func(scanFn func(dest ...any) error) { var publicId, storeId, credType, projectId *string var createTime, updateTime *time.Time require.NoError(scanFn(&publicId, &storeId, &credType, &projectId, &createTime, &updateTime)) data.credLibBase = append(data.credLibBase, credLibBase{ publicId: testDeref(t, publicId), storeId: testDeref(t, storeId), credType: testDeref(t, credType), projectId: testDeref(t, projectId), createTime: testDeref(t, createTime), updateTime: testDeref(t, updateTime), }) }) // Get credential library base table if we're post-refactor. if afterRefactor { queryContext(tx, "select * from credential_vault_library", func(scanFn func(dest ...any) error) { var publicId, storeId, credType, projectId *string var createTime, updateTime *time.Time require.NoError(scanFn(&publicId, &storeId, &credType, &projectId, &createTime, &updateTime)) data.postRefactorVaultCredLibBase = append(data.postRefactorVaultCredLibBase, credLibBase{ publicId: testDeref(t, publicId), storeId: testDeref(t, storeId), credType: testDeref(t, credType), projectId: testDeref(t, projectId), createTime: testDeref(t, createTime), updateTime: testDeref(t, updateTime), }) }) } // Get credential library history base table. queryContext(tx, "select * from credential_library_history_base", func(scanFn func(dest ...any) error) { var historyId *string require.NoError(scanFn(&historyId)) data.credLibHistoryBase = append(data.credLibHistoryBase, credLibHistoryBase{testDeref(t, historyId)}) }) // Get Vault generic credential libraries. vaultGenericCredLibTableName := "credential_vault_library" if afterRefactor { vaultGenericCredLibTableName = "credential_vault_generic_library" } queryContext(tx, fmt.Sprintf("select * from %s", vaultGenericCredLibTableName), func(scanFn func(dest ...any) error) { var publicId, storeId, name, description, vaultPath, httpMethod, credType, projectId *string var createTime, updateTime *time.Time var version *int var httpReqBody *[]byte require.NoError(scanFn(&publicId, &storeId, &name, &description, &createTime, &updateTime, &version, &vaultPath, &httpMethod, &httpReqBody, &credType, &projectId), ) data.vaultGenericCredLibs = append(data.vaultGenericCredLibs, vaultGenericCredLib{ publicId: testDeref(t, publicId), storeId: testDeref(t, storeId), name: testDeref(t, name), description: testDeref(t, description), createTime: testDeref(t, createTime), updateTime: testDeref(t, updateTime), version: testDeref(t, version), vaultPath: testDeref(t, vaultPath), httpMethod: testDeref(t, httpMethod), httpReqBody: testDeref(t, httpReqBody), credType: testDeref(t, credType), projectId: testDeref(t, projectId), mappingOverride: vaultGenericCredLibMappingOvrd{}, // Fetch below. history: make([]vaultGenericCredLibHistory, 0), // Fetch below. }) }) // Get mapping overrides for Vault generic credential libraries. upMappingOvrdTableName := "credential_vault_library_username_password_mapping_override" updMappingOvrdTableName := "credential_vault_library_username_password_domain_mapping_ovrd" sshPkMappingOvrdTableName := "credential_vault_library_ssh_private_key_mapping_override" if afterRefactor { upMappingOvrdTableName = "credential_vault_generic_library_username_password_mapping_ovrd" updMappingOvrdTableName = "credential_vault_generic_library_usern_pass_domain_mapping_ovrd" sshPkMappingOvrdTableName = "credential_vault_generic_library_ssh_private_key_mapping_ovrd" } for _, cl := range data.vaultGenericCredLibs { queryFmt := "select * from %s where library_id = '%s'" getMappingOvrd := false switch cl.credType { case "unspecified": case "username_password": queryFmt = fmt.Sprintf(queryFmt, upMappingOvrdTableName, cl.publicId) getMappingOvrd = true case "username_password_domain": queryFmt = fmt.Sprintf(queryFmt, updMappingOvrdTableName, cl.publicId) getMappingOvrd = true case "ssh_private_key": queryFmt = fmt.Sprintf(queryFmt, sshPkMappingOvrdTableName, cl.publicId) getMappingOvrd = true default: require.Failf("failed to get vault generic mapping overrides", "unknown credential type %s", cl.credType) } if getMappingOvrd { queryContext(tx, queryFmt, func(scanFn func(dest ...any) error) { var libraryId, usernameAttr, passwordAttr, domainAttr, pkAttr, pkPassphraseAttr *string switch cl.credType { case "username_password": require.NoError(scanFn(&libraryId, &usernameAttr, &passwordAttr)) case "username_password_domain": require.NoError(scanFn(&libraryId, &usernameAttr, &passwordAttr, &domainAttr)) case "ssh_private_key": require.NoError(scanFn(&libraryId, &usernameAttr, &pkAttr, &pkPassphraseAttr)) } cl.mappingOverride = vaultGenericCredLibMappingOvrd{ libraryId: testDeref(t, libraryId), usernameAttr: testDeref(t, usernameAttr), passwordAttr: testDeref(t, passwordAttr), domainAttr: testDeref(t, domainAttr), pkAttr: testDeref(t, pkAttr), pkPassphraseAttr: testDeref(t, pkPassphraseAttr), } }) } } // Get history for Vault generic credential libraries. for _, cl := range data.vaultGenericCredLibs { hstTableName := "credential_vault_library_hst" if afterRefactor { hstTableName = "credential_vault_generic_library_hst" } query := fmt.Sprintf("select * from %s where public_id = '%s'", hstTableName, cl.publicId) queryContext(tx, query, func(scanFn func(dest ...any) error) { var publicId, name, description, projectId, storeId, vaultPath, httpMethod, credType, historyId, validRange *string var httpReqBody *[]byte require.NoError(scanFn(&publicId, &name, &description, &projectId, &storeId, &vaultPath, &httpMethod, &httpReqBody, &credType, &historyId, &validRange), ) cl.history = append(cl.history, vaultGenericCredLibHistory{ historyId: testDeref(t, historyId), validRange: testDeref(t, validRange), cl: vaultGenericCredLib{ publicId: testDeref(t, publicId), name: testDeref(t, name), description: testDeref(t, description), projectId: testDeref(t, projectId), storeId: testDeref(t, storeId), vaultPath: testDeref(t, vaultPath), httpMethod: testDeref(t, httpMethod), httpReqBody: testDeref(t, httpReqBody), credType: testDeref(t, credType), }, }) }) } // Get Vault SSH certificate credential libraries. queryContext(tx, "select * from credential_vault_ssh_cert_library", func(scanFn func(dest ...any) error) { var publicId, storeId, name, description, vaultPath, username, keyType, ttl, keyId, credType, projectId, additionalPrincipals *string var createTime, updateTime *time.Time var version, keyBits *int var criticalOptions, extensions *[]byte require.NoError(scanFn(&publicId, &storeId, &name, &description, &createTime, &updateTime, &version, &vaultPath, &username, &keyType, &keyBits, &ttl, &keyId, &criticalOptions, &extensions, &credType, &projectId, &additionalPrincipals), ) data.vaultSshCertCredLibs = append(data.vaultSshCertCredLibs, vaultSshCertCredLib{ publicId: testDeref(t, publicId), storeId: testDeref(t, storeId), name: testDeref(t, name), description: testDeref(t, description), createTime: testDeref(t, createTime), updateTime: testDeref(t, updateTime), version: testDeref(t, version), vaultPath: testDeref(t, vaultPath), username: testDeref(t, username), keyType: testDeref(t, keyType), keyBits: testDeref(t, keyBits), ttl: testDeref(t, ttl), keyId: testDeref(t, keyId), criticalOptions: testDeref(t, criticalOptions), extensions: testDeref(t, extensions), credType: testDeref(t, credType), projectId: testDeref(t, projectId), additionalValidPrincipals: testDeref(t, additionalPrincipals), history: make([]vaultSshCertCredLibHistory, 0), // Fetch below. }) }) // Get history for Vault SSH certificate credential libraries. for _, cl := range data.vaultSshCertCredLibs { query := fmt.Sprintf("select * from credential_vault_ssh_cert_library_hst where public_id = '%s'", cl.publicId) queryContext(tx, query, func(scanFn func(dest ...any) error) { var publicId, storeId, name, description, vaultPath, username, keyType, ttl, credType, projectId, historyId, validRange *string var keyBits *int var criticalOptions, extensions *[]byte require.NoError(scanFn(&publicId, &name, &description, &projectId, &storeId, &vaultPath, &username, &keyType, &keyBits, &ttl, &criticalOptions, &extensions, &credType, &historyId, &validRange), ) cl.history = append(cl.history, vaultSshCertCredLibHistory{ historyId: *historyId, validRange: *validRange, cl: vaultSshCertCredLib{ publicId: testDeref(t, publicId), name: testDeref(t, name), description: testDeref(t, description), projectId: testDeref(t, projectId), storeId: testDeref(t, storeId), vaultPath: testDeref(t, vaultPath), username: testDeref(t, username), keyType: testDeref(t, keyType), keyBits: testDeref(t, keyBits), ttl: testDeref(t, ttl), criticalOptions: testDeref(t, criticalOptions), extensions: testDeref(t, extensions), credType: testDeref(t, credType), }, }) }) } // Get all deleted vault credential libraries. vaultGenericCredLibDeletedTableName := "credential_vault_library_deleted" if afterRefactor { vaultGenericCredLibDeletedTableName = "credential_vault_generic_library_deleted" } vaultSshCertCredLibDeletedTableName := "credential_vault_ssh_cert_library_deleted" deletedTables := []string{vaultGenericCredLibDeletedTableName, vaultSshCertCredLibDeletedTableName} for _, dt := range deletedTables { queryContext(tx, fmt.Sprintf("select * from %s", dt), func(scanFn func(dest ...any) error) { var publicId *string var deleteTime *time.Time require.NoError(scanFn(&publicId, &deleteTime)) cld := credLibDeleted{ publicId: testDeref(t, publicId), deleteTime: testDeref(t, deleteTime), } switch dt { case vaultGenericCredLibDeletedTableName: data.deletedVaultGenericCredLibs = append(data.deletedVaultGenericCredLibs, cld) case vaultSshCertCredLibDeletedTableName: data.deletedVaultSshCertCredLibs = append(data.deletedVaultSshCertCredLibs, cld) } }) } // Get row counts. var rowCountQuery string if !afterRefactor { rowCountQuery = ` select ( select count(*) from credential_library ), ( select count(*) from credential_library_history_base ), ( select count(*) from credential_vault_library_mapping_override ), ( select count(*) from credential_vault_library ), ( select count(*) from credential_vault_library_hst ), ( select count(*) from credential_vault_library_deleted ), ( select count(*) from credential_vault_ssh_cert_library ), ( select count(*) from credential_vault_ssh_cert_library_hst ), ( select count(*) from credential_vault_ssh_cert_library_deleted ), ( select count(*) from credential_vault_library_username_password_mapping_override ), ( select count(*) from credential_vault_library_username_password_domain_mapping_ovrd ), ( select count(*) from credential_vault_library_ssh_private_key_mapping_override ), ( select count(*) from credential_vault_library_issue_credentials ), ( select count(*) from credential_vault_library_list_lookup ), ( select count(*) from credential_vault_library_hst_aggregate ), ( select count(*) from whx_credential_dimension_source ); ` } else { rowCountQuery = ` select ( select count(*) from credential_library ), ( select count(*) from credential_library_history_base ), ( select count(*) from credential_vault_generic_library_mapping_override ), ( select count(*) from credential_vault_library ), ( select count(*) from credential_vault_generic_library ), ( select count(*) from credential_vault_generic_library_hst ), ( select count(*) from credential_vault_generic_library_deleted ), ( select count(*) from credential_vault_ssh_cert_library ), ( select count(*) from credential_vault_ssh_cert_library_hst ), ( select count(*) from credential_vault_ssh_cert_library_deleted ), ( select count(*) from credential_vault_generic_library_username_password_mapping_ovrd ), ( select count(*) from credential_vault_generic_library_usern_pass_domain_mapping_ovrd ), ( select count(*) from credential_vault_generic_library_ssh_private_key_mapping_ovrd ), ( select count(*) from credential_vault_library_issue_credentials ), ( select count(*) from credential_vault_generic_library_list_lookup ), ( select count(*) from credential_vault_generic_library_hst_aggregate ), ( select count(*) from whx_credential_dimension_source ); ` } queryContext(tx, rowCountQuery, func(scanFn func(dest ...any) error) { var cl, clHistBase, clVaultBasePostRefactorOnly, clVaultGenericMappingOvrd, clVaultGeneric, clVaultGenericHist, clVaultGenericDel, clVaultSshCert, clVaultSshCertHist, clVaultSshCertDel, clVaultGenericUpMappingOvrd, clVaultGenericUpdMappingOvrd, clVaultGenericSshPkMappingOverd, clVaultLibIssueCreds, clVaultGenericListLookup, clVaultGenericHistAgg, credDimensionSource *int64 if !afterRefactor { require.NoError(scanFn(&cl, &clHistBase, &clVaultGenericMappingOvrd, &clVaultGeneric, &clVaultGenericHist, &clVaultGenericDel, &clVaultSshCert, &clVaultSshCertHist, &clVaultSshCertDel, &clVaultGenericUpMappingOvrd, &clVaultGenericUpdMappingOvrd, &clVaultGenericSshPkMappingOverd, &clVaultLibIssueCreds, &clVaultGenericListLookup, &clVaultGenericHistAgg, &credDimensionSource), ) } else { require.NoError(scanFn(&cl, &clHistBase, &clVaultGenericMappingOvrd, &clVaultBasePostRefactorOnly, &clVaultGeneric, &clVaultGenericHist, &clVaultGenericDel, &clVaultSshCert, &clVaultSshCertHist, &clVaultSshCertDel, &clVaultGenericUpMappingOvrd, &clVaultGenericUpdMappingOvrd, &clVaultGenericSshPkMappingOverd, &clVaultLibIssueCreds, &clVaultGenericListLookup, &clVaultGenericHistAgg, &credDimensionSource), ) } data.rowCounts = rowCounts{ credLib: testDeref(t, cl), credLibHistory: testDeref(t, clHistBase), credVaultLibPostRefactor: testDeref(t, clVaultBasePostRefactorOnly), credVaultGenericLibMappingOvrd: testDeref(t, clVaultGenericMappingOvrd), credVaultGenericLib: testDeref(t, clVaultGeneric), credVaultGenericLibHistory: testDeref(t, clVaultGenericHist), credVaultGenericLibDeleted: testDeref(t, clVaultGenericDel), credVaultSshCertLib: testDeref(t, clVaultSshCert), credVaultSshCertLibHistory: testDeref(t, clVaultSshCertHist), credVaultSshCertLibDeleted: testDeref(t, clVaultSshCertDel), credVaultGenericLibMappingOvrdUserPass: testDeref(t, clVaultGenericUpMappingOvrd), credVaultGenericLibMappingOvrdUserPassDomain: testDeref(t, clVaultGenericUpdMappingOvrd), credVaultGenericLibMappingOvrdSshPk: testDeref(t, clVaultGenericSshPkMappingOverd), credVaultLibIssueCredentials: testDeref(t, clVaultLibIssueCreds), credVaultGenericLibListLookup: testDeref(t, clVaultGenericListLookup), credVaultGenericLibHistoryAgg: testDeref(t, clVaultGenericHistAgg), credDimensionSource: testDeref(t, credDimensionSource), } }) return data } // testDeref is a generic function that dereferences a pointer type, // safeguarding against a nil pointer dereference. If the input pointer is nil, // it returns the zero-value for the input type. func testDeref[T any](_ testing.TB, in *T) T { if in == nil { return *new(T) } return *in } // dbData is the aggregation of all the data that // testBuildTestVaultCredLibRefactorDbData gets together. Not designed to be // used outside of TestVaultCredLibRefactor. type dbData struct { orgs []orgProj // From iam_scope. projects []orgProj // From iam_scope. credentialStores []vaultCredStore // From credential_vault_store. credLibBase []credLibBase // From credential_library. credLibHistoryBase []credLibHistoryBase // From credential_library_history_base. postRefactorVaultCredLibBase []credLibBase // From credential_vault_library (after refactor only). vaultGenericCredLibs []vaultGenericCredLib // From credential_vault_library (pre-refactor) or credential_vault_generic_library. vaultSshCertCredLibs []vaultSshCertCredLib // From credential_vault_ssh_cert_library. deletedVaultGenericCredLibs []credLibDeleted // From credential_vault_library_deleted (pre-refactor) or credential_vault_generic_library_deleted. deletedVaultSshCertCredLibs []credLibDeleted // From credential_vault_ssh_cert_library_deleted. rowCounts rowCounts } // orgProj holds database data about a org/project scope. Not designed to be // used outside of testBuildTestVaultCredLibRefactorDbData. type orgProj struct { publicId string name string description string createTime time.Time updateTime time.Time typ string parentId string version int } // credLibBase holds database data about a credential library. // Not designed to be used outside of testBuildTestVaultCredLibRefactorDbData. type credLibBase struct { publicId string storeId string credType string projectId string createTime time.Time updateTime time.Time } // credLibHistoryBase holds historical data about a credential library. Not // designed to be used outside of testBuildTestVaultCredLibRefactorDbData. type credLibHistoryBase struct { historyId string } // credLibDeleted holds database data about a deleted credential library. Not // designed to be used outside of testBuildTestVaultCredLibRefactorDbData. type credLibDeleted struct { publicId string deleteTime time.Time } // vaultCredStore holds database data about a Vault credential store. Not // designed to be used outside of testBuildTestVaultCredLibRefactorDbData. type vaultCredStore struct { publicId string projectId string name string description string createTime time.Time updateTime time.Time deleteTime time.Time version int vaultAddr string namespace string caCert []byte tlsServerName string tlsSkipVerify bool workerFilter string } // vaultGenericCredLib holds database data about a Vault generic credential // library. Not designed to be used outside of // testBuildTestVaultCredLibRefactorDbData. type vaultGenericCredLib struct { publicId string storeId string name string description string createTime time.Time updateTime time.Time version int vaultPath string httpMethod string httpReqBody []byte credType string projectId string mappingOverride vaultGenericCredLibMappingOvrd history []vaultGenericCredLibHistory } type vaultGenericCredLibHistory struct { cl vaultGenericCredLib historyId string validRange string } // vaultSshCertCredLib holds database data about a Vault SSH certificate // credential library. Not designed to be used outside of // testBuildTestVaultCredLibRefactorDbData. type vaultSshCertCredLib struct { publicId string storeId string name string description string createTime time.Time updateTime time.Time version int vaultPath string username string keyType string keyBits int ttl string keyId string criticalOptions []byte extensions []byte credType string projectId string additionalValidPrincipals string history []vaultSshCertCredLibHistory } type vaultSshCertCredLibHistory struct { cl vaultSshCertCredLib historyId string validRange string } // vaultGenericCredLibMappingOvrd holds database data about a Vault generic // credential library's mapping override. Not designed to be used outside of // testBuildTestVaultCredLibRefactorDbData. type vaultGenericCredLibMappingOvrd struct { libraryId string usernameAttr string passwordAttr string domainAttr string pkAttr string pkPassphraseAttr string } // rowCounts holds row count information for various database tables and views. // Not designed to be used outside of testBuildTestVaultCredLibRefactorDbData. type rowCounts struct { // credential_library credLib int64 // credential_library_history_base credLibHistory int64 // credential_vault_library (post-refactor meaning only) credVaultLibPostRefactor int64 // credential_vault_library_mapping_override (pre-refactor) or // credential_vault_generic_library_mapping_override credVaultGenericLibMappingOvrd int64 // credential_vault_library (pre-refactor) or // credential_vault_generic_library credVaultGenericLib int64 // credential_vault_library_hst (pre-refactor) or // credential_vault_generic_library_hst credVaultGenericLibHistory int64 // credential_vault_library_deleted (pre-refactor) or // credential_vault_generic_library_deleted credVaultGenericLibDeleted int64 // credential_vault_ssh_cert_library credVaultSshCertLib int64 // credential_vault_ssh_cert_library_hst credVaultSshCertLibHistory int64 // credential_vault_ssh_cert_library_deleted credVaultSshCertLibDeleted int64 // credential_vault_library_username_password_mapping_override // (pre-refactor) or // credential_vault_generic_library_username_password_mapping_ovrd credVaultGenericLibMappingOvrdUserPass int64 // credential_vault_library_username_password_domain_mapping_ovrd // (pre-refactor) or // credential_vault_generic_library_usern_pass_domain_mapping_ovrd credVaultGenericLibMappingOvrdUserPassDomain int64 // credential_vault_library_ssh_private_key_mapping_override (pre-refactor) // or credential_vault_generic_library_ssh_private_key_mapping_ovrd credVaultGenericLibMappingOvrdSshPk int64 // credential_vault_library_issue_credentials credVaultLibIssueCredentials int64 // credential_vault_library_list_lookup (pre-refactor), or // credential_vault_generic_library_list_lookup credVaultGenericLibListLookup int64 // credential_vault_library_hst_aggregate (pre-refactor), or // credential_vault_generic_library_hst_aggregate credVaultGenericLibHistoryAgg int64 // whx_credential_dimension_source credDimensionSource int64 } // testVaultCredLibRefactorDbSetupCounts is an input to // testVaultCredLibRefactorDbSetup that controls the number of // orgs/projects/credential libraries to create. type testVaultCredLibRefactorDbSetupCounts struct { // orgs is the number of org scopes to create. orgs int // projectsPerOrg is the number of project scopes to create per org. projectsPerOrg int // vaultGenericCredLibs is the number of Vault generic credential // libraries to create per project. vaultGenericCredLibs int // vaultGenericCredLibsDeleted is the number of Vault generic credential // libraries to delete per project. Note that we create new credential // libraries for the purposes of deletion so this doesn't affect // vaultGenericCredLibs. vaultGenericCredLibsDeleted int // vaultSshCertCredLibs is the number of Vault SSH Certificate // credential libraries to create per project. vaultSshCertCredLibs int // vaultSshCertCredLibsDeleted is the number of Vault SSH certificate // credential libraries to delete per project. Note that we create new // credential libraries for the purposes of deletion so this doesn't affect // vaultSshCertCredLibs. vaultSshCertCredLibsDeleted int }