aboutsummaryrefslogtreecommitdiffstats
path: root/server
diff options
context:
space:
mode:
authorPierre <pierre.guillot@sonarsource.com>2021-11-12 10:39:19 +0100
committersonartech <sonartech@sonarsource.com>2021-11-15 20:04:34 +0000
commit36f0e6075ebfb3c58c5bc26cfb91460a061201d1 (patch)
treec39327c9169b045eb0c6ec41af8022df906209b9 /server
parent29b7cf350cd0235475e1c09182ab905e4303b442 (diff)
downloadsonarqube-36f0e6075ebfb3c58c5bc26cfb91460a061201d1.tar.gz
sonarqube-36f0e6075ebfb3c58c5bc26cfb91460a061201d1.zip
SONAR-13426 accept tokens on api/project_badges/measure and quality_gate endpoints, including with force auth enabled
Diffstat (limited to 'server')
-rw-r--r--server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/UserSessionInitializer.java5
-rw-r--r--server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/UserSessionInitializerTest.java6
-rw-r--r--server/sonar-webserver-webapi/src/main/java/org/sonar/server/badge/ws/MeasureAction.java2
-rw-r--r--server/sonar-webserver-webapi/src/main/java/org/sonar/server/badge/ws/ProjectBadgesSupport.java42
-rw-r--r--server/sonar-webserver-webapi/src/main/java/org/sonar/server/badge/ws/QualityGateAction.java3
-rw-r--r--server/sonar-webserver-webapi/src/test/java/org/sonar/server/badge/ws/MeasureActionTest.java18
-rw-r--r--server/sonar-webserver-webapi/src/test/java/org/sonar/server/badge/ws/QualityGateActionTest.java5
-rw-r--r--server/sonar-webserver-ws/src/main/java/org/sonar/server/ws/KeyExamples.java2
8 files changed, 51 insertions, 32 deletions
diff --git a/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/UserSessionInitializer.java b/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/UserSessionInitializer.java
index 6f8cc31b90f..205b669eb99 100644
--- a/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/UserSessionInitializer.java
+++ b/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/UserSessionInitializer.java
@@ -50,14 +50,15 @@ public class UserSessionInitializer {
private static final String ACCESS_LOG_LOGIN = "LOGIN";
// SONAR-6546 these urls should be get from WebService
- private static final Set<String> SKIPPED_URLS = ImmutableSet.of(
+ private static final Set<String> SKIPPED_URLS = Set.of(
"/batch/index", "/batch/file",
"/maintenance/*", "/setup/*",
"/sessions/*", "/oauth2/callback/*",
"/api/system/db_migration_status", "/api/system/status", "/api/system/migrate_db",
"/api/server/version",
"/api/users/identity_providers", "/api/l10n/index",
- "/api/authentication/login", "/api/authentication/logout", "/api/authentication/validate");
+ "/api/authentication/login", "/api/authentication/logout", "/api/authentication/validate",
+ "/api/project_badges/measure", "/api/project_badges/quality_gate");
private static final Set<String> URL_USING_PASSCODE = ImmutableSet.of(
"/api/ce/info", "/api/ce/pause", "/api/ce/resume", "/api/system/health", "/api/system/analytics", "/api/system/migrate_es");
diff --git a/server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/UserSessionInitializerTest.java b/server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/UserSessionInitializerTest.java
index 74a87664bdf..df26d723052 100644
--- a/server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/UserSessionInitializerTest.java
+++ b/server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/UserSessionInitializerTest.java
@@ -32,7 +32,6 @@ import org.sonar.api.utils.System2;
import org.sonar.db.DbTester;
import org.sonar.server.authentication.event.AuthenticationEvent;
import org.sonar.server.authentication.event.AuthenticationEvent.Method;
-import org.sonar.server.authentication.event.AuthenticationEvent.Provider;
import org.sonar.server.authentication.event.AuthenticationEvent.Source;
import org.sonar.server.authentication.event.AuthenticationException;
import org.sonar.server.tester.AnonymousMockUserSession;
@@ -42,7 +41,6 @@ import org.sonar.server.user.UserSession;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
@@ -96,6 +94,10 @@ public class UserSessionInitializerTest {
assertPathIsIgnored("/api/users/identity_providers");
assertPathIsIgnored("/api/l10n/index");
+ // exclude project_badge url, as they can be auth. by a token as queryparam
+ assertPathIsIgnored("/api/project_badges/measure");
+ assertPathIsIgnored("/api/project_badges/quality_gate");
+
// exlude passcode urls
assertPathIsIgnoredWithAnonymousAccess("/api/ce/info");
assertPathIsIgnoredWithAnonymousAccess("/api/ce/pause");
diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/badge/ws/MeasureAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/badge/ws/MeasureAction.java
index b69dc764eb3..e85c633816a 100644
--- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/badge/ws/MeasureAction.java
+++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/badge/ws/MeasureAction.java
@@ -141,6 +141,7 @@ public class MeasureAction implements ProjectBadgesWsAction {
response.stream().setMediaType(SVG);
String metricKey = request.mandatoryParam(PARAM_METRIC);
try (DbSession dbSession = dbClient.openSession(false)) {
+ support.validateToken(request);
BranchDto branch = support.getBranch(dbSession, request);
MetricDto metric = dbClient.metricDao().selectByKey(dbSession, metricKey);
checkState(metric != null && metric.isEnabled(), "Metric '%s' hasn't been found", metricKey);
@@ -205,5 +206,4 @@ public class MeasureAction implements ProjectBadgesWsAction {
return value;
}
-
}
diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/badge/ws/ProjectBadgesSupport.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/badge/ws/ProjectBadgesSupport.java
index 95dd958a4d7..981f8a7b947 100644
--- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/badge/ws/ProjectBadgesSupport.java
+++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/badge/ws/ProjectBadgesSupport.java
@@ -19,31 +19,34 @@
*/
package org.sonar.server.badge.ws;
+import javax.annotation.Nullable;
import org.sonar.api.server.ws.Request;
import org.sonar.api.server.ws.WebService;
+import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.component.BranchDto;
+import org.sonar.db.project.ProjectBadgeTokenDto;
import org.sonar.db.project.ProjectDto;
import org.sonar.server.component.ComponentFinder;
import org.sonar.server.exceptions.NotFoundException;
-import org.sonar.server.user.UserSession;
-import static org.sonar.api.web.UserRole.USER;
import static org.sonar.db.component.BranchType.BRANCH;
import static org.sonar.server.ws.KeyExamples.KEY_BRANCH_EXAMPLE_001;
import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001;
+import static org.sonar.server.ws.KeyExamples.PROJECT_BADGE_TOKEN_EXAMPLE;
public class ProjectBadgesSupport {
private static final String PARAM_PROJECT = "project";
private static final String PARAM_BRANCH = "branch";
+ private static final String PARAM_TOKEN = "token";
- private final UserSession userSession;
private final ComponentFinder componentFinder;
+ private final DbClient dbClient;
- public ProjectBadgesSupport(UserSession userSession, ComponentFinder componentFinder) {
- this.userSession = userSession;
+ public ProjectBadgesSupport(ComponentFinder componentFinder, DbClient dbClient) {
this.componentFinder = componentFinder;
+ this.dbClient = dbClient;
}
void addProjectAndBranchParams(WebService.NewAction action) {
@@ -55,6 +58,10 @@ public class ProjectBadgesSupport {
.createParam(PARAM_BRANCH)
.setDescription("Branch key")
.setExampleValue(KEY_BRANCH_EXAMPLE_001);
+ action
+ .createParam(PARAM_TOKEN)
+ .setDescription("Project badge token")
+ .setExampleValue(PROJECT_BADGE_TOKEN_EXAMPLE);
}
BranchDto getBranch(DbSession dbSession, Request request) {
@@ -62,10 +69,6 @@ public class ProjectBadgesSupport {
String projectKey = request.mandatoryParam(PARAM_PROJECT);
String branchName = request.param(PARAM_BRANCH);
ProjectDto project = componentFinder.getProjectOrApplicationByKey(dbSession, projectKey);
- userSession.checkProjectPermission(USER, project);
- if (project.isPrivate()) {
- throw generateInvalidProjectException();
- }
BranchDto branch;
if (branchName == null) {
@@ -87,4 +90,25 @@ public class ProjectBadgesSupport {
private static ProjectBadgesException generateInvalidProjectException() {
return new ProjectBadgesException("Project is invalid");
}
+
+ public void validateToken(Request request) {
+ try (DbSession dbSession = dbClient.openSession(false)) {
+ String projectKey = request.mandatoryParam(PARAM_PROJECT);
+ ProjectDto projectDto;
+ try {
+ projectDto = componentFinder.getProjectOrApplicationByKey(dbSession, projectKey);
+ } catch (NotFoundException e) {
+ throw new NotFoundException("Project has not been found");
+ }
+ String token = request.param(PARAM_TOKEN);
+ if (projectDto.isPrivate() && !isTokenValid(dbSession, projectDto, token)) {
+ throw generateInvalidProjectException();
+ }
+ }
+ }
+
+ private boolean isTokenValid(DbSession dbSession, ProjectDto projectDto, @Nullable String token) {
+ ProjectBadgeTokenDto projectBadgeTokenDto = dbClient.projectBadgeTokenDao().selectTokenByProject(dbSession, projectDto);
+ return token != null && projectBadgeTokenDto != null && token.equals(projectBadgeTokenDto.getToken());
+ }
}
diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/badge/ws/QualityGateAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/badge/ws/QualityGateAction.java
index 3e4f5951360..cf20fd55e66 100644
--- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/badge/ws/QualityGateAction.java
+++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/badge/ws/QualityGateAction.java
@@ -43,7 +43,7 @@ import static org.sonar.server.badge.ws.ETagUtils.RFC1123_DATE;
import static org.sonar.server.badge.ws.ETagUtils.getETag;
import static org.sonarqube.ws.MediaTypes.SVG;
-public class QualityGateAction implements ProjectBadgesWsAction {
+public class QualityGateAction implements ProjectBadgesWsAction {
private final DbClient dbClient;
private final ProjectBadgesSupport support;
@@ -71,6 +71,7 @@ public class QualityGateAction implements ProjectBadgesWsAction {
response.setHeader("Cache-Control", "no-cache");
response.stream().setMediaType(SVG);
try (DbSession dbSession = dbClient.openSession(false)) {
+ support.validateToken(request);
BranchDto branch = support.getBranch(dbSession, request);
Level qualityGateStatus = getQualityGate(dbSession, branch);
String result = svgGenerator.generateQualityGate(qualityGateStatus);
diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/badge/ws/MeasureActionTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/badge/ws/MeasureActionTest.java
index 19bb7c6b2b0..e4e55bf1da3 100644
--- a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/badge/ws/MeasureActionTest.java
+++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/badge/ws/MeasureActionTest.java
@@ -84,7 +84,7 @@ public class MeasureActionTest {
private WsActionTester ws = new WsActionTester(
new MeasureAction(
db.getDbClient(),
- new ProjectBadgesSupport(userSession, new ComponentFinder(db.getDbClient(), null)),
+ new ProjectBadgesSupport(new ComponentFinder(db.getDbClient(), null), db.getDbClient()),
new SvgGenerator(mapSettings.asConfig())));
@Test
@@ -347,19 +347,6 @@ public class MeasureActionTest {
}
@Test
- public void return_error_if_unauthorized() throws ParseException {
- ComponentDto project = db.components().insertPublicProject();
- MetricDto metric = db.measures().insertMetric(m -> m.setKey(BUGS_KEY));
-
- TestResponse response = ws.newRequest()
- .setParam("project", project.getKey())
- .setParam("metric", metric.getKey())
- .execute();
-
- checkError(response, "Insufficient privileges");
- }
-
- @Test
public void fail_on_invalid_quality_gate() {
ComponentDto project = db.components().insertPublicProject();
userSession.registerComponents(project);
@@ -419,7 +406,8 @@ public class MeasureActionTest {
.containsExactlyInAnyOrder(
tuple("project", true),
tuple("branch", false),
- tuple("metric", true));
+ tuple("metric", true),
+ tuple("token", false));
}
private void checkSvg(TestResponse response, String expectedLabel, String expectedValue, Color expectedColorValue) {
diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/badge/ws/QualityGateActionTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/badge/ws/QualityGateActionTest.java
index b5b66edd5dc..f139f890bf0 100644
--- a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/badge/ws/QualityGateActionTest.java
+++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/badge/ws/QualityGateActionTest.java
@@ -64,7 +64,7 @@ public class QualityGateActionTest {
private WsActionTester ws = new WsActionTester(
new QualityGateAction(db.getDbClient(),
- new ProjectBadgesSupport(userSession, new ComponentFinder(db.getDbClient(), null)),
+ new ProjectBadgesSupport(new ComponentFinder(db.getDbClient(), null), db.getDbClient()),
new SvgGenerator(mapSettings.asConfig())));
@Test
@@ -290,7 +290,8 @@ public class QualityGateActionTest {
.extracting(Param::key, Param::isRequired)
.containsExactlyInAnyOrder(
tuple("project", true),
- tuple("branch", false));
+ tuple("branch", false),
+ tuple("token", false));
}
private MetricDto createQualityGateMetric() {
diff --git a/server/sonar-webserver-ws/src/main/java/org/sonar/server/ws/KeyExamples.java b/server/sonar-webserver-ws/src/main/java/org/sonar/server/ws/KeyExamples.java
index 498f6e251d8..34eeb435a63 100644
--- a/server/sonar-webserver-ws/src/main/java/org/sonar/server/ws/KeyExamples.java
+++ b/server/sonar-webserver-ws/src/main/java/org/sonar/server/ws/KeyExamples.java
@@ -35,6 +35,8 @@ public class KeyExamples {
public static final String NAME_WEBHOOK_EXAMPLE_001 = "My Webhook";
public static final String URL_WEBHOOK_EXAMPLE_001 = "https://www.my-webhook-listener.com/sonar";
+ public static final String PROJECT_BADGE_TOKEN_EXAMPLE = "8bb493196edb5896ccb64582499895f187a2ae8f";
+
private KeyExamples() {
// prevent instantiation
}