From 92f07538cf9928e2107d201f8518061a69ac1341 Mon Sep 17 00:00:00 2001 From: Julien Lancelot Date: Thu, 21 Sep 2017 14:00:30 +0200 Subject: [PATCH] SONAR-1330 Add user permission to edit single quality profile --- .../ComputeEngineContainerImplTest.java | 10 +- .../java/org/sonar/db/version/SqTables.java | 1 + .../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 + .../qualityprofile/QProfileEditUsersDao.java | 46 +++ .../qualityprofile/QProfileEditUsersDto.java | 55 ++++ .../QProfileEditUsersMapper.java | 29 ++ .../QProfileEditUsersMapper.xml | 36 +++ .../test/java/org/sonar/db/DaoModuleTest.java | 2 +- .../QProfileEditUsersDaoTest.java | 74 +++++ .../QualityProfileDbTester.java | 12 + .../qualityprofile/ws/AddUserAction.java | 38 ++- .../qualityprofile/ws/QProfileWsSupport.java | 42 ++- .../qualityprofile/ws/AddUserActionTest.java | 301 ++++++++++++++++++ .../client/qualityprofile/AddUserRequest.java | 93 ++++++ .../QualityProfilesService.java | 11 + .../QualityProfilesServiceTest.java | 21 ++ 18 files changed, 772 insertions(+), 10 deletions(-) create mode 100644 server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/QProfileEditUsersDao.java create mode 100644 server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/QProfileEditUsersDto.java create mode 100644 server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/QProfileEditUsersMapper.java create mode 100644 server/sonar-db-dao/src/main/resources/org/sonar/db/qualityprofile/QProfileEditUsersMapper.xml create mode 100644 server/sonar-db-dao/src/test/java/org/sonar/db/qualityprofile/QProfileEditUsersDaoTest.java create mode 100644 server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/AddUserActionTest.java create mode 100644 sonar-ws/src/main/java/org/sonarqube/ws/client/qualityprofile/AddUserRequest.java 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 fefec4144f9..f91d4c72718 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 @@ -110,7 +110,7 @@ public class ComputeEngineContainerImplTest { picoContainer.getComponentAdapters().stream() .map(ComponentAdapter::getComponentImplementation) .collect(Collectors.toList())).contains((Class) StartableHazelcastMember.class, - (Class) CeDistributedInformationImpl.class); + (Class) CeDistributedInformationImpl.class); underTest.stop(); } @@ -139,7 +139,7 @@ public class ComputeEngineContainerImplTest { + 4 // content of ReportAnalysisFailureNotificationModule + 3 // CeCleaningModule + its content + 1 // CeDistributedInformation - ); + ); assertThat(picoContainer.getParent().getComponentAdapters()).hasSize( CONTAINER_ITSELF + 5 // level 3 @@ -152,7 +152,7 @@ public class ComputeEngineContainerImplTest { assertThat(picoContainer.getParent().getParent().getParent().getComponentAdapters()).hasSize( COMPONENTS_IN_LEVEL_1_AT_CONSTRUCTION + 26 // level 1 - + 49 // content of DaoModule + + 50 // content of DaoModule + 3 // content of EsSearchModule + 65 // content of CorePropertyDefinitions + 1 // StopFlagContainer @@ -161,8 +161,8 @@ public class ComputeEngineContainerImplTest { picoContainer.getComponentAdapters().stream() .map(ComponentAdapter::getComponentImplementation) .collect(Collectors.toList())).doesNotContain((Class) StartableHazelcastMember.class, - (Class) CeDistributedInformationImpl.class).contains( - (Class) StandaloneCeDistributedInformation.class); + (Class) CeDistributedInformationImpl.class).contains( + (Class) StandaloneCeDistributedInformation.class); assertThat(picoContainer.getParent().getParent().getParent().getParent()).isNull(); underTest.stop(); 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 35c2a91d202..a621cdc7706 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_users", "quality_gates", "quality_gate_conditions", "rules", 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 d791dbbff31..d6fa06ecacd 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.QProfileEditUsersDao; import org.sonar.db.qualityprofile.QualityProfileDao; import org.sonar.db.rule.RuleDao; import org.sonar.db.rule.RuleRepositoryDao; @@ -114,6 +115,7 @@ public class DaoModule extends Module { PropertiesDao.class, PurgeDao.class, QProfileChangeDao.class, + QProfileEditUsersDao.class, QualityGateConditionDao.class, QualityGateDao.class, QualityProfileDao.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 f694b925d14..bb269e0d2c4 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.QProfileEditUsersDao; import org.sonar.db.qualityprofile.QualityProfileDao; import org.sonar.db.rule.RuleDao; import org.sonar.db.rule.RuleRepositoryDao; @@ -126,6 +127,7 @@ public class DbClient { private final EsQueueDao esQueueDao; private final PluginDao pluginDao; private final BranchDao branchDao; + private final QProfileEditUsersDao qProfileEditUsersDao; public DbClient(Database database, MyBatis myBatis, DBSessions dbSessions, Dao... daos) { this.database = database; @@ -185,6 +187,7 @@ public class DbClient { esQueueDao = getDao(map, EsQueueDao.class); pluginDao = getDao(map, PluginDao.class); branchDao = getDao(map, BranchDao.class); + qProfileEditUsersDao = getDao(map, QProfileEditUsersDao.class); } public DbSession openSession(boolean batch) { @@ -391,6 +394,10 @@ public class DbClient { return branchDao; } + public QProfileEditUsersDao qProfileEditUsersDao() { + return qProfileEditUsersDao; + } + 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 b8ee31f72e7..244cb18af54 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.QProfileEditUsersMapper; import org.sonar.db.qualityprofile.QualityProfileMapper; import org.sonar.db.rule.RuleDto; import org.sonar.db.rule.RuleMapper; @@ -228,6 +229,7 @@ public class MyBatis implements Startable { PropertiesMapper.class, PurgeMapper.class, QProfileChangeMapper.class, + QProfileEditUsersMapper.class, QualityGateConditionMapper.class, QualityGateMapper.class, QualityProfileMapper.class, diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/QProfileEditUsersDao.java b/server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/QProfileEditUsersDao.java new file mode 100644 index 00000000000..f5443fb5c11 --- /dev/null +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/QProfileEditUsersDao.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.UserDto; + +public class QProfileEditUsersDao implements Dao { + + private final System2 system2; + + public QProfileEditUsersDao(System2 system2) { + this.system2 = system2; + } + + public boolean exists(DbSession dbSession, QProfileDto profile, UserDto user) { + return mapper(dbSession).selectByQProfileAndUser(profile.getKee(), user.getId()) != null; + } + + public void insert(DbSession dbSession, QProfileEditUsersDto dto) { + mapper(dbSession).insert(dto, system2.now()); + } + + private static QProfileEditUsersMapper mapper(DbSession dbSession) { + return dbSession.getMapper(QProfileEditUsersMapper.class); + } +} diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/QProfileEditUsersDto.java b/server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/QProfileEditUsersDto.java new file mode 100644 index 00000000000..4dd5f30bb82 --- /dev/null +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/QProfileEditUsersDto.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 QProfileEditUsersDto { + + private String uuid; + private int userId; + private String qProfileUuid; + + public String getUuid() { + return uuid; + } + + public QProfileEditUsersDto setUuid(String uuid) { + this.uuid = uuid; + return this; + } + + public int getUserId() { + return userId; + } + + public QProfileEditUsersDto setUserId(int userId) { + this.userId = userId; + return this; + } + + public String getQProfileUuid() { + return qProfileUuid; + } + + public QProfileEditUsersDto setQProfileUuid(String qProfileUuid) { + this.qProfileUuid = qProfileUuid; + return this; + } + +} diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/QProfileEditUsersMapper.java b/server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/QProfileEditUsersMapper.java new file mode 100644 index 00000000000..8efa5fd5609 --- /dev/null +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/QProfileEditUsersMapper.java @@ -0,0 +1,29 @@ +/* + * 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 QProfileEditUsersMapper { + + QProfileEditUsersDto selectByQProfileAndUser(@Param("qProfileUuid") String qProfileUuid, @Param("userId") int userId); + + void insert(@Param("dto") QProfileEditUsersDto dto, @Param("now") long now); +} diff --git a/server/sonar-db-dao/src/main/resources/org/sonar/db/qualityprofile/QProfileEditUsersMapper.xml b/server/sonar-db-dao/src/main/resources/org/sonar/db/qualityprofile/QProfileEditUsersMapper.xml new file mode 100644 index 00000000000..1e391294d54 --- /dev/null +++ b/server/sonar-db-dao/src/main/resources/org/sonar/db/qualityprofile/QProfileEditUsersMapper.xml @@ -0,0 +1,36 @@ + + + + + + + qeu.uuid as "uuid", + qeu.user_id as "userId", + qeu.qprofile_uuid as "qProfileUuid" + + + + + + insert into qprofile_edit_users( + uuid, + user_id, + qprofile_uuid, + created_at, + ) values ( + #{dto.uuid, jdbcType=VARCHAR}, + #{dto.userId, 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 9d7a3d2786a..986fbd7e2c3 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 + 49); + assertThat(container.size()).isEqualTo(2 + 50); } } diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/qualityprofile/QProfileEditUsersDaoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/qualityprofile/QProfileEditUsersDaoTest.java new file mode 100644 index 00000000000..a3442583975 --- /dev/null +++ b/server/sonar-db-dao/src/test/java/org/sonar/db/qualityprofile/QProfileEditUsersDaoTest.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.UserDto; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.entry; + +public class QProfileEditUsersDaoTest { + + private static final long NOW = 10_000_000_000L; + + private System2 system2 = new TestSystem2().setNow(NOW); + + @Rule + public DbTester db = DbTester.create(system2); + + private QProfileEditUsersDao underTest = db.getDbClient().qProfileEditUsersDao(); + + @Test + public void exists() { + OrganizationDto organization = db.organizations().insert(); + QProfileDto profile = db.qualityProfiles().insert(organization); + QProfileDto anotherProfile = db.qualityProfiles().insert(organization); + UserDto user = db.users().insertUser(); + UserDto anotherUser = db.users().insertUser(); + db.qualityProfiles().addUserPermission(profile, user); + + assertThat(underTest.exists(db.getSession(), profile, user)).isTrue(); + assertThat(underTest.exists(db.getSession(), profile, anotherUser)).isFalse(); + assertThat(underTest.exists(db.getSession(), anotherProfile, user)).isFalse(); + assertThat(underTest.exists(db.getSession(), anotherProfile, anotherUser)).isFalse(); + } + + @Test + public void insert() { + underTest.insert(db.getSession(), new QProfileEditUsersDto() + .setUuid("ABCD") + .setUserId(100) + .setQProfileUuid("QPROFILE") + ); + + assertThat(db.selectFirst(db.getSession(), "select uuid as \"uuid\", user_id as \"userId\", qprofile_uuid as \"qProfileUuid\", created_at as \"createdAt\" from qprofile_edit_users")).contains( + entry("uuid", "ABCD"), + entry("userId", 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 02c53fad43d..7dc2a64ea4b 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 @@ -23,12 +23,14 @@ import java.util.Arrays; import java.util.Optional; import java.util.function.Consumer; import org.sonar.api.rule.Severity; +import org.sonar.core.util.UuidFactoryFast; import org.sonar.db.DbClient; import org.sonar.db.DbSession; 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.UserDto; import static org.apache.commons.lang.math.RandomUtils.nextInt; import static org.apache.commons.lang.math.RandomUtils.nextLong; @@ -108,4 +110,14 @@ public class QualityProfileDbTester { dbSession.commit(); return this; } + + public void addUserPermission(QProfileDto profile, UserDto user){ + dbClient.qProfileEditUsersDao().insert(dbSession, new QProfileEditUsersDto() + .setUuid(UuidFactoryFast.getInstance().create()) + .setUserId(user.getId()) + .setQProfileUuid(profile.getKee()) + ); + dbSession.commit(); + + } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/AddUserAction.java b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/AddUserAction.java index ca0ed070ecd..eb29e55d783 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/AddUserAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/AddUserAction.java @@ -25,19 +25,33 @@ 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.QProfileEditUsersDto; +import org.sonar.db.user.UserDto; 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_USER; import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_LANGUAGE; import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_LOGIN; +import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_ORGANIZATION; import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_QUALITY_PROFILE; public class AddUserAction implements QProfileWsAction { + private final DbClient dbClient; + private final UuidFactory uuidFactory; + private final QProfileWsSupport wsSupport; private final Languages languages; - public AddUserAction(Languages languages) { + public AddUserAction(DbClient dbClient, UuidFactory uuidFactory, QProfileWsSupport wsSupport, Languages languages) { + this.dbClient = dbClient; + this.uuidFactory = uuidFactory; + this.wsSupport = wsSupport; this.languages = languages; } @@ -73,6 +87,26 @@ public class AddUserAction 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); + UserDto user = wsSupport.getUser(dbSession, organization, request.mandatoryParam(PARAM_LOGIN)); + addUser(dbSession, profile, user); + } + response.noContent(); } + + private void addUser(DbSession dbSession, QProfileDto profile, UserDto user) { + if (dbClient.qProfileEditUsersDao().exists(dbSession, profile, user)) { + return; + } + dbClient.qProfileEditUsersDao().insert(dbSession, + new QProfileEditUsersDto() + .setUuid(uuidFactory.create()) + .setUserId(user.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 cdbdd95ba6c..1649c4ccf96 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,11 +29,15 @@ 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.UserDto; import org.sonar.server.organization.DefaultOrganizationProvider; import org.sonar.server.user.UserSession; import org.sonar.server.ws.WsUtils; +import static com.google.common.base.Preconditions.checkArgument; +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.checkRequest; import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_ORGANIZATION; @@ -94,12 +98,46 @@ public class QProfileWsSupport { return profile; } - public void checkPermission(DbSession dbSession, QProfileDto rulesProfile) { - OrganizationDto organization = getOrganization(dbSession, rulesProfile); + public QProfileDto getProfile(DbSession dbSession, OrganizationDto organization, String name, String language) { + QProfileDto profile = dbClient.qualityProfileDao().selectByNameAndLanguage(dbSession, organization, name, language); + checkFound(profile, "Quality Profile for language '%s' and name '%s' does not exist in organization '%s'", language, name, organization.getKey()); + return profile; + } + + public UserDto getUser(DbSession dbSession, OrganizationDto organization, String login) { + UserDto user = dbClient.userDao().selectActiveUserByLogin(dbSession, login); + checkFound(user, "User with login '%s' is not found'", login); + checkMembership(dbSession, organization, user); + return user; + } + + public void checkPermission(DbSession dbSession, QProfileDto profile) { + OrganizationDto organization = getOrganization(dbSession, profile); userSession.checkPermission(OrganizationPermission.ADMINISTER_QUALITY_PROFILES, organization); } + public void checkCanEdit(DbSession dbSession, QProfileDto profile) { + checkNotBuiltInt(profile); + OrganizationDto organization = getOrganization(dbSession, profile); + userSession.checkLoggedIn(); + if (userSession.hasPermission(OrganizationPermission.ADMINISTER_QUALITY_PROFILES, organization)) { + return; + } + UserDto user = dbClient.userDao().selectByLogin(dbSession, userSession.getLogin()); + checkState(user != null, "User from session does not exist"); + if (dbClient.qProfileEditUsersDao().exists(dbSession, profile, user)) { + return; + } + throw insufficientPrivilegesException(); + } + public void checkNotBuiltInt(QProfileDto profile) { checkRequest(!profile.isBuiltIn(), "Operation forbidden for built-in Quality Profile '%s' with language '%s'", profile.getName(), profile.getLanguage()); } + + public void checkMembership(DbSession dbSession, OrganizationDto organization, UserDto user) { + checkArgument(dbClient.organizationMemberDao().select(dbSession, organization.getUuid(), user.getId()).isPresent(), + "User '%s' is not member of organization '%s'", user.getLogin(), organization.getKey()); + } + } diff --git a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/AddUserActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/AddUserActionTest.java new file mode 100644 index 00000000000..282310ee935 --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/AddUserActionTest.java @@ -0,0 +1,301 @@ +/* + * 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.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.sonar.db.permission.OrganizationPermission.ADMINISTER_QUALITY_PROFILES; +import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_LANGUAGE; +import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_LOGIN; +import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_ORGANIZATION; +import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_QUALITY_PROFILE; + +public class AddUserActionTest { + + 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 AddUserAction(db.getDbClient(), uuidFactory, wsSupport, LANGUAGES)); + + @Test + public void test_definition() { + WebService.Action def = ws.getDef(); + assertThat(def.key()).isEqualTo("add_user"); + assertThat(def.isPost()).isTrue(); + assertThat(def.isInternal()).isTrue(); + assertThat(def.params()).extracting(WebService.Param::key).containsExactlyInAnyOrder("organization", "qualityProfile", "language", "login"); + } + + @Test + public void add_user() { + 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().addPermission(OrganizationPermission.ADMINISTER_QUALITY_PROFILES, organization); + + TestResponse response = ws.newRequest() + .setParam(PARAM_QUALITY_PROFILE, profile.getName()) + .setParam(PARAM_LANGUAGE, XOO) + .setParam(PARAM_LOGIN, user.getLogin()) + .setParam(PARAM_ORGANIZATION, organization.getKey()) + .execute(); + + assertThat(response.getStatus()).isEqualTo(204); + assertThat(db.getDbClient().qProfileEditUsersDao().exists(db.getSession(), profile, user)).isTrue(); + } + + @Test + public void does_nothing_when_user_can_already_edit_profile() { + 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); + db.qualityProfiles().addUserPermission(profile, user); + assertThat(db.getDbClient().qProfileEditUsersDao().exists(db.getSession(), profile, user)).isTrue(); + userSession.logIn().addPermission(OrganizationPermission.ADMINISTER_QUALITY_PROFILES, organization); + + ws.newRequest() + .setParam(PARAM_QUALITY_PROFILE, profile.getName()) + .setParam(PARAM_LANGUAGE, XOO) + .setParam(PARAM_LOGIN, user.getLogin()) + .setParam(PARAM_ORGANIZATION, organization.getKey()) + .execute(); + + assertThat(db.getDbClient().qProfileEditUsersDao().exists(db.getSession(), profile, user)).isTrue(); + } + + @Test + public void qp_administers_can_add_user() { + 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().addPermission(OrganizationPermission.ADMINISTER_QUALITY_PROFILES, organization); + + ws.newRequest() + .setParam(PARAM_QUALITY_PROFILE, profile.getName()) + .setParam(PARAM_LANGUAGE, XOO) + .setParam(PARAM_LOGIN, user.getLogin()) + .setParam(PARAM_ORGANIZATION, organization.getKey()) + .execute(); + + assertThat(db.getDbClient().qProfileEditUsersDao().exists(db.getSession(), profile, user)).isTrue(); + } + + @Test + public void qp_editors_can_add_user() { + 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); + 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_LOGIN, user.getLogin()) + .setParam(PARAM_ORGANIZATION, organization.getKey()) + .execute(); + + assertThat(db.getDbClient().qProfileEditUsersDao().exists(db.getSession(), profile, user)).isTrue(); + } + + @Test + public void uses_default_organization_when_no_organization() { + OrganizationDto organization = db.getDefaultOrganization(); + QProfileDto profile = db.qualityProfiles().insert(organization, p -> p.setLanguage(XOO)); + UserDto user = db.users().insertUser(); + db.organizations().addMember(organization, user); + userSession.logIn().addPermission(ADMINISTER_QUALITY_PROFILES, organization); + + ws.newRequest() + .setParam(PARAM_QUALITY_PROFILE, profile.getName()) + .setParam(PARAM_LANGUAGE, XOO) + .setParam(PARAM_LOGIN, user.getLogin() ) + .execute(); + + assertThat(db.getDbClient().qProfileEditUsersDao().exists(db.getSession(), profile, user)).isTrue(); + } + + @Test + public void fail_when_user_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("User with login 'unknown' is not found"); + + ws.newRequest() + .setParam(PARAM_QUALITY_PROFILE, profile.getName()) + .setParam(PARAM_LANGUAGE, XOO) + .setParam(PARAM_LOGIN, "unknown") + .setParam(PARAM_ORGANIZATION, organization.getKey()) + .execute(); + } + + @Test + public void fail_when_qprofile_does_not_exist() { + OrganizationDto organization = db.organizations().insert(); + 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 'unknown' does not exist in organization '%s'", organization.getKey())); + + ws.newRequest() + .setParam(PARAM_QUALITY_PROFILE, "unknown") + .setParam(PARAM_LANGUAGE, XOO) + .setParam(PARAM_LOGIN, user.getLogin()) + .setParam(PARAM_ORGANIZATION, organization.getKey()) + .execute(); + } + + @Test + public void fail_when_qprofile_does_not_belong_to_organization() { + OrganizationDto organization = db.organizations().insert(); + UserDto user = db.users().insertUser(); + db.organizations().addMember(organization, user); + 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_LOGIN, user.getLogin()) + .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_LOGIN, user.getLogin()) + .setParam(PARAM_ORGANIZATION, organization.getKey()) + .execute(); + } + + @Test + public void fail_when_user_is_not_member_of_organization() { + OrganizationDto organization = db.organizations().insert(); + OrganizationDto anotherOrganization = db.organizations().insert(); + UserDto user = db.users().insertUser(); + db.organizations().addMember(anotherOrganization, user); + QProfileDto profile = db.qualityProfiles().insert(organization, p -> p.setLanguage(XOO)); + userSession.logIn().addPermission(OrganizationPermission.ADMINISTER_QUALITY_PROFILES, organization); + + expectedException.expect(IllegalArgumentException.class); + expectedException.expectMessage(format("User '%s' is not member of organization '%s'", user.getLogin(), organization.getKey())); + + ws.newRequest() + .setParam(PARAM_QUALITY_PROFILE, profile.getName()) + .setParam(PARAM_LANGUAGE, XOO) + .setParam(PARAM_LOGIN, 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_LOGIN, 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_LOGIN, user.getLogin()) + .setParam(PARAM_ORGANIZATION, organization.getKey()) + .execute(); + } +} diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/qualityprofile/AddUserRequest.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/qualityprofile/AddUserRequest.java new file mode 100644 index 00000000000..db89c3d7c40 --- /dev/null +++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/qualityprofile/AddUserRequest.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 AddUserRequest { + + private final String organization; + private final String language; + private final String qualityProfile; + private final String userLogin; + + private AddUserRequest(Builder builder) { + this.language = builder.language; + this.organization = builder.organization; + this.qualityProfile = builder.qualityProfile; + this.userLogin = builder.userLogin; + } + + public String getLanguage() { + return language; + } + + public String getOrganization() { + return organization; + } + + public String getQualityProfile() { + return qualityProfile; + } + + public String getUserLogin() { + return userLogin; + } + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + private String organization; + private String qualityProfile; + private String language; + private String userLogin; + + 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 setUserLogin(String userLogin) { + this.userLogin = userLogin; + return this; + } + + public AddUserRequest build() { + return new AddUserRequest(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 ea9ad29636a..38f5c2ca100 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 @@ -32,6 +32,7 @@ import org.sonarqube.ws.client.WsConnector; import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.ACTION_ACTIVATE_RULE; 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; import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.ACTION_COPY; import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.ACTION_CREATE; @@ -48,6 +49,7 @@ import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters. import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_FROM_KEY; 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; import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_ORGANIZATION; import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_PARAMS; import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_PARENT_QUALITY_PROFILE; @@ -56,6 +58,7 @@ import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters. import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_QUALITY_PROFILE; import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_PROJECT_KEY; import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_PROJECT_UUID; +import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_QUALITY_PROFILE; import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_RESET; import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_RULE; import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_SEVERITY; @@ -170,4 +173,12 @@ public class QualityProfilesService extends BaseService { call(postRequest); } + + public void addUser(AddUserRequest request) { + call(new PostRequest(path(ACTION_ADD_USER)) + .setParam(PARAM_ORGANIZATION, request.getOrganization()) + .setParam(PARAM_QUALITY_PROFILE, request.getQualityProfile()) + .setParam(PARAM_LANGUAGE, request.getLanguage()) + .setParam(PARAM_LOGIN, request.getUserLogin())); + } } 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 a3add919ea7..591d1690abc 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 @@ -36,11 +36,13 @@ import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters. import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_FROM_KEY; 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; import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_ORGANIZATION; import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_PARAMS; import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_KEY; import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_QUALITY_PROFILE; import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_PROJECT_KEY; +import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_QUALITY_PROFILE; import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_RULE; import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_SEVERITY; import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_TO_NAME; @@ -195,4 +197,23 @@ public class QualityProfilesServiceTest { .hasParam(PARAM_SEVERITY, Severity.INFO.toString()) .andNoOtherParam(); } + + @Test + public void add_user() { + underTest.addUser(AddUserRequest.builder() + .setOrganization("O1") + .setQualityProfile("P1") + .setLanguage("Xoo") + .setUserLogin("john") + .build()); + PostRequest request = serviceTester.getPostRequest(); + + serviceTester.assertThat(request) + .hasPath("add_user") + .hasParam(PARAM_ORGANIZATION, "O1") + .hasParam(PARAM_QUALITY_PROFILE, "P1") + .hasParam(PARAM_LANGUAGE, "Xoo") + .hasParam(PARAM_LOGIN, "john") + .andNoOtherParam(); + } } -- 2.39.5