]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-15541 Create api/qualitygates/remove_user service
authorZipeng WU <zipeng.wu@sonarsource.com>
Tue, 19 Oct 2021 15:52:51 +0000 (17:52 +0200)
committersonartech <sonartech@sonarsource.com>
Fri, 22 Oct 2021 20:03:28 +0000 (20:03 +0000)
server/sonar-db-dao/src/main/java/org/sonar/db/qualitygate/QualityGateUserPermissionsDao.java
server/sonar-db-dao/src/main/java/org/sonar/db/qualitygate/QualityGateUserPermissionsMapper.java
server/sonar-db-dao/src/main/resources/org/sonar/db/qualitygate/QualityGateUserPermissionsMapper.xml
server/sonar-db-dao/src/test/java/org/sonar/db/qualitygate/QualityGateUserPermissionsDaoTest.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualitygate/ws/AbstractUserAction.java [new file with mode: 0644]
server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualitygate/ws/AddUserAction.java
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/RemoveUserAction.java [new file with mode: 0644]
server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualitygate/ws/QualityGateWsModuleTest.java
server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualitygate/ws/RemoveUserActionTest.java [new file with mode: 0644]

index 5d383b5f5b8c7c5e83f41b76af5a20d463580bbd..18c9440a847622aa09ac93a462dbb4a350370134 100644 (file)
@@ -64,6 +64,10 @@ public class QualityGateUserPermissionsDao implements Dao {
     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());
   }
index 8f3a7c8e26861a1257f24e83611d107612297c5c..5edf1c3aa134264913b2ca370bc9d0790edc45da 100644 (file)
@@ -35,6 +35,8 @@ public interface QualityGateUserPermissionsMapper {
 
   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);
index 628a828f1f73860c8da82a7ffc05bc0149f859ec..149fc766cfb1d95934dd61ea314dad07278681c1 100644 (file)
     </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}
index 048be73e898fd1564c714ae8a0174ed89f377c67..0c956d9bdca752c70eec9ab2f0861fe28fec0176 100644 (file)
@@ -207,6 +207,19 @@ public class QualityGateUserPermissionsDaoTest {
       .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();
diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualitygate/ws/AbstractUserAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualitygate/ws/AbstractUserAction.java
new file mode 100644 (file)
index 0000000..046a113
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * 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);
+}
index c71e858085d848e14d8ecba4d24ccb4b6d848031..6ce85ae10920247dea8061123ff0939c686f5788 100644 (file)
@@ -19,8 +19,6 @@
  */
 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;
@@ -29,22 +27,15 @@ 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 {
+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
@@ -62,40 +53,11 @@ public class AddUserAction implements QualityGatesWsAction {
       .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;
     }
index a8b3afc1d3a543c19e592071dfd75885ba5d78e5..9523165f140273bdb46f6d0ee4f535e3ecdbdf11 100644 (file)
@@ -39,6 +39,7 @@ public class QualityGateWsModule extends Module {
       QualityGatesWs.class,
       QualityGatesWsSupport.class,
       RemoveGroupAction.class,
+      RemoveUserAction.class,
       RenameAction.class,
       SearchAction.class,
       SearchGroupsAction.class,
index dd4e5825aafdb0be81ae5cdfc44b46045193606b..341c053bd876dafdac31e0707c0eaa6e7cd5bd59 100644 (file)
@@ -32,6 +32,7 @@ public class QualityGatesWsParameters {
   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";
 
diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualitygate/ws/RemoveUserAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualitygate/ws/RemoveUserAction.java
new file mode 100644 (file)
index 0000000..f1cbb81
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * 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();
+  }
+}
index f3748dd3ed67f66c053ea8c4b54d2e1e0ed7602d..5a6f375475b4197fd8f34e1464c79273dc50a16d 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 + 22);
+    assertThat(container.size()).isEqualTo(COMPONENTS_IN_EMPTY_COMPONENT_CONTAINER + 23);
   }
 
 }
diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualitygate/ws/RemoveUserActionTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualitygate/ws/RemoveUserActionTest.java
new file mode 100644 (file)
index 0000000..15b3386
--- /dev/null
@@ -0,0 +1,184 @@
+/*
+ * 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);
+  }
+}