Skip to content

Commit 63e4950

Browse files
committed
Improve access token scripts
Test for gitlab4j/gitlab4j-api#1124
1 parent df17d98 commit 63e4950

File tree

2 files changed

+215
-7
lines changed

2 files changed

+215
-7
lines changed

gitlab4j-test/GroupAccessTokenScript.java

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
///usr/bin/env jbang "$0" "$@" ; exit $?
22

33
//DEPS info.picocli:picocli:4.6.3
4-
//DEPS org.gitlab4j:gitlab4j-api:5.4.0
4+
//DEPS https://github.com/jmini/gitlab4j-api/commit/0cca2d8adef178b25fd6b53afe489c7f5ccc2394
55
//JAVA 17
66

77
import java.io.FileInputStream;
@@ -14,6 +14,7 @@
1414
import java.util.concurrent.Callable;
1515

1616
import org.gitlab4j.api.GitLabApi;
17+
import org.gitlab4j.api.GitLabApiException;
1718
import org.gitlab4j.api.models.*;
1819
import org.gitlab4j.api.models.ImpersonationToken.Scope;
1920

@@ -43,7 +44,7 @@ public class GroupAccessTokenScript implements Callable<Integer> {
4344
private String tokenName;
4445

4546
@Option(names = { "-e", "--expiresAt" }, description = "token expiration date")
46-
private Date expiresAt;
47+
private Date expiresAt;
4748

4849
@Option(names = { "-s", "--scope" }, description = "token scopes")
4950
private List<Scope> scopes = new ArrayList<>();
@@ -88,19 +89,17 @@ public Integer call() throws Exception {
8889
break;
8990
case CREATE_GROUP_ACCESS_TOKEN:
9091
var createdToken = gitLabApi.getGroupApi()
91-
.createGroupAccessToken(idOrPath(group), tokenName, expiresAt, scopes.toArray(new Scope[]{}), accessLevel);
92+
.createGroupAccessToken(idOrPath(group), tokenName, expiresAt, scopes.toArray(new Scope[] {}), accessLevel);
9293
System.out.println(createdToken);
9394
break;
9495
case ROTATE_GROUP_ACCESS_TOKEN:
95-
ensureExists(tokenId, "tokenId");
9696
var r = gitLabApi.getGroupApi()
97-
.rotateGroupAccessToken(idOrPath(group), tokenId);
97+
.rotateGroupAccessToken(idOrPath(group), getTokenId(gitLabApi), expiresAt);
9898
System.out.println(r);
9999
break;
100100
case REVOKE_GROUP_ACCESS_TOKEN:
101-
ensureExists(tokenId, "tokenId");
102101
gitLabApi.getGroupApi()
103-
.revokeGroupAccessToken(idOrPath(group), tokenId);
102+
.revokeGroupAccessToken(idOrPath(group), getTokenId(gitLabApi));
104103
break;
105104
default:
106105
throw new IllegalArgumentException("Unexpected value: " + action);
@@ -109,6 +108,21 @@ public Integer call() throws Exception {
109108
return 0;
110109
}
111110

111+
private Long getTokenId(GitLabApi gitLabApi) throws GitLabApiException {
112+
if (tokenId != null) {
113+
return tokenId;
114+
}
115+
ensureExists(tokenName, "tokenName");
116+
List<GroupAccessToken> tokens = gitLabApi.getGroupApi()
117+
.getGroupAccessTokens(idOrPath(group));
118+
GroupAccessToken token = tokens.stream()
119+
.filter(t -> Objects.equals(t.getRevoked(), Boolean.FALSE))
120+
.filter(t -> tokenName.equals(t.getName()))
121+
.findFirst()
122+
.orElseThrow(() -> new IllegalStateException("Could not find token with name '" + tokenName + "' in group '" + idOrPath(group) + "'"));
123+
return token.getId();
124+
}
125+
112126
private void ensureExists(Object value, String optionName) {
113127
if (value == null) {
114128
throw new IllegalStateException("--" + optionName + " must be set");
Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
///usr/bin/env jbang "$0" "$@" ; exit $?
2+
3+
//DEPS info.picocli:picocli:4.6.3
4+
//DEPS https://github.com/jmini/gitlab4j-api/commit/0cca2d8adef178b25fd6b53afe489c7f5ccc2394
5+
//JAVA 17
6+
7+
import java.io.FileInputStream;
8+
import java.io.IOException;
9+
import java.io.InputStream;
10+
import java.nio.file.Files;
11+
import java.nio.file.Path;
12+
import java.nio.file.Paths;
13+
import java.util.ArrayList;
14+
import java.util.Date;
15+
import java.util.List;
16+
import java.util.Objects;
17+
import java.util.Properties;
18+
import java.util.concurrent.Callable;
19+
20+
import org.gitlab4j.api.Constants.ProjectAccessTokenScope;
21+
import org.gitlab4j.api.GitLabApi;
22+
import org.gitlab4j.api.GitLabApiException;
23+
import org.gitlab4j.api.models.AccessLevel;
24+
import org.gitlab4j.api.models.ProjectAccessToken;
25+
26+
import picocli.CommandLine;
27+
import picocli.CommandLine.Command;
28+
import picocli.CommandLine.Option;
29+
import picocli.CommandLine.Parameters;
30+
31+
@Command(name = "ProjectAccessTokenScript", mixinStandardHelpOptions = true, version = "ProjectAccessTokenScript 0.1", description = "Tests for GitLab4J")
32+
public class ProjectAccessTokenScript implements Callable<Integer> {
33+
34+
private static final String CONFIG_FILE_INITIAL_CONTENT = """
35+
GITLAB_URL=https://gitlab.com
36+
GITLAB_AUTH_VALUE=
37+
""";
38+
39+
@Parameters(index = "0", description = "action to execute", defaultValue = "LIST_PROJECT_ACCESS_TOKEN")
40+
private Action action;
41+
42+
@Option(names = { "-p", "--project" }, description = "project id or path")
43+
private String project;
44+
45+
@Option(names = { "-t", "--tokenId" }, description = "token id")
46+
private Long tokenId;
47+
48+
@Option(names = { "-n", "--tokenName" }, description = "token name")
49+
private String tokenName;
50+
51+
@Option(names = { "-e", "--expiresAt" }, description = "token expiration date")
52+
private Date expiresAt;
53+
54+
@Option(names = { "-s", "--scope" }, description = "token scopes")
55+
private List<ProjectAccessTokenScope> scopes = new ArrayList<>();
56+
57+
@Option(names = { "-a", "--accessLevel" }, description = "token access level")
58+
private AccessLevel accessLevel;
59+
60+
@Option(names = { "-c", "--config" }, description = "configuration file location")
61+
String configFile;
62+
63+
private static enum Action {
64+
LIST_PROJECT_ACCESS_TOKEN, GET_PROJECT_ACCESS_TOKEN, CREATE_PROJECT_ACCESS_TOKEN, ROTATE_PROJECT_ACCESS_TOKEN, REVOKE_PROJECT_ACCESS_TOKEN
65+
}
66+
67+
@Override
68+
public Integer call() throws Exception {
69+
Path file;
70+
if (configFile != null) {
71+
file = Paths.get(configFile);
72+
} else {
73+
file = configFile(Paths.get(""));
74+
}
75+
System.out.println("Reading config: " + file.toAbsolutePath());
76+
final Properties prop = configProperties(file);
77+
final String gitLabUrl = readProperty(prop, "GITLAB_URL", "https://gitlab.com");
78+
final String gitLabAuthValue = readProperty(prop, "GITLAB_AUTH_VALUE");
79+
80+
ensureExists(project, "project");
81+
82+
try (GitLabApi gitLabApi = new GitLabApi(gitLabUrl, gitLabAuthValue)) {
83+
switch (action) {
84+
case LIST_PROJECT_ACCESS_TOKEN:
85+
var tokens = gitLabApi.getProjectApi()
86+
.listProjectAccessTokens(idOrPath(project));
87+
System.out.println(tokens);
88+
break;
89+
case GET_PROJECT_ACCESS_TOKEN:
90+
ensureExists(tokenId, "tokenId");
91+
var token = gitLabApi.getProjectApi()
92+
.getProjectAccessToken(idOrPath(project), tokenId);
93+
System.out.println(token);
94+
break;
95+
case CREATE_PROJECT_ACCESS_TOKEN:
96+
var createdToken = gitLabApi.getProjectApi()
97+
.createProjectAccessToken(idOrPath(project), tokenName, scopes, expiresAt, accessLevelValue(accessLevel));
98+
System.out.println(createdToken);
99+
break;
100+
case ROTATE_PROJECT_ACCESS_TOKEN:
101+
var r = gitLabApi.getProjectApi()
102+
.rotateProjectAccessToken(idOrPath(project), getTokenId(gitLabApi), expiresAt);
103+
System.out.println(r);
104+
break;
105+
case REVOKE_PROJECT_ACCESS_TOKEN:
106+
gitLabApi.getProjectApi()
107+
.revokeProjectAccessToken(idOrPath(project), getTokenId(gitLabApi));
108+
break;
109+
default:
110+
throw new IllegalArgumentException("Unexpected value: " + action);
111+
}
112+
}
113+
return 0;
114+
}
115+
116+
private static Long accessLevelValue(AccessLevel value) {
117+
if (value == null) {
118+
return null;
119+
}
120+
return value.value.longValue();
121+
}
122+
123+
private Long getTokenId(GitLabApi gitLabApi) throws GitLabApiException {
124+
if (tokenId != null) {
125+
return tokenId;
126+
}
127+
ensureExists(tokenName, "tokenName");
128+
List<ProjectAccessToken> tokens = gitLabApi.getProjectApi()
129+
.listProjectAccessTokens(idOrPath(project));
130+
ProjectAccessToken token = tokens.stream()
131+
.filter(t -> Objects.equals(t.isRevoked(), Boolean.FALSE))
132+
.filter(t -> tokenName.equals(t.getName()))
133+
.findFirst()
134+
.orElseThrow(() -> new IllegalStateException("Could not find token with name '" + tokenName + "' in project '" + idOrPath(project) + "'"));
135+
return token.getId();
136+
}
137+
138+
private void ensureExists(Object value, String optionName) {
139+
if (value == null) {
140+
throw new IllegalStateException("--" + optionName + " must be set");
141+
}
142+
}
143+
144+
private Object idOrPath(String value) {
145+
if (value.matches("[0-9]+")) {
146+
return Long.valueOf(value);
147+
}
148+
return value;
149+
}
150+
151+
public static Properties configProperties(final Path configFile) {
152+
try (InputStream is = new FileInputStream(configFile.toFile())) {
153+
final Properties properties = new Properties();
154+
properties.load(is);
155+
return properties;
156+
} catch (final IOException e) {
157+
throw new IllegalStateException("Can not read config file", e);
158+
}
159+
}
160+
161+
public static Path configFile(final Path root) {
162+
final Path configFile = root.toAbsolutePath()
163+
.resolve("gitlab-config.properties");
164+
if (!Files.isRegularFile(configFile)) {
165+
try {
166+
Files.writeString(configFile, CONFIG_FILE_INITIAL_CONTENT);
167+
throw new IllegalStateException(String.format("Configuration file '%s' does not exist. An empty configuration file was created", configFile.toAbsolutePath()));
168+
} catch (final IOException e) {
169+
throw new IllegalStateException("Can not write initial config file", e);
170+
}
171+
}
172+
return configFile;
173+
}
174+
175+
public static String readProperty(final Properties p, final String key) {
176+
if (!p.containsKey(key)) {
177+
throw new IllegalStateException(String.format("Configuration file does not contains key '%s'", key));
178+
}
179+
final String value = p.getProperty(key);
180+
if (value == null || value.isBlank()) {
181+
throw new IllegalStateException(String.format("Key '%s' is not defined in configuration file", key));
182+
}
183+
return value;
184+
}
185+
186+
public static String readProperty(final Properties p, final String key, final String defaultValue) {
187+
return p.getProperty(key, defaultValue);
188+
}
189+
190+
public static void main(final String... args) {
191+
final int exitCode = new CommandLine(new ProjectAccessTokenScript()).execute(args);
192+
System.exit(exitCode);
193+
}
194+
}

0 commit comments

Comments
 (0)