From: Julien Lancelot Date: Mon, 25 Sep 2017 12:57:00 +0000 (+0200) Subject: SONAR-1330 Add group permission to edit single quality profile X-Git-Tag: 6.6-RC1~103 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=e212b5945cbd0fdfda9caeac6cf969732f11f74e;p=sonarqube.git SONAR-1330 Add group permission to edit single quality profile --- diff --git a/server/sonar-ce/src/test/java/org/sonar/ce/container/ComputeEngineContainerImplTest.java b/server/sonar-ce/src/test/java/org/sonar/ce/container/ComputeEngineContainerImplTest.java index f91d4c72718..7c71c910ed3 100644 --- a/server/sonar-ce/src/test/java/org/sonar/ce/container/ComputeEngineContainerImplTest.java +++ b/server/sonar-ce/src/test/java/org/sonar/ce/container/ComputeEngineContainerImplTest.java @@ -152,7 +152,7 @@ public class ComputeEngineContainerImplTest { assertThat(picoContainer.getParent().getParent().getParent().getComponentAdapters()).hasSize( COMPONENTS_IN_LEVEL_1_AT_CONSTRUCTION + 26 // level 1 - + 50 // content of DaoModule + + 51 // content of DaoModule + 3 // content of EsSearchModule + 65 // content of CorePropertyDefinitions + 1 // StopFlagContainer diff --git a/server/sonar-db-core/src/main/java/org/sonar/db/version/SqTables.java b/server/sonar-db-core/src/main/java/org/sonar/db/version/SqTables.java index a621cdc7706..093e9d0482f 100644 --- a/server/sonar-db-core/src/main/java/org/sonar/db/version/SqTables.java +++ b/server/sonar-db-core/src/main/java/org/sonar/db/version/SqTables.java @@ -86,6 +86,7 @@ public final class SqTables { "project_qprofiles", "properties", "qprofile_changes", + "qprofile_edit_groups", "qprofile_edit_users", "quality_gates", "quality_gate_conditions", diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/DaoModule.java b/server/sonar-db-dao/src/main/java/org/sonar/db/DaoModule.java index d6fa06ecacd..f3b621fe326 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/DaoModule.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/DaoModule.java @@ -60,6 +60,7 @@ import org.sonar.db.qualitygate.QualityGateDao; import org.sonar.db.qualityprofile.ActiveRuleDao; import org.sonar.db.qualityprofile.DefaultQProfileDao; import org.sonar.db.qualityprofile.QProfileChangeDao; +import org.sonar.db.qualityprofile.QProfileEditGroupsDao; import org.sonar.db.qualityprofile.QProfileEditUsersDao; import org.sonar.db.qualityprofile.QualityProfileDao; import org.sonar.db.rule.RuleDao; @@ -115,6 +116,7 @@ public class DaoModule extends Module { PropertiesDao.class, PurgeDao.class, QProfileChangeDao.class, + QProfileEditGroupsDao.class, QProfileEditUsersDao.class, QualityGateConditionDao.class, QualityGateDao.class, diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/DbClient.java b/server/sonar-db-dao/src/main/java/org/sonar/db/DbClient.java index bb269e0d2c4..ae81a02f4cf 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/DbClient.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/DbClient.java @@ -58,6 +58,7 @@ import org.sonar.db.qualitygate.QualityGateDao; import org.sonar.db.qualityprofile.ActiveRuleDao; import org.sonar.db.qualityprofile.DefaultQProfileDao; import org.sonar.db.qualityprofile.QProfileChangeDao; +import org.sonar.db.qualityprofile.QProfileEditGroupsDao; import org.sonar.db.qualityprofile.QProfileEditUsersDao; import org.sonar.db.qualityprofile.QualityProfileDao; import org.sonar.db.rule.RuleDao; @@ -128,6 +129,7 @@ public class DbClient { private final PluginDao pluginDao; private final BranchDao branchDao; private final QProfileEditUsersDao qProfileEditUsersDao; + private final QProfileEditGroupsDao qProfileEditGroupsDao; public DbClient(Database database, MyBatis myBatis, DBSessions dbSessions, Dao... daos) { this.database = database; @@ -188,6 +190,7 @@ public class DbClient { pluginDao = getDao(map, PluginDao.class); branchDao = getDao(map, BranchDao.class); qProfileEditUsersDao = getDao(map, QProfileEditUsersDao.class); + qProfileEditGroupsDao = getDao(map, QProfileEditGroupsDao.class); } public DbSession openSession(boolean batch) { @@ -398,6 +401,10 @@ public class DbClient { return qProfileEditUsersDao; } + public QProfileEditGroupsDao qProfileEditGroupsDao() { + return qProfileEditGroupsDao; + } + protected K getDao(Map map, Class clazz) { return (K) map.get(clazz); } diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/MyBatis.java b/server/sonar-db-dao/src/main/java/org/sonar/db/MyBatis.java index 244cb18af54..c1bbe7fb0d9 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/MyBatis.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/MyBatis.java @@ -103,6 +103,7 @@ import org.sonar.db.qualityprofile.ActiveRuleMapper; import org.sonar.db.qualityprofile.ActiveRuleParamDto; import org.sonar.db.qualityprofile.DefaultQProfileMapper; import org.sonar.db.qualityprofile.QProfileChangeMapper; +import org.sonar.db.qualityprofile.QProfileEditGroupsMapper; import org.sonar.db.qualityprofile.QProfileEditUsersMapper; import org.sonar.db.qualityprofile.QualityProfileMapper; import org.sonar.db.rule.RuleDto; @@ -229,6 +230,7 @@ public class MyBatis implements Startable { PropertiesMapper.class, PurgeMapper.class, QProfileChangeMapper.class, + QProfileEditGroupsMapper.class, QProfileEditUsersMapper.class, QualityGateConditionMapper.class, QualityGateMapper.class, 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 new file mode 100644 index 00000000000..1f429dc112e --- /dev/null +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/QProfileEditGroupsDao.java @@ -0,0 +1,46 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.db.qualityprofile; + +import org.sonar.api.utils.System2; +import org.sonar.db.Dao; +import org.sonar.db.DbSession; +import org.sonar.db.user.GroupDto; + +public class QProfileEditGroupsDao implements Dao { + + private final System2 system2; + + public QProfileEditGroupsDao(System2 system2) { + this.system2 = system2; + } + + public boolean exists(DbSession dbSession, QProfileDto profile, GroupDto group) { + return mapper(dbSession).selectByQProfileAndGroup(profile.getKee(), group.getId()) != null; + } + + public void insert(DbSession dbSession, QProfileEditGroupsDto dto) { + mapper(dbSession).insert(dto, system2.now()); + } + + private static QProfileEditGroupsMapper mapper(DbSession dbSession) { + return dbSession.getMapper(QProfileEditGroupsMapper.class); + } +} diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/QProfileEditGroupsDto.java b/server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/QProfileEditGroupsDto.java new file mode 100644 index 00000000000..eb24346e575 --- /dev/null +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/QProfileEditGroupsDto.java @@ -0,0 +1,55 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.db.qualityprofile; + +public class QProfileEditGroupsDto { + + private String uuid; + private int groupId; + private String qProfileUuid; + + public String getUuid() { + return uuid; + } + + public QProfileEditGroupsDto setUuid(String uuid) { + this.uuid = uuid; + return this; + } + + public int getGroupId() { + return groupId; + } + + public QProfileEditGroupsDto setGroupId(int groupId) { + this.groupId = groupId; + return this; + } + + public String getQProfileUuid() { + return qProfileUuid; + } + + public QProfileEditGroupsDto setQProfileUuid(String qProfileUuid) { + this.qProfileUuid = qProfileUuid; + return this; + } + +} 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 new file mode 100644 index 00000000000..ce96f4ea4af --- /dev/null +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/QProfileEditGroupsMapper.java @@ -0,0 +1,30 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.db.qualityprofile; + +import org.apache.ibatis.annotations.Param; + +public interface QProfileEditGroupsMapper { + + QProfileEditGroupsDto selectByQProfileAndGroup(@Param("qProfileUuid") String qProfileUuid, @Param("groupId") int groupId); + + void insert(@Param("dto") QProfileEditGroupsDto dto, @Param("now") long now); + +} 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 new file mode 100644 index 00000000000..40622269330 --- /dev/null +++ b/server/sonar-db-dao/src/main/resources/org/sonar/db/qualityprofile/QProfileEditGroupsMapper.xml @@ -0,0 +1,36 @@ + + + + + + + qeg.uuid as "uuid", + qeg.group_id as "groupId", + qeg.qprofile_uuid as "qProfileUuid" + + + + + + insert into qprofile_edit_groups( + uuid, + group_id, + qprofile_uuid, + created_at + ) values ( + #{dto.uuid, jdbcType=VARCHAR}, + #{dto.groupId, jdbcType=INTEGER}, + #{dto.qProfileUuid, jdbcType=VARCHAR}, + #{now, jdbcType=BIGINT} + ) + + + + diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/DaoModuleTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/DaoModuleTest.java index 986fbd7e2c3..4fe76690889 100644 --- a/server/sonar-db-dao/src/test/java/org/sonar/db/DaoModuleTest.java +++ b/server/sonar-db-dao/src/test/java/org/sonar/db/DaoModuleTest.java @@ -29,6 +29,6 @@ public class DaoModuleTest { public void verify_count_of_added_components() { ComponentContainer container = new ComponentContainer(); new DaoModule().configure(container); - assertThat(container.size()).isEqualTo(2 + 50); + assertThat(container.size()).isEqualTo(2 + 51); } } 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 new file mode 100644 index 00000000000..41ed91c6e04 --- /dev/null +++ b/server/sonar-db-dao/src/test/java/org/sonar/db/qualityprofile/QProfileEditGroupsDaoTest.java @@ -0,0 +1,74 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.db.qualityprofile; + +import org.junit.Rule; +import org.junit.Test; +import org.sonar.api.utils.System2; +import org.sonar.api.utils.internal.TestSystem2; +import org.sonar.db.DbTester; +import org.sonar.db.organization.OrganizationDto; +import org.sonar.db.user.GroupDto; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.entry; + +public class QProfileEditGroupsDaoTest { + + private static final long NOW = 10_000_000_000L; + + private System2 system2 = new TestSystem2().setNow(NOW); + + @Rule + public DbTester db = DbTester.create(system2); + + private QProfileEditGroupsDao underTest = db.getDbClient().qProfileEditGroupsDao(); + + @Test + public void exists() { + OrganizationDto organization = db.organizations().insert(); + QProfileDto profile = db.qualityProfiles().insert(organization); + QProfileDto anotherProfile = db.qualityProfiles().insert(organization); + GroupDto group = db.users().insertGroup(organization); + GroupDto anotherGroup = db.users().insertGroup(organization); + db.qualityProfiles().addGroupPermission(profile, group); + + assertThat(underTest.exists(db.getSession(), profile, group)).isTrue(); + assertThat(underTest.exists(db.getSession(), profile, anotherGroup)).isFalse(); + assertThat(underTest.exists(db.getSession(), anotherProfile, group)).isFalse(); + assertThat(underTest.exists(db.getSession(), anotherProfile, anotherGroup)).isFalse(); + } + + @Test + public void insert() { + underTest.insert(db.getSession(), new QProfileEditGroupsDto() + .setUuid("ABCD") + .setGroupId(100) + .setQProfileUuid("QPROFILE") + ); + + assertThat(db.selectFirst(db.getSession(), "select uuid as \"uuid\", group_id as \"groupId\", qprofile_uuid as \"qProfileUuid\", created_at as \"createdAt\" from qprofile_edit_groups")).contains( + entry("uuid", "ABCD"), + entry("groupId", 100L), + entry("qProfileUuid", "QPROFILE"), + entry("createdAt", NOW)); + } + +} diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/qualityprofile/QualityProfileDbTester.java b/server/sonar-db-dao/src/test/java/org/sonar/db/qualityprofile/QualityProfileDbTester.java index 7dc2a64ea4b..98f86b66360 100644 --- a/server/sonar-db-dao/src/test/java/org/sonar/db/qualityprofile/QualityProfileDbTester.java +++ b/server/sonar-db-dao/src/test/java/org/sonar/db/qualityprofile/QualityProfileDbTester.java @@ -30,6 +30,7 @@ import org.sonar.db.DbTester; import org.sonar.db.component.ComponentDto; import org.sonar.db.organization.OrganizationDto; import org.sonar.db.rule.RuleDefinitionDto; +import org.sonar.db.user.GroupDto; import org.sonar.db.user.UserDto; import static org.apache.commons.lang.math.RandomUtils.nextInt; @@ -118,6 +119,14 @@ public class QualityProfileDbTester { .setQProfileUuid(profile.getKee()) ); dbSession.commit(); + } + public void addGroupPermission(QProfileDto profile, GroupDto group){ + dbClient.qProfileEditGroupsDao().insert(dbSession, new QProfileEditGroupsDto() + .setUuid(UuidFactoryFast.getInstance().create()) + .setGroupId(group.getId()) + .setQProfileUuid(profile.getKee()) + ); + dbSession.commit(); } } 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 7251b36acde..7e092cb5231 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 @@ -19,18 +19,42 @@ */ package org.sonar.server.qualityprofile.ws; +import java.util.Arrays; +import org.sonar.api.resources.Language; +import org.sonar.api.resources.Languages; import org.sonar.api.server.ws.Request; import org.sonar.api.server.ws.Response; import org.sonar.api.server.ws.WebService; +import org.sonar.core.util.UuidFactory; +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.db.qualityprofile.QProfileEditGroupsDto; +import org.sonar.db.user.GroupDto; -import static org.sonar.core.util.Uuids.UUID_EXAMPLE_01; +import static org.sonar.core.util.stream.MoreCollectors.toSet; import static org.sonar.server.qualityprofile.ws.QProfileWsSupport.createOrganizationParam; import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.ACTION_ADD_GROUP; import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_GROUP; -import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_PROFILE; +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 AddGroupAction implements QProfileWsAction { + private final DbClient dbClient; + private final UuidFactory uuidFactory; + private final QProfileWsSupport wsSupport; + private final Languages languages; + + public AddGroupAction(DbClient dbClient, UuidFactory uuidFactory, QProfileWsSupport wsSupport, Languages languages) { + this.dbClient = dbClient; + this.uuidFactory = uuidFactory; + this.wsSupport = wsSupport; + this.languages = languages; + } + @Override public void define(WebService.NewController context) { WebService.NewAction action = context @@ -42,10 +66,16 @@ public class AddGroupAction implements QProfileWsAction { .setInternal(true) .setSince("6.6"); - action.createParam(PARAM_PROFILE) - .setDescription("Quality Profile key.") + action.createParam(PARAM_QUALITY_PROFILE) + .setDescription("Quality Profile name") .setRequired(true) - .setExampleValue(UUID_EXAMPLE_01); + .setExampleValue("Recommended quality profile"); + + action + .createParam(PARAM_LANGUAGE) + .setDescription("Quality profile language") + .setRequired(true) + .setPossibleValues(Arrays.stream(languages.all()).map(Language::getKey).collect(toSet())); action.createParam(PARAM_GROUP) .setDescription("Group name") @@ -57,6 +87,25 @@ public class AddGroupAction implements QProfileWsAction { @Override public void handle(Request request, Response response) throws Exception { - // TODO + try (DbSession dbSession = dbClient.openSession(false)) { + OrganizationDto organization = wsSupport.getOrganizationByKey(dbSession, request.param(PARAM_ORGANIZATION)); + QProfileDto profile = wsSupport.getProfile(dbSession, organization, request.mandatoryParam(PARAM_QUALITY_PROFILE), request.mandatoryParam(PARAM_LANGUAGE)); + wsSupport.checkCanEdit(dbSession, profile); + GroupDto user = wsSupport.getGroup(dbSession, organization, request.mandatoryParam(PARAM_GROUP)); + addGroup(dbSession, profile, user); + } + response.noContent(); + } + + private void addGroup(DbSession dbSession, QProfileDto profile, GroupDto group) { + if (dbClient.qProfileEditGroupsDao().exists(dbSession, profile, group)) { + return; + } + dbClient.qProfileEditGroupsDao().insert(dbSession, + new QProfileEditGroupsDto() + .setUuid(uuidFactory.create()) + .setGroupId(group.getId()) + .setQProfileUuid(profile.getKee())); + dbSession.commit(); } } 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 1649c4ccf96..38c11723d09 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 @@ -29,6 +29,7 @@ import org.sonar.db.DbSession; 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.organization.DefaultOrganizationProvider; import org.sonar.server.user.UserSession; @@ -39,6 +40,7 @@ import static com.google.common.base.Preconditions.checkState; import static java.util.Objects.requireNonNull; import static org.sonar.server.user.AbstractUserSession.insufficientPrivilegesException; import static org.sonar.server.ws.WsUtils.checkFound; +import static org.sonar.server.ws.WsUtils.checkFoundWithOptional; import static org.sonar.server.ws.WsUtils.checkRequest; import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_ORGANIZATION; @@ -111,6 +113,12 @@ public class QProfileWsSupport { return user; } + public GroupDto getGroup(DbSession dbSession, OrganizationDto organization, String groupName) { + Optional group = dbClient.groupDao().selectByName(dbSession, organization.getUuid(), groupName); + checkFoundWithOptional(group, "No group with name '%s' in organization '%s'", groupName, organization.getKey()); + return group.get(); + } + public void checkPermission(DbSession dbSession, QProfileDto profile) { OrganizationDto organization = getOrganization(dbSession, profile); userSession.checkPermission(OrganizationPermission.ADMINISTER_QUALITY_PROFILES, organization); diff --git a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/AddGroupActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/AddGroupActionTest.java new file mode 100644 index 00000000000..8b3d1051c0d --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/AddGroupActionTest.java @@ -0,0 +1,293 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.server.qualityprofile.ws; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.sonar.api.resources.Languages; +import org.sonar.api.server.ws.WebService; +import org.sonar.core.util.UuidFactory; +import org.sonar.core.util.UuidFactoryFast; +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.NotFoundException; +import org.sonar.server.language.LanguageTesting; +import org.sonar.server.organization.TestDefaultOrganizationProvider; +import org.sonar.server.tester.UserSessionRule; +import org.sonar.server.ws.TestResponse; +import org.sonar.server.ws.WsActionTester; + +import static java.lang.String.format; +import static org.assertj.core.api.Assertions.assertThat; +import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_GROUP; +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 AddGroupActionTest { + + private static final String XOO = "xoo"; + private static final Languages LANGUAGES = LanguageTesting.newLanguages(XOO); + + @Rule + public ExpectedException expectedException = ExpectedException.none(); + @Rule + public UserSessionRule userSession = UserSessionRule.standalone(); + @Rule + public DbTester db = DbTester.create(); + + private QProfileWsSupport wsSupport = new QProfileWsSupport(db.getDbClient(), userSession, TestDefaultOrganizationProvider.from(db)); + private UuidFactory uuidFactory = UuidFactoryFast.getInstance(); + + private WsActionTester ws = new WsActionTester(new AddGroupAction(db.getDbClient(), uuidFactory, wsSupport, LANGUAGES)); + + @Test + public void test_definition() { + WebService.Action def = ws.getDef(); + assertThat(def.key()).isEqualTo("add_group"); + assertThat(def.isPost()).isTrue(); + assertThat(def.isInternal()).isTrue(); + assertThat(def.params()).extracting(WebService.Param::key).containsExactlyInAnyOrder("organization", "qualityProfile", "language", "group"); + } + + @Test + public void add_group() { + OrganizationDto organization = db.organizations().insert(); + QProfileDto profile = db.qualityProfiles().insert(organization, p -> p.setLanguage(XOO)); + GroupDto group = db.users().insertGroup(organization); + userSession.logIn().addPermission(OrganizationPermission.ADMINISTER_QUALITY_PROFILES, organization); + + TestResponse response = ws.newRequest() + .setParam(PARAM_QUALITY_PROFILE, profile.getName()) + .setParam(PARAM_LANGUAGE, XOO) + .setParam(PARAM_GROUP, group.getName()) + .setParam(PARAM_ORGANIZATION, organization.getKey()) + .execute(); + + assertThat(response.getStatus()).isEqualTo(204); + assertThat(db.getDbClient().qProfileEditGroupsDao().exists(db.getSession(), profile, group)).isTrue(); + } + + @Test + public void does_nothing_when_group_can_already_edit_profile() { + OrganizationDto organization = db.organizations().insert(); + QProfileDto profile = db.qualityProfiles().insert(organization, p -> p.setLanguage(XOO)); + GroupDto group = db.users().insertGroup(organization); + db.qualityProfiles().addGroupPermission(profile, group); + assertThat(db.getDbClient().qProfileEditGroupsDao().exists(db.getSession(), profile, group)).isTrue(); + userSession.logIn().addPermission(OrganizationPermission.ADMINISTER_QUALITY_PROFILES, organization); + + ws.newRequest() + .setParam(PARAM_QUALITY_PROFILE, profile.getName()) + .setParam(PARAM_LANGUAGE, XOO) + .setParam(PARAM_GROUP, group.getName()) + .setParam(PARAM_ORGANIZATION, organization.getKey()) + .execute(); + + assertThat(db.getDbClient().qProfileEditGroupsDao().exists(db.getSession(), profile, group)).isTrue(); + } + + @Test + public void qp_administers_can_add_group() { + OrganizationDto organization = db.organizations().insert(); + QProfileDto profile = db.qualityProfiles().insert(organization, p -> p.setLanguage(XOO)); + GroupDto group = db.users().insertGroup(organization); + userSession.logIn().addPermission(OrganizationPermission.ADMINISTER_QUALITY_PROFILES, organization); + + ws.newRequest() + .setParam(PARAM_QUALITY_PROFILE, profile.getName()) + .setParam(PARAM_LANGUAGE, XOO) + .setParam(PARAM_GROUP, group.getName()) + .setParam(PARAM_ORGANIZATION, organization.getKey()) + .execute(); + + assertThat(db.getDbClient().qProfileEditGroupsDao().exists(db.getSession(), profile, group)).isTrue(); + } + + @Test + public void qp_editors_can_add_group() { + OrganizationDto organization = db.organizations().insert(); + QProfileDto profile = db.qualityProfiles().insert(organization, p -> p.setLanguage(XOO)); + GroupDto group = db.users().insertGroup(organization); + UserDto userAllowedToEditProfile = db.users().insertUser(); + db.qualityProfiles().addUserPermission(profile, userAllowedToEditProfile); + userSession.logIn(userAllowedToEditProfile); + + ws.newRequest() + .setParam(PARAM_QUALITY_PROFILE, profile.getName()) + .setParam(PARAM_LANGUAGE, XOO) + .setParam(PARAM_GROUP, group.getName()) + .setParam(PARAM_ORGANIZATION, organization.getKey()) + .execute(); + + assertThat(db.getDbClient().qProfileEditGroupsDao().exists(db.getSession(), profile, group)).isTrue(); + } + + @Test + public void uses_default_organization_when_no_organization() { + OrganizationDto organization = db.getDefaultOrganization(); + QProfileDto profile = db.qualityProfiles().insert(organization, p -> p.setLanguage(XOO)); + GroupDto group = db.users().insertGroup(organization); + userSession.logIn().addPermission(OrganizationPermission.ADMINISTER_QUALITY_PROFILES, organization); + + ws.newRequest() + .setParam(PARAM_QUALITY_PROFILE, profile.getName()) + .setParam(PARAM_LANGUAGE, XOO) + .setParam(PARAM_GROUP, group.getName()) + .execute(); + + assertThat(db.getDbClient().qProfileEditGroupsDao().exists(db.getSession(), profile, group)).isTrue(); + } + + @Test + public void fail_when_group_does_not_exist() { + OrganizationDto organization = db.organizations().insert(); + QProfileDto profile = db.qualityProfiles().insert(organization, p -> p.setLanguage(XOO)); + userSession.logIn().addPermission(OrganizationPermission.ADMINISTER_QUALITY_PROFILES, organization); + + expectedException.expect(NotFoundException.class); + expectedException.expectMessage(format("No group with name 'unknown' in organization '%s'", organization.getKey())); + + ws.newRequest() + .setParam(PARAM_QUALITY_PROFILE, profile.getName()) + .setParam(PARAM_LANGUAGE, XOO) + .setParam(PARAM_GROUP, "unknown") + .setParam(PARAM_ORGANIZATION, organization.getKey()) + .execute(); + } + + @Test + public void fail_when_qprofile_does_not_exist() { + OrganizationDto organization = db.organizations().insert(); + GroupDto group = db.users().insertGroup(organization); + userSession.logIn().addPermission(OrganizationPermission.ADMINISTER_QUALITY_PROFILES, organization); + + expectedException.expect(NotFoundException.class); + expectedException.expectMessage(format("Quality Profile for language 'xoo' and name 'unknown' does not exist in organization '%s'", organization.getKey())); + + ws.newRequest() + .setParam(PARAM_QUALITY_PROFILE, "unknown") + .setParam(PARAM_LANGUAGE, XOO) + .setParam(PARAM_GROUP, group.getName()) + .setParam(PARAM_ORGANIZATION, organization.getKey()) + .execute(); + } + + @Test + public void fail_when_qprofile_does_not_belong_to_organization() { + OrganizationDto organization = db.organizations().insert(); + GroupDto group = db.users().insertGroup(organization); + OrganizationDto anotherOrganization = db.organizations().insert(); + QProfileDto profile = db.qualityProfiles().insert(anotherOrganization, p -> p.setLanguage(XOO)); + userSession.logIn().addPermission(OrganizationPermission.ADMINISTER_QUALITY_PROFILES, organization); + + expectedException.expect(NotFoundException.class); + expectedException.expectMessage(format("Quality Profile for language 'xoo' and name '%s' does not exist in organization '%s'", profile.getName(), organization.getKey())); + + ws.newRequest() + .setParam(PARAM_QUALITY_PROFILE, profile.getName()) + .setParam(PARAM_LANGUAGE, XOO) + .setParam(PARAM_GROUP, group.getName()) + .setParam(PARAM_ORGANIZATION, organization.getKey()) + .execute(); + } + + @Test + public void fail_when_group_does_not_belong_to_organization() { + OrganizationDto organization = db.organizations().insert(); + OrganizationDto anotherOrganization = db.organizations().insert(); + GroupDto group = db.users().insertGroup(anotherOrganization); + QProfileDto profile = db.qualityProfiles().insert(organization, p -> p.setLanguage(XOO)); + userSession.logIn().addPermission(OrganizationPermission.ADMINISTER_QUALITY_PROFILES, organization); + + expectedException.expect(NotFoundException.class); + expectedException.expectMessage(format("No group with name '%s' in organization '%s'", group.getName(), organization.getKey())); + + ws.newRequest() + .setParam(PARAM_QUALITY_PROFILE, profile.getName()) + .setParam(PARAM_LANGUAGE, XOO) + .setParam(PARAM_GROUP, group.getName()) + .setParam(PARAM_ORGANIZATION, organization.getKey()) + .execute(); + } + + @Test + public void fail_when_wrong_language() { + OrganizationDto organization = db.organizations().insert(); + QProfileDto profile = db.qualityProfiles().insert(organization, p -> p.setLanguage("unknown")); + UserDto user = db.users().insertUser(); + db.organizations().addMember(organization, user); + userSession.logIn().addPermission(OrganizationPermission.ADMINISTER_QUALITY_PROFILES, organization); + + expectedException.expect(NotFoundException.class); + expectedException.expectMessage(format("Quality Profile for language 'xoo' and name '%s' does not exist in organization '%s'", profile.getName(), organization.getKey())); + + ws.newRequest() + .setParam(PARAM_QUALITY_PROFILE, profile.getName()) + .setParam(PARAM_LANGUAGE, XOO) + .setParam(PARAM_GROUP, user.getLogin()) + .setParam(PARAM_ORGANIZATION, organization.getKey()) + .execute(); + } + + @Test + public void fail_when_qp_is_built_in() { + OrganizationDto organization = db.organizations().insert(); + UserDto user = db.users().insertUser(); + db.organizations().addMember(organization, user); + QProfileDto profile = db.qualityProfiles().insert(organization, p -> p.setLanguage(XOO).setIsBuiltIn(true)); + userSession.logIn().addPermission(OrganizationPermission.ADMINISTER_QUALITY_PROFILES, organization); + + expectedException.expect(BadRequestException.class); + expectedException.expectMessage(String.format("Operation forbidden for built-in Quality Profile '%s' with language 'xoo'", profile.getName())); + + ws.newRequest() + .setParam(PARAM_QUALITY_PROFILE, profile.getName()) + .setParam(PARAM_LANGUAGE, XOO) + .setParam(PARAM_GROUP, user.getLogin()) + .setParam(PARAM_ORGANIZATION, organization.getKey()) + .execute(); + } + + @Test + public void fail_when_not_enough_permission() { + OrganizationDto organization = db.organizations().insert(); + QProfileDto profile = db.qualityProfiles().insert(organization, p -> p.setLanguage(XOO)); + UserDto user = db.users().insertUser(); + db.organizations().addMember(organization, user); + userSession.logIn(db.users().insertUser()).addPermission(OrganizationPermission.ADMINISTER_QUALITY_GATES, organization); + + expectedException.expect(ForbiddenException.class); + + ws.newRequest() + .setParam(PARAM_QUALITY_PROFILE, profile.getName()) + .setParam(PARAM_LANGUAGE, XOO) + .setParam(PARAM_GROUP, user.getLogin()) + .setParam(PARAM_ORGANIZATION, organization.getKey()) + .execute(); + } +} diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/qualityprofile/AddGroupRequest.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/qualityprofile/AddGroupRequest.java new file mode 100644 index 00000000000..8fe5c8ac94d --- /dev/null +++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/qualityprofile/AddGroupRequest.java @@ -0,0 +1,93 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarqube.ws.client.qualityprofile; + +import javax.annotation.concurrent.Immutable; + +@Immutable +public class AddGroupRequest { + + private final String organization; + private final String language; + private final String qualityProfile; + private final String group; + + private AddGroupRequest(Builder builder) { + this.language = builder.language; + this.organization = builder.organization; + this.qualityProfile = builder.qualityProfile; + this.group = builder.group; + } + + public String getLanguage() { + return language; + } + + public String getOrganization() { + return organization; + } + + public String getQualityProfile() { + return qualityProfile; + } + + public String getGroup() { + return group; + } + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + private String organization; + private String qualityProfile; + private String language; + private String group; + + private Builder() { + // enforce factory method use + } + + public Builder setLanguage(String language) { + this.language = language; + return this; + } + + public Builder setOrganization(String organization) { + this.organization = organization; + return this; + } + + public Builder setQualityProfile(String qualityProfile) { + this.qualityProfile = qualityProfile; + return this; + } + + public Builder setGroup(String group) { + this.group = group; + return this; + } + + public AddGroupRequest build() { + return new AddGroupRequest(this); + } + } +} diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/qualityprofile/QualityProfilesService.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/qualityprofile/QualityProfilesService.java index bc95c27b7b6..6a087a42088 100644 --- a/sonar-ws/src/main/java/org/sonarqube/ws/client/qualityprofile/QualityProfilesService.java +++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/qualityprofile/QualityProfilesService.java @@ -36,6 +36,7 @@ import static org.sonar.api.server.ws.WebService.Param.PAGE_SIZE; import static org.sonar.api.server.ws.WebService.Param.SELECTED; import static org.sonar.api.server.ws.WebService.Param.TEXT_QUERY; import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.ACTION_ACTIVATE_RULE; +import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.ACTION_ADD_GROUP; import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.ACTION_ADD_PROJECT; import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.ACTION_ADD_USER; import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.ACTION_CHANGE_PARENT; @@ -54,6 +55,7 @@ import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters. import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_COMPARE_TO_SONAR_WAY; import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_DEFAULTS; import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_FROM_KEY; +import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_GROUP; import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_LANGUAGE; import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_NAME; import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_LOGIN; @@ -209,4 +211,12 @@ public class QualityProfilesService extends BaseService { .setParam(PAGE_SIZE, request.getPageSize()), SearchUsersResponse.parser()); } + + public void addGroup(AddGroupRequest request) { + call(new PostRequest(path(ACTION_ADD_GROUP)) + .setParam(PARAM_ORGANIZATION, request.getOrganization()) + .setParam(PARAM_QUALITY_PROFILE, request.getQualityProfile()) + .setParam(PARAM_LANGUAGE, request.getLanguage()) + .setParam(PARAM_GROUP, request.getGroup())); + } } diff --git a/sonar-ws/src/test/java/org/sonarqube/ws/client/qualityprofile/QualityProfilesServiceTest.java b/sonar-ws/src/test/java/org/sonarqube/ws/client/qualityprofile/QualityProfilesServiceTest.java index 655686b3e07..9e4a414cb26 100644 --- a/sonar-ws/src/test/java/org/sonarqube/ws/client/qualityprofile/QualityProfilesServiceTest.java +++ b/sonar-ws/src/test/java/org/sonarqube/ws/client/qualityprofile/QualityProfilesServiceTest.java @@ -39,6 +39,7 @@ import static org.sonar.api.server.ws.WebService.Param.TEXT_QUERY; import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_COMPARE_TO_SONAR_WAY; import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_DEFAULTS; import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_FROM_KEY; +import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_GROUP; import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_LANGUAGE; import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_NAME; import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_LOGIN; @@ -267,4 +268,23 @@ public class QualityProfilesServiceTest { .hasParam(PAGE_SIZE, 50) .andNoOtherParam(); } + + @Test + public void add_group() { + underTest.addGroup(AddGroupRequest.builder() + .setOrganization("O1") + .setQualityProfile("P1") + .setLanguage("Xoo") + .setGroup("users") + .build()); + PostRequest request = serviceTester.getPostRequest(); + + serviceTester.assertThat(request) + .hasPath("add_group") + .hasParam(PARAM_ORGANIZATION, "O1") + .hasParam(PARAM_QUALITY_PROFILE, "P1") + .hasParam(PARAM_LANGUAGE, "Xoo") + .hasParam(PARAM_GROUP, "users") + .andNoOtherParam(); + } }