]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-8735 add WS api/organizations/search_my_organizations 1628/head
authorSébastien Lesaint <sebastien.lesaint@sonarsource.com>
Fri, 3 Feb 2017 15:29:38 +0000 (16:29 +0100)
committerSébastien Lesaint <sebastien.lesaint@sonarsource.com>
Tue, 7 Feb 2017 08:32:45 +0000 (09:32 +0100)
server/sonar-server/src/main/java/org/sonar/server/organization/ws/OrganizationsWsModule.java
server/sonar-server/src/main/java/org/sonar/server/organization/ws/SearchMyOrganizationsAction.java [new file with mode: 0644]
server/sonar-server/src/main/resources/org/sonar/server/organization/ws/example-search_my_organization.json [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/organization/ws/OrganizationsWsModuleTest.java
server/sonar-server/src/test/java/org/sonar/server/organization/ws/SearchMyOrganizationsActionTest.java [new file with mode: 0644]
sonar-db/src/main/java/org/sonar/db/organization/OrganizationDao.java
sonar-db/src/main/java/org/sonar/db/organization/OrganizationMapper.java
sonar-db/src/main/resources/org/sonar/db/organization/OrganizationMapper.xml
sonar-db/src/test/java/org/sonar/db/organization/OrganizationDaoTest.java

index a0ab7987ab6b1a5b2cb4c82d34868c971b357ab1..35e805df8eb559550c42bc85a638cf2903ed3b00 100644 (file)
@@ -32,7 +32,8 @@ public class OrganizationsWsModule extends Module {
       CreateAction.class,
       SearchAction.class,
       UpdateAction.class,
-      DeleteAction.class);
+      DeleteAction.class,
+      SearchMyOrganizationsAction.class);
   }
 
 }
diff --git a/server/sonar-server/src/main/java/org/sonar/server/organization/ws/SearchMyOrganizationsAction.java b/server/sonar-server/src/main/java/org/sonar/server/organization/ws/SearchMyOrganizationsAction.java
new file mode 100644 (file)
index 0000000..cbea639
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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.organization.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.api.utils.text.JsonWriter;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.server.user.UserSession;
+
+import static org.sonar.core.permission.GlobalPermissions.SYSTEM_ADMIN;
+
+public class SearchMyOrganizationsAction implements OrganizationsAction {
+  private static final String ACTION = "search_my_organizations";
+
+  private final UserSession userSession;
+  private final DbClient dbClient;
+
+  public SearchMyOrganizationsAction(UserSession userSession, DbClient dbClient) {
+    this.userSession = userSession;
+    this.dbClient = dbClient;
+  }
+
+  @Override
+  public void define(WebService.NewController context) {
+    context.createAction(ACTION)
+      .setPost(false)
+      .setDescription("List keys of the organizations for which the currently authenticated user has the System Administer permission for.")
+      .setResponseExample(getClass().getResource("example-search_my_organization.json"))
+      .setInternal(true)
+      .setSince("6.3")
+      .setHandler(this);
+  }
+
+  @Override
+  public void handle(Request request, Response response) throws Exception {
+    if (!userSession.isLoggedIn()) {
+      response.noContent();
+      return;
+    }
+
+    try (DbSession dbSession = dbClient.openSession(false);
+      JsonWriter jsonWriter = response.newJsonWriter()) {
+      jsonWriter.beginObject();
+      jsonWriter.name("organizations").beginArray();
+      dbClient.organizationDao().selectByPermission(dbSession, userSession.getUserId(), SYSTEM_ADMIN)
+        .forEach(dto -> jsonWriter.value(dto.getKey()));
+      jsonWriter.endArray();
+      jsonWriter.endObject();
+    }
+  }
+}
diff --git a/server/sonar-server/src/main/resources/org/sonar/server/organization/ws/example-search_my_organization.json b/server/sonar-server/src/main/resources/org/sonar/server/organization/ws/example-search_my_organization.json
new file mode 100644 (file)
index 0000000..4d2fe03
--- /dev/null
@@ -0,0 +1,6 @@
+{
+  "organizations": [
+    "my-org",
+    "foo-corp"
+  ]
+}
index 77f86e28501494e54f54363f6cff614c10959b96..fba235871c03d9807945f80dd9eae1e3355f3492 100644 (file)
@@ -33,7 +33,7 @@ public class OrganizationsWsModuleTest {
     ComponentContainer container = new ComponentContainer();
     underTest.configure(container);
     assertThat(container.getPicoContainer().getComponentAdapters())
-      .hasSize(COMPONENTS_IN_EMPTY_COMPONENT_CONTAINER + 6);
+      .hasSize(COMPONENTS_IN_EMPTY_COMPONENT_CONTAINER + 7);
   }
 
 }
