]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-15798 Quality Profile administration should have limited privileges on projects
authorZipeng WU <zipeng.wu@sonarsource.com>
Tue, 12 Jul 2022 14:48:42 +0000 (16:48 +0200)
committersonartech <sonartech@sonarsource.com>
Wed, 13 Jul 2022 20:03:06 +0000 (20:03 +0000)
server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/ws/AddProjectAction.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/ws/DeleteAction.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/ws/QProfileWsSupport.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/ws/RemoveProjectAction.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/ws/SearchAction.java
server/sonar-webserver-webapi/src/main/resources/org/sonar/server/qualityprofile/ws/search-example.json
server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualityprofile/ws/AddProjectActionTest.java
server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualityprofile/ws/DeleteActionTest.java
server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualityprofile/ws/RemoveProjectActionTest.java
server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualityprofile/ws/SearchActionTest.java

index d5366352372696a78883d2875194625826dc8e64..0ee10c415a407456ac2a216bf06402d884c3b15b 100644 (file)
@@ -87,7 +87,7 @@ public class AddProjectAction implements QProfileWsAction {
 
       ProjectDto project = loadProject(dbSession, request);
       QProfileDto profile = wsSupport.getProfile(dbSession, QProfileReference.fromName(request));
-      checkPermissions(dbSession, profile, project);
+      checkPermissions(profile, project);
       QProfileDto currentProfile = dbClient.qualityProfileDao().selectAssociatedToProjectAndLanguage(dbSession, project, profile.getLanguage());
 
       QProfileDto deactivatedProfile = null;
@@ -112,15 +112,13 @@ public class AddProjectAction implements QProfileWsAction {
     response.noContent();
   }
 
-
   private ProjectDto loadProject(DbSession dbSession, Request request) {
     String projectKey = request.mandatoryParam(PARAM_PROJECT);
     return componentFinder.getProjectByKey(dbSession, projectKey);
   }
 
-  private void checkPermissions(DbSession dbSession, QProfileDto profile, ProjectDto project) {
-    if (wsSupport.canEdit(dbSession, profile)
-      || userSession.hasProjectPermission(UserRole.ADMIN, project)) {
+  private void checkPermissions(QProfileDto profile, ProjectDto project) {
+    if (wsSupport.canAdministrate(profile) || userSession.hasProjectPermission(UserRole.ADMIN, project)) {
       return;
     }
 
index 468a6b7a3ab53bb50b6d6759b5ddd8823130e105..1d184113819471056cfb38f63a974c5aa9b48f2c 100644 (file)
@@ -77,7 +77,7 @@ public class DeleteAction implements QProfileWsAction {
 
     try (DbSession dbSession = dbClient.openSession(false)) {
       QProfileDto profile = wsSupport.getProfile(dbSession, QProfileReference.fromName(request));
-      wsSupport.checkCanEdit(dbSession, profile);
+      wsSupport.checkCanAdministrate(profile);
 
       Collection<QProfileDto> descendants = selectDescendants(dbSession, profile);
       ensureNoneIsMarkedAsDefault(dbSession, profile, descendants);
index bb4bd5129e49ce9e7ed1c40be12d6760231e898c..59ed4c78e5f189dfb710ea6649ff4538bd780863 100644 (file)
@@ -91,19 +91,22 @@ public class QProfileWsSupport {
   }
 
   boolean canEdit(DbSession dbSession, QProfileDto profile) {
-    if (profile.isBuiltIn() || !userSession.isLoggedIn()) {
-      return false;
-    }
-    if (userSession.hasPermission(GlobalPermission.ADMINISTER_QUALITY_PROFILES)) {
+    if (canAdministrate(profile)) {
       return true;
     }
-
     UserDto user = dbClient.userDao().selectByLogin(dbSession, userSession.getLogin());
     checkState(user != null, "User from session does not exist");
     return dbClient.qProfileEditUsersDao().exists(dbSession, profile, user)
       || dbClient.qProfileEditGroupsDao().exists(dbSession, profile, userSession.getGroups());
   }
 
+  boolean canAdministrate(QProfileDto profile) {
+    if (profile.isBuiltIn() || !userSession.isLoggedIn()) {
+      return false;
+    }
+    return userSession.hasPermission(GlobalPermission.ADMINISTER_QUALITY_PROFILES);
+  }
+
   public void checkCanEdit(DbSession dbSession, QProfileDto profile) {
     checkNotBuiltIn(profile);
     if (!canEdit(dbSession, profile)) {
@@ -111,6 +114,13 @@ public class QProfileWsSupport {
     }
   }
 
+  public void checkCanAdministrate(QProfileDto profile) {
+    checkNotBuiltIn(profile);
+    if (!canAdministrate(profile)) {
+      throw insufficientPrivilegesException();
+    }
+  }
+
   void checkNotBuiltIn(QProfileDto profile) {
     checkRequest(!profile.isBuiltIn(), "Operation forbidden for built-in Quality Profile '%s' with language '%s'", profile.getName(), profile.getLanguage());
   }
index 4ab8d7cf5272e651e079e78d46a27b56c44c8ece..f1683a430ab58e8a15aa4c0ecc2e96a14516c480 100644 (file)
@@ -85,7 +85,7 @@ public class RemoveProjectAction implements QProfileWsAction {
     try (DbSession dbSession = dbClient.openSession(false)) {
       ProjectDto project = loadProject(dbSession, request);
       QProfileDto profile = wsSupport.getProfile(dbSession, QProfileReference.fromName(request));
-      checkPermissions(dbSession, profile, project);
+      checkPermissions(profile, project);
 
       dbClient.qualityProfileDao().deleteProjectProfileAssociation(dbSession, project, profile);
       dbSession.commit();
@@ -109,8 +109,8 @@ public class RemoveProjectAction implements QProfileWsAction {
     return componentFinder.getProjectByKey(dbSession, projectKey);
   }
 
-  private void checkPermissions(DbSession dbSession, QProfileDto profile, ProjectDto project) {
-    if (wsSupport.canEdit(dbSession, profile) || userSession.hasProjectPermission(UserRole.ADMIN, project)) {
+  private void checkPermissions(QProfileDto profile, ProjectDto project) {
+    if (wsSupport.canAdministrate(profile) || userSession.hasProjectPermission(UserRole.ADMIN, project)) {
       return;
     }
 
index 4c696b13ff8ccd2f00a038659d5ad0306f48b171..99ce822f25980ba38ed11ca8e4bc71dd476738f0 100644 (file)
@@ -265,8 +265,8 @@ public class SearchAction implements QProfileWsAction {
         .setEdit(!profile.isBuiltIn() && (isGlobalQProfileAdmin || data.isEditable(profile)))
         .setSetAsDefault(!isDefault && isGlobalQProfileAdmin)
         .setCopy(isGlobalQProfileAdmin)
-        .setDelete(!isDefault && !profile.isBuiltIn() && (isGlobalQProfileAdmin || data.isEditable(profile)))
-        .setAssociateProjects(!isDefault && (isGlobalQProfileAdmin || data.isEditable(profile))));
+        .setDelete(!isDefault && !profile.isBuiltIn() && isGlobalQProfileAdmin)
+        .setAssociateProjects(!isDefault && isGlobalQProfileAdmin));
     }
     return response.build();
   }
index eb13119b881487126eb4c37567239b3e92f404c6..18555bcd1fafa3b00a562197c2822c7facf1fd11 100644 (file)
@@ -40,8 +40,8 @@
         "edit": true,
         "setAsDefault": false,
         "copy": false,
-        "delete": true,
-        "associateProjects": true
+        "delete": false,
+        "associateProjects": false
       }
     },
     {
index 52a39342278fdeed085cf8ff56d7b7e701480552..a31d95fc0f9ddaf5b3addbceb846649b1680e282 100644 (file)
@@ -28,6 +28,7 @@ import org.sonar.api.web.UserRole;
 import org.sonar.db.DbClient;
 import org.sonar.db.DbTester;
 import org.sonar.db.component.ComponentDto;
+import org.sonar.db.permission.GlobalPermission;
 import org.sonar.db.project.ProjectDto;
 import org.sonar.db.qualityprofile.QProfileDto;
 import org.sonar.db.user.UserDto;
@@ -96,12 +97,12 @@ public class AddProjectActionTest {
   }
 
   @Test
-  public void as_qprofile_editor() {
+  public void as_qprofile_editor_and_global_admin() {
     UserDto user = db.users().insertUser();
     QProfileDto qualityProfile = db.qualityProfiles().insert(qp -> qp.setLanguage(LANGUAGE_1));
     db.qualityProfiles().addUserPermission(qualityProfile, user);
     ProjectDto project = db.components().insertPrivateProjectDto();
-    userSession.logIn(user);
+    userSession.logIn(user).addPermission(GlobalPermission.ADMINISTER_QUALITY_PROFILES);
 
     call(project, qualityProfile);
 
index cd153c77bf172a5432a11207111246cda3208de5..4d16a9ff66009e6b51d080ee85cc1dd30213aac0 100644 (file)
@@ -30,6 +30,7 @@ import org.sonar.core.util.UuidFactoryFast;
 import org.sonar.db.DbClient;
 import org.sonar.db.DbSession;
 import org.sonar.db.DbTester;
+import org.sonar.db.permission.GlobalPermission;
 import org.sonar.db.project.ProjectDto;
 import org.sonar.db.qualityprofile.QProfileDto;
 import org.sonar.db.user.UserDto;
@@ -93,11 +94,11 @@ public class DeleteActionTest {
   }
 
   @Test
-  public void as_qprofile_editor() {
+  public void as_qprofile_editor_and_global_admin() {
     QProfileDto profile = createProfile();
     UserDto user = db.users().insertUser();
     db.qualityProfiles().addUserPermission(profile, user);
-    userSession.logIn(user);
+    userSession.logIn(user).addPermission(GlobalPermission.ADMINISTER_QUALITY_PROFILES);
 
     TestResponse response = ws.newRequest()
       .setMethod("POST")
index 0fd260ed919b8f259a399c1baf92cdbb3a71e031..45683a0bb18e979ce772fde473b321128aba1151 100644 (file)
@@ -31,6 +31,7 @@ import org.sonar.db.DbClient;
 import org.sonar.db.DbTester;
 import org.sonar.db.component.ComponentDto;
 import org.sonar.db.component.ResourceTypesRule;
+import org.sonar.db.permission.GlobalPermission;
 import org.sonar.db.project.ProjectDto;
 import org.sonar.db.qualityprofile.QProfileDto;
 import org.sonar.db.user.UserDto;
@@ -132,13 +133,13 @@ public class RemoveProjectActionTest {
   }
 
   @Test
-  public void as_qprofile_editor() {
+  public void as_qprofile_editor_and_global_admin() {
     ProjectDto project = db.components().insertPrivateProjectDto();
     QProfileDto profile = db.qualityProfiles().insert(p -> p.setLanguage(LANGUAGE_1));
     db.qualityProfiles().associateWithProject(project, profile);
     UserDto user = db.users().insertUser();
     db.qualityProfiles().addUserPermission(profile, user);
-    userSession.logIn(user);
+    userSession.logIn(user).addPermission(GlobalPermission.ADMINISTER_QUALITY_PROFILES);
 
     call(project, profile);
 
@@ -146,6 +147,20 @@ public class RemoveProjectActionTest {
     verify(qualityProfileChangeEventService).publishRuleActivationToSonarLintClients(project, null, profile);
   }
 
+  @Test
+  public void as_qprofile_editor_fail_if_not_project_nor_global_admin() {
+    ProjectDto project = db.components().insertPrivateProjectDto();
+    QProfileDto profile = db.qualityProfiles().insert(p -> p.setLanguage(LANGUAGE_1));
+    db.qualityProfiles().associateWithProject(project, profile);
+    UserDto user = db.users().insertUser();
+    db.qualityProfiles().addUserPermission(profile, user);
+    userSession.logIn(user);
+
+    assertThatThrownBy(() -> call(project, profile))
+      .isInstanceOf(ForbiddenException.class)
+      .hasMessage("Insufficient privileges");
+  }
+
   @Test
   public void fail_if_not_enough_permissions() {
     userSession.logIn(db.users().insertUser());
index 1af11399c92f454d9a3589ba4cc1c586e686fba9..cef65850221cb36c4bb7d31b64a2a1ae2aaca73e 100644 (file)
@@ -287,9 +287,9 @@ public class SearchActionTest {
       .extracting(QualityProfile::getKey, qp -> qp.getActions().getEdit(), qp -> qp.getActions().getCopy(), qp -> qp.getActions().getSetAsDefault(),
         qp -> qp.getActions().getDelete(), qp -> qp.getActions().getAssociateProjects())
       .containsExactlyInAnyOrder(
-        tuple(profile1.getKee(), true, false, false, true, true),
+        tuple(profile1.getKee(), true, false, false, false, false),
         tuple(profile2.getKee(), false, false, false, false, false),
-        tuple(profile3.getKee(), true, false, false, true, true),
+        tuple(profile3.getKee(), true, false, false, false, false),
         tuple(builtInProfile.getKee(), false, false, false, false, false));
     assertThat(result.getActions().getCreate()).isFalse();
   }