Add GraphQL mutation to remove an organization user
What does this MR do and why?
Organization owners and admins could update an organization user's access
level, but there was no way to delete a user from an organization. This adds
the OrganizationUserDelete GraphQL mutation, backed by
Organizations::OrganizationUsers::DestroyService.
The mutation deletes a single organization user identified by its global ID.
Key points:
- Permission: the existing
:remove_userpolicy ability is renamed to:delete_organization_user(as suggested in the issue). It requiresupdate_organization— org owners/admins or instance admins only. - Last owner protection: the
delete_organization_userability denies the last organization owner, so through GraphQL authorization fails before the service runs and the caller gets a top-level access error rather than a mutation error. The service also carries its own last-owner message withreason: :last_ownerfor any future non-GraphQL caller. - Multiple organizations: a user cannot be deleted from their only
organization. The model's
before_destroycallback previously added an error but did not halt the delete; it nowthrow :aborts so the record is genuinely preserved. - Home organization guard: the service refuses to delete a user from their
home organization (the one
users.organization_idpoints at). Otherwise that column would keep pointing at an organization the user is no longer a member of, andUser#update_home_organization_userwould re-add them the next time the user'sadminflag changed. The error carriesreason: :home_organization.
Known limitation
Deletion is not durable for a user who still holds group or project memberships
inside the organization. Member#create_organization_user_record recreates the
organization_users row whenever a non-pending membership in that organization
is saved. Cleaning up those memberships is out of scope for this MR and is
tracked in #613955
References
- Closes #607884 (closed)
- Follow-up: #613955
Screenshots or screen recordings
Backend-only change; no UI.
How to set up and validate locally
-
Sign in as an instance admin or an owner of the target organization.
-
Ensure the target user belongs to at least two organizations, is not the last owner of the organization you are deleting them from, and that organization is not their home organization.
-
Open GraphiQL at
http://gdk.test:3000/-/graphql-explorerand run:mutation { organizationUserDelete(input: { id: "gid://gitlab/Organizations::OrganizationUser/1" }) { organizationUser { id } errors } } -
Confirm the organization user is deleted and the
errorsarray is empty. -
Re-run against the last owner of an organization and confirm a top-level access error.
-
Re-run against a user who belongs to only one organization and confirm the error
A user must associate with at least one organization. -
Re-run against a user whose
organization_idis the organization you are deleting them from and confirm the errorYou cannot delete a user from their home organization. -
Sign in as a non-owner member and confirm a top-level access error.
Tests:
bundle exec rspec \
spec/services/organizations/organization_users/destroy_service_spec.rb \
spec/policies/organizations/organization_user_policy_spec.rb \
spec/requests/api/graphql/mutations/organizations/organization_users/delete_spec.rbMR acceptance checklist
Evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.