diff --git a/app/controlplane/pkg/biz/group.go b/app/controlplane/pkg/biz/group.go index 3f506410b..760ba3c18 100644 --- a/app/controlplane/pkg/biz/group.go +++ b/app/controlplane/pkg/biz/group.go @@ -453,6 +453,11 @@ func (uc *GroupUseCase) AddMemberToGroup(ctx context.Context, orgID uuid.UUID, o return nil, fmt.Errorf("failed to find user by email: %w", err) } + // Illegal. Org viewers cannot become maintainers + if userMembership != nil && userMembership.Role == authz.RoleViewer && opts.Maintainer { + return nil, NewErrValidationStr("org viewers cannot become group maintainers") + } + // If the user is not found in the organization, send an invitation if userMembership == nil { // We need a requester for creating invitations @@ -764,6 +769,7 @@ func (uc *GroupUseCase) UpdateMemberMaintainerStatus(ctx context.Context, orgID // Find the user by reference or email var userUUID uuid.UUID var userEmail string + var userMembership *Membership // If UserReference is provided, use it to resolve the user ID if opts.UserReference != nil && (opts.UserReference.ID != nil || opts.UserReference.Name != nil) { @@ -771,17 +777,17 @@ func (uc *GroupUseCase) UpdateMemberMaintainerStatus(ctx context.Context, orgID if opts.UserReference.ID != nil { userUUID = *opts.UserReference.ID // Look up the user to verify they exist and get their email - user, err := uc.membershipRepo.FindByOrgAndUser(ctx, orgID, userUUID) + userMembership, err = uc.membershipRepo.FindByOrgAndUser(ctx, orgID, userUUID) if err != nil { return fmt.Errorf("failed to find user by ID: %w", err) } - if user == nil { + if userMembership == nil { return NewErrNotFound("user") } - userEmail = user.User.Email + userEmail = userMembership.User.Email } else if opts.UserReference.Name != nil { // If name (email) is provided, look up the user - userMembership, err := uc.membershipRepo.FindByOrgIDAndUserEmail(ctx, orgID, *opts.UserReference.Name) + userMembership, err = uc.membershipRepo.FindByOrgIDAndUserEmail(ctx, orgID, *opts.UserReference.Name) if err != nil && !IsNotFound(err) { return fmt.Errorf("failed to find user by email: %w", err) } @@ -793,7 +799,7 @@ func (uc *GroupUseCase) UpdateMemberMaintainerStatus(ctx context.Context, orgID } } else { // Fall back to using UserEmail - userMembership, err := uc.membershipRepo.FindByOrgIDAndUserEmail(ctx, orgID, *opts.UserReference.Name) + userMembership, err = uc.membershipRepo.FindByOrgIDAndUserEmail(ctx, orgID, *opts.UserReference.Name) if err != nil && !IsNotFound(err) { return fmt.Errorf("failed to find user by email: %w", err) } @@ -804,6 +810,11 @@ func (uc *GroupUseCase) UpdateMemberMaintainerStatus(ctx context.Context, orgID userEmail = *opts.UserReference.Name } + // illegal combination: org viewers cannot become maintainers + if userMembership != nil && userMembership.Role == authz.RoleViewer && opts.IsMaintainer { + return NewErrValidationStr("org viewers cannot become group maintainers") + } + // Check if the user is a member of the group existingMembership, err := uc.groupRepo.FindGroupMembershipByGroupAndID(ctx, resolvedGroupID, userUUID) if err != nil && !IsNotFound(err) { diff --git a/app/controlplane/pkg/biz/group_integration_test.go b/app/controlplane/pkg/biz/group_integration_test.go index b1c5cd901..bd68422c5 100644 --- a/app/controlplane/pkg/biz/group_integration_test.go +++ b/app/controlplane/pkg/biz/group_integration_test.go @@ -912,7 +912,6 @@ func (s *groupMembersIntegrationTestSuite) TestAddMemberToGroup() { }, UserEmail: "add-user2@example.com", RequesterID: uuid.MustParse(s.user.ID), - Maintainer: true, } _, err := s.Group.AddMemberToGroup(ctx, uuid.MustParse(s.org.ID), opts) @@ -928,6 +927,22 @@ func (s *groupMembersIntegrationTestSuite) TestAddMemberToGroup() { s.NoError(err) s.Equal(3, count) // still the original 3 members }) + + s.Run("a viewer cannot be maintainer", func() { + // Try to add user2 again (who we added in the first test) + opts := &biz.AddMemberToGroupOpts{ + IdentityReference: &biz.IdentityReference{ + ID: &s.group.ID, + }, + UserEmail: "add-user2@example.com", + RequesterID: uuid.MustParse(s.user.ID), + Maintainer: true, + } + + _, err := s.Group.AddMemberToGroup(ctx, uuid.MustParse(s.org.ID), opts) + s.Error(err) + s.True(biz.IsErrValidation(err)) + }) } // Test removing members from groups @@ -1516,8 +1531,7 @@ func (s *groupMembersIntegrationTestSuite) TestUpdateMemberMaintainerStatus() { UserReference: &biz.IdentityReference{ Name: &nonMemberEmail, }, - RequesterID: uuid.MustParse(s.user.ID), - IsMaintainer: true, + RequesterID: uuid.MustParse(s.user.ID), } err = s.Group.UpdateMemberMaintainerStatus(ctx, uuid.MustParse(s.org.ID), updateOpts)