From 975758278cf78e56f8f536a05f211ca4eb1c017a Mon Sep 17 00:00:00 2001 From: Lukasz Jarocki Date: Tue, 5 Apr 2022 12:33:05 +0200 Subject: [PATCH] SONAR-16227 changed generation of tokens in sonarqube to include identifier --- .../server/usertoken/TokenGenerator.java | 2 +- .../server/usertoken/TokenGeneratorImpl.java | 15 +++++--- .../org/sonar/server/usertoken/TokenType.java | 34 +++++++++++++++++++ .../usertoken/TokenGeneratorImplTest.java | 17 +++++++--- .../sonar/server/badge/ws/TokenAction.java | 4 ++- .../server/badge/ws/TokenRenewAction.java | 3 +- .../server/usertoken/ws/GenerateAction.java | 3 +- .../server/badge/ws/TokenActionTest.java | 7 ++-- .../server/badge/ws/TokenRenewActionTest.java | 5 +-- .../usertoken/ws/GenerateActionTest.java | 3 +- 10 files changed, 74 insertions(+), 19 deletions(-) create mode 100644 server/sonar-webserver-auth/src/main/java/org/sonar/server/usertoken/TokenType.java diff --git a/server/sonar-webserver-auth/src/main/java/org/sonar/server/usertoken/TokenGenerator.java b/server/sonar-webserver-auth/src/main/java/org/sonar/server/usertoken/TokenGenerator.java index 8891969dea3..b41644530bf 100644 --- a/server/sonar-webserver-auth/src/main/java/org/sonar/server/usertoken/TokenGenerator.java +++ b/server/sonar-webserver-auth/src/main/java/org/sonar/server/usertoken/TokenGenerator.java @@ -35,7 +35,7 @@ public interface TokenGenerator { * must not contain colon character ":". * */ - String generate(); + String generate(TokenType tokenType); /** * Hash a token.
diff --git a/server/sonar-webserver-auth/src/main/java/org/sonar/server/usertoken/TokenGeneratorImpl.java b/server/sonar-webserver-auth/src/main/java/org/sonar/server/usertoken/TokenGeneratorImpl.java index 01e0d8f5534..235a2fb0285 100644 --- a/server/sonar-webserver-auth/src/main/java/org/sonar/server/usertoken/TokenGeneratorImpl.java +++ b/server/sonar-webserver-auth/src/main/java/org/sonar/server/usertoken/TokenGeneratorImpl.java @@ -24,12 +24,19 @@ import org.apache.commons.codec.binary.Hex; import org.apache.commons.codec.digest.DigestUtils; public class TokenGeneratorImpl implements TokenGenerator { + + private static final String SONARQUBE_TOKEN_PREFIX = "sq"; + @Override - public String generate() { + public String generate(TokenType tokenType) { SecureRandom random = new SecureRandom(); - byte[] bytes = new byte[20]; - random.nextBytes(bytes); - return Hex.encodeHexString(bytes); + byte[] randomBytes = new byte[20]; + random.nextBytes(randomBytes); + return buildIdentifiablePartOfToken(tokenType) + Hex.encodeHexString(randomBytes); + } + + private static String buildIdentifiablePartOfToken(TokenType tokenType) { + return SONARQUBE_TOKEN_PREFIX + tokenType.getIdentifier() + "_"; } @Override diff --git a/server/sonar-webserver-auth/src/main/java/org/sonar/server/usertoken/TokenType.java b/server/sonar-webserver-auth/src/main/java/org/sonar/server/usertoken/TokenType.java new file mode 100644 index 00000000000..8251bc8015c --- /dev/null +++ b/server/sonar-webserver-auth/src/main/java/org/sonar/server/usertoken/TokenType.java @@ -0,0 +1,34 @@ +/* + * SonarQube + * Copyright (C) 2009-2022 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.server.usertoken; + +public enum TokenType { + USER_TOKEN("u"); + + private final String identifier; + + TokenType(String identifier) { + this.identifier = identifier; + } + + public String getIdentifier() { + return identifier; + } +} diff --git a/server/sonar-webserver-auth/src/test/java/org/sonar/server/usertoken/TokenGeneratorImplTest.java b/server/sonar-webserver-auth/src/test/java/org/sonar/server/usertoken/TokenGeneratorImplTest.java index 3469191331e..2a45c30a4a5 100644 --- a/server/sonar-webserver-auth/src/test/java/org/sonar/server/usertoken/TokenGeneratorImplTest.java +++ b/server/sonar-webserver-auth/src/test/java/org/sonar/server/usertoken/TokenGeneratorImplTest.java @@ -24,23 +24,30 @@ import org.junit.Test; import static org.assertj.core.api.Assertions.assertThat; public class TokenGeneratorImplTest { - TokenGeneratorImpl underTest = new TokenGeneratorImpl(); + private TokenGeneratorImpl underTest = new TokenGeneratorImpl(); @Test public void generate_different_tokens() { // this test is not enough to ensure that generated strings are unique, // but it still does a simple and stupid verification - String firstToken = underTest.generate(); - String secondToken = underTest.generate(); + String firstToken = underTest.generate(TokenType.USER_TOKEN); + String secondToken = underTest.generate(TokenType.USER_TOKEN); assertThat(firstToken) .isNotEqualTo(secondToken) - .hasSize(40); + .hasSize(44); + } + + @Test + public void generate_tokenShouldHaveSonarQubePrefix() { + String token = underTest.generate(TokenType.USER_TOKEN); + + assertThat(token).matches("squ_.*"); } @Test public void token_does_not_contain_colon() { - assertThat(underTest.generate()).doesNotContain(":"); + assertThat(underTest.generate(TokenType.USER_TOKEN)).doesNotContain(":"); } @Test diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/badge/ws/TokenAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/badge/ws/TokenAction.java index 1c46bf86ecf..6298e21679a 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/badge/ws/TokenAction.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/badge/ws/TokenAction.java @@ -31,6 +31,7 @@ import org.sonar.db.project.ProjectDto; import org.sonar.db.project.ProjectBadgeTokenDto; import org.sonar.server.user.UserSession; import org.sonar.server.usertoken.TokenGenerator; +import org.sonar.server.usertoken.TokenType; import org.sonarqube.ws.ProjectBadgeToken.TokenWsResponse; import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001; @@ -79,7 +80,8 @@ public class TokenAction implements ProjectBadgesWsAction { ProjectBadgeTokenDto projectBadgeTokenDto = dbClient.projectBadgeTokenDao().selectTokenByProject(dbSession, projectDto); if (projectBadgeTokenDto == null) { - projectBadgeTokenDto = dbClient.projectBadgeTokenDao().insert(dbSession, tokenGenerator.generate(), projectDto, userSession.getUuid(), userSession.getLogin()); + projectBadgeTokenDto = dbClient.projectBadgeTokenDao().insert(dbSession, tokenGenerator.generate(TokenType.USER_TOKEN), + projectDto, userSession.getUuid(), userSession.getLogin()); dbSession.commit(); } diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/badge/ws/TokenRenewAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/badge/ws/TokenRenewAction.java index 777b147da28..23905c9d668 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/badge/ws/TokenRenewAction.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/badge/ws/TokenRenewAction.java @@ -29,6 +29,7 @@ import org.sonar.db.DbSession; import org.sonar.db.project.ProjectDto; import org.sonar.server.user.UserSession; import org.sonar.server.usertoken.TokenGenerator; +import org.sonar.server.usertoken.TokenType; import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001; @@ -72,7 +73,7 @@ public class TokenRenewAction implements ProjectBadgesWsAction { ProjectDto projectDto = dbClient.projectDao().selectProjectByKey(dbSession, projectKey).orElseThrow(() -> new IllegalArgumentException("project not found")); userSession.checkProjectPermission(UserRole.ADMIN, projectDto); - String newGeneratedToken = tokenGenerator.generate(); + String newGeneratedToken = tokenGenerator.generate(TokenType.USER_TOKEN); dbClient.projectBadgeTokenDao().upsert(dbSession, newGeneratedToken, projectDto, userSession.getUuid(), userSession.getLogin()); dbSession.commit(); } diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/usertoken/ws/GenerateAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/usertoken/ws/GenerateAction.java index f031a9de61d..8f79de5619a 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/usertoken/ws/GenerateAction.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/usertoken/ws/GenerateAction.java @@ -29,6 +29,7 @@ import org.sonar.db.user.UserDto; import org.sonar.db.user.UserTokenDto; import org.sonar.server.exceptions.ServerException; import org.sonar.server.usertoken.TokenGenerator; +import org.sonar.server.usertoken.TokenType; import org.sonarqube.ws.UserTokens; import org.sonarqube.ws.UserTokens.GenerateWsResponse; @@ -90,7 +91,7 @@ public class GenerateAction implements UserTokensWsAction { UserDto user = userTokenSupport.getUser(dbSession, request); checkTokenDoesNotAlreadyExists(dbSession, user, name); - String token = tokenGenerator.generate(); + String token = tokenGenerator.generate(TokenType.USER_TOKEN); String tokenHash = hashToken(dbSession, token); UserTokenDto userTokenDto = insertTokenInDb(dbSession, user, name, tokenHash); return buildResponse(userTokenDto, token, user); diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/badge/ws/TokenActionTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/badge/ws/TokenActionTest.java index aaf65087c8a..46fe9733d4b 100644 --- a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/badge/ws/TokenActionTest.java +++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/badge/ws/TokenActionTest.java @@ -29,6 +29,7 @@ import org.sonar.db.component.ComponentDto; import org.sonar.server.exceptions.ForbiddenException; import org.sonar.server.tester.UserSessionRule; import org.sonar.server.usertoken.TokenGenerator; +import org.sonar.server.usertoken.TokenType; import org.sonar.server.ws.TestRequest; import org.sonar.server.ws.TestResponse; import org.sonar.server.ws.WsActionTester; @@ -73,7 +74,7 @@ public class TokenActionTest { public void should_generate_token() { ComponentDto project = db.components().insertPrivateProject(); userSession.logIn().addProjectPermission(UserRole.USER, project); - when(tokenGenerator.generate()).thenReturn("generated_token"); + when(tokenGenerator.generate(TokenType.USER_TOKEN)).thenReturn("generated_token"); TestResponse response = ws.newRequest().setParam("project", project.getKey()).execute(); @@ -84,14 +85,14 @@ public class TokenActionTest { public void should_reuse_generated_token() { ComponentDto project = db.components().insertPrivateProject(); userSession.logIn().addProjectPermission(UserRole.USER, project); - when(tokenGenerator.generate()).thenReturn("generated_token"); + when(tokenGenerator.generate(TokenType.USER_TOKEN)).thenReturn("generated_token"); // first call, generating the token TestResponse firstResponse = ws.newRequest().setParam("project", project.getKey()).execute(); firstResponse.assertJson("{\"token\":\"generated_token\"}"); // 2nd call, reusing the existing token - when(tokenGenerator.generate()).thenReturn("never_generated_token"); + when(tokenGenerator.generate(TokenType.USER_TOKEN)).thenReturn("never_generated_token"); TestResponse secondResponse = ws.newRequest().setParam("project", project.getKey()).execute(); secondResponse.assertJson("{\"token\":\"generated_token\"}"); diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/badge/ws/TokenRenewActionTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/badge/ws/TokenRenewActionTest.java index f070742cd9f..65b2ad86325 100644 --- a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/badge/ws/TokenRenewActionTest.java +++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/badge/ws/TokenRenewActionTest.java @@ -33,6 +33,7 @@ import org.sonar.db.project.ProjectDto; import org.sonar.server.exceptions.ForbiddenException; import org.sonar.server.tester.UserSessionRule; import org.sonar.server.usertoken.TokenGenerator; +import org.sonar.server.usertoken.TokenType; import org.sonar.server.ws.TestRequest; import org.sonar.server.ws.TestResponse; import org.sonar.server.ws.WsActionTester; @@ -86,7 +87,7 @@ public class TokenRenewActionTest { public void should_add_token_when_no_token_yet_and_return_204() { ProjectDto project = db.components().insertPrivateProjectDto(); userSession.logIn().addProjectPermission(UserRole.ADMIN, project); - when(tokenGenerator.generate()).thenReturn("generated_token"); + when(tokenGenerator.generate(TokenType.USER_TOKEN)).thenReturn("generated_token"); TestResponse response = ws.newRequest().setParam("project", project.getKey()).execute(); @@ -100,7 +101,7 @@ public class TokenRenewActionTest { public void should_replace_existing_token_when__token_already_present_and_update_update_at() { ProjectDto project = db.components().insertPrivateProjectDto(); userSession.logIn().addProjectPermission(UserRole.ADMIN, project); - when(tokenGenerator.generate()).thenReturn("generated_token"); + when(tokenGenerator.generate(TokenType.USER_TOKEN)).thenReturn("generated_token"); ws.newRequest().setParam("project", project.getKey()).execute(); //inserting first token with updated at 1000 diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/usertoken/ws/GenerateActionTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/usertoken/ws/GenerateActionTest.java index ad834144e99..d000a55914c 100644 --- a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/usertoken/ws/GenerateActionTest.java +++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/usertoken/ws/GenerateActionTest.java @@ -34,6 +34,7 @@ import org.sonar.server.exceptions.ServerException; import org.sonar.server.exceptions.UnauthorizedException; import org.sonar.server.tester.UserSessionRule; import org.sonar.server.usertoken.TokenGenerator; +import org.sonar.server.usertoken.TokenType; import org.sonar.server.ws.TestRequest; import org.sonar.server.ws.WsActionTester; import org.sonarqube.ws.MediaTypes; @@ -64,7 +65,7 @@ public class GenerateActionTest { @Before public void setUp() { - when(tokenGenerator.generate()).thenReturn("123456789"); + when(tokenGenerator.generate(TokenType.USER_TOKEN)).thenReturn("123456789"); when(tokenGenerator.hash(anyString())).thenReturn("987654321"); } -- 2.39.5