Skip to content

Gracefully handle space-delimited scopes in registry /jwt/auth requests

João Pereira requested to merge jwt-auth-space-delimited-scopes into master

What does this MR do and why?

Fixes Gracefully handle blank spaces in scope query p... (#425045 - closed). Please read the issue description as it provides all the required context.

Given that this change is on a central place (JWT controller), and despite the vast test coverage, we're rolling this out using a feature flag (#427881 (closed)), with a percentage based rollout.

How to set up and validate locally

We'll assume the existence of the gitlab-org/gitlab-test project in your GDK instance and use it for these tests. You can pick any other project, as long as the Container Registry feature is enabled for it.

We'll also assume that your GDK Rails instance is listening at gdk.test:3000 and you have a user root with a PAT foobar, with read permissions on the container registry. Your GDK registry setup must have auth_enabled: true (docs).

We need to query the Rails API and decode a few JWT tokens during these tests. To do so you can either decode it with a JWT debugger on the web (e.g. https://jwt.io/) or use a CLI tool such as jwt-cli. We'll use the latter. We'll also use httpie to make the requests and jq to parse the token from the response, but you can do that with any other tools or manually.

Current behavior

  1. Open up a Rails console and a terminal window/pane.

  2. On your terminal, obtain a JWT token with e.g. pull permissions for repository gitlab-org/gitlab-test/foo (works fine):

    http -a root:foobar http://gdk.test:3000/jwt/auth \
        client_id=="docker" \
        service=="container_registry" \
        scope=="repository:gitlab-org/gitlab-test/foo:pull" | jq -r '.token' | jwt decode -

    Output (timestamp fields will vary, the important part is access):

    Token header
    ------------
    ...
    
    Token claims
    ------------
    {
      "access": [
        {
          "actions": [
            "pull"
          ],
          "meta": {
            "project_path": "gitlab-org/gitlab-test"
          },
          "name": "gitlab-org/gitlab-test/foo",
          "type": "repository"
        }
      ],
      ...
    }
  3. Now let's do the same, but request permissions for two repositories, using two scope parameters (works fine):

    http -a root:foobar http://gdk.test:3000/jwt/auth \
        client_id=="docker" \
        service=="container_registry" \
        scope=="repository:gitlab-org/gitlab-test/b:pull,push" scope=="repository:gitlab-org/gitlab-test/a:pull" | jq -r '.token' | jwt decode -

    Output (timestamp fields will vary, the important part is access):

    Token header
    ------------
    ...
    
    Token claims
    ------------
    {
       "access": [
         {
           "actions": [
             "pull",
             "push"
           ],
           "meta": {
             "project_path": "gitlab-org/gitlab-test"
           },
           "name": "gitlab-org/gitlab-test/b",
           "type": "repository"
         },
         {
           "actions": [
             "pull"
           ],
           "meta": {
             "project_path": "gitlab-org/gitlab-test"
           },
           "name": "gitlab-org/gitlab-test/a",
           "type": "repository"
         }
       ],
       ...
    }
  4. Now let's repeat, but use a space-delimited scope parameter (bug! notice how the returned access only contains one element, the one prior to the blank space):

    http -a root:foobar http://gdk.test:3000/jwt/auth \
        client_id=="docker" \
        service=="container_registry" \
        scope=="repository:gitlab-org/gitlab-test/b:pull,push repository:gitlab-org/gitlab-test/a:pull" | jq -r '.token' | jwt decode -

    Output (timestamp fields will vary, the important part is access):

    Token header
    ------------
    ...
    
    Token claims
    ------------
    {
       "access": [
         {
           "actions": [
             "pull",
             "push"
           ],
           "meta": {
             "project_path": "gitlab-org/gitlab-test"
           },
           "name": "gitlab-org/gitlab-test/b",
           "type": "repository"
         }
       ],
       ...
    }

New behavior

  1. Enable the jwt_auth_space_delimited_scopes feature flag;

    Feature.enable(:jwt_auth_space_delimited_scopes)
  2. Repeat all the tests above. Those that worked should continue to work, the last one (bug), should now work as well, returning two objects within access, just like when we used two scope parameters.

MR acceptance checklist

This checklist encourages us to confirm any changes have been analyzed to reduce risks in quality, performance, reliability, security, and maintainability.

Edited by João Pereira

Merge request reports