Skip to content

Add the ability to revoke Feed Tokens via the Group revocation API

Nick Malcolm requested to merge 468599-revoke-feed-tokens into master

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

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

  1. Log in as the user who will "leak" the token ("Leaker")
  2. Navigate to /-/user_settings/personal_access_tokens
  3. 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.)
    1. Finding the token: Screenshot_2024-08-13_at_12.04.16_PM
  4. 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>
  5. 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.
    1. Finding the token: Screenshot_2024-08-13_at_12.02.47_PM
    2. 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>
  6. Log out or open another browser profile

Group Owner steps

  1. Log in as another user ("Group Owner").
  2. 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.
  3. Enable the FF:
    Feature.enable(:group_agnostic_token_revocation, Group.find(<TOP_LEVEL_GROUP_ID>))
  4. Create or find an API-scoped Personal Access Token
  5. 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"}
  6. Note the response indicates who the Leaker was

As the Leaker

  1. 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>
  2. 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>
  3. Log in as the Leaker and navigate to /-/user_settings/personal_access_tokens
  4. Copy the feed token. Note that it has been rotated.

As a Group Owner for which Leaker is not a member

  1. One way to do this is just to choose another Leaker, e.g.
    > User.human.last.feed_token
    => <ANOTHER_FEED_TOKEN>
  2. 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"}
  3. Validate that the token did not change
    > User.human.last.feed_token
    => <ANOTHER_FEED_TOKEN (unchanged)>

Related to #468599 (closed)

Edited by Nick Malcolm

Merge request reports

Loading