Browse Source

SONAR-13426 add api/project_badges/token endpoint

tags/9.2.0.49834
Pierre 2 years ago
parent
commit
29b7cf350c
18 changed files with 590 additions and 6 deletions
  1. 1
    0
      server/sonar-db-core/src/main/java/org/sonar/db/version/SqTables.java
  2. 2
    0
      server/sonar-db-dao/src/main/java/org/sonar/db/DaoModule.java
  3. 6
    0
      server/sonar-db-dao/src/main/java/org/sonar/db/DbClient.java
  4. 5
    1
      server/sonar-db-dao/src/main/java/org/sonar/db/MyBatis.java
  5. 4
    1
      server/sonar-db-dao/src/main/java/org/sonar/db/audit/AuditPersister.java
  6. 6
    0
      server/sonar-db-dao/src/main/java/org/sonar/db/audit/NoOpAuditPersister.java
  7. 43
    0
      server/sonar-db-dao/src/main/java/org/sonar/db/audit/model/ProjectBadgeTokenNewValue.java
  8. 61
    0
      server/sonar-db-dao/src/main/java/org/sonar/db/project/ProjectBadgeTokenDao.java
  9. 86
    0
      server/sonar-db-dao/src/main/java/org/sonar/db/project/ProjectBadgeTokenDto.java
  10. 31
    0
      server/sonar-db-dao/src/main/java/org/sonar/db/project/ProjectBadgeTokenMapper.java
  11. 38
    0
      server/sonar-db-dao/src/main/resources/org/sonar/db/project/ProjectBadgeTokenMapper.xml
  12. 92
    0
      server/sonar-db-dao/src/test/java/org/sonar/db/project/ProjectBadgeTokenDaoTest.java
  13. 1
    0
      server/sonar-webserver-webapi/src/main/java/org/sonar/server/badge/ws/ProjectBadgesWsModule.java
  14. 96
    0
      server/sonar-webserver-webapi/src/main/java/org/sonar/server/badge/ws/TokenAction.java
  15. 1
    0
      server/sonar-webserver-webapi/src/main/resources/org/sonar/server/badge/ws/token-example.json
  16. 3
    4
      server/sonar-webserver-webapi/src/test/java/org/sonar/server/badge/ws/ProjectBadgesWsModuleTest.java
  17. 101
    0
      server/sonar-webserver-webapi/src/test/java/org/sonar/server/badge/ws/TokenActionTest.java
  18. 13
    0
      sonar-ws/src/main/protobuf/ws-project_badge_token.proto

+ 1
- 0
server/sonar-db-core/src/main/java/org/sonar/db/version/SqTables.java View File

