You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by an...@apache.org on 2008/08/27 17:12:07 UTC
svn commit: r689499 [5/11] - in /jackrabbit/trunk:
jackrabbit-api/src/main/java/org/apache/jackrabbit/api/jsr283/
jackrabbit-api/src/main/java/org/apache/jackrabbit/api/jsr283/retention/
jackrabbit-api/src/main/java/org/apache/jackrabbit/api/jsr283/sec...
Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/acl/ACLEditor.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/acl/ACLEditor.java?rev=689499&r1=689498&r2=689499&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/acl/ACLEditor.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/acl/ACLEditor.java Wed Aug 27 08:12:04 2008
@@ -16,17 +16,18 @@
*/
package org.apache.jackrabbit.core.security.authorization.acl;
-import org.apache.jackrabbit.api.jsr283.security.AccessControlEntry;
import org.apache.jackrabbit.api.jsr283.security.AccessControlException;
import org.apache.jackrabbit.api.jsr283.security.Privilege;
-import org.apache.jackrabbit.api.security.principal.PrincipalManager;
+import org.apache.jackrabbit.api.jsr283.security.AccessControlEntry;
+import org.apache.jackrabbit.api.jsr283.security.AccessControlPolicy;
+import org.apache.jackrabbit.api.jsr283.security.AccessControlList;
import org.apache.jackrabbit.core.NodeImpl;
import org.apache.jackrabbit.core.SecurityItemModifier;
import org.apache.jackrabbit.core.SessionImpl;
import org.apache.jackrabbit.core.security.authorization.AccessControlConstants;
import org.apache.jackrabbit.core.security.authorization.AccessControlEditor;
-import org.apache.jackrabbit.core.security.authorization.PolicyEntry;
-import org.apache.jackrabbit.core.security.authorization.PolicyTemplate;
+import org.apache.jackrabbit.core.security.authorization.AccessControlUtils;
+import org.apache.jackrabbit.core.security.authorization.JackrabbitAccessControlEntry;
import org.apache.jackrabbit.core.security.authorization.PrivilegeRegistry;
import org.apache.jackrabbit.spi.Name;
import org.apache.jackrabbit.spi.commons.conversion.NameException;
@@ -42,9 +43,6 @@
import javax.jcr.Value;
import javax.jcr.ValueFactory;
import java.security.Principal;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
/**
* <code>ACLEditor</code>...
@@ -58,65 +56,86 @@
/**
* Default name for ace nodes
*/
- private static final String DEFAULT_PERMISSION_NAME = "permission";
-
- protected final SessionImpl session;
- private final PrincipalManager principalManager;
+ private static final String DEFAULT_ACE_NAME = "ace";
+ /**
+ * the editing session
+ */
+ private final SessionImpl session;
+ private final PrivilegeRegistry privilegeRegistry;
+ private final AccessControlUtils utils;
- protected ACLEditor(Session editingSession) throws RepositoryException {
+ ACLEditor(Session editingSession, AccessControlUtils utils) {
if (editingSession instanceof SessionImpl) {
session = ((SessionImpl) editingSession);
- principalManager = ((SessionImpl) editingSession).getPrincipalManager();
+ // TODO: review and find better solution
+ privilegeRegistry = new PrivilegeRegistry(session);
} else {
throw new IllegalArgumentException("org.apache.jackrabbit.core.SessionImpl expected. Found " + editingSession.getClass());
}
+ this.utils = utils;
+ }
+
+ /**
+ *
+ * @param aclNode
+ * @return
+ * @throws RepositoryException
+ */
+ AccessControlList getACL(NodeImpl aclNode) throws RepositoryException {
+ return new ACLTemplate(aclNode, privilegeRegistry);
}
//------------------------------------------------< AccessControlEditor >---
/**
- * @see AccessControlEditor#getPolicyTemplate(String)
+ * @see AccessControlEditor#getPolicies(String)
* @param nodePath
*/
- public PolicyTemplate getPolicyTemplate(String nodePath) throws AccessControlException, PathNotFoundException, RepositoryException {
+ public AccessControlPolicy[] getPolicies(String nodePath) throws AccessControlException, PathNotFoundException, RepositoryException {
checkProtectsNode(nodePath);
- PolicyTemplate tmpl = null;
NodeImpl aclNode = getAclNode(nodePath);
- if (aclNode != null) {
- tmpl = new ACLTemplate(aclNode, Collections.EMPTY_SET);
+ if (aclNode == null) {
+ return new AccessControlPolicy[0];
+ } else {
+ return new AccessControlPolicy[] {getACL(aclNode)};
}
- return tmpl;
}
/**
- * @see AccessControlEditor#editPolicyTemplate(String)
+ * @see AccessControlEditor#editAccessControlPolicies(String)
* @param nodePath
*/
- public PolicyTemplate editPolicyTemplate(String nodePath) throws AccessControlException, PathNotFoundException, RepositoryException {
+ public AccessControlPolicy[] editAccessControlPolicies(String nodePath) throws AccessControlException, PathNotFoundException, RepositoryException {
checkProtectsNode(nodePath);
- PolicyTemplate tmpl;
+ AccessControlPolicy acl;
NodeImpl aclNode = getAclNode(nodePath);
if (aclNode == null) {
- tmpl = new ACLTemplate(nodePath);
+ // create an empty acl
+ acl = new ACLTemplate(nodePath, session.getPrincipalManager(), privilegeRegistry);
} else {
- tmpl = new ACLTemplate(aclNode, Collections.EMPTY_SET);
+ acl = getACL(aclNode);
}
- return tmpl;
+ return new AccessControlPolicy[] {acl};
}
/**
- * @see AccessControlEditor#editPolicyTemplate(Principal)
+ * @see AccessControlEditor#editAccessControlPolicies(Principal)
*/
- public PolicyTemplate editPolicyTemplate(Principal principal) throws AccessDeniedException, AccessControlException, RepositoryException {
- throw new AccessControlException("Unable to edit policy for principal " + principal.getName());
+ public AccessControlPolicy[] editAccessControlPolicies(Principal principal) throws AccessDeniedException, AccessControlException, RepositoryException {
+ if (!session.getPrincipalManager().hasPrincipal(principal.getName())) {
+ throw new AccessControlException("Unknown principal.");
+ }
+ // TODO: impl. missing
+ return new AccessControlPolicy[0];
}
/**
- * @see AccessControlEditor#setPolicyTemplate(String,PolicyTemplate)
+ * @see AccessControlEditor#setPolicy(String,AccessControlPolicy)
*/
- public void setPolicyTemplate(String nodePath, PolicyTemplate template) throws RepositoryException {
+ public void setPolicy(String nodePath, AccessControlPolicy policy) throws RepositoryException {
checkProtectsNode(nodePath);
+ checkValidPolicy(nodePath, policy);
NodeImpl aclNode = getAclNode(nodePath);
/* in order to assert that the parent (ac-controlled node) gets modified
@@ -130,18 +149,21 @@
// now (re) create it
aclNode = createAclNode(nodePath);
- PolicyEntry[] entries = template.getEntries();
+ AccessControlEntry[] entries = ((ACLTemplate) policy).getAccessControlEntries();
for (int i = 0; i < entries.length; i++) {
- ACEImpl ace = (ACEImpl) entries[i];
- // TODO: improve
+ JackrabbitAccessControlEntry ace = (JackrabbitAccessControlEntry) entries[i];
+
Name nodeName = getUniqueNodeName(aclNode, ace.isAllow() ? "allow" : "deny");
Name ntName = (ace.isAllow()) ? NT_REP_GRANT_ACE : NT_REP_DENY_ACE;
ValueFactory vf = session.getValueFactory();
+ // create the ACE node
NodeImpl aceNode = addSecurityNode(aclNode, nodeName, ntName);
+
// write the rep:principalName property
String principalName = ace.getPrincipal().getName();
setSecurityProperty(aceNode, P_PRINCIPAL_NAME, vf.createValue(principalName));
+
// ... and the rep:privileges property
Privilege[] pvlgs = ace.getPrivileges();
Value[] names = getPrivilegeNames(pvlgs, vf);
@@ -150,114 +172,56 @@
}
/**
- * @see AccessControlEditor#removePolicyTemplate(String)
+ * @see AccessControlEditor#removePolicy(String,AccessControlPolicy)
*/
- public PolicyTemplate removePolicyTemplate(String nodePath) throws AccessControlException, RepositoryException {
+ public synchronized void removePolicy(String nodePath, AccessControlPolicy policy) throws AccessControlException, RepositoryException {
checkProtectsNode(nodePath);
+ checkValidPolicy(nodePath, policy);
- PolicyTemplate tmpl = null;
NodeImpl aclNode = getAclNode(nodePath);
if (aclNode != null) {
- // need to build the template in order to have a return value.
- tmpl = new ACLTemplate(aclNode, Collections.EMPTY_SET);
removeSecurityItem(aclNode);
- }
- return tmpl;
- }
-
- /**
- * @see AccessControlEditor#getAccessControlEntries(String)
- */
- public AccessControlEntry[] getAccessControlEntries(String nodePath) throws AccessControlException, PathNotFoundException, RepositoryException {
- PolicyTemplate pt = getPolicyTemplate(nodePath);
- if (pt == null) {
- return new AccessControlEntry[0];
} else {
- PolicyEntry[] entries = pt.getEntries();
- List l = new ArrayList();
- for (int i = 0; i < entries.length; i++) {
- if (entries[i].isAllow()) {
- l.add(entries[i]);
- }
- }
- return (AccessControlEntry[]) l.toArray(new AccessControlEntry[l.size()]);
+ throw new AccessControlException("No policy to remove at " + nodePath);
}
}
- /**
- * @see AccessControlEditor#addAccessControlEntry(String,Principal,Privilege[])
- */
- public AccessControlEntry addAccessControlEntry(String nodePath, Principal principal, Privilege[] privileges) throws AccessControlException, PathNotFoundException, RepositoryException {
- // JSR 283 requires that the principal is known TODO: check again.
- if (!principalManager.hasPrincipal(principal.getName())) {
- throw new AccessControlException("Principal " + principal.getName() + " does not exist.");
- }
-
- ACLTemplate pt = (ACLTemplate) editPolicyTemplate(nodePath);
- // TODO: check again. maybe these 'grant-ACE' should be stored/evaluated separated
- int privs = PrivilegeRegistry.getBits(privileges);
- /*
- since added access control entry may never remove privileges that are
- granted by the policy -> retrieve existing allow entries and add
- the new privileges to be granted.
- Reason: PolicyTemplate#setEntry does in fact overwrite (which is fine
- when editing the policy itself, but wrong when adding ACEs over the JCR-API.
- */
- ACEImpl[] existing = pt.getEntries(principal);
- for (int i = 0; i < existing.length; i++) {
- if (existing[i].isAllow()) {
- privs |= existing[i].getPrivilegeBits();
- }
- }
-
- pt.setEntry(new ACEImpl(principal, privs, true));
- setPolicyTemplate(nodePath, pt);
- ACEImpl[] tmpls = pt.getEntries(principal);
- for (int i = 0; i < tmpls.length; i++) {
- if (tmpls[i].isAllow()) {
- return tmpls[i];
- }
- }
- // should never get here
- throw new AccessControlException("Internal error: No access control entry added.");
- }
-
-
- /**
- * @see AccessControlEditor#removeAccessControlEntry(String,AccessControlEntry)
- */
- public boolean removeAccessControlEntry(String nodePath, AccessControlEntry entry) throws AccessControlException, PathNotFoundException, RepositoryException {
- if (!(entry instanceof ACEImpl)) {
- throw new AccessControlException("Unknown AccessControlEntry implementation.");
- }
- // TODO: check again. maybe these 'grant-ACE' should be removed separated
- PolicyTemplate pt = editPolicyTemplate(nodePath);
- boolean removed = pt.removeEntry((ACEImpl) entry);
- if (removed) {
- setPolicyTemplate(nodePath, pt);
- }
- return removed;
- }
-
//--------------------------------------------------------------------------
/**
- * Test if the Node identified by <code>id</code> is itself part of ACL
+ * Check if the Node identified by <code>nodePath</code> is itself part of ACL
* defining content. It this case setting or modifying an AC-policy is
* obviously not possible.
*
* @param nodePath
- * @throws AccessControlException If the given id identifies a Node that
+ * @throws AccessControlException If the given nodePath identifies a Node that
* represents a ACL or ACE item.
* @throws RepositoryException
*/
private void checkProtectsNode(String nodePath) throws RepositoryException {
NodeImpl node = getNode(nodePath);
- if (ACLProvider.protectsNode(node)) {
+ if (utils.isAcItem(node)) {
throw new AccessControlException("Node " + nodePath + " defines ACL or ACE itself.");
}
}
/**
+ * Check if the specified policy can be set/removed from this editor.
+ *
+ * @param nodePath
+ * @param policy
+ * @throws AccessControlException
+ */
+ private static void checkValidPolicy(String nodePath, AccessControlPolicy policy) throws AccessControlException {
+ if (policy == null || !(policy instanceof ACLTemplate)) {
+ throw new AccessControlException("Attempt to set/remove invalid policy " + policy);
+ }
+ ACLTemplate acl = (ACLTemplate) policy;
+ if (!nodePath.equals(acl.getPath())) {
+ throw new AccessControlException("Policy " + policy + " cannot be applied/removed from the node at " + nodePath);
+ }
+ }
+
+ /**
*
* @param path
* @return
@@ -305,18 +269,18 @@
* Create a unique valid name for the Permission nodes to be save.
*
* @param node a name for the child is resolved
- * @param name if missing the {@link #DEFAULT_PERMISSION_NAME} is taken
+ * @param name if missing the {@link #DEFAULT_ACE_NAME} is taken
* @return
* @throws RepositoryException
*/
protected static Name getUniqueNodeName(Node node, String name) throws RepositoryException {
if (name == null) {
- name = DEFAULT_PERMISSION_NAME;
+ name = DEFAULT_ACE_NAME;
} else {
try {
NameParser.checkFormat(name);
} catch (NameException e) {
- name = DEFAULT_PERMISSION_NAME;
+ name = DEFAULT_ACE_NAME;
log.debug("Invalid path name for Permission: " + name + ".");
}
}
Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/acl/ACLProvider.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/acl/ACLProvider.java?rev=689499&r1=689498&r2=689499&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/acl/ACLProvider.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/acl/ACLProvider.java Wed Aug 27 08:12:04 2008
@@ -16,14 +16,16 @@
*/
package org.apache.jackrabbit.core.security.authorization.acl;
-import org.apache.jackrabbit.api.JackrabbitSession;
-import org.apache.jackrabbit.api.jsr283.security.AccessControlEntry;
import org.apache.jackrabbit.api.jsr283.security.AccessControlPolicy;
import org.apache.jackrabbit.api.jsr283.security.Privilege;
+import org.apache.jackrabbit.api.jsr283.security.AccessControlList;
+import org.apache.jackrabbit.api.jsr283.security.AccessControlManager;
import org.apache.jackrabbit.api.security.principal.PrincipalManager;
import org.apache.jackrabbit.core.NodeId;
import org.apache.jackrabbit.core.NodeImpl;
import org.apache.jackrabbit.core.PropertyImpl;
+import org.apache.jackrabbit.core.ItemImpl;
+import org.apache.jackrabbit.core.SessionImpl;
import org.apache.jackrabbit.core.observation.SynchronousEventListener;
import org.apache.jackrabbit.core.security.SecurityConstants;
import org.apache.jackrabbit.core.security.authorization.AbstractAccessControlProvider;
@@ -33,18 +35,19 @@
import org.apache.jackrabbit.core.security.authorization.AccessControlProvider;
import org.apache.jackrabbit.core.security.authorization.CompiledPermissions;
import org.apache.jackrabbit.core.security.authorization.Permission;
-import org.apache.jackrabbit.core.security.authorization.PolicyEntry;
-import org.apache.jackrabbit.core.security.authorization.PolicyTemplate;
import org.apache.jackrabbit.core.security.authorization.PrivilegeRegistry;
+import org.apache.jackrabbit.core.security.authorization.UnmodifiableAccessControlList;
+import org.apache.jackrabbit.core.security.authorization.AccessControlEntryIterator;
+import org.apache.jackrabbit.core.security.authorization.JackrabbitAccessControlEntry;
import org.apache.jackrabbit.core.security.principal.PrincipalImpl;
import org.apache.jackrabbit.spi.Path;
import org.apache.jackrabbit.spi.commons.name.PathFactoryImpl;
import org.apache.jackrabbit.util.Text;
+import org.apache.commons.collections.map.ListOrderedMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.jcr.ItemNotFoundException;
-import javax.jcr.Node;
import javax.jcr.NodeIterator;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
@@ -55,14 +58,14 @@
import javax.jcr.query.Query;
import javax.jcr.query.QueryManager;
import java.security.Principal;
-import java.util.Arrays;
import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Arrays;
/**
* The ACLProvider generates access control policies out of the items stored
@@ -92,18 +95,27 @@
*/
private static final Logger log = LoggerFactory.getLogger(ACLProvider.class);
- private AccessControlEditor systemEditor;
+ /**
+ * the system acl editor.
+ */
+ private ACLEditor systemEditor;
/**
* The node id of the root node
*/
private NodeId rootNodeId;
- //--------------------------------------< AbstractAccessControlProvider >---
+ /**
+ * Flag indicating whether or not this provider should be create the default
+ * ACLs upon initialization.
+ */
+ private boolean initializedWithDefaults;
+
+ //-------------------------------------------------< AccessControlUtils >---
/**
* @see AbstractAccessControlProvider#isAcItem(Path)
*/
- protected boolean isAcItem(Path absPath) throws RepositoryException {
+ public boolean isAcItem(Path absPath) throws RepositoryException {
Path.Element[] elems = absPath.getElements();
for (int i = 0; i < elems.length; i++) {
if (N_POLICY.equals(elems[i].getName())) {
@@ -113,80 +125,55 @@
return false;
}
+ /**
+ * Test if the given node is itself a rep:ACL or a rep:ACE node.
+ * @see AbstractAccessControlProvider#isAcItem(ItemImpl)
+ */
+ public boolean isAcItem(ItemImpl item) throws RepositoryException {
+ NodeImpl n = ((item.isNode()) ? (NodeImpl) item : (NodeImpl) item.getParent());
+ return n.isNodeType(NT_REP_ACL) || n.isNodeType(NT_REP_ACE);
+ }
+
//----------------------------------------------< AccessControlProvider >---
/**
* @see AccessControlProvider#init(Session, Map)
*/
- public void init(Session systemSession, Map options) throws RepositoryException {
- super.init(systemSession, options);
+ public void init(Session systemSession, Map configuration) throws RepositoryException {
+ super.init(systemSession, configuration);
// make sure the workspace of the given systemSession has a
// minimal protection on the root node.
NodeImpl root = (NodeImpl) session.getRootNode();
rootNodeId = root.getNodeId();
- systemEditor = new ACLEditor(systemSession);
-
- if (!isAccessControlled(root)) {
+ systemEditor = new ACLEditor(systemSession, this);
+ initializedWithDefaults = !configuration.containsKey(PARAM_OMIT_DEFAULT_PERMISSIONS);
+ if (initializedWithDefaults && !isAccessControlled(root)) {
initRootACL(session, systemEditor);
}
}
/**
- * @see AccessControlProvider#getPolicy(Path)
+ * @see AccessControlProvider#getEffectivePolicies(Path)
* @param absPath
*/
- public AccessControlPolicy getPolicy(Path absPath) throws ItemNotFoundException, RepositoryException {
+ public AccessControlPolicy[] getEffectivePolicies(Path absPath) throws ItemNotFoundException, RepositoryException {
checkInitialized();
- return getACL(absPath);
- }
- /**
- * @see AccessControlProvider#getAccessControlEntries(Path)
- * @param absPath
- */
- public AccessControlEntry[] getAccessControlEntries(Path absPath) throws RepositoryException {
- checkInitialized();
- ACLImpl acl = getACL(absPath);
-
- // TODO: check again what the expected return value would be.
- // TODO: check again if correct. call probably expensive.
- Map allowed = new HashMap();
- Map denied = new HashMap();
- for (Iterator it = acl.getEntries(); it.hasNext();) {
- ACEImpl ace = (ACEImpl) it.next();
- Principal pc = ace.getPrincipal();
-
- int pv = ace.getPrivilegeBits();
-
- int allowPv = (allowed.containsKey(pc)) ? ((Integer) allowed.get(pc)).intValue() : 0;
- int denyPv = (denied.containsKey(pc)) ? ((Integer) denied.get(pc)).intValue() : 0;
-
- // shortcut:
- if (allowPv == PrivilegeRegistry.ALL) {
- continue;
- }
-
- // if the ace is a granting ACE -> make sure the permissions
- // it grants are not denied by another ACE
- if (ace.isAllow()) {
- // determined those allow-priv from the current ace, that have
- // not been denied by an ace ealier in the evaluation.
- allowPv |= PrivilegeRegistry.diff(pv, denyPv);
- allowed.put(pc, new Integer(allowPv));
- } else {
- // determined those deny-priv from the current ace, that have
- // not been granted by an ace ealier in the evaluation.
- denyPv |= PrivilegeRegistry.diff(pv, allowPv);
- denied.put(pc, new Integer(denyPv));
- }
- }
-
- Set s = new HashSet();
- for (Iterator it = allowed.keySet().iterator(); it.hasNext();) {
- Principal p = (Principal) it.next();
- s.add(new ACEImpl(p, ((Integer) allowed.get(p)).intValue(), true));
+ NodeImpl targetNode = (NodeImpl) session.getNode(session.getJCRPath(absPath));
+ NodeImpl node = getNode(targetNode);
+ List acls = new ArrayList();
+
+ // collect all ACLs effective at node
+ collectAcls(node, acls);
+ // if no effective ACLs are present -> add a default, empty acl.
+ if (acls.isEmpty()) {
+ // no access control information can be retrieved for the specified
+ // node, since neither the node nor any of its parents is access
+ // controlled -> build a default policy.
+ log.warn("No access controlled node present in item hierarchy starting from " + targetNode.getPath());
+ acls.add(new UnmodifiableAccessControlList(Collections.EMPTY_LIST));
}
- return (AccessControlEntry[]) s.toArray(new AccessControlEntry[s.size()]);
+ return (AccessControlList[]) acls.toArray(new AccessControlList[acls.size()]);
}
/**
@@ -194,12 +181,7 @@
*/
public AccessControlEditor getEditor(Session session) {
checkInitialized();
- try {
- return new ACLEditor(session);
- } catch (RepositoryException e) {
- log.debug("Unable to create AccessControlEditor.", e.getMessage());
- return null;
- }
+ return new ACLEditor(session, this);
}
/**
@@ -224,108 +206,58 @@
if (isAdminOrSystem(principals)) {
return true;
} else {
- return new AclPermissions(principals, false).grants(PathFactoryImpl.getInstance().getRootPath(), Permission.READ);
+ CompiledPermissions cp = new AclPermissions(principals, false);
+ return cp.grants(PathFactoryImpl.getInstance().getRootPath(), Permission.READ);
}
}
//------------------------------------------------------------< private >---
- /**
- * Build the ACL that is effective on the Node at
- * <code>absPath</code>. In contrast to {@link #getACL(NodeImpl, Set)}
- * the returned ACL contains all entries that apply to that node.
- *
- * @param absPath
- * @return
- * @throws ItemNotFoundException
- * @throws RepositoryException
- */
- private ACLImpl getACL(Path absPath) throws ItemNotFoundException, RepositoryException {
- return getACL((NodeImpl) session.getNode(session.getJCRPath(absPath)),
- Collections.EMPTY_SET);
- }
/**
- * Build the ACL that is effective on the Node at
- * <code>absPath</code>, but only retrieve those entries that apply to
- * any of the principals whose name is present in the given
- * <code>principalNameFilter</code>.
+ * Returns the given <code>targetNode</code> unless the node itself stores
+ * access control information in which case it's nearest non-ac-parent is
+ * searched and returned.
*
- * @param node
- * @param principalNameFilter
+ * @param targetNode The node for which AC information needs to be retrieved.
* @return
- * @throws ItemNotFoundException
* @throws RepositoryException
*/
- private ACLImpl getACL(NodeImpl node, Set principalNameFilter) throws ItemNotFoundException, RepositoryException {
- // -> build the acl for the Node
- ACLImpl acl;
- // check for special ACL building item
- if (protectsNode(node)) {
- NodeImpl parentNode;
- if (node.isNodeType(NT_REP_ACL)) {
- parentNode = (NodeImpl) node.getParent();
+ private NodeImpl getNode(NodeImpl targetNode) throws RepositoryException {
+ NodeImpl node;
+ if (isAcItem(targetNode)) {
+ if (targetNode.isNodeType(NT_REP_ACL)) {
+ node = (NodeImpl) targetNode.getParent();
} else {
- parentNode = (NodeImpl) node.getParent().getParent();
+ node = (NodeImpl) targetNode.getParent().getParent();
}
- ACLImpl baseACL = buildAcl(parentNode, principalNameFilter);
- acl = new ACLImpl(node.getNodeId(), baseACL, true);
} else {
- // build Acl for non-protection node.
- acl = buildAcl(node, principalNameFilter);
+ node = targetNode;
}
- return acl;
+ return node;
}
/**
- * Constructs the ACLImpl for a regular node, i.e. a node that does not
- * store itself ACL-related information. The ACL to be returned combines both
- * the base-ACL containing the inherited access control information
- * and the access control information provided with the given node itself.
+ * Recursively collects all ACLs that are effective on the specified node.
*
- * @param node the Node to build the ACL for, which must NOT be part of the
+ * @param node the Node to collect the ACLs for, which must NOT be part of the
* structure defined by mix:AccessControllable.
- * @param principalNameFilter
- * @return acl or <code>DefaultACL</code> if neither the node nor any of it's
- * parents is access controlled.
+ * @param acls List used to collect the effective acls.
* @throws RepositoryException
*/
- private ACLImpl buildAcl(NodeImpl node, Set principalNameFilter) throws RepositoryException {
- // preconditions:
- // - node is not null
- // - node is never an ACL building item
- NodeId id = (NodeId) node.getId();
- // retrieve the base-ACL (i.e. the ACL that belongs to parentNode)
- // for this find nearest access controlled parent.
- ACLImpl baseACL = null;
- NodeImpl parentNode = id.equals(rootNodeId) ? null : (NodeImpl) node.getParent();
- while (parentNode != null && baseACL == null) {
- if (isAccessControlled(parentNode)) {
- baseACL = buildAcl(parentNode, principalNameFilter);
- } else {
- parentNode = (rootNodeId.equals(parentNode.getId())) ? null
- : (NodeImpl) parentNode.getParent();
- }
- }
- // the build the effective ACL from the specified Node and the base ACL
- ACLImpl acl;
+ private void collectAcls(NodeImpl node, List acls) throws RepositoryException {
+ // if the given node is access-controlled, construct a new ACL and add
+ // it to the list
if (isAccessControlled(node)) {
- // build acl from access controlled node
+ // build acl for the access controlled node
NodeImpl aclNode = node.getNode(N_POLICY);
- PolicyTemplate tmpl = new ACLTemplate(aclNode, principalNameFilter);
- List localEntries = Arrays.asList(tmpl.getEntries());
-
- acl = new ACLImpl(aclNode.getNodeId(), localEntries, baseACL, false);
- } else if (baseACL != null) {
- // build acl for a non-access controlled item that has a base acl
- acl = new ACLImpl(id, baseACL, false);
- } else {
- // no access control information can be retrieved for the specified
- // node, since neither the node nor any of its parents is access
- // controlled -> build a default policy.
- log.warn("No access controlled node present in item hierarchy starting from " + id);
- acl = new DefaultACL(id);
+ AccessControlList acl = systemEditor.getACL(aclNode);
+ acls.add(new UnmodifiableAccessControlList(acl));
+ }
+ // then, recursively look for access controlled parents up the hierarchy.
+ if (!rootNodeId.equals(node.getId())) {
+ NodeImpl parentNode = (NodeImpl) node.getParent();
+ collectAcls(parentNode, acls);
}
- return acl;
}
/**
@@ -339,12 +271,15 @@
* @param session to the workspace to set-up inital ACL to
* @throws RepositoryException
*/
- private static void initRootACL(JackrabbitSession session, AccessControlEditor editor) throws RepositoryException {
+ private static void initRootACL(SessionImpl session, AccessControlEditor editor) throws RepositoryException {
try {
log.info("Install initial ACL:...");
String rootPath = session.getRootNode().getPath();
- PolicyTemplate tmpl = editor.editPolicyTemplate(rootPath);
+ AccessControlPolicy[] acls = editor.editAccessControlPolicies(rootPath);
+ ACLTemplate acl = (ACLTemplate) acls[0];
+
PrincipalManager pMgr = session.getPrincipalManager();
+ AccessControlManager acMgr = session.getAccessControlManager();
log.info("... Privilege.ALL for administrators.");
Principal administrators;
@@ -355,16 +290,15 @@
log.warn("Administrators principal group is missing.");
administrators = new PrincipalImpl(pName);
}
- PolicyEntry entr = new ACEImpl(administrators, PrivilegeRegistry.ALL, true);
- tmpl.setEntry(entr);
+ Privilege[] privs = new Privilege[]{acMgr.privilegeFromName(Privilege.JCR_ALL)};
+ acl.addAccessControlEntry(administrators, privs);
Principal everyone = pMgr.getEveryone();
- // TODO: to be improved. how to define where everyone has read-access
log.info("... Privilege.READ for everyone.");
- entr = new ACEImpl(everyone, PrivilegeRegistry.READ, true);
- tmpl.setEntry(entr);
+ privs = new Privilege[]{acMgr.privilegeFromName(Privilege.JCR_READ)};
+ acl.addAccessControlEntry(everyone, privs);
- editor.setPolicyTemplate(rootPath, tmpl);
+ editor.setPolicy(rootPath, acl);
session.save();
log.info("... done.");
@@ -391,44 +325,31 @@
return node.isNodeType(NT_REP_ACCESS_CONTROLLABLE) && node.hasNode(N_POLICY);
}
- /**
- * Test if the given node is itself a rep:ACL or a rep:ACE node.
- *
- * @param node
- * @return
- * @throws RepositoryException
- */
- static boolean protectsNode(NodeImpl node) throws RepositoryException {
- return node.isNodeType(NT_REP_ACL) || node.isNodeType(NT_REP_ACE);
- }
-
//------------------------------------------------< CompiledPermissions >---
/**
*
*/
private class AclPermissions extends AbstractCompiledPermissions implements SynchronousEventListener {
- private final Set principalNames;
+ private final List principalNames;
+ private final String jcrReadPrivilegeName;
/**
* flag indicating that there is not 'deny READ'.
* -> simplify {@link #grants(Path, int)} in case of permissions == READ
*/
private boolean readAllowed = false;
- /**
- * flag indicating if only READ is granted
- * -> simplify {@link #grants(Path, int)} in case of permissions != READ
- */
- private boolean readOnly = false;
private AclPermissions(Set principals) throws RepositoryException {
this(principals, true);
}
+
private AclPermissions(Set principals, boolean listenToEvents) throws RepositoryException {
- principalNames = new HashSet(principals.size());
+ principalNames = new ArrayList(principals.size());
for (Iterator it = principals.iterator(); it.hasNext();) {
principalNames.add(((Principal) it.next()).getName());
}
+ jcrReadPrivilegeName = session.getAccessControlManager().privilegeFromName(Privilege.JCR_READ).getName();
if (listenToEvents) {
/*
@@ -436,12 +357,7 @@
is that everyone can READ everywhere -> makes evaluation for
the most common check (can-read) easy.
*/
- searchReadDeny(principalNames);
- /*
- Determine if there is any ACE node that grants another permission
- than READ.
- */
- searchNonReadAllow(principalNames);
+ readAllowed = isReadAllowed(principalNames);
/*
Make sure this AclPermission recalculates the permissions if
@@ -462,79 +378,49 @@
}
/**
- * Search if there is any ACE that defines permissions for any of the
- * principals AND denies-READ.
+ * If this provider defines read-permission for everyone (defined upon
+ * init with default values), search if there is any ACE that defines
+ * permissions for any of the principals AND denies-READ. Otherwise
+ * this shortcut is not possible.
*
* @param principalnames
*/
- private void searchReadDeny(Set principalnames) {
- try {
- QueryManager qm = session.getWorkspace().getQueryManager();
- StringBuffer stmt = new StringBuffer("/jcr:root");
- stmt.append("//element(*,");
- stmt.append(resolver.getJCRName(NT_REP_DENY_ACE));
- stmt.append(")[(");
-
- // where the rep:principalName property exactly matches any of
- // the given principalsNames
- int i = 0;
- Iterator itr = principalnames.iterator();
- while (itr.hasNext()) {
- stmt.append("@").append(resolver.getJCRName(P_PRINCIPAL_NAME)).append(" eq ");
- stmt.append("'").append(itr.next().toString()).append("'");
- if (++i < principalnames.size()) {
- stmt.append(" or ");
+ private boolean isReadAllowed(Collection principalnames) {
+ boolean isReadAllowed = false;
+ if (initializedWithDefaults) {
+ try {
+ QueryManager qm = session.getWorkspace().getQueryManager();
+ StringBuffer stmt = new StringBuffer("/jcr:root");
+ stmt.append("//element(*,");
+ stmt.append(resolver.getJCRName(NT_REP_DENY_ACE));
+ stmt.append(")[(");
+
+ // where the rep:principalName property exactly matches any of
+ // the given principalsNames
+ int i = 0;
+ Iterator itr = principalnames.iterator();
+ while (itr.hasNext()) {
+ stmt.append("@").append(resolver.getJCRName(P_PRINCIPAL_NAME)).append(" eq ");
+ stmt.append("'").append(itr.next().toString()).append("'");
+ if (++i < principalnames.size()) {
+ stmt.append(" or ");
+ }
}
- }
- // AND rep:privileges contains the READ privilege
- stmt.append(") and @ ");
- stmt.append(resolver.getJCRName(P_PRIVILEGES));
- stmt.append(" = '").append(Privilege.READ).append("']");
-
- Query q = qm.createQuery(stmt.toString(), Query.XPATH);
+ // AND rep:privileges contains the READ privilege
+ stmt.append(") and @");
+ stmt.append(resolver.getJCRName(P_PRIVILEGES));
+ stmt.append(" = '").append(jcrReadPrivilegeName).append("']");
- NodeIterator it = q.execute().getNodes();
- readAllowed = !it.hasNext();
- } catch (RepositoryException e) {
- log.error(e.toString());
- // unable to determine... -> no shortcut upon grants
- readAllowed = false;
- }
- }
+ Query q = qm.createQuery(stmt.toString(), Query.XPATH);
- private void searchNonReadAllow(Set principalnames) {
- try {
- QueryManager qm = session.getWorkspace().getQueryManager();
- StringBuffer stmt = new StringBuffer("/jcr:root");
- stmt.append("//element(*,");
- stmt.append(resolver.getJCRName(NT_REP_GRANT_ACE));
- stmt.append(")[(");
- // where the rep:principalName property exactly matches any of
- // the given principalsNames
- int i = 0;
- Iterator itr = principalnames.iterator();
- while (itr.hasNext()) {
- stmt.append("@").append(resolver.getJCRName(P_PRINCIPAL_NAME)).append(" eq ");
- stmt.append("'").append(itr.next().toString()).append("'");
- if (++i < principalnames.size()) {
- stmt.append(" or ");
- }
+ NodeIterator it = q.execute().getNodes();
+ isReadAllowed = !it.hasNext();
+ } catch (RepositoryException e) {
+ log.error(e.toString());
+ // unable to determine... -> no shortcut upon grants
}
-
- // AND rep:privileges contains the READ privilege
- stmt.append(") and @");
- stmt.append(resolver.getJCRName(P_PRIVILEGES));
- stmt.append(" ne \"").append(Privilege.READ).append("\"]");
-
- Query q = qm.createQuery(stmt.toString(), Query.XPATH);
-
- NodeIterator it = q.execute().getNodes();
- readOnly = !it.hasNext();
- } catch (RepositoryException e) {
- log.error(e.toString());
- // unable to determine... -> no shortcut upon grants
- readOnly = false;
}
+ return isReadAllowed;
}
//------------------------------------< AbstractCompiledPermissions >---
@@ -543,11 +429,11 @@
*/
protected Result buildResult(Path absPath) throws RepositoryException {
boolean existingNode = false;
- Node node = null;
+ NodeImpl node = null;
String jcrPath = resolver.getJCRPath(absPath);
if (session.nodeExists(jcrPath)) {
- node = session.getNode(jcrPath);
+ node = (NodeImpl) session.getNode(jcrPath);
existingNode = true;
} else {
// path points to existing prop or non-existing item (node or prop).
@@ -555,7 +441,7 @@
String parentPath = Text.getRelativeParent(jcrPath, 1);
while (parentPath.length() > 0) {
if (session.nodeExists(parentPath)) {
- node = session.getNode(parentPath);
+ node = (NodeImpl) session.getNode(parentPath);
break;
}
parentPath = Text.getRelativeParent(parentPath, 1);
@@ -567,22 +453,59 @@
throw new ItemNotFoundException("Item out of hierarchy.");
}
- // build the ACL for the specified principals at path or at the
- // direct ancestor of path (that must be definition exist).
- ACLImpl acl = getACL((NodeImpl) node, principalNames);
-
- // privileges to expose
- int privileges = acl.getPrivileges();
-
- // calculate the permissions
- int permissions;
- if (existingNode || session.propertyExists(jcrPath)) {
- permissions = acl.getPermissions(session.getItem(jcrPath));
+ boolean isAcItem = isAcItem(absPath);
+
+ // retrieve all ACEs at path or at the direct ancestor of path that
+ // apply for the principal names.
+ AccessControlEntryIterator entries = new Entries(getNode(node), principalNames).iterator();
+ // build a list of ACEs that are defined locally at the node
+ List localACEs;
+ if (existingNode && isAccessControlled(node)) {
+ NodeImpl aclNode = node.getNode(N_POLICY);
+ localACEs = Arrays.asList(systemEditor.getACL(aclNode).getAccessControlEntries());
} else {
- String name = resolver.getJCRName(absPath.getNameElement().getName());
- permissions = acl.getPermissions(name);
+ localACEs = Collections.EMPTY_LIST;
+ }
+ /*
+ Calculate privileges and permissions:
+ Since the ACEs only define privileges on a node and do not allow
+ to add additional restrictions, the permissions can be determined
+ without taking the given target name or target item into account.
+ */
+ int allows = Permission.NONE;
+ int denies = Permission.NONE;
+
+ int allowPrivileges = PrivilegeRegistry.NO_PRIVILEGE;
+ int denyPrivileges = PrivilegeRegistry.NO_PRIVILEGE;
+ int parentAllows = PrivilegeRegistry.NO_PRIVILEGE;
+ int parentDenies = PrivilegeRegistry.NO_PRIVILEGE;
+
+ while (entries.hasNext() && allows != PrivilegeRegistry.ALL) {
+ JackrabbitAccessControlEntry ace = (JackrabbitAccessControlEntry) entries.next();
+ // Determine if the ACE is defined on the node at absPath (locally):
+ // Except for READ-privileges the permissions must be determined
+ // from privileges defined for the parent. Consequently aces
+ // defined locally must be treated different than inherited entries.
+ int entryBits = ace.getPrivilegeBits();
+ boolean isLocal = localACEs.contains(ace);
+ if (!isLocal) {
+ if (ace.isAllow()) {
+ parentAllows |= Permission.diff(entryBits, parentDenies);
+ } else {
+ parentDenies |= Permission.diff(entryBits, parentAllows);
+ }
+ }
+ if (ace.isAllow()) {
+ allowPrivileges |= Permission.diff(entryBits, denyPrivileges);
+ int permissions = Permission.calculatePermissions(allowPrivileges, parentAllows, true, isAcItem);
+ allows |= Permission.diff(permissions, denies);
+ } else {
+ denyPrivileges |= Permission.diff(entryBits, allowPrivileges);
+ int permissions = Permission.calculatePermissions(denyPrivileges, parentDenies, false, isAcItem);
+ denies |= Permission.diff(permissions, allows);
+ }
}
- return new Result(permissions, privileges);
+ return new Result(allows, denies, allowPrivileges, denyPrivileges);
}
//--------------------------------------------< CompiledPermissions >---
@@ -609,8 +532,6 @@
public boolean grants(Path absPath, int permissions) throws RepositoryException {
if (permissions == Permission.READ && readAllowed && !isAcItem(absPath)) {
return true;
- } else if (permissions != Permission.READ && readOnly) {
- return false;
} else {
return super.grants(absPath, permissions);
}
@@ -620,7 +541,7 @@
/**
* @see EventListener#onEvent(EventIterator)
*/
- public void onEvent(EventIterator events) {
+ public synchronized void onEvent(EventIterator events) {
// only invalidate cache if any of the events affects the
// nodes defining permissions for principals compiled here.
boolean clearCache = false;
@@ -628,55 +549,55 @@
try {
Event ev = events.nextEvent();
String path = ev.getPath();
- // TODO: check if valid. check required.
-
switch (ev.getType()) {
case Event.NODE_ADDED:
- // test if the new ACE-nodes affects the permission
- // of any of the 'principals'.
+ // test if the new node is an ACE node that affects
+ // the permission of any of the principals listed in
+ // principalNames.
NodeImpl n = (NodeImpl) session.getNode(path);
- String pName = n.getProperty(P_PRINCIPAL_NAME).getString();
- if (principalNames.contains(pName)) {
- // new ACE entry for the principals -> clear cache
- clearCache = true;
- // if ace is a new DENY -> check if denies reading
+ if (n.isNodeType(NT_REP_ACE) &&
+ principalNames.contains(n.getProperty(P_PRINCIPAL_NAME).getString())) {
+ // and reset the readAllowed flag, if the new
+ // ACE denies READ.
if (readAllowed && n.isNodeType(NT_REP_DENY_ACE)) {
Value[] vs = n.getProperty(P_PRIVILEGES).getValues();
for (int i = 0; i < vs.length; i++) {
- if (Privilege.READ.equals(vs[i].getString())) {
+ if (jcrReadPrivilegeName.equals(vs[i].getString())) {
readAllowed = false;
}
}
}
- // if ace is a new ALLOW -> check if obsoletes read-only
- if (readOnly && n.isNodeType(NT_REP_GRANT_ACE)) {
- Value[] vs = n.getProperty(P_PRIVILEGES).getValues();
- for (int i = 0; i < vs.length; i++) {
- if (!Privilege.READ.equals(vs[i].getString())) {
- readOnly = false;
- }
- }
- }
+ clearCache = true;
}
break;
+ case Event.PROPERTY_REMOVED:
case Event.NODE_REMOVED:
// can't find out if the removed ACL/ACE node was
// relevant for the principals
+ readAllowed = isReadAllowed(principalNames);
clearCache = true;
break;
+ case Event.PROPERTY_ADDED:
case Event.PROPERTY_CHANGED:
- // test if the changed ACE_prop affects the permission
- // of any of the 'principals' (most interesting are
- // changed privileges.
+ // test if the added/changed prop belongs to an ACe
+ // node and affects the permission of any of the
+ // principals listed in principalNames.
PropertyImpl p = (PropertyImpl) session.getProperty(path);
- if (P_PRIVILEGES.equals(p.getQName())) {
- // test if principal-name sibling-prop matches
- pName = ((NodeImpl) p.getParent()).getProperty(P_PRINCIPAL_NAME).toString();
- clearCache = principalNames.contains(pName);
- } else if (P_PRINCIPAL_NAME.equals(p.getQName())) {
- // an ace change its principal-name. that should
- // not happen. -> clear cache to be on the safe side.
- clearCache = true;
+ NodeImpl parent = (NodeImpl) p.getParent();
+ if (parent.isNodeType(NT_REP_ACE)) {
+ String principalName = null;
+ if (P_PRIVILEGES.equals(p.getQName())) {
+ // test if principal-name sibling-prop matches
+ principalName = parent.getProperty(P_PRINCIPAL_NAME).getString();
+ } else if (P_PRINCIPAL_NAME.equals(p.getQName())) {
+ // a new ace or an ace change its principal-name.
+ principalName = p.getString();
+ }
+ if (principalName != null &&
+ principalNames.contains(principalName)) {
+ readAllowed = isReadAllowed(principalNames);
+ clearCache = true;
+ }
}
break;
default:
@@ -692,4 +613,43 @@
}
}
}
+
+ private class Entries {
+
+ private final ListOrderedMap principalNamesToEntries;
+
+ private Entries(NodeImpl node, Collection principalNames) throws RepositoryException {
+ principalNamesToEntries = new ListOrderedMap();
+ for (Iterator it = principalNames.iterator(); it.hasNext();) {
+ principalNamesToEntries.put(it.next(), new ArrayList());
+ }
+ collectEntries(node);
+ }
+
+ private void collectEntries(NodeImpl node) throws RepositoryException {
+ // if the given node is access-controlled, construct a new ACL and add
+ // it to the list
+ if (isAccessControlled(node)) {
+ // build acl for the access controlled node
+ NodeImpl aclNode = node.getNode(N_POLICY);
+ ACLTemplate.collectEntries(aclNode, principalNamesToEntries);
+ }
+ // then, recursively look for access controlled parents up the hierarchy.
+ if (!rootNodeId.equals(node.getId())) {
+ NodeImpl parentNode = (NodeImpl) node.getParent();
+ collectEntries(parentNode);
+ }
+ }
+
+ private AccessControlEntryIterator iterator() {
+ List entries = new ArrayList();
+ for (Iterator it =
+ principalNamesToEntries.asList().iterator(); it.hasNext();) {
+ Object key = it.next();
+ entries.addAll((List) principalNamesToEntries.get(key));
+ }
+ return new AccessControlEntryIterator(entries);
+ }
+ }
+
}
Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/acl/ACLTemplate.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/acl/ACLTemplate.java?rev=689499&r1=689498&r2=689499&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/acl/ACLTemplate.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/acl/ACLTemplate.java Wed Aug 27 08:12:04 2008
@@ -17,16 +17,18 @@
package org.apache.jackrabbit.core.security.authorization.acl;
import org.apache.commons.collections.map.ListOrderedMap;
+import org.apache.jackrabbit.api.jsr283.security.AccessControlEntry;
import org.apache.jackrabbit.api.jsr283.security.AccessControlException;
+import org.apache.jackrabbit.api.jsr283.security.Privilege;
+import org.apache.jackrabbit.api.jsr283.security.AccessControlManager;
import org.apache.jackrabbit.api.security.principal.PrincipalManager;
import org.apache.jackrabbit.core.NodeImpl;
import org.apache.jackrabbit.core.SessionImpl;
import org.apache.jackrabbit.core.security.authorization.AccessControlConstants;
-import org.apache.jackrabbit.core.security.authorization.PolicyEntry;
-import org.apache.jackrabbit.core.security.authorization.PolicyTemplate;
+import org.apache.jackrabbit.core.security.authorization.AccessControlEntryImpl;
+import org.apache.jackrabbit.core.security.authorization.JackrabbitAccessControlList;
import org.apache.jackrabbit.core.security.authorization.PrivilegeRegistry;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import org.apache.jackrabbit.core.security.authorization.Permission;
import javax.jcr.NodeIterator;
import javax.jcr.RepositoryException;
@@ -35,91 +37,128 @@
import java.security.acl.Group;
import java.util.ArrayList;
import java.util.Collections;
-import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
-import java.util.Set;
/**
- * {@link PolicyTemplate}-Implementation for the resource-based {@link ACLImpl}.
- *
- * @see PolicyTemplate
- * @see ACLImpl
+ * Implementation of the {@link JackrabbitAccessControlList} interface that
+ * is detached from the effective access control content. Consequently, any
+ * modifications applied to this ACL only take effect, if the policy gets
+ * {@link org.apache.jackrabbit.api.jsr283.security.AccessControlManager#setPolicy(String, org.apache.jackrabbit.api.jsr283.security.AccessControlPolicy) reapplied}
+ * to the <code>AccessControlManager</code> and the changes are saved.
*/
-class ACLTemplate implements PolicyTemplate {
-
- private static final Logger log = LoggerFactory.getLogger(ACLTemplate.class);
+class ACLTemplate implements JackrabbitAccessControlList {
+ /**
+ * Path of the node this ACL template has been created for.
+ */
private final String path;
- private final String name = ACLImpl.POLICY_NAME;
- private final String description;
/**
* Map containing the entries of this ACL Template using the principal
* name as key. The value represents a List containing maximal one grant
* and one deny ACE per principal.
*/
- private final Map entries = new HashMap();
+ private final Map entries = new ListOrderedMap();
/**
- * Construct a new empty {@link PolicyTemplate}.
+ * The principal manager used for validation checks
*/
- ACLTemplate(String path) {
- this.path = path;
- description = null;
- }
+ private final PrincipalManager principalMgr;
/**
- * Create a {@link PolicyTemplate} that is used to edit an existing ACL
- * node.
+ * The privilege registry
*/
- ACLTemplate(NodeImpl aclNode) throws RepositoryException {
- this(aclNode, Collections.EMPTY_SET);
+ private final PrivilegeRegistry privilegeRegistry;
+
+ /**
+ * Construct a new empty {@link ACLTemplate}.
+ *
+ * @param path
+ * @param principalMgr
+ */
+ ACLTemplate(String path, PrincipalManager principalMgr, PrivilegeRegistry privilegeRegistry) {
+ this.path = path;
+ this.principalMgr = principalMgr;
+ this.privilegeRegistry = privilegeRegistry;
}
/**
- * Create a {@link PolicyTemplate} that is used to edit an existing ACL
- * policy but only lists those entries that match any of the principal
- * names present in the given filter. If the set is empty all entry will
- * be read as local entries. Otherwise only the entries matching any of
- * the principals in the set will be retrieved.
+ * Create a {@link ACLTemplate} that is used to edit an existing ACL
+ * node.
*/
- ACLTemplate(NodeImpl aclNode, Set principalNames) throws RepositoryException {
+ ACLTemplate(NodeImpl aclNode, PrivilegeRegistry privilegeRegistry) throws RepositoryException {
if (aclNode == null || !aclNode.isNodeType(AccessControlConstants.NT_REP_ACL)) {
throw new IllegalArgumentException("Node must be of type: " +
AccessControlConstants.NT_REP_ACL);
}
+ SessionImpl sImpl = (SessionImpl) aclNode.getSession();
path = aclNode.getParent().getPath();
- description = null;
- loadEntries(aclNode, principalNames);
+ principalMgr = sImpl.getPrincipalManager();
+ this.privilegeRegistry = privilegeRegistry;
+
+ // load the entries:
+ AccessControlManager acMgr = sImpl.getAccessControlManager();
+ NodeIterator itr = aclNode.getNodes();
+ while (itr.hasNext()) {
+ NodeImpl aceNode = (NodeImpl) itr.nextNode();
+
+ String principalName = aceNode.getProperty(AccessControlConstants.P_PRINCIPAL_NAME).getString();
+ Principal princ = principalMgr.getPrincipal(principalName);
+
+ Value[] privValues = aceNode.getProperty(AccessControlConstants.P_PRIVILEGES).getValues();
+ Privilege[] privs = new Privilege[privValues.length];
+ for (int i = 0; i < privValues.length; i++) {
+ privs[i] = acMgr.privilegeFromName(privValues[i].getString());
+ }
+ // create a new ACEImpl (omitting validation check)
+ Entry ace = new Entry(
+ princ,
+ privs,
+ aceNode.isNodeType(AccessControlConstants.NT_REP_GRANT_ACE));
+ // add the entry
+ internalAdd(ace);
+ }
}
/**
- * Returns those {@link PolicyEntry entries} of this
- * <code>PolicyTemplate</code> that affect the permissions of the given
- * <code>principal</code>.
+ * Separately collect the entries defined for the principals with the
+ * specified names and return a map consisting of principal name key
+ * and a list of ACEs as value.
*
- * @return the {@link PolicyEntry entries} present in this
- * <code>PolicyTemplate</code> that affect the permissions of the given
- * <code>principal</code>.
+ * @param aclNode
+ * @param princToEntries Map of key = principalName and value = ArrayList
+ * to be filled with ACEs matching the principal names.
+ * @throws RepositoryException
*/
- ACEImpl[] getEntries(Principal principal) {
- List l = internalGetEntries(principal);
- return (ACEImpl[]) l.toArray(new ACEImpl[l.size()]);
- }
+ static void collectEntries(NodeImpl aclNode, Map princToEntries)
+ throws RepositoryException {
+ SessionImpl sImpl = (SessionImpl) aclNode.getSession();
+ PrincipalManager principalMgr = sImpl.getPrincipalManager();
+ AccessControlManager acMgr = sImpl.getAccessControlManager();
- private void checkValidEntry(PolicyEntry entry) throws AccessControlException {
- if (!(entry instanceof ACEImpl)) {
- throw new AccessControlException("Invalid PolicyEntry " + entry + ". Expected instanceof ACEImpl.");
- }
- ACEImpl ace = (ACEImpl) entry;
- // TODO: ev. assert that the principal is known to the repository
- // make sure valid privileges are provided.
- PrivilegeRegistry.getBits(ace.getPrivileges());
+ NodeIterator itr = aclNode.getNodes();
+ while (itr.hasNext()) {
+ NodeImpl aceNode = (NodeImpl) itr.nextNode();
+ String principalName = aceNode.getProperty(AccessControlConstants.P_PRINCIPAL_NAME).getString();
+ // only process aceNode if 'principalName' is contained in the given set
+ if (princToEntries.containsKey(principalName)) {
+ Principal princ = principalMgr.getPrincipal(principalName);
- if (!entry.isAllow() && entry.getPrincipal() instanceof Group) {
- throw new AccessControlException("For group principals permissions can only be added but not denied.");
+ Value[] privValues = aceNode.getProperty(AccessControlConstants.P_PRIVILEGES).getValues();
+ Privilege[] privs = new Privilege[privValues.length];
+ for (int i = 0; i < privValues.length; i++) {
+ privs[i] = acMgr.privilegeFromName(privValues[i].getString());
+ }
+ // create a new ACEImpl (omitting validation check)
+ Entry ace = new Entry(
+ princ,
+ privs,
+ aceNode.isNodeType(AccessControlConstants.NT_REP_GRANT_ACE));
+ // add it to the proper list (e.g. separated by principals)
+ ((List) princToEntries.get(principalName)).add(ace);
+ }
}
}
@@ -140,187 +179,163 @@
}
}
- private synchronized boolean internalAdd(ACEImpl entry) {
+ private synchronized boolean internalAdd(Entry entry) throws AccessControlException {
Principal principal = entry.getPrincipal();
List l = internalGetEntries(principal);
if (l.isEmpty()) {
+ // simple case: just add the new entry
l.add(entry);
entries.put(principal.getName(), l);
return true;
} else {
- return adjustEntries(entry, l);
- }
- }
-
- private static boolean adjustEntries(ACEImpl entry, List l) {
- if (l.contains(entry)) {
- // the same entry is already contained -> no modification
- return false;
- }
+ if (l.contains(entry)) {
+ // the same entry is already contained -> no modification
+ return false;
+ }
+ // ev. need to adjust existing entries
+ Entry complementEntry = null;
+ Entry[] entries = (Entry[]) l.toArray(new Entry[l.size()]);
+ for (int i = 0; i < entries.length; i++) {
+ if (entry.isAllow() == entries[i].isAllow()) {
+ int existingPrivs = entries[i].getPrivilegeBits();
+ if ((existingPrivs | ~entry.getPrivilegeBits()) == -1) {
+ // all privileges to be granted/denied are already present
+ // in the existing entry -> not modified
+ return false;
+ }
+
+ // remove the existing entry and create a new that includes
+ // both the new privileges and the existing onces.
+ l.remove(i);
+ int mergedBits = entries[i].getPrivilegeBits() | entry.getPrivilegeBits();
+ Privilege[] mergedPrivs = privilegeRegistry.getPrivileges(mergedBits);
+ // omit validation check.
+ entry = new Entry(entry.getPrincipal(), mergedPrivs, entry.isAllow());
+ } else {
+ complementEntry = entries[i];
+ }
+ }
- ACEImpl complementEntry = null;
- ACEImpl[] entries = (ACEImpl[]) l.toArray(new ACEImpl[l.size()]);
- for (int i = 0; i < entries.length; i++) {
- ACEImpl t = entries[i];
- if (entry.isAllow() == entries[i].isAllow()) {
- // replace the existing entry with the new one at the end.
- l.remove(i);
- } else {
- complementEntry = t;
+ // make sure, that the complement entry (if existing) does not
+ // grant/deny the same privileges -> remove privs that are now
+ // denied/granted.
+ if (complementEntry != null) {
+ int complPrivs = complementEntry.getPrivilegeBits();
+ int resultPrivs = Permission.diff(complPrivs, entry.getPrivilegeBits());
+ if (resultPrivs == PrivilegeRegistry.NO_PRIVILEGE) {
+ l.remove(complementEntry);
+ } else if (resultPrivs != complPrivs) {
+ l.remove(complementEntry);
+ // omit validation check
+ Entry tmpl = new Entry(entry.getPrincipal(),
+ privilegeRegistry.getPrivileges(resultPrivs),
+ !entry.isAllow());
+ l.add(tmpl);
+ } /* else: does not need to be modified.*/
}
- }
- // make sure, that the complement entry (if existing) does not
- // grant/deny the same privileges -> remove privs that are now
- // denied/granted.
- if (complementEntry != null) {
- int complPrivs = complementEntry.getPrivilegeBits();
- int resultPrivs = PrivilegeRegistry.diff(complPrivs, entry.getPrivilegeBits());
- if (resultPrivs == PrivilegeRegistry.NO_PRIVILEGE) {
- l.remove(complementEntry);
- } else if (resultPrivs != complPrivs) {
- l.remove(complementEntry);
- ACEImpl tmpl = new ACEImpl(entry.getPrincipal(), resultPrivs, !entry.isAllow());
- l.add(tmpl);
- } /* else: does not need to be modified.*/
- }
- // finally add the new entry at the end.
- l.add(entry);
- return true;
- }
-
- private synchronized boolean internalRemove(ACEImpl entry) {
- List l = internalGetEntries(entry.getPrincipal());
- boolean success = l.remove(entry);
- if (l.isEmpty()) {
- entries.remove(entry.getPrincipal().getName());
+ // finally add the new entry at the end.
+ l.add(entry);
+ return true;
}
- return success;
}
/**
- * Read the child nodes of the given node and build {@link ACEImpl}
- * objects. If the filter set is not empty, the entries are
- * collected separately for each principal.
*
- * @param aclNode
- * @param filter Set of principal names used to filter the entries present
- * within this ACL.
- */
- private void loadEntries(NodeImpl aclNode, Set filter)
- throws RepositoryException {
- PrincipalManager pMgr = ((SessionImpl) aclNode.getSession()).getPrincipalManager();
- // NOTE: don't simply add the individual matching entries, instead
- // collect entries separated for the principals first and later add
- // them in the order the need to be evaluated (order of principals).
- // therefore use ListOrderedMap in order to preserve the order of the
- // principalNames passed with the 'filter'.
- String noFilter = "";
- Map princToEntries = new ListOrderedMap();
- if (filter == null || filter.isEmpty()) {
- princToEntries.put(noFilter, new ArrayList());
- } else {
- for (Iterator it = filter.iterator(); it.hasNext();) {
- princToEntries.put(it.next().toString(), new ArrayList());
- }
+ * @param principal
+ * @param privileges
+ * @param isAllow
+ * @throws AccessControlException
+ */
+ private void checkValidEntry(Principal principal, Privilege[] privileges, boolean isAllow) throws AccessControlException {
+ // validate principal
+ if (!principalMgr.hasPrincipal(principal.getName())) {
+ throw new AccessControlException("Principal " + principal.getName() + " does not exist.");
}
-
- NodeIterator itr = aclNode.getNodes();
- while (itr.hasNext()) {
- NodeImpl aceNode = (NodeImpl) itr.nextNode();
- String principalName = aceNode.getProperty(AccessControlConstants.P_PRINCIPAL_NAME).getString();
- // only process aceNode if no filter is present of if the filter
- // contains the principal-name defined with the ace-Node
- String key = (filter == null || filter.isEmpty()) ? noFilter : principalName;
- if (princToEntries.containsKey(key)) {
- Principal princ = pMgr.getPrincipal(principalName);
- Value[] privValues = aceNode.getProperty(AccessControlConstants.P_PRIVILEGES).getValues();
- String[] privNames = new String[privValues.length];
- for (int i = 0; i < privValues.length; i++) {
- privNames[i] = privValues[i].getString();
- }
- // create a new ACEImpl
- ACEImpl ace = new ACEImpl(
- princ,
- PrivilegeRegistry.getBits(privNames),
- aceNode.isNodeType(AccessControlConstants.NT_REP_GRANT_ACE));
- // add it to the proper list (e.g. separated by principals)
- ((List) princToEntries.get(key)).add(ace);
- }
+ // additional validation: a group may not have 'denied' permissions
+ if (!isAllow && principal instanceof Group) {
+ throw new AccessControlException("For group principals permissions can only be added but not denied.");
}
+ }
- // now retrieve the entries for each principal names and add them
- // to the single (complete) list of all entries that need to
- // be evaluated.
- for (Iterator it = princToEntries.keySet().iterator(); it.hasNext();) {
- String princName = it.next().toString();
- for (Iterator entries = ((List) princToEntries.get(princName)).iterator();
- entries.hasNext();) {
- ACEImpl ace = (ACEImpl) entries.next();
- internalAdd(ace);
- }
- }
+ //--------------------------------------------------< AccessControlList >---
+ /**
+ * @see org.apache.jackrabbit.api.jsr283.security.AccessControlList#getAccessControlEntries()
+ */
+ public AccessControlEntry[] getAccessControlEntries() throws RepositoryException {
+ List l = internalGetEntries();
+ return (AccessControlEntry[]) l.toArray(new AccessControlEntry[l.size()]);
}
- //------------------------------------------------< AccessControlPolicy >---
/**
- * @see org.apache.jackrabbit.api.jsr283.security.AccessControlPolicy#getName()
+ * @see org.apache.jackrabbit.api.jsr283.security.AccessControlList#addAccessControlEntry(Principal, Privilege[])
*/
- public String getName() throws RepositoryException {
- return name;
+ public boolean addAccessControlEntry(Principal principal, Privilege[] privileges)
+ throws AccessControlException, RepositoryException {
+ return addEntry(principal, privileges, true, Collections.EMPTY_MAP);
}
/**
- * @see org.apache.jackrabbit.api.jsr283.security.AccessControlPolicy#getDescription()
+ * @see org.apache.jackrabbit.api.jsr283.security.AccessControlList#removeAccessControlEntry(AccessControlEntry)
*/
- public String getDescription() throws RepositoryException {
- return description;
+ public synchronized void removeAccessControlEntry(AccessControlEntry ace)
+ throws AccessControlException, RepositoryException {
+ if (!(ace instanceof Entry)) {
+ throw new AccessControlException("Invalid AccessControlEntry implementation " + ace.getClass().getName() + ".");
+ }
+ List l = internalGetEntries(ace.getPrincipal());
+ if (l.remove(ace)) {
+ if (l.isEmpty()) {
+ entries.remove(ace.getPrincipal().getName());
+ }
+ } else {
+ throw new AccessControlException("AccessControlEntry " + ace + " cannot be removed from ACL defined at " + getPath());
+ }
}
- //-----------------------------------------------------< PolicyTemplate >---
+ //-----------------------------------------------------< JackrabbitAccessControlList >---
/**
- * @see PolicyTemplate#getPath()
+ * @see JackrabbitAccessControlList#getPath()
*/
public String getPath() {
return path;
}
/**
- * @see PolicyTemplate#isEmpty()
+ * @see JackrabbitAccessControlList#isEmpty()
*/
public boolean isEmpty() {
return entries.isEmpty();
}
/**
- * @see PolicyTemplate#size()
+ * @see JackrabbitAccessControlList#size()
*/
public int size() {
return internalGetEntries().size();
}
/**
- * @see PolicyTemplate#getEntries()
+ * @see JackrabbitAccessControlList#addEntry(Principal, Privilege[], boolean)
*/
- public PolicyEntry[] getEntries() {
- List l = internalGetEntries();
- return (PolicyEntry[]) l.toArray(new PolicyEntry[l.size()]);
+ public boolean addEntry(Principal principal, Privilege[] privileges, boolean isAllow)
+ throws AccessControlException, RepositoryException {
+ return addEntry(principal, privileges, isAllow, null);
}
/**
- * @see PolicyTemplate#setEntry(PolicyEntry)
+ * @see JackrabbitAccessControlList#addEntry(Principal, Privilege[], boolean, Map)
*/
- public boolean setEntry(PolicyEntry entry) throws AccessControlException, RepositoryException {
- checkValidEntry(entry);
- return internalAdd((ACEImpl) entry);
- }
+ public boolean addEntry(Principal principal, Privilege[] privileges,
+ boolean isAllow, Map restrictions)
+ throws AccessControlException, RepositoryException {
+ if (restrictions != null && !restrictions.isEmpty()) {
+ throw new AccessControlException("This AccessControlList does not allow for additional restrictions.");
+ }
- /**
- * @see PolicyTemplate#removeEntry(PolicyEntry)
- */
- public boolean removeEntry(PolicyEntry entry) throws AccessControlException, RepositoryException {
- checkValidEntry(entry);
- return internalRemove((ACEImpl) entry);
+ checkValidEntry(principal, privileges, isAllow);
+ Entry ace = new Entry(principal, privileges, isAllow);
+ return internalAdd(ace);
}
//-------------------------------------------------------------< Object >---
@@ -336,10 +351,10 @@
}
/**
- * Returns true if the name and the entries are equal; false otherwise.
+ * Returns true if the path and the entries are equal; false otherwise.
*
* @param obj
- * @return true if the name and the entries are equal; false otherwise.
+ * @return true if the path and the entries are equal; false otherwise.
* @see Object#equals(Object)
*/
public boolean equals(Object obj) {
@@ -348,10 +363,20 @@
}
if (obj instanceof ACLTemplate) {
- ACLTemplate tmpl = (ACLTemplate) obj;
- boolean equalName = (name == null || tmpl.name == null || name.equals(tmpl.name));
- return equalName && entries.equals(tmpl.entries);
+ ACLTemplate acl = (ACLTemplate) obj;
+ return path.equals(acl.path) && entries.equals(acl.entries);
}
return false;
}
+
+ //--------------------------------------------------------------------------
+ /**
+ *
+ */
+ static class Entry extends AccessControlEntryImpl {
+
+ Entry(Principal principal, Privilege[] privileges, boolean allow) throws AccessControlException {
+ super(principal, privileges, allow, Collections.EMPTY_MAP);
+ }
+ }
}