diff options
author | Julien Lancelot <julien.lancelot@sonarsource.com> | 2017-09-27 14:33:56 +0200 |
---|---|---|
committer | Stas Vilchik <stas.vilchik@sonarsource.com> | 2017-10-02 17:18:15 +0200 |
commit | 94a98c6f367c3ecebc4f24e0a123c5091b7525ec (patch) | |
tree | a13db7989714b4b981f057a5c2faa3d855506e44 | |
parent | 843fce55ff4d7c2a8eebdc478ce8fa9cb02a6cea (diff) | |
download | sonarqube-94a98c6f367c3ecebc4f24e0a123c5091b7525ec.tar.gz sonarqube-94a98c6f367c3ecebc4f24e0a123c5091b7525ec.zip |
SONAR-1330 Check edit permissions in qprofiles WS
30 files changed, 646 insertions, 309 deletions
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/QProfileEditGroupsDao.java b/server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/QProfileEditGroupsDao.java index 93df5f96361..ce7859e7d0d 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/QProfileEditGroupsDao.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/QProfileEditGroupsDao.java @@ -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) { diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/QProfileEditGroupsMapper.java b/server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/QProfileEditGroupsMapper.java index 5bdaac413bb..71733115900 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/QProfileEditGroupsMapper.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/QProfileEditGroupsMapper.java @@ -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); diff --git a/server/sonar-db-dao/src/main/resources/org/sonar/db/qualityprofile/QProfileEditGroupsMapper.xml b/server/sonar-db-dao/src/main/resources/org/sonar/db/qualityprofile/QProfileEditGroupsMapper.xml index 50c1d355e1a..ddc8b09d175 100644 --- a/server/sonar-db-dao/src/main/resources/org/sonar/db/qualityprofile/QProfileEditGroupsMapper.xml +++ b/server/sonar-db-dao/src/main/resources/org/sonar/db/qualityprofile/QProfileEditGroupsMapper.xml @@ -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> diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/qualityprofile/QProfileEditGroupsDaoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/qualityprofile/QProfileEditGroupsDaoTest.java index 4b370cbdc0a..2aaf478f926 100644 --- a/server/sonar-db-dao/src/test/java/org/sonar/db/qualityprofile/QProfileEditGroupsDaoTest.java +++ b/server/sonar-db-dao/src/test/java/org/sonar/db/qualityprofile/QProfileEditGroupsDaoTest.java @@ -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 diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/ActivateRuleAction.java b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/ActivateRuleAction.java index 8d1e5d29794..b2d43aec94e 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/ActivateRuleAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/ActivateRuleAction.java @@ -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); } diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/ActivateRulesAction.java b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/ActivateRulesAction.java index cf40a39cf2f..1e2548e110a 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/ActivateRulesAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/ActivateRulesAction.java @@ -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)); } diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/AddGroupAction.java b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/AddGroupAction.java index 8e9a37d367b..74e73865769 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/AddGroupAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/AddGroupAction.java @@ -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) diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/AddProjectAction.java b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/AddProjectAction.java index a36847023f8..95821ee2c39 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/AddProjectAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/AddProjectAction.java @@ -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(); } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/AddUserAction.java b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/AddUserAction.java index a58cd4c4f19..c67bb6cf02c 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/AddUserAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/AddUserAction.java @@ -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) diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/ChangeParentAction.java b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/ChangeParentAction.java index 1e3aad6865b..c90cf81528c 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/ChangeParentAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/ChangeParentAction.java @@ -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); diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/DeactivateRuleAction.java b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/DeactivateRuleAction.java index 10b2721f42b..33b32fb1315 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/DeactivateRuleAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/DeactivateRuleAction.java @@ -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(); diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/DeactivateRulesAction.java b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/DeactivateRulesAction.java index 6d23a6c91d2..51888b6767f 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/DeactivateRulesAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/DeactivateRulesAction.java @@ -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); diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/DeleteAction.java b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/DeleteAction.java index 6921ef04fff..18dd0e15aa5 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/DeleteAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/DeleteAction.java @@ -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); diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/QProfileWsSupport.java b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/QProfileWsSupport.java index 6fb6e0702d8..8f0314f4b13 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/QProfileWsSupport.java +++ b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/QProfileWsSupport.java @@ -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) { diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/RemoveGroupAction.java b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/RemoveGroupAction.java index 83fb547bbf8..1f79c3ab598 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/RemoveGroupAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/RemoveGroupAction.java @@ -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) diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/RemoveProjectAction.java b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/RemoveProjectAction.java index 1ea1ac98715..b3df63abff3 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/RemoveProjectAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/RemoveProjectAction.java @@ -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(); } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/RemoveUserAction.java b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/RemoveUserAction.java index d2b8c6cc89e..a7fc123fd6a 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/RemoveUserAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/RemoveUserAction.java @@ -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) diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/RenameAction.java b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/RenameAction.java index 3801232ac50..cbf9ce28d98 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/RenameAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/RenameAction.java @@ -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 -> { diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/SearchGroupsAction.java b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/SearchGroupsAction.java index b0cc85b5a6c..2a753a4d501 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/SearchGroupsAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/SearchGroupsAction.java @@ -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); } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/SearchUsersAction.java b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/SearchUsersAction.java index 1dcc2514481..9d5cc5c5025 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/SearchUsersAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/SearchUsersAction.java @@ -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); } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/ActivateRuleActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/ActivateRuleActionTest.java index 229a95e4f9c..3d441b089bd 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/ActivateRuleActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/ActivateRuleActionTest.java @@ -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)); + } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/ActivateRulesActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/ActivateRulesActionTest.java index 4caa6f06ce2..c798ea0a9ce 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/ActivateRulesActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/ActivateRulesActionTest.java @@ -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(); } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/AddProjectActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/AddProjectActionTest.java index 47cd8cfd4f1..1ddd508c509 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/AddProjectActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/AddProjectActionTest.java @@ -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) { diff --git a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/ChangeParentActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/ChangeParentActionTest.java index 110b3284175..0d13b987845 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/ChangeParentActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/ChangeParentActionTest.java @@ -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 @@ -319,8 +320,39 @@ public class ChangeParentActionTest { } @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(); } diff --git a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/DeactivateRuleActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/DeactivateRuleActionTest.java index c46792036de..3a114e10538 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/DeactivateRuleActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/DeactivateRuleActionTest.java @@ -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"); @@ -88,52 +90,11 @@ public class DeactivateRuleActionTest { } @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(); + } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/DeactivateRulesActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/DeactivateRulesActionTest.java index 4ce81b1b275..75ff057767b 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/DeactivateRulesActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/DeactivateRulesActionTest.java @@ -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()); diff --git a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/DeleteActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/DeleteActionTest.java index 1ee7fffe84a..ab6bff48355 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/DeleteActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/DeleteActionTest.java @@ -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); @@ -135,6 +140,25 @@ public class DeleteActionTest { } @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(); QProfileDto profile1 = db.qualityProfiles().insert(organization, p -> p.setIsBuiltIn(true)); @@ -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); } diff --git a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/RemoveProjectActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/RemoveProjectActionTest.java index 50576a44a1b..fb91e2ef9bf 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/RemoveProjectActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/RemoveProjectActionTest.java @@ -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) { diff --git a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/RenameActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/RenameActionTest.java index 8211b0d87df..4a8cfa693b5 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/RenameActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/RenameActionTest.java @@ -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() @@ -136,6 +137,34 @@ public class RenameActionTest { } @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"); @@ -202,21 +231,6 @@ public class RenameActionTest { } @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) { diff --git a/tests/src/test/java/org/sonarqube/tests/qualityProfile/QualityProfilesEditTest.java b/tests/src/test/java/org/sonarqube/tests/qualityProfile/QualityProfilesEditTest.java index 02c71b7a9ad..bcf23e3e4cc 100644 --- a/tests/src/test/java/org/sonarqube/tests/qualityProfile/QualityProfilesEditTest.java +++ b/tests/src/test/java/org/sonarqube/tests/qualityProfile/QualityProfilesEditTest.java @@ -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); + } } |