package it.doqui.index.ecmengine.business.personalization.security.permissions;
import it.doqui.index.ecmengine.util.EcmEngineConstants;
import java.io.Serializable;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import net.sf.acegisecurity.Authentication;
import org.alfresco.repo.security.permissions.NodePermissionEntry;
import org.alfresco.repo.security.permissions.PermissionEntry;
import org.alfresco.repo.security.permissions.PermissionReference;
import org.alfresco.repo.security.permissions.impl.PermissionServiceImpl;
import org.alfresco.repo.security.permissions.impl.RequiredPermission;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.security.AccessStatus;
import org.alfresco.service.cmr.security.PermissionService;
import org.alfresco.service.namespace.QName;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
public class MultipleInheritancePermissionServiceImpl extends PermissionServiceImpl {
private static Log log = LogFactory.getLog(EcmEngineConstants.ECMENGINE_ROOT_LOG_CATEGORY + ".business.personalization.security");
public AccessStatus hasPermission(NodeRef nodeRef, PermissionReference perm)
{
// If the node ref is null there is no sensible test to do - and there
// must be no permissions
// - so we allow it
if (nodeRef == null)
{
return AccessStatus.ALLOWED;
}
nodeRef = tenantService.getName(nodeRef);
// If the permission is null we deny
if (perm == null)
{
return AccessStatus.DENIED;
}
// Allow permissions for nodes that do not exist
if (!nodeService.exists(nodeRef))
{
return AccessStatus.ALLOWED;
}
// Get the current authentications
// Use the smart authentication cache to improve permissions performance
Authentication auth = authenticationComponent.getCurrentAuthentication();
Set<String> authorisations = getAuthorisations(auth, nodeRef);
Serializable key = generateKey(authorisations, nodeRef, perm, CacheType.HAS_PERMISSION);
AccessStatus status = accessCache.get(key);
if (status != null)
{
return status;
}
// If the node does not support the given permission there is no point
// doing the test
Set<PermissionReference> available = modelDAO.getAllPermissions(nodeRef);
available.add(getAllPermissionReference());
available.add(OLD_ALL_PERMISSIONS_REFERENCE);
if (!(available.contains(perm)))
{
accessCache.put(key, AccessStatus.DENIED);
return AccessStatus.DENIED;
}
if (authenticationComponent.getCurrentUserName().equals(authenticationComponent.getSystemUserName()))
{
return AccessStatus.ALLOWED;
}
//
// TODO: Dynamic permissions via evaluators
//
/*
* Does the current authentication have the supplied permission on the given node.
*/
QName typeQname = nodeService.getType(nodeRef);
Set<QName> aspectQNames = nodeService.getAspects(nodeRef);
if (perm.equals(OLD_ALL_PERMISSIONS_REFERENCE))
{
perm = getAllPermissionReference();
}
MultipleInheritancePermissionServiceImpl.NodeTest nt =
new MultipleInheritancePermissionServiceImpl.NodeTest(perm, typeQname, aspectQNames);
boolean result = nt.evaluate(authorisations, nodeRef);
if (log.isDebugEnabled())
{
log.debug("Permission <" + perm + "> is "
+ (result ? "allowed" : "denied")
+ " for " + authenticationComponent.getCurrentUserName()
+ " on node " + nodeService.getPath(nodeRef));
}
status = result ? AccessStatus.ALLOWED : AccessStatus.DENIED;
accessCache.put(key, status);
return status;
}
private class NodeTest
{
/*
* The required permission.
*/
PermissionReference required;
/*
* Granters of the permission
*/
Set<PermissionReference> granters;
/*
* The additional permissions required at the node level.
*/
Set<PermissionReference> nodeRequirements = new HashSet<PermissionReference>();
/*
* The additional permissions required on the parent.
*/
Set<PermissionReference> parentRequirements = new HashSet<PermissionReference>();
/*
* The permissions required on all children.
*/
Set<PermissionReference> childrenRequirements = new HashSet<PermissionReference>();
/*
* The type name of the node.
*/
QName typeQName;
/*
* The aspects set on the node.
*/
Set<QName> aspectQNames;
/*
* Constructor just gets the additional requirements
*/
NodeTest(PermissionReference required, QName typeQName, Set<QName> aspectQNames)
{
this.required = required;
this.typeQName = typeQName;
this.aspectQNames = aspectQNames;
// Set the required node permissions
if (required.equals(getPermissionReference(ALL_PERMISSIONS)))
{
nodeRequirements = modelDAO.getRequiredPermissions(getPermissionReference(PermissionService.FULL_CONTROL), typeQName, aspectQNames, RequiredPermission.On.NODE);
}
else
{
nodeRequirements = modelDAO.getRequiredPermissions(required, typeQName, aspectQNames, RequiredPermission.On.NODE);
}
parentRequirements = modelDAO.getRequiredPermissions(required, typeQName, aspectQNames, RequiredPermission.On.PARENT);
childrenRequirements = modelDAO.getRequiredPermissions(required, typeQName, aspectQNames, RequiredPermission.On.CHILDREN);
// Find all the permissions that grant the allowed permission
// All permissions are treated specially.
granters = new LinkedHashSet<PermissionReference>(128, 1.0f);
granters.addAll(modelDAO.getGrantingPermissions(required));
granters.add(getAllPermissionReference());
granters.add(OLD_ALL_PERMISSIONS_REFERENCE);
}
/**
* External hook point
*
* @param authorisations
* @param nodeRef
* @return
*/
boolean evaluate(Set<String> authorisations, NodeRef nodeRef)
{
log.debug("[NodeTest::evaluate] BEGIN");
try {
Set<org.alfresco.util.Pair<String, PermissionReference>> denied = new HashSet<org.alfresco.util.Pair<String, PermissionReference>>();
return evaluate(authorisations, nodeRef, denied, null);
} finally {
log.debug("[NodeTest::evaluate] END");
}
}
/**
* Internal hook point for recursion
*
* @param authorisations
* @param nodeRef
* @param denied
* @param recursiveIn
* @return
*/
private boolean evaluate(Set<String> authorisations, NodeRef nodeRef, Set<org.alfresco.util.Pair<String, PermissionReference>> denied, MutableBoolean recursiveIn)
{
log.debug("[NodeTest::evaluate] BEGIN");
try {
log.debug("[NodeTest::evaluate] Controllo ACL sul nodo: " + nodeRef);
// Do we defer our required test to a parent (yes if not null)
MutableBoolean recursiveOut = null;
Set<org.alfresco.util.Pair<String, PermissionReference>> locallyDenied = new HashSet<org.alfresco.util.Pair<String, PermissionReference>>();
locallyDenied.addAll(denied);
locallyDenied.addAll(getDenied(nodeRef));
// Start out true and "and" all other results
boolean success = true;
// Check the required permissions but not for sets they rely on
// their underlying permissions
if (modelDAO.checkPermission(required)) {
if (parentRequirements.contains(required)) {
if (checkGlobalPermissions(authorisations)
|| checkRequired(authorisations, nodeRef, locallyDenied)) {
// No need to do the recursive test as it has been found
recursiveOut = null;
if (recursiveIn != null) {
recursiveIn.setValue(true);
}
} else {
// Much cheaper to do this as we go then check all the
// stack values for each parent
recursiveOut = new MutableBoolean(false);
}
} else {
// We have to do the test as no parent will help us out
success &= hasSinglePermission(authorisations, nodeRef);
}
if (!success) {
return false;
}
}
// Check the other permissions required on the node
for (PermissionReference pr : nodeRequirements)
{
// Build a new test
NodeTest nt = new NodeTest(pr, typeQName, aspectQNames);
success &= nt.evaluate(authorisations, nodeRef, locallyDenied, null);
if (!success) {
return false;
}
}
// Check the permission required of the parent
if (success) {
log.debug("[NodeTest::evaluate] Controllo ACL nodo: GRANTED - Inizio check ricorsivo");
// ChildAssociationRef car = nodeService.getPrimaryParent(nodeRef);
NodePermissionEntry nodePermissions = permissionsDaoComponent.getPermissions(nodeRef);
if ((nodePermissions == null) || (nodePermissions.inheritPermissions())) {
log.debug("[NodeTest::evaluate] nodePermissions = " + nodePermissions);
log.debug("[NodeTest::evaluate] nodePermissions.inheritPermissions = " + nodePermissions.inheritPermissions());
List<ChildAssociationRef> parents = nodeService.getParentAssocs(nodeRef);
for (ChildAssociationRef car : parents) { // Ciclo su tutti i padri (DoQui)
if (car.getParentRef() != null) {
log.debug("[NodeTest::evaluate] Controllo ACL sul nodo: " + car.getParentRef());
// NodePermissionEntry nodePermissions = permissionsDaoComponent.getPermissions(car.getChildRef());
// if ((nodePermissions == null) || (nodePermissions.inheritPermissions())) {
//
// log.debug("[NodeTest::evaluate] nodePermissions = " + nodePermissions);
// log.debug("[NodeTest::evaluate] nodePermissions.inheritPermissions = " + nodePermissions.inheritPermissions());
locallyDenied.addAll(getDenied(car.getParentRef()));
for (PermissionReference pr : parentRequirements) {
if (pr.equals(required)) {
// Recursive permission
success &= this.evaluate(authorisations, car.getParentRef(), locallyDenied, recursiveOut);
log.debug("[NodeTest::evaluate] Controllo ACL nodo: " + success + " [recursiveOut = " + recursiveOut.getValue() + "]");
if ((recursiveOut != null) && recursiveOut.getValue()) {
if (recursiveIn != null) {
recursiveIn.setValue(true);
}
}
} else {
NodeTest nt = new NodeTest(pr, typeQName, aspectQNames);
success &= nt.evaluate(authorisations, car.getParentRef(), locallyDenied, null);
log.debug("[NodeTest::evaluate] Controllo ACL nodo: " + success);
}
if (!success) {
log.debug("[NodeTest::evaluate] Controllo ACL nodo: DENIED - " +
"Break loop interno per permesso negato su " + car.getParentRef());
break;
}
}
}
if (success) {
log.debug("[NodeTest::evaluate] Controllo ACL nodo: GRANTED - " +
"Break loop esterno per permesso consentito su " + car.getParentRef());
break;
}
}
}
if (!success) {
log.debug("[NodeTest::evaluate] Controllo ACL nodo: DENIED");
return false;
}
}
if ((recursiveOut != null) && (!recursiveOut.getValue())) {
// The required authentication was not resolved in recursion
return false;
}
// Check permissions required of children
if (!childrenRequirements.isEmpty()) {
List<ChildAssociationRef> childAssocRefs = nodeService.getChildAssocs(nodeRef);
for (PermissionReference pr : childrenRequirements) {
for (ChildAssociationRef child : childAssocRefs) {
success &= (hasPermission(child.getChildRef(), pr) == AccessStatus.ALLOWED);
if (!success) {
return false;
}
}
}
}
return success;
} finally {
log.debug("[NodeTest::evaluate] END");
}
}
public boolean hasSinglePermission(Set<String> authorisations, NodeRef nodeRef)
{
nodeRef = tenantService.getName(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
if (checkGlobalPermissions(authorisations))
{
accessCache.put(key, AccessStatus.ALLOWED);
return true;
}
Set<org.alfresco.util.Pair<String, PermissionReference>> denied = new HashSet<org.alfresco.util.Pair<String, PermissionReference>>();
return hasSinglePermission(authorisations, nodeRef, denied);
}
public boolean hasSinglePermission(Set<String> authorisations, NodeRef nodeRef, Set<org.alfresco.util.Pair<String, PermissionReference>> denied)
{
nodeRef = tenantService.getName(nodeRef);
// Add any denied permission to the denied list - these can not
// then
// be used to given authentication.
// A -> B -> C
// If B denies all permissions to any - allowing all permissions
// to
// andy at node A has no effect
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
// The test includes any parent or ancestor requirements
if (checkRequired(authorisations, nodeRef, denied))
{
if (key != null)
{
accessCache.put(key, AccessStatus.ALLOWED);
}
return true;
}
// Se l'accesso fosse consentito dalle ACL del nodo a questo punto ci sarebbe gia` stato un "return true".
boolean allowed = false;
NodePermissionEntry nodePermissions = permissionsDaoComponent.getPermissions(nodeRef);
if ((nodePermissions == null) || (nodePermissions.inheritPermissions()))
{
// ChildAssociationRef car = nodeService.getPrimaryParent(nodeRef);
List<ChildAssociationRef> parentAssocs = nodeService.getParentAssocs(nodeRef);
for (ChildAssociationRef car : parentAssocs) {
// Build the next element of the evaluation chain
if (car.getParentRef() != null)
{
if (hasSinglePermission(authorisations, car.getParentRef(), denied))
{
allowed = true;
} // ELSE - Il valore precedente deve rimanere invariato... basta che l'accesso sia consentito su un ramo
} // ELSE - Il valore precedente deve rimanere invariato... basta che l'accesso sia consentito su un ramo
}
} // ELSE - Il valore precedente deve rimanere invariato... il nodo non eredita nulla dal padre
// Il controllo e` completato... salvo il risultato in cache
if (key != null)
{
accessCache.put(key, (allowed) ? AccessStatus.ALLOWED : AccessStatus.DENIED);
}
return allowed;
}
/**
* Check if we have a global permission
*
* @param authorisations
* @return
*/
private boolean checkGlobalPermissions(Set<String> authorisations)
{
for (PermissionEntry pe : modelDAO.getGlobalPermissionEntries())
{
if (isGranted(pe, authorisations, null))
{
return true;
}
}
return false;
}
/**
* Get the list of permissions denied for this node.
*
* @param nodeRef
* @return
*/
private Set<org.alfresco.util.Pair<String, PermissionReference>> getDenied(NodeRef nodeRef)
{
Set<org.alfresco.util.Pair<String, PermissionReference>> deniedSet = new HashSet<org.alfresco.util.Pair<String, PermissionReference>>();
// Loop over all denied permissions
NodePermissionEntry nodeEntry = permissionsDaoComponent.getPermissions(nodeRef);
if (nodeEntry != null)
{
for (PermissionEntry pe : nodeEntry.getPermissionEntries())
{
if (pe.isDenied())
{
// All the sets that grant this permission must be
// denied
// Note that granters includes the orginal permission
Set<PermissionReference> granters = modelDAO.getGrantingPermissions(pe.getPermissionReference());
for (PermissionReference granter : granters)
{
deniedSet.add(new org.alfresco.util.Pair<String, PermissionReference>(pe.getAuthority(), granter));
}
// All the things granted by this permission must be
// denied
Set<PermissionReference> grantees = modelDAO.getGranteePermissions(pe.getPermissionReference());
for (PermissionReference grantee : grantees)
{
deniedSet.add(new org.alfresco.util.Pair<String, PermissionReference>(pe.getAuthority(), grantee));
}
// All permission excludes all permissions available for
// the node.
if (pe.getPermissionReference().equals(getAllPermissionReference()) || pe.getPermissionReference().equals(OLD_ALL_PERMISSIONS_REFERENCE))
{
for (PermissionReference deny : modelDAO.getAllPermissions(nodeRef))
{
deniedSet.add(new org.alfresco.util.Pair<String, PermissionReference>(pe.getAuthority(), deny));
}
}
}
}
}
return deniedSet;
}
/**
* Check that a given authentication is available on a node
*
* @param authorisations
* @param nodeRef
* @param denied
* @return
*/
private boolean checkRequired(Set<String> authorisations, NodeRef nodeRef, Set<org.alfresco.util.Pair<String, PermissionReference>> denied)
{
NodePermissionEntry nodeEntry = permissionsDaoComponent.getPermissions(nodeRef);
// No permissions set - short cut to deny
if (nodeEntry == null)
{
return false;
}
// Check if each permission allows - the first wins.
// We could have other voting style mechanisms here
for (PermissionEntry pe : nodeEntry.getPermissionEntries())
{
if (isGranted(pe, authorisations, denied))
{
return true;
}
}
return false;
}
/**
* Is a permission granted
*
* @param pe -
* the permissions entry to consider
* @param granters -
* the set of granters
* @param authorisations -
* the set of authorities
* @param denied -
* the set of denied permissions/authority pais
* @return
*/
private boolean isGranted(PermissionEntry pe, Set<String> authorisations,
Set<org.alfresco.util.Pair<String, PermissionReference>> denied)
{
// If the permission entry denies then we just deny
if (pe.isDenied())
{
return false;
}
// The permission is allowed but we deny it as it is in the denied
// set
if (denied != null)
{
org.alfresco.util.Pair<String, PermissionReference> specific =
new org.alfresco.util.Pair<String, PermissionReference>(pe.getAuthority(), required);
if (denied.contains(specific))
{
return false;
}
}
// any deny denies
if (false)
{
if (denied != null)
{
for (String auth : authorisations)
{
org.alfresco.util.Pair<String, PermissionReference> specific =
new org.alfresco.util.Pair<String, PermissionReference>(auth, required);
if (denied.contains(specific))
{
return false;
}
for (PermissionReference perm : granters)
{
specific = new org.alfresco.util.Pair<String, PermissionReference>(auth, perm);
if (denied.contains(specific))
{
return false;
}
}
}
}
}
// If the permission has a match in both the authorities and
// granters list it is allowed
// It applies to the current user and it is granted
if (authorisations.contains(pe.getAuthority()) && granters.contains(pe.getPermissionReference()))
{
{
return true;
}
}
// Default deny
return false;
}
}
}