@@ -78,6 +78,7 @@ public final class SqTables { | |||
"portfolio_references", | |||
"projects", | |||
"project_alm_settings", | |||
"project_badge_token", | |||
"project_branches", | |||
"project_links", | |||
"project_mappings", |
@@ -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, |
@@ -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; | |||
} | |||
} |
@@ -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, |
@@ -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); |
@@ -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 |
@@ -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(); | |||
} | |||
} |
@@ -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()); | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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); | |||
} |
@@ -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> |
@@ -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); | |||
} | |||
} |
@@ -29,6 +29,7 @@ public class ProjectBadgesWsModule extends Module { | |||
ProjectBadgesWs.class, | |||
QualityGateAction.class, | |||
MeasureAction.class, | |||
TokenAction.class, | |||
SvgGenerator.class, | |||
ProjectBadgesSupport.class); | |||
} |
@@ -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(); | |||
} | |||
} |
@@ -0,0 +1 @@ | |||
{"token": "XXXXXXXXXXXXXXX"} |
@@ -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(); | |||
} | |||
} |
@@ -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\"}"); | |||
} | |||
} |
@@ -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; | |||
} |