Skip to content

RemoveOrganizationMember leaves the member's custom-resource policies behind #1801

Description

@whoAbhishekSah

What happens

Removing a user from an organization with the RemoveOrganizationMember RPC deletes their org, project, and group policies and membership relations. But their policies on custom resources (for example resource/aoi) in the org's projects are not touched. The policy row and its SpiceDB rolebinding tuple stay live.

Why

The handler calls membership.RemoveOrganizationMember, and the membership cascade only covers org, project, and group scopes. Custom-resource policy cleanup lives in resource.RemovePrincipalAccess (#1800), which is only called from the deleter during user deletion (DeleteUser). The org-removal path never reaches it.

Consequence

  • An ex-member keeps a live policy on a resource inside an org they were removed from. If they rejoin, or if the permission check does not require org membership, the old access still applies.
  • If the user is later deleted with DeleteUser, the policy row is orphaned: the deleter cleans resource policies per org the user is still a member of, and the user is no longer in any. The user row and SpiceDB tuples are deleted, but the policy row stays in Postgres pointing at a deleted user.

Reproduce (verified on a local server built from #1800)

  1. Create an org, a project, and a custom resource (resource/aoi) in the project.
  2. Add a user to the org. Create a custom role with resource_aoi_* permissions and a policy for the user on the resource.
  3. Call RemoveOrganizationMember for the user.
  4. The user's org and project policies are gone. The resource policy row and its app/rolebinding tuple remain.
  5. Call DeleteUser for the same user. The user row and tuples are gone; the resource policy row remains, orphaned.

Possible fix

Have the org-removal path clean the member's resource policies too, for example by calling resource.RemovePrincipalAccess with the org's project IDs from membership.RemoveOrganizationMember (or from the handler). Separately, DeleteUser could delete any remaining policies by principal id as a final sweep so no orphan rows survive.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions