diff --git a/internal/iam/repository_principal_role.go b/internal/iam/repository_principal_role.go index 6e84dcc9a1..db37d4bf73 100644 --- a/internal/iam/repository_principal_role.go +++ b/internal/iam/repository_principal_role.go @@ -110,14 +110,12 @@ func (r *Repository) AddPrincipalRoles(ctx context.Context, roleId string, roleV return principalRoles, nil } -// SetPrincipalRoles will set the role's principals +// SetPrincipalRoles will set the role's principals. If both userIds and +// groupIds are empty, the principal roles will be cleared. func (r *Repository) SetPrincipalRoles(ctx context.Context, roleId string, roleVersion int, userIds, groupIds []string, opt ...Option) ([]PrincipalRole, int, error) { if roleId == "" { return nil, db.NoRowsAffected, fmt.Errorf("set principal roles: missing role id: %w", db.ErrInvalidParameter) } - if len(userIds) == 0 && len(groupIds) == 0 { - return nil, db.NoRowsAffected, fmt.Errorf("set principal roles: missing either user or groups to delete %w", db.ErrInvalidParameter) - } role := allocRole() role.PublicId = roleId scope, err := role.GetScope(ctx, r.reader) @@ -159,7 +157,7 @@ func (r *Repository) SetPrincipalRoles(ctx context.Context, roleId string, roleV // we need a roleTicket, which won't be redeemed until all the other // writes are successful. We can't just use a single ticket because // we need to write oplog entries for deletes and adds - roleTicket, err := w.GetTicket(role) + roleTicket, err := w.GetTicket(&role) if err != nil { return fmt.Errorf("set principal roles: unable to get ticket for role: %w", err) } @@ -231,6 +229,7 @@ func (r *Repository) SetPrincipalRoles(ctx context.Context, roleId string, roleV if err := w.CreateItems(ctx, toSet.addUserRoles, db.NewOplogMsgs(&userOplogMsgs)); err != nil { return fmt.Errorf("set principal roles: unable to add users: %w", err) } + totalRowsAffected += len(toSet.addUserRoles) msgs = append(msgs, userOplogMsgs...) } if len(toSet.addGroupRoles) > 0 { @@ -238,6 +237,7 @@ func (r *Repository) SetPrincipalRoles(ctx context.Context, roleId string, roleV if err := w.CreateItems(ctx, toSet.addGroupRoles, db.NewOplogMsgs(&grpOplogMsgs)); err != nil { return fmt.Errorf("set principal roles: unable to add groups: %w", err) } + totalRowsAffected += len(toSet.addGroupRoles) msgs = append(msgs, grpOplogMsgs...) } metadata := oplog.Metadata{ @@ -275,85 +275,6 @@ func (r *Repository) SetPrincipalRoles(ctx context.Context, roleId string, roleV return currentPrincipals, totalRowsAffected, nil } -type principalSet struct { - addUserRoles []interface{} - addGroupRoles []interface{} - deleteUserRoles []interface{} - deleteGroupRoles []interface{} -} - -func (r *Repository) principalsToSet(ctx context.Context, role *Role, userIds, groupIds []string) (*principalSet, error) { - if role == nil { - return nil, fmt.Errorf("missing role %w", db.ErrNilParameter) - } - existing, err := r.ListPrincipalRoles(ctx, role.PublicId) - if err != nil { - return nil, fmt.Errorf("unable to list existing principal role %s: %w", role.PublicId, err) - } - existingUsers := map[string]PrincipalRole{} - existingGroups := map[string]PrincipalRole{} - for _, p := range existing { - switch p.GetType() { - case UserRoleType.String(): - existingUsers[p.GetPrincipalId()] = p - case GroupRoleType.String(): - existingGroups[p.GetPrincipalId()] = p - default: - return nil, fmt.Errorf("%s is unknown principal type %s", p.GetPrincipalId(), p.GetType()) - } - } - var newUserRoles []interface{} - userIdsMap := map[string]struct{}{} - for _, id := range userIds { - userIdsMap[id] = struct{}{} - if _, ok := existingUsers[id]; !ok { - usrRole, err := NewUserRole(role.ScopeId, role.PublicId, id) - if err != nil { - return nil, fmt.Errorf("unable to create in memory user role for add: %w", err) - } - newUserRoles = append(newUserRoles, usrRole) - } - } - var newGrpRoles []interface{} - groupIdsMap := map[string]struct{}{} - for _, id := range groupIds { - groupIdsMap[id] = struct{}{} - if _, ok := existingGroups[id]; !ok { - grpRole, err := NewGroupRole(role.ScopeId, role.PublicId, id) - if err != nil { - return nil, fmt.Errorf("unable to create in memory group role for add: %w", err) - } - newGrpRoles = append(newGrpRoles, grpRole) - } - } - var deleteUserRoles []interface{} - for _, p := range existingUsers { - if _, ok := userIdsMap[p.GetPrincipalId()]; !ok { - usrRole, err := NewUserRole(p.GetScopeId(), p.GetRoleId(), p.GetPrincipalId()) - if err != nil { - return nil, fmt.Errorf("unable to create in memory user role for delete: %w", err) - } - deleteUserRoles = append(deleteUserRoles, usrRole) - } - } - var deleteGrpRoles []interface{} - for _, p := range existingGroups { - if _, ok := groupIdsMap[p.GetPrincipalId()]; !ok { - grpRole, err := NewGroupRole(p.GetScopeId(), p.GetRoleId(), p.GetPrincipalId()) - if err != nil { - return nil, fmt.Errorf("unable to create in memory group role for delete: %w", err) - } - deleteGrpRoles = append(deleteGrpRoles, grpRole) - } - } - return &principalSet{ - addUserRoles: newUserRoles, - addGroupRoles: newGrpRoles, - deleteUserRoles: deleteUserRoles, - deleteGroupRoles: deleteGrpRoles, - }, nil -} - // DeletePrincipalRoles principals (userIds and/or groupIds) from a role // (roleId). The role's current db version must match the roleVersion or an // error will be returned. @@ -467,3 +388,82 @@ func (r *Repository) ListPrincipalRoles(ctx context.Context, roleId string, opt } return principals, nil } + +type principalSet struct { + addUserRoles []interface{} + addGroupRoles []interface{} + deleteUserRoles []interface{} + deleteGroupRoles []interface{} +} + +func (r *Repository) principalsToSet(ctx context.Context, role *Role, userIds, groupIds []string) (*principalSet, error) { + if role == nil { + return nil, fmt.Errorf("missing role %w", db.ErrNilParameter) + } + existing, err := r.ListPrincipalRoles(ctx, role.PublicId) + if err != nil { + return nil, fmt.Errorf("unable to list existing principal role %s: %w", role.PublicId, err) + } + existingUsers := map[string]PrincipalRole{} + existingGroups := map[string]PrincipalRole{} + for _, p := range existing { + switch p.GetType() { + case UserRoleType.String(): + existingUsers[p.GetPrincipalId()] = p + case GroupRoleType.String(): + existingGroups[p.GetPrincipalId()] = p + default: + return nil, fmt.Errorf("%s is unknown principal type %s", p.GetPrincipalId(), p.GetType()) + } + } + var newUserRoles []interface{} + userIdsMap := map[string]struct{}{} + for _, id := range userIds { + userIdsMap[id] = struct{}{} + if _, ok := existingUsers[id]; !ok { + usrRole, err := NewUserRole(role.ScopeId, role.PublicId, id) + if err != nil { + return nil, fmt.Errorf("unable to create in memory user role for add: %w", err) + } + newUserRoles = append(newUserRoles, usrRole) + } + } + var newGrpRoles []interface{} + groupIdsMap := map[string]struct{}{} + for _, id := range groupIds { + groupIdsMap[id] = struct{}{} + if _, ok := existingGroups[id]; !ok { + grpRole, err := NewGroupRole(role.ScopeId, role.PublicId, id) + if err != nil { + return nil, fmt.Errorf("unable to create in memory group role for add: %w", err) + } + newGrpRoles = append(newGrpRoles, grpRole) + } + } + var deleteUserRoles []interface{} + for _, p := range existingUsers { + if _, ok := userIdsMap[p.GetPrincipalId()]; !ok { + usrRole, err := NewUserRole(p.GetScopeId(), p.GetRoleId(), p.GetPrincipalId()) + if err != nil { + return nil, fmt.Errorf("unable to create in memory user role for delete: %w", err) + } + deleteUserRoles = append(deleteUserRoles, usrRole) + } + } + var deleteGrpRoles []interface{} + for _, p := range existingGroups { + if _, ok := groupIdsMap[p.GetPrincipalId()]; !ok { + grpRole, err := NewGroupRole(p.GetScopeId(), p.GetRoleId(), p.GetPrincipalId()) + if err != nil { + return nil, fmt.Errorf("unable to create in memory group role for delete: %w", err) + } + deleteGrpRoles = append(deleteGrpRoles, grpRole) + } + } + return &principalSet{ + addUserRoles: newUserRoles, + addGroupRoles: newGrpRoles, + deleteUserRoles: deleteUserRoles, + deleteGroupRoles: deleteGrpRoles, + }, nil +}