public int countByQuery(DbSession dbSession, SearchPermissionQuery query) {
return mapper(dbSession).countByQuery(query);
}
+
+ public void deleteByQualityGateAndGroup(DbSession dbSession, QualityGateDto qualityGate, GroupDto group) {
+ mapper(dbSession).delete(qualityGate.getUuid(), group.getUuid());
+ }
}
List<SearchGroupMembershipDto> selectByQuery(@Param("query") SearchPermissionQuery query, @Param("pagination") Pagination pagination);
int countByQuery(@Param("query") SearchPermissionQuery query);
+
+ void delete(@Param("qualityGateUuid") String qualityGateUuid, @Param("groupUuid") String groupUuid);
+
}
)
</insert>
+ <delete id="delete">
+ delete from qgate_group_permissions
+ where quality_gate_uuid = #{qualityGateUuid, jdbcType=VARCHAR}
+ and group_uuid = #{groupUuid, jdbcType=VARCHAR}
+ </delete>
+
</mapper>
.containsExactly(group1.getUuid(), group2.getUuid(), group3.getUuid());
}
+ @Test
+ public void deleteByQProfileAndGroup() {
+ QualityGateDto qualityGateDto = insertQualityGate();
+ GroupDto group = dbTester.users().insertGroup();
+ insertQualityGateGroupPermission(qualityGateDto.getUuid(), group.getUuid());
+
+ assertThat(underTest.exists(dbSession, qualityGateDto, group)).isTrue();
+
+ underTest.deleteByQualityGateAndGroup(dbSession, qualityGateDto, group);
+
+ assertThat(underTest.exists(dbSession, qualityGateDto, group)).isFalse();
+ }
+
private QualityGateDto insertQualityGate() {
QualityGateDto qg = new QualityGateDto()
.setUuid(randomAlphabetic(5))
--- /dev/null
+/*
+ * 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.sonar.api.server.ws.Request;
+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.qualitygate.QualityGateDto;
+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.PARAM_GATE_NAME;
+import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_GROUP_NAME;
+
+public abstract class AbstractGroupAction implements QualityGatesWsAction {
+ protected final DbClient dbClient;
+ protected final QualityGatesWsSupport wsSupport;
+
+ protected AbstractGroupAction(DbClient dbClient, QualityGatesWsSupport wsSupport) {
+ this.dbClient = dbClient;
+ this.wsSupport = wsSupport;
+ }
+
+ protected void defineGateAndGroupParameters(WebService.NewAction action) {
+ 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);
+ apply(dbSession, qualityGateDto, group);
+ }
+ response.noContent();
+ }
+
+ protected abstract void apply(DbSession dbSession, QualityGateDto qualityGate, GroupDto group);
+
+ private GroupDto getGroup(DbSession dbSession, String groupName) {
+ Optional<GroupDto> group = dbClient.groupDao().selectByName(dbSession, groupName);
+ checkFoundWithOptional(group, "Group with name '%s' is not found", groupName);
+ return group.get();
+ }
+}
*/
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.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;
+public class AddGroupAction extends AbstractGroupAction {
private final UuidFactory uuidFactory;
- private final QualityGatesWsSupport wsSupport;
public AddGroupAction(DbClient dbClient, UuidFactory uuidFactory, QualityGatesWsSupport wsSupport) {
- this.dbClient = dbClient;
+ super(dbClient, wsSupport);
this.uuidFactory = uuidFactory;
- this.wsSupport = wsSupport;
}
@Override
.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");
-
+ super.defineGateAndGroupParameters(action);
}
@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<GroupDto> 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) {
+ protected void apply(DbSession dbSession, QualityGateDto qualityGate, GroupDto group) {
if (dbClient.qualityGateGroupPermissionsDao().exists(dbSession, qualityGate, group)) {
return;
}
@Override
protected void configureModule() {
add(
+ AddGroupAction.class,
AddUserAction.class,
- QualityGatesWsSupport.class,
- QualityGatesWs.class,
- ListAction.class,
- SearchAction.class,
- ShowAction.class,
- CreateAction.class,
- RenameAction.class,
CopyAction.class,
- DestroyAction.class,
- SetAsDefaultAction.class,
- SelectAction.class,
- DeselectAction.class,
+ CreateAction.class,
CreateConditionAction.class,
DeleteConditionAction.class,
- UpdateConditionAction.class,
- ProjectStatusAction.class,
+ DeselectAction.class,
+ DestroyAction.class,
GetByProjectAction.class,
- AddGroupAction.class,
+ ListAction.class,
+ ProjectStatusAction.class,
+ QualityGatesWs.class,
+ QualityGatesWsSupport.class,
+ RemoveGroupAction.class,
+ RenameAction.class,
+ SearchAction.class,
SearchGroupsAction.class,
- SearchUsersAction.class
+ SearchUsersAction.class,
+ SelectAction.class,
+ SetAsDefaultAction.class,
+ ShowAction.class,
+ UpdateConditionAction.class
);
}
}
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 ACTION_REMOVE_GROUP = "remove_group";
public static final String ACTION_SEARCH_GROUPS = "search_groups";
public static final String ACTION_SEARCH_USERS = "search_users";
private QualityGatesWsParameters() {
// prevent instantiation
}
-
}
--- /dev/null
+/*
+ * 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 org.sonar.api.server.ws.WebService;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.qualitygate.QualityGateDto;
+import org.sonar.db.user.GroupDto;
+
+import static org.sonar.server.qualitygate.ws.QualityGatesWsParameters.ACTION_REMOVE_GROUP;
+
+public class RemoveGroupAction extends AbstractGroupAction {
+
+ public RemoveGroupAction(DbClient dbClient, QualityGatesWsSupport wsSupport) {
+ super(dbClient, wsSupport);
+ }
+
+ @Override
+ public void define(WebService.NewController context) {
+ WebService.NewAction action = context
+ .createAction(ACTION_REMOVE_GROUP)
+ .setDescription("Remove the ability from a group to edit a Quality Gate.<br>" +
+ "Requires one of the following permissions:" +
+ "<ul>" +
+ " <li>'Administer Quality Gates'</li>" +
+ " <li>Edit right on the specified quality gate</li>" +
+ "</ul>")
+ .setHandler(this)
+ .setPost(true)
+ .setInternal(true)
+ .setSince("9.2");
+
+ super.defineGateAndGroupParameters(action);
+ }
+
+ @Override
+ protected void apply(DbSession dbSession, QualityGateDto qualityGate, GroupDto group) {
+ if (!dbClient.qualityGateGroupPermissionsDao().exists(dbSession, qualityGate, group)) {
+ return;
+ }
+ dbClient.qualityGateGroupPermissionsDao().deleteByQualityGateAndGroup(dbSession, qualityGate, group);
+ dbSession.commit();
+ }
+}
public void verify_count_of_added_components() {
ComponentContainer container = new ComponentContainer();
new QualityGateWsModule().configure(container);
- assertThat(container.size()).isEqualTo(COMPONENTS_IN_EMPTY_COMPONENT_CONTAINER + 21);
+ assertThat(container.size()).isEqualTo(COMPONENTS_IN_EMPTY_COMPONENT_CONTAINER + 22);
}
}
--- /dev/null
+/*
+ * 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 org.junit.Rule;
+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.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.TestResponse;
+import org.sonar.server.ws.WsActionTester;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.sonar.server.qualitygate.ws.QualityGatesWsParameters.PARAM_GATE_NAME;
+import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_GROUP_NAME;
+
+public class RemoveGroupActionTest {
+
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+ @Rule
+ public UserSessionRule userSession = UserSessionRule.standalone();
+ @Rule
+ public DbTester db = DbTester.create();
+
+ private final DbClient dbClient = db.getDbClient();
+ private final QualityGatesWsSupport wsSupport = new QualityGatesWsSupport(dbClient, userSession, TestComponentFinder.from(db));
+ private final WsActionTester ws = new WsActionTester(new RemoveGroupAction(dbClient, wsSupport));
+
+ @Test
+ public void test_definition() {
+ WebService.Action def = ws.getDef();
+ assertThat(def.key()).isEqualTo("remove_group");
+ assertThat(def.isPost()).isTrue();
+ assertThat(def.isInternal()).isTrue();
+ assertThat(def.params()).extracting(WebService.Param::key).containsExactlyInAnyOrder("groupName", "gateName");
+ }
+
+ @Test
+ public void remove_group() {
+ QualityGateDto qualityGate = db.qualityGates().insertQualityGate();
+ GroupDto group = db.users().insertGroup();
+ db.qualityGates().addGroupPermission(qualityGate, group);
+ userSession.logIn().addPermission(GlobalPermission.ADMINISTER_QUALITY_GATES);
+
+ TestResponse response = ws.newRequest()
+ .setParam(PARAM_GATE_NAME, qualityGate.getName())
+ .setParam(PARAM_GROUP_NAME, group.getName())
+ .execute();
+
+ assertThat(response.getStatus()).isEqualTo(204);
+ assertThat(dbClient.qualityGateGroupPermissionsDao().exists(db.getSession(), qualityGate, group)).isFalse();
+ }
+
+ @Test
+ public void does_nothing_when_group_cannot_edit_gate() {
+ QualityGateDto qualityGate = db.qualityGates().insertQualityGate();
+ GroupDto group = db.users().insertGroup();
+
+ assertThat(dbClient.qualityGateGroupPermissionsDao().exists(db.getSession(), qualityGate, group)).isFalse();
+
+ userSession.logIn().addPermission(GlobalPermission.ADMINISTER_QUALITY_GATES);
+
+ ws.newRequest()
+ .setParam(PARAM_GATE_NAME, qualityGate.getName())
+ .setParam(PARAM_GROUP_NAME, group.getName())
+ .execute();
+
+ assertThat(dbClient.qualityGateGroupPermissionsDao().exists(db.getSession(), qualityGate, group)).isFalse();
+ }
+
+ @Test
+ public void qg_administrators_can_remove_group() {
+ QualityGateDto qualityGate = db.qualityGates().insertQualityGate();
+ GroupDto group = db.users().insertGroup();
+ db.qualityGates().addGroupPermission(qualityGate, group);
+ userSession.logIn().addPermission(GlobalPermission.ADMINISTER_QUALITY_GATES);
+
+ ws.newRequest()
+ .setParam(PARAM_GATE_NAME, qualityGate.getName())
+ .setParam(PARAM_GROUP_NAME, group.getName())
+ .execute();
+
+ assertThat(dbClient.qualityGateGroupPermissionsDao().exists(db.getSession(), qualityGate, group)).isFalse();
+ }
+
+ @Test
+ public void qg_editors_can_remove_group() {
+ QualityGateDto qualityGate = db.qualityGates().insertQualityGate();
+ GroupDto group = db.users().insertGroup();
+ db.qualityGates().addGroupPermission(qualityGate, group);
+ UserDto userAllowedToEditGate = db.users().insertUser();
+ db.qualityGates().addUserPermission(qualityGate, userAllowedToEditGate);
+ userSession.logIn(userAllowedToEditGate);
+
+ ws.newRequest()
+ .setParam(PARAM_GATE_NAME, qualityGate.getName())
+ .setParam(PARAM_GROUP_NAME, group.getName())
+ .execute();
+
+ assertThat(dbClient.qualityGateGroupPermissionsDao().exists(db.getSession(), qualityGate, group)).isFalse();
+ }
+
+ @Test
+ public void fail_when_group_does_not_exist() {
+ QualityGateDto qualityGate = db.qualityGates().insertQualityGate();
+ userSession.logIn().addPermission(GlobalPermission.ADMINISTER_QUALITY_GATES);
+
+ expectedException.expect(NotFoundException.class);
+ expectedException.expectMessage("Group with name 'unknown' is not found");
+
+ ws.newRequest()
+ .setParam(PARAM_GATE_NAME, qualityGate.getName())
+ .setParam(PARAM_GROUP_NAME, "unknown")
+ .execute();
+ }
+
+ @Test
+ public void fail_when_qgate_does_not_exist() {
+ GroupDto group = db.users().insertGroup();
+ userSession.logIn().addPermission(GlobalPermission.ADMINISTER_QUALITY_GATES);
+
+ expectedException.expect(NotFoundException.class);
+ expectedException.expectMessage(String.format("No quality gate has been found for name unknown"));
+
+ ws.newRequest()
+ .setParam(PARAM_GATE_NAME, "unknown")
+ .setParam(PARAM_GROUP_NAME, group.getName())
+ .execute();
+ }
+
+ @Test
+ public void fail_when_qg_is_built_in() {
+ GroupDto group = db.users().insertGroup();
+ QualityGateDto qualityGate = db.qualityGates().insertQualityGate(qg -> qg.setBuiltIn(true));
+ userSession.logIn().addPermission(GlobalPermission.ADMINISTER_QUALITY_GATES);
+
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage(String.format("Operation forbidden for built-in Quality Gate '%s'", qualityGate.getName()));
+
+ ws.newRequest()
+ .setParam(PARAM_GATE_NAME, qualityGate.getName())
+ .setParam(PARAM_GROUP_NAME, group.getName())
+ .execute();
+ }
+
+ @Test
+ public void fail_when_not_enough_permission() {
+ QualityGateDto qualityGate = db.qualityGates().insertQualityGate();
+ GroupDto group = db.users().insertGroup();
+ userSession.logIn(db.users().insertUser()).addPermission(GlobalPermission.ADMINISTER_QUALITY_PROFILES);
+
+ expectedException.expect(ForbiddenException.class);
+
+ ws.newRequest()
+ .setParam(PARAM_GATE_NAME, qualityGate.getName())
+ .setParam(PARAM_GROUP_NAME, group.getName())
+ .execute();
+ }
+}