]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-13426 Validate token for public project when auth is forced
authorPierre <pierre.guillot@sonarsource.com>
Wed, 17 Nov 2021 15:11:26 +0000 (16:11 +0100)
committersonartech <sonartech@sonarsource.com>
Thu, 18 Nov 2021 20:03:32 +0000 (20:03 +0000)
server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/UserSessionInitializer.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/badge/ws/ProjectBadgesSupport.java
server/sonar-webserver-webapi/src/test/java/org/sonar/server/badge/ws/MeasureActionTest.java
server/sonar-webserver-webapi/src/test/java/org/sonar/server/badge/ws/QualityGateActionTest.java

index 5582633876af578f344aef6f87c2b579620272da..fd82dca23184c05b16b32a964f652e96a3dcc0bb 100644 (file)
@@ -19,7 +19,6 @@
  */
 package org.sonar.server.authentication;
 
-import com.google.common.collect.ImmutableSet;
 import java.util.Set;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
index 405cdef4dcadd4fa91ac4567454f6fa5fdf67ac0..b803c2ab4a0ea3b0f6eca97415eb173c3f5dcd9c 100644 (file)
@@ -20,6 +20,7 @@
 package org.sonar.server.badge.ws;
 
 import javax.annotation.Nullable;
+import org.sonar.api.config.Configuration;
 import org.sonar.api.server.ws.Request;
 import org.sonar.api.server.ws.WebService;
 import org.sonar.db.DbClient;
@@ -30,6 +31,8 @@ import org.sonar.db.project.ProjectDto;
 import org.sonar.server.component.ComponentFinder;
 import org.sonar.server.exceptions.NotFoundException;
 
+import static org.sonar.api.CoreProperties.CORE_FORCE_AUTHENTICATION_DEFAULT_VALUE;
+import static org.sonar.api.CoreProperties.CORE_FORCE_AUTHENTICATION_PROPERTY;
 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;
