Add group membership provisioning (Grant/Revoke) - #33
Conversation
Add Grant() and Revoke() methods to groupBuilder to support provisioning users into and out of Atlassian groups via access requests. Changes: - Add AddUserToGroup and RemoveUserFromGroup client methods using the Atlassian Admin v2 group membership API endpoints - Add Grant() method to groupBuilder for adding users to groups - Add Revoke() method to groupBuilder for removing users from groups - Register CAPABILITY_PROVISION for the group resource type in baton_capabilities.json Fixes: CXH-1928
| err := b.client.AddUserToGroup(ctx, groupID, userID) | ||
| if err != nil { | ||
| return nil, fmt.Errorf("baton-atlassian: failed to add user to group: %w", err) | ||
| } | ||
|
|
||
| return nil, nil |
There was a problem hiding this comment.
🟡 Suggestion: Grant is not idempotent. If the user is already a member, Atlassian's membership POST typically returns 409, which surfaces here as an error and fails the access request. Consider detecting the already-exists case and returning annotations.New(&v2.GrantAlreadyExists{}) with a nil error (see CLAUDE.md "Grant Idempotency"). (confidence: medium)
| err := b.client.RemoveUserFromGroup(ctx, groupID, userID) | ||
| if err != nil { | ||
| return nil, fmt.Errorf("baton-atlassian: failed to remove user from group: %w", err) | ||
| } | ||
|
|
||
| return nil, nil |
There was a problem hiding this comment.
🟡 Suggestion: Revoke is not idempotent. If the membership no longer exists, the DELETE likely returns 404 and this returns an error rather than treating it as already-revoked. Consider detecting not-found and returning annotations.New(&v2.GrantAlreadyRevoked{}) with a nil error. (confidence: medium)
| func (c *AtlassianClient) RemoveUserFromGroup(ctx context.Context, groupID, accountID string) error { | ||
| requestURL, err := url.JoinPath(c.getBaseURL(), fmt.Sprintf(groupMembershipEP, c.config.organizationID, groupID, accountID)) | ||
| if err != nil { | ||
| return err | ||
| } | ||
|
|
||
| _, err = c.doRequest(ctx, http.MethodDelete, requestURL, nil, nil) | ||
| if err != nil { | ||
| return err | ||
| } | ||
|
|
||
| return nil | ||
| } |
There was a problem hiding this comment.
🟡 Suggestion: This deletes the membership by treating accountID as the final path segment of .../memberships/{id}. Confirm the Atlassian Admin v2 membership API keys the DELETE on the user's accountId rather than a distinct membership ID — if it expects a membership ID, revokes will 404. The PR notes manual testing is still pending, so please verify this against a live tenant. (confidence: low)
Connector PR Review: Add group membership provisioning (Grant/Revoke)Blocking Issues: 0 | Suggestions: 4 | Threads Resolved: 0 Review SummaryScanned the full PR diff for security and correctness. The change adds Grant/Revoke to groupBuilder, two client methods (AddUserToGroup/RemoveUserFromGroup), and registers CAPABILITY_PROVISION for groups. Entity sources are correct (WHO = principal.Id.Resource, WHAT = entitlement.Resource.Id.Resource; Revoke uses the grant principal/entitlement), and the membership accountId matches the user IDs emitted during sync. No blocking issues found; the suggestions below concern idempotency, an unverified API path key, and stale docs. Security IssuesNone found. Correctness IssuesNone found. Suggestions
Prompt for AI agents |
Summary
Grant()andRevoke()methods togroupBuilderto support provisioning users into and out of Atlassian groups via C1 access requestsAddUserToGroupandRemoveUserFromGroupclient methods using the Atlassian Admin v2 group membership API endpoints (/v2/orgs/{orgId}/directories/-/groups/{groupId}/memberships)CAPABILITY_PROVISIONfor the group resource type inbaton_capabilities.jsonPreviously, attempting to provision a user into a group entitlement returned
resource type group does not have provisioner configured. This change enables group membership management through C1's provisioning system.Implementation Details
The implementation follows the existing workspace role provisioning pattern in
workspaces.go:Grant()validates the principal is a user, extracts group and user IDs, and calls the Atlassian API to add the user to the groupRevoke()extracts IDs from the grant and calls the API to remove the user from the group-directory wildcard, consistent with existing group/user sync endpointsTest Plan
go build ./...)go vetpassesFixes: CXH-1928
Automated PR Notice
This PR was automatically created by c1-dev-bot as a potential implementation.
This code requires: