void addProjectBadgeToken(DbSession dbSession, ProjectBadgeTokenNewValue newValue);
+ void updateProjectBadgeToken(DbSession session, ProjectBadgeTokenNewValue projectBadgeTokenNewValue);
+
void updateUserToken(DbSession dbSession, UserTokenNewValue newValue);
void deleteUserToken(DbSession dbSession, UserTokenNewValue newValue);
// no op
}
+ @Override
+ public void updateProjectBadgeToken(DbSession session, ProjectBadgeTokenNewValue projectBadgeTokenNewValue) {
+ // no op
+ }
+
@Override
public void updateUserToken(DbSession dbSession, UserTokenNewValue newValue) {
// no op
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);
}
@CheckForNull
public ProjectBadgeTokenDto selectTokenByProject(DbSession session, ProjectDto projectDto) {
return mapper(session).selectTokenByProjectUuid(projectDto.getUuid());
-
}
}
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);
}
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,
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
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);
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);
@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));
MeasureAction.class,
TokenAction.class,
SvgGenerator.class,
- ProjectBadgesSupport.class);
+ ProjectBadgesSupport.class,
+ TokenRenewAction.class);
}
}
--- /dev/null
+/*
+ * 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();
+ }
+ }
+
+}
--- /dev/null
+/*
+ * 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);
+ }
+
+}