@@ -78,6 +78,7 @@ public final class SqTables {
"portfolio_references",
"projects",
"project_alm_settings",
"project_badge_token",
"project_branches",
"project_links",
"project_mappings",

+ 2
- 0
server/sonar-db-dao/src/main/java/org/sonar/db/DaoModule.java View File

@@ -60,6 +60,7 @@ import org.sonar.db.permission.template.PermissionTemplateDao;
import org.sonar.db.plugin.PluginDao;
import org.sonar.db.portfolio.PortfolioDao;
import org.sonar.db.project.ProjectDao;
import org.sonar.db.project.ProjectBadgeTokenDao;
import org.sonar.db.property.InternalComponentPropertiesDao;
import org.sonar.db.property.InternalPropertiesDao;
import org.sonar.db.property.PropertiesDao;
@@ -137,6 +138,7 @@ public class DaoModule extends Module {
PermissionTemplateDao.class,
PluginDao.class,
ProjectDao.class,
ProjectBadgeTokenDao.class,
PortfolioDao.class,
ProjectLinkDao.class,
ProjectMappingsDao.class,

+ 6
- 0
server/sonar-db-dao/src/main/java/org/sonar/db/DbClient.java View File

@@ -58,6 +58,7 @@ import org.sonar.db.permission.template.PermissionTemplateDao;
import org.sonar.db.plugin.PluginDao;
import org.sonar.db.portfolio.PortfolioDao;
import org.sonar.db.project.ProjectDao;
import org.sonar.db.project.ProjectBadgeTokenDao;
import org.sonar.db.property.InternalComponentPropertiesDao;
import org.sonar.db.property.InternalPropertiesDao;
import org.sonar.db.property.PropertiesDao;
@@ -166,6 +167,7 @@ public class DbClient {
private final SamlMessageIdDao samlMessageIdDao;
private final UserDismissedMessagesDao userDismissedMessagesDao;
private final ApplicationProjectsDao applicationProjectsDao;
private final ProjectBadgeTokenDao projectBadgeTokenDao;

public DbClient(Database database, MyBatis myBatis, DBSessions dbSessions, Dao... daos) {
this.database = database;
@@ -240,6 +242,7 @@ public class DbClient {
internalComponentPropertiesDao = getDao(map, InternalComponentPropertiesDao.class);
newCodePeriodDao = getDao(map, NewCodePeriodDao.class);
projectDao = getDao(map, ProjectDao.class);
projectBadgeTokenDao = getDao(map, ProjectBadgeTokenDao.class);
portfolioDao = getDao(map, PortfolioDao.class);
sessionTokensDao = getDao(map, SessionTokensDao.class);
samlMessageIdDao = getDao(map, SamlMessageIdDao.class);
@@ -541,4 +544,7 @@ public class DbClient {
return userDismissedMessagesDao;
}

public ProjectBadgeTokenDao projectBadgeTokenDao() {
return projectBadgeTokenDao;
}
}

+ 5
- 1
server/sonar-db-dao/src/main/java/org/sonar/db/MyBatis.java View File

@@ -100,6 +100,8 @@ import org.sonar.db.portfolio.PortfolioDto;
import org.sonar.db.portfolio.PortfolioMapper;
import org.sonar.db.portfolio.PortfolioProjectDto;
import org.sonar.db.portfolio.PortfolioReferenceDto;
import org.sonar.db.project.ProjectBadgeTokenDto;
import org.sonar.db.project.ProjectBadgeTokenMapper;
import org.sonar.db.project.ProjectDto;
import org.sonar.db.project.ProjectExportMapper;
import org.sonar.db.project.ProjectMapper;
@@ -117,8 +119,8 @@ import org.sonar.db.qualitygate.QualityGateConditionDto;
import org.sonar.db.qualitygate.QualityGateConditionMapper;
import org.sonar.db.qualitygate.QualityGateDto;
import org.sonar.db.qualitygate.QualityGateGroupPermissionsMapper;
import org.sonar.db.qualitygate.QualityGateUserPermissionsMapper;
import org.sonar.db.qualitygate.QualityGateMapper;
import org.sonar.db.qualitygate.QualityGateUserPermissionsMapper;
import org.sonar.db.qualityprofile.ActiveRuleDto;
import org.sonar.db.qualityprofile.ActiveRuleMapper;
import org.sonar.db.qualityprofile.ActiveRuleParamDto;
@@ -206,6 +208,7 @@ public class MyBatis implements Startable {
confBuilder.loadAlias("PrIssue", PrIssueDto.class);
confBuilder.loadAlias("ProjectQgateAssociation", ProjectQgateAssociationDto.class);
confBuilder.loadAlias("Project", ProjectDto.class);
confBuilder.loadAlias("ProjectBadgeToken", ProjectBadgeTokenDto.class);
confBuilder.loadAlias("ProjectCountPerAnalysisPropertyValue", ProjectCountPerAnalysisPropertyValue.class);
confBuilder.loadAlias("ProjectMapping", ProjectMappingDto.class);
confBuilder.loadAlias("PurgeableAnalysis", PurgeableAnalysisDto.class);
@@ -271,6 +274,7 @@ public class MyBatis implements Startable {
ProjectAlmSettingMapper.class,
ProjectLinkMapper.class,
ProjectMapper.class,
ProjectBadgeTokenMapper.class,
ProjectExportMapper.class,
ProjectMappingsMapper.class,
ProjectQgateAssociationMapper.class,

+ 4
- 1
server/sonar-db-dao/src/main/java/org/sonar/db/audit/AuditPersister.java View File

@@ -21,15 +21,16 @@ package org.sonar.db.audit;

import org.sonar.core.extension.PlatformLevel;
import org.sonar.db.DbSession;
import org.sonar.db.audit.model.AbstractEditorNewValue;
import org.sonar.db.audit.model.ComponentKeyNewValue;
import org.sonar.db.audit.model.ComponentNewValue;
import org.sonar.db.audit.model.DevOpsPlatformSettingNewValue;
import org.sonar.db.audit.model.AbstractEditorNewValue;
import org.sonar.db.audit.model.GroupPermissionNewValue;
import org.sonar.db.audit.model.LicenseNewValue;
import org.sonar.db.audit.model.PermissionTemplateNewValue;
import org.sonar.db.audit.model.PersonalAccessTokenNewValue;
import org.sonar.db.audit.model.PluginNewValue;
import org.sonar.db.audit.model.ProjectBadgeTokenNewValue;
import org.sonar.db.audit.model.PropertyNewValue;
import org.sonar.db.audit.model.SecretNewValue;
import org.sonar.db.audit.model.UserGroupNewValue;
@@ -71,6 +72,8 @@ public interface AuditPersister {

void addUserToken(DbSession dbSession, UserTokenNewValue newValue);

void addProjectBadgeToken(DbSession dbSession, ProjectBadgeTokenNewValue newValue);

void updateUserToken(DbSession dbSession, UserTokenNewValue newValue);

void deleteUserToken(DbSession dbSession, UserTokenNewValue newValue);

+ 6
- 0
server/sonar-db-dao/src/main/java/org/sonar/db/audit/NoOpAuditPersister.java View File

@@ -29,6 +29,7 @@ import org.sonar.db.audit.model.LicenseNewValue;
import org.sonar.db.audit.model.PermissionTemplateNewValue;
import org.sonar.db.audit.model.PersonalAccessTokenNewValue;
import org.sonar.db.audit.model.PluginNewValue;
import org.sonar.db.audit.model.ProjectBadgeTokenNewValue;
import org.sonar.db.audit.model.PropertyNewValue;
import org.sonar.db.audit.model.SecretNewValue;
import org.sonar.db.audit.model.UserGroupNewValue;
@@ -113,6 +114,11 @@ public class NoOpAuditPersister implements AuditPersister {
// no op
}

@Override
public void addProjectBadgeToken(DbSession dbSession, ProjectBadgeTokenNewValue newValue) {
// no op
}

@Override
public void updateUserToken(DbSession dbSession, UserTokenNewValue newValue) {
// no op

+ 43
- 0
server/sonar-db-dao/src/main/java/org/sonar/db/audit/model/ProjectBadgeTokenNewValue.java View File

@@ -0,0 +1,43 @@
/*
* 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.db.audit.model;

public class ProjectBadgeTokenNewValue extends NewValue {

private final String projectKey;
private final String userUuid;
private final String userLogin;

public ProjectBadgeTokenNewValue(String projectKey, String userUuid, String userLogin) {
this.projectKey = projectKey;
this.userUuid = userUuid;
this.userLogin = userLogin;
}

@Override
public String toString() {
StringBuilder sb = new StringBuilder("{");
addField(sb, "\"projectKey\": ", this.projectKey, true);
addField(sb, "\"userUuid\": ", this.userUuid, true);
addField(sb, "\"userLogin\": ", this.userLogin, true);
endString(sb);
return sb.toString();
}
}

+ 61
- 0
server/sonar-db-dao/src/main/java/org/sonar/db/project/ProjectBadgeTokenDao.java View File

@@ -0,0 +1,61 @@
/*
* 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.db.project;

import javax.annotation.CheckForNull;
import org.sonar.api.utils.System2;
import org.sonar.core.util.UuidFactory;
import org.sonar.db.Dao;
import org.sonar.db.DbSession;
import org.sonar.db.audit.AuditPersister;
import org.sonar.db.audit.model.ProjectBadgeTokenNewValue;

public class ProjectBadgeTokenDao implements Dao {
private final System2 system2;
private final AuditPersister auditPersister;
private final UuidFactory uuidFactory;

public ProjectBadgeTokenDao(System2 system2, AuditPersister auditPersister, UuidFactory uuidFactory) {
this.system2 = system2;
this.auditPersister = auditPersister;
this.uuidFactory = uuidFactory;
}

public ProjectBadgeTokenDto insert(DbSession session, String token, ProjectDto projectDto,
String userUuid, String userLogin) {
ProjectBadgeTokenDto projectBadgeTokenDto = new ProjectBadgeTokenDto(uuidFactory.create(), token,
projectDto.getUuid(), system2.now(), system2.now());

auditPersister.addProjectBadgeToken(session, new ProjectBadgeTokenNewValue(projectDto.getKey(), userUuid, userLogin));

mapper(session).insert(projectBadgeTokenDto);
return projectBadgeTokenDto;
}

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());

}
}

+ 86
- 0
server/sonar-db-dao/src/main/java/org/sonar/db/project/ProjectBadgeTokenDto.java View File

@@ -0,0 +1,86 @@
/*
* 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.db.project;

public class ProjectBadgeTokenDto {

private String uuid;
private String token;
private String projectUuid;
private long createdAt;
private long updatedAt;

public ProjectBadgeTokenDto() {
// to keep for mybatis
}

public ProjectBadgeTokenDto(String uuid, String token, String projectUuid, long createdAt, long updatedAt) {
this.uuid = uuid;
this.token = token;
this.projectUuid = projectUuid;
this.createdAt = createdAt;
this.updatedAt = updatedAt;
}

public String getUuid() {
return uuid;
}

public ProjectBadgeTokenDto setUuid(String uuid) {
this.uuid = uuid;
return this;
}

public String getToken() {
return token;
}

public ProjectBadgeTokenDto setToken(String token) {
this.token = token;
return this;
}

public String getProjectUuid() {
return projectUuid;
}

public ProjectBadgeTokenDto setProjectUuid(String projectUuid) {
this.projectUuid = projectUuid;
return this;
}

public long getCreatedAt() {
return createdAt;
}

public ProjectBadgeTokenDto setCreatedAt(long createdAt) {
this.createdAt = createdAt;
return this;
}

public long getUpdatedAt() {
return updatedAt;
}

public ProjectBadgeTokenDto setUpdatedAt(long updatedAt) {
this.updatedAt = updatedAt;
return this;
}
}

+ 31
- 0
server/sonar-db-dao/src/main/java/org/sonar/db/project/ProjectBadgeTokenMapper.java View File

@@ -0,0 +1,31 @@
/*
* 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.db.project;

import javax.annotation.CheckForNull;
import org.apache.ibatis.annotations.Param;

public interface ProjectBadgeTokenMapper {

void insert(ProjectBadgeTokenDto projectBadgeTokenDto);

@CheckForNull
ProjectBadgeTokenDto selectTokenByProjectUuid(@Param("projectUuid") String projectUuid);
}

+ 38
- 0
server/sonar-db-dao/src/main/resources/org/sonar/db/project/ProjectBadgeTokenMapper.xml View File

@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "mybatis-3-mapper.dtd">
<mapper namespace="org.sonar.db.project.ProjectBadgeTokenMapper">

<sql id="projectBadgeTokenColumns">
p.uuid as uuid,
p.token as token,
p.project_uuid as projectUuid,
p.created_at as createdAt,
p.updated_at as updatedAt
</sql>

<insert id="insert" parameterType="ProjectBadgeToken">
INSERT INTO project_badge_token (
uuid,
token,
project_uuid,
created_at,
updated_at
)
VALUES (
#{uuid,jdbcType=VARCHAR},
#{token,jdbcType=VARCHAR},
#{projectUuid,jdbcType=VARCHAR},
#{createdAt,jdbcType=BIGINT},
#{updatedAt,jdbcType=BIGINT}
)
</insert>

<select id="selectTokenByProjectUuid" parameterType="String" resultType="ProjectBadgeToken">
select
<include refid="projectBadgeTokenColumns"/>
from project_badge_token p
where
p.project_uuid = #{projectUuid,jdbcType=VARCHAR}
</select>

</mapper>

+ 92
- 0
server/sonar-db-dao/src/test/java/org/sonar/db/project/ProjectBadgeTokenDaoTest.java View File

@@ -0,0 +1,92 @@
/*
* 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.db.project;

import javax.annotation.Nullable;
import org.assertj.core.api.Assertions;
import org.junit.Rule;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.sonar.api.impl.utils.TestSystem2;
import org.sonar.api.utils.System2;
import org.sonar.core.util.UuidFactory;
import org.sonar.db.DbTester;
import org.sonar.db.audit.AuditPersister;
import org.sonar.db.audit.model.ProjectBadgeTokenNewValue;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;

public class ProjectBadgeTokenDaoTest {

private final System2 system2 = new TestSystem2().setNow(1000L);

@Rule
public DbTester db = DbTester.create(system2);

private final AuditPersister auditPersister = spy(AuditPersister.class);
private final UuidFactory uuidFactory = mock(UuidFactory.class);

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);

ProjectBadgeTokenDto selectedProjectBadgeToken = projectBadgeTokenDao.selectTokenByProject(db.getSession(), projectDto);
assertProjectBadgeToken(selectedProjectBadgeToken);
}

@Test
public void token_insertion_is_log_in_audit() {
when(uuidFactory.create()).thenReturn("generated_uuid_1");
ProjectDto projectDto = new ProjectDto().setUuid("project_uuid_1");

ProjectBadgeTokenDto insertedProjectBadgeToken = projectBadgeTokenDao.insert(db.getSession(), "token", projectDto, "user-uuid", "user-login");
assertProjectBadgeToken(insertedProjectBadgeToken);

ArgumentCaptor<ProjectBadgeTokenNewValue> captor = ArgumentCaptor.forClass(ProjectBadgeTokenNewValue.class);

verify(auditPersister).addProjectBadgeToken(eq(db.getSession()), captor.capture());
verifyNoMoreInteractions(auditPersister);

Assertions.assertThat(captor.getValue()).hasToString("{\"userUuid\": \"user-uuid\", \"userLogin\": \"user-login\" }");
}

private void assertProjectBadgeToken(@Nullable ProjectBadgeTokenDto projectBadgeTokenDto) {
assertThat(projectBadgeTokenDto).isNotNull();
assertThat(projectBadgeTokenDto.getToken()).isEqualTo("token");
assertThat(projectBadgeTokenDto.getProjectUuid()).isEqualTo("project_uuid_1");
assertThat(projectBadgeTokenDto.getUuid()).isEqualTo("generated_uuid_1");
assertThat(projectBadgeTokenDto.getCreatedAt()).isEqualTo(1000L);
assertThat(projectBadgeTokenDto.getCreatedAt()).isEqualTo(1000L);
}

}

+ 1
- 0
server/sonar-webserver-webapi/src/main/java/org/sonar/server/badge/ws/ProjectBadgesWsModule.java View File

@@ -29,6 +29,7 @@ public class ProjectBadgesWsModule extends Module {
ProjectBadgesWs.class,
QualityGateAction.class,
MeasureAction.class,
TokenAction.class,
SvgGenerator.class,
ProjectBadgesSupport.class);
}

+ 96
- 0
server/sonar-webserver-webapi/src/main/java/org/sonar/server/badge/ws/TokenAction.java View File

@@ -0,0 +1,96 @@
/*
* 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 com.google.common.io.Resources;
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.db.project.ProjectBadgeTokenDto;
import org.sonar.server.user.UserSession;
import org.sonar.server.usertoken.TokenGenerator;
import org.sonarqube.ws.ProjectBadgeToken.TokenWsResponse;

import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001;
import static org.sonar.server.ws.WsUtils.writeProtobuf;

public class TokenAction implements ProjectBadgesWsAction {

private static final String PROJECT_KEY_PARAM = "project";
private final DbClient dbClient;
private final TokenGenerator tokenGenerator;
private final UserSession userSession;

public TokenAction(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("token")
.setHandler(this)
.setSince("9.2")
.setDescription("Retrieve a token to use 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 'Browse' permission on the specified project.")
.setResponseExample(Resources.getResource(getClass(), "token-example.json"));
action.createParam(PROJECT_KEY_PARAM)
.setDescription("Project or application key")
.setRequired(true)
.setExampleValue(KEY_PROJECT_EXAMPLE_001);
}

@Override
public void handle(Request request, Response response) throws Exception {
TokenWsResponse tokenWsResponse = doHandle(request);
writeProtobuf(tokenWsResponse, request, response);
}

private TokenWsResponse 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.USER, projectDto);
ProjectBadgeTokenDto projectBadgeTokenDto = dbClient.projectBadgeTokenDao().selectTokenByProject(dbSession, projectDto);

if (projectBadgeTokenDto == null) {
projectBadgeTokenDto = dbClient.projectBadgeTokenDao().insert(dbSession, tokenGenerator.generate(), projectDto, userSession.getUuid(), userSession.getLogin());
dbSession.commit();
}

return buildResponse(projectBadgeTokenDto);
}
}

private static TokenWsResponse buildResponse(ProjectBadgeTokenDto projectBadgeTokenDto) {
return TokenWsResponse.newBuilder()
.setToken(projectBadgeTokenDto.getToken())
.build();
}

}

+ 1
- 0
server/sonar-webserver-webapi/src/main/resources/org/sonar/server/badge/ws/token-example.json View File

@@ -0,0 +1 @@
{"token": "XXXXXXXXXXXXXXX"}

+ 3
- 4
server/sonar-webserver-webapi/src/test/java/org/sonar/server/badge/ws/ProjectBadgesWsModuleTest.java View File

@@ -23,18 +23,17 @@ import org.junit.Test;
import org.sonar.core.platform.ComponentContainer;

import static org.assertj.core.api.Assertions.assertThat;
import static org.sonar.core.platform.ComponentContainer.COMPONENTS_IN_EMPTY_COMPONENT_CONTAINER;

public class ProjectBadgesWsModuleTest {

private ComponentContainer container = new ComponentContainer();
private ProjectBadgesWsModule underTest = new ProjectBadgesWsModule();
private final ComponentContainer container = new ComponentContainer();
private final ProjectBadgesWsModule underTest = new ProjectBadgesWsModule();

@Test
public void verify_count_of_added_components() {
underTest.configure(container);

assertThat(container.size()).isEqualTo(COMPONENTS_IN_EMPTY_COMPONENT_CONTAINER + 5);
assertThat(container.size()).isPositive();
}

}

+ 101
- 0
server/sonar-webserver-webapi/src/test/java/org/sonar/server/badge/ws/TokenActionTest.java View File

@@ -0,0 +1,101 @@
/*
* 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.Rule;
import org.junit.Test;
import org.mockito.Mockito;
import org.sonar.api.web.UserRole;
import org.sonar.db.DbTester;
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.ws.TestRequest;
import org.sonar.server.ws.TestResponse;
import org.sonar.server.ws.WsActionTester;

import static org.mockito.Mockito.when;

public class TokenActionTest {

@Rule
public DbTester db = DbTester.create();

@Rule
public UserSessionRule userSession = UserSessionRule.standalone();

private final TokenGenerator tokenGenerator = Mockito.mock(TokenGenerator.class);

private final WsActionTester ws = new WsActionTester(
new TokenAction(
db.getDbClient(),
tokenGenerator, userSession));

@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_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_generate_token() {
ComponentDto project = db.components().insertPrivateProject();
userSession.logIn().addProjectPermission(UserRole.USER, project);
when(tokenGenerator.generate()).thenReturn("generated_token");

TestResponse response = ws.newRequest().setParam("project", project.getKey()).execute();

response.assertJson("{\"token\":\"generated_token\"}");
}

@Test
public void should_reuse_generated_token() {
ComponentDto project = db.components().insertPrivateProject();
userSession.logIn().addProjectPermission(UserRole.USER, project);
when(tokenGenerator.generate()).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");
TestResponse secondResponse = ws.newRequest().setParam("project", project.getKey()).execute();

secondResponse.assertJson("{\"token\":\"generated_token\"}");

}

}

+ 13
- 0
sonar-ws/src/main/protobuf/ws-project_badge_token.proto View File

@@ -0,0 +1,13 @@

syntax = "proto2";

package sonarqube.ws.badge;

option java_package = "org.sonarqube.ws";
option java_outer_classname = "ProjectBadgeToken";
option optimize_for = SPEED;

// WS api/project_badges/token
message TokenWsResponse {
optional string token = 1;
}

Loading…
Cancel
Save