diff --git a/server/sonar-server/src/test/java/org/sonar/server/organization/ws/SearchMyOrganizationsActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/organization/ws/SearchMyOrganizationsActionTest.java
new file mode 100644 (file)
index 0000000..ec55b7f
--- /dev/null
@@ -0,0 +1,186 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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.organization.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.permission.GlobalPermissions;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbTester;
+import org.sonar.db.organization.OrganizationDto;
+import org.sonar.db.user.GroupDto;
+import org.sonar.db.user.UserDto;
+import org.sonar.server.tester.UserSessionRule;
+import org.sonar.server.ws.TestResponse;
+import org.sonar.server.ws.WsActionTester;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.sonar.core.permission.GlobalPermissions.SYSTEM_ADMIN;
+import static org.sonar.test.JsonAssert.assertJson;
+
+public class SearchMyOrganizationsActionTest {
+  private static final String NO_ORGANIZATIONS_RESPONSE = "{\"organizations\": []}";
+
+  @Rule
+  public DbTester dbTester = DbTester.create(System2.INSTANCE);
+  @Rule
+  public UserSessionRule userSessionRule = UserSessionRule.standalone();
+
+  private DbClient dbClient = dbTester.getDbClient();
+
+  private WsActionTester underTest = new WsActionTester(new SearchMyOrganizationsAction(userSessionRule, dbClient));
+
+  @Test
+  public void verify_definition() {
+    WebService.Action def = underTest.getDef();
+
+    assertThat(def.key()).isEqualTo("search_my_organizations");
+    assertThat(def.isPost()).isFalse();
+    assertThat(def.isInternal()).isTrue();
+    assertThat(def.since()).isEqualTo("6.3");
+    assertThat(def.description()).isEqualTo("List keys of the organizations for which the currently authenticated user has the System Administer permission for.");
+    assertThat(def.responseExample()).isNotNull();
+
+    assertThat(def.params()).isEmpty();
+  }
+
+  @Test
+  public void verify_response_example() {
+    OrganizationDto organization1 = dbTester.organizations().insertForKey("my-org");
+    OrganizationDto organization2 = dbTester.organizations().insertForKey("foo-corp");
+
+    UserDto user = dbTester.users().insertUser();
+    dbTester.users().insertPermissionOnUser(organization1, user, SYSTEM_ADMIN);
+    dbTester.users().insertPermissionOnUser(organization2, user, SYSTEM_ADMIN);
+
+    userSessionRule.logIn(user);
+
+    TestResponse response = underTest.newRequest().execute();
+
+    assertJson(response.getInput()).isSimilarTo(underTest.getDef().responseExampleAsString());
+  }
+
+  @Test
+  public void returns_empty_response_when_user_is_not_logged_in() {
+    TestResponse response = underTest.newRequest().execute();
+
+    assertThat(response.getStatus()).isEqualTo(204);
+    assertThat(response.getInput()).isEmpty();
+  }
+
+  @Test
+  public void returns_empty_array_when_user_is_logged_in_and_has_no_permission_on_anything() {
+    userSessionRule.logIn();
+
+    TestResponse response = underTest.newRequest().execute();
+
+    assertJson(response.getInput()).isSimilarTo(NO_ORGANIZATIONS_RESPONSE);
+  }
+
+  @Test
+  public void returns_organizations_of_authenticated_user_when_user_has_ADMIN_user_permission_on_some_organization() {
+    UserDto user = dbTester.users().insertUser();
+    dbTester.users().insertPermissionOnUser(dbTester.getDefaultOrganization(), user, SYSTEM_ADMIN);
+    OrganizationDto organization1 = dbTester.organizations().insert();
+    dbTester.users().insertPermissionOnUser(organization1, user, SYSTEM_ADMIN);
+    UserDto otherUser = dbTester.users().insertUser();
+    OrganizationDto organization2 = dbTester.organizations().insert();
+    dbTester.users().insertPermissionOnUser(organization2, otherUser, SYSTEM_ADMIN);
+
+    userSessionRule.logIn(user);
+    assertJson(underTest.newRequest().execute().getInput()).isSimilarTo("{\"organizations\": [" +
+      "\"" + dbTester.getDefaultOrganization().getKey() + "\"," +
+      "\"" + organization1.getKey() + "\"" +
+      "]}");
+
+    userSessionRule.logIn(otherUser);
+    assertJson(underTest.newRequest().execute().getInput()).isSimilarTo("{\"organizations\": [" +
+      "\"" + organization2.getKey() + "\"" +
+      "]}");
+
+    userSessionRule.logIn();
+    assertJson(underTest.newRequest().execute().getInput()).isSimilarTo(NO_ORGANIZATIONS_RESPONSE);
+  }
+
+  @Test
+  public void returns_organizations_of_authenticated_user_when_user_has_ADMIN_group_permission_on_some_organization() {
+    UserDto user = dbTester.users().insertUser();
+    GroupDto defaultGroup = dbTester.users().insertGroup(dbTester.getDefaultOrganization());
+    dbTester.users().insertPermissionOnGroup(defaultGroup, SYSTEM_ADMIN);
+    dbTester.users().insertMember(defaultGroup, user);
+    OrganizationDto organization1 = dbTester.organizations().insert();
+    GroupDto group1 = dbTester.users().insertGroup(organization1);
+    dbTester.users().insertPermissionOnGroup(group1, SYSTEM_ADMIN);
+    dbTester.users().insertMember(group1, user);
+    UserDto otherUser = dbTester.users().insertUser();
+    OrganizationDto organization2 = dbTester.organizations().insert();
+    GroupDto group2 = dbTester.users().insertGroup(organization2);
+    dbTester.users().insertPermissionOnGroup(group2, SYSTEM_ADMIN);
+    dbTester.users().insertMember(group2, otherUser);
+
+    userSessionRule.logIn(user);
+    assertJson(underTest.newRequest().execute().getInput()).isSimilarTo("{\"organizations\": [" +
+      "\"" + dbTester.getDefaultOrganization().getKey() + "\"," +
+      "\"" + organization1.getKey() + "\"" +
+      "]}");
+
+    userSessionRule.logIn(otherUser);
+    assertJson(underTest.newRequest().execute().getInput()).isSimilarTo("{\"organizations\": [" +
+      "\"" + organization2.getKey() + "\"" +
+      "]}");
+
+    userSessionRule.logIn();
+    assertJson(underTest.newRequest().execute().getInput()).isSimilarTo(NO_ORGANIZATIONS_RESPONSE);
+  }
+
+  @Test
+  public void returns_organization_of_authenticated_user_only_for_ADMIN_permission() {
+    UserDto user = dbTester.users().insertUser();
+    OrganizationDto organization1 = dbTester.organizations().insert();
+    OrganizationDto organization2 = dbTester.organizations().insert();
+    GroupDto group = dbTester.users().insertGroup(organization2);
+    dbTester.users().insertMember(group, user);
+    GlobalPermissions.ALL.stream()
+      .filter(s -> !s.equals(SYSTEM_ADMIN))
+      .forEach(s -> {
+        dbTester.users().insertPermissionOnUser(organization1, user, s);
+        dbTester.users().insertPermissionOnGroup(group, s);
+      });
+
+    userSessionRule.logIn(user);
+    assertJson(underTest.newRequest().execute().getInput()).isSimilarTo(NO_ORGANIZATIONS_RESPONSE);
+  }
+
+  @Test
+  public void do_not_return_organization_twice_if_user_has_ADMIN_permission_twice_or_more() {
+    UserDto user = dbTester.users().insertUser();
+    OrganizationDto organization = dbTester.organizations().insert();
+    GroupDto group1 = dbTester.users().insertGroup(organization);
+    dbTester.users().insertPermissionOnGroup(group1, SYSTEM_ADMIN);
+    dbTester.users().insertPermissionOnUser(organization, user, SYSTEM_ADMIN);
+
+    userSessionRule.logIn(user);
+    assertJson(underTest.newRequest().execute().getInput()).isSimilarTo("{\"organizations\": [" +
+      "\"" + organization.getKey() + "\"" +
+      "]}");
+  }
+}
index 04ead578809e8ffaba9a5b4cc71725721579ee44..3994694fa166f4061772da016d82f1bb19f0969c 100644 (file)
@@ -68,6 +68,10 @@ public class OrganizationDao implements Dao {
     return executeLargeInputs(organizationUuids, getMapper(dbSession)::selectByUuids);
   }
 
