Draft: Granular PAT PoC
What does this MR do and why?
This is a PoC that shows we can use Grape annotations and GraphQL directives to perform granular PAT authorization for REST API endpoints and GraphQL queries and mutations.
This snippet shows the directives can be exported and used to perform early authorization checks in a future GitLab Auth Stack external auth reverse proxy server. It also shows they can be used for creating documentation and for static analysis in our CI pipelines.
How to setup
Select a project project with an issue issue.
- Create a PAT in the UI
- in the console, create token permissions
pat = PersonalAccessToken.last pat.update!(granular: true) namespace = project.project_namespace pat.token_permissions.create(namespace: namespace, permissions: [:read_issues]) - Set PAT env var to your just created PAT token:
PAT=glpat-xxx - Verify REST endpoints to get issue details are working (replace
project_idwithproject.idandissue_iidwithissue.iid):output:curl http://gdk.test:3000/api/v4/projects/project_id/issues/issue_iid\?private_token\=$PAT=> {"id":611,"iid":1,"project_id":19,"title":"My Issue",... - Verify REST endpoints to update issue details are not working:
output:
curl http://gdk.test:3000/api/v4/projects/project_id/issues/issue_iid\?title\=My%20New%20Issue%20Title -X PUT -H "PRIVATE-TOKEN: $PAT"=> Access denied: Your Personal Access Token lacks the required permissions: [admin_issues] for "my-group/my-subgroup/my-project". - Verify GraphQL queries to lookup issue titles within the project are working (replace
fullPathwithproject.full_path):output:curl http://gdk.test:3000/api/graphql -X POST \ -H "PRIVATE-TOKEN: $PAT" \ -H 'Content-Type: application/json' \ -d '{"query":"{ project(fullPath: \"my-group/my-subgroup/my-project\") { issues { nodes { title } } } }"}'=> {"data":{"project":{"issues":{"nodes":[{"title":"My Issue"}]}}}} - Verify GraphQL mutations to update issue titles within the project are not working (replace
fullPathwithproject.full_pathandiidwithissue.iid):output:curl http://localhost:3000/api/graphql -X POST \ -H "PRIVATE-TOKEN: $PAT" \ -H 'Content-Type: application/json' \ -d '{"query":"mutation UpdateIssue($projectPath: ID\!, $iid: String\!, $title: String\!) { updateIssue(input: { projectPath: $projectPath, iid: $iid, title: $title }) { issue { iid title } errors } }","variables":{"projectPath":"my-group/my-subgroup/my-project","iid":"1","title":"My New Issue Title"}}'=> {"errors":[{"message":"Access denied: Your Personal Access Token lacks the required permissions: [admin_issues] for \"my-group/my-subgroup/my-project\".","locations":[{"line":1,"column":75}],"path":["updateIssue"]}],"data":{"updateIssue":null}} - Update the PAT permissions to include the
:admin_issuespermissionAuthz::TokenPermission.last.update(permissions: [:admin_issues]) - Execute the previous PUT request and verify the output is something like:
{"id":611,"iid":1,"project_id":19,"title":"My New Issue Title",... - Execute the previous mutation and verify the output is something like:
{"data":{"updateIssue":{"issue":{"iid":"1", "title":"My New Issue Title"},"errors":[]}}}
Edited by Alex Buijs