From 4d27b58d2d208d29bc2ab9517493d52acef36fab Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Mon, 7 Jun 2021 19:35:45 -0500 Subject: [PATCH] Add managed groups test (#1289) --- internal/auth/oidc/testing.go | 14 ++ internal/iam/repository_role_grant.go | 21 +++ .../iam/repository_role_grant_ext_test.go | 135 +++++++++++++++--- 3 files changed, 147 insertions(+), 23 deletions(-) diff --git a/internal/auth/oidc/testing.go b/internal/auth/oidc/testing.go index e4a820f81b..8bf403409a 100644 --- a/internal/auth/oidc/testing.go +++ b/internal/auth/oidc/testing.go @@ -196,6 +196,20 @@ func TestManagedGroup(t *testing.T, conn *gorm.DB, am *AuthMethod, filter string return mg } +// TestManagedGroupMember adds given account IDs to a managed group +func TestManagedGroupMember(t *testing.T, conn *gorm.DB, managedGroupId, memberId string, opt ...Option) *ManagedGroupMemberAccount { + t.Helper() + require := require.New(t) + rw := db.New(conn) + ctx := context.Background() + + mg, err := NewManagedGroupMemberAccount(managedGroupId, memberId, opt...) + require.NoError(err) + + require.NoError(rw.Create(ctx, mg)) + return mg +} + // TestConvertToUrls will convert URL string representations to a slice of // *url.URL func TestConvertToUrls(t *testing.T, urls ...string) []*url.URL { diff --git a/internal/iam/repository_role_grant.go b/internal/iam/repository_role_grant.go index b589517e19..498cddda97 100644 --- a/internal/iam/repository_role_grant.go +++ b/internal/iam/repository_role_grant.go @@ -407,6 +407,24 @@ user_groups (id) as ( users where member_id in (users.id) ), +user_accounts (id) as ( + select public_id + from auth_account, + users + where iam_user_id in (users.id) +), +user_managed_groups (id) as ( + select managed_group_id + from auth_managed_group_member_account, + user_accounts + where member_id in (user_accounts.id) +), +managed_group_roles (role_id) as ( + select role_id + from iam_managed_group_role, + user_managed_groups + where principal_id in (user_managed_groups.id) +), group_roles (role_id) as ( select role_id from iam_group_role, @@ -425,6 +443,9 @@ user_group_roles (role_id) as ( union select role_id from user_roles + union + select role_id + from managed_group_roles ), roles (role_id, grant_scope_id) as ( select iam_role.public_id, diff --git a/internal/iam/repository_role_grant_ext_test.go b/internal/iam/repository_role_grant_ext_test.go index ef6588f096..ebc0f08f67 100644 --- a/internal/iam/repository_role_grant_ext_test.go +++ b/internal/iam/repository_role_grant_ext_test.go @@ -6,8 +6,10 @@ import ( mathrand "math/rand" "testing" + "github.com/hashicorp/boundary/internal/auth/oidc" "github.com/hashicorp/boundary/internal/db" "github.com/hashicorp/boundary/internal/iam" + "github.com/hashicorp/boundary/internal/kms" "github.com/hashicorp/boundary/internal/types/scope" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -21,10 +23,18 @@ func TestGrantsForUser(t *testing.T) { userCount := 10 groupCount := 30 + managedGroupCount := 30 roleCount := 30 // probFactor acts as a mod value; increasing means less probability. 2 = // 50%, 5 = 20%, etc. - probFactor := 4 + probFactor := 5 + // Turning this off will let users be cross-scope instead of in the same + // scope as the OIDC auth method + testManagedGroups := true + // Turning this off means users are not directly added to roles. Useful + // since we can't set users to 0 (or we won't accounts) but if we want to + // test only managed groups. + addUsersDirectly := true o, p := iam.TestScopes( t, @@ -33,16 +43,37 @@ func TestGrantsForUser(t *testing.T) { iam.WithSkipDefaultRoleCreation(true), ) - // We're going to generate a bunch of users, groups, and managed groups. - // These will be randomly assigned and we will record assignations. - users := func() (ret []*iam.User) { - ret = make([]*iam.User, 0, userCount) + kmsCache := kms.TestKms(t, conn, wrap) + databaseWrapper, err := kmsCache.GetWrapper(ctx, o.PublicId, kms.KeyPurposeDatabase) + require.NoError(t, err) + + authMethod := oidc.TestAuthMethod( + t, conn, databaseWrapper, o.GetPublicId(), oidc.ActivePrivateState, + "alice-rp", "fido", + oidc.WithSigningAlgs(oidc.RS256), + oidc.WithIssuer(oidc.TestConvertToUrls(t, "https://www.alice.com")[0]), + oidc.WithApiUrl(oidc.TestConvertToUrls(t, "https://www.alice.com/callback")[0]), + ) + + // We're going to generate a bunch of users (each tied to an account), + // groups, and managed groups. These will be randomly assigned and we will + // record assignations. + users, accounts := func() (usrs []*iam.User, accts []*oidc.Account) { + usrs = make([]*iam.User, 0, userCount) + accts = make([]*oidc.Account, 0, userCount) scopeId := scope.Global.String() - if mathrand.Int()%2 == 0 { + if mathrand.Int()%2 == 0 || testManagedGroups { scopeId = o.GetPublicId() } for i := 0; i < userCount; i++ { - ret = append(ret, iam.TestUser(t, iamRepo, scopeId, iam.WithName(fmt.Sprintf("testuser%d", i)))) + accts = append(accts, oidc.TestAccount(t, conn, authMethod, fmt.Sprintf("sub-%d", i))) + usrs = append(usrs, iam.TestUser( + t, + iamRepo, + scopeId, + iam.WithAccountIds(accts[i].PublicId), + iam.WithName(fmt.Sprintf("testuser%d", i)), + )) } return }() @@ -57,6 +88,13 @@ func TestGrantsForUser(t *testing.T) { } return }() + managedGroups := func() (ret []*oidc.ManagedGroup) { + ret = make([]*oidc.ManagedGroup, 0, managedGroupCount) + for i := 0; i < managedGroupCount; i++ { + ret = append(ret, oidc.TestManagedGroup(t, conn, authMethod, oidc.TestFakeManagedGroupFilter, oidc.WithName(fmt.Sprintf("testmanagedgroup%d", i)))) + } + return + }() roles := func() (ret []*iam.Role) { ret = make([]*iam.Role, 0, roleCount) scopeId := o.GetPublicId() @@ -76,8 +114,7 @@ func TestGrantsForUser(t *testing.T) { userToGroupsMapping := map[string]map[string]bool{} for _, user := range users { for _, group := range groups { - // Give each user about a chance of being in any specific - // group + // Give each user a chance of being in any specific group if mathrand.Int()%probFactor == 0 { userId := user.PublicId groupId := group.PublicId @@ -91,42 +128,82 @@ func TestGrantsForUser(t *testing.T) { } } } + // This variable stores an easy way to lookup, given a managed group ID, whether a + // user is in that group. + userToManagedGroupsMapping := map[string]map[string]bool{} + for i, user := range users { + for _, managedGroup := range managedGroups { + // Give each user (account) a chance of being in any specific managed group + if mathrand.Int()%probFactor == 0 { + userId := user.PublicId + accountId := accounts[i].PublicId + managedGroupId := managedGroup.PublicId + oidc.TestManagedGroupMember(t, conn, managedGroupId, accountId) + currentMapping := userToManagedGroupsMapping[userId] + if currentMapping == nil { + currentMapping = make(map[string]bool) + } + currentMapping[managedGroupId] = true + userToManagedGroupsMapping[userId] = currentMapping + } + } + } // Now, we're going to randomly assign users and groups to roles and also // store mappings userToRolesMapping := map[string]map[string]bool{} groupToRolesMapping := map[string]map[string]bool{} + managedGroupToRolesMapping := map[string]map[string]bool{} + if addUsersDirectly { + for _, role := range roles { + for _, user := range users { + // Give each user a chance of being directly added to any specific + // role + if mathrand.Int()%probFactor == 0 { + roleId := role.PublicId + userId := user.PublicId + iam.TestUserRole(t, conn, roleId, userId) + currentMapping := userToRolesMapping[userId] + if currentMapping == nil { + currentMapping = make(map[string]bool) + } + currentMapping[roleId] = true + userToRolesMapping[userId] = currentMapping + } + } + } + } for _, role := range roles { - for _, user := range users { - // Give each user about a chance of being directly added to - // any specific role + for _, group := range groups { + // Give each group a chance of being directly added to any specific + // role if mathrand.Int()%probFactor == 0 { roleId := role.PublicId - userId := user.PublicId - iam.TestUserRole(t, conn, roleId, userId) - currentMapping := userToRolesMapping[userId] + groupId := group.PublicId + iam.TestGroupRole(t, conn, roleId, groupId) + currentMapping := groupToRolesMapping[groupId] if currentMapping == nil { currentMapping = make(map[string]bool) } currentMapping[roleId] = true - userToRolesMapping[userId] = currentMapping + groupToRolesMapping[groupId] = currentMapping } } } for _, role := range roles { - for _, group := range groups { - // Give each group about a chance of being directly added to - // any specific role + for _, managedGroup := range managedGroups { + // Give each managed group a chance of being directly added to any + // specific role if mathrand.Int()%probFactor == 0 { roleId := role.PublicId - groupId := group.PublicId - iam.TestGroupRole(t, conn, roleId, groupId) - currentMapping := groupToRolesMapping[groupId] + managedGroupId := managedGroup.PublicId + iam.TestManagedGroupRole(t, conn, roleId, managedGroupId) + currentMapping := managedGroupToRolesMapping[managedGroupId] if currentMapping == nil { currentMapping = make(map[string]bool) } currentMapping[roleId] = true - groupToRolesMapping[groupId] = currentMapping + managedGroupToRolesMapping[managedGroupId] = currentMapping } } } @@ -134,6 +211,8 @@ func TestGrantsForUser(t *testing.T) { // Now, fetch the set of grants. We're going to be testing this by looking // at the role IDs of the matching grant tuples. for _, user := range users { + var rolesFromUsers, rolesFromGroups, rolesFromManagedGroups int + tuples, err := iamRepo.GrantsForUser(ctx, user.PublicId) require.NoError(t, err) @@ -150,14 +229,24 @@ func TestGrantsForUser(t *testing.T) { expectedRoleIds := make(map[string]bool, len(tuples)) for roleId := range userToRolesMapping[user.PublicId] { expectedRoleIds[roleId] = true + rolesFromUsers++ } for groupId := range userToGroupsMapping[user.PublicId] { for roleId := range groupToRolesMapping[groupId] { expectedRoleIds[roleId] = true + rolesFromGroups++ + } + } + for managedGroupId := range userToManagedGroupsMapping[user.PublicId] { + for roleId := range managedGroupToRolesMapping[managedGroupId] { + expectedRoleIds[roleId] = true + rolesFromManagedGroups++ } } // Now verify that the expected set and returned set match assert.EqualValues(t, expectedRoleIds, roleIds) + + t.Log("finished user", user.PublicId, "total roles", len(expectedRoleIds), "roles from users", rolesFromUsers, "roles from groups", rolesFromGroups, "roles from managed groups", rolesFromManagedGroups) } }