Skip to content

feat: add RemoveOrganizationMember RPC and remove RemoveOrganizationUser#1550

Open
rohilsurana wants to merge 6 commits intomainfrom
feat/remove-organization-member
Open

feat: add RemoveOrganizationMember RPC and remove RemoveOrganizationUser#1550
rohilsurana wants to merge 6 commits intomainfrom
feat/remove-organization-member

Conversation

@rohilsurana
Copy link
Copy Markdown
Member

@rohilsurana rohilsurana commented Apr 17, 2026

Summary

  • Add RemoveOrganizationMember RPC to FrontierService, replacing the old RemoveOrganizationUser
  • New method supports all principal types (not just users) and performs a full cascade removal: org policies, project policies, group policies, and SpiceDB relations at org and group levels
  • Moves removal logic from deleter service into the membership package, consistent with the AddOrganizationMember and SetOrganizationMemberRole patterns

Changes

  • Proto: Update PROTON_COMMIT to a855a70 (adds RemoveOrganizationMember, removes RemoveOrganizationUser)
  • core/membership/service.go: Add RemoveOrganizationMember, removeRelations helper, principalTypeToAuditType validator, auditOrgMemberRemoved, and ProjectService/GroupService interfaces
  • core/membership/errors.go: Add ErrInvalidPrincipalType for unknown principal types
  • Handler: Replace RemoveOrganizationUser handler with RemoveOrganizationMember, delegating entirely to membership service. Only reachable error branches retained.
  • Authorization: Update interceptor for the new RPC name and request fields. Keeps UpdatePermission for backward compatibility with existing org admin/manager roles.
  • Wiring: Pass projectService and groupService to membership service constructor
  • Tests: Rewrite handler tests, update service test constructors, update e2e test

Cascade behavior

When a member is removed from an org:

  1. Validate principal type via principalTypeToAuditType (else ErrInvalidPrincipalType)
  2. Validate membership exists (else ErrNotMember)
  3. Validate last-owner constraint (else ErrLastOwnerRole)
  4. List all policies for the principal, delete those belonging to org's projects, groups, and the org itself
  5. Remove SpiceDB relations (owner + member) at org level and for each org group
  6. Audit log the removal with the correct entity type for the principal

Proto PR: raystack/proton#475

@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 17, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
frontier Ready Ready Preview, Comment Apr 21, 2026 1:37pm

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 17, 2026

Warning

Rate limit exceeded

@rohilsurana has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 40 minutes and 3 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 40 minutes and 3 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 6486ff64-ef87-44d5-adfc-a26835c2750b

📥 Commits

Reviewing files that changed from the base of the PR and between 2b9c521 and c2af19d.

📒 Files selected for processing (4)
  • core/membership/service.go
  • core/membership/service_test.go
  • internal/api/v1beta1connect/organization.go
  • internal/api/v1beta1connect/organization_test.go
📝 Walkthrough

Walkthrough

Adds membership removal at the membership-service layer (RemoveOrganizationMember), new ProjectService/GroupService deps and mocks, updates API handler/signature/tests/authorization, reorders service wiring in startup, adds an invalid-principal error, and bumps the Makefile PROTON_COMMIT.

Changes

