]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-8134 add dao to verify authorizations
authorSimon Brandhof <simon.brandhof@sonarsource.com>
Fri, 14 Oct 2016 05:54:54 +0000 (07:54 +0200)
committerSimon Brandhof <simon.brandhof@sonarsource.com>
Sun, 16 Oct 2016 17:10:44 +0000 (19:10 +0200)
server/sonar-ce/src/test/java/org/sonar/ce/container/ComputeEngineContainerImplTest.java
sonar-db/src/main/java/org/sonar/db/DaoModule.java
sonar-db/src/main/java/org/sonar/db/DbClient.java
sonar-db/src/main/java/org/sonar/db/MyBatis.java
sonar-db/src/main/java/org/sonar/db/permission/AuthorizationDao.java [new file with mode: 0644]
sonar-db/src/main/java/org/sonar/db/permission/AuthorizationMapper.java [new file with mode: 0644]
sonar-db/src/main/java/org/sonar/db/permission/PermissionMapper.java
sonar-db/src/main/java/org/sonar/db/permission/PermissionRepository.java
sonar-db/src/main/resources/org/sonar/db/permission/AuthorizationMapper.xml [new file with mode: 0644]
sonar-db/src/test/java/org/sonar/db/DaoModuleTest.java
sonar-db/src/test/java/org/sonar/db/permission/AuthorizationDaoTest.java [new file with mode: 0644]

