From b74d7b96bcfbc0c993945b1e821ae79f2caa451e Mon Sep 17 00:00:00 2001 From: Julien Lancelot Date: Thu, 4 Sep 2014 17:56:17 +0200 Subject: [PATCH] SONAR-5590 Update projects.authorization_updated_at on project permission changes --- .../server/issue/ServerIssueStorageTest.java | 9 ++-- .../measure/MeasureFilterExecutorTest.java | 3 +- .../batch/issue/ScanIssueStorageTest.java | 3 +- .../core/permission/PermissionFacade.java | 44 ++++++++++++++----- .../org/sonar/core/resource/ResourceDao.java | 26 ++++++++++- .../org/sonar/core/resource/ResourceDto.java | 10 +++++ .../sonar/core/resource/ResourceMapper.java | 3 ++ .../sonar/core/resource/ResourceMapper.xml | 16 +++++-- .../core/permission/PermissionFacadeTest.java | 18 +++++++- .../org/sonar/core/purge/PurgeDaoTest.java | 2 +- .../DefaultResourcePermissionsTest.java | 2 +- .../sonar/core/resource/ResourceDaoTest.java | 23 +++++++++- .../org/sonar/core/user/AuthorDaoTest.java | 3 +- .../should_add_user_permission-result.xml | 6 ++- .../should_add_user_permission.xml | 6 ++- .../should_apply_permission_template.xml | 4 ++ .../should_delete_group_permission-result.xml | 6 ++- .../should_delete_group_permission.xml | 6 ++- .../should_delete_user_permission-result.xml | 6 ++- .../should_delete_user_permission.xml | 6 ++- ..._insert_anyone_group_permission-result.xml | 5 +++ .../should_insert_anyone_group_permission.xml | 7 ++- .../should_insert_group_permission-result.xml | 6 ++- .../should_insert_group_permission.xml | 6 ++- .../core/resource/ResourceDaoTest/fixture.xml | 12 +++-- .../ResourceDaoTest/insert-result.xml | 8 ++-- .../update_authorization_date-result.xml | 8 ++++ .../update_authorization_date.xml | 8 ++++ 28 files changed, 218 insertions(+), 44 deletions(-) create mode 100644 sonar-core/src/test/resources/org/sonar/core/resource/ResourceDaoTest/update_authorization_date-result.xml create mode 100644 sonar-core/src/test/resources/org/sonar/core/resource/ResourceDaoTest/update_authorization_date.xml diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/ServerIssueStorageTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/ServerIssueStorageTest.java index 03978792abd..374d515ff28 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/issue/ServerIssueStorageTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/issue/ServerIssueStorageTest.java @@ -26,6 +26,7 @@ import org.sonar.api.rule.RuleKey; import org.sonar.api.rules.Rule; import org.sonar.api.rules.RuleFinder; import org.sonar.api.rules.RuleQuery; +import org.sonar.api.utils.System2; import org.sonar.core.persistence.AbstractDaoTestCase; import org.sonar.core.resource.ResourceDao; @@ -40,7 +41,7 @@ public class ServerIssueStorageTest extends AbstractDaoTestCase { public void load_component_id_from_db() throws Exception { setupData("load_component_id_from_db"); - ServerIssueStorage storage = new ServerIssueStorage(getMyBatis(), new FakeRuleFinder(), new ResourceDao(getMyBatis())); + ServerIssueStorage storage = new ServerIssueStorage(getMyBatis(), new FakeRuleFinder(), new ResourceDao(getMyBatis(), System2.INSTANCE)); long componentId = storage.componentId(new DefaultIssue().setComponentKey("struts:Action.java")); assertThat(componentId).isEqualTo(123); @@ -50,7 +51,7 @@ public class ServerIssueStorageTest extends AbstractDaoTestCase { public void fail_to_load_component_id_if_unknown_component() throws Exception { setupData("empty"); - ServerIssueStorage storage = new ServerIssueStorage(getMyBatis(), new FakeRuleFinder(), new ResourceDao(getMyBatis())); + ServerIssueStorage storage = new ServerIssueStorage(getMyBatis(), new FakeRuleFinder(), new ResourceDao(getMyBatis(), System2.INSTANCE)); try { storage.componentId(new DefaultIssue().setComponentKey("struts:Action.java")); fail(); @@ -63,7 +64,7 @@ public class ServerIssueStorageTest extends AbstractDaoTestCase { public void load_project_id_from_db() throws Exception { setupData("load_project_id_from_db"); - ServerIssueStorage storage = new ServerIssueStorage(getMyBatis(), new FakeRuleFinder(), new ResourceDao(getMyBatis())); + ServerIssueStorage storage = new ServerIssueStorage(getMyBatis(), new FakeRuleFinder(), new ResourceDao(getMyBatis(), System2.INSTANCE)); long projectId = storage.projectId(new DefaultIssue().setProjectKey("struts")); assertThat(projectId).isEqualTo(1); @@ -73,7 +74,7 @@ public class ServerIssueStorageTest extends AbstractDaoTestCase { public void fail_to_load_project_id_if_unknown_component() throws Exception { setupData("empty"); - ServerIssueStorage storage = new ServerIssueStorage(getMyBatis(), new FakeRuleFinder(), new ResourceDao(getMyBatis())); + ServerIssueStorage storage = new ServerIssueStorage(getMyBatis(), new FakeRuleFinder(), new ResourceDao(getMyBatis(), System2.INSTANCE)); try { storage.projectId(new DefaultIssue().setProjectKey("struts")); fail(); diff --git a/server/sonar-server/src/test/java/org/sonar/server/measure/MeasureFilterExecutorTest.java b/server/sonar-server/src/test/java/org/sonar/server/measure/MeasureFilterExecutorTest.java index 8edc2aa6b5a..527b782b6ce 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/measure/MeasureFilterExecutorTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/measure/MeasureFilterExecutorTest.java @@ -25,6 +25,7 @@ import org.junit.Test; import org.sonar.api.measures.CoreMetrics; import org.sonar.api.measures.Metric; import org.sonar.api.utils.DateUtils; +import org.sonar.api.utils.System2; import org.sonar.core.persistence.TestDatabase; import org.sonar.core.resource.ResourceDao; import org.sonar.core.resource.SnapshotDto; @@ -59,7 +60,7 @@ public class MeasureFilterExecutorTest { @Before public void before() { - executor = new MeasureFilterExecutor(db.myBatis(), db.database(), new ResourceDao(db.myBatis())); + executor = new MeasureFilterExecutor(db.myBatis(), db.database(), new ResourceDao(db.myBatis(), System2.INSTANCE)); } @Test diff --git a/sonar-batch/src/test/java/org/sonar/batch/issue/ScanIssueStorageTest.java b/sonar-batch/src/test/java/org/sonar/batch/issue/ScanIssueStorageTest.java index 8866851b3da..1d0144ce4ee 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/issue/ScanIssueStorageTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/issue/ScanIssueStorageTest.java @@ -31,6 +31,7 @@ import org.sonar.api.rule.RuleKey; import org.sonar.api.rules.Rule; import org.sonar.api.rules.RuleFinder; import org.sonar.api.rules.RuleQuery; +import org.sonar.api.utils.System2; import org.sonar.batch.ProjectTree; import org.sonar.batch.index.SnapshotCache; import org.sonar.core.persistence.AbstractDaoTestCase; @@ -55,7 +56,7 @@ public class ScanIssueStorageTest extends AbstractDaoTestCase { @Before public void setUp() throws Exception { - storage = new ScanIssueStorage(getMyBatis(), new FakeRuleFinder(), snapshotCache, new ResourceDao(getMyBatis()), projectTree); + storage = new ScanIssueStorage(getMyBatis(), new FakeRuleFinder(), snapshotCache, new ResourceDao(getMyBatis(), System2.INSTANCE), projectTree); } @Test diff --git a/sonar-core/src/main/java/org/sonar/core/permission/PermissionFacade.java b/sonar-core/src/main/java/org/sonar/core/permission/PermissionFacade.java index 9c2f39c4fcf..f8956373d00 100644 --- a/sonar-core/src/main/java/org/sonar/core/permission/PermissionFacade.java +++ b/sonar-core/src/main/java/org/sonar/core/permission/PermissionFacade.java @@ -30,11 +30,7 @@ import org.sonar.api.task.TaskComponent; import org.sonar.core.persistence.MyBatis; import org.sonar.core.resource.ResourceDao; import org.sonar.core.resource.ResourceDto; -import org.sonar.core.user.GroupDto; -import org.sonar.core.user.GroupRoleDto; -import org.sonar.core.user.RoleDao; -import org.sonar.core.user.UserDao; -import org.sonar.core.user.UserRoleDto; +import org.sonar.core.user.*; import javax.annotation.Nullable; @@ -68,11 +64,14 @@ public class PermissionFacade implements TaskComponent, ServerComponent { this.settings = settings; } - public void insertUserPermission(@Nullable Long resourceId, Long userId, String permission, @Nullable SqlSession session) { + private void insertUserPermission(@Nullable Long resourceId, Long userId, String permission, boolean updateProjectAuthorizationDate, @Nullable SqlSession session) { UserRoleDto userRoleDto = new UserRoleDto() .setRole(permission) .setUserId(userId) .setResourceId(resourceId); + if (updateProjectAuthorizationDate) { + updateProjectAuthorizationDate(resourceId, session); + } if (session != null) { roleDao.insertUserRole(userRoleDto, session); } else { @@ -80,6 +79,10 @@ public class PermissionFacade implements TaskComponent, ServerComponent { } } + public void insertUserPermission(@Nullable Long resourceId, Long userId, String permission, @Nullable SqlSession session) { + insertUserPermission(resourceId, userId, permission, true, session); + } + public void insertUserPermission(@Nullable Long resourceId, Long userId, String permission) { insertUserPermission(resourceId, userId, permission, null); } @@ -89,6 +92,7 @@ public class PermissionFacade implements TaskComponent, ServerComponent { .setRole(permission) .setUserId(userId) .setResourceId(resourceId); + updateProjectAuthorizationDate(resourceId, session); if (session != null) { roleDao.deleteUserRole(userRoleDto, session); } else { @@ -100,11 +104,12 @@ public class PermissionFacade implements TaskComponent, ServerComponent { deleteUserPermission(resourceId, userId, permission, null); } - public void insertGroupPermission(@Nullable Long resourceId, @Nullable Long groupId, String permission, @Nullable SqlSession session) { + private void insertGroupPermission(@Nullable Long resourceId, @Nullable Long groupId, String permission, boolean updateProjectAuthorizationDate, @Nullable SqlSession session) { GroupRoleDto groupRole = new GroupRoleDto() .setRole(permission) .setGroupId(groupId) .setResourceId(resourceId); + updateProjectAuthorizationDate(resourceId, session); if (session != null) { roleDao.insertGroupRole(groupRole, session); } else { @@ -112,6 +117,10 @@ public class PermissionFacade implements TaskComponent, ServerComponent { } } + private void insertGroupPermission(@Nullable Long resourceId, @Nullable Long groupId, String permission, @Nullable SqlSession session) { + insertGroupPermission(resourceId, groupId, permission, true, session); + } + public void insertGroupPermission(@Nullable Long resourceId, @Nullable Long groupId, String permission) { insertGroupPermission(resourceId, groupId, permission, null); } @@ -132,6 +141,7 @@ public class PermissionFacade implements TaskComponent, ServerComponent { .setRole(permission) .setGroupId(groupId) .setResourceId(resourceId); + updateProjectAuthorizationDate(resourceId, session); if (session != null) { roleDao.deleteGroupRole(groupRole, session); } else { @@ -154,6 +164,19 @@ public class PermissionFacade implements TaskComponent, ServerComponent { } } + /** + * For each modification of permission on a project, update the authorization_updated_at to help ES reindex only relevant changes + */ + private void updateProjectAuthorizationDate(@Nullable Long projectId, @Nullable SqlSession session){ + if (projectId != null) { + if (session != null) { + resourceDao.updateAuthorizationDate(projectId, session); + } else { + resourceDao.updateAuthorizationDate(projectId); + } + } + } + /** * Load permission template and load associated collections of users and groups permissions */ @@ -174,18 +197,19 @@ public class PermissionFacade implements TaskComponent, ServerComponent { PermissionTemplateDto permissionTemplate = getPermissionTemplateWithPermissions(templateKey); SqlSession session = myBatis.openSession(false); try { + updateProjectAuthorizationDate(resourceId, session); removeAllPermissions(resourceId, session); List usersPermissions = permissionTemplate.getUsersPermissions(); if (usersPermissions != null) { for (PermissionTemplateUserDto userPermission : usersPermissions) { - insertUserPermission(resourceId, userPermission.getUserId(), userPermission.getPermission(), session); + insertUserPermission(resourceId, userPermission.getUserId(), userPermission.getPermission(), false, session); } } List groupsPermissions = permissionTemplate.getGroupsPermissions(); if (groupsPermissions != null) { for (PermissionTemplateGroupDto groupPermission : groupsPermissions) { Long groupId = groupPermission.getGroupId() == null ? null : groupPermission.getGroupId(); - insertGroupPermission(resourceId, groupId, groupPermission.getPermission(), session); + insertGroupPermission(resourceId, groupId, groupPermission.getPermission(), false, session); } } session.commit(); @@ -198,7 +222,7 @@ public class PermissionFacade implements TaskComponent, ServerComponent { return roleDao.countResourceGroupRoles(resourceId) + roleDao.countResourceUserRoles(resourceId); } - public void removeAllPermissions(Long resourceId, SqlSession session) { + protected void removeAllPermissions(Long resourceId, SqlSession session) { roleDao.deleteGroupRolesByResourceId(resourceId, session); roleDao.deleteUserRolesByResourceId(resourceId, session); } diff --git a/sonar-core/src/main/java/org/sonar/core/resource/ResourceDao.java b/sonar-core/src/main/java/org/sonar/core/resource/ResourceDao.java index 94b54f042b6..901bf3a1507 100644 --- a/sonar-core/src/main/java/org/sonar/core/resource/ResourceDao.java +++ b/sonar-core/src/main/java/org/sonar/core/resource/ResourceDao.java @@ -24,6 +24,7 @@ import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import org.apache.ibatis.session.SqlSession; import org.sonar.api.component.Component; +import org.sonar.api.utils.System2; import org.sonar.core.component.ComponentDto; import org.sonar.core.persistence.DaoComponent; import org.sonar.core.persistence.DbSession; @@ -41,9 +42,11 @@ import static com.google.common.collect.Lists.newArrayList; public class ResourceDao implements DaoComponent { private MyBatis mybatis; + private System2 system2; - public ResourceDao(MyBatis mybatis) { + public ResourceDao(MyBatis mybatis, System2 system2) { this.mybatis = mybatis; + this.system2 = system2; } public List getResources(ResourceQuery query) { @@ -141,10 +144,12 @@ public class ResourceDao implements DaoComponent { public ResourceDao insertOrUpdate(ResourceDto... resources) { SqlSession session = mybatis.openSession(false); ResourceMapper mapper = session.getMapper(ResourceMapper.class); + Date now = new Date(system2.now()); try { for (ResourceDto resource : resources) { if (resource.getId() == null) { - resource.setCreatedAt(new Date()); + resource.setCreatedAt(now); + resource.setAuthorizationUpdatedAt(now); mapper.insert(resource); } else { mapper.update(resource); @@ -157,6 +162,23 @@ public class ResourceDao implements DaoComponent { return this; } + public void updateAuthorizationDate(Long projectId, SqlSession session) { + session.getMapper(ResourceMapper.class).updateAuthorizationDate(projectId, new Date(system2.now())); + } + + /** + * Should not be called from batch side (used to reindex permission in E/S) + */ + public void updateAuthorizationDate(Long projectId) { + SqlSession session = mybatis.openSession(false); + try { + updateAuthorizationDate(projectId, session); + session.commit(); + } finally { + MyBatis.closeQuietly(session); + } + } + public Collection selectComponentsByIds(Collection ids) { if (ids.isEmpty()) { return Collections.emptyList(); diff --git a/sonar-core/src/main/java/org/sonar/core/resource/ResourceDto.java b/sonar-core/src/main/java/org/sonar/core/resource/ResourceDto.java index 93dff81318e..5cdcf247ced 100644 --- a/sonar-core/src/main/java/org/sonar/core/resource/ResourceDto.java +++ b/sonar-core/src/main/java/org/sonar/core/resource/ResourceDto.java @@ -38,6 +38,7 @@ public class ResourceDto { private Long copyResourceId; private Long personId; private Date createdAt; + private Date authorizationUpdatedAt; public Long getId() { return id; @@ -173,4 +174,13 @@ public class ResourceDto { this.createdAt = date;// NOSONAR May expose internal representation by incorporating reference to mutable object return this; } + + public Date getAuthorizationUpdatedAt() { + return authorizationUpdatedAt; + } + + public ResourceDto setAuthorizationUpdatedAt(Date authorizationUpdatedAt) { + this.authorizationUpdatedAt = authorizationUpdatedAt; + return this; + } } diff --git a/sonar-core/src/main/java/org/sonar/core/resource/ResourceMapper.java b/sonar-core/src/main/java/org/sonar/core/resource/ResourceMapper.java index f40caa1a426..259a1228b67 100644 --- a/sonar-core/src/main/java/org/sonar/core/resource/ResourceMapper.java +++ b/sonar-core/src/main/java/org/sonar/core/resource/ResourceMapper.java @@ -26,6 +26,7 @@ import org.sonar.core.component.ComponentDto; import javax.annotation.Nullable; import java.util.Collection; +import java.util.Date; import java.util.List; public interface ResourceMapper { @@ -86,4 +87,6 @@ public interface ResourceMapper { void update(ResourceDto resource); + void updateAuthorizationDate(@Param("projectId") Long projectId, @Param("authorizationDate") Date authorizationDate); + } diff --git a/sonar-core/src/main/resources/org/sonar/core/resource/ResourceMapper.xml b/sonar-core/src/main/resources/org/sonar/core/resource/ResourceMapper.xml index f32095b71da..d7cf97970f6 100644 --- a/sonar-core/src/main/resources/org/sonar/core/resource/ResourceMapper.xml +++ b/sonar-core/src/main/resources/org/sonar/core/resource/ResourceMapper.xml @@ -18,7 +18,8 @@ p.language as language, p.copy_resource_id as copyResourceId, p.person_id as personId, - p.created_at as createdAt + p.created_at as createdAt, + p.authorization_updated_at as authorizationUpdatedAt @@ -81,6 +82,7 @@ +