Skip to content

feat: recognize GitLab token types and their reach when resolving repositories - #3

Merged
pikann merged 2 commits into
Paca-AI:masterfrom
gorlix:feat/token-type-reach
Oct 29, 2026
Merged

pikann merged 2 commits into
Paca-AI:masterfrom
gorlix:feat/token-type-reach

Conversation

@gorlix

@gorlix gorlix commented Oct 28, 2026

Copy link
Copy Markdown

Closes #2

What

Classifies each access token at setup (project, group, service account or personal) and uses what it covers when resolving repositories.

  • CompleteIdentitySetup stores token_type, resource_id and resource_path. Project and group tokens are told by the bot user name GitLab gives them (project_<id>_bot…, group_<id>_bot…) and the path is looked up via GET /projects/:id or GET /groups/:id (if the lookup fails the type is kept and the path stays empty). Any other bot user is a service account (its name may be chosen by its creator, so it is told by GitLab's bot flag); a human user holds a personal token.
  • identityOf sets DisplayName: "Project token · acme/ui", "Group token · acme/frontend", "Service account · ", "Personal token · ".
  • ResolveRepository skips a project token whose project doesn't match and a group token whose path doesn't contain the repo, then requires access_level >= Developer, so a public repo is never bound to a non-member token. When every connection is skipped, the error says what each one covers (or that its role is too low) and asks for a token that is a member of the repository.
  • MintCredential now also refuses AccessWrite at access level 0.
  • Identities stored before this change are classified lazily from the stored username and bot flag on first load and then persisted.

Migration

New 0003_identity_token_reach.sql adds the three columns, leaving existing rows empty. 0002 is untouched. If you'd rather fold it into 0002 (v0.6.0 is not released yet), say so and I'll change it.

Verification

  • Unit tests extend the fake GitLab with project/group bot users, /groups/:id and per-token roles.
  • Verified against a real GitLab CE in Docker, Postgres 16, TinyGo 0.41.1 and golangci-lint (0 issues). The real-GitLab run covers project, group, personal and service-account tokens, a group token and a sibling group sharing a name prefix, a public repository, a Reporter-level token, candidate order, lazy classification of existing rows and writes at level 0.
  • Verified in a real Paca core with a real GitLab CE: migrations 0001–0003, display names, project/group token linking, rejection of repos outside a token's reach, and webhook ingestion.
  • Verified against gitlab.com with a personal token and a group service account (Developer on the group): the connections are named "Personal token · " and "Service account · ", repositories in the group resolve, a repository outside it and a public one it is not a member of are refused, and a write credential is refused for the latter.

Notes

  • Project and group access tokens are available on GitLab.com only with Premium or Ultimate (and on self-managed with any licence). On GitLab.com Free the common cases are a personal token or a service account, which the same rules handle.
  • The plugin's coverage messages ("The connected project token covers only …") currently do not reach the user: core maps NotFound/Forbidden from ResolveRepository to a generic ErrNotVisible. The generic UI text is acceptable; surfacing the detail would need a core change, which is out of scope here.
  • Not covered in the core run: Reporter-level project tokens, service accounts and the public-repo case against a non-member; these are covered by the unit tests and the real-GitLab runs above.

🤖 Generated with Claude Code

gorlix and others added 2 commits October 28, 2026 16:20
Classify each access token at setup (project, group, service account or
personal) and store the type, the project or group id and its path on the
identity, with a new migration for the columns. Project and group tokens
are told by the bot user name GitLab gives them; any other bot user is a
service account, whose name its creator may choose, and a human user holds
a personal token. Identities stored before get classified lazily on first
load.

ResolveRepository skips connections whose token cannot cover the
repository, requires at least the Developer role so a public project is
never bound to a non-member token, and explains what each connection
covers (or that its role is too low) when none qualifies. The hint no
longer names only group access tokens, which GitLab.com offers on Premium
and above. MintCredential refuses writes to non-members too. The display
name now says what a connection covers, e.g. "Project token · acme/ui" or
"Service account · <name>".
Extend the fake GitLab with project and group bot users, /groups/:id and
per-token roles on /projects/:id, and test classification (including
service accounts with a chosen name), display names, candidate filtering
and ordering, lazy classification of existing rows, a token whose role on
its own repository is too low, the error text for a service account, and
the refusal of writes at access level 0.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

@pikann pikann left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! Thank you for the contribution! 🚀

@pikann
pikann merged commit 316ba11 into Paca-AI:master Oct 29, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Recognize GitLab token types (personal / project / group) and their reach when resolving repositories

2 participants