fix(controlplane): restrict owner-role invitations to org owners (CP-1)

Merged
MI
4mo ago
+367-165

Fixes #3264

What

OrgInvitationUseCase.Create accepted any caller-supplied role verbatim. The Casbin authz middleware gates OrgInvitationService/Create with PolicyOrganizationInvitationsCreate (admin/owner only), but RoleAdmin.IsAdmin() short-circuits the middleware — so an Admin 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.

Change

  • When the requested invitation role is RoleOwner, the biz layer now enforces PolicyOrganizationManageOwners against the caller's authz subject. The check is shared with the existing owner-scoped guards in MembershipUseCase.UpdateRole / DeleteOther through a single enforceManageOwners helper, with the denial messages defined once. Non-owner callers receive ErrUnauthorized("only organization owners can invite owners").
  • The service layer passes the caller's authz subject (org role for users, api-token:<id> for API tokens) down via a new callerRole parameter; the group/project invitation flows pass the requester's membership role.
  • This intentionally also applies to instance admins: they hold PolicyOrganizationInvitationsCreate but deliberately not PolicyOrganizationManageOwners, so they can no longer seed owners into organizations they administer.
  • GroupUseCase.handleNonExistingUser now 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