]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-15500 Create api/qualitygates/add_user service
authorZipeng WU <zipeng.wu@sonarsource.com>
Mon, 11 Oct 2021 15:54:08 +0000 (17:54 +0200)
committersonartech <sonartech@sonarsource.com>
Fri, 22 Oct 2021 20:03:27 +0000 (20:03 +0000)
16 files changed:
server/sonar-db-core/src/main/java/org/sonar/db/version/SqTables.java
server/sonar-db-dao/src/main/java/org/sonar/db/DaoModule.java
server/sonar-db-dao/src/main/java/org/sonar/db/DbClient.java
server/sonar-db-dao/src/main/java/org/sonar/db/MyBatis.java
server/sonar-db-dao/src/main/java/org/sonar/db/qualitygate/QualityGateUserPermissionsDao.java [new file with mode: 0644]
server/sonar-db-dao/src/main/java/org/sonar/db/qualitygate/QualityGateUserPermissionsDto.java [new file with mode: 0644]
server/sonar-db-dao/src/main/java/org/sonar/db/qualitygate/QualityGateUserPermissionsMapper.java [new file with mode: 0644]
server/sonar-db-dao/src/main/resources/org/sonar/db/qualitygate/QualityGateUserPermissionsMapper.xml [new file with mode: 0644]
server/sonar-db-dao/src/test/java/org/sonar/db/qualitygate/QualityGateUserPermissionsDaoTest.java [new file with mode: 0644]
server/sonar-db-dao/src/testFixtures/java/org/sonar/db/qualitygate/QualityGateDbTester.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualitygate/ws/AddUserAction.java [new file with mode: 0644]
server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualitygate/ws/QualityGateWsModule.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualitygate/ws/QualityGatesWsParameters.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualitygate/ws/QualityGatesWsSupport.java
server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualitygate/ws/AddUserActionTest.java [new file with mode: 0644]
server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualitygate/ws/QualityGateWsModuleTest.java

