Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,6 @@ gitlab4j-mock-config-*.properties

# git-changelog plugin #
.okhttpcache

# IntelliJ HTTP Client private environment (contains tokens)
http-client.private.env.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,49 @@ public class ImpersonationToken implements Serializable {

/** Enum to specify the scope of an ImpersonationToken. */
public enum Scope {
/** Grants complete read/write access to the API. */
API,

/** Grants read access to the API. */
READ_API,

/** Grants read-only access to the user's profile. */
READ_USER,

/** Grants read-only access to repositories on private projects using Git-over-HTTP. */
READ_REPOSITORY,

/** Grants read-write access to repositories on private projects using Git-over-HTTP. */
WRITE_REPOSITORY,

/** Grants read (pull) access to a Container Registry. */
READ_REGISTRY,

/** Grants write (push) access to a Container Registry. */
WRITE_REGISTRY,

/** Grants pull access through the dependency proxy. */
READ_VIRTUAL_REGISTRY,

/** Grants push, pull and delete access through the dependency proxy. */
WRITE_VIRTUAL_REGISTRY,

/** Grants create access to the runners. */
CREATE_RUNNER,

/** Grants access to manage the runners. */
MANAGE_RUNNER,

/** Grants access to GitLab Duo related API endpoints. */
AI_FEATURES,

/** Grants permission to perform Kubernetes API calls using the agent for Kubernetes. */
K8S_PROXY,

/** Grants permission to rotate this token using the personal access token API. */
SELF_ROTATE,

/** Grants permission to perform API actions as any user in the system, when authenticated as an admin user. */
SUDO;

private static JacksonJsonEnumHelper<Scope> enumHelper = new JacksonJsonEnumHelper<>(Scope.class);
Expand Down
64 changes: 61 additions & 3 deletions gitlab4j-models/src/main/java/org/gitlab4j/models/Constants.java
Original file line number Diff line number Diff line change
Expand Up @@ -882,7 +882,22 @@ public enum ApplicationScope {
PROFILE,

/** Grants read-only access to the user's primary email address using OpenID Connect. */
EMAIL;
EMAIL,

/** Grants read (pull) access to a Container Registry. */
READ_REGISTRY,

/** Grants write (push) access to a Container Registry. */
WRITE_REGISTRY,

/** Grants pull access through the dependency proxy. */
READ_VIRTUAL_REGISTRY,

/** Grants push, pull and delete access through the dependency proxy. */
WRITE_VIRTUAL_REGISTRY,

/** Grants permission to rotate this token using the personal access token API. */
SELF_ROTATE;

private static JacksonJsonEnumHelper<ApplicationScope> enumHelper =
new JacksonJsonEnumHelper<>(ApplicationScope.class);
Expand Down Expand Up @@ -1185,8 +1200,26 @@ public String toString() {

/** Enum to use for specifying the deploy token scope. */
public enum DeployTokenScope {
/** Grants read-only access to repositories on private projects using Git-over-HTTP. */
READ_REPOSITORY,
READ_REGISTRY;

/** Grants read (pull) access to a Container Registry. */
READ_REGISTRY,

/** Grants write (push) access to a Container Registry. */
WRITE_REGISTRY,

/** Grants pull access through the dependency proxy. */
READ_VIRTUAL_REGISTRY,

/** Grants push, pull and delete access through the dependency proxy. */
WRITE_VIRTUAL_REGISTRY,

/** Grants read access to the package registry. */
READ_PACKAGE_REGISTRY,

/** Grants write access to the package registry. */
WRITE_PACKAGE_REGISTRY;

private static JacksonJsonEnumHelper<DeployTokenScope> enumHelper =
new JacksonJsonEnumHelper<>(DeployTokenScope.class);
Expand All @@ -1209,13 +1242,38 @@ public String toString() {

/** Enum to use for specifying the project token scope. */
public enum ProjectAccessTokenScope {
/** Grants complete read/write access to the scoped project API. */
API,

/** Grants read access to the scoped project API. */
READ_API,

/** Grants read (pull) access to a Container Registry. */
READ_REGISTRY,

/** Grants write (push) access to a Container Registry. */
WRITE_REGISTRY,

/** Grants read-only access to repositories on private projects using Git-over-HTTP. */
READ_REPOSITORY,

/** Grants read-write access to repositories on private projects using Git-over-HTTP. */
WRITE_REPOSITORY,
CREATE_RUNNER;

/** Grants create access to the runners. */
CREATE_RUNNER,

/** Grants access to manage the runners. */
MANAGE_RUNNER,

/** Grants access to GitLab Duo related API endpoints. */
AI_FEATURES,

/** Grants permission to perform Kubernetes API calls using the agent for Kubernetes. */
K8S_PROXY,

/** Grants permission to rotate this token using the personal access token API. */
SELF_ROTATE;

private static JacksonJsonEnumHelper<ProjectAccessTokenScope> enumHelper =
new JacksonJsonEnumHelper<>(ProjectAccessTokenScope.class);
Expand Down
7 changes: 7 additions & 0 deletions http-client.private.env.json.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"dev": {
"token": "glpat-xxxxxxxxxxxxxxxxxxxx",
"project_id": "12345",
"group_id": "6789"
}
}
137 changes: 137 additions & 0 deletions verify-scopes.http
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
### Verify that the added scopes exist in GitLab
###
### Prerequisites:
### Copy http-client.private.env.json.example to http-client.private.env.json
### and fill in your gitlab.com token, project_id, and group_id.
### Then select the "dev" environment in IntelliJ before running.
###
### How the proof works:
### GitLab validates scopes AFTER authentication. If a scope is valid,
### the request succeeds (or fails for permission reasons: 403/404).
### If a scope is INVALID, GitLab returns 400 with
### "scopes does not have a valid value".
###
### Each section includes a negative test with a deliberately invalid
### scope to demonstrate that GitLab does reject unknown scopes.


### --- ApplicationScope: self_rotate ---
###
### The self-service PAT endpoint (POST /api/v4/user/personal_access_tokens)
### only accepts k8s_proxy and self_rotate scopes (by design, GitLab 16.5+).
### The other ApplicationScope scopes (read_registry, write_registry, etc.)
### are valid PAT scopes but can only be set via the admin endpoint or the UI.
###
### Reference: https://docs.gitlab.com/user/profile/personal_access_tokens/#personal-access-token-scopes

### Positive: self_rotate scope (should return 201)
POST https://gitlab.com/api/v4/user/personal_access_tokens
PRIVATE-TOKEN: {{token}}
Content-Type: application/json

{
"name": "verify-self-rotate",
"scopes": ["self_rotate"],
"expires_at": "2026-12-31"
}

### Negative: invalid scope (should return 400)
POST https://gitlab.com/api/v4/user/personal_access_tokens
PRIVATE-TOKEN: {{token}}
Content-Type: application/json

{
"name": "verify-invalid-scope",
"scopes": ["THIS_SCOPE_DOES_NOT_EXIST"],
"expires_at": "2026-12-31"
}

### --- ProjectAccessTokenScope scopes ---
###
### Requires Maintainer access on the project.
### Reference: https://docs.gitlab.com/user/project/settings/project_access_tokens/#scopes-for-a-project-access-token

### Positive: newly added scopes (should return 201, NOT 400)
POST https://gitlab.com/api/v4/projects/{{project_id}}/access_tokens
PRIVATE-TOKEN: {{token}}
Content-Type: application/json

{
"name": "verify-project-scopes-valid",
"scopes": ["manage_runner", "ai_features", "k8s_proxy", "self_rotate"],
"expires_at": "2026-12-31",
"access_level": 30
}

### Negative: invalid scope (should return 400)
POST https://gitlab.com/api/v4/projects/{{project_id}}/access_tokens
PRIVATE-TOKEN: {{token}}
Content-Type: application/json

{
"name": "verify-project-scopes-invalid",
"scopes": ["manage_runner", "THIS_SCOPE_DOES_NOT_EXIST"],
"expires_at": "2026-12-31",
"access_level": 30
}

### --- ImpersonationToken.Scope (Group Access Token) scopes ---
###
### Requires Owner access on the group.
### Reference: https://docs.gitlab.com/user/group/settings/group_access_tokens/#scopes-for-a-group-access-token

### Positive: newly added scopes (should return 400 Bad request - User does not have permission to create group access token)
POST https://gitlab.com/api/v4/groups/{{group_id}}/access_tokens
PRIVATE-TOKEN: {{token}}
Content-Type: application/json

{
"name": "verify-group-scopes-valid",
"scopes": ["create_runner", "manage_runner", "ai_features", "self_rotate", "read_virtual_registry", "write_virtual_registry"],
"expires_at": "2026-12-31",
"access_level": 30
}

### Negative: invalid scope (should return 400 scopes does not have a valid value)
POST https://gitlab.com/api/v4/groups/{{group_id}}/access_tokens
PRIVATE-TOKEN: {{token}}
Content-Type: application/json

{
"name": "verify-group-scopes-invalid",
"scopes": ["create_runner", "THIS_SCOPE_DOES_NOT_EXIST"],
"expires_at": "2026-12-31",
"access_level": 30
}

### --- DeployTokenScope scopes ---
###
### Requires Maintainer access on the project.
### Reference: https://docs.gitlab.com/user/project/deploy_tokens/#scope

### Positive: newly added scopes (should return 201, NOT 400)
POST https://gitlab.com/api/v4/projects/{{project_id}}/deploy_tokens
PRIVATE-TOKEN: {{token}}
Content-Type: application/json

{
"name": "verify-deploy-scopes-valid",
"scopes": ["write_registry", "read_virtual_registry", "write_virtual_registry", "read_package_registry", "write_package_registry"],
"expires_at": "2026-12-31"
}

### Negative: invalid scope (should return 400 scopes does not have a valid value)
POST https://gitlab.com/api/v4/projects/{{project_id}}/deploy_tokens
PRIVATE-TOKEN: {{token}}
Content-Type: application/json

{
"name": "verify-deploy-scopes-invalid",
"scopes": ["read_repository", "THIS_SCOPE_DOES_NOT_EXIST"],
"expires_at": "2026-12-31"
}

### --- Cleanup ---
### If any tokens were created above, revoke them:
### GET https://gitlab.com/api/v4/personal_access_tokens
### then DELETE https://gitlab.com/api/v4/personal_access_tokens/:id
Loading