]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-1330 Check edit permissions in qprofiles WS
authorJulien Lancelot <julien.lancelot@sonarsource.com>
Wed, 27 Sep 2017 12:33:56 +0000 (14:33 +0200)
committerStas Vilchik <stas.vilchik@sonarsource.com>
Mon, 2 Oct 2017 15:18:15 +0000 (17:18 +0200)
30 files changed:
server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/QProfileEditGroupsDao.java
server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/QProfileEditGroupsMapper.java
server/sonar-db-dao/src/main/resources/org/sonar/db/qualityprofile/QProfileEditGroupsMapper.xml
server/sonar-db-dao/src/test/java/org/sonar/db/qualityprofile/QProfileEditGroupsDaoTest.java
server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/ActivateRuleAction.java
server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/ActivateRulesAction.java
server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/AddGroupAction.java
server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/AddProjectAction.java
server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/AddUserAction.java
server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/ChangeParentAction.java
server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/DeactivateRuleAction.java
server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/DeactivateRulesAction.java
server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/DeleteAction.java
server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/QProfileWsSupport.java
server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/RemoveGroupAction.java
server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/RemoveProjectAction.java
server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/RemoveUserAction.java
server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/RenameAction.java
server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/SearchGroupsAction.java
server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/SearchUsersAction.java
server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/ActivateRuleActionTest.java
server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/ActivateRulesActionTest.java
server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/AddProjectActionTest.java
server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/ChangeParentActionTest.java
server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/DeactivateRuleActionTest.java
server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/DeactivateRulesActionTest.java
server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/DeleteActionTest.java
server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/RemoveProjectActionTest.java
server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/RenameActionTest.java
tests/src/test/java/org/sonarqube/tests/qualityProfile/QualityProfilesEditTest.java

index 93df5f96361d6ef16c925b7d2ac4ef32131ae68d..ce7859e7d0da1088999f3e6dbe71be03f907bb45 100644 (file)
@@ -20,6 +20,7 @@
 package org.sonar.db.qualityprofile;
 
 import java.util.Collection;
+import java.util.Collections;
 import java.util.List;
 import org.sonar.api.utils.System2;
 import org.sonar.db.Dao;
@@ -30,6 +31,7 @@ import org.sonar.db.organization.OrganizationDto;
 import org.sonar.db.user.GroupDto;
 
 import static org.sonar.core.util.stream.MoreCollectors.toList;
