Add the ability to revoke Feed Tokens via the Group revocation API
What does this MR do and why?
Add the ability to revoke Feed Tokens via the Group revocation API
The Group Token Revocation API allows you to pass certain types of tokens and have them revoked IF the token gives access to the group, a subgroup, or a project within the group.
This commit adds the ability to revoke one type of Feed Token - the long lived user-scoped feed token. (It does not allow revocation of the path-based user feed token.)
There isn't actually a token record for Feed Tokens, they're a column on User. (Compare with PersonalAccessTokens or DeployTokens, which are their own model.) Feed tokens therefore can't technically be revoked, but instead we can rotate them and not inform the caller of the new value. In this way we have made the old token inoperative.
If the token is successfully revoked, the API returns a minimal amount
of information about the User whose feed token it was. This response
is quite different to the response given when a PAT or DeployToken are
revoked, but that makes sense since there isn't a token record.
Returning scopes
, expires_at
, revoked
, expired
et. al does not
make sense.
The MR refactors part of AgnosticTokenRevocationService
to make it
even more generic so that we can specify the presenter used when the API
renders the user. Previously there was an assumption in the code that
there was an object on which we could call .user
.
It utilises ResetTokenService
added in Adds a service to reset a User's feed token (!161393 - merged).
It adds an additional source
, following the pattern of https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/services/personal_access_tokens/revoke_service.rb
This feature still exists behind the
group_agnostic_token_revocation
feature flag. The
rollout issue for that is at
#468606
Resolves Add support for revoking Feed Tokens to Group T... (#468599 - closed)
Todo
-
Refactor into a revocation service that will do logging: Adds a service to reset a User's feed token (!161393 - merged)
MR acceptance checklist
Please evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.
Screenshots or screen recordings
Screenshots are required for UI changes, and strongly recommended for all other merge requests.
Before | After |
---|---|
How to set up and validate locally
Numbered steps to set up and validate the change are strongly suggested.
"Leaker" steps
- Log in as the user who will "leak" the token ("Leaker")
- Navigate to
/-/user_settings/personal_access_tokens
- Copy the feed token. If it lacks the
glft-
prefix, reset the token first. (Depending on the age of your GDK & user, it might have been set before Add prefix to feed tokens (#376749 - closed) was released.) - Using the token, access a feed. For example:
% curl "https://gdk.test:3443/tokengroup/tokensubgroupa/tokensubgroupb/project/-/issues.atom?feed_token=<YOUR_TOKEN_HERE>" <?xml version="1.0" encoding="UTF-8"?> <feed xmlns="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/"> <title>project issues</title>
- Visit a project or group issue page, click the three dot icon top right, and copy the path-specific feed token from either the Calendar or RSS feed. It will have the form
glft-BIGHASHHERE-USERID
. Note this for later.- Finding the token:
- Example using it:
% curl "https://gdk.test:3443/my-awesome-group/a-project/-/issues.atom?feed_token=<PATH_SPECIFIC_FEED_TOKEN>" <?xml version="1.0" encoding="UTF-8"?> <feed xmlns="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/"> <title>a project issues</title>
- Log out or open another browser profile
Group Owner steps
- Log in as another user ("Group Owner").
- Create or find a top-level group that you own and that has the Leaker as a member of either the group, a subgroup, or a project in the group.
- Enable the FF:
Feature.enable(:group_agnostic_token_revocation, Group.find(<TOP_LEVEL_GROUP_ID>))
- Create or find an API-scoped Personal Access Token
- As the Group Owner, revoke the "Leaker's" feed token (the one copied from the user profile, not the path-specific feed token):
% curl --header "PRIVATE-TOKEN: <GROUP_OWNER_PAT>" -XPOST https://gdk.test:3443/api/v4/groups/<TOP_LEVEL_GROUP_ID>/tokens/revoke -H "Content-Type: application/json" --data '{"token":"<LEAKED_FEED_TOKEN>"}' {"id":22,"username":"nm","name":"Nick Malcolm"}
- Note the response indicates who the Leaker was
As the Leaker
- As the Leaker, note that the feed token no longer works:
% curl "https://gdk.test:3443/tokengroup/tokensubgroupa/tokensubgroupb/project/-/issues.atom?feed_token=<YOUR_TOKEN_HERE>" <html><body>You are being <a href="https://gdk.test:3443/users/sign_in">redirected</a>.</body></html>
- As the leaker, note that the path-specific feed token no longer works either. This is because the token is made by hashing (path+feed token), and the underlying feed token changed.
% curl "https://gdk.test:3443/my-awesome-group/a-project/-/issues.atom?feed_token=<PATH_SPECIFIC_FEED_TOKEN>" <html><body>You are being <a href="https://gdk.test:3443/users/sign_in">redirected</a>.</body></html>
- Log in as the Leaker and navigate to
/-/user_settings/personal_access_tokens
- Copy the feed token. Note that it has been rotated.
As a Group Owner for which Leaker is not a member
- One way to do this is just to choose another Leaker, e.g.
> User.human.last.feed_token => <ANOTHER_FEED_TOKEN>
- Attempt to revoke the feed token:
% curl --header "PRIVATE-TOKEN: <GROUP_OWNER_PAT>" -XPOST https://gdk.test:3443/api/v4/groups/<TOP_LEVEL_GROUP_ID>/tokens/revoke -H "Content-Type: application/json" --data '{"token":"<ANOTHER_FEED_TOKEN>"}' {"message":"422 Unprocessable Entity"}
- Validate that the token did not change
> User.human.last.feed_token => <ANOTHER_FEED_TOKEN (unchanged)>
Related to #468599 (closed)