Skip to content

Conversation

javirln
Copy link
Member

@javirln javirln commented Jun 26, 2025

This patch introduces support for managing users within a group. Key changes include:

  • New API endpoints:
    • AddMembers to add users to a group
    • RemoveMembers to remove users from a group
  • Audit logging for membership changes
  • Extended test coverage to include the new endpoints
  • API-level refactor to use a generic IdentityReference for group-related requests
  • New CLI commands for group and membership management (currently hidden):
    • group
    • group describe
    • group list
    • group delete
    • group member add
    • group member list
    • group member delete
  • CLI commands handle pagination when needed and confirmation prompts for deletions

Permissions matrix:

  • Admins and Owners have full access to group and membership operations
  • Only Admins, Owners, and Group Maintainers can add or remove members
  • Viewers and Org Members can list groups and their members

Pending:

  • Support for updating a user’s role within a group.
  • CLI support for updating groups

CLI Examples:

Creating a group

$ chainloop group create --name "Operations" --description "Focused group dedicated to day to day operations"
┌──────────────────────────────────────┬────────────┬──────────────────────────────────────────────────┬──────────────────────┬──────────────────────┐
│ ID                                   │ NAME       │ DESCRIPTION                                      │ CREATED AT           │ UPDATED AT           │
├──────────────────────────────────────┼────────────┼──────────────────────────────────────────────────┼──────────────────────┼──────────────────────┤
│ 69314171-9705-473f-988c-3a57365aa7cd │ Operations │ Focused group dedicated to day to day operations │ 2025-06-26T15:44:18Z │ 2025-06-26T15:44:18Z │
└──────────────────────────────────────┴────────────┴──────────────────────────────────────────────────┴──────────────────────┴──────────────────────┘
INF Group created successfully

Adding members to a group

$ chainloop group member add --name "Operations" --email "john@chainloop.local"
INF Member john@chainloop.local successfully added to group Operations

Listing members of a group

$ chainloop group member list --name Operations                                
┌──────────────────────────────────────────────┬────────────┬─────────────────────┐
│ EMAIL                                        │ ROLE       │ ADDED AT            │
├──────────────────────────────────────────────┼────────────┼─────────────────────┤
│ john@chainloop.local                         │ Member     │ 26 Jun 25 15:45 UTC │
│ Sarah De los Angeles <sarah@chainloop.local> │ Maintainer │ 26 Jun 25 15:44 UTC │
└──────────────────────────────────────────────┴────────────┴─────────────────────┘
INF Showing [1-2] out of 2

Removing members from a group

$ chainloop group member delete --name Operations --email "john@chainloop.local"
WRN You are about to remove the user "john@chainloop.local" from the group "Operations"

To confirm, please type "1QKW-7MAU"
1QKW-7MAU 
INF Member john@chainloop.local successfully removed from group Operations

Removing groups

$ chainloop group delete --name Operations
WRN Are you sure you want to delete the group 'Operations'?
To confirm, please type "RA72-4LQC"
RA72-4LQC
INF Group 'Operations' has been removed

@javirln javirln self-assigned this Jun 26, 2025
@javirln javirln requested review from jiparis and migmartri June 26, 2025 16:00
@javirln javirln marked this pull request as ready for review June 26, 2025 16:00
@migmartri
Copy link
Member

chore(groups)

maybe more like feature? :)

@migmartri
Copy link
Member

Great description of the PR @javirln!

@javirln javirln changed the title chore(groups): Allow group membership management feat(groups): Allow group membership management Jun 27, 2025
@migmartri
Copy link
Member

@javirln let me know once you have fixed the conflicts

@javirln javirln force-pushed the feat/pfm-3208-members branch from c7a9e37 to b093750 Compare June 27, 2025 14:40
javirln added 3 commits June 30, 2025 09:28
Signed-off-by: Javier Rodriguez <javier@chainloop.dev>
Signed-off-by: Javier Rodriguez <javier@chainloop.dev>
Signed-off-by: Javier Rodriguez <javier@chainloop.dev>
@javirln javirln force-pushed the feat/pfm-3208-members branch from a21a6a2 to 5c8a978 Compare June 30, 2025 07:28
}

// Update the user membership with the role of maintainer
_, err = tx.Membership.Create().
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I'm not wrong, we agreed on not adding the OrgAdmin as maintainer. They can declare other maintainers, but they don't need to be maintainers as they are already admins (and maintainers are not mandatory)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in GitHub, the creator of the group becomes a maintainer automatically, then they can leave though

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, not a big deal at all. Let's keep it.


func newGroupDeleteCmd() *cobra.Command {
var groupName string
var force bool
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is force used?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it is from a previous implementation, removing it.

return cmd
}

func GroupListTableOutput(groupListResult *action.GroupListResult) error {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it might not need to be exposed

}

// Helper function to format time string
func formatTime(timeStr string) string {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this doesn't exist already?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't seen it, we parse the time individually, see the example of workflow list

p.CreatedAt.Format(time.RFC822),

}

// Update the user membership with the role of maintainer
_, err = tx.Membership.Create().
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in GitHub, the creator of the group becomes a maintainer automatically, then they can leave though

Hidden: true,
}

cmd.AddCommand(newGroupCreateCmd(), newGroupDescribeCmd(), newGroupListCmd(), newGroupDeleteCmd(), newGroupMembersCmd())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The GroupUpdate operation is implemented in backend but not exposed in the CLI. We can do it later.

return false, NewErrValidationStr("organization ID, group ID, and user ID cannot be empty")
}

membership, err := uc.groupRepo.FindGroupMembershipByGroupAndID(ctx, groupID, userID)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think here we should now use the enforcer.


// If a specific policy was provided, check if the user's role allows that policy
if policy != nil && s.enforcer != nil {
pass, err := s.enforcer.Enforce(userRole, policy)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here, userRole would be the Organization role, not the "maintainer" role. The idea would be to remove the above if isMaintainer check (or leave it as a double check), and look for the membership role of the user in the group. If found (it would be role:group:maintainer, we use it to enforce the permission. You can just use the current helpers:

       // Allow if user has admin or owner role
	if userRole == string(authz.RoleAdmin) || userRole == string(authz.RoleOwner) {
		return nil
	}
...
      return authorizeResource(ctx, policy, authz.ResourceTypeGroup, resolvedGroupID)

This way we rely entirely on roles to authorize the operation. It might look more complex, but it's way more flexible, since we can add later other roles, or remove some permissions from the current one, etc.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update: authorizeResource cannot be used since it only works if RBAC is enabled (Member role). You'll need to iterate through user resource memberships from the context.

@jiparis
Copy link
Member

jiparis commented Jun 30, 2025

@javirln we are almost there. I found that the Role is not used at all, since it's still using the maintainer flag for everything.

javirln added 3 commits June 30, 2025 12:08
Signed-off-by: Javier Rodriguez <javier@chainloop.dev>
Signed-off-by: Javier Rodriguez <javier@chainloop.dev>
Signed-off-by: Javier Rodriguez <javier@chainloop.dev>
Copy link
Member

@migmartri migmartri left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @javirln

@javirln javirln merged commit 30ddb14 into chainloop-dev:main Jun 30, 2025
13 checks passed
@jiparis
Copy link
Member

jiparis commented Jun 30, 2025

Thanks @javirln it works great!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants