Add granular token authorization for REST API
What does this MR do and why?
Add authorization for granular personal access tokens based on required permissions and scope boundary for REST API.
References
Issue: #566607
How to set up and validate locally
- Copy this patch and apply it (
pbpaste | git apply
):diff --git a/app/services/personal_access_tokens/create_service.rb b/app/services/personal_access_tokens/create_service.rb index c31caaf864e4..b8a702ed0b2f 100644 --- a/app/services/personal_access_tokens/create_service.rb +++ b/app/services/personal_access_tokens/create_service.rb @@ -43,3 +43,4 @@ def personal_access_token_params organization_id: organization_id, - description: params[:description] + description: params[:description], + granular: params[:scopes] == ['granular'] } diff --git a/lib/api/issues.rb b/lib/api/issues.rb index f7b907dd1bf2..48980d6a3cc3 100644 --- a/lib/api/issues.rb +++ b/lib/api/issues.rb @@ -289,2 +289,3 @@ class Issues < ::API::Base route_setting :mcp, tool_name: :create_issue, params: Helpers::IssuesHelpers.create_issue_mcp_params + route_setting :authorization, permissions: :create_issue, boundary_type: :project post ':id/issues' do diff --git a/lib/gitlab/auth.rb b/lib/gitlab/auth.rb index cb7c055dec1c..f76e666b8d7c 100644 --- a/lib/gitlab/auth.rb +++ b/lib/gitlab/auth.rb @@ -25,3 +25,4 @@ module Auth SELF_ROTATE_SCOPE, - MCP_SCOPE + MCP_SCOPE, + :granular ].freeze
- In Rails console, create a granular PAT for a user and copy a URL to create an issue with the token:
reload! user = User.human.last token = PersonalAccessTokens::CreateService.new( current_user: user, target_user: user, organization_id: user.organization_id, params: { expires_at: 1.month.from_now, scopes: ['granular'], name: 'gPAT' } ).execute[:personal_access_token] project = user.projects.first scope = Authz::GranularScope.new(namespace: project.project_namespace, permissions: [:create_issue]) Authz::GranularScopeService.new(token).add_granular_scopes(scope) IO.popen('pbcopy', 'w') { |f| f.puts "curl http://#{Gitlab.host_with_port}/api/v4/projects/#{project.id}/issues\?title\=My%20New%20Issue%20Title -X POST -H \"PRIVATE-TOKEN: #{token.token}\"" }
- Paste the URL to create an issue in another terminal instance and execute it. It should not succeed and output an error stating the feature is not yet supported:
{"error":"insufficient_granular_scope","error_description":"Granular tokens are not yet supported"}
- Enable the
authorize_granular_pats
feature flag in the console:Feature.enable(:authorize_granular_pats)
- Retry the previous request. It should succeed and create an issue.
- Delete the token's granular scopes:
token.granular_scopes.delete_all
- Retry the previous request. It should not succeed and output an error stating the missing required scope:
{"error":"insufficient_granular_scope","error_description":"Access denied: Your Personal Access Token lacks the required permissions: [create_issue] for \"my-namespace/my-project\"."}
- Copy a URL for an unsupported endpoint:
IO.popen('pbcopy', 'w') { |f| f.puts "curl http://#{Gitlab.host_with_port}/api/v4/projects/#{project.id}/issues/#{project.issues.last.iid}\?title\=My%20Updated%20Issue%20Title -X PUT -H \"PRIVATE-TOKEN: #{token.token}\"" }
- Paste the URL in another terminal instance and execute it. It should not succeed and output an error stating the required boundary and permissions could not be determined:
{"error":"insufficient_granular_scope","error_description":"Unable to determine boundary and permissions for authorization"}
Edited by Alex Buijs