Cohort / File(s) Summary
Build config
Makefile
Bumped PROTON_COMMIT hash used by make proto.
Membership core
core/membership/service.go, core/membership/errors.go
Added ProjectService and GroupService interfaces, ErrInvalidPrincipalType, and implemented Service.RemoveOrganizationMember (policy cascade deletion, relation cleanup, audit logging, helper functions).
Membership mocks
core/membership/mocks/project_service.go, core/membership/mocks/group_service.go
Added autogenerated testify mocks for ProjectService and GroupService with List methods, expecters, and constructors.
Membership tests
core/membership/service_test.go
Updated service constructor wiring to include project/group mocks and added comprehensive tests for RemoveOrganizationMember.
Dependency wiring
cmd/serve.go
Reordered initialization to create projectRepository/projectService earlier and pass projectService/groupService into membership service construction.
Connect interface & mocks
internal/api/v1beta1connect/interfaces.go, internal/api/v1beta1connect/mocks/membership_service.go
Added RemoveOrganizationMember to MembershipService interface and corresponding mock method/call helpers.
API handler
internal/api/v1beta1connect/organization.go
Renamed handler from RemoveOrganizationUser→RemoveOrganizationMember, switched implementation to call membershipService.RemoveOrganizationMember, adjusted request/response types and error-code mappings.
Handler tests & auth
internal/api/v1beta1connect/organization_test.go, pkg/server/connect_interceptors/authorization.go
Replaced handler tests to target RemoveOrganizationMember; updated authorization mapping and org ID extraction to use OrgId in the new request.
E2E test
test/e2e/regression/api_test.go
Updated scenario to call Client.RemoveOrganizationMember with OrgId, PrincipalId, and PrincipalType.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Suggested reviewers

  • whoAbhishekSah
  • AmanGIT07
  • rsbh
🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coveralls
Copy link
Copy Markdown

coveralls commented Apr 17, 2026

Coverage Report for CI Build 24725159879

Coverage increased (+0.1%) to 42.205%

Details

  • Coverage increased (+0.1%) from the base build.
  • Patch coverage: 25 uncovered changes across 4 files (123 of 148 lines covered, 83.11%).
  • No coverage regressions found.

Uncovered Changes

File Changed Covered %
core/membership/service.go 124 108 87.1%
cmd/serve.go 4 0 0.0%
pkg/server/connect_interceptors/authorization.go 3 0 0.0%
internal/api/v1beta1connect/organization.go 17 15 88.24%

Coverage Regressions

No coverage regressions found.


Coverage Stats

Coverage Status
Relevant Lines: 36887
Covered Lines: 15568
Line Coverage: 42.2%
Coverage Strength: 11.93 hits per line

💛 - Coveralls

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (4)
test/e2e/regression/api_test.go (1)

290-294: Strengthen this regression to assert cascade cleanup.

This swap only proves the principal disappears from ListUsers. The new RPC’s risky behavior is the cascade through project/group policies and relations, and that can regress while this test still passes. Add at least one post-removal access check against a project, group, or resource created under the org.

internal/api/v1beta1connect/organization_test.go (1)

1009-1111: Consider adding a non-user principal test case.

The PR objective highlights that RemoveOrganizationMember "supports all principal types (not limited to users)", but every test case hardcodes schema.UserPrincipal. A table entry exercising schema.ServiceUserPrincipal would guard against regressions in principal-type plumbing from the handler through the service.

core/membership/service.go (2)

225-242: Consider validating principalType at the entry.

Unlike AddOrganizationMember / SetOrganizationMemberRole, there's no validatePrincipal (or even a simple allowlist check) for principalType. An empty or unknown value silently falls through to policyService.List, almost always returns zero rows, and surfaces as ErrNotMember — indistinguishable from a legitimate "not a member" case. Rejecting unknown principal types up front (via ErrInvalidPrincipal or similar) would give callers clearer errors and prevent misleading audit entries for malformed inputs.


262-268: Broad policyService.List query for cross-org users.

policy.Filter{PrincipalID, PrincipalType} with no OrgID returns the principal's policies across every org they belong to; the subsequent switch then filters down to this org's resources. For users who are members of many orgs, this fetches significantly more rows than necessary.

If policy.Filter supports it, scoping project/group lookups to the pre-computed orgProjectIDs/orgGroupIDs (or filtering per-resource-type with OrgID) would be tighter. Not a correctness issue, but worth considering for hot paths.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 7d8d6e4c-3662-440a-b319-6bc66d03f48d

📥 Commits

Reviewing files that changed from the base of the PR and between a8a19f3 and 4606aec.