@@ -44,10 +47,12 @@ public class ProjectBadgesSupport {
 
   private final ComponentFinder componentFinder;
   private final DbClient dbClient;
+  private final Configuration config;
 
-  public ProjectBadgesSupport(ComponentFinder componentFinder, DbClient dbClient) {
+  public ProjectBadgesSupport(ComponentFinder componentFinder, DbClient dbClient, Configuration config) {
     this.componentFinder = componentFinder;
     this.dbClient = dbClient;
+    this.config = config;
   }
 
   void addProjectAndBranchParams(WebService.NewAction action) {
@@ -101,8 +106,9 @@ public class ProjectBadgesSupport {
       } catch (NotFoundException e) {
         throw new NotFoundException(PROJECT_HAS_NOT_BEEN_FOUND);
       }
-      String token = request.param(PARAM_TOKEN);
-      if (projectDto.isPrivate() && !isTokenValid(dbSession, projectDto, token)) {
+      boolean tokenInvalid = !isTokenValid(dbSession, projectDto, request.param(PARAM_TOKEN));
+      boolean forceAuthEnabled = config.getBoolean(CORE_FORCE_AUTHENTICATION_PROPERTY).orElse(CORE_FORCE_AUTHENTICATION_DEFAULT_VALUE);
+      if ((projectDto.isPrivate() || forceAuthEnabled) && tokenInvalid) {
         throw new NotFoundException(PROJECT_HAS_NOT_BEEN_FOUND);
       }
     }
index e319a912e71475f3b0f61e48a7b0e0015b3e9b68..c94cc4258ccb13f37bb137d97fda78883aab8d6d 100644 (file)
@@ -26,20 +26,25 @@ import java.text.ParseException;
 import java.text.SimpleDateFormat;
 import java.util.Date;
 import java.util.Locale;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.ExpectedException;
 import org.junit.runner.RunWith;
+import org.sonar.api.CoreProperties;
+import org.sonar.api.config.Configuration;
 import org.sonar.api.config.internal.MapSettings;
 import org.sonar.api.measures.CoreMetrics;
 import org.sonar.api.measures.Metric.Level;
 import org.sonar.api.server.ws.WebService;
 import org.sonar.api.server.ws.WebService.Param;
+import org.sonar.core.util.UuidFactoryFast;
 import org.sonar.db.DbTester;
 import org.sonar.db.component.BranchType;
 import org.sonar.db.component.ComponentDto;
 import org.sonar.db.component.ComponentTesting;
 import org.sonar.db.metric.MetricDto;
+import org.sonar.db.project.ProjectDto;
 import org.sonar.db.user.UserDto;
 import org.sonar.server.badge.ws.SvgGenerator.Color;
 import org.sonar.server.component.ComponentFinder;
@@ -79,14 +84,20 @@ public class MeasureActionTest {
   @Rule
   public DbTester db = DbTester.create();
 
-  private MapSettings mapSettings = new MapSettings().setProperty("sonar.sonarcloud.enabled", false);
+  private final MapSettings mapSettings = new MapSettings().setProperty("sonar.sonarcloud.enabled", false);
+  private final Configuration config = mapSettings.asConfig();
 
-  private WsActionTester ws = new WsActionTester(
+  private final WsActionTester ws = new WsActionTester(
     new MeasureAction(
       db.getDbClient(),
-      new ProjectBadgesSupport(new ComponentFinder(db.getDbClient(), null), db.getDbClient()),
+      new ProjectBadgesSupport(new ComponentFinder(db.getDbClient(), null), db.getDbClient(), config),
       new SvgGenerator(mapSettings.asConfig())));
 
+  @Before
+  public void before(){
+    mapSettings.setProperty(CoreProperties.CORE_FORCE_AUTHENTICATION_PROPERTY, false);
+  }
+
   @Test
   public void int_measure() {
     ComponentDto project = db.components().insertPublicProject();
@@ -318,7 +329,7 @@ public class MeasureActionTest {
   }
 
   @Test
-  public void return_error_on_private_project() throws ParseException {
+  public void return_error_on_private_project_without_token() throws ParseException {
     ComponentDto project = db.components().insertPrivateProject();
     UserDto user = db.users().insertUser();
     userSession.logIn(user).addProjectPermission(USER, project);
@@ -332,6 +343,57 @@ public class MeasureActionTest {
     checkError(response, "Project has not been found");
   }
 
+  @DataProvider
+  public static Object[][] publicProject_forceAuth_accessGranted(){
+    return new Object[][] {
+      // public project, force auth : works depending on token's validity
+      {true, true, true, true},
+      {true, true, false, false},
+
+      // public project, no force auth : access always granted
+      {true, false, true, true},
+      {true, false, false, true},
+
+      // private project, regardless of force auth, access granted depending on token's validity:
+      {false, true, true, true},
+      {false, true, false, false},
+      {false, false, true, true},
+      {false, false, false, false},
+    };
+  }
+
+  @Test
+  @UseDataProvider("publicProject_forceAuth_accessGranted")
+  public void badge_accessible_on_private_project_with_token(boolean publicProject, boolean forceAuth,
+                                                               boolean validToken, boolean accessGranted) throws ParseException {
+    ComponentDto projectAsComponent = publicProject ? db.components().insertPublicProject() : db.components().insertPrivateProject();
+    userSession.registerComponents(projectAsComponent);
+    MetricDto metric = db.measures().insertMetric(m -> m.setKey(BUGS_KEY).setValueType(INT.name()));
+
+    db.measures().insertLiveMeasure(projectAsComponent, metric, m -> m.setValue(10_000d));
+    ProjectDto project = db.getDbClient().projectDao().selectProjectByKey(db.getSession(), projectAsComponent.getKey())
+      .orElseThrow(() -> new IllegalStateException("project not found"));
+
+    String token = db.getDbClient().projectBadgeTokenDao()
+      .insert(db.getSession(), UuidFactoryFast.getInstance().create(), project, "user-uuid", "user-login")
+      .getToken();
+    db.commit();
+
+    mapSettings.setProperty(CoreProperties.CORE_FORCE_AUTHENTICATION_PROPERTY, forceAuth);
+
+    TestResponse response = ws.newRequest()
+      .setParam("project", projectAsComponent.getKey())
+      .setParam("metric", metric.getKey())
+      .setParam("token", validToken ? token : "invalid-token")
+      .execute();
+
+    if(accessGranted){
+      checkSvg(response, "bugs", "10k", DEFAULT);
+    }else{
+      checkError(response, "Project has not been found");
+    }
+  }
+
   @Test
   public void return_error_on_provisioned_project() throws ParseException {
     ComponentDto project = db.components().insertPublicProject();
index 29fc9c9d4d2bfaeac9260eedc1fb4995a75b6c91..de3c268b23a9f3af9d655600fe01a726b214cb17 100644 (file)
  */
 package org.sonar.server.badge.ws;
 
+import com.tngtech.java.junit.dataprovider.DataProvider;
+import com.tngtech.java.junit.dataprovider.DataProviderRunner;
+import com.tngtech.java.junit.dataprovider.UseDataProvider;
 import java.text.ParseException;
 import java.text.SimpleDateFormat;
 import java.util.Arrays;
 import java.util.Date;
 import java.util.Locale;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.ExpectedException;
+import org.junit.runner.RunWith;
+import org.sonar.api.CoreProperties;
+import org.sonar.api.config.Configuration;
 import org.sonar.api.config.internal.MapSettings;
 import org.sonar.api.measures.Metric.Level;
 import org.sonar.api.server.ws.WebService;
 import org.sonar.api.server.ws.WebService.Param;
+import org.sonar.core.util.UuidFactoryFast;
 import org.sonar.db.DbTester;
 import org.sonar.db.component.ComponentDto;
 import org.sonar.db.component.ComponentTesting;
 import org.sonar.db.measure.LiveMeasureDto;
 import org.sonar.db.metric.MetricDto;
+import org.sonar.db.project.ProjectDto;
 import org.sonar.db.user.UserDto;
 import org.sonar.server.component.ComponentFinder;
 import org.sonar.server.tester.UserSessionRule;
@@ -51,6 +60,7 @@ import static org.sonar.api.measures.Metric.ValueType.LEVEL;
 import static org.sonar.api.web.UserRole.USER;
 import static org.sonar.db.component.BranchType.BRANCH;
 
+@RunWith(DataProviderRunner.class)
 public class QualityGateActionTest {
 
   @Rule
@@ -60,13 +70,20 @@ public class QualityGateActionTest {
   @Rule
   public DbTester db = DbTester.create();
 
-  private final MapSettings mapSettings = new MapSettings().setProperty("sonar.sonarcloud.enabled", false);
+  private final MapSettings mapSettings = new MapSettings().setProperty("sonar.sonarcloud.enabled", false).setProperty(CoreProperties.CORE_FORCE_AUTHENTICATION_PROPERTY, false);
+  private final Configuration config = mapSettings.asConfig();
 
-  private WsActionTester ws = new WsActionTester(
+  private final WsActionTester ws = new WsActionTester(
     new QualityGateAction(db.getDbClient(),
-      new ProjectBadgesSupport(new ComponentFinder(db.getDbClient(), null), db.getDbClient()),
+      new ProjectBadgesSupport(new ComponentFinder(db.getDbClient(), null), db.getDbClient(), config),
       new SvgGenerator(mapSettings.asConfig())));
 
+
+  @Before
+  public void before(){
+    mapSettings.setProperty(CoreProperties.CORE_FORCE_AUTHENTICATION_PROPERTY, false);
+  }
+
   @Test
   public void quality_gate_passed() {
     ComponentDto project = db.components().insertPublicProject();
@@ -95,6 +112,58 @@ public class QualityGateActionTest {
     checkResponse(response, ERROR);
   }
 
+
+  @DataProvider
+  public static Object[][] publicProject_forceAuth_validToken_accessGranted(){
+    return new Object[][] {
+      // public project, force auth : access granted depending on token's validity
+      {true, true, true, true},
+      {true, true, false, false},
+
+      // public project, no force auth : access always granted
+      {true, false, true, true},
+      {true, false, false, true},
+
+      // private project, regardless of force auth, access granted depending on token's validity:
+      {false, true, true, true},
+      {false, true, false, false},
+      {false, false, true, true},
+      {false, false, false, false},
+    };
+  }
+
+  @Test
+  @UseDataProvider("publicProject_forceAuth_validToken_accessGranted")
+  public void badge_accessible_on_private_project_with_token(boolean publicProject, boolean forceAuth,
+                                                               boolean validToken, boolean accessGranted) throws ParseException {
+    ComponentDto projectAsComponent = publicProject ? db.components().insertPublicProject() : db.components().insertPrivateProject();
+    userSession.registerComponents(projectAsComponent);
+    MetricDto metric = createQualityGateMetric();
+
+    db.measures().insertLiveMeasure(projectAsComponent, metric, m -> m.setData(OK.name()));
+    ProjectDto project = db.getDbClient().projectDao().selectProjectByKey(db.getSession(), projectAsComponent.getKey())
+      .orElseThrow(() -> new IllegalStateException("project not found"));
+
+    String token = db.getDbClient().projectBadgeTokenDao()
+      .insert(db.getSession(), UuidFactoryFast.getInstance().create(), project, "user-uuid", "user-login")
+      .getToken();
+    db.commit();
+
+    mapSettings.setProperty(CoreProperties.CORE_FORCE_AUTHENTICATION_PROPERTY, forceAuth);
+
+    TestResponse response = ws.newRequest()
+      .setParam("project", projectAsComponent.getKey())
+      .setParam("token", validToken ? token : "invalid-token")
+      .execute();
+
+    if(accessGranted){
+      checkResponse(response, OK);
+    }else{
+      checkError(response, "Project has not been found");
+    }
+
+  }
+
   @Test
   public void etag_should_be_different_if_quality_gate_is_different() {
     ComponentDto project = db.components().insertPublicProject();