diff options
author | Simon Brandhof <simon.brandhof@sonarsource.com> | 2016-10-14 07:54:54 +0200 |
---|---|---|
committer | Simon Brandhof <simon.brandhof@sonarsource.com> | 2016-10-16 19:10:44 +0200 |
commit | bf7b10fc5ca07faa4666836c2c53fd40febc46bb (patch) | |
tree | a02ee68473d7754dcd5e64c8a608b3d569c4092d /sonar-db | |
parent | 8a475eff47709d959f096026abf0df8e0da48e47 (diff) | |
download | sonarqube-bf7b10fc5ca07faa4666836c2c53fd40febc46bb.tar.gz sonarqube-bf7b10fc5ca07faa4666836c2c53fd40febc46bb.zip |
SONAR-8134 add dao to verify authorizations
Diffstat (limited to 'sonar-db')
10 files changed, 325 insertions, 12 deletions
diff --git a/sonar-db/src/main/java/org/sonar/db/DaoModule.java b/sonar-db/src/main/java/org/sonar/db/DaoModule.java index f607cd29d4d..35b3ec91854 100644 --- a/sonar-db/src/main/java/org/sonar/db/DaoModule.java +++ b/sonar-db/src/main/java/org/sonar/db/DaoModule.java @@ -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, diff --git a/sonar-db/src/main/java/org/sonar/db/DbClient.java b/sonar-db/src/main/java/org/sonar/db/DbClient.java index a86820b115d..ac37cfe50f8 100644 --- a/sonar-db/src/main/java/org/sonar/db/DbClient.java +++ b/sonar-db/src/main/java/org/sonar/db/DbClient.java @@ -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; } diff --git a/sonar-db/src/main/java/org/sonar/db/MyBatis.java b/sonar-db/src/main/java/org/sonar/db/MyBatis.java index 66ff9549f00..a7c75e9b5ca 100644 --- a/sonar-db/src/main/java/org/sonar/db/MyBatis.java +++ b/sonar-db/src/main/java/org/sonar/db/MyBatis.java @@ -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 index 00000000000..3f2f32da72e --- /dev/null +++ b/sonar-db/src/main/java/org/sonar/db/permission/AuthorizationDao.java @@ -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 index 00000000000..fcf9d1274c7 --- /dev/null +++ b/sonar-db/src/main/java/org/sonar/db/permission/AuthorizationMapper.java @@ -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); + +} diff --git a/sonar-db/src/main/java/org/sonar/db/permission/PermissionMapper.java b/sonar-db/src/main/java/org/sonar/db/permission/PermissionMapper.java index d8d1c029ef1..a24d3e6b2ac 100644 --- a/sonar-db/src/main/java/org/sonar/db/permission/PermissionMapper.java +++ b/sonar-db/src/main/java/org/sonar/db/permission/PermissionMapper.java @@ -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 { diff --git a/sonar-db/src/main/java/org/sonar/db/permission/PermissionRepository.java b/sonar-db/src/main/java/org/sonar/db/permission/PermissionRepository.java index d909d0ff0fe..f9f00b66b07 100644 --- a/sonar-db/src/main/java/org/sonar/db/permission/PermissionRepository.java +++ b/sonar-db/src/main/java/org/sonar/db/permission/PermissionRepository.java @@ -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); } @@ -110,6 +101,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 */ public void applyDefaultPermissionTemplate(DbSession session, long componentId) { @@ -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 index 00000000000..6a05e22892d --- /dev/null +++ b/sonar-db/src/main/resources/org/sonar/db/permission/AuthorizationMapper.xml @@ -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> diff --git a/sonar-db/src/test/java/org/sonar/db/DaoModuleTest.java b/sonar-db/src/test/java/org/sonar/db/DaoModuleTest.java index a87ffc547e0..aa47872bb1c 100644 --- a/sonar-db/src/test/java/org/sonar/db/DaoModuleTest.java +++ b/sonar-db/src/test/java/org/sonar/db/DaoModuleTest.java @@ -29,6 +29,6 @@ public class DaoModuleTest { public void verify_count_of_added_components() { ComponentContainer container = new ComponentContainer(); new DaoModule().configure(container); - assertThat(container.size()).isEqualTo(2 + 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 index 00000000000..7599aea951b --- /dev/null +++ b/sonar-db/src/test/java/org/sonar/db/permission/AuthorizationDaoTest.java @@ -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(); + } + +} |