diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-patch-context.xml b/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-patch-context.xml
index eaad20fc4d..469a4ec2fe 100644
--- a/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-patch-context.xml
+++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-patch-context.xml
@@ -63,7 +63,7 @@
-
+
@@ -76,7 +76,7 @@
-
+
@@ -92,7 +92,7 @@
-
+
@@ -108,4 +108,17 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-service-context.xml b/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-service-context.xml
index 197429a496..4937f3a39c 100644
--- a/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-service-context.xml
+++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-service-context.xml
@@ -1479,4 +1479,49 @@
+
+
+ ${spaces.store}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/dataset/DataSetServiceImpl.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/dataset/DataSetServiceImpl.java
index 7db15e5327..f279699c9e 100644
--- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/dataset/DataSetServiceImpl.java
+++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/dataset/DataSetServiceImpl.java
@@ -6,7 +6,9 @@ import java.io.InputStreamReader;
import java.io.Reader;
import java.io.Serializable;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
@@ -24,6 +26,7 @@ import org.alfresco.module.org_alfresco_module_rm.recordfolder.RecordFolderServi
import org.alfresco.module.org_alfresco_module_rm.role.FilePlanRoleService;
import org.alfresco.module.org_alfresco_module_rm.role.Role;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
+import org.alfresco.repo.security.authority.RMAuthority;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.StoreRef;
@@ -374,7 +377,7 @@ public class DataSetServiceImpl implements DataSetService, RecordsManagementMode
// Create "all" role group for root node
String allRoles = authorityService.createAuthority(AuthorityType.GROUP, allRoleShortName,
- "All Roles", null);
+ "All Roles", new HashSet(Arrays.asList(RMAuthority.ZONE_APP_RM)));
// Put all the role groups in it
Set roles = filePlanRoleService.getRoles(rmRoot);
diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/patch/RMv21RolesPatch.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/patch/RMv21RolesPatch.java
new file mode 100644
index 0000000000..d370c59b34
--- /dev/null
+++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/patch/RMv21RolesPatch.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2005-2013 Alfresco Software Limited.
+ *
+ * This file is part of Alfresco
+ *
+ * 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 .
+ */
+package org.alfresco.module.org_alfresco_module_rm.patch;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.alfresco.module.org_alfresco_module_rm.fileplan.FilePlanService;
+import org.alfresco.module.org_alfresco_module_rm.role.FilePlanRoleService;
+import org.alfresco.module.org_alfresco_module_rm.role.Role;
+import org.alfresco.repo.module.AbstractModuleComponent;
+import org.alfresco.repo.security.authority.RMAuthority;
+import org.alfresco.service.cmr.repository.NodeRef;
+import org.alfresco.service.cmr.security.AuthorityService;
+import org.alfresco.service.cmr.security.AuthorityType;
+import org.springframework.beans.factory.BeanNameAware;
+
+/**
+ * Adds the existing rm roles to a new zone "APP.RM"
+ *
+ * @author Tuna Aksoy
+ * @since 2.1
+ */
+public class RMv21RolesPatch extends AbstractModuleComponent implements BeanNameAware
+{
+ private FilePlanService filePlanService;
+ private FilePlanRoleService filePlanRoleService;
+ private AuthorityService authorityService;
+
+ public void setFilePlanService(FilePlanService filePlanService)
+ {
+ this.filePlanService = filePlanService;
+ }
+
+ public void setFilePlanRoleService(FilePlanRoleService filePlanRoleService)
+ {
+ this.filePlanRoleService = filePlanRoleService;
+ }
+
+ public void setAuthorityService(AuthorityService authorityService)
+ {
+ this.authorityService = authorityService;
+ }
+
+ @Override
+ protected void executeInternal() throws Throwable
+ {
+ Set filePlans = filePlanService.getFilePlans();
+ for (NodeRef filePlan : filePlans)
+ {
+ boolean parentAddedToZone = false;
+ Set roles = filePlanRoleService.getRoles(filePlan);
+ for (Role role : roles)
+ {
+ String roleGroupName = role.getRoleGroupName();
+ addAuthorityToZone(roleGroupName);
+ if (parentAddedToZone == false)
+ {
+ String allRolesGroup = authorityService.getName(AuthorityType.GROUP, "AllRoles" + filePlan.getId());
+ addAuthorityToZone(allRolesGroup);
+ parentAddedToZone = true;
+ }
+ }
+ }
+ }
+
+ private void addAuthorityToZone(String roleGroupName)
+ {
+ authorityService.addAuthorityToZones(roleGroupName, new HashSet(Arrays.asList(RMAuthority.ZONE_APP_RM)));
+ }
+}
diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/role/FilePlanRoleServiceImpl.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/role/FilePlanRoleServiceImpl.java
index e8f04cacb5..4d8b7f5f8f 100644
--- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/role/FilePlanRoleServiceImpl.java
+++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/role/FilePlanRoleServiceImpl.java
@@ -22,6 +22,7 @@ import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
+import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
@@ -39,6 +40,7 @@ import org.alfresco.repo.policy.JavaBehaviour;
import org.alfresco.repo.policy.PolicyComponent;
import org.alfresco.repo.policy.Behaviour.NotificationFrequency;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
+import org.alfresco.repo.security.authority.RMAuthority;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
@@ -184,7 +186,7 @@ public class FilePlanRoleServiceImpl implements FilePlanRoleService,
public NodeRef doWork()
{
// Create "all" role group for root node
- String allRoles = authorityService.createAuthority(AuthorityType.GROUP, getAllRolesGroupShortName(rmRootNode), "All Roles", null);
+ String allRoles = authorityService.createAuthority(AuthorityType.GROUP, getAllRolesGroupShortName(rmRootNode), "All Roles", new HashSet(Arrays.asList(RMAuthority.ZONE_APP_RM)));
// Set the permissions
permissionService.setInheritParentPermissions(rmRootNode, false);
@@ -607,7 +609,7 @@ public class FilePlanRoleServiceImpl implements FilePlanRoleService,
// Create a group that relates to the records management role
Set zones = new HashSet(2);
zones.add(getZoneName(rmRootNode));
- zones.add(AuthorityService.ZONE_APP_DEFAULT);
+ zones.add(RMAuthority.ZONE_APP_RM);
String roleGroup = authorityService.createAuthority(AuthorityType.GROUP, fullRoleName, roleDisplayLabel, zones);
// Add the roleGroup to the "all" role group
diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/BootstrapTestDataGet.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/BootstrapTestDataGet.java
index dcc9d0d330..3738580214 100644
--- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/BootstrapTestDataGet.java
+++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/BootstrapTestDataGet.java
@@ -22,7 +22,9 @@ import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
+import java.util.Arrays;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Map;
import java.util.Set;
@@ -40,6 +42,7 @@ import org.alfresco.module.org_alfresco_module_rm.recordfolder.RecordFolderServi
import org.alfresco.module.org_alfresco_module_rm.security.RecordsManagementSecurityService;
import org.alfresco.module.org_alfresco_module_rm.security.Role;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
+import org.alfresco.repo.security.authority.RMAuthority;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.StoreRef;
@@ -70,15 +73,15 @@ public class BootstrapTestDataGet extends DeclarativeWebScript
implements RecordsManagementModel, ApplicationContextAware
{
private static Log logger = LogFactory.getLog(BootstrapTestDataGet.class);
-
+
private static final String ARG_SITE_NAME = "site";
private static final String ARG_IMPORT = "import";
-
+
private static final String XML_IMPORT = "alfresco/module/org_alfresco_module_rm/dod5015/DODExampleFilePlan.xml";
private static final String charsetName = "UTF-8";
-
+
private static final StoreRef SPACES_STORE = new StoreRef(StoreRef.PROTOCOL_WORKSPACE, "SpacesStore");
-
+
private NodeService nodeService;
private SearchService searchService;
private RecordsManagementService recordsManagementService;
@@ -89,70 +92,70 @@ public class BootstrapTestDataGet extends DeclarativeWebScript
private RecordsManagementSecurityService recordsManagementSecurityService;
private AuthorityService authorityService;
private RecordsManagementSearchBehaviour recordsManagementSearchBehaviour;
- private DispositionService dispositionService;
+ private DispositionService dispositionService;
private ApplicationContext applicationContext;
-
+
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException
{
this.applicationContext = applicationContext;
}
-
+
public void setNodeService(NodeService nodeService)
{
this.nodeService = nodeService;
}
-
+
public void setSearchService(SearchService searchService)
{
this.searchService = searchService;
}
-
+
public void setDispositionService(DispositionService dispositionService)
{
this.dispositionService = dispositionService;
}
-
+
public void setRecordsManagementService(RecordsManagementService recordsManagementService)
{
this.recordsManagementService = recordsManagementService;
}
-
+
public void setRecordsManagementActionService(RecordsManagementActionService recordsManagementActionService)
{
this.recordsManagementActionService = recordsManagementActionService;
}
-
- public void setImporterService(ImporterService importerService)
+
+ public void setImporterService(ImporterService importerService)
{
this.importerService = importerService;
}
-
+
public void setSiteService(SiteService siteService)
{
this.siteService = siteService;
}
-
+
public void setPermissionService(PermissionService permissionService)
{
this.permissionService = permissionService;
}
-
+
public void setAuthorityService(AuthorityService authorityService)
{
this.authorityService = authorityService;
}
-
+
public void setRecordsManagementSecurityService(RecordsManagementSecurityService recordsManagementSecurityService)
{
this.recordsManagementSecurityService = recordsManagementSecurityService;
}
-
+
public void setRecordsManagementSearchBehaviour(RecordsManagementSearchBehaviour searchBehaviour)
{
this.recordsManagementSearchBehaviour = searchBehaviour;
}
-
+
@Override
public Map executeImpl(WebScriptRequest req, Status status, Cache cache)
{
@@ -162,14 +165,14 @@ public class BootstrapTestDataGet extends DeclarativeWebScript
{
importData = Boolean.parseBoolean(req.getParameter(ARG_IMPORT));
}
-
+
// resolve rm site
String siteName = RmSiteType.DEFAULT_SITE_NAME;
if (req.getParameter(ARG_SITE_NAME) != null)
{
siteName = req.getParameter(ARG_SITE_NAME);
}
-
+
if (importData)
{
SiteInfo site = siteService.getSite(siteName);
@@ -177,14 +180,14 @@ public class BootstrapTestDataGet extends DeclarativeWebScript
{
throw new AlfrescoRuntimeException("Records Management site does not exist: " + siteName);
}
-
+
// resolve documentLibrary (filePlan) container
NodeRef filePlan = siteService.getContainer(siteName, RmSiteType.COMPONENT_DOCUMENT_LIBRARY);
if (filePlan == null)
{
filePlan = siteService.createContainer(siteName, RmSiteType.COMPONENT_DOCUMENT_LIBRARY, TYPE_FILE_PLAN, null);
}
-
+
// import the RM test data ACP into the the provided filePlan node reference
InputStream is = BootstrapTestDataGet.class.getClassLoader().getResourceAsStream(XML_IMPORT);
if (is == null)
@@ -203,31 +206,31 @@ public class BootstrapTestDataGet extends DeclarativeWebScript
Location location = new Location(filePlan);
importerService.importView(viewReader, location, null, null);
}
-
+
// Patch data
- BootstrapTestDataGet.patchLoadedData(applicationContext, searchService, nodeService, recordsManagementService,
+ BootstrapTestDataGet.patchLoadedData(applicationContext, searchService, nodeService, recordsManagementService,
recordsManagementActionService, permissionService,
authorityService, recordsManagementSecurityService,
recordsManagementSearchBehaviour,
dispositionService);
-
+
Map model = new HashMap(1, 1.0f);
model.put("success", true);
-
+
return model;
}
-
+
/**
* Temp method to patch AMP'ed data
- *
+ *
* @param searchService
* @param nodeService
* @param recordsManagementService
* @param recordsManagementActionService
*/
public static void patchLoadedData( final ApplicationContext applicationContext,
- final SearchService searchService,
- final NodeService nodeService,
+ final SearchService searchService,
+ final NodeService nodeService,
final RecordsManagementService recordsManagementService,
final RecordsManagementActionService recordsManagementActionService,
final PermissionService permissionService,
@@ -249,20 +252,20 @@ public class BootstrapTestDataGet extends DeclarativeWebScript
logger.info("Updating permissions for rm root: " + rmRoot);
permissionService.setInheritParentPermissions(rmRoot, false);
}
-
+
String allRoleShortName = "AllRoles" + rmRoot.getId();
String allRoleGroupName = authorityService.getName(AuthorityType.GROUP, allRoleShortName);
-
+
if (authorityService.authorityExists(allRoleGroupName) == false)
- {
+ {
logger.info("Creating all roles group for root node: " + rmRoot.toString());
-
+
// Create "all" role group for root node
- String allRoles = authorityService.createAuthority(AuthorityType.GROUP,
- allRoleShortName,
- "All Roles",
- null);
-
+ String allRoles = authorityService.createAuthority(AuthorityType.GROUP,
+ allRoleShortName,
+ "All Roles",
+ new HashSet(Arrays.asList(RMAuthority.ZONE_APP_RM)));
+
// Put all the role groups in it
Set roles = recordsManagementSecurityService.getRoles(rmRoot);
for (Role role : roles)
@@ -270,22 +273,22 @@ public class BootstrapTestDataGet extends DeclarativeWebScript
logger.info(" - adding role group " + role.getRoleGroupName() + " to all roles group");
authorityService.addAuthority(allRoles, role.getRoleGroupName());
}
-
+
// Set the permissions
permissionService.setPermission(rmRoot, allRoles, RMPermissionModel.READ_RECORDS, true);
}
}
-
+
// Make sure all the containers do not inherit permissions
ResultSet rs = searchService.query(SPACES_STORE, SearchService.LANGUAGE_LUCENE, "TYPE:\"rma:recordsManagementContainer\"");
try
{
logger.info("Bootstraping " + rs.length() + " record containers ...");
-
+
for (NodeRef container : rs.getNodeRefs())
{
String containerName = (String)nodeService.getProperty(container, ContentModel.PROP_NAME);
-
+
// Set permissions
if (permissionService.getInheritParentPermissions(container) == true)
{
@@ -298,24 +301,24 @@ public class BootstrapTestDataGet extends DeclarativeWebScript
{
rs.close();
}
-
+
// fix up the test dataset to fire initial events for disposition schedules
rs = searchService.query(SPACES_STORE, SearchService.LANGUAGE_LUCENE, "TYPE:\"rma:recordFolder\"");
try
{
logger.info("Bootstraping " + rs.length() + " record folders ...");
-
+
for (NodeRef recordFolder : rs.getNodeRefs())
{
String folderName = (String)nodeService.getProperty(recordFolder, ContentModel.PROP_NAME);
-
+
// Set permissions
if (permissionService.getInheritParentPermissions(recordFolder) == true)
{
logger.info("Updating permissions for record folder: " + folderName);
permissionService.setInheritParentPermissions(recordFolder, false);
}
-
+
if (nodeService.hasAspect(recordFolder, ASPECT_DISPOSITION_LIFECYCLE) == false)
{
// See if the folder has a disposition schedule that needs to be applied
@@ -328,7 +331,7 @@ public class BootstrapTestDataGet extends DeclarativeWebScript
recordService.initialiseRecordFolder(recordFolder);
}
}
-
+
// fixup the search behaviour aspect for the record folder
logger.info("Setting up search aspect for record folder: " + folderName);
recordManagementSearchBehaviour.fixupSearchAspect(recordFolder);
@@ -338,12 +341,12 @@ public class BootstrapTestDataGet extends DeclarativeWebScript
{
rs.close();
}
-
+
return null;
}
};
-
+
AuthenticationUtil.runAs(runAsWork, AuthenticationUtil.getAdminUserName());
-
+
}
}
\ No newline at end of file
diff --git a/rm-server/source/java/org/alfresco/repo/security/authority/RMAuthority.java b/rm-server/source/java/org/alfresco/repo/security/authority/RMAuthority.java
new file mode 100644
index 0000000000..db8106a536
--- /dev/null
+++ b/rm-server/source/java/org/alfresco/repo/security/authority/RMAuthority.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2005-2013 Alfresco Software Limited.
+ *
+ * This file is part of Alfresco
+ *
+ * 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 .
+ */
+package org.alfresco.repo.security.authority;
+
+/**
+ * Interface for defining constants
+ *
+ * @author Tuna Aksoy
+ * @since 2.1
+ */
+public interface RMAuthority
+{
+ /**
+ * The default rm zone.
+ */
+ public static String ZONE_APP_RM = "APP.RM";
+}
diff --git a/rm-server/source/java/org/alfresco/repo/security/authority/RMAuthorityDAOImpl.java b/rm-server/source/java/org/alfresco/repo/security/authority/RMAuthorityDAOImpl.java
new file mode 100644
index 0000000000..bb5c68cfd8
--- /dev/null
+++ b/rm-server/source/java/org/alfresco/repo/security/authority/RMAuthorityDAOImpl.java
@@ -0,0 +1,1408 @@
+/*
+ * Copyright (C) 2005-2011 Alfresco Software Limited.
+ *
+ * This file is part of Alfresco
+ *
+ * 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 .
+ */
+package org.alfresco.repo.security.authority;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.regex.Pattern;
+
+import org.alfresco.error.AlfrescoRuntimeException;
+import org.alfresco.model.ContentModel;
+import org.alfresco.query.CannedQuery;
+import org.alfresco.query.CannedQueryFactory;
+import org.alfresco.query.CannedQueryResults;
+import org.alfresco.query.PagingRequest;
+import org.alfresco.query.PagingResults;
+import org.alfresco.repo.cache.SimpleCache;
+import org.alfresco.repo.domain.permissions.AclDAO;
+import org.alfresco.repo.node.NodeServicePolicies;
+import org.alfresco.repo.policy.JavaBehaviour;
+import org.alfresco.repo.policy.PolicyComponent;
+import org.alfresco.repo.search.impl.lucene.AbstractLuceneQueryParser;
+import org.alfresco.repo.security.authentication.AuthenticationUtil;
+import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
+import org.alfresco.repo.security.person.PersonServiceImpl;
+import org.alfresco.repo.tenant.TenantService;
+import org.alfresco.repo.transaction.AlfrescoTransactionSupport;
+import org.alfresco.service.cmr.dictionary.DictionaryService;
+import org.alfresco.service.cmr.repository.ChildAssociationRef;
+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.repository.datatype.DefaultTypeConverter;
+import org.alfresco.service.cmr.search.ResultSet;
+import org.alfresco.service.cmr.search.ResultSetRow;
+import org.alfresco.service.cmr.search.SearchParameters;
+import org.alfresco.service.cmr.search.SearchService;
+import org.alfresco.service.cmr.security.AuthorityService.AuthorityFilter;
+import org.alfresco.service.cmr.security.AuthorityType;
+import org.alfresco.service.cmr.security.NoSuchPersonException;
+import org.alfresco.service.cmr.security.PersonService;
+import org.alfresco.service.cmr.security.PersonService.PersonInfo;
+import org.alfresco.service.namespace.NamespacePrefixResolver;
+import org.alfresco.service.namespace.NamespaceService;
+import org.alfresco.service.namespace.QName;
+import org.alfresco.service.namespace.RegexQNamePattern;
+import org.alfresco.util.EqualsHelper;
+import org.alfresco.util.ISO9075;
+import org.alfresco.util.Pair;
+import org.alfresco.util.ParameterCheck;
+import org.alfresco.util.SearchLanguageConversion;
+import org.alfresco.util.registry.NamedObjectRegistry;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * FIXME !!!!
+ * This class overrides the original {@link AuthorityDAOImpl} class
+ * We need to modify two private methods in the original class
+ *
+ * addAuthorityNameIfMatches(Set authorities, String authorityName, AuthorityType type)
+ *
+ * and
+ *
+ * addAuthorityNameIfMatches(Set authorities, String authorityName, AuthorityType type, Pattern pattern)
+ *
+ * After changing the modifiers for those methods, this class can extend from the original one and those methods can
+ * overriden in this class. For the time being the whole class has been copied and the needed changes have been
+ * made directly in this class.
+ *
+ */
+public class RMAuthorityDAOImpl implements AuthorityDAO, NodeServicePolicies.BeforeDeleteNodePolicy, NodeServicePolicies.OnUpdatePropertiesPolicy
+{
+ private static Log logger = LogFactory.getLog(AuthorityDAOImpl.class);
+
+ private static final String CANNED_QUERY_AUTHS_LIST = "authsGetAuthoritiesCannedQueryFactory"; // see authority-services-context.xml
+
+ private StoreRef storeRef;
+
+ private NodeService nodeService;
+
+ private NamespacePrefixResolver namespacePrefixResolver;
+
+ private QName qnameAssocSystem;
+
+ private QName qnameAssocAuthorities;
+
+ private QName qnameAssocZones;
+
+ private SearchService searchService;
+
+ private DictionaryService dictionaryService;
+
+ private PersonService personService;
+
+ private TenantService tenantService;
+
+ private SimpleCache, NodeRef> authorityLookupCache;
+
+ private static final NodeRef NULL_NODEREF = new NodeRef("null", "null", "null");
+
+ private SimpleCache> userAuthorityCache;
+
+ private SimpleCache, List> zoneAuthorityCache;
+
+ private SimpleCache> childAuthorityCache;
+
+ /** System Container ref cache (Tennant aware) */
+ private Map systemContainerRefs = new ConcurrentHashMap(4);
+
+ private AclDAO aclDao;
+
+ private PolicyComponent policyComponent;
+
+ /** The number of authorities in a zone to pre-cache, allowing quick generation of 'first n' results. */
+ private int zoneAuthoritySampleSize = 10000;
+
+ private NamedObjectRegistry> cannedQueryRegistry;
+
+ private static final Collection SEARCHABLE_AUTHORITY_TYPES = new LinkedList();
+ static
+ {
+ SEARCHABLE_AUTHORITY_TYPES.add(AuthorityType.ROLE);
+ SEARCHABLE_AUTHORITY_TYPES.add(AuthorityType.GROUP);
+ }
+
+ public RMAuthorityDAOImpl()
+ {
+ super();
+ }
+
+
+ /**
+ * Sets number of authorities in a zone to pre-cache, allowing quick generation of 'first n' results and adaption of
+ * search technique based on hit rate.
+ *
+ * @param zoneAuthoritySampleSize
+ * the zoneAuthoritySampleSize to set
+ */
+ public void setZoneAuthoritySampleSize(int zoneAuthoritySampleSize)
+ {
+ this.zoneAuthoritySampleSize = zoneAuthoritySampleSize;
+ }
+
+ public void setStoreUrl(String storeUrl)
+ {
+ this.storeRef = new StoreRef(storeUrl);
+ }
+
+ public void setDictionaryService(DictionaryService dictionaryService)
+ {
+ this.dictionaryService = dictionaryService;
+ }
+
+ public void setNamespacePrefixResolver(NamespacePrefixResolver namespacePrefixResolver)
+ {
+ this.namespacePrefixResolver = namespacePrefixResolver;
+ qnameAssocSystem = QName.createQName("sys", "system", namespacePrefixResolver);
+ qnameAssocAuthorities = QName.createQName("sys", "authorities", namespacePrefixResolver);
+ qnameAssocZones = QName.createQName("sys", "zones", namespacePrefixResolver);
+ }
+
+ public void setNodeService(NodeService nodeService)
+ {
+ this.nodeService = nodeService;
+ }
+
+ public void setSearchService(SearchService searchService)
+ {
+ this.searchService = searchService;
+ }
+
+ public void setAuthorityLookupCache(SimpleCache, NodeRef> authorityLookupCache)
+ {
+ this.authorityLookupCache = authorityLookupCache;
+ }
+
+ public void setUserAuthorityCache(SimpleCache> userAuthorityCache)
+ {
+ this.userAuthorityCache = userAuthorityCache;
+ }
+
+ public void setZoneAuthorityCache(SimpleCache, List> zoneAuthorityCache)
+ {
+ this.zoneAuthorityCache = zoneAuthorityCache;
+ }
+
+ public void setChildAuthorityCache(SimpleCache> childAuthorityCache)
+ {
+ this.childAuthorityCache = childAuthorityCache;
+ }
+
+ public void setPersonService(PersonService personService)
+ {
+ this.personService = personService;
+ }
+
+ public void setTenantService(TenantService tenantService)
+ {
+ this.tenantService = tenantService;
+ }
+
+ public void setAclDAO(AclDAO aclDao)
+ {
+ this.aclDao = aclDao;
+ }
+
+ public void setPolicyComponent(PolicyComponent policyComponent)
+ {
+ this.policyComponent = policyComponent;
+ }
+
+ public void setCannedQueryRegistry(NamedObjectRegistry> cannedQueryRegistry)
+ {
+ this.cannedQueryRegistry = cannedQueryRegistry;
+ }
+
+
+ public boolean authorityExists(String name)
+ {
+ NodeRef ref = getAuthorityOrNull(name);
+ return ref != null;
+ }
+
+ public void addAuthority(Collection parentNames, String childName)
+ {
+ Set parentRefs = new HashSet(parentNames.size() * 2);
+ AuthorityType authorityType = AuthorityType.getAuthorityType(childName);
+ boolean isUser = authorityType.equals(AuthorityType.USER);
+ boolean notUserOrGroup = !isUser && !authorityType.equals(AuthorityType.GROUP);
+ for (String parentName : parentNames)
+ {
+ NodeRef parentRef = getAuthorityOrNull(parentName);
+ if (parentRef == null)
+ {
+ throw new UnknownAuthorityException("An authority was not found for " + parentName);
+ }
+ if (notUserOrGroup
+ && !(authorityType.equals(AuthorityType.ROLE) && AuthorityType.getAuthorityType(parentName).equals(
+ AuthorityType.ROLE)))
+ {
+ throw new AlfrescoRuntimeException("Authorities of the type " + authorityType
+ + " may not be added to other authorities");
+ }
+ removeCachedChildAuthorities(parentRef);
+ parentRefs.add(parentRef);
+ }
+ NodeRef childRef = getAuthorityOrNull(childName);
+
+ if (childRef == null)
+ {
+ throw new UnknownAuthorityException("An authority was not found for " + childName);
+ }
+
+ // Normalize the user name if necessary
+ if (isUser)
+ {
+ childName = (String) nodeService.getProperty(childRef, ContentModel.PROP_USERNAME);
+ }
+
+ nodeService.addChild(parentRefs, childRef, ContentModel.ASSOC_MEMBER, QName.createQName("cm", childName,
+ namespacePrefixResolver));
+ if (isUser)
+ {
+ userAuthorityCache.remove(childName);
+ }
+ else
+ {
+ userAuthorityCache.clear();
+ }
+ }
+
+ public void createAuthority(String name, String authorityDisplayName, Set authorityZones)
+ {
+ HashMap props = new HashMap();
+ props.put(ContentModel.PROP_AUTHORITY_NAME, name);
+ props.put(ContentModel.PROP_AUTHORITY_DISPLAY_NAME, authorityDisplayName);
+ NodeRef childRef;
+ NodeRef authorityContainerRef = getAuthorityContainer();
+ childRef = nodeService.createNode(authorityContainerRef, ContentModel.ASSOC_CHILDREN, QName.createQName("cm", name, namespacePrefixResolver),
+ ContentModel.TYPE_AUTHORITY_CONTAINER, props).getChildRef();
+ if (authorityZones != null)
+ {
+ Set zoneRefs = new HashSet(authorityZones.size() * 2);
+ String currentUserDomain = tenantService.getCurrentUserDomain();
+ for (String authorityZone : authorityZones)
+ {
+ zoneRefs.add(getOrCreateZone(authorityZone));
+ zoneAuthorityCache.remove(new Pair(currentUserDomain, authorityZone));
+ }
+ zoneAuthorityCache.remove(new Pair(currentUserDomain, null));
+ nodeService.addChild(zoneRefs, childRef, ContentModel.ASSOC_IN_ZONE, QName.createQName("cm", name, namespacePrefixResolver));
+ }
+ authorityLookupCache.put(cacheKey(name), childRef);
+ }
+
+ private NodeRef cacheKey(NodeRef nodeRef)
+ {
+ return tenantService.getName(nodeRef);
+ }
+
+ private Pair cacheKey(String authorityName)
+ {
+ String tenantDomain = AuthorityType.getAuthorityType(authorityName) == AuthorityType.USER ? tenantService.getDomain(authorityName) : tenantService.getCurrentUserDomain();
+ return new Pair(tenantDomain, authorityName);
+ }
+
+ public void deleteAuthority(String name)
+ {
+ NodeRef nodeRef = getAuthorityOrNull(name);
+ if (nodeRef == null)
+ {
+ throw new UnknownAuthorityException("An authority was not found for " + name);
+ }
+ String currentUserDomain = tenantService.getCurrentUserDomain();
+ for (String authorityZone : getAuthorityZones(name))
+ {
+ zoneAuthorityCache.remove(new Pair(currentUserDomain, authorityZone));
+ }
+ zoneAuthorityCache.remove(new Pair(currentUserDomain, null));
+ removeParentsFromChildAuthorityCache(nodeRef);
+ authorityLookupCache.remove(cacheKey(name));
+ userAuthorityCache.clear();
+
+ nodeService.deleteNode(nodeRef);
+ }
+
+ // Get authorities by type and/or zone (both cannot be null)
+ public PagingResults getAuthorities(AuthorityType type, String zoneName, String displayNameFilter, boolean sortByDisplayName, boolean sortAscending, PagingRequest pagingRequest)
+ {
+ ParameterCheck.mandatory("pagingRequest", pagingRequest);
+
+ if ((type == null) && (zoneName == null))
+ {
+ throw new IllegalArgumentException("Type and/or zoneName required - both cannot be null");
+ }
+
+ if ((zoneName == null) && (type.equals(AuthorityType.USER)))
+ {
+ return getUserAuthoritiesImpl(displayNameFilter, sortByDisplayName, sortAscending, pagingRequest);
+ }
+
+ NodeRef containerRef = null;
+ if (zoneName != null)
+ {
+ containerRef = getZone(zoneName);
+ if (containerRef == null)
+ {
+ throw new UnknownAuthorityException("A zone was not found for " + zoneName);
+ }
+ }
+ else
+ {
+ containerRef = getAuthorityContainer();
+ }
+
+ return getAuthoritiesImpl(type, containerRef, displayNameFilter, sortByDisplayName, sortAscending, pagingRequest);
+ }
+
+ private PagingResults getAuthoritiesImpl(AuthorityType type, NodeRef containerRef, String displayNameFilter, boolean sortByDisplayName, boolean sortAscending, PagingRequest pagingRequest)
+ {
+ Long start = (logger.isDebugEnabled() ? System.currentTimeMillis() : null);
+
+ if (type != null)
+ {
+ switch (type)
+ {
+ case GROUP:
+ case ROLE:
+ case USER:
+ // drop through
+ break;
+ default:
+ throw new UnsupportedOperationException("Unexpected authority type: "+type);
+ }
+ }
+
+ // get canned query
+ GetAuthoritiesCannedQueryFactory getAuthoritiesCannedQueryFactory = (GetAuthoritiesCannedQueryFactory)cannedQueryRegistry.getNamedObject(CANNED_QUERY_AUTHS_LIST);
+ CannedQuery cq = getAuthoritiesCannedQueryFactory.getCannedQuery(type, containerRef, displayNameFilter, sortByDisplayName, sortAscending, pagingRequest);
+
+ // execute canned query
+ final CannedQueryResults results = cq.execute();
+
+ PagingResults finalResults = new PagingResults()
+ {
+ @Override
+ public String getQueryExecutionId()
+ {
+ return results.getQueryExecutionId();
+ }
+ @Override
+ public List getPage()
+ {
+ List auths = new ArrayList(results.getPageCount());
+ for (AuthorityInfo authInfo : results.getPage())
+ {
+ auths.add(authInfo.getAuthorityName());
+ }
+ return auths;
+ }
+ @Override
+ public boolean hasMoreItems()
+ {
+ return results.hasMoreItems();
+ }
+ @Override
+ public Pair getTotalResultCount()
+ {
+ return results.getTotalResultCount();
+ }
+ };
+
+ if (start != null)
+ {
+ int cnt = finalResults.getPage().size();
+ int skipCount = pagingRequest.getSkipCount();
+ int maxItems = pagingRequest.getMaxItems();
+ boolean hasMoreItems = finalResults.hasMoreItems();
+ int pageNum = (skipCount / maxItems) + 1;
+
+ logger.debug("getAuthoritiesByType: "+cnt+" items in "+(System.currentTimeMillis()-start)+" msecs [type="+type+",pageNum="+pageNum+",skip="+skipCount+",max="+maxItems+",hasMorePages="+hasMoreItems+",filter="+displayNameFilter+"]");
+ }
+
+ return finalResults;
+ }
+
+ // delegate to PersonService.getPeople
+ private PagingResults getUserAuthoritiesImpl(String displayNameFilter, boolean sortByDisplayName, boolean sortAscending, PagingRequest pagingRequest)
+ {
+ List> filter = null;
+ if (displayNameFilter != null)
+ {
+ filter = new ArrayList>();
+ filter.add(new Pair(ContentModel.PROP_USERNAME, displayNameFilter));
+ }
+
+ List> sort = null;
+ if (sortByDisplayName)
+ {
+ sort = new ArrayList>();
+ sort.add(new Pair(ContentModel.PROP_USERNAME, sortAscending));
+ }
+
+ final PagingResults ppr = personService.getPeople(filter, true, sort, pagingRequest);
+
+ List result = ppr.getPage();
+ final List auths = new ArrayList(result.size());
+
+ for (PersonInfo person : result)
+ {
+ auths.add(person.getUserName());
+ }
+
+ return new PagingResults()
+ {
+ @Override
+ public String getQueryExecutionId()
+ {
+ return ppr.getQueryExecutionId();
+ }
+ @Override
+ public List getPage()
+ {
+ return auths;
+ }
+ @Override
+ public boolean hasMoreItems()
+ {
+ return ppr.hasMoreItems();
+ }
+ @Override
+ public Pair getTotalResultCount()
+ {
+ return ppr.getTotalResultCount();
+ }
+ };
+ }
+
+ public Set getRootAuthorities(AuthorityType type, String zoneName)
+ {
+ NodeRef container = (zoneName == null ? getAuthorityContainer() : getZone(zoneName));
+ if (container == null)
+ {
+ // The zone doesn't even exist so there are no root authorities
+ return Collections.emptySet();
+ }
+
+ return getRootAuthoritiesUnderContainer(container, type);
+ }
+
+ public Set findAuthorities(AuthorityType type, String parentAuthority, boolean immediate,
+ String displayNamePattern, String zoneName)
+ {
+ Long start = (logger.isDebugEnabled() ? System.currentTimeMillis() : null);
+
+ Pattern pattern = displayNamePattern == null ? null : Pattern.compile(SearchLanguageConversion.convert(
+ SearchLanguageConversion.DEF_LUCENE, SearchLanguageConversion.DEF_REGEX, displayNamePattern),
+ Pattern.CASE_INSENSITIVE);
+
+ // Use SQL to determine root authorities
+ Set rootAuthorities = null;
+ if (parentAuthority == null && immediate)
+ {
+ rootAuthorities = getRootAuthorities(type, zoneName);
+ if (pattern == null)
+ {
+ if (start != null)
+ {
+ logger.debug("findAuthorities (rootAuthories): "+rootAuthorities.size()+" items in "+(System.currentTimeMillis()-start)+" msecs [type="+type+",zone="+zoneName+"]");
+ }
+
+ return rootAuthorities;
+ }
+ }
+
+ // Use a Lucene search for other criteria
+ Set authorities = new TreeSet();
+ SearchParameters sp = new SearchParameters();
+ sp.addStore(this.storeRef);
+ sp.setLanguage("lucene");
+ StringBuilder query = new StringBuilder(500);
+ if (type == null || type == AuthorityType.USER)
+ {
+ if (type == null)
+ {
+ query.append("((");
+ }
+ query.append("TYPE:\"").append(ContentModel.TYPE_PERSON).append("\"");
+ if (displayNamePattern != null)
+ {
+ query.append(" AND @").append(
+ AbstractLuceneQueryParser.escape("{" + ContentModel.PROP_USERNAME.getNamespaceURI() + "}"
+ + ISO9075.encode(ContentModel.PROP_USERNAME.getLocalName()))).append(":\"").append(
+ AbstractLuceneQueryParser.escape(displayNamePattern)).append("\"");
+
+ }
+ if (type == null)
+ {
+ query.append(") OR (");
+ }
+ }
+ if (type != AuthorityType.USER)
+ {
+ query.append("TYPE:\"").append(ContentModel.TYPE_AUTHORITY_CONTAINER).append("\"");
+ if (displayNamePattern != null)
+ {
+ query.append(" AND (");
+ if (!displayNamePattern.startsWith("*"))
+ {
+ // Allow for the appropriate type prefix in the authority name
+ Collection authorityTypes = type == null ? SEARCHABLE_AUTHORITY_TYPES
+ : Collections.singleton(type);
+ boolean first = true;
+ for (AuthorityType subType: authorityTypes)
+ {
+ if (first)
+ {
+ first = false;
+ }
+ else
+ {
+ query.append(" OR ");
+ }
+ query.append("@").append(
+ AbstractLuceneQueryParser.escape("{" + ContentModel.PROP_AUTHORITY_NAME.getNamespaceURI() + "}"
+ + ISO9075.encode(ContentModel.PROP_AUTHORITY_NAME.getLocalName()))).append(":\"");
+ query.append(getName(subType, AbstractLuceneQueryParser.escape(displayNamePattern))).append("\"");
+
+ }
+ }
+ else
+ {
+ query.append("@").append(
+ AbstractLuceneQueryParser.escape("{" + ContentModel.PROP_AUTHORITY_NAME.getNamespaceURI() + "}"
+ + ISO9075.encode(ContentModel.PROP_AUTHORITY_NAME.getLocalName()))).append(":\"");
+ query.append(getName(type, AbstractLuceneQueryParser.escape(displayNamePattern))).append("\"");
+ }
+ query.append(" OR @").append(
+ AbstractLuceneQueryParser.escape("{" + ContentModel.PROP_AUTHORITY_DISPLAY_NAME.getNamespaceURI() + "}"
+ + ISO9075.encode(ContentModel.PROP_AUTHORITY_DISPLAY_NAME.getLocalName()))).append(
+ ":\"").append(AbstractLuceneQueryParser.escape(displayNamePattern)).append("\")");
+ }
+ if (type == null)
+ {
+ query.append("))");
+ }
+ }
+ if (parentAuthority != null)
+ {
+ if(immediate)
+ {
+ // use PARENT
+ NodeRef parentAuthorityNodeRef = getAuthorityNodeRefOrNull(parentAuthority);
+ if(parentAuthorityNodeRef != null)
+ {
+ query.append(" AND PARENT:\"").append(AbstractLuceneQueryParser.escape(parentAuthorityNodeRef.toString())).append("\"");
+ }
+ else
+ {
+ throw new UnknownAuthorityException("An authority was not found for " + parentAuthority);
+ }
+ }
+ else
+ {
+ // use PATH
+ query.append(" AND PATH:\"/sys:system/sys:authorities/cm:").append(ISO9075.encode(parentAuthority));
+ query.append("//*\"");
+ }
+ }
+ if (zoneName != null)
+ {
+ // Zones are all direct links to those within so it is safe to use PARENT to look them up
+ NodeRef zoneNodeRef = getZone(zoneName);
+ if (zoneNodeRef != null)
+ {
+ query.append(" AND PARENT:\"").append(AbstractLuceneQueryParser.escape(zoneNodeRef.toString())).append("\"");
+ }
+ else
+ {
+ throw new UnknownAuthorityException("A zone was not found for " + zoneName);
+ }
+ }
+ sp.setQuery(query.toString());
+ sp.setMaxItems(100);
+ ResultSet rs = null;
+ try
+ {
+ rs = searchService.query(sp);
+
+ for (ResultSetRow row : rs)
+ {
+ NodeRef nodeRef = row.getNodeRef();
+ QName idProp = dictionaryService.isSubClass(nodeService.getType(nodeRef),
+ ContentModel.TYPE_AUTHORITY_CONTAINER) ? ContentModel.PROP_AUTHORITY_NAME
+ : ContentModel.PROP_USERNAME;
+ addAuthorityNameIfMatches(authorities, DefaultTypeConverter.INSTANCE.convert(String.class, nodeService
+ .getProperty(nodeRef, idProp)), type, pattern);
+ }
+
+ // If we asked for root authorities, we must do an intersection with the set of root authorities
+ if (rootAuthorities != null)
+ {
+ authorities.retainAll(rootAuthorities);
+ }
+
+ if (start != null)
+ {
+ logger.debug("findAuthorities: "+authorities.size()+" items in "+(System.currentTimeMillis()-start)+" msecs [type="+type+",zone="+zoneName+",parent="+parentAuthority+",immediate="+immediate+",filter="+displayNamePattern+"]");
+ }
+
+ return authorities;
+ }
+ finally
+ {
+ if (rs != null)
+ {
+ rs.close();
+ }
+ }
+ }
+
+ public Set getContainedAuthorities(AuthorityType type, String parentName, boolean immediate)
+ {
+ AuthorityType parentAuthorityType = AuthorityType.getAuthorityType(parentName);
+ if (parentAuthorityType == AuthorityType.USER)
+ {
+ // Users never contain other authorities
+ return Collections. emptySet();
+ }
+ else
+ {
+ NodeRef nodeRef = getAuthorityOrNull(parentName);
+ if (nodeRef == null)
+ {
+ throw new UnknownAuthorityException("An authority was not found for " + parentName);
+ }
+
+ Set authorities = new TreeSet();
+ listAuthorities(type, nodeRef, authorities, false, !immediate, false);
+ return authorities;
+ }
+ }
+
+ public void removeAuthority(String parentName, String childName)
+ {
+ NodeRef parentRef = getAuthorityOrNull(parentName);
+ if (parentRef == null)
+ {
+ throw new UnknownAuthorityException("An authority was not found for " + parentName);
+ }
+ NodeRef childRef = getAuthorityOrNull(childName);
+ if (childRef == null)
+ {
+ throw new UnknownAuthorityException("An authority was not found for " + childName);
+ }
+ nodeService.removeChild(parentRef, childRef);
+ removeCachedChildAuthorities(parentRef);
+ if (AuthorityType.getAuthorityType(childName) == AuthorityType.USER)
+ {
+ userAuthorityCache.remove(childName);
+ }
+ else
+ {
+ userAuthorityCache.clear();
+ }
+ }
+
+ public Set getContainingAuthorities(AuthorityType type, String name, boolean immediate)
+ {
+ // Optimize for the case where we want all the authorities that a user belongs to
+ if (!immediate && AuthorityType.getAuthorityType(name) == AuthorityType.USER)
+ {
+ // Get the unfiltered set of authorities from the cache or generate it
+ Set authorities = userAuthorityCache.get(name);
+ if (authorities == null)
+ {
+ authorities = new TreeSet();
+ listAuthorities(null, name, authorities, true, true);
+ userAuthorityCache.put(name, authorities);
+ }
+ // If we wanted the unfiltered set we are done
+ if (type == null)
+ {
+ return authorities;
+ }
+ // Apply the filtering by type
+ Set filteredAuthorities = new TreeSet();
+ for (String authority : authorities)
+ {
+ addAuthorityNameIfMatches(filteredAuthorities, authority, type);
+ }
+ return filteredAuthorities;
+ }
+ // Otherwise, crawl the DB for the answer
+ else
+ {
+ Set authorities = new TreeSet();
+ listAuthorities(type, name, authorities, true, !immediate);
+ return authorities;
+ }
+ }
+
+ public Set getContainingAuthoritiesInZone(AuthorityType type, String authority, final String zoneName, AuthorityFilter filter, int size)
+ {
+ // Retrieved the cached 'sample' of authorities in the zone
+ String currentUserDomain = tenantService.getCurrentUserDomain();
+ Pair cacheKey = new Pair(currentUserDomain, zoneName);
+ List zoneAuthorities = zoneAuthorityCache.get(cacheKey);
+ final int maxToProcess = Math.max(size, zoneAuthoritySampleSize);
+ if (zoneAuthorities == null)
+ {
+ zoneAuthorities = AuthenticationUtil.runAs(new RunAsWork>()
+ {
+ @Override
+ public List doWork() throws Exception
+ {
+ NodeRef root = zoneName == null ? getAuthorityContainer() : getZone(zoneName);
+ if (root == null)
+ {
+ return Collections.emptyList();
+ }
+ return nodeService.getChildAssocs(root, null, null, maxToProcess, false);
+ }
+ }, tenantService.getDomainUser(AuthenticationUtil.getSystemUserName(), currentUserDomain));
+ zoneAuthorityCache.put(cacheKey, zoneAuthorities);
+ }
+
+ // Now search each for the required authority. If the number of results is greater than or close to the size
+ // limit, then this will be the most efficient route
+ Set result = new TreeSet();
+ final int maxResults = size > 0 ? size : Integer.MAX_VALUE;
+ int hits = 0, processed = 0;
+ for (ChildAssociationRef groupAssoc : zoneAuthorities)
+ {
+ String containing = groupAssoc.getQName().getLocalName();
+ AuthorityType containingType = AuthorityType.getAuthorityType(containing);
+ processed++;
+ // Cache the authority by key, if appropriate
+ switch (containingType)
+ {
+ case USER:
+ case ADMIN:
+ case GUEST:
+ break;
+ default:
+ Pair containingKey = cacheKey(containing);
+ if (!authorityLookupCache.contains(containingKey))
+ {
+ authorityLookupCache.put(containingKey, groupAssoc.getChildRef());
+ }
+ }
+ if ((type == null || containingType == type)
+ && (authority == null || isAuthorityContained(groupAssoc.getChildRef(), authority))
+ && (filter == null || filter.includeAuthority(containing)))
+ {
+ result.add(containing);
+ if (++hits == maxResults)
+ {
+ break;
+ }
+ }
+
+ // If this top down search is not providing an adequate hit count then resort to a naiive unlimited search
+ if (processed >= maxToProcess)
+ {
+ Set unfilteredResult;
+ boolean filterZone;
+ if (authority == null)
+ {
+ unfilteredResult = new HashSet(getAuthorities(type, zoneName, null, false, true, new PagingRequest(0, filter == null ? maxResults : Integer.MAX_VALUE, null)).getPage());
+ if (filter == null)
+ {
+ return unfilteredResult;
+ }
+ filterZone = false;
+ }
+ else
+ {
+ unfilteredResult = getContainingAuthorities(type, authority, false);
+ filterZone = zoneName != null;
+ }
+ Set newResult = new TreeSet(result);
+ int i=newResult.size();
+ for (String container : unfilteredResult)
+ {
+ // Do not call the filter multiple times on the same result in case it is 'stateful'
+ if (!result.contains(container) && (filter == null || filter.includeAuthority(container))
+ && (!filterZone || getAuthorityZones(container).contains(zoneName)))
+ {
+ newResult.add(container);
+ if (++i >= maxResults)
+ {
+ break;
+ }
+ }
+ }
+ result = newResult;
+ break;
+ }
+ }
+ return result;
+ }
+
+ public String getShortName(String name)
+ {
+ AuthorityType type = AuthorityType.getAuthorityType(name);
+ if (type.isFixedString())
+ {
+ return "";
+ }
+ else if (type.isPrefixed())
+ {
+ return name.substring(type.getPrefixString().length());
+ }
+ else
+ {
+ return name;
+ }
+ }
+
+ public String getName(AuthorityType type, String shortName)
+ {
+ if (type.isFixedString())
+ {
+ return type.getFixedString();
+ }
+ else if (type.isPrefixed())
+ {
+ return type.getPrefixString() + shortName;
+ }
+ else
+ {
+ return shortName;
+ }
+ }
+
+ private void addAuthorityNameIfMatches(Set authorities, String authorityName, AuthorityType type)
+ {
+ if (type == null || AuthorityType.getAuthorityType(authorityName).equals(type) && !getAuthorityZones(authorityName).contains("APP.RM"))
+ {
+ authorities.add(authorityName);
+ }
+ }
+
+ private void addAuthorityNameIfMatches(Set authorities, String authorityName, AuthorityType type, Pattern pattern)
+ {
+ if (type == null || AuthorityType.getAuthorityType(authorityName).equals(type) && !getAuthorityZones(authorityName).contains("APP.RM"))
+ {
+ if (pattern == null)
+ {
+ authorities.add(authorityName);
+ }
+ else
+ {
+ if (pattern.matcher(getShortName(authorityName)).matches())
+ {
+ authorities.add(authorityName);
+ }
+ else
+ {
+ String displayName = getAuthorityDisplayName(authorityName);
+ if (displayName != null && pattern.matcher(displayName).matches())
+ {
+ authorities.add(authorityName);
+ }
+ }
+ }
+ }
+ }
+
+ private void listAuthorities(AuthorityType type, String name, Set authorities, boolean parents, boolean recursive)
+ {
+ AuthorityType localType = AuthorityType.getAuthorityType(name);
+ if (localType.equals(AuthorityType.GUEST))
+ {
+ // Nothing to do
+ }
+ else
+ {
+ NodeRef ref = getAuthorityOrNull(name);
+
+ if (ref != null)
+ {
+ listAuthorities(type, ref, authorities, parents, recursive, false);
+ }
+ else if (!localType.equals(AuthorityType.USER))
+ {
+ // Don't worry about missing person objects. It might be the system user or a user yet to be
+ // auto-created
+ throw new UnknownAuthorityException("An authority was not found for " + name);
+ }
+ }
+ }
+
+ private void listAuthorities(AuthorityType type, NodeRef nodeRef, Set authorities, boolean parents, boolean recursive, boolean includeNode)
+ {
+ if (includeNode)
+ {
+ String authorityName = DefaultTypeConverter.INSTANCE.convert(String.class, nodeService
+ .getProperty(nodeRef, dictionaryService.isSubClass(nodeService.getType(nodeRef),
+ ContentModel.TYPE_AUTHORITY_CONTAINER) ? ContentModel.PROP_AUTHORITY_NAME
+ : ContentModel.PROP_USERNAME));
+ addAuthorityNameIfMatches(authorities, authorityName, type);
+ }
+
+ // Loop over children if we want immediate children or are in recursive mode
+ if (!includeNode || recursive)
+ {
+ if (parents)
+ {
+ List cars = nodeService.getParentAssocs(nodeRef, ContentModel.ASSOC_MEMBER, RegexQNamePattern.MATCH_ALL);
+
+ for (ChildAssociationRef car : cars)
+ {
+ listAuthorities(type, car.getParentRef(), authorities, true, recursive, true);
+ }
+ }
+ else
+ {
+ List cars = getCachedChildAuthorities(nodeRef);
+ if (cars == null)
+ {
+ cars = nodeService.getChildAssocs(nodeRef, RegexQNamePattern.MATCH_ALL,
+ RegexQNamePattern.MATCH_ALL, false);
+ if (!cars.isEmpty() && cars.get(0).getTypeQName().equals(ContentModel.ASSOC_MEMBER))
+ {
+ putCachedChildAuthorities(nodeRef, cars);
+ }
+ }
+
+ // Take advantage of the fact that the authority name is on the child association
+ for (ChildAssociationRef car : cars)
+ {
+ String childName = car.getQName().getLocalName();
+ AuthorityType childType = AuthorityType.getAuthorityType(childName);
+ addAuthorityNameIfMatches(authorities, childName, type);
+ if (recursive && childType != AuthorityType.USER)
+ {
+ listAuthorities(type, car.getChildRef(), authorities, false, true, false);
+ }
+ }
+ }
+ }
+ }
+
+
+ // Take advantage of the fact that the authority name is on the child association
+ public boolean isAuthorityContained(NodeRef authorityNodeRef, String authorityToFind)
+ {
+ List cars = getCachedChildAuthorities(authorityNodeRef);
+ if (cars == null)
+ {
+ cars = nodeService.getChildAssocs(authorityNodeRef, RegexQNamePattern.MATCH_ALL,
+ RegexQNamePattern.MATCH_ALL, false);
+ putCachedChildAuthorities(authorityNodeRef, cars);
+ }
+
+ // Loop over children recursively to find authorityToFind
+ for (ChildAssociationRef car : cars)
+ {
+ String authorityName = car.getQName().getLocalName();
+ if (authorityToFind.equals(authorityName)
+ || AuthorityType.getAuthorityType(authorityName) != AuthorityType.USER
+ && isAuthorityContained(car.getChildRef(), authorityToFind))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void removeParentsFromChildAuthorityCache(NodeRef nodeRef)
+ {
+ for (ChildAssociationRef car: nodeService.getParentAssocs(nodeRef))
+ {
+ NodeRef parentRef = car.getParentRef();
+ if (dictionaryService.isSubClass(nodeService.getType(parentRef), ContentModel.TYPE_AUTHORITY_CONTAINER))
+ {
+ removeCachedChildAuthorities(parentRef);
+ }
+ }
+ }
+
+ private NodeRef getAuthorityOrNull(final String name)
+ {
+ try
+ {
+ if (AuthorityType.getAuthorityType(name).equals(AuthorityType.USER))
+ {
+ return personService.getPerson(name, false);
+ }
+ else if (AuthorityType.getAuthorityType(name).equals(AuthorityType.GUEST) || AuthorityType.getAuthorityType(name).equals(AuthorityType.ADMIN))
+ {
+ return null;
+ }
+ else
+ {
+ Pair cacheKey = cacheKey(name);
+ NodeRef result = authorityLookupCache.get(cacheKey);
+ if (result == null)
+ {
+ List results = nodeService.getChildAssocs(getAuthorityContainer(),
+ ContentModel.ASSOC_CHILDREN, QName.createQName("cm", name, namespacePrefixResolver), false);
+ result = results.isEmpty() ? NULL_NODEREF :results.get(0).getChildRef();
+ authorityLookupCache.put(cacheKey, result);
+ }
+ return result == NULL_NODEREF ? null : result;
+ }
+ }
+ catch (NoSuchPersonException e)
+ {
+ return null;
+ }
+ }
+
+ /**
+ * @return Returns the authority container, which must exist
+ */
+ private NodeRef getAuthorityContainer()
+ {
+ return getSystemContainer(qnameAssocAuthorities);
+ }
+
+ /**
+ * @return Returns the zone container, which must exist
+ */
+ private NodeRef getZoneContainer()
+ {
+ return getSystemContainer(qnameAssocZones);
+ }
+
+ /**
+ * Return the system container for the specified assoc name.
+ * The containers are cached in a thread safe Tenant aware cache.
+ *
+ * @param assocQName
+ *
+ * @return System container, which must exist
+ */
+ private NodeRef getSystemContainer(QName assocQName)
+ {
+ final String cacheKey = tenantService.getCurrentUserDomain() + assocQName.toString();
+ NodeRef systemContainerRef = systemContainerRefs.get(cacheKey);
+ if (systemContainerRef == null)
+ {
+ NodeRef rootNodeRef = nodeService.getRootNode(this.storeRef);
+ List results = nodeService.getChildAssocs(rootNodeRef, RegexQNamePattern.MATCH_ALL, qnameAssocSystem, false);
+ if (results.size() == 0)
+ {
+ throw new AlfrescoRuntimeException("Required system path not found: " + qnameAssocSystem);
+ }
+ NodeRef sysNodeRef = results.get(0).getChildRef();
+ results = nodeService.getChildAssocs(sysNodeRef, RegexQNamePattern.MATCH_ALL, assocQName, false);
+ if (results.size() == 0)
+ {
+ throw new AlfrescoRuntimeException("Required path not found: " + assocQName);
+ }
+ systemContainerRef = results.get(0).getChildRef();
+ systemContainerRefs.put(cacheKey, systemContainerRef);
+ }
+ return systemContainerRef;
+ }
+
+ public NodeRef getAuthorityNodeRefOrNull(String name)
+ {
+ return getAuthorityOrNull(name);
+ }
+
+ public String getAuthorityName(NodeRef authorityRef)
+ {
+ String name = null;
+ if (nodeService.exists(authorityRef))
+ {
+ QName type = nodeService.getType(authorityRef);
+ if (dictionaryService.isSubClass(type, ContentModel.TYPE_AUTHORITY_CONTAINER))
+ {
+ name = (String) nodeService.getProperty(authorityRef, ContentModel.PROP_AUTHORITY_NAME);
+ }
+ else if (dictionaryService.isSubClass(type, ContentModel.TYPE_PERSON))
+ {
+ name = (String) nodeService.getProperty(authorityRef, ContentModel.PROP_USERNAME);
+ }
+ }
+ return name;
+ }
+
+ public String getAuthorityDisplayName(String authorityName)
+ {
+ NodeRef ref = getAuthorityOrNull(authorityName);
+ if (ref == null)
+ {
+ return null;
+ }
+ Serializable value = nodeService.getProperty(ref, ContentModel.PROP_AUTHORITY_DISPLAY_NAME);
+ if (value == null)
+ {
+ return null;
+ }
+ return DefaultTypeConverter.INSTANCE.convert(String.class, value);
+ }
+
+ public void setAuthorityDisplayName(String authorityName, String authorityDisplayName)
+ {
+ NodeRef ref = getAuthorityOrNull(authorityName);
+ if (ref == null)
+ {
+ return;
+ }
+ nodeService.setProperty(ref, ContentModel.PROP_AUTHORITY_DISPLAY_NAME, authorityDisplayName);
+
+ }
+
+ public NodeRef getOrCreateZone(String zoneName)
+ {
+ return getOrCreateZone(zoneName, true);
+ }
+
+ private NodeRef getOrCreateZone(String zoneName, boolean create)
+ {
+ NodeRef zoneContainerRef = getZoneContainer();
+ QName zoneQName = QName.createQName("cm", zoneName, namespacePrefixResolver);
+ List results = nodeService.getChildAssocs(zoneContainerRef, ContentModel.ASSOC_CHILDREN, zoneQName, false);
+ if (results.isEmpty())
+ {
+ if (create)
+ {
+ HashMap props = new HashMap();
+ props.put(ContentModel.PROP_NAME, zoneName);
+ return nodeService.createNode(zoneContainerRef, ContentModel.ASSOC_CHILDREN, zoneQName, ContentModel.TYPE_ZONE, props).getChildRef();
+ }
+ else
+ {
+ return null;
+ }
+ }
+ else
+ {
+ return results.get(0).getChildRef();
+ }
+ }
+
+ public NodeRef getZone(String zoneName)
+ {
+ return getOrCreateZone(zoneName, false);
+ }
+
+ public Set getAuthorityZones(String name)
+ {
+ Set zones = new TreeSet();
+ NodeRef childRef = getAuthorityOrNull(name);
+ if (childRef == null)
+ {
+ return null;
+ }
+ List results = nodeService.getParentAssocs(childRef, ContentModel.ASSOC_IN_ZONE, RegexQNamePattern.MATCH_ALL);
+ if (results.isEmpty())
+ {
+ return zones;
+ }
+
+ for (ChildAssociationRef current : results)
+ {
+ NodeRef zoneRef = current.getParentRef();
+ Serializable value = nodeService.getProperty(zoneRef, ContentModel.PROP_NAME);
+ if (value == null)
+ {
+ continue;
+ }
+ else
+ {
+ String zone = DefaultTypeConverter.INSTANCE.convert(String.class, value);
+ zones.add(zone);
+ }
+ }
+ return zones;
+ }
+
+ public Set getAllAuthoritiesInZone(String zoneName, AuthorityType type)
+ {
+ NodeRef zoneRef = getZone(zoneName);
+ if (zoneRef == null)
+ {
+ return Collections.emptySet();
+ }
+ return new HashSet(getAuthoritiesImpl(type, zoneRef, null, false, false, new PagingRequest(0, Integer.MAX_VALUE, null)).getPage());
+ }
+
+ public void addAuthorityToZones(String authorityName, Set zones)
+ {
+ if ((zones != null) && (zones.size() > 0))
+ {
+ Set zoneRefs = new HashSet(zones.size() * 2);
+ for (String authorityZone : zones)
+ {
+ zoneRefs.add(getOrCreateZone(authorityZone));
+ }
+ NodeRef authRef = getAuthorityOrNull(authorityName);
+ if (authRef != null)
+ {
+ // Normalize the user name if necessary
+ if (AuthorityType.getAuthorityType(authorityName) == AuthorityType.USER)
+ {
+ authorityName = (String) nodeService.getProperty(authRef, ContentModel.PROP_USERNAME);
+ }
+
+ nodeService.addChild(zoneRefs, authRef, ContentModel.ASSOC_IN_ZONE, QName.createQName("cm", authorityName, namespacePrefixResolver));
+ }
+ }
+ }
+
+ public void removeAuthorityFromZones(String authorityName, Set zones)
+ {
+ if ((zones != null) && (zones.size() > 0))
+ {
+ NodeRef authRef = getAuthorityOrNull(authorityName);
+ List results = nodeService.getParentAssocs(authRef, ContentModel.ASSOC_IN_ZONE, RegexQNamePattern.MATCH_ALL);
+ for (ChildAssociationRef current : results)
+ {
+ NodeRef zoneRef = current.getParentRef();
+ Serializable value = nodeService.getProperty(zoneRef, ContentModel.PROP_NAME);
+ if (value == null)
+ {
+ continue;
+ }
+ else
+ {
+ String testZone = DefaultTypeConverter.INSTANCE.convert(String.class, value);
+ if (zones.contains(testZone))
+ {
+ nodeService.removeChildAssociation(current);
+ }
+ }
+ }
+ }
+ }
+
+ private Set getRootAuthoritiesUnderContainer(NodeRef container, AuthorityType type)
+ {
+ if (type != null && type.equals(AuthorityType.USER))
+ {
+ return Collections. emptySet();
+ }
+ Collection childRefs = nodeService.getChildAssocsWithoutParentAssocsOfType(container, ContentModel.ASSOC_MEMBER);
+ Set authorities = new TreeSet();
+ for (ChildAssociationRef childRef : childRefs)
+ {
+ addAuthorityNameIfMatches(authorities, childRef.getQName().getLocalName(), type);
+ }
+ return authorities;
+ }
+
+ // Listen out for person removals so that we can clear cached authorities
+ public void beforeDeleteNode(NodeRef nodeRef)
+ {
+ userAuthorityCache.remove(getAuthorityName(nodeRef));
+ removeParentsFromChildAuthorityCache(nodeRef);
+ }
+
+ public void onUpdateProperties(NodeRef nodeRef, Map before, Map after)
+ {
+ boolean isAuthority = dictionaryService.isSubClass(nodeService.getType(nodeRef),
+ ContentModel.TYPE_AUTHORITY_CONTAINER);
+ QName idProp = isAuthority ? ContentModel.PROP_AUTHORITY_NAME : ContentModel.PROP_USERNAME;
+ String authBefore = DefaultTypeConverter.INSTANCE.convert(String.class, before.get(idProp));
+ if (authBefore == null)
+ {
+ // Node has just been created; nothing to do
+ return;
+ }
+ String authAfter = DefaultTypeConverter.INSTANCE.convert(String.class, after.get(idProp));
+ if (!EqualsHelper.nullSafeEquals(authBefore, authAfter))
+ {
+ if (AlfrescoTransactionSupport.getResource(PersonServiceImpl.KEY_ALLOW_UID_UPDATE) != null || authBefore.equalsIgnoreCase(authAfter))
+ {
+ if (isAuthority)
+ {
+ if (authBefore != null)
+ {
+ // Fix any ACLs
+ aclDao.renameAuthority(authBefore, authAfter);
+ }
+
+ // Fix primary association local name
+ QName newAssocQName = QName.createQName("cm", authAfter, namespacePrefixResolver);
+ ChildAssociationRef assoc = nodeService.getPrimaryParent(nodeRef);
+ nodeService.moveNode(nodeRef, assoc.getParentRef(), assoc.getTypeQName(), newAssocQName);
+
+ // Fix other non-case sensitive parent associations
+ QName oldAssocQName = QName.createQName("cm", authBefore, namespacePrefixResolver);
+ newAssocQName = QName.createQName("cm", authAfter, namespacePrefixResolver);
+ for (ChildAssociationRef parent : nodeService.getParentAssocs(nodeRef))
+ {
+ if (!parent.isPrimary() && parent.getQName().equals(oldAssocQName))
+ {
+ nodeService.removeChildAssociation(parent);
+ nodeService.addChild(parent.getParentRef(), parent.getChildRef(), parent.getTypeQName(),
+ newAssocQName);
+ }
+ }
+ authorityLookupCache.clear();
+
+ // Cache is out of date
+ userAuthorityCache.clear();
+ }
+ else
+ {
+ userAuthorityCache.remove(authBefore);
+ }
+ removeParentsFromChildAuthorityCache(nodeRef);
+ }
+ else
+ {
+ throw new UnsupportedOperationException("The name of an authority can not be changed");
+ }
+ }
+ }
+
+ public void init()
+ {
+ // Listen out for person removals so that we can clear cached authorities
+ this.policyComponent.bindClassBehaviour(QName.createQName(NamespaceService.ALFRESCO_URI, "beforeDeleteNode"), ContentModel.TYPE_PERSON, new JavaBehaviour(
+ this, "beforeDeleteNode"));
+ // Listen out for updates to persons and authority containers to handle renames
+ this.policyComponent.bindClassBehaviour(QName.createQName(NamespaceService.ALFRESCO_URI, "onUpdateProperties"), ContentModel.TYPE_AUTHORITY, new JavaBehaviour(
+ this, "onUpdateProperties"));
+ }
+
+ private List getCachedChildAuthorities(NodeRef parentNodeRef)
+ {
+ return childAuthorityCache.get(cacheKey(parentNodeRef));
+ }
+
+ private void removeCachedChildAuthorities(NodeRef parentNodeRef)
+ {
+ childAuthorityCache.remove(cacheKey(parentNodeRef));
+ }
+
+ private void putCachedChildAuthorities(NodeRef parentNodeRef, List children)
+ {
+ childAuthorityCache.put(cacheKey(parentNodeRef), children);
+ }
+}