return mapper(dbSession).countByQuery(query);
}
+ public void deleteByQualityGateAndUser(DbSession dbSession, QualityGateDto qualityGate, UserDto user) {
+ mapper(dbSession).delete(qualityGate.getUuid(), user.getUuid());
+ }
+
public void deleteByUser(DbSession dbSession, UserDto user) {
mapper(dbSession).deleteByUser(user.getUuid());
}
int countByQuery(@Param("query") SearchPermissionQuery query);
+ void delete(@Param("qualityGateUuid") String qualityGateUuid, @Param("userUuid") String userUuid);
+
void deleteByUser(@Param("userUuid") String userUuid);
void deleteByQualityGate(@Param("qualityGateUuid") String qualityGateUuid);
</where>
</sql>
+ <delete id="delete">
+ delete from qgate_user_permissions
+ where quality_gate_uuid = #{qualityGateUuid}
+ and user_uuid = #{userUuid}
+ </delete>
+
<delete id="deleteByUser">
delete from qgate_user_permissions
where user_uuid = #{userUuid}
.containsExactly(user1.getUuid(), user2.getUuid(), user3.getUuid());
}
+ @Test
+ public void deleteByQualityGateAndUser() {
+ QualityGateDto qualityGate = db.qualityGates().insertQualityGate();
+ UserDto user = db.users().insertUser();
+ db.qualityGates().addUserPermission(qualityGate, user);
+
+ assertThat(underTest.exists(dbSession, qualityGate, user)).isTrue();
+
+ underTest.deleteByQualityGateAndUser(dbSession, qualityGate, user);
+
+ assertThat(underTest.exists(dbSession, qualityGate, user)).isFalse();
+ }
+
@Test
public void deleteByUser() {
QualityGateDto qualityGateDto1 = db.qualityGates().insertQualityGate();
--- /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.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.UserDto;
+
+import static org.sonar.server.exceptions.NotFoundException.checkFound;
+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.user.UsersWsParameters.PARAM_LOGIN;
+
+public abstract class AbstractUserAction implements QualityGatesWsAction {
+ protected final DbClient dbClient;
+ protected final QualityGatesWsSupport wsSupport;
+
+ protected AbstractUserAction(DbClient dbClient, QualityGatesWsSupport wsSupport) {
+ this.dbClient = dbClient;
+ this.wsSupport = wsSupport;
+ }
+
+ protected void defineGateAndUserParameters(WebService.NewAction action) {
+ action.createParam(PARAM_GATE_NAME)
+ .setDescription("Quality Gate name")
+ .setRequired(true)
+ .setMaximumLength(NAME_MAXIMUM_LENGTH)
+ .setExampleValue("SonarSource Way");
+
+ action.createParam(PARAM_LOGIN)
+ .setDescription("User login")
+ .setRequired(true)
+ .setExampleValue("john.doe");
+ }
+
+ @Override
+ public void handle(Request request, Response response) throws Exception {
+ final String login = request.mandatoryParam(PARAM_LOGIN);
+ final String qualityGateName = request.mandatoryParam(PARAM_GATE_NAME);
+
+ try (DbSession dbSession = dbClient.openSession(false)) {
+ QualityGateDto qualityGateDto = wsSupport.getByName(dbSession, qualityGateName);
+ wsSupport.checkCanLimitedEdit(dbSession, qualityGateDto);
+ UserDto user = getUser(dbSession, login);
+ apply(dbSession, qualityGateDto, user);
+ }
+ response.noContent();
+ }
+
+ private UserDto getUser(DbSession dbSession, String login) {
+ UserDto user = dbClient.userDao().selectByLogin(dbSession, login);
+ checkFound(user, "User with login '%s' is not found", login);
+ return user;
+ }
+
+ protected abstract void apply(DbSession dbSession, QualityGateDto qualityGate, UserDto user);
+}
*/
package org.sonar.server.qualitygate.ws;
-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.QualityGateUserPermissionsDto;
import org.sonar.db.user.UserDto;
-import static org.sonar.server.exceptions.NotFoundException.checkFound;
-import static org.sonar.server.qualitygate.ws.CreateAction.NAME_MAXIMUM_LENGTH;
import static org.sonar.server.qualitygate.ws.QualityGatesWsParameters.ACTION_ADD_USER;
-import static org.sonar.server.qualitygate.ws.QualityGatesWsParameters.PARAM_GATE_NAME;
-import static org.sonarqube.ws.client.user.UsersWsParameters.PARAM_LOGIN;
-public class AddUserAction implements QualityGatesWsAction {
+public class AddUserAction extends AbstractUserAction {
- private final DbClient dbClient;
private final UuidFactory uuidFactory;
- private final QualityGatesWsSupport wsSupport;
public AddUserAction(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_LOGIN)
- .setDescription("User login")
- .setRequired(true)
- .setExampleValue("john.doe");
-
+ super.defineGateAndUserParameters(action);
}
@Override
- public void handle(Request request, Response response) throws Exception {
- final String login = request.mandatoryParam(PARAM_LOGIN);
- final String qualityGateName = request.mandatoryParam(PARAM_GATE_NAME);
-
- try (DbSession dbSession = dbClient.openSession(false)) {
- QualityGateDto qualityGateDto = wsSupport.getByName(dbSession, qualityGateName);
- wsSupport.checkCanLimitedEdit(dbSession, qualityGateDto);
- UserDto user = getUser(dbSession, login);
- addUser(dbSession, qualityGateDto, user);
- }
- response.noContent();
- }
-
- private UserDto getUser(DbSession dbSession, String login) {
- UserDto user = dbClient.userDao().selectByLogin(dbSession, login);
- checkFound(user, "User with login '%s' is not found", login);
- return user;
- }
-
- private void addUser(DbSession dbSession, QualityGateDto qualityGate, UserDto user) {
+ protected void apply(DbSession dbSession, QualityGateDto qualityGate, UserDto user) {
if (dbClient.qualityGateUserPermissionDao().exists(dbSession, qualityGate, user)) {
return;
}
QualityGatesWs.class,
QualityGatesWsSupport.class,
RemoveGroupAction.class,
+ RemoveUserAction.class,
RenameAction.class,
SearchAction.class,
SearchGroupsAction.class,
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_REMOVE_USER = "remove_user";
public static final String ACTION_SEARCH_GROUPS = "search_groups";
public static final String ACTION_SEARCH_USERS = "search_users";
--- /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.UserDto;
+
+import static org.sonar.server.qualitygate.ws.QualityGatesWsParameters.ACTION_REMOVE_USER;
+
+public class RemoveUserAction extends AbstractUserAction {
+
+ public RemoveUserAction(DbClient dbClient, QualityGatesWsSupport wsSupport) {
+ super(dbClient, wsSupport);
+ }
+
+ @Override
+ public void define(WebService.NewController context) {
+ WebService.NewAction action = context
+ .createAction(ACTION_REMOVE_USER)
+ .setDescription("Remove the ability from an user 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.defineGateAndUserParameters(action);
+ }
+
+ @Override
+ protected void apply(DbSession dbSession, QualityGateDto qualityGate, UserDto user) {
+ if (!dbClient.qualityGateUserPermissionDao().exists(dbSession, qualityGate, user)) {
+ return;
+ }
+ dbClient.qualityGateUserPermissionDao().deleteByQualityGateAndUser(dbSession, qualityGate, user);
+ 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 + 22);
+ assertThat(container.size()).isEqualTo(COMPONENTS_IN_EMPTY_COMPONENT_CONTAINER + 23);
}
}
--- /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.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.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.user.UsersWsParameters.PARAM_LOGIN;
+
+public class RemoveUserActionTest {
+
+ @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 RemoveUserAction(dbClient, wsSupport));
+
+ @Test
+ public void test_definition() {
+ WebService.Action def = ws.getDef();
+ assertThat(def.key()).isEqualTo("remove_user");
+ assertThat(def.isPost()).isTrue();
+ assertThat(def.isInternal()).isTrue();
+ assertThat(def.params()).extracting(WebService.Param::key).containsExactlyInAnyOrder("login", "gateName");
+ }
+
+ @Test
+ public void remove_user() {
+ QualityGateDto qualityGate = db.qualityGates().insertQualityGate();
+ UserDto user = db.users().insertUser();
+ db.qualityGates().addUserPermission(qualityGate, user);
+ userSession.logIn().addPermission(GlobalPermission.ADMINISTER_QUALITY_GATES);
+
+ TestResponse response = ws.newRequest()
+ .setParam(PARAM_GATE_NAME, qualityGate.getName())
+ .setParam(PARAM_LOGIN, user.getLogin())
+ .execute();
+
+ assertThat(response.getStatus()).isEqualTo(204);
+ assertThat(dbClient.qualityGateUserPermissionDao().exists(db.getSession(), qualityGate, user)).isFalse();
+ }
+
+ @Test
+ public void does_nothing_when_user_cannot_edit_gate() {
+ QualityGateDto qualityGate = db.qualityGates().insertQualityGate();
+ UserDto user = db.users().insertUser();
+
+ assertThat(dbClient.qualityGateUserPermissionDao().exists(db.getSession(), qualityGate, user)).isFalse();
+
+ userSession.logIn().addPermission(GlobalPermission.ADMINISTER_QUALITY_GATES);
+
+ ws.newRequest()
+ .setParam(PARAM_GATE_NAME, qualityGate.getName())
+ .setParam(PARAM_LOGIN, user.getLogin())
+ .execute();
+
+ assertThat(dbClient.qualityGateUserPermissionDao().exists(db.getSession(), qualityGate, user)).isFalse();
+ }
+
+ @Test
+ public void qg_administrators_can_remove_user() {
+ QualityGateDto qualityGate = db.qualityGates().insertQualityGate();
+ UserDto user = db.users().insertUser();
+ db.qualityGates().addUserPermission(qualityGate, user);
+ userSession.logIn().addPermission(GlobalPermission.ADMINISTER_QUALITY_GATES);
+
+ ws.newRequest()
+ .setParam(PARAM_GATE_NAME, qualityGate.getName())
+ .setParam(PARAM_LOGIN, user.getLogin())
+ .execute();
+
+ assertThat(dbClient.qualityGateUserPermissionDao().exists(db.getSession(), qualityGate, user)).isFalse();
+ }
+
+ @Test
+ public void qg_editors_can_remove_user() {
+ QualityGateDto qualityGate = db.qualityGates().insertQualityGate();
+ UserDto user = db.users().insertUser();
+ db.qualityGates().addUserPermission(qualityGate, user);
+ UserDto userAllowedToEditGate = db.users().insertUser();
+ db.qualityGates().addUserPermission(qualityGate, userAllowedToEditGate);
+ userSession.logIn(userAllowedToEditGate);
+
+ ws.newRequest()
+ .setParam(PARAM_GATE_NAME, qualityGate.getName())
+ .setParam(PARAM_LOGIN, user.getLogin())
+ .execute();
+
+ assertThat(dbClient.qualityGateUserPermissionDao().exists(db.getSession(), qualityGate, user)).isFalse();
+ }
+
+ @Test
+ public void fail_when_user_does_not_exist() {
+ QualityGateDto qualityGate = db.qualityGates().insertQualityGate();
+ userSession.logIn().addPermission(GlobalPermission.ADMINISTER_QUALITY_GATES);
+
+ final TestRequest request = ws.newRequest()
+ .setParam(PARAM_GATE_NAME, qualityGate.getName())
+ .setParam(PARAM_LOGIN, "unknown");
+
+ assertThatThrownBy(() -> request.execute())
+ .isInstanceOf(NotFoundException.class)
+ .hasMessage("User with login 'unknown' is not found");
+ }
+
+ @Test
+ public void fail_when_qgate_does_not_exist() {
+ UserDto user = db.users().insertUser();
+ userSession.logIn().addPermission(GlobalPermission.ADMINISTER_QUALITY_GATES);
+
+ final TestRequest request = ws.newRequest()
+ .setParam(PARAM_GATE_NAME, "unknown")
+ .setParam(PARAM_LOGIN, user.getLogin());
+
+ assertThatThrownBy(() -> request.execute())
+ .isInstanceOf(NotFoundException.class)
+ .hasMessage("No quality gate has been found for name unknown");
+ }
+
+ @Test
+ public void fail_when_qg_is_built_in() {
+ UserDto user = db.users().insertUser();
+ QualityGateDto qualityGate = db.qualityGates().insertQualityGate(qg -> qg.setBuiltIn(true));
+ userSession.logIn().addPermission(GlobalPermission.ADMINISTER_QUALITY_GATES);
+
+ final TestRequest request = ws.newRequest()
+ .setParam(PARAM_GATE_NAME, qualityGate.getName())
+ .setParam(PARAM_LOGIN, user.getLogin());
+
+ assertThatThrownBy(() -> request.execute())
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessage(String.format("Operation forbidden for built-in Quality Gate '%s'", qualityGate.getName()));
+ }
+
+ @Test
+ public void fail_when_not_enough_permission() {
+ QualityGateDto qualityGate = db.qualityGates().insertQualityGate();
+ UserDto user = db.users().insertUser();
+ userSession.logIn(db.users().insertUser());
+
+ final TestRequest request = ws.newRequest()
+ .setParam(PARAM_GATE_NAME, qualityGate.getName())
+ .setParam(PARAM_LOGIN, user.getLogin());
+
+ assertThatThrownBy(() -> request.execute())
+ .isInstanceOf(ForbiddenException.class);
+ }
+}