/* * #%L * Alfresco Repository * %% * Copyright (C) 2005 - 2016 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of * the paid license agreement will prevail. Otherwise, the software is * provided under the following open source license terms: * * Alfresco 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. * * Alfresco 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 Alfresco. If not, see . * #L% */ package org.alfresco.repo.site; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.io.Serializable; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import org.alfresco.model.ContentModel; import org.alfresco.query.PagingRequest; import org.alfresco.query.PagingResults; import org.alfresco.repo.node.archive.NodeArchiveService; import org.alfresco.repo.node.archive.RestoreNodeReport; import org.alfresco.repo.node.archive.RestoreNodeReport.RestoreStatus; import org.alfresco.repo.security.authentication.AuthenticationComponent; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; import org.alfresco.repo.security.permissions.AccessDeniedException; import org.alfresco.repo.transaction.RetryingTransactionHelper; import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; import org.alfresco.service.cmr.coci.CheckOutCheckInService; import org.alfresco.service.cmr.lock.LockService; import org.alfresco.service.cmr.lock.LockStatus; import org.alfresco.service.cmr.lock.LockType; import org.alfresco.service.cmr.lock.NodeLockedException; import org.alfresco.service.cmr.model.FileFolderService; import org.alfresco.service.cmr.model.FileInfo; import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.ContentWriter; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.StoreRef; import org.alfresco.service.cmr.security.AccessPermission; import org.alfresco.service.cmr.security.AuthorityService; import org.alfresco.service.cmr.security.AuthorityType; import org.alfresco.service.cmr.security.MutableAuthenticationService; import org.alfresco.service.cmr.security.PermissionService; import org.alfresco.service.cmr.security.PersonService; import org.alfresco.service.cmr.site.SiteInfo; import org.alfresco.service.cmr.site.SiteService; import org.alfresco.service.cmr.site.SiteVisibility; import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.QName; import org.alfresco.test_category.OwnJVMTestsCategory; import org.alfresco.util.Pair; import org.alfresco.util.PropertyMap; import org.alfresco.util.test.junitrules.AlfrescoPerson; import org.alfresco.util.test.junitrules.ApplicationContextInit; import org.alfresco.util.test.junitrules.RunAsFullyAuthenticatedRule; import org.alfresco.util.test.junitrules.TemporarySites; import org.alfresco.util.test.junitrules.TemporarySites.TestSiteAndMemberInfo; import org.alfresco.util.test.junitrules.TemporarySitesTest; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.junit.BeforeClass; import org.junit.ClassRule; import org.junit.Rule; import org.junit.Test; import org.junit.experimental.categories.Category; import org.junit.rules.RuleChain; import org.junit.rules.TestName; import org.springframework.extensions.webscripts.GUID; /** * This class contains some tests for the {@link SiteServiceImpl} - in addition to those already * included in {@link SiteServiceImplTest}. This uses JUnit 4 annotations and JUnit Rules. * * TODO Refactor the two classes together into one common approach. * * @author Neil Mc Erlean * @since 4.0.3 */ @Category(OwnJVMTestsCategory.class) public class SiteServiceImplMoreTest { protected static Log log = LogFactory.getLog(SiteServiceImplMoreTest.class); // Rule to initialise the default Alfresco spring configuration public static ApplicationContextInit APP_CONTEXT_INIT = ApplicationContextInit.createStandardContextWithOverrides("classpath:sites/test-" + TemporarySitesTest.class.getSimpleName() + "-context.xml"); // A rule to manage test nodes reused across all the test methods public static TemporarySites STATIC_TEST_SITES = new TemporarySites(APP_CONTEXT_INIT); // Tie them together in a static Rule Chain @ClassRule public static RuleChain staticRuleChain = RuleChain.outerRule(APP_CONTEXT_INIT) .around(STATIC_TEST_SITES); public RunAsFullyAuthenticatedRule runAllTestsAsAdmin = new RunAsFullyAuthenticatedRule(AuthenticationUtil.getAdminUserName()); public TemporarySites perMethodTestSites = new TemporarySites(APP_CONTEXT_INIT); public AlfrescoPerson testUser = new AlfrescoPerson(APP_CONTEXT_INIT); // Need to ensure the rules are in the correct order so that we're authenticated as admin before trying to create the person. @Rule public RuleChain ruleChain = RuleChain.outerRule(runAllTestsAsAdmin) .around(perMethodTestSites) .around(testUser); @Rule public TestName testName = new TestName(); // Various services private static AuthorityService AUTHORITY_SERVICE; private static NamespaceService NAMESPACE_SERVICE; private static NodeService NODE_SERVICE; private static NodeArchiveService NODE_ARCHIVE_SERVICE; private static SiteService SITE_SERVICE; private static CheckOutCheckInService COCI_SERVICE; private static RetryingTransactionHelper TRANSACTION_HELPER; private static PermissionService PERMISSION_SERVICE; private static MutableAuthenticationService AUTHENTICATION_SERVICE; private static PersonService PERSON_SERVICE; private static FileFolderService FILE_FOLDER_SERVICE; private static AuthenticationComponent AUTHENTICATION_COMPONENT; private static LockService LOCK_SERVICE; private static String TEST_SITE_NAME, TEST_SUB_SITE_NAME; private static TestSiteAndMemberInfo TEST_SITE_WITH_MEMBERS; @BeforeClass public static void initStaticData() throws Exception { AUTHORITY_SERVICE = APP_CONTEXT_INIT.getApplicationContext().getBean("AuthorityService", AuthorityService.class); NAMESPACE_SERVICE = APP_CONTEXT_INIT.getApplicationContext().getBean("namespaceService", NamespaceService.class); NODE_SERVICE = APP_CONTEXT_INIT.getApplicationContext().getBean("NodeService", NodeService.class); NODE_ARCHIVE_SERVICE = APP_CONTEXT_INIT.getApplicationContext().getBean("nodeArchiveService", NodeArchiveService.class); SITE_SERVICE = APP_CONTEXT_INIT.getApplicationContext().getBean("siteService", SiteService.class); COCI_SERVICE = APP_CONTEXT_INIT.getApplicationContext().getBean("checkOutCheckInService", CheckOutCheckInService.class); TRANSACTION_HELPER = APP_CONTEXT_INIT.getApplicationContext().getBean("retryingTransactionHelper", RetryingTransactionHelper.class); PERMISSION_SERVICE = APP_CONTEXT_INIT.getApplicationContext().getBean("permissionServiceImpl", PermissionService.class); AUTHENTICATION_SERVICE = APP_CONTEXT_INIT.getApplicationContext().getBean("authenticationService", MutableAuthenticationService.class); PERSON_SERVICE = APP_CONTEXT_INIT.getApplicationContext().getBean("PersonService", PersonService.class); FILE_FOLDER_SERVICE = APP_CONTEXT_INIT.getApplicationContext().getBean("FileFolderService",FileFolderService.class); AUTHENTICATION_COMPONENT = APP_CONTEXT_INIT.getApplicationContext().getBean("authenticationComponent",AuthenticationComponent.class); LOCK_SERVICE = APP_CONTEXT_INIT.getApplicationContext().getBean("lockService",LockService.class); // We'll create this test content as admin. final String admin = AuthenticationUtil.getAdminUserName(); TEST_SITE_NAME = GUID.generate(); TEST_SUB_SITE_NAME = GUID.generate(); final QName subSiteType = QName.createQName("testsite", "testSubsite", NAMESPACE_SERVICE); STATIC_TEST_SITES.createSite("sitePreset", TEST_SITE_NAME, "siteTitle", "siteDescription", SiteVisibility.PUBLIC, admin); STATIC_TEST_SITES.createSite("sitePreset", TEST_SUB_SITE_NAME, "siteTitle", "siteDescription", SiteVisibility.PUBLIC, subSiteType, admin); TEST_SITE_WITH_MEMBERS = STATIC_TEST_SITES.createTestSiteWithUserPerRole(SiteServiceImplMoreTest.class.getSimpleName(), "sitePreset", SiteVisibility.PUBLIC, admin); } /** * This method ensures that {@link SiteService#listSites(String)} includes content subtypes of {@link SiteModel#TYPE_SITE st:site}. */ @Test public void listSitesIncludingSubTypesOfSite() throws Exception { TRANSACTION_HELPER.doInTransaction(new RetryingTransactionCallback() { public Void execute() throws Throwable { PagingResults sites = SITE_SERVICE.listSites(null, null, new PagingRequest(0, 1024)); Map sitesByName = new HashMap(); for (SiteInfo site : sites.getPage()) { sitesByName.put(site.getShortName(), site); } assertNotNull("st:site missing.", sitesByName.get(TEST_SITE_NAME)); assertNotNull("subtype of st:site missing.", sitesByName.get(TEST_SUB_SITE_NAME)); return null; } }); } /** * This method ensures that sites can be deleted by any SiteManager, not just the Site Creator (ALF-15257). */ @Test public void anySiteManagerShouldBeAbleToDeleteASite() throws Exception { TRANSACTION_HELPER.doInTransaction(new RetryingTransactionCallback() { public Void execute() throws Throwable { // We already have a site with 4 users created by the TestSiteAndMemberInfo rule above. // We'll use the testUser from the rule above - oh how I wish JUnit supported @Test-level rules, so I didn't have to create that user for all tests. SITE_SERVICE.setMembership(TEST_SITE_WITH_MEMBERS.siteInfo.getShortName(), testUser.getUsername(), SiteModel.SITE_MANAGER); // Now switch to run as that user and try to delete the site. AuthenticationUtil.runAs(new RunAsWork() { @Override public Void doWork() throws Exception { SITE_SERVICE.deleteSite(TEST_SITE_WITH_MEMBERS.siteInfo.getShortName()); return null; } }, testUser.getUsername()); return null; } }); } /** * This test ensures that site deleted before MNT-10109 fix (i.e. deleted with their associated authorities) could be restored * with site-groups recreation * * @throws Exception */ @Test public void deleteSiteDeleteAuthoritiesAndRestoreEnsuringSiteGroupsWasRecreated() throws Exception { final String siteShortName = "testsite-" + System.currentTimeMillis(); final SiteServiceImpl siteServiceImpl = (SiteServiceImpl)SITE_SERVICE; log.debug("Creating test site called: " + siteShortName); // Create site final TestSiteAndMemberInfo testSiteAndMemberInfo = perMethodTestSites.createTestSiteWithUserPerRole(siteShortName, "sitePreset", SiteVisibility.PUBLIC, AuthenticationUtil.getAdminUserName()); // Delete permissions and site final Map membersBefore = TRANSACTION_HELPER.doInTransaction(new RetryingTransactionCallback>() { public Map execute() throws Throwable { NodeRef siteNodeRef = testSiteAndMemberInfo.siteInfo.getNodeRef(); Map membersBefore = SITE_SERVICE.listMembers(siteShortName, null, null, 0, true); log.debug(membersBefore.size() + " members..."); for (Map.Entry entry : membersBefore.entrySet()) { log.debug(entry); } Map> groupsMemberships = new HashMap>(); log.debug("About to delete site-related groups."); // delete authorities Set permissions = PERMISSION_SERVICE.getSettablePermissions(SiteModel.TYPE_SITE); for (String permission : permissions) { String prefixSiteRoleGroup = siteServiceImpl.getSiteRoleGroup(siteShortName, permission, true); Set groupUsers = AUTHORITY_SERVICE.getContainedAuthorities(null, prefixSiteRoleGroup, true); groupsMemberships.put(prefixSiteRoleGroup, groupUsers); AUTHORITY_SERVICE.deleteAuthority(prefixSiteRoleGroup); } //emulate onDelete site behavior before MNT-10109 fix NODE_SERVICE.setProperty(siteNodeRef, QName.createQName(null, "memberships"), (Serializable)groupsMemberships); log.debug("Site-related groups deleted."); log.debug("About to delete site."); SITE_SERVICE.deleteSite(siteShortName); log.debug("Site deleted."); return membersBefore; } }); // restore the site TRANSACTION_HELPER.doInTransaction(new RetryingTransactionCallback() { public Void execute() throws Throwable { assertThatArchivedNodeExists(testSiteAndMemberInfo.siteInfo.getNodeRef(), "Site node not found in archive."); // ensure there are no authorities Set permissions = PERMISSION_SERVICE.getSettablePermissions(SiteModel.TYPE_SITE); for (String permission : permissions) { String permissionGroupShortName = siteServiceImpl.getSiteRoleGroup(siteShortName, permission, false); String authorityName = AUTHORITY_SERVICE.getName(AuthorityType.GROUP, permissionGroupShortName); assertTrue("Authotiry should not exist : " + authorityName, !AUTHORITY_SERVICE.authorityExists(authorityName)); } log.debug("About to restore site node from archive"); final NodeRef archivedSiteNode = NODE_ARCHIVE_SERVICE.getArchivedNode(testSiteAndMemberInfo.siteInfo.getNodeRef()); RestoreNodeReport report = NODE_ARCHIVE_SERVICE.restoreArchivedNode(archivedSiteNode); // ...which should work assertEquals("Failed to restore site from archive", RestoreStatus.SUCCESS, report.getStatus()); log.debug("Successfully restored site from arhive."); return null; } }); TRANSACTION_HELPER.doInTransaction(new RetryingTransactionCallback>() { public Map execute() throws Throwable { // The site itself should have been restored, of course... assertTrue("The site noderef was not restored as expected", NODE_SERVICE.exists(testSiteAndMemberInfo.siteInfo.getNodeRef())); Map members = SITE_SERVICE.listMembers(siteShortName, null, null, 0, true); assertEquals("Not all member have been restored", membersBefore.size(), members.size()); log.debug(members.size() + " members..."); for (Map.Entry entry : SITE_SERVICE.listMembers(siteShortName, null, null, 0, true).entrySet()) { log.debug(entry); } // Group authority nodes should be restored or recreated for (String role : SITE_SERVICE.getSiteRoles()) { final String siteGroup = SITE_SERVICE.getSiteRoleGroup(siteShortName, role); assertTrue("Site group for role " + role + " did not exist after site restoration", AUTHORITY_SERVICE.authorityExists(siteGroup)); } Set currentManagers = AUTHORITY_SERVICE.getContainedAuthorities(AuthorityType.USER, siteServiceImpl.getSiteRoleGroup(siteShortName, SiteModel.SITE_MANAGER, true), false); // ensure that there is at least one site manager log.debug("Current Managers " + currentManagers); assertTrue("There should be at least one site manager", !currentManagers.isEmpty()); return null; } }); // remove site completely TRANSACTION_HELPER.doInTransaction(new RetryingTransactionCallback() { public Void execute() throws Throwable { log.debug("About to delete site completely."); SITE_SERVICE.deleteSite(siteShortName); log.debug("About to purge site from trashcan."); // get archive node reference String storePath = "archive://SpacesStore"; StoreRef storeRef = new StoreRef(storePath); NodeRef archivedNodeRef = new NodeRef(storeRef, testSiteAndMemberInfo.siteInfo.getNodeRef().getId()); NODE_ARCHIVE_SERVICE.purgeArchivedNode(archivedNodeRef); return null; } }); } private void createUser(String userName, String nameSuffix) { if (AUTHENTICATION_SERVICE.authenticationExists(userName)) { return; } AUTHENTICATION_SERVICE.createAuthentication(userName, "PWD".toCharArray()); PropertyMap ppOne = new PropertyMap(4); ppOne.put(ContentModel.PROP_USERNAME, userName); ppOne.put(ContentModel.PROP_FIRSTNAME, "firstName" + nameSuffix); ppOne.put(ContentModel.PROP_LASTNAME, "lastName" + nameSuffix); ppOne.put(ContentModel.PROP_EMAIL, "email" + nameSuffix + "@email.com"); ppOne.put(ContentModel.PROP_JOBTITLE, "jobTitle"); PERSON_SERVICE.createPerson(ppOne); } /** * Added as part of MNT-14671 : Site with document locked for Edit Offline cannot be deleted. * This test checks that the owner of a site can delete the site even if there are locked files, * that belong to the owner or to other members of that site; * * This test also checks that after restore the locks are restored correctly for all the locked * files; * * TODO in MNT-15855: * It should also checks the case when there is a working copy (simulate lock for offline edit) * * @throws Exception */ @Test public void deleteSiteDeleteAuthoritiesAndRestoreEnsuringLocksAreRestored() throws Exception { final String userOwner = "UserOwner"; final String userCollaborator = "UserColaborator"; final String userPrefix = "dart";// delete and restore test // create the users TRANSACTION_HELPER.doInTransaction(new RetryingTransactionCallback() { public Void execute() throws Throwable { createUser(userOwner, userPrefix); createUser(userCollaborator, userPrefix); return null; } }); final String siteShortName = "testsite-" + System.currentTimeMillis(); log.debug("Creating test site called: " + siteShortName); // Create site final TestSiteAndMemberInfo testSiteAndMemberInfo = perMethodTestSites.createTestSiteWithUserPerRole( siteShortName, "sitePreset", SiteVisibility.PUBLIC, userOwner); // create some documents into the test site as the owner of the site AUTHENTICATION_COMPONENT.setCurrentUser(userOwner); /* * [site] {siteShortName} * | * --- [siteContainer] {componentId} * | * --- [cm:content] fileFolderPrefix + "file.txt" * | * |-- [folder] fileFolderPrefix + "folder" * | * |-- [cm:content] fileFolderPrefix + "fileInFolder.txt" * | * |-- [folder] fileFolderPrefix + "subfolder" * | * |-- [cm:content] fileFolderPrefix + "fileInSubfolder.txt" */ String fileFolderPrefix = "TESTLOCK_"; String componentId = "doclib"; NodeRef siteContainer = SITE_SERVICE.createContainer(siteShortName, componentId, ContentModel.TYPE_FOLDER, null); final FileInfo fileInfo = FILE_FOLDER_SERVICE.create( siteContainer, fileFolderPrefix + "file.txt", ContentModel.TYPE_CONTENT); ContentWriter writer = FILE_FOLDER_SERVICE.getWriter(fileInfo.getNodeRef()); writer.putContent("Just some old content that doesn't mean anything"); FileInfo folder1Info = FILE_FOLDER_SERVICE.create( siteContainer, fileFolderPrefix + "folder", ContentModel.TYPE_FOLDER); FileInfo fileInfo2 = FILE_FOLDER_SERVICE.create( folder1Info.getNodeRef(), fileFolderPrefix + "fileInFolder.txt", ContentModel.TYPE_CONTENT); ContentWriter writer2 = FILE_FOLDER_SERVICE.getWriter(fileInfo2.getNodeRef()); writer2.putContent("Just some old content that doesn't mean anything"); FileInfo folder2Info = FILE_FOLDER_SERVICE.create( folder1Info.getNodeRef(), fileFolderPrefix + "subfolder", ContentModel.TYPE_FOLDER); FileInfo fileInfo3 = FILE_FOLDER_SERVICE.create( folder2Info.getNodeRef(), fileFolderPrefix + "fileInSubfolder.txt", ContentModel.TYPE_CONTENT); ContentWriter writer3 = FILE_FOLDER_SERVICE.getWriter(fileInfo3.getNodeRef()); writer3.putContent("Just some old content that doesn't mean anything"); // make sure there are no locks on the fileInfo yet assertEquals(LockStatus.NO_LOCK, LOCK_SERVICE.getLockStatus(fileInfo.getNodeRef())); // lock a file as userOwner TRANSACTION_HELPER.doInTransaction(new RetryingTransactionCallback() { public Void execute() throws Throwable { LOCK_SERVICE.lock(fileInfo.getNodeRef(), LockType.WRITE_LOCK); return null; } }); // make sure we have a lock now assertEquals(LockStatus.LOCK_OWNER, LOCK_SERVICE.getLockStatus(fileInfo.getNodeRef())); checkThatNonMembersCanNotCreateFiles(userCollaborator, fileFolderPrefix, folder2Info); // make sure we are running as userOwner AUTHENTICATION_COMPONENT.setCurrentUser(userOwner); // make userCollaborator a member of the site TRANSACTION_HELPER.doInTransaction(new RetryingTransactionCallback() { public Object execute() throws Throwable { SITE_SERVICE.setMembership(siteShortName, userCollaborator, SiteModel.SITE_COLLABORATOR); return null; } }); // now, as userCollaborator create a file and lock it AUTHENTICATION_COMPONENT.setCurrentUser(userCollaborator); final FileInfo fileInfoForCollaboratorUser = FILE_FOLDER_SERVICE.create( folder2Info.getNodeRef(), fileFolderPrefix + "userCollaborator.txt", ContentModel.TYPE_CONTENT); ContentWriter writer4 = FILE_FOLDER_SERVICE.getWriter(fileInfoForCollaboratorUser.getNodeRef()); writer4.putContent("Just some old content that doesn't mean anything"); // Check that the node is not currently locked assertEquals(LockStatus.NO_LOCK, LOCK_SERVICE.getLockStatus(fileInfoForCollaboratorUser.getNodeRef())); TRANSACTION_HELPER.doInTransaction(new RetryingTransactionCallback() { public Void execute() throws Throwable { // lock the file as userCollaborator LOCK_SERVICE.lock(fileInfoForCollaboratorUser.getNodeRef(), LockType.WRITE_LOCK); return null; } }); // Test valid lock assertEquals(LockStatus.LOCK_OWNER, LOCK_SERVICE.getLockStatus(fileInfoForCollaboratorUser.getNodeRef())); // switch back to userOwner so we can call delete the site AUTHENTICATION_COMPONENT.setCurrentUser(userOwner); // Delete the site TRANSACTION_HELPER.doInTransaction(new RetryingTransactionCallback() { public Void execute() throws Throwable { log.debug("About to delete site."); AUTHENTICATION_COMPONENT.getCurrentUserName(); SITE_SERVICE.deleteSite(siteShortName); log.debug("Site deleted."); return null; } }); // restore the site TRANSACTION_HELPER.doInTransaction(new RetryingTransactionCallback() { public Void execute() throws Throwable { assertThatArchivedNodeExists(testSiteAndMemberInfo.siteInfo.getNodeRef(), "Site node not found in archive."); log.debug("About to restore site node from archive"); final NodeRef archivedSiteNode = NODE_ARCHIVE_SERVICE.getArchivedNode(testSiteAndMemberInfo.siteInfo.getNodeRef()); RestoreNodeReport report = NODE_ARCHIVE_SERVICE.restoreArchivedNode(archivedSiteNode); // ...which should work assertEquals("Failed to restore site from archive", RestoreStatus.SUCCESS, report.getStatus()); log.debug("Successfully restored site from arhive."); return null; } }); // check that the files have been restored and all the locks are present AUTHENTICATION_COMPONENT.setCurrentUser(userCollaborator); assertEquals(LockStatus.LOCK_OWNER, LOCK_SERVICE.getLockStatus(fileInfoForCollaboratorUser.getNodeRef())); AUTHENTICATION_COMPONENT.setCurrentUser(userOwner); assertEquals(LockStatus.LOCK_OWNER, LOCK_SERVICE.getLockStatus(fileInfo.getNodeRef())); // remove site completely TRANSACTION_HELPER.doInTransaction(new RetryingTransactionCallback() { public Void execute() throws Throwable { // if we apply the sys:temporary aspect to the site, the NodeService will not // archive it i.e. this bit of clean up will be a bit faster. NODE_SERVICE.addAspect(testSiteAndMemberInfo.siteInfo.getNodeRef(), ContentModel.ASPECT_TEMPORARY, null); log.debug("About to delete site completely."); SITE_SERVICE.deleteSite(siteShortName); log.debug("About to purge site from trashcan."); // get archive node reference String storePath = "archive://SpacesStore"; StoreRef storeRef = new StoreRef(storePath); NodeRef archivedNodeRef = new NodeRef(storeRef, testSiteAndMemberInfo.siteInfo.getNodeRef().getId()); NODE_ARCHIVE_SERVICE.purgeArchivedNode(archivedNodeRef); return null; } }); } private void checkThatNonMembersCanNotCreateFiles(final String userCollaborator, String fileFolderPrefix, FileInfo folder2Info) { // now, as another user, that is not yet a member, try to create a file in this site // this use case is not really relevant to test method, but it is a good test for permissions AUTHENTICATION_COMPONENT.setCurrentUser(userCollaborator); try { FileInfo fileInfoForTestNewFileAsAnotherUser = FILE_FOLDER_SERVICE.create( folder2Info.getNodeRef(), fileFolderPrefix + "user2.txt", ContentModel.TYPE_CONTENT); ContentWriter writer3_user2 = FILE_FOLDER_SERVICE.getWriter(fileInfoForTestNewFileAsAnotherUser.getNodeRef()); writer3_user2.putContent("Just some old content that doesn't mean anything"); fail("We should not reach this point. the user that tries to run this code, add the file, is not yet a member of the site"); } catch (AccessDeniedException e) { // Expected } } /** * This test ensures that when sites are deleted (moved to the trashcan) and then restored, that the 4 role-based groups are * restored correctly and that any users who were members of those groups are made members once more. * * @see SiteServiceImpl#deleteSite(String) * @see SiteServiceImpl#onRestoreNode(org.alfresco.service.cmr.repository.ChildAssociationRef) */ @Test public void deleteSiteAndRestoreEnsuringSiteGroupsAreRecovered() throws Exception { // Implementation note: as of Alfresco 4.2, authorities cannot be archived and are always hard-deleted. // Therefore, on site restore, the SiteService *recreates* the deleted groups associated with the restored site. // Having recreated them, it then needs to add any old members (users, TODO what about groups?) into those newly created site groups. // It does this by writing the site members and their roles onto a property on the st:site node in the archive & reading them on restore. // Choose a site name that will link back to this test case... final String siteShortName = testName.getMethodName(); log.debug("Creating test site called: " + siteShortName); // ...and create the site final TestSiteAndMemberInfo testSiteAndMemberInfo = perMethodTestSites.createTestSiteWithUserPerRole(siteShortName, "sitePreset", SiteVisibility.PUBLIC, AuthenticationUtil.getAdminUserName()); // Now get the various site-related data that we want to examine after deletion & restoration final TestData testData = TRANSACTION_HELPER.doInTransaction(new RetryingTransactionCallback() { public TestData execute() throws Throwable { Map properties = new HashMap(); properties.put(ContentModel.PROP_NAME, "testcontent"); properties.put(ContentModel.PROP_DESCRIPTION, "content - test doc for test"); ChildAssociationRef testDoc = NODE_SERVICE.createNode(testSiteAndMemberInfo.doclib, ContentModel.ASSOC_CONTAINS, QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "testcontent"), ContentModel.TYPE_CONTENT, properties); NodeRef testDocNodeRef = testDoc.getChildRef(); // change all groups to have the permissions from a contributor PERMISSION_SERVICE.deletePermissions(testDocNodeRef); PERMISSION_SERVICE.setInheritParentPermissions(testDocNodeRef, false); assertTrue("Permissions should be cleared", PERMISSION_SERVICE.getAllSetPermissions(testDocNodeRef).isEmpty()); Set permissions = PERMISSION_SERVICE.getSettablePermissions(SiteModel.TYPE_SITE); for (String permission : permissions) { String siteRoleGroup = SITE_SERVICE.getSiteRoleGroup(siteShortName, permission); PERMISSION_SERVICE.setPermission(testDocNodeRef, siteRoleGroup, "SiteContributor", true); } final Map userNameToRoleMap = new HashMap(); // Which users are members of which groups? for (String role : SITE_SERVICE.getSiteRoles()) { // putAll here shouldn't overwrite any keys in the above map as each authority should only have one role in a site. userNameToRoleMap.putAll(SITE_SERVICE.listMembers(siteShortName, null, role, 0, true)); } // Some sanity checking before we delete the site final String siteContributorGroup = SITE_SERVICE.getSiteRoleGroup(siteShortName, "SiteContributor"); assertTrue("Site contributor user was not in site contributors group", AUTHORITY_SERVICE.getContainedAuthorities(AuthorityType.USER, siteContributorGroup, true).contains(testSiteAndMemberInfo.siteContributor)); assertEquals("Site contributor user did not have expected Contributor role", SiteModel.SITE_CONTRIBUTOR, userNameToRoleMap.get(testSiteAndMemberInfo.siteContributor)); log.debug("About to delete site."); SITE_SERVICE.deleteSite(siteShortName); log.debug("Site deleted."); return new TestData(userNameToRoleMap, testDocNodeRef); } }); TRANSACTION_HELPER.doInTransaction(new RetryingTransactionCallback() { public Void execute() throws Throwable { assertThatArchivedNodeExists(testSiteAndMemberInfo.siteInfo.getNodeRef(), "Site node not found in archive."); // At this point we might assert that the groups associated with the site were gone, but that's an implementation detail really. log.debug("About to restore site node from archive"); final NodeRef archivedSiteNode = NODE_ARCHIVE_SERVICE.getArchivedNode(testSiteAndMemberInfo.siteInfo.getNodeRef()); RestoreNodeReport report = NODE_ARCHIVE_SERVICE.restoreArchivedNode(archivedSiteNode); // ...which should work assertEquals("Failed to restore site from archive", RestoreStatus.SUCCESS, report.getStatus()); log.debug("Successfully restored site from arhive."); return null; } }); final Map associatedGroups = TRANSACTION_HELPER.doInTransaction(new RetryingTransactionCallback>() { public Map execute() throws Throwable { // The site itself should have been restored, of course... assertTrue("The site noderef was not restored as expected", NODE_SERVICE.exists(testSiteAndMemberInfo.siteInfo.getNodeRef())); // But the group authority nodes should be restored (recreated) as well. for (String role : SITE_SERVICE.getSiteRoles()) { final String siteGroup = SITE_SERVICE.getSiteRoleGroup(siteShortName, role); assertTrue("Site group for role " + role + " did not exist after site restoration", AUTHORITY_SERVICE.authorityExists(siteGroup)); } log.debug(SITE_SERVICE.listMembers(siteShortName, null, null, 0, true).size() + " members..."); for (Map.Entry entry : SITE_SERVICE.listMembers(siteShortName, null, null, 0, true).entrySet()) { log.debug(entry); } // And finally, the original members of the site should have been given the same membership that they had before. for (Map.Entry entry : testData.userNameToRoleMap.entrySet()) { assertEquals("Unexpected role for site user: " + entry.getKey(), entry.getValue(), SITE_SERVICE.getMembersRole(siteShortName, entry.getKey())); } // When the site is restored custom permissions on contents should be also restored Set accessPermissions = PERMISSION_SERVICE.getAllSetPermissions(testData.testDocNodeRef); Map associatedGroups = new HashMap(); for(AccessPermission access : accessPermissions) { associatedGroups.put(access.getAuthority(), access.getPermission()); } Set permissions = PERMISSION_SERVICE.getSettablePermissions(SiteModel.TYPE_SITE); for (String permission : permissions) { String siteRoleGroup = SITE_SERVICE.getSiteRoleGroup(siteShortName, permission); assertTrue("all groups should have the permissions from a contributor on test content", "SiteContributor".equals(associatedGroups.get(siteRoleGroup))); } return associatedGroups; } }); TRANSACTION_HELPER.doInTransaction(new RetryingTransactionCallback() { public Void execute() throws Throwable { log.debug("About to delete site completely."); SITE_SERVICE.deleteSite(siteShortName); for (String authority : associatedGroups.keySet()) { assertTrue("Associated groups should remain after site delete", AUTHORITY_SERVICE.authorityExists(authority)); } return null; } }); TRANSACTION_HELPER.doInTransaction(new RetryingTransactionCallback() { public Void execute() throws Throwable { log.debug("About to purge site from trashcan."); // get archive node reference String storePath = "archive://SpacesStore"; StoreRef storeRef = new StoreRef(storePath); NodeRef archivedNodeRef = new NodeRef(storeRef, testSiteAndMemberInfo.siteInfo.getNodeRef().getId()); NODE_ARCHIVE_SERVICE.purgeArchivedNode(archivedNodeRef); for (String authority : associatedGroups.keySet()) { assertTrue("Associated groups should be deleted on site purge", !AUTHORITY_SERVICE.authorityExists(authority)); } return null; } }); } @Test public void testSiteMembersPaged() { // Choose a site name that will link back to this test case... final String siteShortName = testName.getMethodName(); log.debug("Creating test site called: " + siteShortName); TRANSACTION_HELPER.doInTransaction(new RetryingTransactionCallback() { public Void execute() throws Throwable { // ...and create the site final TestSiteAndMemberInfo testSiteAndMemberInfo = perMethodTestSites.createTestSiteWithUserPerRole(siteShortName, "sitePreset", SiteVisibility.PUBLIC, AuthenticationUtil.getAdminUserName()); List> sortProps = new ArrayList>(1); sortProps.add(new Pair(SiteService.SortFields.FirstName, true)); PagingResults pagedMembers = SITE_SERVICE.listMembersPaged(siteShortName, true, sortProps, new PagingRequest(100)); assertNotNull(pagedMembers); assertNotNull(pagedMembers.getPage()); assertNotNull(pagedMembers.getQueryExecutionId()); assertFalse(pagedMembers.hasMoreItems()); log.debug("About to delete site completely."); SITE_SERVICE.deleteSite(siteShortName); return null; } }); } @Test public void testTokenizer() { String[] res = SiteServiceImpl.tokenizeFilterLowercase("Fred"); assertNotNull(res); assertEquals(1, res.length); assertEquals("fred", res[0]); res = SiteServiceImpl.tokenizeFilterLowercase("king kong lives"); assertNotNull(res); assertEquals(3, res.length); assertEquals("king", res[0]); assertEquals("kong", res[1]); assertEquals("lives", res[2]); res = SiteServiceImpl.tokenizeFilterLowercase("KING Kong livES"); assertNotNull(res); assertEquals(3, res.length); assertEquals("king", res[0]); assertEquals("kong", res[1]); assertEquals("lives", res[2]); res = SiteServiceImpl.tokenizeFilterLowercase(null); assertNotNull(res); assertEquals(0, res.length); } /** * MNT-16043: Site Owner and Site Manager can delete working copy, * Site Collaborator cannot * * @throws Exception */ @Test public void testSiteRolesPersmissionsToDeleteWorkingCopy() throws Exception { final String userSiteOwner = "UserSiteOwner"; final String userSiteManager = "UserSiteManager"; final String userSiteCollaborator = "UserSiteCollaborator"; final String userPrefix = "delete-locked-file"; // create the users TRANSACTION_HELPER.doInTransaction(new RetryingTransactionCallback() { public Void execute() throws Throwable { createUser(userSiteOwner, userPrefix); createUser(userSiteManager, userPrefix); createUser(userSiteCollaborator, userPrefix); return null; } }); final String siteShortName = userPrefix + "Site" + System.currentTimeMillis(); final String dummyContent = "Just some old content that doesn't mean anything"; // Create site final TestSiteAndMemberInfo testSiteAndMemberInfo = perMethodTestSites.createTestSiteWithUserPerRole(siteShortName, "sitePreset", SiteVisibility.PUBLIC, userSiteOwner); // create 1 file into the site as the owner of the site AUTHENTICATION_COMPONENT.setCurrentUser(userSiteOwner); NodeRef siteContainer = SITE_SERVICE.createContainer(siteShortName, "doclib", ContentModel.TYPE_FOLDER, null); final FileInfo fileInfo1 = FILE_FOLDER_SERVICE.create(siteContainer, "fileInfo1.txt", ContentModel.TYPE_CONTENT); ContentWriter writer1 = FILE_FOLDER_SERVICE.getWriter(fileInfo1.getNodeRef()); writer1.putContent(dummyContent); final FileInfo fileInfo2 = FILE_FOLDER_SERVICE.create(siteContainer, "fileInfo2.txt", ContentModel.TYPE_CONTENT); ContentWriter writer2 = FILE_FOLDER_SERVICE.getWriter(fileInfo2.getNodeRef()); writer2.putContent(dummyContent); final FileInfo fileInfo3 = FILE_FOLDER_SERVICE.create(siteContainer, "fileInfo3.txt", ContentModel.TYPE_CONTENT); ContentWriter writer3 = FILE_FOLDER_SERVICE.getWriter(fileInfo2.getNodeRef()); writer3.putContent(dummyContent); // Site Manager - can delete working copy but cannot delete the original // locked file TRANSACTION_HELPER.doInTransaction(new RetryingTransactionCallback() { public Void execute() throws Throwable { // lock a file as userOwner NodeRef workingCopy = COCI_SERVICE.checkout(fileInfo1.getNodeRef()); assertNotNull(workingCopy); // make userSiteManager a member of the site SITE_SERVICE.setMembership(siteShortName, userSiteManager, SiteModel.SITE_MANAGER); // make sure we are running as userSiteManager AUTHENTICATION_COMPONENT.setCurrentUser(userSiteManager); // try delete locked file by owner try { NODE_SERVICE.deleteNode(fileInfo1.getNodeRef()); fail("Cannot perform operation since the node" + fileInfo1.getNodeRef() + " is locked"); } catch (NodeLockedException ex) { // do nothing - is expected } // simulate delete from Share - delete the working copy NODE_SERVICE.deleteNode(workingCopy); return null; } }); // Site Owner - can delete working copy but cannot delete the original // locked file TRANSACTION_HELPER.doInTransaction(new RetryingTransactionCallback() { public Void execute() throws Throwable { AUTHENTICATION_COMPONENT.setCurrentUser(userSiteOwner); // lock a file as userOwner NodeRef workingCopy = COCI_SERVICE.checkout(fileInfo2.getNodeRef()); assertNotNull(workingCopy); try { NODE_SERVICE.deleteNode(fileInfo2.getNodeRef()); fail("Cannot perform operation since the node" + fileInfo1.getNodeRef() + " is locked"); } catch (NodeLockedException ex) { // do nothing- expected } NODE_SERVICE.deleteNode(workingCopy); return null; } }); // Site COLLABORATOR - cannot delete working copy or original file TRANSACTION_HELPER.doInTransaction(new RetryingTransactionCallback() { public Void execute() throws Throwable { // lock a file as userOwner AUTHENTICATION_COMPONENT.setCurrentUser(userSiteOwner); NodeRef workingCopy = COCI_SERVICE.checkout(fileInfo3.getNodeRef()); assertNotNull(workingCopy); // make userSiteCollaborator a member of the site SITE_SERVICE.setMembership(siteShortName, userSiteCollaborator, SiteModel.SITE_COLLABORATOR); // make sure we are running as userSiteManager AUTHENTICATION_COMPONENT.setCurrentUser(userSiteCollaborator); try { NODE_SERVICE.deleteNode(fileInfo3.getNodeRef()); fail("You do not have the appropriate permissions to perform this operation"); } catch (AccessDeniedException ex) { // do nothing - is expected } // try delete locked file by owner try { NODE_SERVICE.deleteNode(workingCopy); fail("You do not have the appropriate permissions to perform this operation"); } catch (AccessDeniedException ex) { // do nothing - is expected } return null; } }); AUTHENTICATION_COMPONENT.setCurrentUser(userSiteOwner); // Delete the site TRANSACTION_HELPER.doInTransaction(new RetryingTransactionCallback() { public Void execute() throws Throwable { AUTHENTICATION_COMPONENT.getCurrentUserName(); SITE_SERVICE.deleteSite(siteShortName); return null; } }); } private void assertThatArchivedNodeExists(NodeRef originalNodeRef, String failureMsg) { final NodeRef archivedNodeRef = NODE_ARCHIVE_SERVICE.getArchivedNode(originalNodeRef); assertTrue(failureMsg, NODE_SERVICE.exists(archivedNodeRef)); } public static class TestData { public final Map userNameToRoleMap; public final NodeRef testDocNodeRef; public TestData(Map userNameToRoleMap, NodeRef testDocNodeRef) { this.userNameToRoleMap = userNameToRoleMap; this.testDocNodeRef = testDocNodeRef; } } }