mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-07-24 17:32:48 +00:00
Improvements to cold permission performance and caching.
git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@5247 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
@@ -20,7 +20,7 @@
|
|||||||
* and Open Source Software ("FLOSS") applications as described in Alfresco's
|
* and Open Source Software ("FLOSS") applications as described in Alfresco's
|
||||||
* FLOSS exception. You should have recieved a copy of the text describing
|
* FLOSS exception. You should have recieved a copy of the text describing
|
||||||
* the FLOSS exception, and it is also available here:
|
* the FLOSS exception, and it is also available here:
|
||||||
* http://www.alfresco.com/legal/licensing"
|
* http://www.alfresco.com/legal/licensing
|
||||||
*/
|
*/
|
||||||
package org.alfresco.repo.security.permissions.impl;
|
package org.alfresco.repo.security.permissions.impl;
|
||||||
|
|
||||||
@@ -61,17 +61,15 @@ import org.apache.commons.logging.LogFactory;
|
|||||||
import org.springframework.beans.factory.InitializingBean;
|
import org.springframework.beans.factory.InitializingBean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Alfresco implementation of a permissions service against our APIs for the
|
* The Alfresco implementation of a permissions service against our APIs for the permissions model and permissions persistence.
|
||||||
* permissions model and permissions persistence.
|
|
||||||
*
|
*
|
||||||
* @author andyh
|
* @author andyh
|
||||||
*/
|
*/
|
||||||
public class PermissionServiceImpl implements PermissionServiceSPI, InitializingBean
|
public class PermissionServiceImpl implements PermissionServiceSPI, InitializingBean
|
||||||
{
|
{
|
||||||
|
|
||||||
static SimplePermissionReference OLD_ALL_PERMISSIONS_REFERENCE = new SimplePermissionReference(
|
static SimplePermissionReference OLD_ALL_PERMISSIONS_REFERENCE = new SimplePermissionReference(QName.createQName(
|
||||||
QName.createQName("", PermissionService.ALL_PERMISSIONS),
|
"", PermissionService.ALL_PERMISSIONS), PermissionService.ALL_PERMISSIONS);
|
||||||
PermissionService.ALL_PERMISSIONS);
|
|
||||||
|
|
||||||
private static Log log = LogFactory.getLog(PermissionServiceImpl.class);
|
private static Log log = LogFactory.getLog(PermissionServiceImpl.class);
|
||||||
|
|
||||||
@@ -205,7 +203,7 @@ public class PermissionServiceImpl implements PermissionServiceSPI, Initializing
|
|||||||
{
|
{
|
||||||
throw new IllegalArgumentException("Property 'authenticationComponent' has not been set");
|
throw new IllegalArgumentException("Property 'authenticationComponent' has not been set");
|
||||||
}
|
}
|
||||||
if(authorityService == null)
|
if (authorityService == null)
|
||||||
{
|
{
|
||||||
throw new IllegalArgumentException("Property 'authorityService' has not been set");
|
throw new IllegalArgumentException("Property 'authorityService' has not been set");
|
||||||
}
|
}
|
||||||
@@ -218,7 +216,8 @@ public class PermissionServiceImpl implements PermissionServiceSPI, Initializing
|
|||||||
throw new IllegalArgumentException("Property 'policyComponent' has not been set");
|
throw new IllegalArgumentException("Property 'policyComponent' has not been set");
|
||||||
}
|
}
|
||||||
|
|
||||||
policyComponent.bindClassBehaviour(QName.createQName(NamespaceService.ALFRESCO_URI, "onMoveNode"), ContentModel.TYPE_BASE, new JavaBehaviour(this, "onMoveNode"));
|
policyComponent.bindClassBehaviour(QName.createQName(NamespaceService.ALFRESCO_URI, "onMoveNode"),
|
||||||
|
ContentModel.TYPE_BASE, new JavaBehaviour(this, "onMoveNode"));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -320,8 +319,7 @@ public class PermissionServiceImpl implements PermissionServiceSPI, Initializing
|
|||||||
@Override
|
@Override
|
||||||
public String toString()
|
public String toString()
|
||||||
{
|
{
|
||||||
return accessStatus + " " + this.permission + " - " +
|
return accessStatus + " " + this.permission + " - " + this.authority + " (" + this.authorityType + ")";
|
||||||
this.authority + " (" + this.authorityType + ")";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -397,17 +395,12 @@ public class PermissionServiceImpl implements PermissionServiceSPI, Initializing
|
|||||||
return AccessStatus.ALLOWED;
|
return AccessStatus.ALLOWED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Get the current authentications
|
// Get the current authentications
|
||||||
// Use the smart authentication cache to improve permissions performance
|
// Use the smart authentication cache to improve permissions performance
|
||||||
Authentication auth = authenticationComponent.getCurrentAuthentication();
|
Authentication auth = authenticationComponent.getCurrentAuthentication();
|
||||||
Set<String> authorisations = getAuthorisations(auth, nodeRef);
|
Set<String> authorisations = getAuthorisations(auth, nodeRef);
|
||||||
|
|
||||||
Serializable key = generateKey(
|
Serializable key = generateKey(authorisations, nodeRef, perm, CacheType.HAS_PERMISSION);
|
||||||
authorisations,
|
|
||||||
nodeRef,
|
|
||||||
perm);
|
|
||||||
AccessStatus status = accessCache.get(key);
|
AccessStatus status = accessCache.get(key);
|
||||||
if (status != null)
|
if (status != null)
|
||||||
{
|
{
|
||||||
@@ -436,8 +429,7 @@ public class PermissionServiceImpl implements PermissionServiceSPI, Initializing
|
|||||||
//
|
//
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Does the current authentication have the supplied permission on the
|
* Does the current authentication have the supplied permission on the given node.
|
||||||
* given node.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
QName typeQname = nodeService.getType(nodeRef);
|
QName typeQname = nodeService.getType(nodeRef);
|
||||||
@@ -461,18 +453,22 @@ public class PermissionServiceImpl implements PermissionServiceSPI, Initializing
|
|||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum CacheType
|
||||||
|
{
|
||||||
|
HAS_PERMISSION, SINGLE_PERMISSION, SINGLE_PERMISSION_GLOBAL;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Key for a cache object is built from all the known Authorities (which can
|
* Key for a cache object is built from all the known Authorities (which can change dynamically so they must all be used) the NodeRef ID and the permission reference itself.
|
||||||
* change dynamically so they must all be used) the NodeRef ID and the
|
* This gives a unique key for each permission test.
|
||||||
* permission reference itself. This gives a unique key for each permission
|
|
||||||
* test.
|
|
||||||
*/
|
*/
|
||||||
static Serializable generateKey(Set<String> auths, NodeRef nodeRef, PermissionReference perm)
|
static Serializable generateKey(Set<String> auths, NodeRef nodeRef, PermissionReference perm, CacheType type)
|
||||||
{
|
{
|
||||||
LinkedHashSet<Serializable> key = new LinkedHashSet<Serializable>();
|
LinkedHashSet<Serializable> key = new LinkedHashSet<Serializable>();
|
||||||
key.add(perm.toString());
|
key.add(perm.toString());
|
||||||
key.addAll(auths);
|
key.addAll(auths);
|
||||||
key.add(nodeRef);
|
key.add(nodeRef);
|
||||||
|
key.add(type);
|
||||||
return key;
|
return key;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -584,7 +580,6 @@ public class PermissionServiceImpl implements PermissionServiceSPI, Initializing
|
|||||||
return permissionsDaoComponent.getInheritParentPermissions(nodeRef);
|
return permissionsDaoComponent.getInheritParentPermissions(nodeRef);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public PermissionReference getPermissionReference(QName qname, String permissionName)
|
public PermissionReference getPermissionReference(QName qname, String permissionName)
|
||||||
{
|
{
|
||||||
return modelDAO.getPermissionReference(qname, permissionName);
|
return modelDAO.getPermissionReference(qname, permissionName);
|
||||||
@@ -866,22 +861,33 @@ public class PermissionServiceImpl implements PermissionServiceSPI, Initializing
|
|||||||
|
|
||||||
public boolean hasSinglePermission(Set<String> authorisations, NodeRef nodeRef)
|
public boolean hasSinglePermission(Set<String> authorisations, NodeRef nodeRef)
|
||||||
{
|
{
|
||||||
|
Serializable key = generateKey(
|
||||||
|
authorisations,
|
||||||
|
nodeRef,
|
||||||
|
this.required, CacheType.SINGLE_PERMISSION_GLOBAL);
|
||||||
|
|
||||||
|
AccessStatus status = accessCache.get(key);
|
||||||
|
if (status != null)
|
||||||
|
{
|
||||||
|
return status == AccessStatus.ALLOWED;
|
||||||
|
}
|
||||||
|
|
||||||
// Check global permission
|
// Check global permission
|
||||||
|
|
||||||
if (checkGlobalPermissions(authorisations))
|
if (checkGlobalPermissions(authorisations))
|
||||||
{
|
{
|
||||||
|
accessCache.put(key, AccessStatus.ALLOWED);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Set<Pair<String, PermissionReference>> denied = new HashSet<Pair<String, PermissionReference>>();
|
Set<Pair<String, PermissionReference>> denied = new HashSet<Pair<String, PermissionReference>>();
|
||||||
|
|
||||||
// Keep track of permission that are denied
|
return hasSinglePermission(authorisations, nodeRef, denied);
|
||||||
|
|
||||||
// Permissions are only evaluated up the primary parent chain
|
}
|
||||||
// TODO: Do not ignore non primary permissions
|
|
||||||
ChildAssociationRef car = nodeService.getPrimaryParent(nodeRef);
|
public boolean hasSinglePermission(Set<String> authorisations, NodeRef nodeRef,
|
||||||
// Work up the parent chain evaluating permissions.
|
Set<Pair<String, PermissionReference>> denied)
|
||||||
while (car != null)
|
|
||||||
{
|
{
|
||||||
// Add any denied permission to the denied list - these can not
|
// Add any denied permission to the denied list - these can not
|
||||||
// then
|
// then
|
||||||
@@ -891,39 +897,78 @@ public class PermissionServiceImpl implements PermissionServiceSPI, Initializing
|
|||||||
// to
|
// to
|
||||||
// andy at node A has no effect
|
// andy at node A has no effect
|
||||||
|
|
||||||
denied.addAll(getDenied(car.getChildRef()));
|
denied.addAll(getDenied(nodeRef));
|
||||||
|
|
||||||
|
// Cache non denied
|
||||||
|
Serializable key = null;
|
||||||
|
if (denied.size() == 0)
|
||||||
|
{
|
||||||
|
key = generateKey(authorisations, nodeRef, this.required, CacheType.SINGLE_PERMISSION);
|
||||||
|
}
|
||||||
|
if (key != null)
|
||||||
|
{
|
||||||
|
AccessStatus status = accessCache.get(key);
|
||||||
|
if (status != null)
|
||||||
|
{
|
||||||
|
return status == AccessStatus.ALLOWED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// If the current node allows the permission we are done
|
// If the current node allows the permission we are done
|
||||||
// The test includes any parent or ancestor requirements
|
// The test includes any parent or ancestor requirements
|
||||||
if (checkRequired(authorisations, car.getChildRef(), denied))
|
if (checkRequired(authorisations, nodeRef, denied))
|
||||||
{
|
{
|
||||||
|
if (key != null)
|
||||||
|
{
|
||||||
|
accessCache.put(key, AccessStatus.ALLOWED);
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Permissions are only evaluated up the primary parent chain
|
||||||
|
// TODO: Do not ignore non primary permissions
|
||||||
|
ChildAssociationRef car = nodeService.getPrimaryParent(nodeRef);
|
||||||
|
|
||||||
// Build the next element of the evaluation chain
|
// Build the next element of the evaluation chain
|
||||||
if (car.getParentRef() != null)
|
if (car.getParentRef() != null)
|
||||||
{
|
{
|
||||||
NodePermissionEntry nodePermissions = permissionsDaoComponent.getPermissions(car.getChildRef());
|
NodePermissionEntry nodePermissions = permissionsDaoComponent.getPermissions(nodeRef);
|
||||||
if ((nodePermissions == null) || (nodePermissions.inheritPermissions()))
|
if ((nodePermissions == null) || (nodePermissions.inheritPermissions()))
|
||||||
{
|
{
|
||||||
car = nodeService.getPrimaryParent(car.getParentRef());
|
if(hasSinglePermission(authorisations, car.getParentRef(), denied))
|
||||||
|
{
|
||||||
|
if (key != null)
|
||||||
|
{
|
||||||
|
accessCache.put(key, AccessStatus.ALLOWED);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
car = null;
|
if (key != null)
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
car = null;
|
accessCache.put(key, AccessStatus.DENIED);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Support meta data permissions on the root node?
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (key != null)
|
||||||
|
{
|
||||||
|
accessCache.put(key, AccessStatus.DENIED);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (key != null)
|
||||||
|
{
|
||||||
|
accessCache.put(key, AccessStatus.DENIED);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -982,7 +1027,8 @@ public class PermissionServiceImpl implements PermissionServiceSPI, Initializing
|
|||||||
|
|
||||||
// All permission excludes all permissions available for
|
// All permission excludes all permissions available for
|
||||||
// the node.
|
// the node.
|
||||||
if (pe.getPermissionReference().equals(getAllPermissionReference()) || pe.getPermissionReference().equals(OLD_ALL_PERMISSIONS_REFERENCE))
|
if (pe.getPermissionReference().equals(getAllPermissionReference())
|
||||||
|
|| pe.getPermissionReference().equals(OLD_ALL_PERMISSIONS_REFERENCE))
|
||||||
{
|
{
|
||||||
for (PermissionReference deny : modelDAO.getAllPermissions(nodeRef))
|
for (PermissionReference deny : modelDAO.getAllPermissions(nodeRef))
|
||||||
{
|
{
|
||||||
|
@@ -20,12 +20,13 @@
|
|||||||
* and Open Source Software ("FLOSS") applications as described in Alfresco's
|
* and Open Source Software ("FLOSS") applications as described in Alfresco's
|
||||||
* FLOSS exception. You should have recieved a copy of the text describing
|
* FLOSS exception. You should have recieved a copy of the text describing
|
||||||
* the FLOSS exception, and it is also available here:
|
* the FLOSS exception, and it is also available here:
|
||||||
* http://www.alfresco.com/legal/licensing"
|
* http://www.alfresco.com/legal/licensing
|
||||||
*/
|
*/
|
||||||
package org.alfresco.repo.security.permissions.impl.model;
|
package org.alfresco.repo.security.permissions.impl.model;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.io.Serializable;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
@@ -728,18 +729,43 @@ public class PermissionModel implements ModelDAO, InitializingBean
|
|||||||
return pg;
|
return pg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Serializable generateKey(PermissionReference required, QName qName, Set<QName> aspectQNames,
|
||||||
|
RequiredPermission.On on)
|
||||||
|
{
|
||||||
|
LinkedHashSet<Serializable> key = new LinkedHashSet<Serializable>();
|
||||||
|
key.add(required.toString());
|
||||||
|
key.add(qName);
|
||||||
|
key.addAll(aspectQNames);
|
||||||
|
key.add(on.toString());
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private HashMap<Serializable, Set<PermissionReference>> requiredPermissionsCache = new HashMap<Serializable, Set<PermissionReference>>(
|
||||||
|
1024);
|
||||||
|
|
||||||
public Set<PermissionReference> getRequiredPermissions(PermissionReference required, QName qName,
|
public Set<PermissionReference> getRequiredPermissions(PermissionReference required, QName qName,
|
||||||
Set<QName> aspectQNames, RequiredPermission.On on)
|
Set<QName> aspectQNames, RequiredPermission.On on)
|
||||||
|
{
|
||||||
|
// Cache lookup as this is static
|
||||||
|
|
||||||
|
Serializable key = generateKey(required, qName, aspectQNames, on);
|
||||||
|
|
||||||
|
Set<PermissionReference> answer = requiredPermissionsCache.get(key);
|
||||||
|
if (answer == null)
|
||||||
{
|
{
|
||||||
PermissionGroup pg = getBasePermissionGroupOrNull(getPermissionGroupOrNull(required));
|
PermissionGroup pg = getBasePermissionGroupOrNull(getPermissionGroupOrNull(required));
|
||||||
if (pg == null)
|
if (pg == null)
|
||||||
{
|
{
|
||||||
return getRequirementsForPermission(required, on);
|
answer = getRequirementsForPermission(required, on);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return getRequirementsForPermissionGroup(pg, on, qName, aspectQNames);
|
answer = getRequirementsForPermissionGroup(pg, on, qName, aspectQNames);
|
||||||
}
|
}
|
||||||
|
requiredPermissionsCache.put(key, answer);
|
||||||
|
}
|
||||||
|
return answer;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Reference in New Issue
Block a user