index 5d2b3174ddbd4dc349e3e0b97e3378c8bf71c5d9..1feb4af67ad75d2d32f097d43423c4c7904c9322 100644 (file)
@@ -87,6 +87,7 @@ public final class SqTables {
     "qprofile_edit_groups",
     "qprofile_edit_users",
     "quality_gates",
+    "qgate_user_permissions",
     "qgate_group_permissions",
     "quality_gate_conditions",
     "saml_message_ids",
index f288a33053db9c2dc79fe8f35b06139f4e3ce330..2212356d10d553897bd14208111bfdb1e18c6f11 100644 (file)
@@ -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.QualityGateUserPermissionsDao;
 import org.sonar.db.qualityprofile.ActiveRuleDao;
 import org.sonar.db.qualityprofile.DefaultQProfileDao;
 import org.sonar.db.qualityprofile.QProfileChangeDao;
@@ -146,6 +147,7 @@ public class DaoModule extends Module {
     QProfileEditUsersDao.class,
     QualityGateConditionDao.class,
     QualityGateDao.class,
+    QualityGateUserPermissionsDao.class,
     QualityProfileDao.class,
     QualityProfileExportDao.class,
     RoleDao.class,
index 3efb4aee142fe06c1ceee3e02ca869bd48970c39..2eb29ee9eb0aa3a3355d3e0ea54831028aeff153 100644 (file)
@@ -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.QualityGateUserPermissionsDao;
 import org.sonar.db.qualityprofile.ActiveRuleDao;
 import org.sonar.db.qualityprofile.DefaultQProfileDao;
 import org.sonar.db.qualityprofile.QProfileChangeDao;
@@ -134,6 +135,7 @@ public class DbClient {
   private final PurgeDao purgeDao;
   private final QualityGateDao qualityGateDao;
   private final QualityGateConditionDao gateConditionDao;
+  private final QualityGateUserPermissionsDao qualityGateUserPermissionsDao;
   private final ProjectQgateAssociationDao projectQgateAssociationDao;
   private final DuplicationDao duplicationDao;
   private final NotificationQueueDao notificationQueueDao;
@@ -209,6 +211,7 @@ public class DbClient {
     eventComponentChangeDao = getDao(map, EventComponentChangeDao.class);
     purgeDao = getDao(map, PurgeDao.class);
     qualityGateDao = getDao(map, QualityGateDao.class);
+    qualityGateUserPermissionsDao = getDao(map, QualityGateUserPermissionsDao.class);
     gateConditionDao = getDao(map, QualityGateConditionDao.class);
     projectQgateAssociationDao = getDao(map, ProjectQgateAssociationDao.class);
     duplicationDao = getDao(map, DuplicationDao.class);
@@ -417,6 +420,10 @@ public class DbClient {
     return gateConditionDao;
   }
 
+  public QualityGateUserPermissionsDao qualityGateUserPermissionDao() {
+    return qualityGateUserPermissionsDao;
+  }
+
   public ProjectQgateAssociationDao projectQgateAssociationDao() {
     return projectQgateAssociationDao;
   }
index 5d25b239f8f745ac133418bd654c73ae5a54e41e..03b4dd33ab34f0fd327459a3f66eb2d3590d00a9 100644 (file)
@@ -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.QualityGateUserPermissionsMapper;
 import org.sonar.db.qualitygate.QualityGateMapper;
 import org.sonar.db.qualityprofile.ActiveRuleDto;
 import org.sonar.db.qualityprofile.ActiveRuleMapper;
@@ -278,6 +279,7 @@ public class MyBatis implements Startable {
       QProfileEditUsersMapper.class,
       QualityGateConditionMapper.class,
       QualityGateMapper.class,
+      QualityGateUserPermissionsMapper.class,
       QualityProfileMapper.class,
       QualityProfileExportMapper.class,
       RoleMapper.class,
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/qualitygate/QualityGateUserPermissionsDao.java b/server/sonar-db-dao/src/main/java/org/sonar/db/qualitygate/QualityGateUserPermissionsDao.java
new file mode 100644 (file)
index 0000000..e79dc9e
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * 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 javax.annotation.Nullable;
+import org.sonar.api.utils.System2;
+import org.sonar.db.Dao;
+import org.sonar.db.DbSession;
+import org.sonar.db.user.UserDto;
+
+public class QualityGateUserPermissionsDao implements Dao {
+
+  private final System2 system2;
+
+  public QualityGateUserPermissionsDao(System2 system2) {
+    this.system2 = system2;
+  }
+
+  public boolean exists(DbSession dbSession, QualityGateDto qualityGate, UserDto user) {
+    return this.exists(dbSession, qualityGate.getUuid(), user.getUuid());
+  }
+
+  public boolean exists(DbSession dbSession, @Nullable String qualityGateUuid, @Nullable String userUuid) {
+    if (qualityGateUuid == null || userUuid == null) {
+      return false;
+    }
+    return selectByQualityGateAndUser(dbSession, qualityGateUuid, userUuid) != null;
+  }
+
+  public QualityGateUserPermissionsDto selectByQualityGateAndUser(DbSession dbSession, String qualityGateUuid, String userUuid) {
+    return mapper(dbSession).selectByQualityGateAndUser(qualityGateUuid, userUuid);
+  }
+
+  public void insert(DbSession dbSession, QualityGateUserPermissionsDto dto) {
+    mapper(dbSession).insert(dto, system2.now());
+  }
+
+  private static QualityGateUserPermissionsMapper mapper(DbSession dbSession) {
+    return dbSession.getMapper(QualityGateUserPermissionsMapper.class);
+  }
+}
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/qualitygate/QualityGateUserPermissionsDto.java b/server/sonar-db-dao/src/main/java/org/sonar/db/qualitygate/QualityGateUserPermissionsDto.java
new file mode 100644 (file)
index 0000000..5d0dcc1
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * 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;
+
+public class QualityGateUserPermissionsDto {
+
+  private String uuid;
+  private String userUuid;
+  private String qualityGateUuid;
+
+  public QualityGateUserPermissionsDto() {
+  }
+
+  public QualityGateUserPermissionsDto(String uuid, String userUuid, String qualityGateUuid) {
+    this.uuid = uuid;
+    this.userUuid = userUuid;
+    this.qualityGateUuid = qualityGateUuid;
+  }
+
+  public String getUuid() {
+    return uuid;
+  }
+
+  public QualityGateUserPermissionsDto setUuid(String uuid) {
+    this.uuid = uuid;
+    return this;
+  }
+
+  public String getUserUuid() {
+    return userUuid;
+  }
+
+  public QualityGateUserPermissionsDto setUserUuid(String userUuid) {
+    this.userUuid = userUuid;
+    return this;
+  }
+
+  public String getQualityGateUuid() {
+    return qualityGateUuid;
+  }
+
+  public QualityGateUserPermissionsDto setQualityGateUuid(String qProfileUuid) {
+    this.qualityGateUuid = qProfileUuid;
+    return this;
+  }
+
+}
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/qualitygate/QualityGateUserPermissionsMapper.java b/server/sonar-db-dao/src/main/java/org/sonar/db/qualitygate/QualityGateUserPermissionsMapper.java
new file mode 100644 (file)
index 0000000..b67289f
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * 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 org.apache.ibatis.annotations.Param;
+
+public interface QualityGateUserPermissionsMapper {
+
+  QualityGateUserPermissionsDto selectByQualityGateAndUser(@Param("qualityGateUuid") String qualityGateUuid, @Param("userUuid") String userUuid);
+
+  void insert(@Param("dto") QualityGateUserPermissionsDto dto, @Param("now") long now);
+
+}
diff --git a/server/sonar-db-dao/src/main/resources/org/sonar/db/qualitygate/QualityGateUserPermissionsMapper.xml b/server/sonar-db-dao/src/main/resources/org/sonar/db/qualitygate/QualityGateUserPermissionsMapper.xml
new file mode 100644 (file)
index 0000000..411e29b
--- /dev/null
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "mybatis-3-mapper.dtd">
+
+<mapper namespace="org.sonar.db.qualitygate.QualityGateUserPermissionsMapper">
+
+  <sql id="sqlColumns">
+    qup.uuid as "uuid",
+    qup.user_uuid as "userUuid",
+    qup.quality_gate_uuid as "qualityGateUuid"
+  </sql>
+
+  <select id="selectByQualityGateAndUser" parameterType="String" resultType="org.sonar.db.qualitygate.QualityGateUserPermissionsDto">
+    select
+    <include refid="sqlColumns"/>
+    from qgate_user_permissions qup
+    where
+    qup.user_uuid = #{userUuid}
+    and qup.quality_gate_uuid = #{qualityGateUuid}
+  </select>
+
+  <insert id="insert" useGeneratedKeys="false" parameterType="map">
+    insert into qgate_user_permissions(
+    uuid,
+    user_uuid,
+    quality_gate_uuid,
+    created_at
+    ) values (
+    #{dto.uuid, jdbcType=VARCHAR},
+    #{dto.userUuid, jdbcType=VARCHAR},
+    #{dto.qualityGateUuid, jdbcType=VARCHAR},
+    #{now, jdbcType=BIGINT}
+    )
+  </insert>
+
+
+</mapper>
+
diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/qualitygate/QualityGateUserPermissionsDaoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/qualitygate/QualityGateUserPermissionsDaoTest.java
new file mode 100644 (file)
index 0000000..1ce2127
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * 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 org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.utils.System2;
+import org.sonar.db.DbSession;
+import org.sonar.db.DbTester;
+import org.sonar.db.user.UserDbTester;
+import org.sonar.db.user.UserDto;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class QualityGateUserPermissionsDaoTest {
+  @Rule
+  public final DbTester db = DbTester.create(System2.INSTANCE);
+
+  private final DbSession dbSession = db.getSession();
+  private final UserDbTester userDbTester = new UserDbTester(db);
+  private final QualityGateDbTester qualityGateDbTester = new QualityGateDbTester(db);
+  private final QualityGateUserPermissionsDao underTest = db.getDbClient().qualityGateUserPermissionDao();
+
+  @Test
+  public void insert() {
+    UserDto user = userDbTester.insertUser();
+    QualityGateDto qualityGate = qualityGateDbTester.insertQualityGate();
+    QualityGateUserPermissionsDto qualityGateUserPermissions = new QualityGateUserPermissionsDto("uuid", user.getUuid(), qualityGate.getUuid());
+    underTest.insert(dbSession, qualityGateUserPermissions);
+    dbSession.commit();
+
+    QualityGateUserPermissionsDto fromDB = underTest.selectByQualityGateAndUser(dbSession, qualityGate.getUuid(), user.getUuid());
+    assertThat(fromDB.getQualityGateUuid()).isEqualTo(qualityGate.getUuid());
+    assertThat(fromDB.getUserUuid()).isEqualTo(user.getUuid());
+    assertThat(fromDB.getUuid()).isEqualTo("uuid");
+  }
+
+  @Test
+  public void exist() {
+    UserDto user = userDbTester.insertUser();
+    QualityGateDto qualityGate = qualityGateDbTester.insertQualityGate();
+    QualityGateUserPermissionsDto qualityGateUserPermissions = new QualityGateUserPermissionsDto("uuid", user.getUuid(), qualityGate.getUuid());
+    underTest.insert(dbSession, qualityGateUserPermissions);
+    dbSession.commit();
+
+    assertThat(underTest.exists(dbSession, qualityGate.getUuid(), user.getUuid())).isTrue();
+    assertThat(underTest.exists(dbSession, qualityGate, user)).isTrue();
+  }
+
+}
\ No newline at end of file
index e6da05b13ddcf4902e0c71d454a8f7511fec4af4..6dbbe27587ec68f9cb06921c0f0fa471b7412332 100644 (file)
@@ -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.UserDto;
 
 import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
 import static org.apache.commons.lang.RandomStringUtils.randomNumeric;
@@ -102,4 +103,13 @@ public class QualityGateDbTester {
   public Optional<String> selectQGateUuidByComponentUuid(String componentUuid) {
     return dbClient.projectQgateAssociationDao().selectQGateUuidByProjectUuid(dbSession, componentUuid);
   }
+
+  public void addUserPermission(QualityGateDto qualityGateDto, UserDto user) {
+    dbClient.qualityGateUserPermissionDao().insert(dbSession, new QualityGateUserPermissionsDto()
+      .setUuid(Uuids.createFast())
+      .setUserUuid(user.getUuid())
+      .setQualityGateUuid(qualityGateDto.getUuid())
+    );
+    dbSession.commit();
+  }
 }
diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualitygate/ws/AddUserAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualitygate/ws/AddUserAction.java
new file mode 100644 (file)
index 0000000..c71e858
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * 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.core.util.UuidFactory;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.qualitygate.QualityGateDto;
+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 {
+
+  private final DbClient dbClient;
+  private final UuidFactory uuidFactory;
+  private final QualityGatesWsSupport wsSupport;
+
+  public AddUserAction(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_USER)
+      .setDescription("Allow a 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");
+
+    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);
+      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) {
+    if (dbClient.qualityGateUserPermissionDao().exists(dbSession, qualityGate, user)) {
+      return;
+    }
+    dbClient.qualityGateUserPermissionDao().insert(dbSession,
+      new QualityGateUserPermissionsDto()
+        .setUuid(uuidFactory.create())
+        .setUserUuid(user.getUuid())
+        .setQualityGateUuid(qualityGate.getUuid()));
+    dbSession.commit();
+  }
+
+}
index c323de119be50b9dd4ec8c932f19194d3b559934..b453501cf57246da69fc99a7b006d93a5ccf6f56 100644 (file)
@@ -25,6 +25,7 @@ public class QualityGateWsModule extends Module {
   @Override
   protected void configureModule() {
     add(
+      AddUserAction.class,
       QualityGatesWsSupport.class,
       QualityGatesWs.class,
       ListAction.class,
index 77a15c4983101c078854f0043244d6ecef3f2f0e..960ec78b0fd9502fabb4f81f05a0fa4cf632ebba 100644 (file)
@@ -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_USER = "add_user";
 
   public static final String PARAM_ANALYSIS_ID = "analysisId";
   public static final String PARAM_BRANCH = "branch";
index c48519d43154842c6e23fee66cfb7ea15486278c..c3daf8d406da5f564bfa6ec31b0c4b006729141c 100644 (file)
@@ -86,6 +86,18 @@ public class QualityGatesWsSupport {
     userSession.checkPermission(ADMINISTER_QUALITY_GATES);
   }
 
+  void checkCanLimitedEdit(DbSession dbSession, QualityGateDto qualityGate) {
+    checkNotBuiltIn(qualityGate);
+    if (!userSession.hasPermission(ADMINISTER_QUALITY_GATES) && !userHasPermission(dbSession, qualityGate)) {
+      throw insufficientPrivilegesException();
+    }
+  }
+
+  boolean userHasPermission(DbSession dbSession, QualityGateDto qualityGate) {
+    return userSession.isLoggedIn() && dbClient.qualityGateUserPermissionDao().exists(dbSession, qualityGate.getUuid(), userSession.getUuid());
+  }
+
+
   void checkCanAdminProject(ProjectDto project) {
     if (userSession.hasPermission(ADMINISTER_QUALITY_GATES)
       || userSession.hasProjectPermission(ADMIN, project)) {
diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualitygate/ws/AddUserActionTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualitygate/ws/AddUserActionTest.java
new file mode 100644 (file)
index 0000000..b2fea3f
--- /dev/null
@@ -0,0 +1,167 @@
+/*
+ * 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.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.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 AddUserActionTest {
+  @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 AddUserAction(dbClient, uuidFactory, wsSupport));
+
+  @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("login", "gateName");
+  }
+
+  @Test
+  public void add_user() {
+    QualityGateDto qualityGateDto = db.qualityGates().insertQualityGate();
+    UserDto user = db.users().insertUser();
+    userSession.logIn().addPermission(GlobalPermission.ADMINISTER_QUALITY_GATES);
+
+    TestResponse response = ws.newRequest()
+      .setParam(PARAM_GATE_NAME, qualityGateDto.getName())
+      .setParam(PARAM_LOGIN, user.getLogin())
+      .execute();
+
+    assertThat(response.getStatus()).isEqualTo(204);
+    assertThat(dbClient.qualityGateUserPermissionDao().exists(db.getSession(), qualityGateDto, user)).isTrue();
+  }
+
+  @Test
+  public void does_nothing_when_user_can_already_edit_qualityGateDto() {
+    QualityGateDto qualityGateDto = db.qualityGates().insertQualityGate();
+    UserDto user = db.users().insertUser();
+    db.qualityGates().addUserPermission(qualityGateDto, user);
+    assertThat(dbClient.qualityGateUserPermissionDao().exists(db.getSession(), qualityGateDto, user)).isTrue();
+    userSession.logIn().addPermission(GlobalPermission.ADMINISTER_QUALITY_GATES);
+
+    ws.newRequest()
+      .setParam(PARAM_GATE_NAME, qualityGateDto.getName())
+      .setParam(PARAM_LOGIN, user.getLogin())
+      .execute();
+
+    assertThat(dbClient.qualityGateUserPermissionDao().exists(db.getSession(), qualityGateDto, user)).isTrue();
+  }
+
+  @Test
+  public void quality_gate_administers_can_add_user() {
+    QualityGateDto qualityGateDto = db.qualityGates().insertQualityGate();
+    UserDto user = db.users().insertUser();
+    userSession.logIn().addPermission(GlobalPermission.ADMINISTER_QUALITY_GATES);
+
+    ws.newRequest()
+      .setParam(PARAM_GATE_NAME, qualityGateDto.getName())
+      .setParam(PARAM_LOGIN, user.getLogin())
+      .execute();
+
+    assertThat(dbClient.qualityGateUserPermissionDao().exists(db.getSession(), qualityGateDto, user)).isTrue();
+  }
+
+  @Test
+  public void quality_gate_editors_can_add_user() {
+    QualityGateDto qualityGateDto = db.qualityGates().insertQualityGate();
+    UserDto user = db.users().insertUser();
+    UserDto userAllowedToEditQualityGate = db.users().insertUser();
+    db.qualityGates().addUserPermission(qualityGateDto, userAllowedToEditQualityGate);
+    userSession.logIn(userAllowedToEditQualityGate);
+
+    ws.newRequest()
+      .setParam(PARAM_GATE_NAME, qualityGateDto.getName())
+      .setParam(PARAM_LOGIN, user.getLogin())
+      .execute();
+
+    assertThat(dbClient.qualityGateUserPermissionDao().exists(db.getSession(), qualityGateDto, user)).isTrue();
+  }
+
+  @Test
+  public void fail_when_user_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_LOGIN, "unknown");
+
+    assertThatThrownBy(() -> request.execute())
+      .isInstanceOf(NotFoundException.class)
+      .hasMessage("User with login 'unknown' is not found");
+
+  }
+
+  @Test
+  public void fail_when_qualityGateDto_does_not_exist() {
+    UserDto user = db.users().insertUser();
+    userSession.logIn().addPermission(GlobalPermission.ADMINISTER_QUALITY_GATES);
+
+    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_not_enough_permission() {
+    QualityGateDto qualityGateDto = db.qualityGates().insertQualityGate();
+    UserDto user = db.users().insertUser();
+
+    TestRequest request = ws.newRequest()
+      .setParam(PARAM_GATE_NAME, qualityGateDto.getName())
+      .setParam(PARAM_LOGIN, user.getLogin());
+
+    assertThatThrownBy(() -> request.execute()).isInstanceOf(ForbiddenException.class);
+  }
+}
\ No newline at end of file
index 073a71ca5a2e2a47770c716dea4fabfdf35c1b00..dc59e33e6004e43b07e6727693e3919317edacce 100644 (file)
@@ -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 + 17);
+    assertThat(container.size()).isEqualTo(COMPONENTS_IN_EMPTY_COMPONENT_CONTAINER + 18);
   }
 
 }