Repository navigation
fix(controlplane): restrict owner-role invitations to org owners (CP-1) - #3263
Conversation
|
Re: the behavior-change note on instance-admin / owner distinction — in practice Admin and Owner currently behave the same in Chainloop, so this distinction has historically been moot. However, we have plans to enable explicit Owner-only features downstream (e.g. billing, org deletion, SSO management), so we're tightening the boundary now so the 🤖 Posted by Maximus bot (Claude Code) on behalf of @migmartri |
9988cd0 to
c08f461
Compare
AI Session Analysis
|
| Status | Attribution | File | Lines |
|---|---|---|---|
| modified | ai | app/controlplane/pkg/biz/orginvitation_integration_test.go |
+195 / -214 |
| modified | ai | app/controlplane/pkg/biz/membership.go |
+15 / -6 |
| modified | ai | app/controlplane/pkg/biz/orginvitation.go |
+1 / -17 |
| modified | ai | app/controlplane/pkg/biz/group.go |
+7 / -1 |
Policies (4, 1 failing)
| Status | Policy | Material | Messages |
|---|---|---|---|
| ✅ Passed | ai-config-ai-agents-allowed |
ai-coding-session-321ef4 |
- |
| ✅ Passed | ai-config-no-dangerous-commands |
ai-coding-session-321ef4 |
- |
ai-config-no-secrets |
ai-coding-session-321ef4 |
|
|
| ✅ Passed | ai-config-mcp-servers-allowed |
ai-coding-session-321ef4 |
- |
Powered by Chainloop and Chainloop Trace
f5650b9 to
e034f25
Compare
OrgInvitationUseCase.Create accepted any caller-supplied role verbatim, so an
Admin (who passes the Casbin middleware via IsAdmin() short-circuit) could
invite a new external email as MEMBERSHIP_ROLE_ORG_OWNER. AcceptPendingInvitations
then auto-creates an Owner membership, bypassing PolicyOrganizationManageOwners
(the Owner-only manage-owners gate) and bootstrapping a fresh Owner via the
invitation path.
Add an enforceManageOwners check in the biz layer: when the requested role is
RoleOwner, enforce PolicyOrganizationManageOwners against the caller's role.
This mirrors the existing guard in MembershipUseCase.UpdateRole/DeleteOther.
The service layer passes the caller's authz subject down via a new callerRole
parameter; the two existing biz callers (group/project invitation flows) pass
the requester's membership role. Non-owner callers now receive
ErrUnauthorized("only organization owners can invite owners").
Assisted-by: Claude Code
Signed-off-by: Miguel Martinez Trivino <miguel@chainloop.dev>
- share a single enforceManageOwners helper between MembershipUseCase and OrgInvitationUseCase, with the denial messages defined once - guard against a nil requester membership in GroupUseCase.handleNonExistingUser - rework TestCreateOwnerGuard as a table-driven test, drop unused setup and pin the intentional instance-admin denial - replace s.T().Run subtests with suite-style s.Run in the invitation tests Assisted-by: Claude Code Signed-off-by: Miguel Martinez Trivino <miguel@chainloop.dev> Chainloop-Trace-Sessions: 321ef432-c960-4e92-bc01-9e70c8dba97d
e034f25 to
b116b18
Compare
Assisted-by: Claude Code Signed-off-by: Miguel Martinez Trivino <miguel@chainloop.dev>
b116b18 to
c56d555
Compare
chainloop-cli 1.104.0 Created-by: HarmonybrewBot Commit-by: HarmonybrewBot Merged-by: HarmonybrewBot Description: Created by `brew bump` --- Created with `brew bump-formula-pr`.<details> <summary>release notes</summary> <pre>## What's Changed * Bump Helm Chart and Dagger Version => v1.103.1 by @github-actions[bot] in chainloop-dev/chainloop#3259 * fix(ci): align secrets-scan material name with contract by @migmartri in chainloop-dev/chainloop#3262 * fix: harden GitHub Actions workflows against zizmor findings by @migmartri in chainloop-dev/chainloop#3260 * feat: add opencode chainloop-trace plugin by @migmartri in chainloop-dev/chainloop#3267 * ci: bump docker/login-action to v4.4.0 by @matiasinsaurralde in chainloop-dev/chainloop#3261 * fix(controlplane): restrict owner-role invitations to org owners (CP-1) by @migmartri in chainloop-dev/chainloop#3263 * feat: introduce PR mode for attestations by @migmartri in chainloop-dev/chainloop#3268 * docs: fix broken GUAC integration link in README by @tknura in chainloop-dev/chainloop#3272 * fix(cli): correct registered integration add usage string to --opt by @tknura in chainloop-dev/chainloop#3271 * fix: upgrade extras/dagger deps to resolve 4 security advisories by @migmartri in chainloop-dev/chainloop#3277 * fix(crafter): surface auto-discovery validation errors by @waveywaves in chainloop-dev/chainloop#2983 * docs: regenerate CLI reference for registered integration add usage by @matiasinsaurralde in chainloop-dev/chainloop#3279 * fix: upgrade Go toolchain to 1.26.5 for GO-2026-4970-stdlib by @chainloop-platform[bot] in chainloop-dev/chainloop#3278 * feat(cli): explode archive materials in chainloop att add by @javirln in chainloop-dev/chainloop#3254 * fix(materials): do not record container image digest as tag by @tknura in chainloop-dev/chainloop#3281 * fix(cli): accept matching --name and metadata.name in contract commands by @tknura in chainloop-dev/chainloop#3282 * fix(dispatcher): improve fanout delivery timeouts and retry budget by @migmartri in chainloop-dev/chainloop#3270 * refactor: remove the public attribute from workflows by @matiasinsaurralde in chainloop-dev/chainloop#3283 * fix(cli): preserve verbatim contract bytes when splitting apply documents by @javirln in chainloop-dev/chainloop#3233 **Full Changelog**: https://github.com/chainloop-dev/chainloop/compare/v1.103.1...v1.104.0</pre> <p>View the full release notes at <a href="https://github.com/chainloop-dev/chainloop/releases/tag/v1.104.0">https://github.com/chainloop-dev/chainloop/releases/tag/v1.104.0</a>.</p> </details> <hr> See merge request: Harmonybrew/homebrew-core!14158
Fixes #3264
What
OrgInvitationUseCase.Createaccepted any caller-supplied role verbatim. The Casbin authz middleware gatesOrgInvitationService/CreatewithPolicyOrganizationInvitationsCreate(admin/owner only), butRoleAdmin.IsAdmin()short-circuits the middleware — so an Admin could invite a new external email asMEMBERSHIP_ROLE_ORG_OWNER.AcceptPendingInvitationsthen auto-creates an Owner membership, bypassingPolicyOrganizationManageOwners(the Owner-only manage-owners gate) and bootstrapping a fresh Owner via the invitation path.Change
RoleOwner, the biz layer now enforcesPolicyOrganizationManageOwnersagainst the caller's authz subject. The check is shared with the existing owner-scoped guards inMembershipUseCase.UpdateRole/DeleteOtherthrough a singleenforceManageOwnershelper, with the denial messages defined once. Non-owner callers receiveErrUnauthorized("only organization owners can invite owners").api-token:<id>for API tokens) down via a newcallerRoleparameter; the group/project invitation flows pass the requester's membership role.PolicyOrganizationInvitationsCreatebut deliberately notPolicyOrganizationManageOwners, so they can no longer seed owners into organizations they administer.GroupUseCase.handleNonExistingUsernow guards against a nil requester membership instead of dereferencing it.Parts of this change were produced with AI assistance (Claude Code); see the commit trailers.
🤖 Posted by Maximus bot (Claude Code) on behalf of @migmartri