From 06672268a78369a8442e1d8f291b2d80c6d95f9a Mon Sep 17 00:00:00 2001 From: belen-pruvost-sonarsource Date: Wed, 13 Oct 2021 18:25:51 +0200 Subject: [PATCH] SONAR-15441 - Create api/qualitygates/add_group endpoint --- .../src/main/java/org/sonar/db/DaoModule.java | 2 + .../src/main/java/org/sonar/db/DbClient.java | 7 + .../src/main/java/org/sonar/db/MyBatis.java | 2 + .../QualityGateGroupPermissionsDao.java | 59 ++++++ .../QualityGateGroupPermissionsDto.java | 73 ++++++++ .../QualityGateGroupPermissionsMapper.java | 32 ++++ .../QualityGateGroupPermissionsMapper.xml | 46 +++++ .../QualityGateGroupPermissionsDaoTest.java | 92 +++++++++ .../db/qualitygate/QualityGateDbTester.java | 10 + .../server/qualitygate/ws/AddGroupAction.java | 112 +++++++++++ .../qualitygate/ws/QualityGateWsModule.java | 4 +- .../ws/QualityGatesWsParameters.java | 1 + .../qualitygate/ws/QualityGatesWsSupport.java | 8 +- .../qualitygate/ws/AddGroupActionTest.java | 175 ++++++++++++++++++ .../ws/QualityGateWsModuleTest.java | 2 +- 15 files changed, 621 insertions(+), 4 deletions(-) create mode 100644 server/sonar-db-dao/src/main/java/org/sonar/db/qualitygate/QualityGateGroupPermissionsDao.java create mode 100644 server/sonar-db-dao/src/main/java/org/sonar/db/qualitygate/QualityGateGroupPermissionsDto.java create mode 100644 server/sonar-db-dao/src/main/java/org/sonar/db/qualitygate/QualityGateGroupPermissionsMapper.java create mode 100644 server/sonar-db-dao/src/main/resources/org/sonar/db/qualitygate/QualityGateGroupPermissionsMapper.xml create mode 100644 server/sonar-db-dao/src/test/java/org/sonar/db/qualitygate/QualityGateGroupPermissionsDaoTest.java create mode 100644 server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualitygate/ws/AddGroupAction.java create mode 100644 server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualitygate/ws/AddGroupActionTest.java 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 2212356d10d..ea48041aed2 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 @@ -67,6 +67,7 @@ import org.sonar.db.purge.PurgeDao; import org.sonar.db.qualitygate.ProjectQgateAssociationDao; import org.sonar.db.qualitygate.QualityGateConditionDao; import org.sonar.db.qualitygate.QualityGateDao; +import org.sonar.db.qualitygate.QualityGateGroupPermissionsDao; import org.sonar.db.qualitygate.QualityGateUserPermissionsDao; import org.sonar.db.qualityprofile.ActiveRuleDao; import org.sonar.db.qualityprofile.DefaultQProfileDao; @@ -147,6 +148,7 @@ public class DaoModule extends Module { QProfileEditUsersDao.class, QualityGateConditionDao.class, QualityGateDao.class, + QualityGateGroupPermissionsDao.class, QualityGateUserPermissionsDao.class, QualityProfileDao.class, QualityProfileExportDao.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 2eb29ee9eb0..99fa1daa6fa 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 @@ -65,6 +65,7 @@ import org.sonar.db.purge.PurgeDao; import org.sonar.db.qualitygate.ProjectQgateAssociationDao; import org.sonar.db.qualitygate.QualityGateConditionDao; import org.sonar.db.qualitygate.QualityGateDao; +import org.sonar.db.qualitygate.QualityGateGroupPermissionsDao; import org.sonar.db.qualitygate.QualityGateUserPermissionsDao; import org.sonar.db.qualityprofile.ActiveRuleDao; import org.sonar.db.qualityprofile.DefaultQProfileDao; @@ -135,6 +136,7 @@ public class DbClient { private final PurgeDao purgeDao; private final QualityGateDao qualityGateDao; private final QualityGateConditionDao gateConditionDao; + private final QualityGateGroupPermissionsDao qualityGateGroupPermissionsDao; private final QualityGateUserPermissionsDao qualityGateUserPermissionsDao; private final ProjectQgateAssociationDao projectQgateAssociationDao; private final DuplicationDao duplicationDao; @@ -213,6 +215,7 @@ public class DbClient { qualityGateDao = getDao(map, QualityGateDao.class); qualityGateUserPermissionsDao = getDao(map, QualityGateUserPermissionsDao.class); gateConditionDao = getDao(map, QualityGateConditionDao.class); + qualityGateGroupPermissionsDao = getDao(map, QualityGateGroupPermissionsDao.class); projectQgateAssociationDao = getDao(map, ProjectQgateAssociationDao.class); duplicationDao = getDao(map, DuplicationDao.class); notificationQueueDao = getDao(map, NotificationQueueDao.class); @@ -424,6 +427,10 @@ public class DbClient { return qualityGateUserPermissionsDao; } + public QualityGateGroupPermissionsDao qualityGateGroupPermissionsDao() { + return qualityGateGroupPermissionsDao; + } + public ProjectQgateAssociationDao projectQgateAssociationDao() { return projectQgateAssociationDao; } 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 03b4dd33ab3..dfb93d67616 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 @@ -115,6 +115,7 @@ import org.sonar.db.qualitygate.ProjectQgateAssociationMapper; import org.sonar.db.qualitygate.QualityGateConditionDto; import org.sonar.db.qualitygate.QualityGateConditionMapper; import org.sonar.db.qualitygate.QualityGateDto; +import org.sonar.db.qualitygate.QualityGateGroupPermissionsMapper; import org.sonar.db.qualitygate.QualityGateUserPermissionsMapper; import org.sonar.db.qualitygate.QualityGateMapper; import org.sonar.db.qualityprofile.ActiveRuleDto; @@ -279,6 +280,7 @@ public class MyBatis implements Startable { QProfileEditUsersMapper.class, QualityGateConditionMapper.class, QualityGateMapper.class, + QualityGateGroupPermissionsMapper.class, QualityGateUserPermissionsMapper.class, QualityProfileMapper.class, QualityProfileExportMapper.class, diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/qualitygate/QualityGateGroupPermissionsDao.java b/server/sonar-db-dao/src/main/java/org/sonar/db/qualitygate/QualityGateGroupPermissionsDao.java new file mode 100644 index 00000000000..eecb9287f33 --- /dev/null +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/qualitygate/QualityGateGroupPermissionsDao.java @@ -0,0 +1,59 @@ +/* + * SonarQube + * Copyright (C) 2009-2021 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.qualitygate; + +import java.util.Collection; +import org.sonar.api.utils.System2; +import org.sonar.db.Dao; +import org.sonar.db.DbSession; +import org.sonar.db.user.GroupDto; + +import static org.sonar.core.util.stream.MoreCollectors.toList; +import static org.sonar.db.DatabaseUtils.executeLargeInputs; + +public class QualityGateGroupPermissionsDao implements Dao { + private final System2 system2; + + public QualityGateGroupPermissionsDao(System2 system2) { + this.system2 = system2; + } + + public boolean exists(DbSession dbSession, QualityGateDto qualityGate, GroupDto group) { + return this.exists(dbSession, qualityGate.getUuid(), group.getUuid()); + } + + public boolean exists(DbSession dbSession, String qualityGateUuid, String groupUuid) { + return mapper(dbSession).selectByQualityGateAndGroup(qualityGateUuid, groupUuid) != null; + } + + public boolean exists(DbSession dbSession, QualityGateDto qualityGate, Collection groups) { + return !executeLargeInputs(groups.stream().map(GroupDto::getUuid).collect(toList()), + partition -> mapper(dbSession).selectByQualityGateAndGroups(qualityGate.getUuid(), partition)) + .isEmpty(); + } + + public void insert(DbSession dbSession, QualityGateGroupPermissionsDto dto) { + mapper(dbSession).insert(dto, system2.now()); + } + + private static QualityGateGroupPermissionsMapper mapper(DbSession dbSession) { + return dbSession.getMapper(QualityGateGroupPermissionsMapper.class); + } +} diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/qualitygate/QualityGateGroupPermissionsDto.java b/server/sonar-db-dao/src/main/java/org/sonar/db/qualitygate/QualityGateGroupPermissionsDto.java new file mode 100644 index 00000000000..0e5813c5f5a --- /dev/null +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/qualitygate/QualityGateGroupPermissionsDto.java @@ -0,0 +1,73 @@ +/* + * SonarQube + * Copyright (C) 2009-2021 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.qualitygate; + +import java.util.Date; + +/** + * @since 9.2 + */ +public class QualityGateGroupPermissionsDto { + + private String uuid; + private String groupUuid; + private String qualityGateUuid; + private Date createdAt; + + public QualityGateGroupPermissionsDto() { + } + + public String getUuid() { + return uuid; + } + + public QualityGateGroupPermissionsDto setUuid(String uuid) { + this.uuid = uuid; + return this; + } + + public String getGroupUuid() { + return groupUuid; + } + + public QualityGateGroupPermissionsDto setGroupUuid(String groupUuid) { + this.groupUuid = groupUuid; + return this; + } + + public String getQualityGateUuid() { + return qualityGateUuid; + } + + public QualityGateGroupPermissionsDto setQualityGateUuid(String qualityGateUuid) { + this.qualityGateUuid = qualityGateUuid; + return this; + } + + public Date getCreatedAt() { + return createdAt; + } + + public QualityGateGroupPermissionsDto setCreatedAt(Date createdAt) { + this.createdAt = createdAt; + return this; + } + +} diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/qualitygate/QualityGateGroupPermissionsMapper.java b/server/sonar-db-dao/src/main/java/org/sonar/db/qualitygate/QualityGateGroupPermissionsMapper.java new file mode 100644 index 00000000000..1c46b5b1385 --- /dev/null +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/qualitygate/QualityGateGroupPermissionsMapper.java @@ -0,0 +1,32 @@ +/* + * SonarQube + * Copyright (C) 2009-2021 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.qualitygate; + +import java.util.List; +import org.apache.ibatis.annotations.Param; + +public interface QualityGateGroupPermissionsMapper { + + QualityGateGroupPermissionsDto selectByQualityGateAndGroup(@Param("qualityGateUuid") String qualityGateUuid, @Param("groupUuid") String groupUuid); + + List selectByQualityGateAndGroups(@Param("qualityGateUuid") String qualityGateUuid, @Param("groupUuids") List groupUuids); + + void insert(@Param("dto") QualityGateGroupPermissionsDto dto, @Param("now") long now); +} diff --git a/server/sonar-db-dao/src/main/resources/org/sonar/db/qualitygate/QualityGateGroupPermissionsMapper.xml b/server/sonar-db-dao/src/main/resources/org/sonar/db/qualitygate/QualityGateGroupPermissionsMapper.xml new file mode 100644 index 00000000000..efec92a4183 --- /dev/null +++ b/server/sonar-db-dao/src/main/resources/org/sonar/db/qualitygate/QualityGateGroupPermissionsMapper.xml @@ -0,0 +1,46 @@ + + + + + + + qggp.uuid as "uuid", + qggp.group_uuid as "groupUuid", + qggp.quality_gate_uuid as "qualityGateUuid" + + + + + + + + insert into qgate_group_permissions( + uuid, + group_uuid, + quality_gate_uuid, + created_at + ) values ( + #{dto.uuid, jdbcType=VARCHAR}, + #{dto.groupUuid, jdbcType=VARCHAR}, + #{dto.qualityGateUuid, jdbcType=VARCHAR}, + #{now, jdbcType=BIGINT} + ) + + + diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/qualitygate/QualityGateGroupPermissionsDaoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/qualitygate/QualityGateGroupPermissionsDaoTest.java new file mode 100644 index 00000000000..b94e8ee3e15 --- /dev/null +++ b/server/sonar-db-dao/src/test/java/org/sonar/db/qualitygate/QualityGateGroupPermissionsDaoTest.java @@ -0,0 +1,92 @@ +/* + * SonarQube + * Copyright (C) 2009-2021 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.qualitygate; + +import java.util.Date; +import java.util.List; +import org.junit.Rule; +import org.junit.Test; +import org.sonar.api.utils.System2; +import org.sonar.core.util.Uuids; +import org.sonar.db.DbSession; +import org.sonar.db.DbTester; +import org.sonar.db.user.GroupDto; +import org.sonar.db.user.GroupTesting; + +import static org.apache.commons.lang.RandomStringUtils.randomAlphabetic; +import static org.assertj.core.api.Assertions.assertThat; + +public class QualityGateGroupPermissionsDaoTest { + + @Rule + public DbTester dbTester = DbTester.create(System2.INSTANCE); + + private DbSession dbSession = dbTester.getSession(); + private QualityGateGroupPermissionsDao underTest = dbTester.getDbClient().qualityGateGroupPermissionsDao(); + + @Test + public void itInsertsAndExistsReturnsTrue() { + GroupDto group = GroupTesting.newGroupDto(); + QualityGateDto qualityGateDto = insertQualityGate(); + QualityGateGroupPermissionsDto qualityGateGroupPermission = insertQualityGateGroupPermission(qualityGateDto.getUuid(), group.getUuid()); + + assertThat(qualityGateGroupPermission.getUuid()).isNotNull(); + assertThat(underTest.exists(dbSession, qualityGateDto, group)).isTrue(); + assertThat(underTest.exists(dbSession, qualityGateDto.getUuid(), group.getUuid())).isTrue(); + } + + @Test + public void existsReturnsTrueForListOfGroups() { + GroupDto group1 = GroupTesting.newGroupDto(); + GroupDto group2 = GroupTesting.newGroupDto(); + QualityGateDto qualityGateDto = insertQualityGate(); + QualityGateGroupPermissionsDto qualityGateGroupPermission = insertQualityGateGroupPermission(qualityGateDto.getUuid(), group1.getUuid()); + + assertThat(qualityGateGroupPermission.getUuid()).isNotNull(); + assertThat(underTest.exists(dbSession, qualityGateDto, List.of(group1, group2))).isTrue(); + assertThat(underTest.exists(dbSession, qualityGateDto.getUuid(), group1.getUuid())).isTrue(); + assertThat(underTest.exists(dbSession, qualityGateDto.getUuid(), group2.getUuid())).isFalse(); + } + + @Test + public void existsReturnsFalseWhenQGEditGroupsDoesNotExist() { + assertThat(underTest.exists(dbSession, randomAlphabetic(5), randomAlphabetic(5))).isFalse(); + } + + private QualityGateDto insertQualityGate() { + QualityGateDto qg = new QualityGateDto() + .setUuid(randomAlphabetic(5)) + .setName(randomAlphabetic(5)); + dbTester.getDbClient().qualityGateDao().insert(dbTester.getSession(), qg); + dbTester.commit(); + return qg; + } + + private QualityGateGroupPermissionsDto insertQualityGateGroupPermission(String qualityGateUuid, String groupUuid) { + QualityGateGroupPermissionsDto qgg = new QualityGateGroupPermissionsDto() + .setUuid(Uuids.create()) + .setQualityGateUuid(qualityGateUuid) + .setGroupUuid(groupUuid) + .setCreatedAt(new Date()); + underTest.insert(dbTester.getSession(), qgg); + dbTester.commit(); + return qgg; + } +} diff --git a/server/sonar-db-dao/src/testFixtures/java/org/sonar/db/qualitygate/QualityGateDbTester.java b/server/sonar-db-dao/src/testFixtures/java/org/sonar/db/qualitygate/QualityGateDbTester.java index 6dbbe27587e..1243594bf5f 100644 --- a/server/sonar-db-dao/src/testFixtures/java/org/sonar/db/qualitygate/QualityGateDbTester.java +++ b/server/sonar-db-dao/src/testFixtures/java/org/sonar/db/qualitygate/QualityGateDbTester.java @@ -30,6 +30,7 @@ import org.sonar.db.DbTester; import org.sonar.db.metric.MetricDto; import org.sonar.db.project.ProjectDto; import org.sonar.db.property.PropertyDto; +import org.sonar.db.user.GroupDto; import org.sonar.db.user.UserDto; import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric; @@ -104,6 +105,15 @@ public class QualityGateDbTester { return dbClient.projectQgateAssociationDao().selectQGateUuidByProjectUuid(dbSession, componentUuid); } + public void addGroupPermission(QualityGateDto qualityGateDto, GroupDto group) { + dbClient.qualityGateGroupPermissionsDao().insert(dbSession, new QualityGateGroupPermissionsDto() + .setUuid(Uuids.createFast()) + .setGroupUuid(group.getUuid()) + .setQualityGateUuid(qualityGateDto.getUuid()) + ); + dbSession.commit(); + } + public void addUserPermission(QualityGateDto qualityGateDto, UserDto user) { dbClient.qualityGateUserPermissionDao().insert(dbSession, new QualityGateUserPermissionsDto() .setUuid(Uuids.createFast()) diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualitygate/ws/AddGroupAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualitygate/ws/AddGroupAction.java new file mode 100644 index 00000000000..c3cfa0c7c6b --- /dev/null +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualitygate/ws/AddGroupAction.java @@ -0,0 +1,112 @@ +/* + * SonarQube + * Copyright (C) 2009-2021 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.qualitygate.ws; + +import java.util.Optional; +import org.jetbrains.annotations.NotNull; +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.qualitygate.QualityGateDto; +import org.sonar.db.qualitygate.QualityGateGroupPermissionsDto; +import org.sonar.db.user.GroupDto; + +import static org.sonar.server.exceptions.NotFoundException.checkFoundWithOptional; +import static org.sonar.server.qualitygate.ws.CreateAction.NAME_MAXIMUM_LENGTH; +import static org.sonar.server.qualitygate.ws.QualityGatesWsParameters.ACTION_ADD_GROUP; +import static org.sonar.server.qualitygate.ws.QualityGatesWsParameters.PARAM_GATE_NAME; +import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_GROUP_NAME; + +public class AddGroupAction implements QualityGatesWsAction { + private final DbClient dbClient; + private final UuidFactory uuidFactory; + private final QualityGatesWsSupport wsSupport; + + public AddGroupAction(DbClient dbClient, UuidFactory uuidFactory, QualityGatesWsSupport wsSupport) { + this.dbClient = dbClient; + this.uuidFactory = uuidFactory; + this.wsSupport = wsSupport; + } + + @Override + public void define(WebService.NewController context) { + WebService.NewAction action = context + .createAction(ACTION_ADD_GROUP) + .setDescription("Allow a group of users to edit a Quality Gate.
" + + "Requires one of the following permissions:" + + "
    " + + "
  • 'Administer Quality Gates'
  • " + + "
  • Edit right on the specified quality gate
  • " + + "
") + .setHandler(this) + .setPost(true) + .setInternal(true) + .setSince("9.2"); + + action.createParam(PARAM_GATE_NAME) + .setDescription("Quality Gate name") + .setRequired(true) + .setMaximumLength(NAME_MAXIMUM_LENGTH) + .setExampleValue("SonarSource Way"); + + action.createParam(PARAM_GROUP_NAME) + .setDescription("Group name or 'anyone' (case insensitive)") + .setRequired(true) + .setExampleValue("sonar-administrators"); + + } + + @Override + public void handle(Request request, Response response) throws Exception { + final String groupName = request.mandatoryParam(PARAM_GROUP_NAME); + final String qualityGateName = request.mandatoryParam(PARAM_GATE_NAME); + + try (DbSession dbSession = dbClient.openSession(false)) { + QualityGateDto qualityGateDto = wsSupport.getByName(dbSession, qualityGateName); + wsSupport.checkCanLimitedEdit(dbSession, qualityGateDto); + GroupDto group = getGroup(dbSession, groupName); + addGroup(dbSession, qualityGateDto, group); + } + response.noContent(); + } + + @NotNull + private GroupDto getGroup(DbSession dbSession, String groupName) { + Optional group = dbClient.groupDao().selectByName(dbSession, groupName); + checkFoundWithOptional(group, "Group with name '%s' is not found", groupName); + return group.get(); + } + + private void addGroup(DbSession dbSession, QualityGateDto qualityGate, GroupDto group) { + if (dbClient.qualityGateGroupPermissionsDao().exists(dbSession, qualityGate, group)) { + return; + } + dbClient.qualityGateGroupPermissionsDao().insert(dbSession, + new QualityGateGroupPermissionsDto() + .setUuid(uuidFactory.create()) + .setGroupUuid(group.getUuid()) + .setQualityGateUuid(qualityGate.getUuid())); + dbSession.commit(); + } + +} diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualitygate/ws/QualityGateWsModule.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualitygate/ws/QualityGateWsModule.java index b453501cf57..b018c949ffb 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualitygate/ws/QualityGateWsModule.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualitygate/ws/QualityGateWsModule.java @@ -42,8 +42,8 @@ public class QualityGateWsModule extends Module { DeleteConditionAction.class, UpdateConditionAction.class, ProjectStatusAction.class, - GetByProjectAction.class - + GetByProjectAction.class, + AddGroupAction.class ); } } diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualitygate/ws/QualityGatesWsParameters.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualitygate/ws/QualityGatesWsParameters.java index 960ec78b0fd..cc2e826581e 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualitygate/ws/QualityGatesWsParameters.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualitygate/ws/QualityGatesWsParameters.java @@ -29,6 +29,7 @@ public class QualityGatesWsParameters { public static final String ACTION_CREATE = "create"; public static final String ACTION_CREATE_CONDITION = "create_condition"; public static final String ACTION_UPDATE_CONDITION = "update_condition"; + public static final String ACTION_ADD_GROUP = "add_group"; public static final String ACTION_ADD_USER = "add_user"; public static final String PARAM_ANALYSIS_ID = "analysisId"; diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualitygate/ws/QualityGatesWsSupport.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualitygate/ws/QualityGatesWsSupport.java index c3daf8d406d..a958f63c638 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualitygate/ws/QualityGatesWsSupport.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualitygate/ws/QualityGatesWsSupport.java @@ -88,11 +88,17 @@ public class QualityGatesWsSupport { void checkCanLimitedEdit(DbSession dbSession, QualityGateDto qualityGate) { checkNotBuiltIn(qualityGate); - if (!userSession.hasPermission(ADMINISTER_QUALITY_GATES) && !userHasPermission(dbSession, qualityGate)) { + if (!userSession.hasPermission(ADMINISTER_QUALITY_GATES) + && !userHasPermission(dbSession, qualityGate) + && !userHasGroupPermission(dbSession, qualityGate)) { throw insufficientPrivilegesException(); } } + boolean userHasGroupPermission(DbSession dbSession, QualityGateDto qualityGate) { + return userSession.isLoggedIn() && dbClient.qualityGateGroupPermissionsDao().exists(dbSession, qualityGate, userSession.getGroups()); + } + boolean userHasPermission(DbSession dbSession, QualityGateDto qualityGate) { return userSession.isLoggedIn() && dbClient.qualityGateUserPermissionDao().exists(dbSession, qualityGate.getUuid(), userSession.getUuid()); } diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualitygate/ws/AddGroupActionTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualitygate/ws/AddGroupActionTest.java new file mode 100644 index 00000000000..4b871cc4f2d --- /dev/null +++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualitygate/ws/AddGroupActionTest.java @@ -0,0 +1,175 @@ +/* + * SonarQube + * Copyright (C) 2009-2021 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.qualitygate.ws; + +import com.tngtech.java.junit.dataprovider.DataProviderRunner; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.sonar.api.server.ws.WebService; +import org.sonar.api.utils.System2; +import org.sonar.core.util.UuidFactory; +import org.sonar.core.util.UuidFactoryFast; +import org.sonar.db.DbClient; +import org.sonar.db.DbTester; +import org.sonar.db.permission.GlobalPermission; +import org.sonar.db.qualitygate.QualityGateDto; +import org.sonar.db.user.GroupDto; +import org.sonar.db.user.UserDto; +import org.sonar.server.component.TestComponentFinder; +import org.sonar.server.exceptions.ForbiddenException; +import org.sonar.server.exceptions.NotFoundException; +import org.sonar.server.tester.UserSessionRule; +import org.sonar.server.ws.TestRequest; +import org.sonar.server.ws.TestResponse; +import org.sonar.server.ws.WsActionTester; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.sonar.server.qualitygate.ws.QualityGatesWsParameters.PARAM_GATE_NAME; +import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_GROUP_NAME; + +@RunWith(DataProviderRunner.class) +public class AddGroupActionTest { + @Rule + public UserSessionRule userSession = UserSessionRule.standalone(); + @Rule + public DbTester db = DbTester.create(System2.INSTANCE); + + private final DbClient dbClient = db.getDbClient(); + private final QualityGatesWsSupport wsSupport = new QualityGatesWsSupport(dbClient, userSession, TestComponentFinder.from(db)); + private final UuidFactory uuidFactory = UuidFactoryFast.getInstance(); + private final WsActionTester ws = new WsActionTester(new AddGroupAction(dbClient, uuidFactory, wsSupport)); + + @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("groupName", "gateName"); + } + + @Test + public void add_group() { + QualityGateDto qualityGateDto = db.qualityGates().insertQualityGate(); + GroupDto group = db.users().insertDefaultGroup(); + userSession.logIn().addPermission(GlobalPermission.ADMINISTER_QUALITY_GATES); + + TestResponse response = ws.newRequest() + .setParam(PARAM_GATE_NAME, qualityGateDto.getName()) + .setParam(PARAM_GROUP_NAME, group.getName()) + .execute(); + + assertThat(response.getStatus()).isEqualTo(204); + assertThat(dbClient.qualityGateGroupPermissionsDao().exists(db.getSession(), qualityGateDto, group)).isTrue(); + } + + @Test + public void does_nothing_when_group_can_already_edit_qualityGateDto() { + QualityGateDto qualityGateDto = db.qualityGates().insertQualityGate(); + GroupDto group = db.users().insertDefaultGroup(); + + db.qualityGates().addGroupPermission(qualityGateDto, group); + assertThat(dbClient.qualityGateGroupPermissionsDao().exists(db.getSession(), qualityGateDto, group)).isTrue(); + userSession.logIn().addPermission(GlobalPermission.ADMINISTER_QUALITY_GATES); + + ws.newRequest() + .setParam(PARAM_GATE_NAME, qualityGateDto.getName()) + .setParam(PARAM_GROUP_NAME, group.getName()) + .execute(); + + assertThat(dbClient.qualityGateGroupPermissionsDao().exists(db.getSession(), qualityGateDto, group)).isTrue(); + } + + @Test + public void quality_gate_administers_can_add_group() { + QualityGateDto qualityGateDto = db.qualityGates().insertQualityGate(); + GroupDto group = db.users().insertDefaultGroup(); + userSession.logIn().addPermission(GlobalPermission.ADMINISTER_QUALITY_GATES); + + ws.newRequest() + .setParam(PARAM_GATE_NAME, qualityGateDto.getName()) + .setParam(PARAM_GROUP_NAME, group.getName()) + .execute(); + + assertThat(dbClient.qualityGateGroupPermissionsDao().exists(db.getSession(), qualityGateDto, group)).isTrue(); + } + + @Test + public void quality_gate_editors_can_add_group() { + QualityGateDto qualityGateDto = db.qualityGates().insertQualityGate(); + + GroupDto originalGroup = db.users().insertDefaultGroup(); + UserDto userAllowedToEditQualityGate = db.users().insertUser(); + db.users().insertMember(originalGroup, userAllowedToEditQualityGate); + + db.qualityGates().addGroupPermission(qualityGateDto, originalGroup); + userSession.logIn(userAllowedToEditQualityGate).setGroups(originalGroup); + GroupDto newGroup = db.users().insertGroup(); + + ws.newRequest() + .setParam(PARAM_GATE_NAME, qualityGateDto.getName()) + .setParam(PARAM_GROUP_NAME, newGroup.getName()) + .execute(); + + assertThat(dbClient.qualityGateGroupPermissionsDao().exists(db.getSession(), qualityGateDto, originalGroup)).isTrue(); + } + + @Test + public void fail_when_group_does_not_exist() { + QualityGateDto qualityGateDto = db.qualityGates().insertQualityGate(); + userSession.logIn().addPermission(GlobalPermission.ADMINISTER_QUALITY_GATES); + + TestRequest request = ws.newRequest() + .setParam(PARAM_GATE_NAME, qualityGateDto.getName()) + .setParam(PARAM_GROUP_NAME, "unknown"); + + assertThatThrownBy(() -> request.execute()) + .isInstanceOf(NotFoundException.class) + .hasMessage("Group with name 'unknown' is not found"); + } + + @Test + public void fail_when_qualityGateDto_does_not_exist() { + GroupDto group = db.users().insertDefaultGroup(); + userSession.logIn().addPermission(GlobalPermission.ADMINISTER_QUALITY_GATES); + + TestRequest request = ws.newRequest() + .setParam(PARAM_GATE_NAME, "unknown") + .setParam(PARAM_GROUP_NAME, group.getName()); + + assertThatThrownBy(() -> request.execute()) + .isInstanceOf(NotFoundException.class) + .hasMessage("No quality gate has been found for name unknown"); + } + + @Test + public void fail_when_not_enough_permission() { + QualityGateDto qualityGateDto = db.qualityGates().insertQualityGate(); + GroupDto group = db.users().insertDefaultGroup(); + + TestRequest request = ws.newRequest() + .setParam(PARAM_GATE_NAME, qualityGateDto.getName()) + .setParam(PARAM_GROUP_NAME, group.getName()); + + assertThatThrownBy(() -> request.execute()).isInstanceOf(ForbiddenException.class); + } +} diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualitygate/ws/QualityGateWsModuleTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualitygate/ws/QualityGateWsModuleTest.java index dc59e33e600..d7b30969647 100644 --- a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualitygate/ws/QualityGateWsModuleTest.java +++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualitygate/ws/QualityGateWsModuleTest.java @@ -30,7 +30,7 @@ public class QualityGateWsModuleTest { public void verify_count_of_added_components() { ComponentContainer container = new ComponentContainer(); new QualityGateWsModule().configure(container); - assertThat(container.size()).isEqualTo(COMPONENTS_IN_EMPTY_COMPONENT_CONTAINER + 18); + assertThat(container.size()).isEqualTo(COMPONENTS_IN_EMPTY_COMPONENT_CONTAINER + 19); } } -- 2.39.5