API endpoint to allow enterprise group Owners to delete enterprise users
What does this MR do and why?
Implements #520825 (closed)
Adds a DELETE :id/enterprise_users/:user_id
endpoint to the Group enterprise users API.
The endpoint:
- Allows the owner of an enterprise group to delete enterprise users in their group
- Supports soft and hard deletion
- Re-uses the existing
User#delete_async
method, which relies onDeleteUserWorker
, so deletion logic has parity with regular user deletion
The endpoint is very similar to the logic in Admin::UsersController#destroy
, with different authorization behaviour since this endpoint is EE-specific.
To achieve this, I added a new condition :can_delete_enterprise_user
to EE::UserPolicy
, and implement a new rule that gives users meeting that condition the :destroy_user
ability.
Considerations
-
Since this operation is destructive I considered whether we'd want to release it behind a feature flag. I consulted "When to use feature flags" which explicitly mentions you may not need a flag for a new API endpoint. - The scenario I'd worry most about is the endpoint being abused to delete users inappropriately. However since the endpoint is restricted to Enterprise group owners, this seems very unlikely. In case a rogue group owner were able to delete users inappropriately, and we needed to restrict their ability before we could ship a revert, we could in theory modify the rogue account's permissions.
-
I believe we need a changelog entry for this change -
I believe we also need to update documentation for this change, specifically the Group enterprise users API page.
References
Issue: #520825 (closed)
Screenshots or screen recordings
API-only change.
How to set up and validate locally
Set up a group with users
I recommend setting up two groups: one you can use to test successful deletion, and another you can use to test unsuccessful deletion.
- Follow these steps to ensure your GDK instance simulates SaaS
- Log in to your instance as
root
- Create a few new users (
Admin
-->Users
-->New user
) a. One user will be the administrator of your enterprise group. Edit their account and give them a password, so they can log in to your instance. - Create a group named
Enterprise group
(Admin
-->Groups
-->New group
) - Visit the admin page for the new group and go to user management (
Groups
--> -->Manage Access
) - Invite your group admin to be
Owner
of the group (Invite members
--> choose your admin user --> give themOwner
) - Either invite the other users as developers as
root
, or log in as your admin user and invite them that way. - As
root
, remove yourself as owner of the group. - You now have a group with 1
owner
user and 2developer
users.
Claim enterprise users
- Get a Rails console
gdk rails c
- For each of your non-owner users, set their
enterprise_group_id
to "claim" them as EE users:
group = Group.find_by(path: 'enterprise-group')
[<username1>, <username2>].each { |name| User.find_by(username: name).user_detail.update!(enterprise_group_id: group.id) }
Test the API call
- Log in as your enterprise group owner
- Generate a Personal Access Token, with
api
scope at e.g. https://gdk.test:3443/-/user_settings/personal_access_tokens (User icon
-->Edit profile
-->Access Tokens
a. Note the access token before you navigate away from this page - Grab a shell prompt
- Set variables to set up your API call:
TOKEN=<your Personal Access Token>
GROUP_ID=<your group id>
USER_ID=<ID of the user to delete>
- Send HTTP request with
curl
:
> curl -X DELETE \
--header "PRIVATE-TOKEN: ${TOKEN}" \
"https://gdk.test:3443/api/v4/groups/${GROUP_ID}/enterprise_users/${USER_ID}"
- Observe blank response in terminal (good -- the endpoint returns
204 OK
- Grab a Rails console
gdk rails c
- Verify the user was queued up for deletion
user_1 = User.find_by(username: <user1 username>)
ghost_migration = Users::GhostUserMigration.find_by(user_id: user_1.id)
- Observe the
GhostUserMigration
exists andhard_delete: false
. - Try the same API request for user 2, but try a hard delete. Back at the shell prompt:
> USER_ID=<user 2 ID>
> curl -X DELETE \
--header "PRIVATE-TOKEN: ${TOKEN}" \
"https://gdk.test:3443/api/v4/groups/${GROUP_ID}/enterprise_users/${USER_ID}?hard_delete=true"
- Verify in a Rails console:
user_2 = User.find_by(username: <user2 username>)
ghost_migration = Users::GhostUserMigration.find_by(user_id: user_2.id)
- Observe the
GhostUserMigration
exists, andhard_delete: true
Test the error path
- I'd recommend testing out the unhappy path for this as well. One way to do this is:
- Create another user and invite them to the existing enterprise group
- Claim the user as an enterprise user via Rails console
- Create a new enterprise group
- Create a new user who will be owner of the new enterprise group, and invite them to be owner of the group
- Remove yourself from the group
- Log in as the new enterprise group's owner
- Create a new Personal Access Token
- Set up the new API call:
TOKEN=<new enterprise group owner's PAT>
USER_ID=<user you just added to the OLD enterprise group>
- Make the API call again
- Observe the response
{"message":"404 Group Not Found"}
and the user was not deleted
MR 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.