index f46233de17873f0db60d592b6c8bf7011d1057bc..444c3ae5d9a6e8f17e1e3c933391b56e5d8888f5 100644 (file)
@@ -106,7 +106,7 @@ public class ComputeEngineContainerImplTest {
     assertThat(picoContainer.getParent().getParent().getParent().getComponentAdapters()).hasSize(
       COMPONENTS_IN_LEVEL_1_AT_CONSTRUCTION
         + 24 // level 1
-        + 52 // content of DaoModule
+        + 53 // content of DaoModule
         + 2 // content of EsSearchModule
         + 62 // content of CorePropertyDefinitions
         + 1 // content of CePropertyDefinitions
index f607cd29d4dbe40cbf6b224bc79ecb6807a4f2ea..35b3ec918546ecfac37d4bb690296313941a4031 100644 (file)
@@ -50,6 +50,7 @@ import org.sonar.db.measure.custom.CustomMeasureDao;
 import org.sonar.db.metric.MetricDao;
 import org.sonar.db.notification.NotificationQueueDao;
 import org.sonar.db.organization.OrganizationDao;
+import org.sonar.db.permission.AuthorizationDao;
 import org.sonar.db.permission.GroupPermissionDao;
 import org.sonar.db.permission.UserPermissionDao;
 import org.sonar.db.permission.template.PermissionTemplateCharacteristicDao;
@@ -79,6 +80,7 @@ public class DaoModule extends Module {
   private static final List<Class<? extends Dao>> classes = ImmutableList.<Class<? extends Dao>>builder().add(
     ActiveDashboardDao.class,
     AuthorDao.class,
+    AuthorizationDao.class,
     PermissionDao.class,
     CeActivityDao.class,
     CeQueueDao.class,
index a86820b115da191c158d669ac1137769206f4dbd..ac37cfe50f82f921100c8426eda810db04eee138 100644 (file)
@@ -50,6 +50,7 @@ import org.sonar.db.measure.custom.CustomMeasureDao;
 import org.sonar.db.metric.MetricDao;
 import org.sonar.db.notification.NotificationQueueDao;
 import org.sonar.db.organization.OrganizationDao;
+import org.sonar.db.permission.AuthorizationDao;
 import org.sonar.db.permission.GroupPermissionDao;
 import org.sonar.db.permission.PermissionDao;
 import org.sonar.db.permission.UserPermissionDao;
@@ -79,6 +80,7 @@ public class DbClient {
 
   private final Database database;
   private final MyBatis myBatis;
+  private final AuthorizationDao authorizationDao;
   private final OrganizationDao organizationDao;
   private final QualityProfileDao qualityProfileDao;
   private final LoadedTemplateDao loadedTemplateDao;
@@ -140,6 +142,7 @@ public class DbClient {
     for (Dao dao : daos) {
       map.put(dao.getClass(), dao);
     }
+    authorizationDao = getDao(map, AuthorizationDao.class);
     organizationDao = getDao(map, OrganizationDao.class);
     qualityProfileDao = getDao(map, QualityProfileDao.class);
     loadedTemplateDao = getDao(map, LoadedTemplateDao.class);
@@ -206,6 +209,10 @@ public class DbClient {
     return database;
   }
 
+  public AuthorizationDao authorizationDao() {
+    return authorizationDao;
+  }
+
   public OrganizationDao organizationDao() {
     return organizationDao;
   }
index 66ff9549f003fcd437cebe5f75e782d129e1c466..a7c75e9b5ca5c03542a8a35732dd07961dc85aeb 100644 (file)
@@ -84,6 +84,7 @@ import org.sonar.db.notification.NotificationQueueDto;
 import org.sonar.db.notification.NotificationQueueMapper;
 import org.sonar.db.organization.OrganizationDto;
 import org.sonar.db.organization.OrganizationMapper;
+import org.sonar.db.permission.AuthorizationMapper;
 import org.sonar.db.permission.GroupPermissionDto;
 import org.sonar.db.permission.GroupPermissionMapper;
 import org.sonar.db.permission.PermissionMapper;
@@ -228,6 +229,7 @@ public class MyBatis {
       ActiveDashboardMapper.class,
       ActiveRuleMapper.class,
       AuthorMapper.class,
+      AuthorizationMapper.class,
       CeActivityMapper.class,
       CeQueueMapper.class,
       CeScannerContextMapper.class,
diff --git a/sonar-db/src/main/java/org/sonar/db/permission/AuthorizationDao.java b/sonar-db/src/main/java/org/sonar/db/permission/AuthorizationDao.java
new file mode 100644 (file)
index 0000000..3f2f32d
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * 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.db.permission;
+
+import java.util.Set;
+import org.sonar.db.Dao;
+import org.sonar.db.DbSession;
+
+/**
+ * The SQL requests used to verify authorization (the permissions
+ * granted to users)
+ *
+ * @see GroupPermissionDao for CRUD of table group_roles
+ * @see UserPermissionDao for CRUD of table user_roles
+ */
+public class AuthorizationDao implements Dao {
+
+  /**
+   * Loads all the permissions granted to logged-in user for the specified organization
+   */
+  public Set<String> selectOrganizationPermissions(DbSession dbSession, String organizationUuid, long userId) {
+    return mapper(dbSession).selectOrganizationPermissions(organizationUuid, userId);
+  }
+
+  /**
+   * Loads all the permissions granted to anonymous user for the specified organization
+   */
+  public Set<String> selectOrganizationPermissionsOfAnonymous(DbSession dbSession, String organizationUuid) {
+    return mapper(dbSession).selectOrganizationPermissionsOfAnonymous(organizationUuid);
+  }
+
+  /**
+   * Loads all the permissions granted to logged-in user for the specified root component (project)
+   */
+  public Set<String> selectRootComponentPermissions(DbSession dbSession, long rootComponentId, long userId) {
+    return mapper(dbSession).selectRootComponentPermissions(rootComponentId, userId);
+  }
+
+  /**
+   * Loads all the permissions granted to anonymous user for the specified root component (project)
+   */
+  public Set<String> selectRootComponentPermissionsOfAnonymous(DbSession dbSession, long rootComponentId) {
+    return mapper(dbSession).selectRootComponentPermissionsOfAnonymous(rootComponentId);
+  }
+
+  private static AuthorizationMapper mapper(DbSession dbSession) {
+    return dbSession.getMapper(AuthorizationMapper.class);
+  }
+}
diff --git a/sonar-db/src/main/java/org/sonar/db/permission/AuthorizationMapper.java b/sonar-db/src/main/java/org/sonar/db/permission/AuthorizationMapper.java
new file mode 100644 (file)
index 0000000..fcf9d12
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * 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.db.permission;
+
+import java.util.Set;
+import org.apache.ibatis.annotations.Param;
+
+/**
+ * @see AuthorizationDao
+ */
+public interface AuthorizationMapper {
+
+  Set<String> selectOrganizationPermissions(@Param("organizationUuid") String organizationUuid,
+    @Param("userId") long userId);
+
+  Set<String> selectOrganizationPermissionsOfAnonymous(@Param("organizationUuid") String organizationUuid);
+
+  Set<String> selectRootComponentPermissions(@Param("rootComponentId") long rootComponentId, @Param("userId") long userId);
+
+  Set<String> selectRootComponentPermissionsOfAnonymous(@Param("rootComponentId") long rootComponentId);
+
+}
index d8d1c029ef1f6b6bb69bd0ddeb9674b2cfd0e2f3..a24d3e6b2ace0354b7dcb58ae1067a85d2011f16 100644 (file)
@@ -26,8 +26,8 @@ import org.apache.ibatis.annotations.Param;
 /**
  * Only the requests involving both user and group permissions.
  *
- * @see GroupPermissionMapper
- * @see UserPermissionMapper
+ * @see GroupPermissionMapper for CRUD of table group_roles
+ * @see UserPermissionMapper for CRUD of table user_roles
  */
 public interface PermissionMapper {
 
index d909d0ff0fe16699bb1a9d9b4af5601e6f61ee47..f9f00b66b074bea0c60a251f4788bc3c8abea8da 100644 (file)
@@ -56,15 +56,6 @@ public class PermissionRepository {
     this.settings = settings;
   }
 
-  /**
-   * For each modification of permission on a project, update the authorization_updated_at to help ES reindex only relevant changes
-   */
-  private void updateProjectAuthorizationDate(DbSession session, @Nullable Long projectId) {
-    if (projectId != null) {
-      dbClient.resourceDao().updateAuthorizationDate(projectId, session);
-    }
-  }
-
   public void applyPermissionTemplate(DbSession session, String templateUuid, ComponentDto project) {
     applyPermissionTemplate(session, templateUuid, project, null);
   }
@@ -109,6 +100,13 @@ public class PermissionRepository {
     }
   }
 
+  /**
+   * For each modification of permission on a project, update the authorization_updated_at to help ES reindex only relevant changes
+   */
+  private void updateProjectAuthorizationDate(DbSession dbSession, long projectId) {
+    dbClient.resourceDao().updateAuthorizationDate(projectId, dbSession);
+  }
+
   /**
    * Warning, this method is also used by the Developer Cockpit plugin
    */
@@ -127,6 +125,7 @@ public class PermissionRepository {
    * permission template for the resource qualifier.
    */
   private String getApplicablePermissionTemplateKey(DbSession session, final String componentKey, String qualifier) {
+    // FIXME performance issue here, we should not load all templates
     List<PermissionTemplateDto> allPermissionTemplates = dbClient.permissionTemplateDao().selectAll(session);
     List<PermissionTemplateDto> matchingTemplates = new ArrayList<>();
     for (PermissionTemplateDto permissionTemplateDto : allPermissionTemplates) {
diff --git a/sonar-db/src/main/resources/org/sonar/db/permission/AuthorizationMapper.xml b/sonar-db/src/main/resources/org/sonar/db/permission/AuthorizationMapper.xml
new file mode 100644 (file)
index 0000000..6a05e22
--- /dev/null
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+
+<mapper namespace="org.sonar.db.permission.AuthorizationMapper">
+
+  <select id="selectOrganizationPermissions" parameterType="map" resultType="string">
+    select gr.role
+    from group_roles gr
+    where
+    gr.organization_uuid=#{organizationUuid,jdbcType=VARCHAR} and
+    gr.resource_id is null and
+    (gr.group_id is null or gr.group_id in (select gu.group_id from groups_users gu where gu.user_id=#{userId,jdbcType=BIGINT}))
+    union
+    select ur.role
+    from user_roles ur
+    where
+    ur.organization_uuid=#{organizationUuid,jdbcType=VARCHAR} and
+    ur.user_id=#{userId,jdbcType=BIGINT}
+  </select>
+
+  <select id="selectOrganizationPermissionsOfAnonymous" parameterType="map" resultType="string">
+    select gr.role
+    from group_roles gr
+    where
+    gr.organization_uuid=#{organizationUuid,jdbcType=VARCHAR} and
+    gr.resource_id is null and
+    gr.group_id is null
+  </select>
+
+  <select id="selectRootComponentPermissions" parameterType="map" resultType="string">
+    select gr.role
+    from group_roles gr
+    where
+    gr.resource_id=#{rootComponentId,jdbcType=BIGINT} and
+    (gr.group_id is null or gr.group_id in (select gu.group_id from groups_users gu where gu.user_id=#{userId,jdbcType=BIGINT}))
+    union
+    select ur.role
+    from user_roles ur
+    where
+    ur.resource_id=#{rootComponentId,jdbcType=BIGINT} and
+    ur.user_id=#{userId,jdbcType=BIGINT}
+  </select>
+
+  <select id="selectRootComponentPermissionsOfAnonymous" parameterType="map" resultType="string">
+    select gr.role
+    from group_roles gr
+    where
+    gr.resource_id=#{rootComponentId,jdbcType=BIGINT} and
+    gr.group_id is null
+  </select>
+
+</mapper>
index a87ffc547e03d0c6667db4c47b68edd2043edd18..aa47872bb1cb8d1e0e526aa5d216b6d040ecfece 100644 (file)
@@ -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 + 52);
+    assertThat(container.size()).isEqualTo(2 + 53);
   }
 }
diff --git a/sonar-db/src/test/java/org/sonar/db/permission/AuthorizationDaoTest.java b/sonar-db/src/test/java/org/sonar/db/permission/AuthorizationDaoTest.java
new file mode 100644 (file)
index 0000000..7599aea
--- /dev/null
@@ -0,0 +1,146 @@
+/*
+ * 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.db.permission;
+
+import java.util.Set;
+import org.junit.Before;
+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.component.ComponentDto;
+import org.sonar.db.organization.OrganizationDto;
+import org.sonar.db.organization.OrganizationTesting;
+import org.sonar.db.user.GroupDto;
+import org.sonar.db.user.UserDto;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.sonar.db.organization.OrganizationTesting.newOrganizationDto;
+
+public class AuthorizationDaoTest {
+
+  @Rule
+  public DbTester db = DbTester.create(System2.INSTANCE);
+  private DbSession dbSession = db.getSession();
+  private AuthorizationDao underTest = new AuthorizationDao();
+  private OrganizationDto org;
+  private UserDto user;
+  private GroupDto group1;
+  private GroupDto group2;
+
+  @Before
+  public void setUp() throws Exception {
+    org = OrganizationTesting.insert(db, newOrganizationDto());
+    user = db.users().insertUser();
+    group1 = db.users().insertGroup(org, "group1");
+    group2 = db.users().insertGroup(org, "group2");
+  }
+
+  /**
+   * Union of the permissions granted to:
+   * - the user
+   * - the groups which user is member
+   * - anyone
+   */
+  @Test
+  public void selectOrganizationPermissions_for_logged_in_user() {
+    db.users().insertMember(group1, user);
+    db.users().insertPermissionOnUser(org, user, "perm1");
+    db.users().insertPermissionOnGroup(group1, "perm2");
+    db.users().insertPermissionOnAnyone(org, "perm3");
+
+    // ignored permissions, user is not member of this group
+    db.users().insertPermissionOnGroup(group2, "ignored");
+
+    Set<String> permissions = underTest.selectOrganizationPermissions(dbSession, org.getUuid(), user.getId());
+
+    assertThat(permissions).containsOnly("perm1", "perm2", "perm3");
+  }
+
+  /**
+   * Anonymous user only benefits from the permissions granted to
+   * "Anyone"
+   */
+  @Test
+  public void selectOrganizationPermissions_for_anonymous_user() {
+    db.users().insertPermissionOnAnyone(org, "perm1");
+
+    // ignored permissions
+    db.users().insertPermissionOnUser(org, user, "ignored");
+    db.users().insertPermissionOnGroup(group1, "ignored");
+
+    Set<String> permissions = underTest.selectOrganizationPermissionsOfAnonymous(dbSession, org.getUuid());
+
+    assertThat(permissions).containsOnly("perm1");
+  }
+
+  /**
+   * Union of the permissions granted to:
+   * - the user
+   * - the groups which user is member
+   * - anyone
+   */
+  @Test
+  public void selectRootComponentPermissions_for_logged_in_user() {
+    db.users().insertMember(group1, user);
+    ComponentDto project1 = db.components().insertProject();
+    db.users().insertProjectPermissionOnAnyone("perm1", project1);
+    db.users().insertProjectPermissionOnGroup(group1, "perm2", project1);
+    db.users().insertProjectPermissionOnUser(user, "perm3", project1);
+
+    // ignored permissions
+    db.users().insertPermissionOnAnyone(org, "ignored");
+    db.users().insertPermissionOnGroup(group2, "ignored");
+    ComponentDto project2 = db.components().insertProject();
+
+    Set<String> permissions = underTest.selectRootComponentPermissions(dbSession, project1.getId(), user.getId());
+    assertThat(permissions).containsOnly("perm1", "perm2", "perm3");
+
+    // non granted project
+    permissions = underTest.selectRootComponentPermissions(dbSession, project2.getId(), user.getId());
+    assertThat(permissions).isEmpty();
+  }
+
+  /**
+   * Anonymous user only benefits from the permissions granted to
+   * "Anyone"
+   */
+  @Test
+  public void selectRootComponentPermissions_for_anonymous_user() {
+    ComponentDto project1 = db.components().insertProject();
+    db.users().insertProjectPermissionOnAnyone("perm1", project1);
+
+    // ignored permissions
+    db.users().insertPermissionOnAnyone(org, "ignored");
+    db.users().insertPermissionOnUser(org, user, "ignored");
+    db.users().insertPermissionOnGroup(group1, "ignored");
+    ComponentDto project2 = db.components().insertProject();
+    db.users().insertProjectPermissionOnGroup(group1, "ignored", project2);
+
+    Set<String> permissions = underTest.selectRootComponentPermissionsOfAnonymous(dbSession, project1.getId());
+    assertThat(permissions).containsOnly("perm1");
+
+    // non granted project
+    permissions = underTest.selectRootComponentPermissionsOfAnonymous(dbSession, project2.getId());
+    assertThat(permissions).isEmpty();
+  }
+
+}