aboutsummaryrefslogtreecommitdiffstats
path: root/server
diff options
context:
space:
mode:
authorPierre <pierre.guillot@sonarsource.com>2021-11-10 17:43:31 +0100
committersonartech <sonartech@sonarsource.com>2021-11-15 20:04:34 +0000
commit29b7cf350cd0235475e1c09182ab905e4303b442 (patch)
treec22449965b8efb03b9a8ecb3d224c701eb9b68bb /server
parentbe652549aac881f0db55ee1ca7cb4871cc4babd5 (diff)
downloadsonarqube-29b7cf350cd0235475e1c09182ab905e4303b442.tar.gz
sonarqube-29b7cf350cd0235475e1c09182ab905e4303b442.zip
SONAR-13426 add api/project_badges/token endpoint
Diffstat (limited to 'server')
-rw-r--r--server/sonar-db-core/src/main/java/org/sonar/db/version/SqTables.java1
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/DaoModule.java2
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/DbClient.java6
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/MyBatis.java6
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/audit/AuditPersister.java5
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/audit/NoOpAuditPersister.java6
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/audit/model/ProjectBadgeTokenNewValue.java43
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/project/ProjectBadgeTokenDao.java61
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/project/ProjectBadgeTokenDto.java86
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/project/ProjectBadgeTokenMapper.java31
-rw-r--r--server/sonar-db-dao/src/main/resources/org/sonar/db/project/ProjectBadgeTokenMapper.xml38
-rw-r--r--server/sonar-db-dao/src/test/java/org/sonar/db/project/ProjectBadgeTokenDaoTest.java92
-rw-r--r--server/sonar-webserver-webapi/src/main/java/org/sonar/server/badge/ws/ProjectBadgesWsModule.java1
-rw-r--r--server/sonar-webserver-webapi/src/main/java/org/sonar/server/badge/ws/TokenAction.java96
-rw-r--r--server/sonar-webserver-webapi/src/main/resources/org/sonar/server/badge/ws/token-example.json1
-rw-r--r--server/sonar-webserver-webapi/src/test/java/org/sonar/server/badge/ws/ProjectBadgesWsModuleTest.java7
-rw-r--r--server/sonar-webserver-webapi/src/test/java/org/sonar/server/badge/ws/TokenActionTest.java101
17 files changed, 577 insertions, 6 deletions
diff --git a/server/sonar-db-core/src/main/java/org/sonar/db/version/SqTables.java b/server/sonar-db-core/src/main/java/org/sonar/db/version/SqTables.java
index 15c4ff52275..33e58ca8716 100644
--- a/server/sonar-db-core/src/main/java/org/sonar/db/version/SqTables.java
+++ b/server/sonar-db-core/src/main/java/org/sonar/db/version/SqTables.java
@@ -78,6 +78,7 @@ public final class SqTables {
"portfolio_references",
"projects",
"project_alm_settings",
+ "project_badge_token",
"project_branches",
"project_links",
"project_mappings",
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/DaoModule.java b/server/sonar-db-dao/src/main/java/org/sonar/db/DaoModule.java
index ea48041aed2..cc088c10d12 100644
--- a/server/sonar-db-dao/src/main/java/org/sonar/db/DaoModule.java
+++ b/server/sonar-db-dao/src/main/java/org/sonar/db/DaoModule.java
@@ -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,
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/DbClient.java b/server/sonar-db-dao/src/main/java/org/sonar/db/DbClient.java
index 99fa1daa6fa..32fb4f65628 100644
--- a/server/sonar-db-dao/src/main/java/org/sonar/db/DbClient.java
+++ b/server/sonar-db-dao/src/main/java/org/sonar/db/DbClient.java
@@ -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;
+ }
}
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/MyBatis.java b/server/sonar-db-dao/src/main/java/org/sonar/db/MyBatis.java
index 298be87c731..4db01625cce 100644
--- a/server/sonar-db-dao/src/main/java/org/sonar/db/MyBatis.java
+++ b/server/sonar-db-dao/src/main/java/org/sonar/db/MyBatis.java
@@ -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,
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 60987dbf424..ac41ffe6e36 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
@@ -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);
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 2de7874e9f8..1923b0cca13 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
@@ -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;
@@ -114,6 +115,11 @@ public class NoOpAuditPersister implements AuditPersister {
}
@Override
+ public void addProjectBadgeToken(DbSession dbSession, ProjectBadgeTokenNewValue newValue) {
+ // 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/audit/model/ProjectBadgeTokenNewValue.java b/server/sonar-db-dao/src/main/java/org/sonar/db/audit/model/ProjectBadgeTokenNewValue.java
new file mode 100644
index 00000000000..9d53395b7a7
--- /dev/null
+++ b/server/sonar-db-dao/src/main/java/org/sonar/db/audit/model/ProjectBadgeTokenNewValue.java
@@ -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();
+ }
+}
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
new file mode 100644
index 00000000000..8c9758f1845
--- /dev/null
+++ b/server/sonar-db-dao/src/main/java/org/sonar/db/project/ProjectBadgeTokenDao.java
@@ -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());
+
+ }
+}
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/project/ProjectBadgeTokenDto.java b/server/sonar-db-dao/src/main/java/org/sonar/db/project/ProjectBadgeTokenDto.java
new file mode 100644
index 00000000000..1611ab037c4
--- /dev/null
+++ b/server/sonar-db-dao/src/main/java/org/sonar/db/project/ProjectBadgeTokenDto.java
@@ -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;
+ }
+}
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
new file mode 100644
index 00000000000..3f8cda7408c
--- /dev/null
+++ b/server/sonar-db-dao/src/main/java/org/sonar/db/project/ProjectBadgeTokenMapper.java
@@ -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);
+}
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
new file mode 100644
index 00000000000..59967a17741
--- /dev/null
+++ b/server/sonar-db-dao/src/main/resources/org/sonar/db/project/ProjectBadgeTokenMapper.xml
@@ -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>
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
new file mode 100644
index 00000000000..6f99c2cdb13
--- /dev/null
+++ b/server/sonar-db-dao/src/test/java/org/sonar/db/project/ProjectBadgeTokenDaoTest.java
@@ -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);
+ }
+
+}
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 eb86931bda3..bb695a08db8 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
@@ -29,6 +29,7 @@ public class ProjectBadgesWsModule extends Module {
ProjectBadgesWs.class,
QualityGateAction.class,
MeasureAction.class,
+ TokenAction.class,
SvgGenerator.class,
ProjectBadgesSupport.class);
}
diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/badge/ws/TokenAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/badge/ws/TokenAction.java
new file mode 100644
index 00000000000..51b8adac08d
--- /dev/null
+++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/badge/ws/TokenAction.java
@@ -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();
+ }
+
+}
diff --git a/server/sonar-webserver-webapi/src/main/resources/org/sonar/server/badge/ws/token-example.json b/server/sonar-webserver-webapi/src/main/resources/org/sonar/server/badge/ws/token-example.json
new file mode 100644
index 00000000000..677556816b2
--- /dev/null
+++ b/server/sonar-webserver-webapi/src/main/resources/org/sonar/server/badge/ws/token-example.json
@@ -0,0 +1 @@
+{"token": "XXXXXXXXXXXXXXX"}
diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/badge/ws/ProjectBadgesWsModuleTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/badge/ws/ProjectBadgesWsModuleTest.java
index d2d564e59ac..ed470ad73f6 100644
--- a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/badge/ws/ProjectBadgesWsModuleTest.java
+++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/badge/ws/ProjectBadgesWsModuleTest.java
@@ -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();
}
}
diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/badge/ws/TokenActionTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/badge/ws/TokenActionTest.java
new file mode 100644
index 00000000000..44ba81fea12
--- /dev/null
+++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/badge/ws/TokenActionTest.java
@@ -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\"}");
+
+ }
+
+}