aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLukasz Jarocki <lukasz.jarocki@sonarsource.com>2021-11-15 16:06:52 +0100
committersonartech <sonartech@sonarsource.com>2021-11-16 20:03:54 +0000
commit814f9f9f0f46f64b8d0029f8815ddfaab9423639 (patch)
tree0950d8eb7abfbde461cd4360f27ff137c4ea8219
parent5396d999bfe805baa4ff7671422df88931af7a8d (diff)
downloadsonarqube-814f9f9f0f46f64b8d0029f8815ddfaab9423639.tar.gz
sonarqube-814f9f9f0f46f64b8d0029f8815ddfaab9423639.zip
SONAR-13427 Added a new endpoint to renew the project badge token
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/audit/AuditPersister.java2
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/audit/NoOpAuditPersister.java5
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/project/ProjectBadgeTokenDao.java10
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/project/ProjectBadgeTokenMapper.java2
-rw-r--r--server/sonar-db-dao/src/main/resources/org/sonar/db/project/ProjectBadgeTokenMapper.xml9
-rw-r--r--server/sonar-db-dao/src/test/java/org/sonar/db/project/ProjectBadgeTokenDaoTest.java59
-rw-r--r--server/sonar-webserver-webapi/src/main/java/org/sonar/server/badge/ws/ProjectBadgesWs.java2
-rw-r--r--server/sonar-webserver-webapi/src/main/java/org/sonar/server/badge/ws/ProjectBadgesWsModule.java3
-rw-r--r--server/sonar-webserver-webapi/src/main/java/org/sonar/server/badge/ws/TokenRenewAction.java81
-rw-r--r--server/sonar-webserver-webapi/src/test/java/org/sonar/server/badge/ws/TokenRenewActionTest.java116
10 files changed, 280 insertions, 9 deletions
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/audit/AuditPersister.java b/server/sonar-db-dao/src/main/java/org/sonar/db/audit/AuditPersister.java
index ac41ffe6e36..03657a8016f 100644
--- a/server/sonar-db-dao/src/main/java/org/sonar/db/audit/AuditPersister.java
+++ b/server/sonar-db-dao/src/main/java/org/sonar/db/audit/AuditPersister.java
@@ -74,6 +74,8 @@ public interface AuditPersister {
void addProjectBadgeToken(DbSession dbSession, ProjectBadgeTokenNewValue newValue);
+ void updateProjectBadgeToken(DbSession session, ProjectBadgeTokenNewValue projectBadgeTokenNewValue);
+
void updateUserToken(DbSession dbSession, UserTokenNewValue newValue);
void deleteUserToken(DbSession dbSession, UserTokenNewValue newValue);
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/audit/NoOpAuditPersister.java b/server/sonar-db-dao/src/main/java/org/sonar/db/audit/NoOpAuditPersister.java
index 1923b0cca13..30c16d31a95 100644
--- a/server/sonar-db-dao/src/main/java/org/sonar/db/audit/NoOpAuditPersister.java
+++ b/server/sonar-db-dao/src/main/java/org/sonar/db/audit/NoOpAuditPersister.java
@@ -120,6 +120,11 @@ public class NoOpAuditPersister implements AuditPersister {
}
@Override
+ public void updateProjectBadgeToken(DbSession session, ProjectBadgeTokenNewValue projectBadgeTokenNewValue) {
+ // no op
+ }
+
+ @Override
public void updateUserToken(DbSession dbSession, UserTokenNewValue newValue) {
// no op
}
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/project/ProjectBadgeTokenDao.java b/server/sonar-db-dao/src/main/java/org/sonar/db/project/ProjectBadgeTokenDao.java
index 8c9758f1845..9565bf0a0a7 100644
--- a/server/sonar-db-dao/src/main/java/org/sonar/db/project/ProjectBadgeTokenDao.java
+++ b/server/sonar-db-dao/src/main/java/org/sonar/db/project/ProjectBadgeTokenDao.java
@@ -49,6 +49,15 @@ public class ProjectBadgeTokenDao implements Dao {
return projectBadgeTokenDto;
}
+ public void upsert(DbSession session, String token, ProjectDto projectDto, String userUuid, String userLogin) {
+ if(selectTokenByProject(session, projectDto) == null) {
+ insert(session, token, projectDto, userUuid, userLogin);
+ } else {
+ mapper(session).update(token, projectDto.getUuid(), system2.now());
+ auditPersister.updateProjectBadgeToken(session, new ProjectBadgeTokenNewValue(projectDto.getKey(), userUuid, userLogin));
+ }
+ }
+
private static ProjectBadgeTokenMapper mapper(DbSession session) {
return session.getMapper(ProjectBadgeTokenMapper.class);
}
@@ -56,6 +65,5 @@ public class ProjectBadgeTokenDao implements Dao {
@CheckForNull
public ProjectBadgeTokenDto selectTokenByProject(DbSession session, ProjectDto projectDto) {
return mapper(session).selectTokenByProjectUuid(projectDto.getUuid());
-
}
}
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/project/ProjectBadgeTokenMapper.java b/server/sonar-db-dao/src/main/java/org/sonar/db/project/ProjectBadgeTokenMapper.java
index 3f8cda7408c..55dd6106267 100644
--- a/server/sonar-db-dao/src/main/java/org/sonar/db/project/ProjectBadgeTokenMapper.java
+++ b/server/sonar-db-dao/src/main/java/org/sonar/db/project/ProjectBadgeTokenMapper.java
@@ -26,6 +26,8 @@ public interface ProjectBadgeTokenMapper {
void insert(ProjectBadgeTokenDto projectBadgeTokenDto);
+ int update(@Param("token") String token, @Param("projectUuid") String projectUuid, @Param("updatedAt") long updatedAt);
+
@CheckForNull
ProjectBadgeTokenDto selectTokenByProjectUuid(@Param("projectUuid") String projectUuid);
}
diff --git a/server/sonar-db-dao/src/main/resources/org/sonar/db/project/ProjectBadgeTokenMapper.xml b/server/sonar-db-dao/src/main/resources/org/sonar/db/project/ProjectBadgeTokenMapper.xml
index 59967a17741..739fd28f5ab 100644
--- a/server/sonar-db-dao/src/main/resources/org/sonar/db/project/ProjectBadgeTokenMapper.xml
+++ b/server/sonar-db-dao/src/main/resources/org/sonar/db/project/ProjectBadgeTokenMapper.xml
@@ -10,6 +10,15 @@
p.updated_at as updatedAt
</sql>
+ <update id="update" parameterType="map" useGeneratedKeys="false">
+ update project_badge_token
+ set
+ token = #{token, jdbcType=VARCHAR},
+ updated_at = #{updatedAt, jdbcType=BIGINT}
+ where
+ project_uuid = #{projectUuid, jdbcType=VARCHAR}
+ </update>
+
<insert id="insert" parameterType="ProjectBadgeToken">
INSERT INTO project_badge_token (
uuid,
diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/project/ProjectBadgeTokenDaoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/project/ProjectBadgeTokenDaoTest.java
index 6f99c2cdb13..1e50c8c337b 100644
--- a/server/sonar-db-dao/src/test/java/org/sonar/db/project/ProjectBadgeTokenDaoTest.java
+++ b/server/sonar-db-dao/src/test/java/org/sonar/db/project/ProjectBadgeTokenDaoTest.java
@@ -51,17 +51,16 @@ public class ProjectBadgeTokenDaoTest {
private final ProjectBadgeTokenDao projectBadgeTokenDao = new ProjectBadgeTokenDao(system2, auditPersister, uuidFactory);
-
@Test
public void should_insert_and_select_by_project_uuid() {
when(uuidFactory.create()).thenReturn("generated_uuid_1");
ProjectDto projectDto = new ProjectDto().setUuid("project_uuid_1");
ProjectBadgeTokenDto insertedProjectBadgeToken = projectBadgeTokenDao.insert(db.getSession(), "token", projectDto, "userUuid", "userLogin");
- assertProjectBadgeToken(insertedProjectBadgeToken);
+ assertProjectBadgeToken(insertedProjectBadgeToken, "token");
ProjectBadgeTokenDto selectedProjectBadgeToken = projectBadgeTokenDao.selectTokenByProject(db.getSession(), projectDto);
- assertProjectBadgeToken(selectedProjectBadgeToken);
+ assertProjectBadgeToken(selectedProjectBadgeToken, "token");
}
@Test
@@ -70,7 +69,7 @@ public class ProjectBadgeTokenDaoTest {
ProjectDto projectDto = new ProjectDto().setUuid("project_uuid_1");
ProjectBadgeTokenDto insertedProjectBadgeToken = projectBadgeTokenDao.insert(db.getSession(), "token", projectDto, "user-uuid", "user-login");
- assertProjectBadgeToken(insertedProjectBadgeToken);
+ assertProjectBadgeToken(insertedProjectBadgeToken, "token");
ArgumentCaptor<ProjectBadgeTokenNewValue> captor = ArgumentCaptor.forClass(ProjectBadgeTokenNewValue.class);
@@ -80,9 +79,57 @@ public class ProjectBadgeTokenDaoTest {
Assertions.assertThat(captor.getValue()).hasToString("{\"userUuid\": \"user-uuid\", \"userLogin\": \"user-login\" }");
}
- private void assertProjectBadgeToken(@Nullable ProjectBadgeTokenDto projectBadgeTokenDto) {
+ @Test
+ public void upsert_existing_token_and_select_by_project_uuid() {
+ when(uuidFactory.create()).thenReturn("generated_uuid_1");
+ ProjectDto projectDto = new ProjectDto().setUuid("project_uuid_1");
+
+ // first insert
+ ProjectBadgeTokenDto insertedProjectBadgeToken = projectBadgeTokenDao.insert(db.getSession(), "token", projectDto, "user-uuid", "user-login");
+ assertProjectBadgeToken(insertedProjectBadgeToken, "token");
+
+ // renew
+ projectBadgeTokenDao.upsert(db.getSession(), "new-token", projectDto, "user-uuid", "user-login");
+ ProjectBadgeTokenDto selectedProjectBadgeToken = projectBadgeTokenDao.selectTokenByProject(db.getSession(), projectDto);
+ assertProjectBadgeToken(selectedProjectBadgeToken, "new-token");
+ }
+
+ @Test
+ public void upsert_non_existing_token_and_select_by_project_uuid() {
+ when(uuidFactory.create()).thenReturn("generated_uuid_1");
+ ProjectDto projectDto = new ProjectDto().setUuid("project_uuid_1");
+
+ // renew
+ projectBadgeTokenDao.upsert(db.getSession(), "new-token", projectDto, "user-uuid", "user-login");
+ ProjectBadgeTokenDto selectedProjectBadgeToken = projectBadgeTokenDao.selectTokenByProject(db.getSession(), projectDto);
+ assertProjectBadgeToken(selectedProjectBadgeToken, "new-token");
+ }
+
+
+ @Test
+ public void token_upsert_is_log_in_audit() {
+ when(uuidFactory.create()).thenReturn("generated_uuid_1");
+ ProjectDto projectDto = new ProjectDto().setUuid("project_uuid_1");
+
+ // fist insert
+ projectBadgeTokenDao.insert(db.getSession(), "token", projectDto, "user-uuid", "user-login");
+ ArgumentCaptor<ProjectBadgeTokenNewValue> captor = ArgumentCaptor.forClass(ProjectBadgeTokenNewValue.class);
+ verify(auditPersister).addProjectBadgeToken(eq(db.getSession()), captor.capture());
+
+ // upsert
+ projectBadgeTokenDao.upsert(db.getSession(), "new-token", projectDto, "user-uuid", "user-login");
+ ProjectBadgeTokenDto selectedProjectBadgeToken = projectBadgeTokenDao.selectTokenByProject(db.getSession(), projectDto);
+ assertProjectBadgeToken(selectedProjectBadgeToken, "new-token");
+
+ verify(auditPersister).updateProjectBadgeToken(eq(db.getSession()), captor.capture());
+ verifyNoMoreInteractions(auditPersister);
+
+ Assertions.assertThat(captor.getValue()).hasToString("{\"userUuid\": \"user-uuid\", \"userLogin\": \"user-login\" }");
+ }
+
+ private void assertProjectBadgeToken(@Nullable ProjectBadgeTokenDto projectBadgeTokenDto, String expectedToken) {
assertThat(projectBadgeTokenDto).isNotNull();
- assertThat(projectBadgeTokenDto.getToken()).isEqualTo("token");
+ assertThat(projectBadgeTokenDto.getToken()).isEqualTo(expectedToken);
assertThat(projectBadgeTokenDto.getProjectUuid()).isEqualTo("project_uuid_1");
assertThat(projectBadgeTokenDto.getUuid()).isEqualTo("generated_uuid_1");
assertThat(projectBadgeTokenDto.getCreatedAt()).isEqualTo(1000L);
diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/badge/ws/ProjectBadgesWs.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/badge/ws/ProjectBadgesWs.java
index 6661522c4da..a8edb3d09e1 100644
--- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/badge/ws/ProjectBadgesWs.java
+++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/badge/ws/ProjectBadgesWs.java
@@ -32,7 +32,7 @@ public class ProjectBadgesWs implements WebService {
@Override
public void define(Context context) {
- NewController controller = context.createController("api/project_badges");
+ NewController controller = context.createController("api/project_badges");
controller.setDescription("Generate badges based on quality gates or measures");
controller.setSince("7.1");
actions.forEach(action -> action.define(controller));
diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/badge/ws/ProjectBadgesWsModule.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/badge/ws/ProjectBadgesWsModule.java
index bb695a08db8..c9cf545d6f7 100644
--- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/badge/ws/ProjectBadgesWsModule.java
+++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/badge/ws/ProjectBadgesWsModule.java
@@ -31,6 +31,7 @@ public class ProjectBadgesWsModule extends Module {
MeasureAction.class,
TokenAction.class,
SvgGenerator.class,
- ProjectBadgesSupport.class);
+ ProjectBadgesSupport.class,
+ TokenRenewAction.class);
}
}
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
new file mode 100644
index 00000000000..0b915a7a6d7
--- /dev/null
+++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/badge/ws/TokenRenewAction.java
@@ -0,0 +1,81 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 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.badge.ws;
+
+import org.sonar.api.server.ws.Request;
+import org.sonar.api.server.ws.Response;
+import org.sonar.api.server.ws.WebService;
+import org.sonar.api.server.ws.WebService.NewAction;
+import org.sonar.api.web.UserRole;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.project.ProjectDto;
+import org.sonar.server.user.UserSession;
+import org.sonar.server.usertoken.TokenGenerator;
+
+import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001;
+
+public class TokenRenewAction implements ProjectBadgesWsAction {
+
+ private static final String PROJECT_KEY_PARAM = "project";
+ private final DbClient dbClient;
+ private final TokenGenerator tokenGenerator;
+ private final UserSession userSession;
+
+ public TokenRenewAction(DbClient dbClient, TokenGenerator tokenGenerator, UserSession userSession) {
+ this.dbClient = dbClient;
+ this.tokenGenerator = tokenGenerator;
+ this.userSession = userSession;
+ }
+
+ @Override
+ public void define(WebService.NewController controller) {
+ NewAction action = controller.createAction("renew_token")
+ .setHandler(this)
+ .setSince("9.2")
+ .setPost(true)
+ .setDescription("Creates new token replacing any existing token for project badge access for private projects.<br/>" +
+ "This token can be used to authenticate with api/project_badges/quality_gate and api/project_badges/measure endpoints.<br/>" +
+ "Requires 'Administer' permission on the specified project.");
+ action.createParam(PROJECT_KEY_PARAM)
+ .setDescription("Project key")
+ .setRequired(true)
+ .setExampleValue(KEY_PROJECT_EXAMPLE_001);
+ }
+
+ @Override
+ public void handle(Request request, Response response) throws Exception {
+ doHandle(request);
+ response.noContent();
+ }
+
+ private void doHandle(Request request) {
+ try (DbSession dbSession = dbClient.openSession(false)) {
+ String projectKey = request.mandatoryParam(PROJECT_KEY_PARAM);
+
+ ProjectDto projectDto = dbClient.projectDao().selectProjectByKey(dbSession, projectKey).orElseThrow(() -> new IllegalArgumentException("project not found"));
+ userSession.checkProjectPermission(UserRole.ADMIN, projectDto);
+ String newGeneratedToken = tokenGenerator.generate();
+ dbClient.projectBadgeTokenDao().upsert(dbSession, newGeneratedToken, projectDto, userSession.getUuid(), userSession.getLogin());
+ dbSession.commit();
+ }
+ }
+
+}
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
new file mode 100644
index 00000000000..9fa6074acb5
--- /dev/null
+++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/badge/ws/TokenRenewActionTest.java
@@ -0,0 +1,116 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 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.badge.ws;
+
+import org.assertj.core.api.Assertions;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mockito;
+import org.sonar.api.utils.System2;
+import org.sonar.api.web.UserRole;
+import org.sonar.db.DbTester;
+import org.sonar.db.component.ComponentDto;
+import org.sonar.db.project.ProjectBadgeTokenDto;
+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.ws.TestRequest;
+import org.sonar.server.ws.TestResponse;
+import org.sonar.server.ws.WsActionTester;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class TokenRenewActionTest {
+
+ private final System2 system2 = mock(System2.class);
+
+ @Rule
+ public DbTester db = DbTester.create(system2);
+
+ @Rule
+ public UserSessionRule userSession = UserSessionRule.standalone();
+
+ private final TokenGenerator tokenGenerator = Mockito.mock(TokenGenerator.class);
+
+ private final WsActionTester ws = new WsActionTester(
+ new TokenRenewAction(
+ db.getDbClient(),
+ tokenGenerator, userSession));
+
+ @Before
+ public void before(){
+ when(system2.now()).thenReturn(1000L);
+ }
+
+ @Test
+ public void missing_project_parameter_should_fail() {
+ TestRequest request = ws.newRequest();
+ Assertions.assertThatThrownBy(request::execute)
+ .hasMessage("The 'project' parameter is missing")
+ .isInstanceOf(IllegalArgumentException.class);
+ }
+
+ @Test
+ public void missing_project_admin_permission_should_fail() {
+ ComponentDto project = db.components().insertPrivateProject();
+
+ TestRequest request = ws.newRequest().setParam("project", project.getKey());
+
+ Assertions.assertThatThrownBy(request::execute)
+ .hasMessage("Insufficient privileges")
+ .isInstanceOf(ForbiddenException.class);
+ }
+
+ @Test
+ 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");
+
+ TestResponse response = ws.newRequest().setParam("project", project.getKey()).execute();
+
+ ProjectBadgeTokenDto projectBadgeTokenDto = db.getDbClient().projectBadgeTokenDao().selectTokenByProject(db.getSession(), project);
+ assertThat(projectBadgeTokenDto).isNotNull();
+ assertThat(projectBadgeTokenDto.getToken()).isEqualTo("generated_token");
+ response.assertNoContent();
+ }
+
+ @Test
+ 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");
+
+ ws.newRequest().setParam("project", project.getKey()).execute(); //inserting first token with updated at 1000
+
+ when(system2.now()).thenReturn(2000L);
+ ws.newRequest().setParam("project", project.getKey()).execute(); //replacing first token with updated at 2000
+
+ ProjectBadgeTokenDto projectBadgeTokenDto = db.getDbClient().projectBadgeTokenDao().selectTokenByProject(db.getSession(), project);
+ assertThat(projectBadgeTokenDto).isNotNull();
+ assertThat(projectBadgeTokenDto.getToken()).isEqualTo("generated_token");
+ assertThat(projectBadgeTokenDto.getUpdatedAt()).isEqualTo(2000L);
+ }
+
+}