+import static org.sonar.db.DatabaseUtils.executeLargeInputs;
 
 public class QProfileEditGroupsDao implements Dao {
 
@@ -40,7 +42,12 @@ public class QProfileEditGroupsDao implements Dao {
   }
 
   public boolean exists(DbSession dbSession, QProfileDto profile, GroupDto group) {
-    return mapper(dbSession).selectByQProfileAndGroup(profile.getKee(), group.getId()) != null;
+    return exists(dbSession, profile, Collections.singletonList(group));
+  }
+
+  public boolean exists(DbSession dbSession, QProfileDto profile, Collection<GroupDto> groups) {
+    return !executeLargeInputs(groups.stream().map(GroupDto::getId).collect(toList()), partition -> mapper(dbSession).selectByQProfileAndGroups(profile.getKee(), partition))
+      .isEmpty();
   }
 
   public int countByQuery(DbSession dbSession, SearchGroupsQuery query) {
@@ -52,8 +59,8 @@ public class QProfileEditGroupsDao implements Dao {
   }
 
   public List<String> selectQProfileUuidsByOrganizationAndGroups(DbSession dbSession, OrganizationDto organization, Collection<GroupDto> groups) {
-    return DatabaseUtils.executeLargeInputs(groups.stream().map(GroupDto::getId).collect(toList()), g ->
-      mapper(dbSession).selectQProfileUuidsByOrganizationAndGroups(organization.getUuid(), g));
+    return DatabaseUtils.executeLargeInputs(groups.stream().map(GroupDto::getId).collect(toList()),
+      g -> mapper(dbSession).selectQProfileUuidsByOrganizationAndGroups(organization.getUuid(), g));
   }
 
   public void insert(DbSession dbSession, QProfileEditGroupsDto dto) {
index 5bdaac413bb46d6f86c90c14e828d7d843be1cd5..717331159004268657390007f27f059b754ba0df 100644 (file)
@@ -25,7 +25,7 @@ import org.sonar.db.Pagination;
 
 public interface QProfileEditGroupsMapper {
 
-  QProfileEditGroupsDto selectByQProfileAndGroup(@Param("qProfileUuid") String qProfileUuid, @Param("groupId") int groupId);
+  List<QProfileEditGroupsDto> selectByQProfileAndGroups(@Param("qProfileUuid") String qProfileUuid, @Param("groupIds") List<Integer> groupIds);
 
   int countByQuery(@Param("query") SearchGroupsQuery query);
 
index 50c1d355e1af51b7e7ea0ca1a71208aad3924e7f..ddc8b09d175cc11146ba643cb83e5675bb2daf5d 100644 (file)
@@ -9,12 +9,14 @@
     qeg.qprofile_uuid as "qProfileUuid"
   </sql>
 
-  <select id="selectByQProfileAndGroup" parameterType="map" resultType="org.sonar.db.qualityprofile.QProfileEditGroupsDto">
+  <select id="selectByQProfileAndGroups" parameterType="map" resultType="org.sonar.db.qualityprofile.QProfileEditGroupsDto">
     select
     <include refid="sqlColumns"/>
     from qprofile_edit_groups qeg
     where
-    qeg.group_id = #{groupId, jdbcType=INTEGER}
+    <foreach collection="groupIds" open="(" close=")" item="groupId" separator=" or ">
+      qeg.group_id = #{groupId, jdbcType=INTEGER}
+    </foreach>
     and qeg.qprofile_uuid = #{qProfileUuid, jdbcType=VARCHAR}
   </select>
 
index 4b370cbdc0a8b9a3aaa324fcbd8da79917fcd3ae..2aaf478f926974f14670cae48b2c940bf3ab8980 100644 (file)
@@ -19,7 +19,6 @@
  */
 package org.sonar.db.qualityprofile;
 
-import java.util.Collections;
 import org.junit.Rule;
 import org.junit.Test;
 import org.sonar.api.utils.System2;
@@ -30,6 +29,8 @@ import org.sonar.db.organization.OrganizationDto;
 import org.sonar.db.user.GroupDto;
 
 import static java.util.Arrays.asList;
+import static java.util.Collections.emptyList;
+import static java.util.Collections.singletonList;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.entry;
 import static org.assertj.core.api.Assertions.tuple;
@@ -62,6 +63,9 @@ public class QProfileEditGroupsDaoTest {
     assertThat(underTest.exists(db.getSession(), profile, anotherGroup)).isFalse();
     assertThat(underTest.exists(db.getSession(), anotherProfile, group)).isFalse();
     assertThat(underTest.exists(db.getSession(), anotherProfile, anotherGroup)).isFalse();
+    assertThat(underTest.exists(db.getSession(), profile, asList(group, anotherGroup))).isTrue();
+    assertThat(underTest.exists(db.getSession(), profile, singletonList(anotherGroup))).isFalse();
+    assertThat(underTest.exists(db.getSession(), profile, emptyList())).isFalse();
   }
 
   @Test
@@ -219,7 +223,7 @@ public class QProfileEditGroupsDaoTest {
       .doesNotContain(anotherProfile.getKee());
     assertThat(underTest.selectQProfileUuidsByOrganizationAndGroups(db.getSession(), organization, asList(group1, group2, group3)))
       .containsExactlyInAnyOrder(profile1.getKee(), profile2.getKee());
-    assertThat(underTest.selectQProfileUuidsByOrganizationAndGroups(db.getSession(), organization, Collections.emptyList())).isEmpty();
+    assertThat(underTest.selectQProfileUuidsByOrganizationAndGroups(db.getSession(), organization, emptyList())).isEmpty();
   }
 
   @Test
index 8d1e5d29794a72eb30d4e69ff789fac986dc7695..b2d43aec94ec0f8c14f7b42e2280f7ec25a18455 100644 (file)
@@ -28,6 +28,7 @@ import org.sonar.api.server.ws.WebService;
 import org.sonar.api.utils.KeyValueFormat;
 import org.sonar.db.DbClient;
 import org.sonar.db.DbSession;
+import org.sonar.db.organization.OrganizationDto;
 import org.sonar.db.qualityprofile.QProfileDto;
 import org.sonar.server.qualityprofile.RuleActivation;
 import org.sonar.server.qualityprofile.RuleActivator;
@@ -36,8 +37,8 @@ import org.sonar.server.user.UserSession;
 import static java.lang.String.format;
 import static org.sonar.core.util.Uuids.UUID_EXAMPLE_01;
 import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.ACTION_ACTIVATE_RULE;
-import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_PARAMS;
 import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_KEY;
+import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_PARAMS;
 import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_RESET;
 import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_RULE;
 import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_SEVERITY;
@@ -60,7 +61,11 @@ public class ActivateRuleAction implements QProfileWsAction {
     WebService.NewAction activate = controller
       .createAction(ACTION_ACTIVATE_RULE)
       .setDescription("Activate a rule on a Quality Profile.<br> " +
-        "Requires to be logged in and the 'Administer Quality Profiles' permission.")
+        "Requires one of the following permissions:" +
+        "<ul>" +
+        "  <li>'Administer Quality Profiles'</li>" +
+        "  <li>Edit right on the specified quality profile</li>" +
+        "</ul>")
       .setHandler(this)
       .setPost(true)
       .setSince("4.4");
@@ -96,8 +101,8 @@ public class ActivateRuleAction implements QProfileWsAction {
     try (DbSession dbSession = dbClient.openSession(false)) {
       String profileKey = request.mandatoryParam(PARAM_KEY);
       QProfileDto profile = wsSupport.getProfile(dbSession, QProfileReference.fromKey(profileKey));
-      wsSupport.checkPermission(dbSession, profile);
-      wsSupport.checkNotBuiltInt(profile);
+      OrganizationDto organization = wsSupport.getOrganization(dbSession, profile);
+      wsSupport.checkCanEdit(dbSession, organization, profile);
       RuleActivation activation = readActivation(request);
       ruleActivator.activateAndCommit(dbSession, activation, profile);
     }
index cf40a39cf2ff5803bb9e7e2e1fbc202b8921ba2d..1e2548e110a340231d45c6130daf0cabf234f8db 100644 (file)
@@ -25,6 +25,7 @@ import org.sonar.api.server.ws.Response;
 import org.sonar.api.server.ws.WebService;
 import org.sonar.db.DbClient;
 import org.sonar.db.DbSession;
+import org.sonar.db.organization.OrganizationDto;
 import org.sonar.db.qualityprofile.QProfileDto;
 import org.sonar.server.qualityprofile.BulkChangeResult;
 import org.sonar.server.qualityprofile.RuleActivator;
@@ -59,7 +60,11 @@ public class ActivateRulesAction implements QProfileWsAction {
     WebService.NewAction activate = controller
       .createAction(ACTION_ACTIVATE_RULES)
       .setDescription("Bulk-activate rules on one quality profile.<br> " +
-        "Requires to be logged in and the 'Administer Quality Profiles' permission.")
+        "Requires one of the following permissions:" +
+        "<ul>" +
+        "  <li>'Administer Quality Profiles'</li>" +
+        "  <li>Edit right on the specified quality profile</li>" +
+        "</ul>")
       .setPost(true)
       .setSince("4.4")
       .setHandler(this);
@@ -85,7 +90,8 @@ public class ActivateRulesAction implements QProfileWsAction {
     BulkChangeResult result;
     try (DbSession dbSession = dbClient.openSession(false)) {
       QProfileDto profile = wsSupport.getProfile(dbSession, fromKey(qualityProfileKey));
-      wsSupport.checkPermission(dbSession, profile);
+      OrganizationDto organization = wsSupport.getOrganization(dbSession, profile);
+      wsSupport.checkCanEdit(dbSession, organization, profile);
       wsSupport.checkNotBuiltInt(profile);
       result = ruleActivator.bulkActivateAndCommit(dbSession, ruleQueryFactory.createRuleQuery(dbSession, request), profile, request.param(PARAM_TARGET_SEVERITY));
     }
index 8e9a37d367b614ae8ebb9fde5efead695937922e..74e73865769c4776347a5212909335ca4fe7efe9 100644 (file)
@@ -60,7 +60,11 @@ public class AddGroupAction implements QProfileWsAction {
     WebService.NewAction action = context
       .createAction(ACTION_ADD_GROUP)
       .setDescription("Allow a group to edit a Quality Profile.<br>" +
-        "Requires the 'Administer Quality Profiles' permission or the ability to edit the quality profile.")
+        "Requires one of the following permissions:" +
+        "<ul>" +
+        "  <li>'Administer Quality Profiles'</li>" +
+        "  <li>Edit right on the specified quality profile</li>" +
+        "</ul>")
       .setHandler(this)
       .setPost(true)
       .setInternal(true)
index a36847023f8517ef11f61cc13760fbc18086114a..95821ee2c39bf54dfdb0abb8bf386f27a896b8fd 100644 (file)
@@ -28,14 +28,14 @@ import org.sonar.api.web.UserRole;
 import org.sonar.db.DbClient;
 import org.sonar.db.DbSession;
 import org.sonar.db.component.ComponentDto;
-import org.sonar.db.permission.OrganizationPermission;
+import org.sonar.db.organization.OrganizationDto;
 import org.sonar.db.qualityprofile.QProfileDto;
 import org.sonar.server.component.ComponentFinder;
-import org.sonar.server.exceptions.ForbiddenException;
 import org.sonar.server.user.UserSession;
 
 import static org.sonar.core.util.Uuids.UUID_EXAMPLE_08;
 import static org.sonar.server.component.ComponentFinder.ParamNames.PROJECT_UUID_AND_KEY;
+import static org.sonar.server.user.AbstractUserSession.insufficientPrivilegesException;
 import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001;
 import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.ACTION_ADD_PROJECT;
 import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_PROJECT;
@@ -62,7 +62,12 @@ public class AddProjectAction implements QProfileWsAction {
     NewAction action = controller.createAction(ACTION_ADD_PROJECT)
       .setSince("5.2")
       .setDescription("Associate a project with a quality profile.<br> " +
-        "Requires to be logged in and the 'Administer Quality Profiles' permission.")
+        "Requires one of the following permissions:" +
+        "<ul>" +
+        "  <li>'Administer Quality Profiles'</li>" +
+        "  <li>Edit right on the specified quality profile</li>" +
+        "  <li>Administer right on the specified project</li>" +
+        "</ul>")
       .setPost(true)
       .setHandler(this);
 
@@ -88,6 +93,8 @@ public class AddProjectAction implements QProfileWsAction {
     try (DbSession dbSession = dbClient.openSession(false)) {
       ComponentDto project = loadProject(dbSession, request);
       QProfileDto profile = wsSupport.getProfile(dbSession, QProfileReference.from(request));
+      OrganizationDto organization = wsSupport.getOrganization(dbSession, profile);
+      checkPermissions(dbSession, organization, profile, project);
 
       if (!profile.getOrganizationUuid().equals(project.getOrganizationUuid())) {
         throw new IllegalArgumentException("Project and quality profile must have the same organization");
@@ -110,15 +117,15 @@ public class AddProjectAction implements QProfileWsAction {
   private ComponentDto loadProject(DbSession dbSession, Request request) {
     String projectKey = request.param(PARAM_PROJECT);
     String projectUuid = request.param(PARAM_PROJECT_UUID);
-    ComponentDto project = componentFinder.getByUuidOrKey(dbSession, projectUuid, projectKey, PROJECT_UUID_AND_KEY);
-    checkAdministrator(project);
-    return project;
+    return componentFinder.getByUuidOrKey(dbSession, projectUuid, projectKey, PROJECT_UUID_AND_KEY);
   }
 
-  private void checkAdministrator(ComponentDto project) {
-    if (!userSession.hasPermission(OrganizationPermission.ADMINISTER_QUALITY_PROFILES, project.getOrganizationUuid()) &&
-      !userSession.hasComponentPermission(UserRole.ADMIN, project)) {
-      throw new ForbiddenException("Insufficient privileges");
+  private void checkPermissions(DbSession dbSession, OrganizationDto organization, QProfileDto profile, ComponentDto project) {
+    if (wsSupport.canEdit(dbSession, organization, profile)
+      || userSession.hasComponentPermission(UserRole.ADMIN, project)) {
+      return;
     }
+
+    throw insufficientPrivilegesException();
   }
 }
index a58cd4c4f19fdbe885ecc18554397627492afc7f..c67bb6cf02c2ab034834d82112c32c8ef25f4559 100644 (file)
@@ -60,7 +60,11 @@ public class AddUserAction implements QProfileWsAction {
     WebService.NewAction action = context
       .createAction(ACTION_ADD_USER)
       .setDescription("Allow a user to edit a Quality Profile.<br>" +
-        "Requires the 'Administer Quality Profiles' permission or the ability to edit the quality profile.")
+        "Requires one of the following permissions:" +
+        "<ul>" +
+        "  <li>'Administer Quality Profiles'</li>" +
+        "  <li>Edit right on the specified quality profile</li>" +
+        "</ul>")
       .setHandler(this)
       .setPost(true)
       .setInternal(true)
index 1e3aad6865bba21443e3fd962de084fc4c118831..c90cf81528c4c301a63e9eb17d2c3935206305b2 100644 (file)
@@ -34,7 +34,6 @@ import org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters;
 
 import static org.apache.commons.lang.StringUtils.isEmpty;
 import static org.sonar.core.util.Uuids.UUID_EXAMPLE_02;
-import static org.sonar.db.permission.OrganizationPermission.ADMINISTER_QUALITY_PROFILES;
 import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_LANGUAGE;
 import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_PARENT_KEY;
 
@@ -61,7 +60,11 @@ public class ChangeParentAction implements QProfileWsAction {
       .setSince("5.2")
       .setPost(true)
       .setDescription("Change a quality profile's parent.<br>" +
-        "Requires to be logged in and the 'Administer Quality Profiles' permission.")
+        "Requires one of the following permissions:" +
+        "<ul>" +
+        "  <li>'Administer Quality Profiles'</li>" +
+        "  <li>Edit right on the specified quality profile</li>" +
+        "</ul>")
       .setHandler(this);
 
     QProfileWsSupport.createOrganizationParam(inheritance)
@@ -87,11 +90,8 @@ public class ChangeParentAction implements QProfileWsAction {
 
     try (DbSession dbSession = dbClient.openSession(false)) {
       QProfileDto profile = wsSupport.getProfile(dbSession, reference);
-      String organizationUuid = profile.getOrganizationUuid();
-      OrganizationDto organization = dbClient.organizationDao().selectByUuid(dbSession, organizationUuid)
-        .orElseThrow(() -> new IllegalStateException(String.format("Could not find organization with uuid '%s' of profile '%s'", organizationUuid, profile.getKee())));
-      userSession.checkPermission(ADMINISTER_QUALITY_PROFILES, organization);
-      wsSupport.checkNotBuiltInt(profile);
+      OrganizationDto organization = wsSupport.getOrganization(dbSession, profile);
+      wsSupport.checkCanEdit(dbSession, organization, profile);
 
       String parentKey = request.param(PARAM_PARENT_KEY);
       String parentName = request.param(QualityProfileWsParameters.PARAM_PARENT_QUALITY_PROFILE);
index 10b2721f42b11c70ccb15d51b2634b7d8bc5b21b..33b32fb13153b8c3716447fb980b4bca163c2a08 100644 (file)
@@ -25,6 +25,7 @@ import org.sonar.api.server.ws.Response;
 import org.sonar.api.server.ws.WebService;
 import org.sonar.db.DbClient;
 import org.sonar.db.DbSession;
+import org.sonar.db.organization.OrganizationDto;
 import org.sonar.db.qualityprofile.QProfileDto;
 import org.sonar.server.qualityprofile.RuleActivator;
 import org.sonar.server.user.UserSession;
@@ -51,8 +52,12 @@ public class DeactivateRuleAction implements QProfileWsAction {
   public void define(WebService.NewController controller) {
     WebService.NewAction deactivate = controller
       .createAction(ACTION_DEACTIVATE_RULE)
-      .setDescription("Deactivate a rule on a Quality profile.<br> " +
-        "Requires to be logged in and the 'Administer Quality Profiles' permission.")
+      .setDescription("Deactivate a rule on a quality profile.<br> " +
+        "Requires one of the following permissions:" +
+        "<ul>" +
+        "  <li>'Administer Quality Profiles'</li>" +
+        "  <li>Edit right on the specified quality profile</li>" +
+        "</ul>")
       .setHandler(this)
       .setPost(true)
       .setSince("4.4");
@@ -77,8 +82,8 @@ public class DeactivateRuleAction implements QProfileWsAction {
     userSession.checkLoggedIn();
     try (DbSession dbSession = dbClient.openSession(false)) {
       QProfileDto profile = wsSupport.getProfile(dbSession, QProfileReference.fromKey(qualityProfileKey));
-      wsSupport.checkPermission(dbSession, profile);
-      wsSupport.checkNotBuiltInt(profile);
+      OrganizationDto organization = wsSupport.getOrganization(dbSession, profile);
+      wsSupport.checkCanEdit(dbSession, organization, profile);
       ruleActivator.deactivateAndCommit(dbSession, profile, ruleKey);
     }
     response.noContent();
index 6d23a6c91d2eaf6aec261eb2242aeb37cdfaf1e0..51888b6767ff465ec6a8a3bc66ff3d924e50e33f 100644 (file)
@@ -24,6 +24,7 @@ import org.sonar.api.server.ws.Response;
 import org.sonar.api.server.ws.WebService;
 import org.sonar.db.DbClient;
 import org.sonar.db.DbSession;
+import org.sonar.db.organization.OrganizationDto;
 import org.sonar.db.qualityprofile.QProfileDto;
 import org.sonar.server.qualityprofile.BulkChangeResult;
 import org.sonar.server.qualityprofile.RuleActivator;
@@ -57,7 +58,11 @@ public class DeactivateRulesAction implements QProfileWsAction {
     WebService.NewAction deactivate = controller
       .createAction(ACTION_DEACTIVATE_RULES)
       .setDescription("Bulk deactivate rules on Quality profiles.<br>" +
-        "Requires to be logged in and the 'Administer Quality Profiles' permission.")
+        "Requires one of the following permissions:" +
+        "<ul>" +
+        "  <li>'Administer Quality Profiles'</li>" +
+        "  <li>Edit right on the specified quality profile</li>" +
+        "</ul>")
       .setPost(true)
       .setSince("4.4")
       .setHandler(this);
@@ -78,8 +83,8 @@ public class DeactivateRulesAction implements QProfileWsAction {
     BulkChangeResult result;
     try (DbSession dbSession = dbClient.openSession(false)) {
       QProfileDto profile = wsSupport.getProfile(dbSession, QProfileReference.fromKey(qualityProfileKey));
-      wsSupport.checkPermission(dbSession, profile);
-      wsSupport.checkNotBuiltInt(profile);
+      OrganizationDto organization = wsSupport.getOrganization(dbSession, profile);
+      wsSupport.checkCanEdit(dbSession, organization, profile);
       result = ruleActivator.bulkDeactivateAndCommit(dbSession, ruleQueryFactory.createRuleQuery(dbSession, request), profile);
     }
     writeResponse(result, response);
index 6921ef04ffffde03a7de949ddc6d11a200d3df41..18dd0e15aa554202fb98c1acdcab7924891d5f3e 100644 (file)
@@ -31,12 +31,12 @@ import org.sonar.api.server.ws.WebService.NewController;
 import org.sonar.core.util.stream.MoreCollectors;
 import org.sonar.db.DbClient;
 import org.sonar.db.DbSession;
+import org.sonar.db.organization.OrganizationDto;
 import org.sonar.db.qualityprofile.QProfileDto;
 import org.sonar.server.qualityprofile.QProfileFactory;
 import org.sonar.server.user.UserSession;
 
 import static com.google.common.base.Preconditions.checkArgument;
-import static org.sonar.db.permission.OrganizationPermission.ADMINISTER_QUALITY_PROFILES;
 import static org.sonar.server.qualityprofile.ws.QProfileWsSupport.createOrganizationParam;
 
 public class DeleteAction implements QProfileWsAction {
@@ -59,7 +59,11 @@ public class DeleteAction implements QProfileWsAction {
   public void define(NewController controller) {
     NewAction action = controller.createAction("delete")
       .setDescription("Delete a quality profile and all its descendants. The default quality profile cannot be deleted.<br> " +
-        "Requires to be logged in and the 'Administer Quality Profiles' permission.")
+        "Requires one of the following permissions:" +
+        "<ul>" +
+        "  <li>'Administer Quality Profiles'</li>" +
+        "  <li>Edit right on the specified quality profile</li>" +
+        "</ul>")
       .setSince("5.2")
       .setPost(true)
       .setHandler(this);
@@ -75,8 +79,8 @@ public class DeleteAction implements QProfileWsAction {
 
     try (DbSession dbSession = dbClient.openSession(false)) {
       QProfileDto profile = wsSupport.getProfile(dbSession, QProfileReference.from(request));
-      userSession.checkPermission(ADMINISTER_QUALITY_PROFILES, profile.getOrganizationUuid());
-      wsSupport.checkNotBuiltInt(profile);
+      OrganizationDto organization = wsSupport.getOrganization(dbSession, profile);
+      wsSupport.checkCanEdit(dbSession, organization, profile);
 
       List<QProfileDto> descendants = selectDescendants(dbSession, profile);
       ensureNoneIsMarkedAsDefault(dbSession, profile, descendants);
index 6fb6e0702d8218c4815f69836abed6c1abb8daa5..8f0314f4b1308ea25d3cfbd49a42a2eb78a37cab 100644 (file)
@@ -124,20 +124,25 @@ public class QProfileWsSupport {
     userSession.checkPermission(OrganizationPermission.ADMINISTER_QUALITY_PROFILES, organization);
   }
 
-  public void checkCanEdit(DbSession dbSession, OrganizationDto organization, QProfileDto profile) {
-    checkNotBuiltInt(profile);
-    userSession.checkLoggedIn();
+  boolean canEdit(DbSession dbSession, OrganizationDto organization, QProfileDto profile) {
+    if (profile.isBuiltIn() || !userSession.isLoggedIn()) {
+      return false;
+    }
     if (userSession.hasPermission(OrganizationPermission.ADMINISTER_QUALITY_PROFILES, organization)) {
-      return;
+      return true;
     }
+
     UserDto user = dbClient.userDao().selectByLogin(dbSession, userSession.getLogin());
     checkState(user != null, "User from session does not exist");
-    if (dbClient.qProfileEditUsersDao().exists(dbSession, profile, user)
-      || dbClient.qProfileEditGroupsDao().selectQProfileUuidsByOrganizationAndGroups(dbSession, organization, userSession.getGroups()).contains(profile.getKee())) {
-      return;
-    }
+    return dbClient.qProfileEditUsersDao().exists(dbSession, profile, user)
+      || dbClient.qProfileEditGroupsDao().exists(dbSession, profile, userSession.getGroups());
+  }
 
-    throw insufficientPrivilegesException();
+  public void checkCanEdit(DbSession dbSession, OrganizationDto organization, QProfileDto profile) {
+    checkNotBuiltInt(profile);
+    if (!canEdit(dbSession, organization, profile)) {
+      throw insufficientPrivilegesException();
+    }
   }
 
   public void checkNotBuiltInt(QProfileDto profile) {
index 83fb547bbf8e8fcf6c6a65501494a615b938b4bb..1f79c3ab59850de4052d3c78aa0c98926efe600f 100644 (file)
@@ -56,7 +56,11 @@ public class RemoveGroupAction implements QProfileWsAction {
     WebService.NewAction action = context
       .createAction(ACTION_REMOVE_GROUP)
       .setDescription("Remove the ability from a group to edit a Quality Profile.<br>" +
-        "Requires the 'Administer Quality Profiles' permission or the ability to edit the quality profile.")
+        "Requires one of the following permissions:" +
+        "<ul>" +
+        "  <li>'Administer Quality Profiles'</li>" +
+        "  <li>Edit right on the specified quality profile</li>" +
+        "</ul>")
       .setHandler(this)
       .setPost(true)
       .setInternal(true)
index 1ea1ac98715b8d05dd2d6929090cda984eebc115..b3df63abff3f041a5deec45eaf8a14baf1ec5891 100644 (file)
@@ -28,13 +28,13 @@ import org.sonar.api.web.UserRole;
 import org.sonar.db.DbClient;
 import org.sonar.db.DbSession;
 import org.sonar.db.component.ComponentDto;
-import org.sonar.db.permission.OrganizationPermission;
+import org.sonar.db.organization.OrganizationDto;
 import org.sonar.db.qualityprofile.QProfileDto;
 import org.sonar.server.component.ComponentFinder;
-import org.sonar.server.exceptions.ForbiddenException;
 import org.sonar.server.user.UserSession;
 
 import static org.sonar.core.util.Uuids.UUID_EXAMPLE_09;
+import static org.sonar.server.user.AbstractUserSession.insufficientPrivilegesException;
 import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001;
 import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.ACTION_REMOVE_PROJECT;
 import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_PROJECT;
@@ -61,7 +61,12 @@ public class RemoveProjectAction implements QProfileWsAction {
     NewAction action = controller.createAction(ACTION_REMOVE_PROJECT)
       .setSince("5.2")
       .setDescription("Remove a project's association with a quality profile.<br> " +
-        "Requires to be logged in and the 'Administer Quality Profiles' permission.")
+        "Requires one of the following permissions:" +
+        "<ul>" +
+        "  <li>'Administer Quality Profiles'</li>" +
+        "  <li>Edit right on the specified quality profile</li>" +
+        "  <li>Administer right on the specified project</li>" +
+        "</ul>")
       .setPost(true)
       .setHandler(this);
     QProfileReference.defineParams(action, languages);
@@ -85,7 +90,8 @@ public class RemoveProjectAction implements QProfileWsAction {
     try (DbSession dbSession = dbClient.openSession(false)) {
       ComponentDto project = loadProject(dbSession, request);
       QProfileDto profile = wsSupport.getProfile(dbSession, QProfileReference.from(request));
-
+      OrganizationDto organization = wsSupport.getOrganization(dbSession, profile);
+      checkPermissions(dbSession, organization, profile, project);
       if (!profile.getOrganizationUuid().equals(project.getOrganizationUuid())) {
         throw new IllegalArgumentException("Project and Quality profile must have the same organization");
       }
@@ -100,15 +106,14 @@ public class RemoveProjectAction implements QProfileWsAction {
   private ComponentDto loadProject(DbSession dbSession, Request request) {
     String projectKey = request.param(PARAM_PROJECT);
     String projectUuid = request.param(PARAM_PROJECT_UUID);
-    ComponentDto project = componentFinder.getByUuidOrKey(dbSession, projectUuid, projectKey, ComponentFinder.ParamNames.PROJECT_UUID_AND_PROJECT);
-    checkAdministrator(project);
-    return project;
+    return componentFinder.getByUuidOrKey(dbSession, projectUuid, projectKey, ComponentFinder.ParamNames.PROJECT_UUID_AND_PROJECT);
   }
 
-  private void checkAdministrator(ComponentDto project) {
-    if (!userSession.hasPermission(OrganizationPermission.ADMINISTER_QUALITY_PROFILES, project.getOrganizationUuid()) &&
-      !userSession.hasComponentPermission(UserRole.ADMIN, project)) {
-      throw new ForbiddenException("Insufficient privileges");
+  private void checkPermissions(DbSession dbSession, OrganizationDto organization, QProfileDto profile, ComponentDto project) {
+    if (wsSupport.canEdit(dbSession, organization, profile) || userSession.hasComponentPermission(UserRole.ADMIN, project)) {
+      return;
     }
+
+    throw insufficientPrivilegesException();
   }
 }
index d2b8c6cc89ef36cdde2b3f299baab333dc9b072e..a7fc123fd6a35dc855b68f2c42fcd0c891843f8e 100644 (file)
@@ -56,7 +56,11 @@ public class RemoveUserAction implements QProfileWsAction {
     WebService.NewAction action = context
       .createAction(ACTION_REMOVE_USER)
       .setDescription("Remove the ability from a user to edit a Quality Profile.<br>" +
-        "Requires the 'Administer Quality Profiles' permission or the ability to edit the quality profile.")
+        "Requires one of the following permissions:" +
+        "<ul>" +
+        "  <li>'Administer Quality Profiles'</li>" +
+        "  <li>Edit right on the specified quality profile</li>" +
+        "</ul>")
       .setHandler(this)
       .setPost(true)
       .setInternal(true)
index 3801232ac504188e09e69bc8cd7f72eaf526e6dd..cbf9ce28d98ae7afed0345d74b6794b32c97fc98 100644 (file)
@@ -34,10 +34,9 @@ import org.sonar.server.user.UserSession;
 import static java.lang.String.format;
 import static java.util.Optional.ofNullable;
 import static org.sonar.core.util.Uuids.UUID_EXAMPLE_01;
-import static org.sonar.db.permission.OrganizationPermission.ADMINISTER_QUALITY_PROFILES;
 import static org.sonar.server.ws.WsUtils.checkRequest;
-import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_NAME;
 import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_KEY;
+import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_NAME;
 
 public class RenameAction implements QProfileWsAction {
 
@@ -58,7 +57,11 @@ public class RenameAction implements QProfileWsAction {
     NewAction setDefault = controller.createAction("rename")
       .setSince("5.2")
       .setDescription("Rename a quality profile.<br> " +
-        "Requires to be logged in and the 'Administer Quality Profiles' permission.")
+        "Requires one of the following permissions:" +
+        "<ul>" +
+        "  <li>'Administer Quality Profiles'</li>" +
+        "  <li>Edit right on the specified quality profile</li>" +
+        "</ul>")
       .setPost(true)
       .setHandler(this);
 
@@ -89,17 +92,13 @@ public class RenameAction implements QProfileWsAction {
 
     try (DbSession dbSession = dbClient.openSession(false)) {
       QProfileDto qualityProfile = wsSupport.getProfile(dbSession, QProfileReference.fromKey(profileKey));
-
-      String organizationUuid = qualityProfile.getOrganizationUuid();
-      userSession.checkPermission(ADMINISTER_QUALITY_PROFILES, organizationUuid);
-      wsSupport.checkNotBuiltInt(qualityProfile);
+      OrganizationDto organization = wsSupport.getOrganization(dbSession, qualityProfile);
+      wsSupport.checkCanEdit(dbSession, organization, qualityProfile);
 
       if (newName.equals(qualityProfile.getName())) {
         return;
       }
 
-      OrganizationDto organization = dbClient.organizationDao().selectByUuid(dbSession, organizationUuid)
-        .orElseThrow(() -> new IllegalStateException("No organization found for uuid " + organizationUuid));
       String language = qualityProfile.getLanguage();
       ofNullable(dbClient.qualityProfileDao().selectByNameAndLanguage(dbSession, organization, newName, language))
         .ifPresent(found -> {
index b0cc85b5a6c240ef5082f3500d78dc3266fa2b1b..2a753a4d5013777d3a6bebf6396445cfa3cc143b 100644 (file)
@@ -81,7 +81,11 @@ public class SearchGroupsAction implements QProfileWsAction {
     WebService.NewAction action = context
       .createAction(ACTION_SEARCH_GROUPS)
       .setDescription("List the groups that are allowed to edit a Quality Profile.<br>" +
-        "Requires the 'Administer Quality Profiles' permission or the ability to edit the quality profile.")
+        "Requires one of the following permissions:" +
+        "<ul>" +
+        "  <li>'Administer Quality Profiles'</li>" +
+        "  <li>Edit right on the specified quality profile</li>" +
+        "</ul>")
       .setHandler(this)
       .setInternal(true)
       .addSelectionModeParam()
@@ -130,7 +134,8 @@ public class SearchGroupsAction implements QProfileWsAction {
           .addAllGroups(groupMemberships.stream()
             .map(groupsMembership -> toGroup(groupsById.get(groupsMembership.getGroupId()), groupsMembership.isSelected()))
             .collect(toList()))
-          .setPaging(buildPaging(wsRequest, total)).build(), request, response);
+          .setPaging(buildPaging(wsRequest, total)).build(),
+        request, response);
     }
   }
 
index 1dcc251448167edf305ff7c80fd796c688c4564e..9d5cc5c5025832b789775f7045b71c79b4592e74 100644 (file)
@@ -85,7 +85,11 @@ public class SearchUsersAction implements QProfileWsAction {
     WebService.NewAction action = context
       .createAction(ACTION_SEARCH_USERS)
       .setDescription("List the users that are allowed to edit a Quality Profile.<br>" +
-        "Requires the 'Administer Quality Profiles' permission or the ability to edit the quality profile.")
+        "Requires one of the following permissions:" +
+        "<ul>" +
+        "  <li>'Administer Quality Profiles'</li>" +
+        "  <li>Edit right on the specified quality profile</li>" +
+        "</ul>")
       .setHandler(this)
       .setInternal(true)
       .addSearchQuery("freddy", "names", "logins")
@@ -132,7 +136,8 @@ public class SearchUsersAction implements QProfileWsAction {
           .addAllUsers(usersMembership.stream()
             .map(userMembershipDto -> toUser(usersById.get(userMembershipDto.getUserId()), userMembershipDto.isSelected()))
             .collect(toList()))
-          .setPaging(buildPaging(wsRequest, total)).build(), request, response);
+          .setPaging(buildPaging(wsRequest, total)).build(),
+        request, response);
     }
   }
 
index 229a95e4f9c23ea084f96cd02445e5415acd1ae4..3d441b089bd8bb75646d582ae90c6b1a59020c06 100644 (file)
@@ -35,6 +35,7 @@ import org.sonar.db.organization.OrganizationDto;
 import org.sonar.db.permission.OrganizationPermission;
 import org.sonar.db.qualityprofile.QProfileDto;
 import org.sonar.db.rule.RuleTesting;
+import org.sonar.db.user.UserDto;
 import org.sonar.server.exceptions.BadRequestException;
 import org.sonar.server.exceptions.ForbiddenException;
 import org.sonar.server.exceptions.UnauthorizedException;
@@ -58,15 +59,15 @@ import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.
 public class ActivateRuleActionTest {
 
   @Rule
-  public DbTester dbTester = DbTester.create();
+  public DbTester db = DbTester.create();
   @Rule
   public UserSessionRule userSession = UserSessionRule.standalone();
   @Rule
   public ExpectedException expectedException = ExpectedException.none();
 
-  private DbClient dbClient = dbTester.getDbClient();
+  private DbClient dbClient = db.getDbClient();
   private RuleActivator ruleActivator = mock(RuleActivator.class);
-  private QProfileWsSupport wsSupport = new QProfileWsSupport(dbClient, userSession, TestDefaultOrganizationProvider.from(dbTester));
+  private QProfileWsSupport wsSupport = new QProfileWsSupport(dbClient, userSession, TestDefaultOrganizationProvider.from(db));
 
   private WsActionTester ws = new WsActionTester(new ActivateRuleAction(dbClient, ruleActivator, userSession, wsSupport));
 
@@ -75,8 +76,8 @@ public class ActivateRuleActionTest {
 
   @Before
   public void before() {
-    defaultOrganization = dbTester.getDefaultOrganization();
-    organization = dbTester.organizations().insert();
+    defaultOrganization = db.getDefaultOrganization();
+    organization = db.organizations().insert();
   }
 
   @Test
@@ -92,7 +93,7 @@ public class ActivateRuleActionTest {
   }
 
   @Test
-  public void should_fail_if_not_logged_in() {
+  public void fail_if_not_logged_in() {
     TestRequest request = ws.newRequest()
       .setMethod("POST")
       .setParam(PARAM_RULE, RuleTesting.newRule().getKey().toString())
@@ -104,9 +105,9 @@ public class ActivateRuleActionTest {
   }
 
   @Test
-  public void should_fail_if_not_organization_quality_profile_administrator() {
-    userSession.logIn().addPermission(OrganizationPermission.ADMINISTER_QUALITY_PROFILES, defaultOrganization);
-    QProfileDto qualityProfile = dbTester.qualityProfiles().insert(organization);
+  public void fail_if_not_organization_quality_profile_administrator() {
+    userSession.logIn(db.users().insertUser());
+    QProfileDto qualityProfile = db.qualityProfiles().insert(organization);
     TestRequest request = ws.newRequest()
       .setMethod("POST")
       .setParam(PARAM_RULE, RuleTesting.newRuleDto().getKey().toString())
@@ -119,9 +120,9 @@ public class ActivateRuleActionTest {
 
   @Test
   public void fail_activate_if_built_in_profile() {
-    userSession.logIn().addPermission(OrganizationPermission.ADMINISTER_QUALITY_PROFILES, defaultOrganization);
+    userSession.logIn(db.users().insertUser()).addPermission(OrganizationPermission.ADMINISTER_QUALITY_PROFILES, defaultOrganization);
 
-    QProfileDto qualityProfile = dbTester.qualityProfiles().insert(defaultOrganization, profile -> profile.setIsBuiltIn(true).setName("Xoo profile").setLanguage("xoo"));
+    QProfileDto qualityProfile = db.qualityProfiles().insert(defaultOrganization, profile -> profile.setIsBuiltIn(true).setName("Xoo profile").setLanguage("xoo"));
     TestRequest request = ws.newRequest()
       .setMethod("POST")
       .setParam(PARAM_RULE, RuleTesting.newRuleDto().getKey().toString())
@@ -136,7 +137,7 @@ public class ActivateRuleActionTest {
   @Test
   public void activate_rule_in_default_organization() {
     userSession.logIn().addPermission(OrganizationPermission.ADMINISTER_QUALITY_PROFILES, defaultOrganization);
-    QProfileDto qualityProfile = dbTester.qualityProfiles().insert(defaultOrganization);
+    QProfileDto qualityProfile = db.qualityProfiles().insert(defaultOrganization);
     RuleKey ruleKey = RuleTesting.randomRuleKey();
     TestRequest request = ws.newRequest()
       .setMethod("POST")
@@ -160,7 +161,7 @@ public class ActivateRuleActionTest {
   @Test
   public void activate_rule_in_specific_organization() {
     userSession.logIn().addPermission(OrganizationPermission.ADMINISTER_QUALITY_PROFILES, organization);
-    QProfileDto qualityProfile = dbTester.qualityProfiles().insert(organization);
+    QProfileDto qualityProfile = db.qualityProfiles().insert(organization);
     RuleKey ruleKey = RuleTesting.randomRuleKey();
     TestRequest request = ws.newRequest()
       .setMethod("POST")
@@ -179,4 +180,24 @@ public class ActivateRuleActionTest {
     assertThat(captor.getValue().getSeverity()).isEqualTo(Severity.BLOCKER);
     assertThat(captor.getValue().isReset()).isFalse();
   }
+
+  @Test
+  public void as_qprofile_editor() {
+    UserDto user = db.users().insertUser();
+    QProfileDto qualityProfile = db.qualityProfiles().insert(organization);
+    db.qualityProfiles().addUserPermission(qualityProfile, user);
+    userSession.logIn(user);
+    RuleKey ruleKey = RuleTesting.randomRuleKey();
+
+    ws.newRequest()
+      .setMethod("POST")
+      .setParam(PARAM_RULE, ruleKey.toString())
+      .setParam(PARAM_KEY, qualityProfile.getKee())
+      .setParam("severity", "BLOCKER")
+      .setParam("params", "key1=v1;key2=v2")
+      .setParam("reset", "false")
+      .execute();
+
+    verify(ruleActivator).activateAndCommit(any(DbSession.class), any(RuleActivation.class), any(QProfileDto.class));
+  }
 }
index 4caa6f06ce2d6a0720dad55dadd2fd620336dd9d..c798ea0a9ceaca716348c69074c020091b854a2c 100644 (file)
@@ -25,15 +25,18 @@ import org.junit.Test;
 import org.junit.rules.ExpectedException;
 import org.sonar.api.server.ws.WebService;
 import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
 import org.sonar.db.DbTester;
 import org.sonar.db.organization.OrganizationDto;
-import org.sonar.db.permission.OrganizationPermission;
 import org.sonar.db.qualityprofile.QProfileDto;
+import org.sonar.db.user.GroupDto;
+import org.sonar.db.user.UserDto;
 import org.sonar.server.exceptions.BadRequestException;
 import org.sonar.server.exceptions.ForbiddenException;
 import org.sonar.server.exceptions.UnauthorizedException;
 import org.sonar.server.organization.TestDefaultOrganizationProvider;
 import org.sonar.server.qualityprofile.RuleActivator;
+import org.sonar.server.rule.index.RuleQuery;
 import org.sonar.server.rule.ws.RuleQueryFactory;
 import org.sonar.server.tester.UserSessionRule;
 import org.sonar.server.ws.TestRequest;
@@ -41,8 +44,14 @@ import org.sonar.server.ws.WsActionTester;
 
 import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
 import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.sonar.db.permission.OrganizationPermission.ADMINISTER_QUALITY_PROFILES;
 import static org.sonar.server.platform.db.migration.def.VarcharColumnDef.UUID_SIZE;
+import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_ORGANIZATION;
 import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_TARGET_KEY;
 
 public class ActivateRulesActionTest {
@@ -55,7 +64,7 @@ public class ActivateRulesActionTest {
   public ExpectedException expectedException = ExpectedException.none();
 
   private DbClient dbClient = db.getDbClient();
-  private RuleActivator ruleActivator = mock(RuleActivator.class);
+  private RuleActivator ruleActivator = mock(RuleActivator.class, RETURNS_DEEP_STUBS);
   private QProfileWsSupport wsSupport = new QProfileWsSupport(dbClient, userSession, TestDefaultOrganizationProvider.from(db));
   private RuleQueryFactory ruleQueryFactory = mock(RuleQueryFactory.class);
 
@@ -104,7 +113,39 @@ public class ActivateRulesActionTest {
   }
 
   @Test
-  public void should_fail_if_not_logged_in() {
+  public void as_global_qprofile_admin() {
+    userSession.logIn(db.users().insertUser()).addPermission(ADMINISTER_QUALITY_PROFILES, organization);
+    QProfileDto qualityProfile = db.qualityProfiles().insert(organization);
+
+    ws.newRequest()
+      .setMethod("POST")
+      .setParam(PARAM_ORGANIZATION, organization.getKey())
+      .setParam(PARAM_TARGET_KEY, qualityProfile.getKee())
+      .execute();
+
+    verify(ruleActivator).bulkActivateAndCommit(any(DbSession.class), any(RuleQuery.class), any(QProfileDto.class), anyString());
+  }
+
+  @Test
+  public void as_qprofile_editor() {
+    UserDto user = db.users().insertUser();
+    GroupDto group = db.users().insertGroup(organization);
+    QProfileDto qualityProfile = db.qualityProfiles().insert(organization);
+    db.organizations().addMember(organization, user);
+    db.qualityProfiles().addGroupPermission(qualityProfile, group);
+    userSession.logIn(user).setGroups(group);
+
+    ws.newRequest()
+      .setMethod("POST")
+      .setParam(PARAM_ORGANIZATION, organization.getKey())
+      .setParam(PARAM_TARGET_KEY, qualityProfile.getKee())
+      .execute();
+
+    verify(ruleActivator).bulkActivateAndCommit(any(DbSession.class), any(RuleQuery.class), any(QProfileDto.class), anyString());
+  }
+
+  @Test
+  public void fail_if_not_logged_in() {
     TestRequest request = ws.newRequest()
       .setMethod("POST")
       .setParam(PARAM_TARGET_KEY, randomAlphanumeric(UUID_SIZE));
@@ -116,7 +157,7 @@ public class ActivateRulesActionTest {
 
   @Test
   public void fail_if_built_in_profile() {
-    userSession.logIn().addPermission(OrganizationPermission.ADMINISTER_QUALITY_PROFILES, defaultOrganization);
+    userSession.logIn().addPermission(ADMINISTER_QUALITY_PROFILES, defaultOrganization);
     QProfileDto qualityProfile = db.qualityProfiles().insert(defaultOrganization, p -> p.setIsBuiltIn(true));
     TestRequest request = ws.newRequest()
       .setMethod("POST")
@@ -128,15 +169,15 @@ public class ActivateRulesActionTest {
   }
 
   @Test
-  public void should_fail_if_not_organization_quality_profile_administrator() {
-    userSession.logIn().addPermission(OrganizationPermission.ADMINISTER_QUALITY_PROFILES, defaultOrganization);
+  public void fail_if_not_enough_permission() {
+    userSession.logIn(db.users().insertUser());
     QProfileDto qualityProfile = db.qualityProfiles().insert(organization);
-    TestRequest request = ws.newRequest()
-      .setMethod("POST")
-      .setParam(PARAM_TARGET_KEY, qualityProfile.getKee());
 
     expectedException.expect(ForbiddenException.class);
 
-    request.execute();
+    ws.newRequest()
+      .setMethod("POST")
+      .setParam(PARAM_TARGET_KEY, qualityProfile.getKee())
+      .execute();
   }
 }
index 47cd8cfd4f19ea3875c204f3bb2a2cf88118b936..1ddd508c50920d96655ec4f6a61d4839bdf8ff65 100644 (file)
@@ -31,6 +31,7 @@ import org.sonar.db.DbTester;
 import org.sonar.db.component.ComponentDto;
 import org.sonar.db.organization.OrganizationDto;
 import org.sonar.db.qualityprofile.QProfileDto;
+import org.sonar.db.user.UserDto;
 import org.sonar.server.component.TestComponentFinder;
 import org.sonar.server.exceptions.ForbiddenException;
 import org.sonar.server.exceptions.NotFoundException;
@@ -65,7 +66,7 @@ public class AddProjectActionTest {
   private WsActionTester tester = new WsActionTester(underTest);
 
   @Test
-  public void test_definition() {
+  public void definition() {
     WebService.Action definition = tester.getDef();
     assertThat(definition.since()).isEqualTo("5.2");
     assertThat(definition.isPost()).isTrue();
@@ -114,10 +115,24 @@ public class AddProjectActionTest {
   }
 
   @Test
-  public void throw_IAE_if_profile_and_project_are_in_different_organizations() {
+  public void as_qprofile_editor() {
+    OrganizationDto organization = db.organizations().insert();
+    UserDto user = db.users().insertUser();
+    QProfileDto qualityProfile = db.qualityProfiles().insert(organization, qp -> qp.setLanguage(LANGUAGE_1));
+    db.qualityProfiles().addUserPermission(qualityProfile, user);
+    ComponentDto project = db.components().insertPrivateProject(organization);
+    userSession.logIn(user);
+
+    call(organization, project, qualityProfile);
+
+    assertProjectIsAssociatedToProfile(project, qualityProfile);
+  }
+
+  @Test
+  public void fail_if_profile_and_project_are_in_different_organizations() {
     OrganizationDto org1 = db.organizations().insert();
     OrganizationDto org2 = db.organizations().insert();
-    logInAsProfileAdmin(org1);
+    logInAsProfileAdmin(org2);
     ComponentDto project = db.components().insertPrivateProject(org1);
     QProfileDto profileInOrg2 = db.qualityProfiles().insert(org2, p -> p.setLanguage(LANGUAGE_1));
 
@@ -125,12 +140,10 @@ public class AddProjectActionTest {
     expectedException.expectMessage("Project and quality profile must have the same organization");
 
     call(org2, project, profileInOrg2);
-
-    assertProjectIsNotAssociatedToProfile(project, profileInOrg2);
   }
 
   @Test
-  public void throw_NotFoundException_if_profile_is_not_found_in_specified_organization() {
+  public void fail_if_profile_is_not_found_in_specified_organization() {
     OrganizationDto org1 = db.organizations().insert();
     OrganizationDto org2 = db.organizations().insert();
     logInAsProfileAdmin(org1);
@@ -142,8 +155,6 @@ public class AddProjectActionTest {
       .expectMessage("Quality Profile for language '" + LANGUAGE_1 + "' and name '" + profileInOrg2.getName() + "' does not exist in organization '" + org1.getKey() + "'");
 
     call(org1, project, profileInOrg2);
-
-    assertProjectIsNotAssociatedToProfile(project, profileInOrg2);
   }
 
   @Test
@@ -181,7 +192,7 @@ public class AddProjectActionTest {
   public void project_administrator_can_change_profile() throws Exception {
     ComponentDto project = db.components().insertPrivateProject(db.getDefaultOrganization());
     QProfileDto profile = db.qualityProfiles().insert(db.getDefaultOrganization());
-    userSession.logIn().addProjectPermission(UserRole.ADMIN, project);
+    userSession.logIn(db.users().insertUser()).addProjectPermission(UserRole.ADMIN, project);
 
     call(project, profile);
 
@@ -190,7 +201,7 @@ public class AddProjectActionTest {
 
   @Test
   public void throw_ForbiddenException_if_not_project_nor_organization_administrator() {
-    userSession.logIn();
+    userSession.logIn(db.users().insertUser());
     ComponentDto project = db.components().insertPrivateProject(db.getDefaultOrganization());
     QProfileDto profile = db.qualityProfiles().insert(db.getDefaultOrganization());
 
@@ -244,7 +255,7 @@ public class AddProjectActionTest {
   public void fail_when_using_branch_db_key() throws Exception {
     OrganizationDto organization = db.organizations().insert();
     ComponentDto project = db.components().insertMainBranch(organization);
-    userSession.logIn().addProjectPermission(UserRole.ADMIN, project);
+    userSession.logIn(db.users().insertUser()).addProjectPermission(UserRole.ADMIN, project);
     ComponentDto branch = db.components().insertProjectBranch(project);
     QProfileDto profile = db.qualityProfiles().insert(organization);
 
@@ -261,7 +272,7 @@ public class AddProjectActionTest {
   public void fail_when_using_branch_uuid() throws Exception {
     OrganizationDto organization = db.organizations().insert();
     ComponentDto project = db.components().insertMainBranch(organization);
-    userSession.logIn().addProjectPermission(UserRole.ADMIN, project);
+    userSession.logIn(db.users().insertUser()).addProjectPermission(UserRole.ADMIN, project);
     ComponentDto branch = db.components().insertProjectBranch(project);
     QProfileDto profile = db.qualityProfiles().insert(organization);
 
@@ -285,7 +296,7 @@ public class AddProjectActionTest {
   }
 
   private void logInAsProfileAdmin(OrganizationDto organization) {
-    userSession.logIn().addPermission(ADMINISTER_QUALITY_PROFILES, organization);
+    userSession.logIn(db.users().insertUser()).addPermission(ADMINISTER_QUALITY_PROFILES, organization);
   }
 
   private TestResponse call(ComponentDto project, QProfileDto qualityProfile) {
index 110b3284175241f95baac27cab8c5b87a7316dfb..0d13b98784557bb7b9198bdbd7986ade225ca041 100644 (file)
@@ -44,6 +44,7 @@ import org.sonar.db.qualityprofile.QProfileDto;
 import org.sonar.db.qualityprofile.QualityProfileTesting;
 import org.sonar.db.rule.RuleDefinitionDto;
 import org.sonar.db.rule.RuleTesting;
+import org.sonar.db.user.UserDto;
 import org.sonar.server.es.EsClient;
 import org.sonar.server.es.EsTester;
 import org.sonar.server.es.SearchOptions;
@@ -79,13 +80,13 @@ import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.
 public class ChangeParentActionTest {
 
   @Rule
-  public DbTester dbTester = new DbTester(System2.INSTANCE, null);
+  public DbTester db = new DbTester(System2.INSTANCE, null);
   @Rule
   public EsTester esTester = new EsTester(new RuleIndexDefinition(new MapSettings().asConfig()));
   @Rule
-  public UserSessionRule userSessionRule = UserSessionRule.standalone();
+  public UserSessionRule userSession = UserSessionRule.standalone();
   @Rule
-  public ExpectedException thrown = ExpectedException.none();
+  public ExpectedException expectedException = ExpectedException.none();
 
   private DbClient dbClient;
   private DbSession dbSession;
@@ -100,15 +101,15 @@ public class ChangeParentActionTest {
 
   @Before
   public void setUp() {
-    dbClient = dbTester.getDbClient();
-    dbSession = dbTester.getSession();
+    dbClient = db.getDbClient();
+    dbSession = db.getSession();
     EsClient esClient = esTester.client();
     ruleIndex = new RuleIndex(esClient);
     ruleIndexer = new RuleIndexer(esClient, dbClient);
     activeRuleIndexer = new ActiveRuleIndexer(dbClient, esClient);
     RuleActivatorContextFactory ruleActivatorContextFactory = new RuleActivatorContextFactory(dbClient);
     TypeValidations typeValidations = new TypeValidations(Collections.emptyList());
-    ruleActivator = new RuleActivator(System2.INSTANCE, dbClient, ruleIndex, ruleActivatorContextFactory, typeValidations, activeRuleIndexer, userSessionRule);
+    ruleActivator = new RuleActivator(System2.INSTANCE, dbClient, ruleIndex, ruleActivatorContextFactory, typeValidations, activeRuleIndexer, userSession);
 
     ChangeParentAction underTest = new ChangeParentAction(
       dbClient,
@@ -119,17 +120,17 @@ public class ChangeParentActionTest {
         ruleActivatorContextFactory,
         typeValidations,
         activeRuleIndexer,
-        userSessionRule),
+        userSession),
       new Languages(),
       new QProfileWsSupport(
         dbClient,
-        userSessionRule,
-        TestDefaultOrganizationProvider.from(dbTester)),
-      userSessionRule);
+        userSession,
+        TestDefaultOrganizationProvider.from(db)),
+      userSession);
 
     ws = new WsActionTester(underTest);
-    organization = dbTester.organizations().insert();
-    userSessionRule.logIn().addPermission(ADMINISTER_QUALITY_PROFILES, organization.getUuid());
+    organization = db.organizations().insert();
+    userSession.logIn().addPermission(ADMINISTER_QUALITY_PROFILES, organization.getUuid());
   }
 
   @Test
@@ -318,9 +319,40 @@ public class ChangeParentActionTest {
     assertThat(ruleIndex.search(new RuleQuery().setActivation(true).setQProfile(child), new SearchOptions()).getIds()).isEmpty();
   }
 
+  @Test
+  public void as_qprofile_editor() throws Exception {
+    QProfileDto parent1 = createProfile();
+    QProfileDto parent2 = createProfile();
+    QProfileDto child = createProfile();
+
+    RuleDefinitionDto rule1 = createRule();
+    RuleDefinitionDto rule2 = createRule();
+    createActiveRule(rule1, parent1);
+    createActiveRule(rule2, parent2);
+    ruleIndexer.commitAndIndex(dbSession, asList(rule1.getKey(), rule2.getKey()));
+    activeRuleIndexer.indexOnStartup(emptySet());
+    // Set parent 1
+    ruleActivator.setParentAndCommit(dbSession, child, parent1);
+    UserDto user = db.users().insertUser();
+    db.qualityProfiles().addUserPermission(child, user);
+    userSession.logIn(user);
+
+    ws.newRequest()
+      .setMethod("POST")
+      .setParam(PARAM_KEY, child.getKee())
+      .setParam(PARAM_PARENT_KEY, parent2.getKee())
+      .execute();
+
+    List<OrgActiveRuleDto> activeRules2 = dbClient.activeRuleDao().selectByProfile(dbSession, child);
+    assertThat(activeRules2).hasSize(1);
+    assertThat(activeRules2.get(0).getKey().getRuleKey().rule()).isEqualTo(rule2.getRuleKey());
+
+    assertThat(ruleIndex.search(new RuleQuery().setActivation(true).setQProfile(child), new SearchOptions()).getIds()).hasSize(1);
+  }
+
   @Test
   public void fail_if_built_in_profile() {
-    QProfileDto child = dbTester.qualityProfiles().insert(organization, p -> p
+    QProfileDto child = db.qualityProfiles().insert(organization, p -> p
       .setLanguage(language.getKey())
       .setIsBuiltIn(true));
 
@@ -332,7 +364,7 @@ public class ChangeParentActionTest {
       .setParam(PARAM_KEY, child.getKee())
       .setParam(PARAM_PARENT_KEY, "palap");
 
-    thrown.expect(BadRequestException.class);
+    expectedException.expect(BadRequestException.class);
 
     request.execute();
   }
@@ -349,7 +381,7 @@ public class ChangeParentActionTest {
       .setParam(PARAM_KEY, child.getKee())
       .setParam(PARAM_PARENT_QUALITY_PROFILE, "polop")
       .setParam(PARAM_PARENT_KEY, "palap");
-    thrown.expect(IllegalArgumentException.class);
+    expectedException.expect(IllegalArgumentException.class);
     request
       .execute();
   }
@@ -368,13 +400,13 @@ public class ChangeParentActionTest {
       .setParam(PARAM_ORGANIZATION, organization.getKey())
       .setParam(PARAM_PARENT_KEY, "palap");
 
-    thrown.expect(IllegalArgumentException.class);
+    expectedException.expect(IllegalArgumentException.class);
     request.execute();
   }
 
   @Test
   public void fail_if_missing_permission() throws Exception {
-    userSessionRule.logIn();
+    userSession.logIn(db.users().insertUser());
 
     QProfileDto child = createProfile();
 
@@ -382,15 +414,15 @@ public class ChangeParentActionTest {
       .setMethod("POST")
       .setParam(PARAM_KEY, child.getKee());
 
-    thrown.expect(ForbiddenException.class);
-    thrown.expectMessage("Insufficient privileges");
+    expectedException.expect(ForbiddenException.class);
+    expectedException.expectMessage("Insufficient privileges");
     request.execute();
   }
 
   @Test
   public void fail_if_missing_permission_for_this_organization() throws Exception {
-    OrganizationDto organization2 = dbTester.organizations().insert();
-    userSessionRule.logIn().addPermission(ADMINISTER_QUALITY_PROFILES, organization2.getUuid());
+    OrganizationDto organization2 = db.organizations().insert();
+    userSession.logIn(db.users().insertUser()).addPermission(ADMINISTER_QUALITY_PROFILES, organization2.getUuid());
 
     QProfileDto child = createProfile();
 
@@ -398,8 +430,8 @@ public class ChangeParentActionTest {
       .setMethod("POST")
       .setParam(PARAM_KEY, child.getKee());
 
-    thrown.expect(ForbiddenException.class);
-    thrown.expectMessage("Insufficient privileges");
+    expectedException.expect(ForbiddenException.class);
+    expectedException.expectMessage("Insufficient privileges");
     request.execute();
   }
 
index c46792036de1d97b83042b21d111084ee509f3e7..3a114e10538726828dbbab3ee293e6d1512142b2 100644 (file)
@@ -34,6 +34,7 @@ import org.sonar.db.organization.OrganizationDto;
 import org.sonar.db.permission.OrganizationPermission;
 import org.sonar.db.qualityprofile.QProfileDto;
 import org.sonar.db.rule.RuleTesting;
+import org.sonar.db.user.UserDto;
 import org.sonar.server.exceptions.BadRequestException;
 import org.sonar.server.exceptions.ForbiddenException;
 import org.sonar.server.exceptions.UnauthorizedException;
@@ -51,6 +52,7 @@ import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 import static org.sonar.server.platform.db.migration.def.VarcharColumnDef.UUID_SIZE;
 import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_KEY;
+import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_ORGANIZATION;
 import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_RULE;
 
 public class DeactivateRuleActionTest {
@@ -65,7 +67,7 @@ public class DeactivateRuleActionTest {
   private RuleActivator ruleActivator = mock(RuleActivator.class);
   private QProfileWsSupport wsSupport = new QProfileWsSupport(dbClient, userSession, TestDefaultOrganizationProvider.from(db));
   private DeactivateRuleAction underTest = new DeactivateRuleAction(dbClient, ruleActivator, userSession, wsSupport);
-  private WsActionTester wsActionTester = new WsActionTester(underTest);
+  private WsActionTester ws = new WsActionTester(underTest);
   private OrganizationDto defaultOrganization;
   private OrganizationDto organization;
 
@@ -76,8 +78,8 @@ public class DeactivateRuleActionTest {
   }
 
   @Test
-  public void define_deactivate_rule_action() {
-    WebService.Action definition = wsActionTester.getDef();
+  public void definition() {
+    WebService.Action definition = ws.getDef();
     assertThat(definition).isNotNull();
     assertThat(definition.isPost()).isTrue();
     assertThat(definition.params()).extracting(WebService.Param::key).containsExactlyInAnyOrder("key", "rule");
@@ -87,53 +89,12 @@ public class DeactivateRuleActionTest {
     assertThat(ruleKey.deprecatedKey()).isEqualTo("rule_key");
   }
 
-  @Test
-  public void should_fail_if_not_logged_in() {
-    TestRequest request = wsActionTester.newRequest()
-      .setMethod("POST")
-      .setParam(PARAM_RULE, RuleTesting.newRuleDto().getKey().toString())
-      .setParam(PARAM_KEY, randomAlphanumeric(UUID_SIZE));
-
-    expectedException.expect(UnauthorizedException.class);
-
-    request.execute();
-  }
-
-  @Test
-  public void should_fail_if_not_organization_quality_profile_administrator() {
-    userSession.logIn().addPermission(OrganizationPermission.ADMINISTER_QUALITY_PROFILES, defaultOrganization);
-    QProfileDto qualityProfile = db.qualityProfiles().insert(organization);
-    TestRequest request = wsActionTester.newRequest()
-      .setMethod("POST")
-      .setParam(PARAM_RULE, RuleTesting.newRuleDto().getKey().toString())
-      .setParam(PARAM_KEY, qualityProfile.getKee());
-
-    expectedException.expect(ForbiddenException.class);
-
-    request.execute();
-  }
-
-  @Test
-  public void fail_deactivate_if_built_in_profile() {
-    userSession.logIn().addPermission(OrganizationPermission.ADMINISTER_QUALITY_PROFILES, defaultOrganization);
-
-    QProfileDto qualityProfile = db.qualityProfiles().insert(defaultOrganization, profile -> profile.setIsBuiltIn(true));
-    TestRequest request = wsActionTester.newRequest()
-      .setMethod("POST")
-      .setParam(PARAM_RULE, RuleTesting.newRuleDto().getKey().toString())
-      .setParam(PARAM_KEY, qualityProfile.getKee());
-
-    expectedException.expect(BadRequestException.class);
-
-    request.execute();
-  }
-
   @Test
   public void deactivate_rule_in_default_organization() {
-    userSession.logIn().addPermission(OrganizationPermission.ADMINISTER_QUALITY_PROFILES, defaultOrganization);
+    userSession.logIn(db.users().insertUser()).addPermission(OrganizationPermission.ADMINISTER_QUALITY_PROFILES, defaultOrganization);
     QProfileDto qualityProfile = db.qualityProfiles().insert(defaultOrganization);
     RuleKey ruleKey = RuleTesting.randomRuleKey();
-    TestRequest request = wsActionTester.newRequest()
+    TestRequest request = ws.newRequest()
       .setMethod("POST")
       .setParam(PARAM_RULE, ruleKey.toString())
       .setParam(PARAM_KEY, qualityProfile.getKee());
@@ -150,10 +111,10 @@ public class DeactivateRuleActionTest {
 
   @Test
   public void deactivate_rule_in_specific_organization() {
-    userSession.logIn().addPermission(OrganizationPermission.ADMINISTER_QUALITY_PROFILES, organization);
+    userSession.logIn(db.users().insertUser()).addPermission(OrganizationPermission.ADMINISTER_QUALITY_PROFILES, organization);
     QProfileDto qualityProfile = db.qualityProfiles().insert(organization);
     RuleKey ruleKey = RuleTesting.randomRuleKey();
-    TestRequest request = wsActionTester.newRequest()
+    TestRequest request = ws.newRequest()
       .setMethod("POST")
       .setParam("organization", organization.getKey())
       .setParam(PARAM_RULE, ruleKey.toString())
@@ -168,4 +129,63 @@ public class DeactivateRuleActionTest {
     assertThat(captor.getValue()).isEqualTo(ruleKey);
     assertThat(qProfileDtoCaptor.getValue().getKee()).isEqualTo(qualityProfile.getKee());
   }
+
+  @Test
+  public void as_qprofile_editor() {
+    UserDto user = db.users().insertUser();
+    QProfileDto qualityProfile = db.qualityProfiles().insert(organization);
+    db.qualityProfiles().addUserPermission(qualityProfile, user);
+    userSession.logIn(user);
+    RuleKey ruleKey = RuleTesting.randomRuleKey();
+
+    ws.newRequest()
+      .setMethod("POST")
+      .setParam(PARAM_ORGANIZATION, organization.getKey())
+      .setParam(PARAM_RULE, ruleKey.toString())
+      .setParam(PARAM_KEY, qualityProfile.getKee())
+      .execute();
+
+    verify(ruleActivator).deactivateAndCommit(any(DbSession.class), any(QProfileDto.class), any(RuleKey.class));
+  }
+
+  @Test
+  public void fail_if_not_logged_in() {
+    TestRequest request = ws.newRequest()
+      .setMethod("POST")
+      .setParam(PARAM_RULE, RuleTesting.newRuleDto().getKey().toString())
+      .setParam(PARAM_KEY, randomAlphanumeric(UUID_SIZE));
+
+    expectedException.expect(UnauthorizedException.class);
+
+    request.execute();
+  }
+
+  @Test
+  public void fail_if_not_organization_quality_profile_administrator() {
+    userSession.logIn(db.users().insertUser()).addPermission(OrganizationPermission.ADMINISTER_QUALITY_PROFILES, defaultOrganization);
+    QProfileDto qualityProfile = db.qualityProfiles().insert(organization);
+    TestRequest request = ws.newRequest()
+      .setMethod("POST")
+      .setParam(PARAM_RULE, RuleTesting.newRuleDto().getKey().toString())
+      .setParam(PARAM_KEY, qualityProfile.getKee());
+
+    expectedException.expect(ForbiddenException.class);
+
+    request.execute();
+  }
+
+  @Test
+  public void fail_deactivate_if_built_in_profile() {
+    userSession.logIn(db.users().insertUser()).addPermission(OrganizationPermission.ADMINISTER_QUALITY_PROFILES, defaultOrganization);
+
+    QProfileDto qualityProfile = db.qualityProfiles().insert(defaultOrganization, profile -> profile.setIsBuiltIn(true));
+    TestRequest request = ws.newRequest()
+      .setMethod("POST")
+      .setParam(PARAM_RULE, RuleTesting.newRuleDto().getKey().toString())
+      .setParam(PARAM_KEY, qualityProfile.getKee());
+
+    expectedException.expect(BadRequestException.class);
+
+    request.execute();
+  }
 }
index 4ce81b1b275cff742825a1e12c0e8c316539fee8..75ff057767b87a6aab52a239f49d77d79565b7a4 100644 (file)
@@ -25,15 +25,18 @@ import org.junit.Test;
 import org.junit.rules.ExpectedException;
 import org.sonar.api.server.ws.WebService;
 import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
 import org.sonar.db.DbTester;
 import org.sonar.db.organization.OrganizationDto;
-import org.sonar.db.permission.OrganizationPermission;
 import org.sonar.db.qualityprofile.QProfileDto;
+import org.sonar.db.user.GroupDto;
+import org.sonar.db.user.UserDto;
 import org.sonar.server.exceptions.BadRequestException;
 import org.sonar.server.exceptions.ForbiddenException;
 import org.sonar.server.exceptions.UnauthorizedException;
 import org.sonar.server.organization.TestDefaultOrganizationProvider;
 import org.sonar.server.qualityprofile.RuleActivator;
+import org.sonar.server.rule.index.RuleQuery;
 import org.sonar.server.rule.ws.RuleQueryFactory;
 import org.sonar.server.tester.UserSessionRule;
 import org.sonar.server.ws.TestRequest;
@@ -41,37 +44,42 @@ import org.sonar.server.ws.WsActionTester;
 
 import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
 import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.sonar.db.permission.OrganizationPermission.ADMINISTER_QUALITY_PROFILES;
 import static org.sonar.server.platform.db.migration.def.VarcharColumnDef.UUID_SIZE;
+import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_ORGANIZATION;
 import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_TARGET_KEY;
 
 public class DeactivateRulesActionTest {
 
   @Rule
-  public DbTester dbTester = DbTester.create();
+  public DbTester db = DbTester.create();
   @Rule
   public UserSessionRule userSession = UserSessionRule.standalone();
   @Rule
   public ExpectedException thrown = ExpectedException.none();
 
-  private DbClient dbClient = dbTester.getDbClient();
-  private RuleActivator ruleActivator = mock(RuleActivator.class);
-  private QProfileWsSupport wsSupport = new QProfileWsSupport(dbClient, userSession, TestDefaultOrganizationProvider.from(dbTester));
+  private DbClient dbClient = db.getDbClient();
+  private RuleActivator ruleActivator = mock(RuleActivator.class, RETURNS_DEEP_STUBS);
+  private QProfileWsSupport wsSupport = new QProfileWsSupport(dbClient, userSession, TestDefaultOrganizationProvider.from(db));
   private RuleQueryFactory ruleQueryFactory = mock(RuleQueryFactory.class);
   private DeactivateRulesAction underTest = new DeactivateRulesAction(ruleQueryFactory, userSession, ruleActivator, wsSupport, dbClient);
-  private WsActionTester wsActionTester = new WsActionTester(underTest);
+  private WsActionTester ws = new WsActionTester(underTest);
   private OrganizationDto defaultOrganization;
   private OrganizationDto organization;
 
   @Before
   public void before() {
-    defaultOrganization = dbTester.getDefaultOrganization();
-    organization = dbTester.organizations().insert();
+    defaultOrganization = db.getDefaultOrganization();
+    organization = db.organizations().insert();
   }
 
   @Test
-  public void define_bulk_deactivate_rule_action() {
-    WebService.Action definition = wsActionTester.getDef();
+  public void definition() {
+    WebService.Action definition = ws.getDef();
     assertThat(definition).isNotNull();
     assertThat(definition.isPost()).isTrue();
     assertThat(definition.params()).extracting(WebService.Param::key).containsExactlyInAnyOrder(
@@ -100,8 +108,41 @@ public class DeactivateRulesActionTest {
   }
 
   @Test
-  public void should_fail_if_not_logged_in() {
-    TestRequest request = wsActionTester.newRequest()
+  public void as_global_admin() {
+    UserDto user = db.users().insertUser();
+    QProfileDto qualityProfile = db.qualityProfiles().insert(organization);
+    userSession.logIn(user).addPermission(ADMINISTER_QUALITY_PROFILES, organization);
+
+    ws.newRequest()
+      .setMethod("POST")
+      .setParam(PARAM_ORGANIZATION, organization.getKey())
+      .setParam(PARAM_TARGET_KEY, qualityProfile.getKee())
+      .execute();
+
+    verify(ruleActivator).bulkDeactivateAndCommit(any(DbSession.class), any(RuleQuery.class), any(QProfileDto.class));
+  }
+
+  @Test
+  public void as_qprofile_editor() {
+    UserDto user = db.users().insertUser();
+    GroupDto group = db.users().insertGroup(organization);
+    QProfileDto qualityProfile = db.qualityProfiles().insert(organization);
+    db.organizations().addMember(organization, user);
+    db.qualityProfiles().addGroupPermission(qualityProfile, group);
+    userSession.logIn(user).setGroups(group);
+
+    ws.newRequest()
+      .setMethod("POST")
+      .setParam(PARAM_ORGANIZATION, organization.getKey())
+      .setParam(PARAM_TARGET_KEY, qualityProfile.getKee())
+      .execute();
+
+    verify(ruleActivator).bulkDeactivateAndCommit(any(DbSession.class), any(RuleQuery.class), any(QProfileDto.class));
+  }
+
+  @Test
+  public void fail_if_not_logged_in() {
+    TestRequest request = ws.newRequest()
       .setMethod("POST")
       .setParam(PARAM_TARGET_KEY, randomAlphanumeric(UUID_SIZE));
 
@@ -111,9 +152,9 @@ public class DeactivateRulesActionTest {
 
   @Test
   public void fail_if_built_in_profile() {
-    userSession.logIn().addPermission(OrganizationPermission.ADMINISTER_QUALITY_PROFILES, defaultOrganization);
-    QProfileDto qualityProfile = dbTester.qualityProfiles().insert(defaultOrganization, p -> p.setIsBuiltIn(true));
-    TestRequest request = wsActionTester.newRequest()
+    userSession.logIn().addPermission(ADMINISTER_QUALITY_PROFILES, defaultOrganization);
+    QProfileDto qualityProfile = db.qualityProfiles().insert(defaultOrganization, p -> p.setIsBuiltIn(true));
+    TestRequest request = ws.newRequest()
       .setMethod("POST")
       .setParam(PARAM_TARGET_KEY, qualityProfile.getKee());
 
@@ -123,10 +164,10 @@ public class DeactivateRulesActionTest {
   }
 
   @Test
-  public void should_fail_if_not_organization_quality_profile_administrator() {
-    userSession.logIn().addPermission(OrganizationPermission.ADMINISTER_QUALITY_PROFILES, defaultOrganization);
-    QProfileDto qualityProfile = dbTester.qualityProfiles().insert(organization);
-    TestRequest request = wsActionTester.newRequest()
+  public void fail_if_not_organization_quality_profile_administrator() {
+    userSession.logIn(db.users().insertUser()).addPermission(ADMINISTER_QUALITY_PROFILES, defaultOrganization);
+    QProfileDto qualityProfile = db.qualityProfiles().insert(organization);
+    TestRequest request = ws.newRequest()
       .setMethod("POST")
       .setParam(PARAM_TARGET_KEY, qualityProfile.getKee());
 
index 1ee7fffe84a4a592fb7a0254e93d4efeee135be4..ab6bff483558fc9282a0ebd6105b3a45bdbfcc68 100644 (file)
@@ -34,6 +34,7 @@ import org.sonar.db.DbTester;
 import org.sonar.db.component.ComponentDto;
 import org.sonar.db.organization.OrganizationDto;
 import org.sonar.db.qualityprofile.QProfileDto;
+import org.sonar.db.user.UserDto;
 import org.sonar.server.exceptions.BadRequestException;
 import org.sonar.server.exceptions.ForbiddenException;
 import org.sonar.server.exceptions.NotFoundException;
@@ -49,6 +50,10 @@ import org.sonar.server.ws.WsActionTester;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.mockito.Mockito.mock;
 import static org.sonar.db.permission.OrganizationPermission.ADMINISTER_QUALITY_PROFILES;
+import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_KEY;
+import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_LANGUAGE;
+import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_ORGANIZATION;
+import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_QUALITY_PROFILE;
 
 public class DeleteActionTest {
 
@@ -59,7 +64,7 @@ public class DeleteActionTest {
   @Rule
   public ExpectedException expectedException = ExpectedException.none();
   @Rule
-  public UserSessionRule userSessionRule = UserSessionRule.standalone();
+  public UserSessionRule userSession = UserSessionRule.standalone();
 
   private DbClient dbClient = db.getDbClient();
   private DbSession dbSession = db.getSession();
@@ -67,8 +72,8 @@ public class DeleteActionTest {
 
   private DeleteAction underTest = new DeleteAction(
     new Languages(LanguageTesting.newLanguage(A_LANGUAGE)),
-    new QProfileFactoryImpl(dbClient, UuidFactoryFast.getInstance(), System2.INSTANCE, activeRuleIndexer), dbClient, userSessionRule,
-    new QProfileWsSupport(dbClient, userSessionRule, TestDefaultOrganizationProvider.from(db)));
+    new QProfileFactoryImpl(dbClient, UuidFactoryFast.getInstance(), System2.INSTANCE, activeRuleIndexer), dbClient, userSession,
+    new QProfileWsSupport(dbClient, userSession, TestDefaultOrganizationProvider.from(db)));
   private WsActionTester ws = new WsActionTester(underTest);
 
   @Test
@@ -83,7 +88,7 @@ public class DeleteActionTest {
 
     TestResponse response = ws.newRequest()
       .setMethod("POST")
-      .setParam("profileKey", profile1.getKee())
+      .setParam(PARAM_KEY, profile1.getKee())
       .execute();
     assertThat(response.getStatus()).isEqualTo(HttpURLConnection.HTTP_NO_CONTENT);
 
@@ -103,8 +108,8 @@ public class DeleteActionTest {
 
     TestResponse response = ws.newRequest()
       .setMethod("POST")
-      .setParam("language", profile1.getLanguage())
-      .setParam("qualityProfile", profile1.getName())
+      .setParam(PARAM_LANGUAGE, profile1.getLanguage())
+      .setParam(PARAM_QUALITY_PROFILE, profile1.getName())
       .execute();
 
     assertThat(response.getStatus()).isEqualTo(HttpURLConnection.HTTP_NO_CONTENT);
@@ -124,9 +129,9 @@ public class DeleteActionTest {
 
     TestResponse response = ws.newRequest()
       .setMethod("POST")
-      .setParam("organization", organization.getKey())
-      .setParam("language", profile1.getLanguage())
-      .setParam("qualityProfile", profile1.getName())
+      .setParam(PARAM_ORGANIZATION, organization.getKey())
+      .setParam(PARAM_LANGUAGE, profile1.getLanguage())
+      .setParam(PARAM_QUALITY_PROFILE, profile1.getName())
       .execute();
     assertThat(response.getStatus()).isEqualTo(HttpURLConnection.HTTP_NO_CONTENT);
 
@@ -134,6 +139,25 @@ public class DeleteActionTest {
     verifyProfileExists(profile2);
   }
 
+  @Test
+  public void as_qprofile_editor() {
+    OrganizationDto organization = db.organizations().insert();
+    QProfileDto profile = createProfile(organization);
+    UserDto user = db.users().insertUser();
+    db.qualityProfiles().addUserPermission(profile, user);
+    userSession.logIn(user);
+
+    TestResponse response = ws.newRequest()
+      .setMethod("POST")
+      .setParam(PARAM_ORGANIZATION, organization.getKey())
+      .setParam(PARAM_LANGUAGE, profile.getLanguage())
+      .setParam(PARAM_QUALITY_PROFILE, profile.getName())
+      .execute();
+    assertThat(response.getStatus()).isEqualTo(HttpURLConnection.HTTP_NO_CONTENT);
+
+    verifyProfileDoesNotExist(profile, organization);
+  }
+
   @Test
   public void fail_if_built_in_profile() {
     OrganizationDto organization = db.organizations().insert();
@@ -144,44 +168,40 @@ public class DeleteActionTest {
 
     ws.newRequest()
       .setMethod("POST")
-      .setParam("profileKey", profile1.getKee())
+      .setParam(PARAM_KEY, profile1.getKee())
       .execute();
   }
 
   @Test
-  public void throw_ForbiddenException_if_not_profile_administrator() {
-    OrganizationDto organization1 = db.organizations().insert();
-    OrganizationDto organization2 = db.organizations().insert();
-
-    QProfileDto profileInOrg1 = createProfile(organization1);
-    QProfileDto profileInOrg2 = createProfile(organization2);
-
-    logInAsQProfileAdministrator(organization1);
+  public void fail_if_not_profile_administrator() {
+    OrganizationDto organization = db.organizations().insert();
+    QProfileDto qprofile = createProfile(organization);
+    userSession.logIn(db.users().insertUser());
 
     expectedException.expect(ForbiddenException.class);
     expectedException.expectMessage("Insufficient privileges");
 
     ws.newRequest()
       .setMethod("POST")
-      .setParam("profileKey", profileInOrg2.getKee())
+      .setParam(PARAM_KEY, qprofile.getKee())
       .execute();
   }
 
   @Test
-  public void throw_UnauthorizedException_if_not_logged_in() {
+  public void fail_if_not_logged_in() {
     QProfileDto profile = createProfile(db.getDefaultOrganization());
 
     expectedException.expect(UnauthorizedException.class);
 
     ws.newRequest()
       .setMethod("POST")
-      .setParam("profileKey", profile.getKee())
+      .setParam(PARAM_KEY, profile.getKee())
       .execute();
   }
 
   @Test
-  public void throw_IAE_if_missing_parameters() {
-    userSessionRule.logIn();
+  public void fail_if_missing_parameters() {
+    userSession.logIn();
 
     expectedException.expect(IllegalArgumentException.class);
     expectedException.expectMessage("If no quality profile key is specified, language and name must be set");
@@ -192,7 +212,7 @@ public class DeleteActionTest {
   }
 
   @Test
-  public void throw_IAE_if_missing_language_parameter() {
+  public void fail_if_missing_language_parameter() {
     OrganizationDto organization = db.organizations().insert();
     QProfileDto profile = createProfile(organization);
     logInAsQProfileAdministrator(organization);
@@ -202,13 +222,13 @@ public class DeleteActionTest {
 
     ws.newRequest()
       .setMethod("POST")
-      .setParam("organization", organization.getKey())
+      .setParam(PARAM_ORGANIZATION, organization.getKey())
       .setParam("profileName", profile.getName())
       .execute();
   }
 
   @Test
-  public void throw_IAE_if_missing_name_parameter() throws Exception {
+  public void fail_if_missing_name_parameter() throws Exception {
     OrganizationDto organization = db.organizations().insert();
     QProfileDto profile = createProfile(organization);
     logInAsQProfileAdministrator(organization);
@@ -218,13 +238,13 @@ public class DeleteActionTest {
 
     ws.newRequest()
       .setMethod("POST")
-      .setParam("organization", organization.getKey())
-      .setParam("language", profile.getLanguage())
+      .setParam(PARAM_ORGANIZATION, organization.getKey())
+      .setParam(PARAM_LANGUAGE, profile.getLanguage())
       .execute();
   }
 
   @Test
-  public void throw_IAE_if_too_many_parameters_to_reference_profile() {
+  public void fail_if_too_many_parameters_to_reference_profile() {
     OrganizationDto organization = db.organizations().insert();
     QProfileDto profile = createProfile(organization);
     logInAsQProfileAdministrator(organization);
@@ -234,28 +254,28 @@ public class DeleteActionTest {
 
     ws.newRequest()
       .setMethod("POST")
-      .setParam("organization", organization.getKey())
-      .setParam("language", profile.getLanguage())
+      .setParam(PARAM_ORGANIZATION, organization.getKey())
+      .setParam(PARAM_LANGUAGE, profile.getLanguage())
       .setParam("profileName", profile.getName())
-      .setParam("profileKey", profile.getKee())
+      .setParam(PARAM_KEY, profile.getKee())
       .execute();
   }
 
   @Test
-  public void throw_NotFoundException_if_profile_does_not_exist() {
-    userSessionRule.logIn();
+  public void fail_if_profile_does_not_exist() {
+    userSession.logIn();
 
     expectedException.expect(NotFoundException.class);
     expectedException.expectMessage("Quality Profile with key 'does_not_exist' does not exist");
 
     ws.newRequest()
       .setMethod("POST")
-      .setParam("profileKey", "does_not_exist")
+      .setParam(PARAM_KEY, "does_not_exist")
       .execute();
   }
 
   @Test
-  public void throw_ISE_if_deleting_default_profile() {
+  public void fail_if_deleting_default_profile() {
     OrganizationDto organization = db.organizations().insert();
     QProfileDto profile = createProfile(organization);
     db.qualityProfiles().setAsDefault(profile);
@@ -266,12 +286,12 @@ public class DeleteActionTest {
 
     ws.newRequest()
       .setMethod("POST")
-      .setParam("profileKey", profile.getKee())
+      .setParam(PARAM_KEY, profile.getKee())
       .execute();
   }
 
   @Test
-  public void throw_ISE_if_a_descendant_is_marked_as_default() {
+  public void fail_if_a_descendant_is_marked_as_default() {
     OrganizationDto organization = db.organizations().insert();
     QProfileDto parentProfile = createProfile(organization);
     QProfileDto childProfile = db.qualityProfiles().insert(organization, p -> p.setLanguage(A_LANGUAGE).setParentKee(parentProfile.getKee()));
@@ -284,7 +304,7 @@ public class DeleteActionTest {
 
     ws.newRequest()
       .setMethod("POST")
-      .setParam("profileKey", parentProfile.getKee())
+      .setParam(PARAM_KEY, parentProfile.getKee())
       .execute();
   }
 
@@ -303,8 +323,8 @@ public class DeleteActionTest {
   }
 
   private void logInAsQProfileAdministrator(OrganizationDto organization) {
-    userSessionRule
-      .logIn()
+    userSession
+      .logIn(db.users().insertUser())
       .addPermission(ADMINISTER_QUALITY_PROFILES, organization);
   }
 
index 50576a44a1b727cb2a8995f993daba67367108af..fb91e2ef9bfda16d1692f1510f0fa464e8704911 100644 (file)
@@ -33,6 +33,7 @@ import org.sonar.db.component.ComponentDto;
 import org.sonar.db.component.ResourceTypesRule;
 import org.sonar.db.organization.OrganizationDto;
 import org.sonar.db.qualityprofile.QProfileDto;
+import org.sonar.db.user.UserDto;
 import org.sonar.server.component.ComponentFinder;
 import org.sonar.server.exceptions.ForbiddenException;
 import org.sonar.server.exceptions.NotFoundException;
@@ -128,7 +129,22 @@ public class RemoveProjectActionTest {
     ComponentDto project = db.components().insertPrivateProject(db.getDefaultOrganization());
     QProfileDto profile = db.qualityProfiles().insert(db.getDefaultOrganization());
     db.qualityProfiles().associateWithProject(project, profile);
-    userSession.logIn().addProjectPermission(UserRole.ADMIN, project);
+    userSession.logIn(db.users().insertUser()).addProjectPermission(UserRole.ADMIN, project);
+
+    call(project, profile);
+
+    assertProjectIsNotAssociatedToProfile(project, profile);
+  }
+
+  @Test
+  public void as_qprofile_editor() {
+    OrganizationDto organization = db.organizations().insert();
+    ComponentDto project = db.components().insertPrivateProject(organization);
+    QProfileDto profile = db.qualityProfiles().insert(organization, p -> p.setLanguage(LANGUAGE_1));
+    db.qualityProfiles().associateWithProject(project, profile);
+    UserDto user = db.users().insertUser();
+    db.qualityProfiles().addUserPermission(profile, user);
+    userSession.logIn(user);
 
     call(project, profile);
 
@@ -136,8 +152,8 @@ public class RemoveProjectActionTest {
   }
 
   @Test
-  public void throw_ForbiddenException_if_not_project_nor_organization_administrator() {
-    userSession.logIn();
+  public void fail_if_not_enough_permissions() {
+    userSession.logIn(db.users().insertUser());
     ComponentDto project = db.components().insertPrivateProject(db.getDefaultOrganization());
     QProfileDto profile = db.qualityProfiles().insert(db.getDefaultOrganization());
 
@@ -148,7 +164,7 @@ public class RemoveProjectActionTest {
   }
 
   @Test
-  public void throw_UnauthorizedException_if_not_logged_in() {
+  public void fail_if_not_logged_in() {
     userSession.anonymous();
     ComponentDto project = db.components().insertPrivateProject(db.getDefaultOrganization());
     QProfileDto profile = db.qualityProfiles().insert(db.getDefaultOrganization());
@@ -160,7 +176,7 @@ public class RemoveProjectActionTest {
   }
 
   @Test
-  public void throw_NotFoundException_if_project_does_not_exist() {
+  public void fail_if_project_does_not_exist() {
     logInAsProfileAdmin();
     QProfileDto profile = db.qualityProfiles().insert(db.getDefaultOrganization());
 
@@ -174,7 +190,7 @@ public class RemoveProjectActionTest {
   }
 
   @Test
-  public void throw_NotFoundException_if_profile_does_not_exist() {
+  public void fail_if_profile_does_not_exist() {
     logInAsProfileAdmin();
     ComponentDto project = db.components().insertPrivateProject(db.getDefaultOrganization());
 
@@ -232,7 +248,7 @@ public class RemoveProjectActionTest {
   }
 
   private void logInAsProfileAdmin() {
-    userSession.logIn().addPermission(ADMINISTER_QUALITY_PROFILES, db.getDefaultOrganization());
+    userSession.logIn(db.users().insertUser()).addPermission(ADMINISTER_QUALITY_PROFILES, db.getDefaultOrganization());
   }
 
   private TestResponse call(ComponentDto project, QProfileDto qualityProfile) {
index 8211b0d87df12911cfc91f74b6a9e49171eeade7..4a8cfa693b5327ee7039da0edb5e174cd6067d47 100644 (file)
@@ -32,6 +32,7 @@ import org.sonar.db.DbTester;
 import org.sonar.db.organization.OrganizationDto;
 import org.sonar.db.qualityprofile.QProfileDto;
 import org.sonar.db.qualityprofile.QualityProfileTesting;
+import org.sonar.db.user.UserDto;
 import org.sonar.server.exceptions.BadRequestException;
 import org.sonar.server.exceptions.ForbiddenException;
 import org.sonar.server.exceptions.NotFoundException;
@@ -50,7 +51,7 @@ public class RenameActionTest {
   @Rule
   public DbTester db = DbTester.create(System2.INSTANCE);
   @Rule
-  public UserSessionRule userSessionRule = UserSessionRule.standalone();
+  public UserSessionRule userSession = UserSessionRule.standalone();
   @Rule
   public ExpectedException expectedException = ExpectedException.none();
 
@@ -65,8 +66,8 @@ public class RenameActionTest {
   @Before
   public void setUp() {
     TestDefaultOrganizationProvider defaultOrganizationProvider = TestDefaultOrganizationProvider.from(db);
-    QProfileWsSupport wsSupport = new QProfileWsSupport(dbClient, userSessionRule, defaultOrganizationProvider);
-    RenameAction underTest = new RenameAction(dbClient, userSessionRule, wsSupport);
+    QProfileWsSupport wsSupport = new QProfileWsSupport(dbClient, userSession, defaultOrganizationProvider);
+    RenameAction underTest = new RenameAction(dbClient, userSession, wsSupport);
     ws = new WsActionTester(underTest);
 
     organization = db.organizations().insert();
@@ -112,7 +113,7 @@ public class RenameActionTest {
   public void allow_same_name_in_different_organizations() {
     OrganizationDto organizationX = db.organizations().insert();
     OrganizationDto organizationY = db.organizations().insert();
-    userSessionRule.logIn()
+    userSession.logIn()
       .addPermission(ADMINISTER_QUALITY_PROFILES, organizationX);
 
     QProfileDto qualityProfile1 = QualityProfileTesting.newQualityProfileDto()
@@ -135,6 +136,34 @@ public class RenameActionTest {
     assertThat(reloaded.getName()).isEqualTo("Duplicated name");
   }
 
+  @Test
+  public void allow_100_characters_as_name_and_not_more() throws Exception {
+    logInAsQProfileAdministrator();
+    String qualityProfileKey = createNewValidQualityProfileKey();
+
+    String a100charName = "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890";
+    call(qualityProfileKey, a100charName);
+
+    expectedException.expect(BadRequestException.class);
+    expectedException.expectMessage("Name is too long (>100 characters)");
+
+    String a101charName = "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901";
+    call(qualityProfileKey, a101charName);
+  }
+
+  @Test
+  public void as_qprofile_editor() {
+    QProfileDto qualityProfile = db.qualityProfiles().insert(organization);
+    UserDto user = db.users().insertUser();
+    db.qualityProfiles().addUserPermission(qualityProfile, user);
+    userSession.logIn(user);
+
+    call(qualityProfile.getKee(), "the new name");
+
+    QProfileDto reloaded = db.getDbClient().qualityProfileDao().selectByUuid(db.getSession(), qualityProfile.getKee());
+    assertThat(reloaded.getName()).isEqualTo("the new name");
+  }
+
   @Test
   public void fail_if_parameter_profile_is_missing() throws Exception {
     logInAsQProfileAdministrator();
@@ -156,10 +185,10 @@ public class RenameActionTest {
   }
 
   @Test
-  public void throw_ForbiddenException_if_not_profile_administrator() throws Exception {
+  public void fail_if_not_profile_administrator() throws Exception {
     OrganizationDto organizationX = db.organizations().insert();
     OrganizationDto organizationY = db.organizations().insert();
-    userSessionRule.logIn()
+    userSession.logIn(db.users().insertUser())
       .addPermission(ADMINISTER_QUALITY_PROFILES, organizationX);
 
     QProfileDto qualityProfile = QualityProfileTesting.newQualityProfileDto()
@@ -174,7 +203,7 @@ public class RenameActionTest {
   }
 
   @Test
-  public void throw_UnauthorizedException_if_not_logged_in() throws Exception {
+  public void fail_if_not_logged_in() throws Exception {
     expectedException.expect(UnauthorizedException.class);
     expectedException.expectMessage("Authentication is required");
 
@@ -201,21 +230,6 @@ public class RenameActionTest {
     call(qualityProfileKey, "the new name");
   }
 
-  @Test
-  public void allow_100_characters_as_name_and_not_more() throws Exception {
-    logInAsQProfileAdministrator();
-    String qualityProfileKey = createNewValidQualityProfileKey();
-
-    String a100charName = "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890";
-    call(qualityProfileKey, a100charName);
-
-    expectedException.expect(BadRequestException.class);
-    expectedException.expectMessage("Name is too long (>100 characters)");
-
-    String a101charName = "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901";
-    call(qualityProfileKey, a101charName);
-  }
-
   @Test
   public void fail_if_blank_renaming() {
     String qualityProfileKey = createNewValidQualityProfileKey();
@@ -263,9 +277,7 @@ public class RenameActionTest {
   }
 
   private void logInAsQProfileAdministrator() {
-    userSessionRule
-      .logIn()
-      .addPermission(ADMINISTER_QUALITY_PROFILES, organization);
+    userSession.logIn(db.users().insertUser()).addPermission(ADMINISTER_QUALITY_PROFILES, organization);
   }
 
   private void call(@Nullable String key, @Nullable String name) {
index 02c71b7a9ad8f3bcc318b8bd21c4f6a082cfd037..bcf23e3e4ccea9f70bbe14df23555a71ddb0d7d8 100644 (file)
@@ -20,6 +20,7 @@
 package org.sonarqube.tests.qualityProfile;
 
 import com.sonar.orchestrator.Orchestrator;
+import java.util.function.Predicate;
 import org.junit.ClassRule;
 import org.junit.Rule;
 import org.junit.Test;
@@ -27,12 +28,13 @@ import org.sonarqube.tests.Category6Suite;
 import org.sonarqube.tests.Tester;
 import org.sonarqube.ws.Common;
 import org.sonarqube.ws.Organizations.Organization;
-import org.sonarqube.ws.QualityProfiles;
 import org.sonarqube.ws.QualityProfiles.CreateWsResponse;
 import org.sonarqube.ws.QualityProfiles.SearchGroupsResponse;
 import org.sonarqube.ws.QualityProfiles.SearchUsersResponse;
+import org.sonarqube.ws.QualityProfiles.SearchWsResponse;
 import org.sonarqube.ws.WsUserGroups;
-import org.sonarqube.ws.WsUsers;
+import org.sonarqube.ws.WsUsers.CreateWsResponse.User;
+import org.sonarqube.ws.client.PostRequest;
 import org.sonarqube.ws.client.permission.AddUserWsRequest;
 import org.sonarqube.ws.client.qualityprofile.AddGroupRequest;
 import org.sonarqube.ws.client.qualityprofile.AddUserRequest;
@@ -41,12 +43,15 @@ import org.sonarqube.ws.client.qualityprofile.RemoveUserRequest;
 import org.sonarqube.ws.client.qualityprofile.SearchGroupsRequest;
 import org.sonarqube.ws.client.qualityprofile.SearchUsersRequest;
 import org.sonarqube.ws.client.qualityprofile.SearchWsRequest;
+import org.sonarqube.ws.client.qualityprofile.ShowRequest;
 
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.tuple;
 import static org.sonarqube.ws.QualityProfiles.SearchGroupsResponse.Group;
 
 public class QualityProfilesEditTest {
+  private static final String RULE_ONE_BUG_PER_LINE = "xoo:OneBugIssuePerLine";
+  private static final String RULE_ONE_ISSUE_PER_LINE = "xoo:OneIssuePerLine";
 
   @ClassRule
   public static Orchestrator orchestrator = Category6Suite.ORCHESTRATOR;
@@ -57,8 +62,8 @@ public class QualityProfilesEditTest {
   @Test
   public void search_users_allowed_to_edit_a_profile() {
     Organization organization = tester.organizations().generate();
-    WsUsers.CreateWsResponse.User user1 = tester.users().generateMember(organization, u -> u.setEmail("user1@email.com"));
-    WsUsers.CreateWsResponse.User user2 = tester.users().generateMember(organization, u -> u.setEmail("user2@email.com"));
+    User user1 = tester.users().generateMember(organization, u -> u.setEmail("user1@email.com"));
+    User user2 = tester.users().generateMember(organization, u -> u.setEmail("user2@email.com"));
     CreateWsResponse.QualityProfile xooProfile = tester.qProfiles().createXooProfile(organization);
     addUserPermission(organization, user1, xooProfile);
 
@@ -69,7 +74,8 @@ public class QualityProfilesEditTest {
       .setSelected("all")
       .build());
 
-    assertThat(users.getUsersList()).extracting(SearchUsersResponse.User::getLogin, SearchUsersResponse.User::getName, SearchUsersResponse.User::getAvatar, SearchUsersResponse.User::getSelected)
+    assertThat(users.getUsersList())
+      .extracting(SearchUsersResponse.User::getLogin, SearchUsersResponse.User::getName, SearchUsersResponse.User::getAvatar, SearchUsersResponse.User::getSelected)
       .containsExactlyInAnyOrder(
         tuple(user1.getLogin(), user1.getName(), "3acc837f898bdaa338b7cd7a9ab6dd5b", true),
         tuple(user2.getLogin(), user2.getName(), "fd6926c24d76d650a365ae350784e048", false),
@@ -81,8 +87,8 @@ public class QualityProfilesEditTest {
   @Test
   public void add_and_remove_user() {
     Organization organization = tester.organizations().generate();
-    WsUsers.CreateWsResponse.User user1 = tester.users().generateMember(organization);
-    WsUsers.CreateWsResponse.User user2 = tester.users().generateMember(organization);
+    User user1 = tester.users().generateMember(organization);
+    User user2 = tester.users().generateMember(organization);
     CreateWsResponse.QualityProfile xooProfile = tester.qProfiles().createXooProfile(organization);
 
     // No user added
@@ -92,8 +98,8 @@ public class QualityProfilesEditTest {
       .setLanguage(xooProfile.getLanguage())
       .setSelected("selected")
       .build()).getUsersList())
-      .extracting(SearchUsersResponse.User::getLogin)
-      .isEmpty();
+        .extracting(SearchUsersResponse.User::getLogin)
+        .isEmpty();
 
     // Add user 1
     addUserPermission(organization, user1, xooProfile);
@@ -103,8 +109,8 @@ public class QualityProfilesEditTest {
       .setLanguage(xooProfile.getLanguage())
       .setSelected("selected")
       .build()).getUsersList())
-      .extracting(SearchUsersResponse.User::getLogin)
-      .containsExactlyInAnyOrder(user1.getLogin());
+        .extracting(SearchUsersResponse.User::getLogin)
+        .containsExactlyInAnyOrder(user1.getLogin());
 
     // Remove user 1
     tester.qProfiles().service().removeUser(RemoveUserRequest.builder()
@@ -119,8 +125,8 @@ public class QualityProfilesEditTest {
       .setLanguage(xooProfile.getLanguage())
       .setSelected("selected")
       .build()).getUsersList())
-      .extracting(SearchUsersResponse.User::getLogin)
-      .isEmpty();
+        .extracting(SearchUsersResponse.User::getLogin)
+        .isEmpty();
   }
 
   @Test
@@ -161,8 +167,8 @@ public class QualityProfilesEditTest {
       .setLanguage(xooProfile.getLanguage())
       .setSelected("selected")
       .build()).getGroupsList())
-      .extracting(Group::getName)
-      .isEmpty();
+        .extracting(Group::getName)
+        .isEmpty();
 
     // Add group 1
     addGroupPermission(organization, group1, xooProfile);
@@ -172,8 +178,8 @@ public class QualityProfilesEditTest {
       .setLanguage(xooProfile.getLanguage())
       .setSelected("selected")
       .build()).getGroupsList())
-      .extracting(Group::getName)
-      .containsExactlyInAnyOrder(group1.getName());
+        .extracting(Group::getName)
+        .containsExactlyInAnyOrder(group1.getName());
 
     // Remove group 1
     tester.qProfiles().service().removeGroup(RemoveGroupRequest.builder()
@@ -188,14 +194,14 @@ public class QualityProfilesEditTest {
       .setLanguage(xooProfile.getLanguage())
       .setSelected("selected")
       .build()).getGroupsList())
-      .extracting(Group::getName)
-      .isEmpty();
+        .extracting(Group::getName)
+        .isEmpty();
   }
 
   @Test
   public void actions_when_user_can_edit_profiles() {
     Organization organization = tester.organizations().generate();
-    WsUsers.CreateWsResponse.User user = tester.users().generateMember(organization);
+    User user = tester.users().generateMember(organization);
     CreateWsResponse.QualityProfile xooProfile1 = tester.qProfiles().createXooProfile(organization);
     addUserPermission(organization, user, xooProfile1);
     CreateWsResponse.QualityProfile xooProfile2 = tester.qProfiles().createXooProfile(organization);
@@ -204,34 +210,63 @@ public class QualityProfilesEditTest {
     addGroupPermission(organization, group, xooProfile2);
     CreateWsResponse.QualityProfile xooProfile3 = tester.qProfiles().createXooProfile(organization);
 
-    QualityProfiles.SearchWsResponse result = tester.as(user.getLogin())
+    SearchWsResponse result = tester.as(user.getLogin())
       .qProfiles().service().search(new SearchWsRequest().setOrganizationKey(organization.getKey()));
     assertThat(result.getActions().getCreate()).isFalse();
     assertThat(result.getProfilesList())
-      .extracting(QualityProfiles.SearchWsResponse.QualityProfile::getKey, qp -> qp.getActions().getEdit(), qp -> qp.getActions().getCopy(), qp -> qp.getActions().getSetAsDefault())
-    .contains(
-      tuple(xooProfile1.getKey(), true, false, false),
-      tuple(xooProfile2.getKey(), true, false, false),
-      tuple(xooProfile3.getKey(), false, false, false));
+      .extracting(SearchWsResponse.QualityProfile::getKey, qp -> qp.getActions().getEdit(), qp -> qp.getActions().getCopy(), qp -> qp.getActions().getSetAsDefault())
+      .contains(
+        tuple(xooProfile1.getKey(), true, false, false),
+        tuple(xooProfile2.getKey(), true, false, false),
+        tuple(xooProfile3.getKey(), false, false, false));
   }
 
   @Test
   public void actions_when_user_is_global_qprofile_administer() {
     Organization organization = tester.organizations().generate();
-    WsUsers.CreateWsResponse.User user = tester.users().generateMember(organization);
+    User user = tester.users().generateMember(organization);
     CreateWsResponse.QualityProfile xooProfile = tester.qProfiles().createXooProfile(organization);
     tester.wsClient().permissions().addUser(new AddUserWsRequest().setOrganization(organization.getKey()).setLogin(user.getLogin()).setPermission("profileadmin"));
 
-    QualityProfiles.SearchWsResponse result = tester.as(user.getLogin())
+    SearchWsResponse result = tester.as(user.getLogin())
       .qProfiles().service().search(new SearchWsRequest().setOrganizationKey(organization.getKey()));
     assertThat(result.getActions().getCreate()).isTrue();
     assertThat(result.getProfilesList())
-      .extracting(QualityProfiles.SearchWsResponse.QualityProfile::getKey, qp -> qp.getActions().getEdit(), qp -> qp.getActions().getCopy(), qp -> qp.getActions().getSetAsDefault())
+      .extracting(SearchWsResponse.QualityProfile::getKey, qp -> qp.getActions().getEdit(), qp -> qp.getActions().getCopy(), qp -> qp.getActions().getSetAsDefault())
       .contains(
         tuple(xooProfile.getKey(), true, true, true));
   }
 
-  private void addUserPermission(Organization organization, WsUsers.CreateWsResponse.User user, CreateWsResponse.QualityProfile qProfile){
+  @Test
+  public void bulk_activate_rules_as_editor() {
+    Organization org = tester.organizations().generate();
+    CreateWsResponse.QualityProfile xooProfile = tester.qProfiles().createXooProfile(org);
+    User individualEditor = tester.users().generateMember(org);
+    addUserPermission(org, individualEditor, xooProfile);
+    WsUserGroups.Group group = tester.groups().generate(org);
+    addGroupPermission(org, group, xooProfile);
+    User groupEditor = tester.users().generateMember(org);
+    tester.groups().addMemberToGroups(org, groupEditor.getLogin(), group.getName());
+    // activate rule a group editor
+    tester.as(groupEditor.getLogin()).qProfiles().activateRule(xooProfile, RULE_ONE_BUG_PER_LINE);
+    tester.as(groupEditor.getLogin()).qProfiles().activateRule(xooProfile, RULE_ONE_ISSUE_PER_LINE);
+    SearchWsResponse.QualityProfile sonarWay = getProfile(org, p -> "Sonar way".equals(p.getName()) && "xoo".equals(p.getLanguage()) && p.getIsBuiltIn());
+
+    // Bulk activate rules as individual editor
+    tester.as(individualEditor.getLogin()).wsClient().wsConnector()
+      .call(new PostRequest("api/qualityprofiles/activate_rules")
+        .setParam("targetKey", xooProfile.getKey())
+        .setParam("qprofile", xooProfile.getKey())
+        .setParam("activation", "false")
+        .setParam("compareToProfile", sonarWay.getKey()))
+      .failIfNotSuccessful();
+
+    // Check that the profile has no missing rule from the Sonar way profile
+    assertThat(tester.qProfiles().service().show(new ShowRequest().setKey(xooProfile.getKey()).setCompareToSonarWay(true)).getCompareToSonarWay().getMissingRuleCount())
+      .isZero();
+  }
+
+  private void addUserPermission(Organization organization, User user, CreateWsResponse.QualityProfile qProfile) {
     tester.qProfiles().service().addUser(AddUserRequest.builder()
       .setOrganization(organization.getKey())
       .setQualityProfile(qProfile.getName())
@@ -240,7 +275,7 @@ public class QualityProfilesEditTest {
       .build());
   }
 
-  private void addGroupPermission(Organization organization, WsUserGroups.Group group, CreateWsResponse.QualityProfile qProfile){
+  private void addGroupPermission(Organization organization, WsUserGroups.Group group, CreateWsResponse.QualityProfile qProfile) {
     tester.qProfiles().service().addGroup(AddGroupRequest.builder()
       .setOrganization(organization.getKey())
       .setQualityProfile(qProfile.getName())
@@ -248,4 +283,12 @@ public class QualityProfilesEditTest {
       .setGroup(group.getName())
       .build());
   }
+
+  private SearchWsResponse.QualityProfile getProfile(Organization organization, Predicate<SearchWsResponse.QualityProfile> filter) {
+    return tester.qProfiles().service().search(new SearchWsRequest()
+      .setOrganizationKey(organization.getKey())).getProfilesList()
+      .stream()
+      .filter(filter)
+      .findAny().orElseThrow(IllegalStateException::new);
+  }
 }