⛔ Files ignored due to path filters (2)
  • proto/v1beta1/frontier.pb.go is excluded by !**/*.pb.go, !proto/**
  • proto/v1beta1/frontierv1beta1connect/frontier.connect.go is excluded by !proto/**
📒 Files selected for processing (12)
  • Makefile
  • cmd/serve.go
  • core/membership/mocks/group_service.go
  • core/membership/mocks/project_service.go
  • core/membership/service.go
  • core/membership/service_test.go
  • internal/api/v1beta1connect/interfaces.go
  • internal/api/v1beta1connect/mocks/membership_service.go
  • internal/api/v1beta1connect/organization.go
  • internal/api/v1beta1connect/organization_test.go
  • pkg/server/connect_interceptors/authorization.go
  • test/e2e/regression/api_test.go

Comment thread core/membership/service.go Outdated
Comment thread internal/api/v1beta1connect/organization.go Outdated
Comment thread pkg/server/connect_interceptors/authorization.go
@rohilsurana
Copy link
Copy Markdown
Member Author

rohilsurana commented Apr 17, 2026

Test Results (9/9 passed)

# Scenario Expected Result
1 Remove with principalType=app/serviceuser Rejected failed_precondition
2 Remove last owner of org Rejected (ErrLastOwnerRole) failed_precondition
3 Remove non-member Rejected (ErrNotMember) failed_precondition
4 Bob has org + project + group access before removal All true All true
5 Remove bob (owner + project owner + group owner) Success {}
6 Bob has zero access after removal All false All false
7 Bob not in ListOrganizationUsers Absent Absent
8 Re-remove already-removed bob Rejected (ErrNotMember) failed_precondition
9 Remove charlie (viewer, no sub-resource roles) Success + no access Success + no access

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR replaces the legacy RemoveOrganizationUser RPC with a more general RemoveOrganizationMember RPC, and moves cascade-removal logic into the membership service to support additional principal types and consistent cleanup across org resources.

Changes:

  • Introduces RemoveOrganizationMember RPC (and removes RemoveOrganizationUser) across proto/connect stubs, handlers, interceptors, and e2e coverage.
  • Adds membership-service cascade removal: deletes org/project/group policies and removes org/group relations; adds corresponding audit record.
  • Updates wiring and tests to use the new RPC and new membership service constructor dependencies.

Reviewed changes

Copilot reviewed 14 out of 15 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
test/e2e/regression/api_test.go Updates regression coverage to call RemoveOrganizationMember with principal type.
proto/v1beta1/frontierv1beta1connect/frontier.connect.go Regenerated connect client/handler stubs for the new RPC name and request/response types.
pkg/server/connect_interceptors/authorization.go Updates authorization validation mapping for the new RPC (currently has a merge conflict).
internal/api/v1beta1connect/organization.go Replaces handler implementation to delegate removal to membership service and map new errors.
internal/api/v1beta1connect/organization_test.go Rewrites handler tests to mock membership service instead of org/user/deleter services.
internal/api/v1beta1connect/mocks/membership_service.go Adds mock for RemoveOrganizationMember.
internal/api/v1beta1connect/interfaces.go Extends MembershipService interface with RemoveOrganizationMember.
core/membership/service.go Adds cascade member removal, relation cleanup helper, and audit record helper; adds project/group service deps.
core/membership/service_test.go Updates constructors to pass new project/group service mocks.
core/membership/mocks/project_service.go Adds generated mock for ProjectService used by membership service.
core/membership/mocks/group_service.go Adds generated mock for GroupService used by membership service.
core/membership/errors.go Adds ErrInvalidPrincipalType for new RPC validation.
cmd/serve.go Wires project/group services into membership service constructor.
Makefile Updates PROTON_COMMIT (commit differs from PR description).
Comments suppressed due to low confidence (1)

pkg/server/connect_interceptors/authorization.go:368

  • File contains unresolved git merge-conflict markers (<<<<<<<, =======, >>>>>>>) inside authorizationValidationMap, which will not compile and may also drop/duplicate RPC validation entries (e.g., AddOrganizationUsers vs RemoveOrganizationMember). Resolve the conflict by removing the markers and keeping the correct authorization handlers (remove the old RemoveOrganizationUser entry, add/keep RemoveOrganizationMember, and ensure AddOrganizationUsers validation still exists).
	"/raystack.frontier.v1beta1.FrontierService/RemoveOrganizationMember": func(ctx context.Context, handler *v1beta1connect.ConnectHandler, req connect.AnyRequest) error {
		pbreq := req.(*connect.Request[frontierv1beta1.RemoveOrganizationMemberRequest])
		return handler.IsAuthorized(ctx, relation.Object{Namespace: schema.OrganizationNamespace, ID: pbreq.Msg.GetOrgId()}, schema.UpdatePermission, req)
	},
	"/raystack.frontier.v1beta1.FrontierService/SetOrganizationMemberRole": func(ctx context.Context, handler *v1beta1connect.ConnectHandler, req connect.AnyRequest) error {
		pbreq := req.(*connect.Request[frontierv1beta1.SetOrganizationMemberRoleRequest])
		return handler.IsAuthorized(ctx, relation.Object{Namespace: schema.OrganizationNamespace, ID: pbreq.Msg.GetOrgId()}, schema.PolicyManagePermission, req)
	},
	"/raystack.frontier.v1beta1.FrontierService/ListOrganizationInvitations": func(ctx context.Context, handler *v1beta1connect.ConnectHandler, req connect.AnyRequest) error {
		pbreq := req.(*connect.Request[frontierv1beta1.ListOrganizationInvitationsRequest])
		return handler.IsAuthorized(ctx, relation.Object{Namespace: schema.OrganizationNamespace, ID: pbreq.Msg.GetOrgId()}, schema.InvitationListPermission, req)
	},
	"/raystack.frontier.v1beta1.FrontierService/CreateOrganizationInvitation": func(ctx context.Context, handler *v1beta1connect.ConnectHandler, req connect.AnyRequest) error {

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread core/membership/service.go
Comment thread core/membership/service.go Outdated
Comment thread core/membership/service.go
Comment thread Makefile
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 14 out of 15 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread core/membership/service.go Outdated
Comment thread internal/api/v1beta1connect/organization.go Outdated
Comment thread internal/api/v1beta1connect/organization.go Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: cce64d6a-cbeb-4c30-a7da-ec78e343f1d5

📥 Commits

Reviewing files that changed from the base of the PR and between 4606aec and f7682aa.

⛔ Files ignored due to path filters (2)
  • proto/v1beta1/frontier.pb.go is excluded by !**/*.pb.go, !proto/**
  • proto/v1beta1/frontierv1beta1connect/frontier.connect.go is excluded by !proto/**
📒 Files selected for processing (13)
  • Makefile
  • cmd/serve.go
  • core/membership/errors.go
  • core/membership/mocks/group_service.go
  • core/membership/mocks/project_service.go
  • core/membership/service.go
  • core/membership/service_test.go
  • internal/api/v1beta1connect/interfaces.go
  • internal/api/v1beta1connect/mocks/membership_service.go
  • internal/api/v1beta1connect/organization.go
  • internal/api/v1beta1connect/organization_test.go
  • pkg/server/connect_interceptors/authorization.go
  • test/e2e/regression/api_test.go
✅ Files skipped from review due to trivial changes (2)
  • core/membership/errors.go
  • core/membership/mocks/project_service.go
🚧 Files skipped from review as they are similar to previous changes (6)
  • pkg/server/connect_interceptors/authorization.go
  • internal/api/v1beta1connect/interfaces.go
  • Makefile
  • internal/api/v1beta1connect/organization.go
  • test/e2e/regression/api_test.go
  • internal/api/v1beta1connect/organization_test.go

Comment thread core/membership/service.go
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants