diff --git a/internal/db/schema/migrations/postgres/4/01_iam.up.sql b/internal/db/schema/migrations/postgres/4/01_iam.up.sql new file mode 100644 index 0000000000..a1c95d102d --- /dev/null +++ b/internal/db/schema/migrations/postgres/4/01_iam.up.sql @@ -0,0 +1,56 @@ +begin; + +-- fix ordering of fields in iam_acct_info for auth_password_account select +-- portion of union. requires recreating both views because of deps. + +drop view iam_user_acct_info; +drop view iam_acct_info; + +create view iam_acct_info as +select + aa.iam_user_id, + oa.subject as login_name, + oa.public_id as primary_account_id, + oa.full_name as full_name, + oa.email as email +from + iam_scope s, + auth_account aa, + auth_oidc_account oa +where + aa.public_id = oa.public_id and + aa.auth_method_id = s.primary_auth_method_id +union +select + aa.iam_user_id, + pa.login_name, + pa.public_id as primary_account_id, + '' as full_name, + '' as email +from + iam_scope s, + auth_account aa, + auth_password_account pa +where + aa.public_id = pa.public_id and + aa.auth_method_id = s.primary_auth_method_id; + + +create view iam_user_acct_info as +select + u.public_id, + u.scope_id, + u.name, + u.description, + u.create_time, + u.update_time, + u.version, + i.primary_account_id, + i.login_name, + i.full_name, + i.email +from + iam_user u +left outer join iam_acct_info i on u.public_id = i.iam_user_id; + +commit; \ No newline at end of file diff --git a/internal/db/schema/postgres_migration.gen.go b/internal/db/schema/postgres_migration.gen.go index 106ac1d77c..316fddec4c 100644 --- a/internal/db/schema/postgres_migration.gen.go +++ b/internal/db/schema/postgres_migration.gen.go @@ -4,7 +4,7 @@ package schema func init() { migrationStates["postgres"] = migrationState{ - binarySchemaVersion: 3002, + binarySchemaVersion: 4001, upMigrations: map[int][]byte{ 1: []byte(` create domain wt_public_id as text @@ -5933,6 +5933,62 @@ where aa.auth_method_id = s.primary_auth_method_id; +-- iam_user_acct_info provides a simple way to retrieve entries that include +-- both the iam_user fields with an outer join to the user's account info. +create view iam_user_acct_info as +select + u.public_id, + u.scope_id, + u.name, + u.description, + u.create_time, + u.update_time, + u.version, + i.primary_account_id, + i.login_name, + i.full_name, + i.email +from + iam_user u +left outer join iam_acct_info i on u.public_id = i.iam_user_id; +`), + 4001: []byte(` +-- fix ordering of fields in iam_acct_info for auth_password_account select +-- portion of union. requires recreating both views because of deps. + +drop view iam_user_acct_info; +drop view iam_acct_info; + +create view iam_acct_info as +select + aa.iam_user_id, + oa.subject as login_name, + oa.public_id as primary_account_id, + oa.full_name as full_name, + oa.email as email +from + iam_scope s, + auth_account aa, + auth_oidc_account oa +where + aa.public_id = oa.public_id and + aa.auth_method_id = s.primary_auth_method_id +union +select + aa.iam_user_id, + pa.login_name, + pa.public_id as primary_account_id, + '' as full_name, + '' as email +from + iam_scope s, + auth_account aa, + auth_password_account pa +where + aa.public_id = pa.public_id and + aa.auth_method_id = s.primary_auth_method_id; + + -- iam_user_acct_info provides a simple way to retrieve entries that include -- both the iam_user fields with an outer join to the user's account info. create view iam_user_acct_info as diff --git a/internal/iam/repository_user_test.go b/internal/iam/repository_user_test.go index 0621b92d0c..a360b7614a 100644 --- a/internal/iam/repository_user_test.go +++ b/internal/iam/repository_user_test.go @@ -116,6 +116,85 @@ func TestRepository_CreateUser(t *testing.T) { } } +// TestRepository_LookupUser_WithDifferentPrimaryAuthMethods ensures that the +// when different auth method types are primary, the correct primary account +// info is returned from Repository.LookupUser(...) +func TestRepository_LookupUser_WithDifferentPrimaryAuthMethods(t *testing.T) { + t.Parallel() + ctx := context.Background() + conn, _ := db.TestSetup(t, "postgres") + wrapper := db.TestWrapper(t) + kmsCache := kms.TestKms(t, conn, wrapper) + repo := iam.TestRepo(t, conn, wrapper) + org, _ := iam.TestScopes(t, repo) + databaseWrapper, err := kmsCache.GetWrapper(context.Background(), org.PublicId, kms.KeyPurposeDatabase) + require.NoError(t, err) + + var accountIds []string + oidcAm := oidc.TestAuthMethod(t, conn, databaseWrapper, org.PublicId, oidc.ActivePrivateState, "alice-rp", "fido", + oidc.WithIssuer(oidc.TestConvertToUrls(t, "https://alice.com")[0]), + oidc.WithSigningAlgs(oidc.RS256), + oidc.WithApiUrl(oidc.TestConvertToUrls(t, "http://localhost")[0])) + aa := oidc.TestAccount(t, conn, oidcAm, "alice", oidc.WithFullName("alice eve smith"), oidc.WithEmail("alice@example.com")) + accountIds = append(accountIds, aa.PublicId) + + pwAms := password.TestAuthMethods(t, conn, org.PublicId, 1) + require.Equal(t, 1, len(pwAms)) + pwAcct := password.TestAccount(t, conn, pwAms[0].PublicId, "want-login-name") + accountIds = append(accountIds, pwAcct.PublicId) + + u := iam.TestUser(t, repo, org.PublicId) + newAccts, err := repo.AddUserAccounts(context.Background(), u.PublicId, u.Version, accountIds) + require.NoError(t, err) + sort.Strings(newAccts) + require.Equal(t, accountIds, newAccts) + + tests := []struct { + name string + primaryAuthMethodId string + wantLoginName string + wantPrimaryAcctId string + wantFullName string + wantEmail string + }{ + { + name: "oidc", + primaryAuthMethodId: oidcAm.PublicId, + wantLoginName: "alice", + wantPrimaryAcctId: aa.PublicId, + wantFullName: "alice eve smith", + wantEmail: "alice@example.com", + }, + { + name: "password", + primaryAuthMethodId: pwAms[0].PublicId, + wantLoginName: "want-login-name", + wantPrimaryAcctId: pwAcct.PublicId, + wantFullName: "", + wantEmail: "", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + + s, err := repo.LookupScope(context.Background(), org.PublicId) + require.NoError(err) + iam.TestSetPrimaryAuthMethod(t, repo, s, tt.primaryAuthMethodId) + + got, gotAccts, err := repo.LookupUser(ctx, u.PublicId) + require.NoError(err) + + sort.Strings(gotAccts) + assert.Equal(accountIds, gotAccts) + + assert.Equal(tt.wantLoginName, got.LoginName) + assert.Equal(tt.wantPrimaryAcctId, got.PrimaryAccountId) + assert.Equal(tt.wantFullName, got.FullName) + assert.Equal(tt.wantEmail, got.Email) + }) + } +} func TestRepository_UpdateUser(t *testing.T) { t.Parallel() conn, _ := db.TestSetup(t, "postgres")