From 86921283357e6ac963da7126566f1f69c3d4c9e0 Mon Sep 17 00:00:00 2001 From: Malte Meister Date: Fri, 9 Apr 2021 17:40:22 +0200 Subject: [PATCH] feat: implement createPersonalAccessToken renames ImpersonationToken > PersonalAccessToken and adds missing scopes, factors out common logic --- src/main/java/org/gitlab4j/api/UserApi.java | 73 ++++++++++++------- ...ionToken.java => PersonalAccessToken.java} | 6 +- .../org/gitlab4j/api/TestGitLabApiBeans.java | 4 +- .../java/org/gitlab4j/api/TestUserApi.java | 62 ++++++++++++---- 4 files changed, 100 insertions(+), 45 deletions(-) rename src/main/java/org/gitlab4j/api/models/{ImpersonationToken.java => PersonalAccessToken.java} (88%) diff --git a/src/main/java/org/gitlab4j/api/UserApi.java b/src/main/java/org/gitlab4j/api/UserApi.java index f734e5441..d14377b94 100644 --- a/src/main/java/org/gitlab4j/api/UserApi.java +++ b/src/main/java/org/gitlab4j/api/UserApi.java @@ -14,8 +14,8 @@ import org.gitlab4j.api.GitLabApi.ApiVersion; import org.gitlab4j.api.models.CustomAttribute; import org.gitlab4j.api.models.Email; -import org.gitlab4j.api.models.ImpersonationToken; -import org.gitlab4j.api.models.ImpersonationToken.Scope; +import org.gitlab4j.api.models.PersonalAccessToken; +import org.gitlab4j.api.models.PersonalAccessToken.Scope; import org.gitlab4j.api.models.SshKey; import org.gitlab4j.api.models.User; import org.gitlab4j.api.utils.EmailChecker; @@ -829,7 +829,7 @@ public void deleteSshKey(Object userIdOrUsername, Integer keyId) throws GitLabAp * @return a list of a specified user's impersonation tokens * @throws GitLabApiException if any exception occurs */ - public List getImpersonationTokens(Object userIdOrUsername) throws GitLabApiException { + public List getImpersonationTokens(Object userIdOrUsername) throws GitLabApiException { return (getImpersonationTokens(userIdOrUsername, null)); } @@ -843,12 +843,12 @@ public List getImpersonationTokens(Object userIdOrUsername) * @return a list of a specified user's impersonation tokens * @throws GitLabApiException if any exception occurs */ - public List getImpersonationTokens(Object userIdOrUsername, ImpersonationState state) throws GitLabApiException { + public List getImpersonationTokens(Object userIdOrUsername, ImpersonationState state) throws GitLabApiException { GitLabApiForm formData = new GitLabApiForm() .withParam("state", state) .withParam(PER_PAGE_PARAM, getDefaultPerPage()); Response response = get(Response.Status.OK, formData.asMap(), "users", getUserIdOrUsername(userIdOrUsername), "impersonation_tokens"); - return (response.readEntity(new GenericType>() {})); + return (response.readEntity(new GenericType>() {})); } /** @@ -861,14 +861,14 @@ public List getImpersonationTokens(Object userIdOrUsername, * @return the specified impersonation token * @throws GitLabApiException if any exception occurs */ - public ImpersonationToken getImpersonationToken(Object userIdOrUsername, Integer tokenId) throws GitLabApiException { + public PersonalAccessToken getImpersonationToken(Object userIdOrUsername, Integer tokenId) throws GitLabApiException { if (tokenId == null) { throw new RuntimeException("tokenId cannot be null"); } Response response = get(Response.Status.OK, null, "users", getUserIdOrUsername(userIdOrUsername), "impersonation_tokens", tokenId); - return (response.readEntity(ImpersonationToken.class)); + return (response.readEntity(PersonalAccessToken.class)); } /** @@ -880,7 +880,7 @@ public ImpersonationToken getImpersonationToken(Object userIdOrUsername, Integer * @param tokenId the impersonation token ID to get * @return the specified impersonation token as an Optional instance */ - public Optional getOptionalImpersonationToken(Object userIdOrUsername, Integer tokenId) { + public Optional getOptionalImpersonationToken(Object userIdOrUsername, Integer tokenId) { try { return (Optional.ofNullable(getImpersonationToken(userIdOrUsername, tokenId))); } catch (GitLabApiException glae) { @@ -897,25 +897,11 @@ public Optional getOptionalImpersonationToken(Object userIdO * @param name the name of the impersonation token, required * @param expiresAt the expiration date of the impersonation token, optional * @param scopes an array of scopes of the impersonation token - * @return the created ImpersonationToken instance + * @return the created PersonalAccessToken instance * @throws GitLabApiException if any exception occurs */ - public ImpersonationToken createImpersonationToken(Object userIdOrUsername, String name, Date expiresAt, Scope[] scopes) throws GitLabApiException { - - if (scopes == null || scopes.length == 0) { - throw new RuntimeException("scopes cannot be null or empty"); - } - - GitLabApiForm formData = new GitLabApiForm() - .withParam("name", name, true) - .withParam("expires_at", expiresAt); - - for (Scope scope : scopes) { - formData.withParam("scopes[]", scope.toString()); - } - - Response response = post(Response.Status.CREATED, formData, "users", getUserIdOrUsername(userIdOrUsername), "impersonation_tokens"); - return (response.readEntity(ImpersonationToken.class)); + public PersonalAccessToken createImpersonationToken(Object userIdOrUsername, String name, Date expiresAt, Scope[] scopes) throws GitLabApiException { + return createPersonalAccessTokenOrImpersonationToken(userIdOrUsername, name, expiresAt, scopes, true); } /** @@ -937,6 +923,43 @@ public void revokeImpersonationToken(Object userIdOrUsername, Integer tokenId) t delete(expectedStatus, null, "users", getUserIdOrUsername(userIdOrUsername), "impersonation_tokens", tokenId); } + /** + * Create a personal access token. Available only for admin users. + * + *
GitLab Endpoint: POST /users/:user_id/personal_access_tokens
+ * + * @param userIdOrUsername the user in the form of an Integer(ID), String(username), or User instance + * @param name the name of the personal access token, required + * @param expiresAt the expiration date of the personal access token, optional + * @param scopes an array of scopes of the personal access token + * @return the created PersonalAccessToken instance + * @throws GitLabApiException if any exception occurs + */ + public PersonalAccessToken createPersonalAccessToken(Object userIdOrUsername, String name, Date expiresAt, Scope[] scopes) throws GitLabApiException { + return createPersonalAccessTokenOrImpersonationToken(userIdOrUsername, name, expiresAt, scopes, false); + } + + // as per https://docs.gitlab.com/ee/api/README.html#impersonation-tokens, impersonation tokens are a type of + // personal access token + private PersonalAccessToken createPersonalAccessTokenOrImpersonationToken(Object userIdOrUsername, String name, Date expiresAt, Scope[] scopes, boolean impersonation) throws GitLabApiException { + + if (scopes == null || scopes.length == 0) { + throw new RuntimeException("scopes cannot be null or empty"); + } + + GitLabApiForm formData = new GitLabApiForm() + .withParam("name", name, true) + .withParam("expires_at", expiresAt); + + for (Scope scope : scopes) { + formData.withParam("scopes[]", scope.toString()); + } + + String tokenTypePathArg = impersonation ? "impersonation_tokens" : "personal_access_tokens"; + Response response = post(Response.Status.CREATED, formData, "users", getUserIdOrUsername(userIdOrUsername), tokenTypePathArg); + return (response.readEntity(PersonalAccessToken.class)); + } + /** * Populate the REST form with data from the User instance. * diff --git a/src/main/java/org/gitlab4j/api/models/ImpersonationToken.java b/src/main/java/org/gitlab4j/api/models/PersonalAccessToken.java similarity index 88% rename from src/main/java/org/gitlab4j/api/models/ImpersonationToken.java rename to src/main/java/org/gitlab4j/api/models/PersonalAccessToken.java index 4e841aa84..194b05b4f 100644 --- a/src/main/java/org/gitlab4j/api/models/ImpersonationToken.java +++ b/src/main/java/org/gitlab4j/api/models/PersonalAccessToken.java @@ -9,12 +9,12 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonValue; -public class ImpersonationToken { +public class PersonalAccessToken { - /** Enum to specify the scope of an ImpersonationToken. */ + /** Enum to specify the scope of a PersonalAccessToken. */ public enum Scope { - API, READ_USER, READ_REPOSITORY, WRITE_REPOSITORY, READ_REGISTRY, SUDO; + API, READ_API, READ_USER, READ_REPOSITORY, WRITE_REPOSITORY, READ_REGISTRY, WRITE_REGISTRY, SUDO; private static JacksonJsonEnumHelper enumHelper = new JacksonJsonEnumHelper<>(Scope.class); diff --git a/src/test/java/org/gitlab4j/api/TestGitLabApiBeans.java b/src/test/java/org/gitlab4j/api/TestGitLabApiBeans.java index 93e774e5e..329f16d7e 100644 --- a/src/test/java/org/gitlab4j/api/TestGitLabApiBeans.java +++ b/src/test/java/org/gitlab4j/api/TestGitLabApiBeans.java @@ -66,7 +66,7 @@ import org.gitlab4j.api.models.GpgSignature; import org.gitlab4j.api.models.Group; import org.gitlab4j.api.models.HealthCheckInfo; -import org.gitlab4j.api.models.ImpersonationToken; +import org.gitlab4j.api.models.PersonalAccessToken; import org.gitlab4j.api.models.ImportStatus; import org.gitlab4j.api.models.Issue; import org.gitlab4j.api.models.IssueLink; @@ -671,7 +671,7 @@ public void testUser() throws Exception { @Test public void testImpersonationToken() throws Exception { - ImpersonationToken token = unmarshalResource(ImpersonationToken.class, "impersonation-token.json"); + PersonalAccessToken token = unmarshalResource(PersonalAccessToken.class, "impersonation-token.json"); assertTrue(compareJson(token, "impersonation-token.json")); } diff --git a/src/test/java/org/gitlab4j/api/TestUserApi.java b/src/test/java/org/gitlab4j/api/TestUserApi.java index e9e3f889c..83c07611a 100644 --- a/src/test/java/org/gitlab4j/api/TestUserApi.java +++ b/src/test/java/org/gitlab4j/api/TestUserApi.java @@ -19,8 +19,8 @@ import javax.ws.rs.core.Response; import org.gitlab4j.api.models.Email; -import org.gitlab4j.api.models.ImpersonationToken; -import org.gitlab4j.api.models.ImpersonationToken.Scope; +import org.gitlab4j.api.models.PersonalAccessToken; +import org.gitlab4j.api.models.PersonalAccessToken.Scope; import org.gitlab4j.api.models.SshKey; import org.gitlab4j.api.models.User; import org.gitlab4j.api.models.Version; @@ -56,7 +56,8 @@ public class TestUserApi extends AbstractIntegrationTest { private static final String TEST_BLOCK_USERNAME = HelperUtils.getProperty(BLOCK_USERNAME_KEY); private static final String TEST_SUDO_AS_USERNAME = HelperUtils.getProperty(SUDO_AS_USERNAME_KEY); - private static final String TEST_IMPERSONATION_TOKEN_NAME = "token1"; + private static final String TEST_IMPERSONATION_TOKEN_NAME = "ipt_1"; + private static final String TEST_PERSONAL_ACCESS_TOKEN_NAME = "pat_1"; private static final String TEST_SSH_KEY = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC3rWzl/oPAD+Em2iGTmR81HcYZsopvnKp7jelI4XS91fT1NjCRrGsxf5Mw/" + "KnmtBjhk+kQjkhIrnsBDcs6DZWtNcHJtyWJZrYsfxMTqWCaQv+OTRwVboqS2pmPcbK3gizUd5GCLFTKbg4OMpdywTwi6NAPwQ" + @@ -268,12 +269,12 @@ public void testCreateImpersonationToken() throws GitLabApiException, ParseExcep User user = gitLabApi.getUserApi().getCurrentUser(); - // NOTE: READ_REGISTRY scope is left out because the GitLab server docker instance does not have the - // registry configured and the test would thus fail. - Scope[] scopes = {Scope.API, Scope.READ_USER, Scope.READ_REPOSITORY, Scope.WRITE_REPOSITORY, Scope.SUDO}; + // NOTE: READ_REGISTRY & WRITE_REGISTRY scopes are left out because the GitLab server docker instance does not + // have the registry configured and the test would thus fail. + Scope[] scopes = {Scope.API, Scope.READ_API, Scope.READ_USER, Scope.READ_REPOSITORY, Scope.WRITE_REPOSITORY, Scope.SUDO}; Date expiresAt = ISO8601.toDate("2018-01-01T00:00:00Z"); - ImpersonationToken token = null; + PersonalAccessToken token = null; try { token = gitLabApi.getUserApi().createImpersonationToken(user, TEST_IMPERSONATION_TOKEN_NAME, expiresAt, scopes); @@ -299,13 +300,13 @@ public void testGetOptionalImpersonationToken() throws GitLabApiException, Parse Scope[] scopes = {Scope.API, Scope.READ_USER}; Date expiresAt = ISO8601.toDate("2018-01-01T00:00:00Z"); - ImpersonationToken token = null; + PersonalAccessToken token = null; try { token = gitLabApi.getUserApi().createImpersonationToken(user.getId(), TEST_IMPERSONATION_TOKEN_NAME, expiresAt, scopes); assertNotNull(token); - Optional optional = gitLabApi.getUserApi().getOptionalImpersonationToken(user.getId(), token.getId()); + Optional optional = gitLabApi.getUserApi().getOptionalImpersonationToken(user.getId(), token.getId()); assertTrue(optional.isPresent()); assertEquals(token.getId(), optional.get().getId()); gitLabApi.getUserApi().revokeImpersonationToken(user.getId(), token.getId()); @@ -327,16 +328,16 @@ public void testGetImpersonationTokens() throws GitLabApiException, ParseExcepti User user = gitLabApi.getUserApi().getCurrentUser(); Scope[] scopes = {Scope.API, Scope.READ_USER}; Date expiresAt = ISO8601.toDate("2018-01-01T00:00:00Z"); - ImpersonationToken createdToken = gitLabApi.getUserApi().createImpersonationToken(user.getId(), TEST_IMPERSONATION_TOKEN_NAME, expiresAt, scopes); + PersonalAccessToken createdToken = gitLabApi.getUserApi().createImpersonationToken(user.getId(), TEST_IMPERSONATION_TOKEN_NAME, expiresAt, scopes); assertNotNull(createdToken); - ImpersonationToken token = gitLabApi.getUserApi().getImpersonationToken(user.getId(), createdToken.getId()); + PersonalAccessToken token = gitLabApi.getUserApi().getImpersonationToken(user.getId(), createdToken.getId()); assertNotNull(token); assertEquals(createdToken.getId(), token.getId()); assertEquals(TEST_IMPERSONATION_TOKEN_NAME, token.getName()); assertEquals(createdToken.getExpiresAt(), token.getExpiresAt()); - List tokens = gitLabApi.getUserApi().getImpersonationTokens(user.getId()); + List tokens = gitLabApi.getUserApi().getImpersonationTokens(user.getId()); assertNotNull(tokens); assertTrue(tokens.size() > 0); @@ -344,15 +345,15 @@ public void testGetImpersonationTokens() throws GitLabApiException, ParseExcepti } @Test - public void testDeleteImpersonationTokens() throws GitLabApiException, ParseException { + public void testRevokeImpersonationToken() throws GitLabApiException, ParseException { User user = gitLabApi.getUserApi().getCurrentUser(); Scope[] scopes = {Scope.API, Scope.READ_USER}; Date expiresAt = ISO8601.toDate("2018-01-01T00:00:00Z"); - ImpersonationToken createdToken = gitLabApi.getUserApi().createImpersonationToken(user.getId(), TEST_IMPERSONATION_TOKEN_NAME + "a", expiresAt, scopes); + PersonalAccessToken createdToken = gitLabApi.getUserApi().createImpersonationToken(user.getId(), TEST_IMPERSONATION_TOKEN_NAME + "a", expiresAt, scopes); assertNotNull(createdToken); - ImpersonationToken token = gitLabApi.getUserApi().getImpersonationToken(user.getId(), createdToken.getId()); + PersonalAccessToken token = gitLabApi.getUserApi().getImpersonationToken(user.getId(), createdToken.getId()); assertNotNull(token); assertEquals(createdToken.getId(), token.getId()); @@ -361,6 +362,37 @@ public void testDeleteImpersonationTokens() throws GitLabApiException, ParseExce assertFalse(token.getActive()); } + @Test + public void testCreatePersonalAccessToken() throws GitLabApiException, ParseException { + + User user = gitLabApi.getUserApi().getCurrentUser(); + + // NOTE: READ_REGISTRY & WRITE_REGISTRY scopes are left out because the GitLab server docker instance does not + // have the registry configured and the test would thus fail. + Scope[] scopes = {Scope.API, Scope.READ_API, Scope.READ_USER, Scope.READ_REPOSITORY, Scope.WRITE_REPOSITORY, Scope.SUDO}; + Date expiresAt = ISO8601.toDate("2018-01-01T00:00:00Z"); + + PersonalAccessToken token = null; + try { + + token = gitLabApi.getUserApi().createPersonalAccessToken(user, TEST_PERSONAL_ACCESS_TOKEN_NAME, expiresAt, scopes); + + assertNotNull(token); + assertNotNull(token.getId()); + assertEquals(TEST_PERSONAL_ACCESS_TOKEN_NAME, token.getName()); + assertEquals(expiresAt.getTime(), token.getExpiresAt().getTime()); + assertEquals(scopes.length, token.getScopes().size()); + assertThat(token.getScopes(), contains(scopes)); + + } finally { + if (user != null && token != null) { + // GitLab doesn't have this API method yet - not a big issue since multiple tokens with the same name + // can be created. Note that you won't see a token in the UI unless the expiry date is in the future. +// gitLabApi.getUserApi().revokePersonalAccessToken(user.getId(), token.getId()); + } + } + } + @Test public void testGetSshKeys() throws GitLabApiException {