Handle membership deletions and role updates in iam policies

Signed-off-by: Émile Ré <emile@getprobo.com>
This commit is contained in:
Émile Ré
2026-01-16 16:09:04 +04:00
committed by Bryan Frimin
parent 00eb3cde5e
commit 3669df3f8a
6 changed files with 66 additions and 28 deletions

View File

@@ -112,12 +112,15 @@ func (a *Authorizer) authorize(ctx context.Context, conn pg.Conn, params Authori
}
// Only set principal.organization_id if they have a role in this org
var principalOrgID string
var scopedPrincipalAttrs map[string]string
if membership != nil && role != "" {
principalOrgID = membership.OrganizationID.String()
scopedPrincipalAttrs = map[string]string{
"organization_id": membership.OrganizationID.String(),
"role": membership.Role.String(),
}
}
principalAttrs, err := a.buildPrincipalAttributes(ctx, conn, params.Principal, principalOrgID)
principalAttrs, err := a.buildPrincipalAttributes(ctx, conn, params.Principal, scopedPrincipalAttrs)
if err != nil {
return fmt.Errorf("cannot build principal attributes: %w", err)
}
@@ -176,12 +179,12 @@ func (a *Authorizer) buildPrincipalAttributes(
ctx context.Context,
conn pg.Conn,
principalID gid.GID,
organizationID string,
defaultAttrs map[string]string,
) (map[string]string, error) {
attrs := map[string]string{
"id": principalID.String(),
"organization_id": organizationID,
"id": principalID.String(),
}
maps.Copy(attrs, defaultAttrs)
if entity, ok := coredata.NewEntityFromID(principalID); ok {
if attributer, ok := entity.(AuthorizationAttributer); ok {

View File

@@ -48,6 +48,9 @@ const (
ActionMembershipUpdate = "iam:membership:update"
ActionMembershipDelete = "iam:membership:delete"
// Membership role actions
ActionMembershipRoleSetOwner = "iam:membership-role:set-owner"
// Membership Profile actions
ActionMembershipProfileGet = "iam:membership-profile:get"

View File

@@ -123,9 +123,23 @@ var IAMOwnerPolicy = policy.NewPolicy(
WithSID("full-org-access").
When(policy.Equals("principal.organization_id", "resource.organization_id")),
// Full access to member management (scoped to own organization)
policy.Allow("iam:membership:*").
WithSID("full-membership-access").
// Full access to member management (scoped to own organization), except deletion on SCIM sourced memberships
policy.Allow(
ActionMembershipGet,
ActionMembershipList,
ActionMembershipUpdate,
).
WithSID("membership-owner-access").
When(policy.Equals("principal.organization_id", "resource.organization_id")),
policy.Allow(
ActionMembershipDelete,
).
WithSID("membership-deletion-owner-access").
When(policy.NotEquals("resource.source", "SCIM")),
// Can set other members OWNER
policy.Allow(ActionMembershipRoleSetOwner).
WithSID("membership-role-owner-access").
When(policy.Equals("principal.organization_id", "resource.organization_id")),
// Full access to membership profiles (scoped to own organization)
@@ -185,11 +199,19 @@ var IAMAdminPolicy = policy.NewPolicy(
// Can manage memberships (scoped to own organization)
policy.Allow(
ActionMembershipGet,
ActionMembershipUpdate,
).
WithSID("membership-admin-access").
When(policy.Equals("principal.organization_id", "resource.organization_id")),
policy.Allow(
ActionMembershipUpdate,
).
WithSID("membership-role-admin-access").
When(
policy.Equals("principal.organization_id", "resource.organization_id"),
policy.NotEquals("resource.role", "OWNER"),
),
// Can view membership profiles (scoped to own organization)
policy.Allow(ActionMembershipProfileGet).
WithSID("membership-profile-admin-access").