+  public List<OrganizationDto> selectByPermission(DbSession dbSession, Integer userId, String permission) {
+    return getMapper(dbSession).selectByPermission(userId, permission);
+  }
+
   /**
    * Retrieve the default template of the specified organization if:
    * <ol>
index 94a35be76be54a1f98df5abcd9097b93966a0fdc..8a32e0f08846c8c1ec08bdf4bc6e3e55b7d56895 100644 (file)
@@ -37,6 +37,8 @@ public interface OrganizationMapper {
 
   List<OrganizationDto> selectByUuids(@Param("uuids") List<String> uuids);
 
+  List<OrganizationDto> selectByPermission(@Param("userId") Integer userId, @Param("permission") String permission);
+
   DefaultTemplates selectDefaultTemplatesByUuid(@Param("uuid") String uuid);
 
   /**
index 6b39c43e7f07f6396b35a376177832ba8898085e..5b5a44ce52616a121cbe1e2c93a5bec5972e49ce 100644 (file)
     </where>
   </sql>
 
+  <select id="selectByPermission" parameterType="map" resultType="Organization">
+    select
+      <include refid="selectColumns"/>
+    from organizations org
+    inner join user_roles u on
+      u.organization_uuid = org.uuid
+      and u.user_id = #{userId,jdbcType=INTEGER}
+      and u.resource_id is null
+      and u.role = #{permission,jdbcType=VARCHAR}
+    union
+    select
+      <include refid="selectColumns"/>
+    from organizations org
+    inner join group_roles g on
+      g.organization_uuid = org.uuid
+      and g.resource_id is null
+      and g.role = #{permission,jdbcType=VARCHAR}
+    inner join groups_users gu on
+      gu.group_id = g.group_id
+      and gu.user_id = #{userId,jdbcType=INTEGER}
+  </select>
+
   <insert id="insert" parameterType="Organization" useGeneratedKeys="false">
     insert into organizations
     (
index 7f4d8b79fc080dc7fc4a1d64af47f8df5173245a..23bcce30f6667d674fe3987edcde9e147b7aa740 100644 (file)
@@ -39,6 +39,8 @@ import org.sonar.api.utils.System2;
 import org.sonar.db.DbClient;
 import org.sonar.db.DbSession;
 import org.sonar.db.DbTester;
+import org.sonar.db.user.GroupDto;
+import org.sonar.db.user.UserDto;
 
 import static com.google.common.collect.ImmutableSet.of;
 import static java.util.Collections.singleton;
@@ -68,6 +70,8 @@ public class OrganizationDaoTest {
     .setDescription("the description 2")
     .setUrl("the url 2")
     .setAvatarUrl("the avatar url 2");
+  public static final String PERMISSION_1 = "foo";
+  public static final String PERMISSION_2 = "bar";
 
   private System2 system2 = mock(System2.class);
 
@@ -375,7 +379,7 @@ public class OrganizationDaoTest {
     insertOrganization(ORGANIZATION_DTO_1);
     insertOrganization(ORGANIZATION_DTO_2);
 
-    OrganizationQuery organizationQuery = newQueryWithKeys("foo", "bar", "dog");
+    OrganizationQuery organizationQuery = newQueryWithKeys(PERMISSION_1, PERMISSION_2, "dog");
     assertThat(underTest.selectByQuery(dbSession, organizationQuery, 0, 10))
       .isEmpty();
   }
@@ -385,7 +389,7 @@ public class OrganizationDaoTest {
     insertOrganization(ORGANIZATION_DTO_1);
     insertOrganization(ORGANIZATION_DTO_2);
 
-    OrganizationQuery organizationQuery = newQueryWithKeys(ORGANIZATION_DTO_1.getKey(), "foo", ORGANIZATION_DTO_2.getKey(), "bar", "dog");
+    OrganizationQuery organizationQuery = newQueryWithKeys(ORGANIZATION_DTO_1.getKey(), PERMISSION_1, ORGANIZATION_DTO_2.getKey(), PERMISSION_2, "dog");
     assertThat(underTest.selectByQuery(dbSession, organizationQuery, 0, 10))
       .hasSize(2)
       .extracting(OrganizationDto::getUuid)
@@ -460,22 +464,22 @@ public class OrganizationDaoTest {
   @Test
   public void getDefaultTemplates_returns_data_when_project_default_templates_column_is_not_null() {
     insertOrganization(ORGANIZATION_DTO_1);
-    underTest.setDefaultTemplates(dbSession, ORGANIZATION_DTO_1.getUuid(), new DefaultTemplates().setProjectUuid("foo"));
+    underTest.setDefaultTemplates(dbSession, ORGANIZATION_DTO_1.getUuid(), new DefaultTemplates().setProjectUuid(PERMISSION_1));
 
-    verifyGetDefaultTemplates(ORGANIZATION_DTO_1, "foo", null);
+    verifyGetDefaultTemplates(ORGANIZATION_DTO_1, PERMISSION_1, null);
   }
 
   @Test
   public void getDefaultTemplates_returns_data_when_project_and_view_default_template_column_are_not_null() {
     insertOrganization(ORGANIZATION_DTO_1);
-    setDefaultTemplate(ORGANIZATION_DTO_1, "foo", "bar");
+    setDefaultTemplate(ORGANIZATION_DTO_1, PERMISSION_1, PERMISSION_2);
 
-    verifyGetDefaultTemplates(ORGANIZATION_DTO_1, "foo", "bar");
+    verifyGetDefaultTemplates(ORGANIZATION_DTO_1, PERMISSION_1, PERMISSION_2);
   }
 
   @Test
   public void getDefaultTemplates_returns_empty_when_only_view_default_template_column_is_not_null() {
-    dirtyInsertWithDefaultTemplate("uuid1", null, "bar");
+    dirtyInsertWithDefaultTemplate("uuid1", null, PERMISSION_2);
 
     assertThat(underTest.getDefaultTemplates(dbSession, "uuid1"))
       .isEmpty();
@@ -492,7 +496,7 @@ public class OrganizationDaoTest {
   @Test
   public void getDefaultTemplates_is_case_sensitive() {
     insertOrganization(ORGANIZATION_DTO_1);
-    underTest.setDefaultTemplates(dbSession, ORGANIZATION_DTO_1.getUuid(), new DefaultTemplates().setProjectUuid("foo").setViewUuid("bar"));
+    underTest.setDefaultTemplates(dbSession, ORGANIZATION_DTO_1.getUuid(), new DefaultTemplates().setProjectUuid(PERMISSION_1).setViewUuid(PERMISSION_2));
 
     assertThat(underTest.getDefaultTemplates(dbSession, ORGANIZATION_DTO_1.getUuid().toUpperCase(Locale.ENGLISH)))
       .isEmpty();
@@ -527,7 +531,7 @@ public class OrganizationDaoTest {
     expectedException.expect(NullPointerException.class);
     expectedException.expectMessage("defaultTemplates.project can't be null");
 
-    underTest.setDefaultTemplates(dbSession, "uuid", new DefaultTemplates().setViewUuid("foo"));
+    underTest.setDefaultTemplates(dbSession, "uuid", new DefaultTemplates().setViewUuid(PERMISSION_1));
   }
 
   @Test
@@ -684,6 +688,134 @@ public class OrganizationDaoTest {
     assertThat(dbTester.countRowsOfTable("organizations")).isEqualTo(0);
   }
 
+  @Test
+  public void selectByPermission_returns_organization_when_user_has_ADMIN_user_permission_on_some_organization() {
+    UserDto user = dbTester.users().insertUser();
+    OrganizationDto organization1 = dbTester.organizations().insert();
+    dbTester.users().insertPermissionOnUser(organization1, user, PERMISSION_2);
+    OrganizationDto organization2 = dbTester.organizations().insert();
+    dbTester.users().insertPermissionOnUser(organization2, user, PERMISSION_2);
+    UserDto otherUser = dbTester.users().insertUser();
+    OrganizationDto organization3 = dbTester.organizations().insert();
+    dbTester.users().insertPermissionOnUser(organization3, otherUser, PERMISSION_2);
+
+    assertThat(underTest.selectByPermission(dbSession, user.getId().intValue(), PERMISSION_2))
+      .extracting(OrganizationDto::getUuid)
+      .containsOnly(organization1.getUuid(), organization2.getUuid());
+
+    assertThat(underTest.selectByPermission(dbSession, otherUser.getId().intValue(), PERMISSION_2))
+      .extracting(OrganizationDto::getUuid)
+      .containsOnly(organization3.getUuid());
+
+    assertThat(underTest.selectByPermission(dbSession, 1234, PERMISSION_2))
+      .isEmpty();
+  }
+
+  @Test
+  public void selectByPermission_returns_organization_when_user_has_ADMIN_group_permission_on_some_organization() {
+    UserDto user = dbTester.users().insertUser();
+    OrganizationDto organization1 = dbTester.organizations().insert();
+    GroupDto defaultGroup = dbTester.users().insertGroup(organization1);
+    dbTester.users().insertPermissionOnGroup(defaultGroup, PERMISSION_1);
+    dbTester.users().insertMember(defaultGroup, user);
+    OrganizationDto organization2 = dbTester.organizations().insert();
+    GroupDto group1 = dbTester.users().insertGroup(organization2);
+    dbTester.users().insertPermissionOnGroup(group1, PERMISSION_1);
+    dbTester.users().insertMember(group1, user);
+    UserDto otherUser = dbTester.users().insertUser();
+    OrganizationDto organization3 = dbTester.organizations().insert();
+    GroupDto group2 = dbTester.users().insertGroup(organization3);
+    dbTester.users().insertPermissionOnGroup(group2, PERMISSION_1);
+    dbTester.users().insertMember(group2, otherUser);
+
+    assertThat(underTest.selectByPermission(dbSession, user.getId().intValue(), PERMISSION_1))
+      .extracting(OrganizationDto::getUuid)
+      .containsOnly(organization1.getUuid(), organization2.getUuid());
+
+    assertThat(underTest.selectByPermission(dbSession, otherUser.getId().intValue(), PERMISSION_1))
+      .extracting(OrganizationDto::getUuid)
+      .containsOnly(organization3.getUuid());
+
+    assertThat(underTest.selectByPermission(dbSession, 1234, PERMISSION_1))
+      .isEmpty();
+  }
+
+  @Test
+  public void selectByPermission_return_organization_only_once_even_if_user_has_ADMIN_permission_twice_or_more() {
+    String permission = "destroy";
+    UserDto user = dbTester.users().insertUser();
+    OrganizationDto organization = dbTester.organizations().insert();
+    GroupDto group1 = dbTester.users().insertGroup(organization);
+    dbTester.users().insertPermissionOnGroup(group1, permission);
+    dbTester.users().insertMember(group1, user);
+    GroupDto group2 = dbTester.users().insertGroup(organization);
+    dbTester.users().insertPermissionOnGroup(group2, permission);
+    dbTester.users().insertMember(group2, user);
+    dbTester.users().insertPermissionOnUser(organization, user, permission);
+
+    assertThat(underTest.selectByPermission(dbSession, user.getId().intValue(), permission))
+      .extracting(OrganizationDto::getUuid)
+      .containsOnlyOnce(organization.getUuid());
+  }
+
+  @Test
+  public void selectByPermission_returns_organization_only_if_user_has_specific_permission_by_user_permission() {
+    OrganizationDto organization = dbTester.organizations().insert();
+    OrganizationDto otherOrganization = dbTester.organizations().insert();
+    UserDto user = dbTester.users().insertUser();
+    dbTester.users().insertPermissionOnUser(organization, user, PERMISSION_1);
+    dbTester.users().insertPermissionOnUser(otherOrganization, user, PERMISSION_2);
+    UserDto otherUser = dbTester.users().insertUser();
+    dbTester.users().insertPermissionOnUser(organization, otherUser, PERMISSION_2);
+    dbTester.users().insertPermissionOnUser(otherOrganization, otherUser, PERMISSION_1);
+
+    assertThat(underTest.selectByPermission(dbSession, user.getId().intValue(), PERMISSION_1))
+      .extracting(OrganizationDto::getUuid)
+      .containsOnlyOnce(organization.getUuid());
+    assertThat(underTest.selectByPermission(dbSession, user.getId().intValue(), PERMISSION_2))
+      .extracting(OrganizationDto::getUuid)
+      .containsOnlyOnce(otherOrganization.getUuid());
+    assertThat(underTest.selectByPermission(dbSession, otherUser.getId().intValue(), PERMISSION_1))
+      .extracting(OrganizationDto::getUuid)
+      .containsOnlyOnce(otherOrganization.getUuid());
+    assertThat(underTest.selectByPermission(dbSession, otherUser.getId().intValue(), PERMISSION_2))
+      .extracting(OrganizationDto::getUuid)
+      .containsOnlyOnce(organization.getUuid());
+  }
+
+  @Test
+  public void selectByPermission_returns_organization_only_if_user_has_specific_permission_by_group_permission() {
+    OrganizationDto organization = dbTester.organizations().insert();
+    OrganizationDto otherOrganization = dbTester.organizations().insert();
+    GroupDto group1 = dbTester.users().insertGroup(organization);
+    GroupDto group2 = dbTester.users().insertGroup(organization);
+    GroupDto otherGroup1 = dbTester.users().insertGroup(otherOrganization);
+    GroupDto otherGroup2 = dbTester.users().insertGroup(otherOrganization);
+    dbTester.users().insertPermissionOnGroup(group1, PERMISSION_1);
+    dbTester.users().insertPermissionOnGroup(otherGroup2, PERMISSION_2);
+    dbTester.users().insertPermissionOnGroup(group2, PERMISSION_2);
+    dbTester.users().insertPermissionOnGroup(otherGroup1, PERMISSION_1);
+    UserDto user = dbTester.users().insertUser();
+    dbTester.users().insertMember(group1, user);
+    dbTester.users().insertMember(otherGroup2, user);
+    UserDto otherUser = dbTester.users().insertUser();
+    dbTester.users().insertMember(group2, otherUser);
+    dbTester.users().insertMember(otherGroup1, otherUser);
+
+    assertThat(underTest.selectByPermission(dbSession, user.getId().intValue(), PERMISSION_1))
+      .extracting(OrganizationDto::getUuid)
+      .containsOnlyOnce(organization.getUuid());
+    assertThat(underTest.selectByPermission(dbSession, user.getId().intValue(), PERMISSION_2))
+      .extracting(OrganizationDto::getUuid)
+      .containsOnlyOnce(otherOrganization.getUuid());
+    assertThat(underTest.selectByPermission(dbSession, otherUser.getId().intValue(), PERMISSION_1))
+      .extracting(OrganizationDto::getUuid)
+      .containsOnlyOnce(otherOrganization.getUuid());
+    assertThat(underTest.selectByPermission(dbSession, otherUser.getId().intValue(), PERMISSION_2))
+      .extracting(OrganizationDto::getUuid)
+      .containsOnlyOnce(organization.getUuid());
+  }
+
   private void expectDtoCanNotBeNull() {
     expectedException.expect(NullPointerException.class);
     expectedException.expectMessage("OrganizationDto can't be null");