From commits-return-5587-apmail-jackrabbit-commits-archive=jackrabbit.apache.org@jackrabbit.apache.org Wed Mar 19 13:59:40 2008 Return-Path: Delivered-To: apmail-jackrabbit-commits-archive@www.apache.org Received: (qmail 65799 invoked from network); 19 Mar 2008 13:59:40 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (140.211.11.2) by minotaur.apache.org with SMTP; 19 Mar 2008 13:59:40 -0000 Received: (qmail 92974 invoked by uid 500); 19 Mar 2008 13:59:34 -0000 Delivered-To: apmail-jackrabbit-commits-archive@jackrabbit.apache.org Received: (qmail 92910 invoked by uid 500); 19 Mar 2008 13:59:33 -0000 Mailing-List: contact commits-help@jackrabbit.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@jackrabbit.apache.org Delivered-To: mailing list commits@jackrabbit.apache.org Received: (qmail 92884 invoked by uid 99); 19 Mar 2008 13:59:33 -0000 Received: from nike.apache.org (HELO nike.apache.org) (192.87.106.230) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 19 Mar 2008 06:59:33 -0700 X-ASF-Spam-Status: No, hits=-2000.0 required=10.0 tests=ALL_TRUSTED X-Spam-Check-By: apache.org Received: from [140.211.11.3] (HELO eris.apache.org) (140.211.11.3) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 19 Mar 2008 13:58:41 +0000 Received: by eris.apache.org (Postfix, from userid 65534) id AF2441A9852; Wed, 19 Mar 2008 06:58:37 -0700 (PDT) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r638834 [7/14] - in /jackrabbit/trunk: jackrabbit-api/src/main/java/org/apache/jackrabbit/api/ jackrabbit-api/src/main/java/org/apache/jackrabbit/api/security/ jackrabbit-api/src/main/java/org/apache/jackrabbit/api/security/principal/ jackr... Date: Wed, 19 Mar 2008 13:57:11 -0000 To: commits@jackrabbit.apache.org From: angela@apache.org X-Mailer: svnmailer-1.0.8 Message-Id: <20080319135837.AF2441A9852@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org Added: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/combined/ACLImpl.java URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/combined/ACLImpl.java?rev=638834&view=auto ============================================================================== --- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/combined/ACLImpl.java (added) +++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/combined/ACLImpl.java Wed Mar 19 06:56:13 2008 @@ -0,0 +1,147 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.jackrabbit.core.security.authorization.combined; + +import org.apache.jackrabbit.core.security.authorization.Permission; +import org.apache.jackrabbit.core.security.authorization.PrivilegeRegistry; +import org.apache.jackrabbit.util.Text; + +import javax.jcr.RepositoryException; +import javax.jcr.Node; +import javax.jcr.Item; +import java.security.Principal; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +/** + * ACLImpl... + */ +class ACLImpl { + + private final Set acPaths; + private final Map principalToEntryArray; + + private int privileges = -1; + + ACLImpl(Map principalToEntryArray, Set acPaths) { + this.acPaths = acPaths; + this.principalToEntryArray = principalToEntryArray; + } + + Set getAcPaths() { + return acPaths; + } + + /** + * @param target Existing target item for which the permissions will be + * evaluated. + * @param protectsACL + * @return + * @throws RepositoryException + */ + int getPermissions(Item target, boolean protectsACL) throws RepositoryException { + int allows = 0; + int denies = 0; + for (Iterator it = principalToEntryArray.keySet().iterator(); + it.hasNext() && allows != Permission.ALL;) { + Principal princ = (Principal) it.next(); + PolicyEntryImpl[] aces = (PolicyEntryImpl[]) principalToEntryArray.get(princ); + // loop over all entries and evaluate allows/denies for those + // matching the given jcrPath + for (int i = 0; i < aces.length; i++) { + PolicyEntryImpl entr = aces[i]; + // TODO: check again if correct + if (entr.matches(target)) { + int privs = entr.getPrivilegeBits(); + int permissions = Permission.calculatePermissions(privs, privs, protectsACL); + if (entr.isAllow()) { + allows |= Permission.diff(permissions, denies); + } else { + denies |= Permission.diff(permissions, allows); + } + } + } + } + return allows; + } + + /** + * + * @param parent Existing parent of the target to be evaluated. + * @param targetName name of a non-existing child item to calculate the + * permissions for. + * @param protectsACL + * @return + * @throws RepositoryException + */ + int getPermissions(Node parent, String targetName, boolean protectsACL) throws RepositoryException { + int allows = 0; + int denies = 0; + String jcrPath = parent.getPath() + "/" + targetName; + for (Iterator it = principalToEntryArray.keySet().iterator(); + it.hasNext() && allows != Permission.ALL;) { + Principal princ = (Principal) it.next(); + PolicyEntryImpl[] aces = (PolicyEntryImpl[]) principalToEntryArray.get(princ); + // loop over all entries and evaluate allows/denies for those + // matching the given jcrPath + // TODO: check if correct + for (int i = 0; i < aces.length; i++) { + PolicyEntryImpl entr = aces[i]; + if (entr.matches(jcrPath)) { + int privs = entr.getPrivilegeBits(); + int permissions = Permission.calculatePermissions(privs, privs, protectsACL); + if (entr.isAllow()) { + allows |= Permission.diff(permissions, denies); + } else { + denies |= Permission.diff(permissions, allows); + } + } + } + } + return allows; + } + + int getPrivileges(String nodePath) throws RepositoryException { + if (privileges == -1) { + int allows = 0; + int denies = 0; + for (Iterator it = principalToEntryArray.keySet().iterator(); + it.hasNext() && allows != Permission.ALL;) { + Principal princ = (Principal) it.next(); + PolicyEntryImpl[] aces = (PolicyEntryImpl[]) principalToEntryArray.get(princ); + // loop over all entries and evaluate allows/denies for those + // matching the given jcrPath + for (int i = 0; i < aces.length; i++) { + PolicyEntryImpl entr = aces[i]; + // TODO: check again which ACEs must be respected. + // TODO: maybe ancestor-defs only if glob = *? + String np = entr.getNodePath(); + if (np.equals(nodePath) || Text.isDescendant(np, nodePath)) { + if (entr.isAllow()) { + allows |= PrivilegeRegistry.diff(entr.getPrivilegeBits(), denies); + } else { + denies |= PrivilegeRegistry.diff(entr.getPrivilegeBits(), allows); + } + } + } + } + privileges = allows; + } + return privileges; + } +} \ No newline at end of file Propchange: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/combined/ACLImpl.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/combined/ACLImpl.java ------------------------------------------------------------------------------ svn:keywords = author date id revision url Added: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/combined/CombinedEditor.java URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/combined/CombinedEditor.java?rev=638834&view=auto ============================================================================== --- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/combined/CombinedEditor.java (added) +++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/combined/CombinedEditor.java Wed Mar 19 06:56:13 2008 @@ -0,0 +1,359 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.jackrabbit.core.security.authorization.combined; + +import org.apache.jackrabbit.core.NodeId; +import org.apache.jackrabbit.core.NodeImpl; +import org.apache.jackrabbit.core.SessionImpl; +import org.apache.jackrabbit.api.security.principal.PrincipalManager; +import org.apache.jackrabbit.core.security.authorization.AccessControlEditor; +import org.apache.jackrabbit.core.security.authorization.PolicyTemplate; +import org.apache.jackrabbit.core.security.authorization.AccessControlConstants; +import org.apache.jackrabbit.core.security.authorization.PrivilegeRegistry; +import org.apache.jackrabbit.core.security.authorization.acl.ACLEditor; +import org.apache.jackrabbit.core.security.jsr283.security.AccessControlException; +import org.apache.jackrabbit.core.security.jsr283.security.Privilege; +import org.apache.jackrabbit.core.security.principal.PrincipalImpl; +import org.apache.jackrabbit.core.security.principal.ItemBasedPrincipal; +import org.apache.jackrabbit.spi.Name; +import org.apache.jackrabbit.spi.Path; +import org.apache.jackrabbit.spi.commons.conversion.NamePathResolver; +import org.apache.jackrabbit.util.Text; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.jcr.ItemNotFoundException; +import javax.jcr.NodeIterator; +import javax.jcr.RepositoryException; +import javax.jcr.Value; +import javax.jcr.ValueFactory; +import java.security.Principal; +import java.util.ArrayList; +import java.util.List; + +/** + * CombinedEditor... + */ +class CombinedEditor extends ACLEditor { + + // TODO: must make sure, that store paths/globs do not contain remapped prefixes from the session + + private static Logger log = LoggerFactory.getLogger(CombinedEditor.class); + + private final SessionImpl session; + private final NamePathResolver systemResolver; + private final Path acRootPath; + + CombinedEditor(SessionImpl session, NamePathResolver systemResolver, + Path acRootPath) throws RepositoryException { + super(session); + this.session = session; + this.systemResolver = systemResolver; + this.acRootPath = acRootPath; + } + + PolicyTemplateImpl editPolicyTemplate(Principal principal) throws RepositoryException { + if (!session.getPrincipalManager().hasPrincipal(principal.getName())) { + throw new AccessControlException("Unknown principal."); + } + NodeId nid = getAcId(principal); + if (nid == null) { + nid = createAcNode(principal).getNodeId(); + } + + PolicyTemplate pt = getPolicyTemplate(nid); + if (pt instanceof PolicyTemplateImpl) { + return (PolicyTemplateImpl) pt; + } else { + // should never get here. + throw new AccessControlException(); + } + } + + PolicyTemplateImpl getPolicyTemplate(Principal principal) throws RepositoryException { + if (!session.getPrincipalManager().hasPrincipal(principal.getName())) { + throw new AccessControlException("Unknown principal."); + } + + NodeId nid = getAcId(principal); + if (nid != null) { + PolicyTemplate pt = getPolicyTemplate(nid); + if (pt instanceof PolicyTemplateImpl) { + return (PolicyTemplateImpl) pt; + } + } + + // no policy for the given principal + log.debug("No combined policy template for Principal " + principal.getName()); + return null; + } + + //------------------------------------------------< AccessControlEditor >--- + /** + * @see AccessControlEditor#getPolicyTemplate(NodeId) + */ + public PolicyTemplate getPolicyTemplate(NodeId id) throws AccessControlException, ItemNotFoundException, RepositoryException { + checkProtectsNode(id); + + NodeImpl acNode = getAcNode(id); + if (acNode != null) { + if (isAccessControlled(acNode)) { + return buildTemplate(acNode); + } else { + log.debug("No local policy defined for Node " + id); + return null; + } + } else { + // nodeID not below rep:accesscontrol -> delegate to ACLEditor + return super.getPolicyTemplate(id); + } + } + + /** + * @see AccessControlEditor#editPolicyTemplate(NodeId) + */ + public PolicyTemplate editPolicyTemplate(NodeId id) throws AccessControlException, ItemNotFoundException, RepositoryException { + checkProtectsNode(id); + + NodeImpl acNode = getAcNode(id); + if (acNode != null) { + return buildTemplate(acNode); + } else { + // nodeID not below rep:accesscontrol -> delegate to ACLEditor + return super.editPolicyTemplate(id); + } + } + + /** + * @see AccessControlEditor#setPolicyTemplate(NodeId, PolicyTemplate) + */ + public void setPolicyTemplate(NodeId id, PolicyTemplate template) throws AccessControlException, ItemNotFoundException, RepositoryException { + checkProtectsNode(id); + + if (template instanceof PolicyTemplateImpl) { + PolicyTemplateImpl at = (PolicyTemplateImpl) template; + if (!id.equals(at.getNodeId())) { + throw new AccessControlException("Attempt to store PolicyTemplate to a wrong node."); + } + NodeImpl acNode = getAcNode(id); + if (acNode == null) { + throw new ItemNotFoundException("No such node " + id); + } + + /* + in order to assert that the parent (ac-controlled node) gets + modified an existing ACL node is removed first and the recreated. + this also asserts that all ACEs are cleared without having to + access and removed the explicitely + */ + NodeImpl aclNode; + if (acNode.hasNode(N_POLICY)) { + aclNode = acNode.getNode(N_POLICY); + removeSecurityItem(aclNode); + } + /* now (re) create it */ + aclNode = addSecurityNode(acNode, N_POLICY, NT_REP_ACL); + + /* add all entries defined on the template */ + PolicyEntryImpl[] aces = (PolicyEntryImpl[]) template.getEntries(); + for (int i = 0; i < aces.length; i++) { + PolicyEntryImpl ace = aces[i]; + + // create the ACE node + Name nodeName = getUniqueNodeName(aclNode, "entry"); + Name ntName = (ace.isAllow()) ? NT_REP_GRANT_ACE : NT_REP_DENY_ACE; + NodeImpl aceNode = addSecurityNode(aclNode, nodeName, ntName); + + ValueFactory vf = session.getValueFactory(); + // write the rep:principalName property + setSecurityProperty(aceNode, P_PRINCIPAL_NAME, vf.createValue(ace.getPrincipal().getName())); + // ... and the rep:privileges property + Privilege[] privs = ace.getPrivileges(); + Value[] vs = new Value[privs.length]; + for (int j = 0; j < privs.length; j++) { + vs[i] = vf.createValue(privs[j].getName()); + } + setSecurityProperty(aceNode, P_PRIVILEGES, vs); + setSecurityProperty(aceNode, P_NODE_PATH, vf.createValue(ace.getNodePath())); + setSecurityProperty(aceNode, P_GLOB, vf.createValue(ace.getGlob())); + } + } else { + // try super class + super.setPolicyTemplate(id, template); + } + } + + /** + * @see AccessControlEditor#removePolicyTemplate(NodeId) + */ + public PolicyTemplate removePolicyTemplate(NodeId id) throws AccessControlException, ItemNotFoundException, RepositoryException { + checkProtectsNode(id); + + NodeImpl acNode = getAcNode(id); + if (acNode != null) { + if (isAccessControlled(acNode)) { + // build the template in order to have a return value + PolicyTemplate tmpl = buildTemplate(acNode); + removeSecurityItem(acNode.getNode(N_POLICY)); + return tmpl; + } else { + log.debug("No policy present to remove at " + id); + return null; + } + } else { + // nodeID not below rep:accesscontrol -> delegate to ACLEditor + return super.removePolicyTemplate(id); + } + } + + // TODO: check if get/add/remove entries are properly handled by super-class + + //------------------------------------------------------------< private >--- + /** + * + * @param nodeId + * @return + * @throws AccessControlException + * @throws RepositoryException + */ + private NodeImpl getAcNode(NodeId nodeId) throws AccessControlException, RepositoryException { + NodeImpl n = session.getNodeById(nodeId); + Path p = session.getHierarchyManager().getPath(n.getNodeId()); + if (p.isDescendantOf(acRootPath)) { + return n; + } else { + // node outside of rep:accesscontrol tree -> not handled by this editor. + return null; + } + } + + private NodeId getAcId(Principal principal) throws RepositoryException { + Path acPath = session.getQPath(getPathToAcNode(principal)); + return session.getHierarchyManager().resolveNodePath(acPath); + } + + private NodeImpl createAcNode(Principal principal) throws RepositoryException { + String acPath = getPathToAcNode(principal); + String[] segms = Text.explode(acPath, '/', false); + NodeImpl node = (NodeImpl) session.getRootNode(); + for (int i = 0; i < segms.length; i++) { + Name nName = session.getQName(segms[i]); + if (node.hasNode(nName)) { + node = node.getNode(nName); + if (!node.isNodeType(NT_REP_ACCESS_CONTROL)) { + // should never get here. + throw new RepositoryException("Internal error: Unexpected nodetype " + node.getPrimaryNodeType().getName() + " below /rep:accessControl"); + } + } else { + node = addSecurityNode(node, nName, NT_REP_ACCESS_CONTROL); + } + } + return node; + } + + /** + * Test if the Node identified by id is itself part of ACL + * defining content. It this case setting or modifying an AC-policy is + * obviously not possible. + * + * @param id + * @throws AccessControlException If the given id identifies a Node that + * represents a ACL or ACE item. + * @throws RepositoryException + */ + private void checkProtectsNode(NodeId id) throws RepositoryException { + NodeImpl node = session.getNodeById(id); + if (node.isNodeType(NT_REP_ACL) || node.isNodeType(NT_REP_ACE)) { + throw new AccessControlException("Node " + id + " defines ACL or ACE."); + } + } + + private String getPathToAcNode(Principal principal) throws RepositoryException { + StringBuffer princPath = new StringBuffer(session.getJCRPath(acRootPath)); + if (principal instanceof ItemBasedPrincipal) { + princPath.append(((ItemBasedPrincipal) principal).getPath()); + } else { + princPath.append("/"); + princPath.append(Text.escapeIllegalJcrChars(principal.getName())); + } + return princPath.toString(); + } + + /** + * + * @param node + * @return + * @throws RepositoryException + */ + private boolean isAccessControlled(NodeImpl node) throws RepositoryException { + return node.isNodeType(NT_REP_ACCESS_CONTROL) && node.hasNode(N_POLICY); + } + + private PolicyTemplate buildTemplate(NodeImpl acNode) throws RepositoryException { + Principal principal; + String principalName = Text.unescapeIllegalJcrChars(acNode.getName()); + PrincipalManager pMgr = ((SessionImpl) acNode.getSession()).getPrincipalManager(); + if (pMgr.hasPrincipal(principalName)) { + principal = pMgr.getPrincipal(principalName); + } else { + log.warn("Principal with name " + principalName + " unknown to PrincipalManager."); + // TODO: rather throw? + principal = new PrincipalImpl(principalName); + } + return new PolicyTemplateImpl(getEntries(acNode, principal), principal, acNode.getNodeId()); + } + + private List getEntries(NodeImpl acNode, Principal principal) throws RepositoryException { + List entries = new ArrayList(); + if (acNode.isNodeType(NT_REP_ACCESS_CONTROL) && acNode.hasNode(N_POLICY)) { + NodeImpl aclNode = acNode.getNode(N_POLICY); + // loop over all entries in the aclNode for the princ-Principal + // and compare if they apply to the Node with 'nodeId' + for (NodeIterator aceNodes = aclNode.getNodes(); aceNodes.hasNext();) { + NodeImpl aceNode = (NodeImpl) aceNodes.nextNode(); + PolicyEntryImpl ace = createFromNode(aceNode, principal); + if (ace != null) { + entries.add(ace); + } + } + } + return entries; + } + + private PolicyEntryImpl createFromNode(NodeImpl node, Principal principal) throws RepositoryException { + if (!node.isNodeType(AccessControlConstants.NT_REP_ACE)) { + log.warn("Unexpected nodetype. Was not rep:ACE."); + return null; + } + + boolean allow = node.isNodeType(NT_REP_GRANT_ACE); + + Value[] pValues = node.getProperty(P_PRIVILEGES).getValues(); + String[] pNames = new String[pValues.length]; + for (int i = 0; i < pValues.length; i++) { + pNames[i] = pValues[i].getString(); + } + int privileges = PrivilegeRegistry.getBits(pNames); + + String nodePath = node.getProperty(P_NODE_PATH).getString(); + String glob = node.getProperty(P_GLOB).getString(); + + // TODO: mk sure principal and principal-name in node match + + return new PolicyEntryImpl(principal, privileges, allow, nodePath, glob); + } +} \ No newline at end of file Propchange: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/combined/CombinedEditor.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/combined/CombinedEditor.java ------------------------------------------------------------------------------ svn:keywords = author date id revision url Added: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/combined/CombinedProvider.java URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/combined/CombinedProvider.java?rev=638834&view=auto ============================================================================== --- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/combined/CombinedProvider.java (added) +++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/combined/CombinedProvider.java Wed Mar 19 06:56:13 2008 @@ -0,0 +1,352 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.jackrabbit.core.security.authorization.combined; + +import org.apache.jackrabbit.core.NodeId; +import org.apache.jackrabbit.core.NodeImpl; +import org.apache.jackrabbit.core.SessionImpl; +import org.apache.jackrabbit.core.security.SecurityConstants; +import org.apache.jackrabbit.api.security.principal.PrincipalManager; +import org.apache.jackrabbit.core.security.authorization.AbstractAccessControlProvider; +import org.apache.jackrabbit.core.security.authorization.AbstractCompiledPermissions; +import org.apache.jackrabbit.core.security.authorization.AccessControlConstants; +import org.apache.jackrabbit.core.security.authorization.AccessControlEditor; +import org.apache.jackrabbit.core.security.authorization.AccessControlProvider; +import org.apache.jackrabbit.core.security.authorization.CompiledPermissions; +import org.apache.jackrabbit.core.security.authorization.PrivilegeRegistry; +import org.apache.jackrabbit.core.security.authorization.GlobPattern; +import org.apache.jackrabbit.core.security.authorization.acl.ACLEditor; +import org.apache.jackrabbit.core.security.jsr283.security.AccessControlEntry; +import org.apache.jackrabbit.core.security.principal.PrincipalImpl; +import org.apache.jackrabbit.spi.Path; +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.RepositoryException; +import javax.jcr.Session; +import javax.jcr.Node; +import javax.jcr.observation.Event; +import javax.jcr.observation.ObservationManager; +import javax.jcr.observation.EventListener; +import javax.jcr.observation.EventIterator; +import java.security.Principal; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import java.util.HashSet; + +/** + * CombinedProvider... + */ +public class CombinedProvider extends AbstractAccessControlProvider implements AccessControlConstants { + + private static Logger log = LoggerFactory.getLogger(CombinedProvider.class); + + // TODO: add means to show effective-policy to a user. + // TODO: TOBEFIXED add means to create user-based ACLs (currently editor is not exposed in the API) + // TODO: TOBEFIXED proper evaluation of permissions respecting resource-based ACLs. + // TODO: TOBEFIXED assert proper evaluation order of group/non-group principal-ACLs + + private SessionImpl session; + private ObservationManager obsMgr; + + private CombinedEditor editor; + private NodeImpl acRoot; + + protected CombinedProvider() { + super("Combined AC policy", "Policy evaluating user-based and resource-based ACLs."); + } + //----------------------------------------------< AccessControlProvider >--- + /** + * @see AccessControlProvider#init(javax.jcr.Session, java.util.Map) + */ + public void init(Session systemSession, Map options) throws RepositoryException { + if (initialized) { + throw new IllegalStateException("already initialized"); + } + if (!(systemSession instanceof SessionImpl)) { + throw new RepositoryException("SessionImpl (system session) expected."); + } + session = (SessionImpl) systemSession; + obsMgr = session.getWorkspace().getObservationManager(); + + String rootPath = acRoot.getPath(); + editor = new CombinedEditor(session, session.getNamePathResolver(), + session.getQPath(rootPath)); + try { + log.info("Install initial ACL:..."); + + PrincipalManager pMgr = session.getPrincipalManager(); + log.info("... Privilege.ALL for administrators."); + Principal administrators; + String pName = SecurityConstants.ADMINISTRATORS_NAME; + if (pMgr.hasPrincipal(pName)) { + administrators = pMgr.getPrincipal(pName); + } else { + log.warn("Administrators principal group is missing."); + administrators = new PrincipalImpl(pName); + } + + String glob = GlobPattern.WILDCARD_ALL; + PolicyTemplateImpl pt = editor.editPolicyTemplate(administrators); + pt.setEntry(new PolicyEntryImpl(administrators, PrivilegeRegistry.ALL, true, rootPath, glob)); + editor.setPolicyTemplate(pt.getNodeId(), pt); + + Principal everyone = pMgr.getEveryone(); + // TODO: to be improved. how to define where everyone has read-access + log.info("... Privilege.READ for everyone."); + pt = editor.editPolicyTemplate(everyone); + pt.setEntry(new PolicyEntryImpl(everyone, PrivilegeRegistry.READ, true, rootPath, glob)); + editor.setPolicyTemplate(pt.getNodeId(), pt); + + session.save(); + log.info("... done."); + + } catch (RepositoryException e) { + log.error("Failed to set-up minimal access control for root node of workspace " + session.getWorkspace().getName()); + session.getRootNode().refresh(false); + throw e; + } + + + NodeImpl root = (NodeImpl) session.getRootNode(); + if (root.hasNode(N_ACCESSCONTROL)) { + // TODO: make sure its a node with the correct nodetype + acRoot = root.getNode(N_ACCESSCONTROL); + if (!acRoot.isNodeType(NT_REP_ACCESS_CONTROL)) { + throw new RepositoryException("Error while initializing Access Control Provider: Found ac-root to be wrong node type " + acRoot.getPrimaryNodeType().getName()); + } + } else { + acRoot = root.addNode(N_ACCESSCONTROL, NT_REP_ACCESS_CONTROL, null); + } + initialized = true; + } + + /** + * @see AccessControlProvider#getAccessControlEntries(org.apache.jackrabbit.core.NodeId) + */ + public AccessControlEntry[] getAccessControlEntries(NodeId nodeId) throws RepositoryException { + checkInitialized(); + // TODO: TOBEFIXED + return new AccessControlEntry[0]; + } + + /** + * @see AccessControlProvider#getEditor(javax.jcr.Session) + */ + public AccessControlEditor getEditor(Session editingSession) { + checkInitialized(); + if (editingSession instanceof SessionImpl) { + try { + return new CombinedEditor((SessionImpl) editingSession, + session.getNamePathResolver(), + session.getQPath(acRoot.getPath())); + } catch (RepositoryException e) { + // should never get here + log.error("Internal error:", e.getMessage()); + } + } + + log.debug("Unable to createFromNode " + CombinedEditor.class.getName() + "."); + return null; + } + + /** + * @see AccessControlProvider#compilePermissions(Set) + */ + public CompiledPermissions compilePermissions(Set principals) throws ItemNotFoundException, RepositoryException { + checkInitialized(); + if (isAdminOrSystem(principals)) { + return getAdminPermissions(); + } else { + // TODO: include the resource-based ACLs! + return new CompiledPermissionImpl(principals); + } + } + + //----------------------------------------< private | package protected >--- + /** + * Test if the given path points to a Node (or an existing or non existing + * direct decendant of an existing Node) that stores AC-information + * + * @param path + * @return + * @throws RepositoryException + */ + private boolean isAccessControlItem(Path path) throws ItemNotFoundException, RepositoryException { + NodeImpl node; + String absPath = session.getJCRPath(path); + if (session.nodeExists(absPath)) { + node = (NodeImpl) session.getNode(absPath); + } else { + // path points to existing prop or non-existing item (node or prop). + String parentPath = Text.getRelativeParent(absPath, 1); + if (session.nodeExists(parentPath)) { + node = (NodeImpl) session.getNode(parentPath); + } else { + throw new ItemNotFoundException("No item exists at " + absPath + " nor at its direct ancestor."); + } + } + return node.isNodeType(ACLEditor.NT_REP_ACL) || node.isNodeType(ACLEditor.NT_REP_ACE); + } + + /** + * + * @param principals + * @return + * @throws RepositoryException + */ + private ACLImpl getACL(Set principals) throws RepositoryException { + // acNodes must be ordered in the same order as the principals + // in order to obtain proper acl-evalution in case the given + // principal-set is ordered. + Map princToACEs = new ListOrderedMap(); + Set acPaths = new HashSet(); + // build acl-hierarchy assuming that principal-order determines the + // acl-inheritance. + for (Iterator it = principals.iterator(); it.hasNext();) { + Principal princ = (Principal) it.next(); + PolicyTemplateImpl at = editor.getPolicyTemplate(princ); + if (at == null) { + log.debug("No matching ACL node found for principal " + princ.getName() + " -> principal ignored."); + } else { + // retrieve the ACEs from the node + PolicyEntryImpl[] aces = (PolicyEntryImpl[]) at.getEntries(); + princToACEs.put(princ, aces); + + Path p = session.getHierarchyManager().getPath(at.getNodeId()); + acPaths.add(session.getJCRPath(p)); + } + } + return new ACLImpl(princToACEs, acPaths); + } + + //-----------------------------------------------------< CompiledPolicy >--- + /** + * + */ + private class CompiledPermissionImpl extends AbstractCompiledPermissions + implements EventListener { + + private final Set principals; + private ACLImpl acl; + + /** + * @param principals + * @throws RepositoryException + */ + private CompiledPermissionImpl(Set principals) throws RepositoryException { + + this.principals = principals; + acl = getACL(principals); + + // TODO: describe + // TODO: rather on CombinedProvider? -> but must keep references to the CompiledPermission then....? + int events = Event.PROPERTY_CHANGED | Event.PROPERTY_ADDED | + Event.PROPERTY_REMOVED | Event.NODE_ADDED | Event.NODE_REMOVED; + String[] ntNames = new String[] { + session.getJCRName(NT_REP_ACE) + }; + obsMgr.addEventListener(this, events, acRoot.getPath(), true, null, ntNames, true); + } + + //------------------------------------< AbstractCompiledPermissions >--- + /** + * @see AbstractCompiledPermissions#buildResult(Path) + */ + protected Result buildResult(Path absPath) throws RepositoryException { + if (!absPath.isAbsolute()) { + throw new RepositoryException("Absolute path expected."); + } + + String jcrPath = session.getJCRPath(absPath); + boolean isAclItem = isAccessControlItem(absPath); + + int permissions; + if (session.itemExists(jcrPath)) { + permissions = acl.getPermissions(session.getItem(jcrPath), isAclItem); + } else { + Node parent = session.getNode(Text.getRelativeParent(jcrPath, 1)); + String name = session.getJCRName(absPath.getNameElement().getName()); + permissions = acl.getPermissions(parent, name, isAclItem); + } + /* privileges can only be determined for existing nodes. + not for properties and neither for non-existing nodes. */ + int privileges = (session.nodeExists(jcrPath)) ? acl.getPrivileges(jcrPath) : PrivilegeRegistry.NO_PRIVILEGE; + return new Result(permissions, privileges); + } + + //--------------------------------------------< CompiledPermissions >--- + /** + * @see CompiledPermissions#close() + */ + public void close() { + try { + obsMgr.removeEventListener(this); + } catch (RepositoryException e) { + log.error("Internal error: ", e.getMessage()); + } + super.close(); + } + + //--------------------------------------------------< EventListener >--- + /** + * @see EventListener#onEvent(EventIterator) + */ + public void onEvent(EventIterator events) { + Set acPaths = acl.getAcPaths(); + try { + boolean reload = false; + while (events.hasNext() && !reload) { + Event ev = events.nextEvent(); + String path = ev.getPath(); + // only invalidate cache if any of the events affects the + // nodes defining permissions for the principals. + switch (ev.getType()) { + case Event.NODE_ADDED: + case Event.NODE_REMOVED: + reload = acPaths.contains(Text.getRelativeParent(path, 2)); + break; + case Event.PROPERTY_ADDED: + case Event.PROPERTY_CHANGED: + case Event.PROPERTY_REMOVED: + reload = acPaths.contains(Text.getRelativeParent(path, 3)); + break; + default: + // illegal event-type: should never occur. ignore + reload = false; + break; + } + + } + + // eventually reload the ACL and clear the cache + if (reload) { + // reload the acl + acl = getACL(principals); + clearCache(); + } + } catch (RepositoryException e) { + // should never get here + log.warn("Internal error: ", e.getMessage()); + } + } + } +} \ No newline at end of file Propchange: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/combined/CombinedProvider.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/combined/CombinedProvider.java ------------------------------------------------------------------------------ svn:keywords = author date id revision url Added: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/combined/PolicyEntryImpl.java URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/combined/PolicyEntryImpl.java?rev=638834&view=auto ============================================================================== --- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/combined/PolicyEntryImpl.java (added) +++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/combined/PolicyEntryImpl.java Wed Mar 19 06:56:13 2008 @@ -0,0 +1,172 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.jackrabbit.core.security.authorization.combined; + +import org.apache.jackrabbit.core.security.authorization.PolicyEntry; +import org.apache.jackrabbit.core.security.authorization.PrivilegeRegistry; +import org.apache.jackrabbit.core.security.authorization.GlobPattern; +import org.apache.jackrabbit.core.security.jsr283.security.Privilege; +import org.apache.jackrabbit.core.security.jsr283.security.AccessControlEntry; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.jcr.RepositoryException; +import javax.jcr.Item; +import java.security.Principal; + +/** + * PolicyEntryImpl... + */ +class PolicyEntryImpl implements PolicyEntry { + + private static Logger log = LoggerFactory.getLogger(PolicyEntryImpl.class); + + /** + * Privileges defined for this entry. + */ + private final int privileges; + + /** + * If the actions contained are allowed or denied + */ + private final boolean allow; + + /** + * The Principal of this entry + */ + private final Principal principal; + + private final String nodePath; + private final String glob; + + /** + * Globbing pattern + */ + private final GlobPattern pattern; + + /** + * Hash code being calculated on demand. + */ + private int hashCode = -1; + + /** + * Constructs an new entry. + * + * @param principal + * @param privileges + * @param allow + */ + PolicyEntryImpl(Principal principal, int privileges, boolean allow, + String nodePath, String glob) { + if (principal == null || nodePath == null) { + throw new IllegalArgumentException("Neither principal nor nodePath must be null."); + } + this.principal = principal; + this.privileges = privileges; + this.allow = allow; + this.nodePath = nodePath; + this.glob = (glob == null) ? GlobPattern.WILDCARD_ALL : glob; + + pattern = GlobPattern.create(nodePath + "/" +glob); + } + + int getPrivilegeBits() { + return privileges; + } + + String getNodePath() { + return nodePath; + } + + String getGlob() { + return glob; + } + + boolean matches(String jcrPath) throws RepositoryException { + return pattern.matches(jcrPath); + } + + boolean matches(Item item) throws RepositoryException { + return pattern.matches(item); + } + + //-------------------------------------------------< AccessControlEntry >--- + /** + * @see AccessControlEntry#getPrincipal() + */ + public Principal getPrincipal() { + return principal; + } + + /** + * @see AccessControlEntry#getPrivileges() + */ + public Privilege[] getPrivileges() { + return PrivilegeRegistry.getPrivileges(privileges); + } + + //--------------------------------------------------------< PolicyEntry >--- + /** + * @return true if all actions contained in this Entry are allowed + * @see PolicyEntry#isAllow() + */ + public boolean isAllow() { + return allow; + } + + //-------------------------------------------------------------< Object >--- + /** + * @see Object#hashCode() + */ + public int hashCode() { + if (hashCode == -1) { + int h = 17; + h = 37 * h + principal.getName().hashCode(); + h = 37 * h + privileges; + h = 37 * h + Boolean.valueOf(allow).hashCode(); + h = 37 * h + nodePath.hashCode(); + h = 37 * h + glob.hashCode(); + hashCode = h; + } + return hashCode; + } + + /** + * Returns true if the principal, the allow-flag, all privileges and + * the nodepath and the glob string are equal or the same, respectively. + * + * @param obj + * @return + * @see Object#equals(Object) + */ + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + + if (obj instanceof PolicyEntryImpl) { + PolicyEntryImpl tmpl = (PolicyEntryImpl) obj; + // TODO: check again if comparing principal-name is sufficient + return principal.getName().equals(tmpl.principal.getName()) && + allow == tmpl.allow && + privileges == tmpl.privileges && + glob.equals(tmpl.glob); + } + return false; + } + +} \ No newline at end of file Propchange: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/combined/PolicyEntryImpl.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/combined/PolicyEntryImpl.java ------------------------------------------------------------------------------ svn:keywords = author date id revision url Added: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/combined/PolicyTemplateImpl.java URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/combined/PolicyTemplateImpl.java?rev=638834&view=auto ============================================================================== --- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/combined/PolicyTemplateImpl.java (added) +++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/combined/PolicyTemplateImpl.java Wed Mar 19 06:56:13 2008 @@ -0,0 +1,133 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.jackrabbit.core.security.authorization.combined; + +import org.apache.jackrabbit.core.NodeId; +import org.apache.jackrabbit.core.security.authorization.PolicyEntry; +import org.apache.jackrabbit.core.security.authorization.PolicyTemplate; +import org.apache.jackrabbit.core.security.jsr283.security.AccessControlException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.jcr.RepositoryException; +import java.security.Principal; +import java.util.ArrayList; +import java.util.List; +import java.util.Iterator; + +/** + * PolicyTemplateImpl... + */ +class PolicyTemplateImpl implements PolicyTemplate { + + private static Logger log = LoggerFactory.getLogger(PolicyTemplateImpl.class); + + private final Principal principal; + private final NodeId acNodeId; + private final List entries = new ArrayList(); + + + PolicyTemplateImpl(List aceTemplates, Principal principal, NodeId acNodeId) { + this.principal = principal; + this.entries.addAll(aceTemplates); + this.acNodeId = acNodeId; + } + + NodeId getNodeId() { + return acNodeId; + } + + Principal getPrincipal() { + return principal; + } + + //-----------------------------------------------------< PolicyTemplate >--- + + public boolean isEmpty() { + return entries.isEmpty(); + } + + public int size() { + return entries.size(); + } + + public PolicyEntry[] getEntries() { + return (PolicyEntry[]) entries.toArray(new PolicyEntry[entries.size()]); + } + + public boolean setEntry(PolicyEntry entry) throws AccessControlException, RepositoryException { + if (entry instanceof PolicyEntryImpl && + principal.equals(entry.getPrincipal())) { + return internalAddEntry((PolicyEntryImpl) entry); + } else { + throw new AccessControlException("Invalid entry."); + } + } + + public boolean removeEntry(PolicyEntry entry) throws AccessControlException, RepositoryException { + return entries.remove(entry); + } + + //------------------------------------------------< AccessControlPolicy >--- + /** + * @see org.apache.jackrabbit.core.security.jsr283.security.AccessControlPolicy#getName() + */ + public String getName() throws RepositoryException { + return getClass().getName(); + } + + /** + * @see org.apache.jackrabbit.core.security.jsr283.security.AccessControlPolicy#getName() + */ + public String getDescription() throws RepositoryException { + return "Template for the user-based ACL: each ACL defining the access permissions for a single principal."; + } + + //-------------------------------------------------------------------------- + /** + * + * @param entry + * @return + */ + private synchronized boolean internalAddEntry(PolicyEntryImpl entry) { + if (entries.contains(entry)) { + log.debug("Entry is already contained in policy -> no modification."); + return false; + } + + PolicyEntryImpl existing = null; + for (Iterator it = entries.iterator(); it.hasNext() && existing == null;) { + PolicyEntryImpl ex = (PolicyEntryImpl) it.next(); + if (ex.getNodePath().equals(entry.getNodePath()) && + ex.getGlob().equals(entry.getGlob()) && + ex.isAllow() == entry.isAllow()) { + log.debug("Replacing existing policy entry: NodePath = " + + entry.getNodePath() +"; Glob = " + + entry.getGlob() + "; Changing privileges from " + + ex.getPrivilegeBits() + " to " + entry.getPrivilegeBits()); + existing = ex; + } + } + + if (existing != null) { + int index = entries.indexOf(existing); + return entries.set(index, entry) != null; + } else { + return entries.add(entry); + } + } +} \ No newline at end of file Propchange: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/combined/PolicyTemplateImpl.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/combined/PolicyTemplateImpl.java ------------------------------------------------------------------------------ svn:keywords = author date id revision url Added: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/jsr283/security/AccessControlEntry.java URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/jsr283/security/AccessControlEntry.java?rev=638834&view=auto ============================================================================== --- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/jsr283/security/AccessControlEntry.java (added) +++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/jsr283/security/AccessControlEntry.java Wed Mar 19 06:56:13 2008 @@ -0,0 +1,23 @@ +package org.apache.jackrabbit.core.security.jsr283.security; + +import java.security.Principal; + +/** + * An AccessControlEntry represents the association of one or more + * Privilege objects with a specific Principal. + * + * @since JCR 2.0 + */ +public interface AccessControlEntry { + /** + * Returns the principal associated with this access control entry. + * @return a Principal. + */ + public Principal getPrincipal(); + + /** + * Returns the privileges associated with this access control entry. + * @return an array of Privileges. + */ + public Privilege[] getPrivileges(); +} Propchange: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/jsr283/security/AccessControlEntry.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/jsr283/security/AccessControlEntry.java ------------------------------------------------------------------------------ svn:keywords = author date id revision url Added: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/jsr283/security/AccessControlException.java URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/jsr283/security/AccessControlException.java?rev=638834&view=auto ============================================================================== --- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/jsr283/security/AccessControlException.java (added) +++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/jsr283/security/AccessControlException.java Wed Mar 19 06:56:13 2008 @@ -0,0 +1,52 @@ +package org.apache.jackrabbit.core.security.jsr283.security; + +import javax.jcr.RepositoryException; + +/** + * Exception thrown by access control related methods of + * AccessControlManager. + * + * @since JCR 2.0 + */ +public class AccessControlException extends RepositoryException { + + /** + * Constructs a new instance of this class with null as its + * detail message. + */ + public AccessControlException() { + super(); + } + + /** + * Constructs a new instance of this class with the specified detail + * message. + * + * @param message the detail message. The detail message is saved for + * later retrieval by the {@link #getMessage()} method. + */ + public AccessControlException(String message) { + super(message); + } + + /** + * Constructs a new instance of this class with the specified detail + * message and root cause. + * + * @param message the detail message. The detail message is saved for + * later retrieval by the {@link #getMessage()} method. + * @param rootCause root failure cause + */ + public AccessControlException(String message, Throwable rootCause) { + super(message, rootCause); + } + + /** + * Constructs a new instance of this class with the specified root cause. + * + * @param rootCause root failure cause + */ + public AccessControlException(Throwable rootCause) { + super(rootCause); + } +} Propchange: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/jsr283/security/AccessControlException.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/jsr283/security/AccessControlException.java ------------------------------------------------------------------------------ svn:keywords = author date id revision url Added: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/jsr283/security/AccessControlManager.java URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/jsr283/security/AccessControlManager.java?rev=638834&view=auto ============================================================================== --- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/jsr283/security/AccessControlManager.java (added) +++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/jsr283/security/AccessControlManager.java Wed Mar 19 06:56:13 2008 @@ -0,0 +1,330 @@ +package org.apache.jackrabbit.core.security.jsr283.security; + +import javax.jcr.PathNotFoundException; +import javax.jcr.RepositoryException; +import javax.jcr.AccessDeniedException; +import javax.jcr.UnsupportedRepositoryOperationException; +import java.security.Principal; + +/** + * The AccessControlManager object is accessed via + * {@link javax.jcr.Session#getAccessControlManager()}. It provides methods for: + *
    + *
  • Access control discovery
  • + *
  • Assigning access control policies
  • + *
  • Assigning access control entries
  • + *
+ * + * @since JCR 2.0 + */ +public interface AccessControlManager { + + /** + * Returns the privileges supported for absolute path absPath, + * which must be an existing node. + *

+ * This method does not return the privileges held by the session. Instead, + * it returns the privileges that the repository supports. + * + * @param absPath an absolute path. + * @return an array of Privileges. + * @throws PathNotFoundException if no node at absPath exists + * or the session does not have privilege to + * retrieve the node. + * @throws RepositoryException if another error occurs. + */ + public Privilege[] getSupportedPrivileges(String absPath) + throws PathNotFoundException, RepositoryException; + + /** + * Returns whether the session has the specified privileges for absolute + * path absPath, which must be an existing node. + *

+ * Testing an aggregate privilege is equivalent to testing each nonaggregate + * privilege among the set returned by calling + * Privilege.getAggregatePrivileges() for that privilege. + * + * @param absPath an absolute path. + * @param privileges an array of Privileges. + * @return true if the session has the specified privileges; + * false otherwise. + * @throws PathNotFoundException if no node at absPath exists + * or the session does not have privilege to + * retrieve the node. + * @throws RepositoryException if another error occurs. + */ + public boolean hasPrivileges(String absPath, Privilege[] privileges) + throws PathNotFoundException, RepositoryException; + + /** + * Returns the privileges the session has for absolute path absPath, which + * must be an existing node. + *

+ * The returned privileges are those for which {@link #hasPrivileges} would + * return true. + * + * @param absPath an absolute path. + * @return an array of Privileges. + * @throws PathNotFoundException if no node at absPath exists + * or the session does not have privilege to + * retrieve the node. + * @throws RepositoryException if another error occurs. + */ + public Privilege[] getPrivileges(String absPath) + throws PathNotFoundException, RepositoryException; + + /** + * Returns the AccessControlPolicy that has been set to + * the node at absPath or null if no + * policy has been set. This method reflects the binding state including + * transient policy modifications. + *

+ * Use {@link #getEffectivePolicy(String)} in order to determine the + * policy that effectively applies at absPath. + * + * @param absPath an absolute path. + * @return an AccessControlPolicy object or null. + * @throws PathNotFoundException if no node at absPath exists + * or the session does not have privilege to + * retrieve the node. + * @throws AccessDeniedException if the session lacks + * GET_ACCESS_CONTROL privilege + * for the absPath node. + * @throws RepositoryException if another error occurs. + */ + public AccessControlPolicy getPolicy(String absPath) + throws PathNotFoundException, AccessDeniedException, RepositoryException; + + /** + * Returns the AccessControlPolicy that currently is in effect + * at the node at absPath. This may be an + * AccessControlPolicy set through this API or some + * implementation specific (default) policy. + *

+ * + * @param absPath an absolute path. + * @return an AccessControlPolicy object. + * @throws PathNotFoundException if no node at absPath exists + * or the session does not have privilege to + * retrieve the node. + * @throws AccessDeniedException if the session lacks + * READ_ACCESS_CONTROL privilege + * for the absPath node. + * @throws RepositoryException if another error occurs. + */ + public AccessControlPolicy getEffectivePolicy(String absPath) + throws PathNotFoundException, AccessDeniedException, RepositoryException; + + /** + * Returns the access control policies that are capable of being applied to + * the node at absPath. + * + * @param absPath an absolute path. + * @return an AccessControlPolicyIterator over the applicable + * access control policies or an empty iterator if no policies are + * applicable. + * @throws PathNotFoundException if no node at absPath exists + * or the session does not have privilege to + * retrieve the node. + * @throws AccessDeniedException if the session lacks + * READ_ACCESS_CONTROL privilege + * for the absPath node. + * @throws RepositoryException if another error occurs. + */ + public AccessControlPolicyIterator getApplicablePolicies(String absPath) + throws PathNotFoundException, AccessDeniedException, RepositoryException; + + /** + * Binds the policy to the node at absPath. + *

+ * Only one policy may be bound at a time. If more than one policy per node + * is required, the implementation should provide an appropriate aggregate + * policy among those returned by getApplicablePolicies(absPath). + * The access control policy does not take effect until a save + * is performed. + *

+ * If the node has access control entries that were bound to it through the + * JCR API prior to the setPolicy call, then these entries may + * be deleted. Any implementation-specific (non-JCR) access control + * settings may be changed in response to a successful call to + * setPolicy. + * + * @param absPath an absolute path. + * @param policy the AccessControlPolicy to be applied. + * @throws PathNotFoundException if no node at absPath exists + * or the session does not have privilege to + * retrieve the node. + * @throws AccessControlException if the policy is not applicable. + * @throws AccessDeniedException if the session lacks + * MODIFY_ACCESS_CONTROL + * privilege for the absPath node. + * @throws RepositoryException if another error occurs. + */ + public void setPolicy(String absPath, AccessControlPolicy policy) + throws PathNotFoundException, AccessControlException, + AccessDeniedException, RepositoryException; + + /** + * Removes the AccessControlPolicy from the node at absPath and + * returns it. + *

+ * + * An AccessControlPolicy can only be removed if it was + * bound to the specified node through this API before. The effect of the + * removal only takes place upon Session.save(). Whichever + * defaults the implementation applies now take effect. + * Note, that an implementation default or any other effective + * AccessControlPolicy that has not been applied to the node + * before may never be removed using this method. + * + * @param absPath an absolute path. + * @return the removed AccessControlPolicy. + * @throws PathNotFoundException if no node at absPath exists + * or the session does not have privilege to + * retrieve the node. + * @throws AccessControlException if no policy exists. + * @throws AccessDeniedException if the session lacks + * MODIFY_ACCESS_CONTROL + * privilege for the absPath node. + * @throws RepositoryException if another error occurs. + */ + public AccessControlPolicy removePolicy(String absPath) + throws PathNotFoundException, AccessControlException, + AccessDeniedException, RepositoryException; + + /** + * Returns all access control entries assigned to the node at absPath + * including transient modifications made to the entries at absPath. + *

+ * This method is only guaranteed to return an AccessControlEntry + * if that AccessControlEntry has been assigned through this API. + * + * @param absPath an absolute path + * @return all access control entries assigned at to specified node. + * @throws PathNotFoundException if no node at absPath exists + * or the session does not have privilege to + * retrieve the node. + * @throws AccessDeniedException if the session lacks + * READ_ACCESS_CONTROL privilege + * for the absPath node. + * @throws UnsupportedRepositoryOperationException if access control + * is not supported. + * @throws RepositoryException if another error occurs. + */ + public AccessControlEntry[] getAccessControlEntries(String absPath) + throws PathNotFoundException, AccessDeniedException, UnsupportedRepositoryOperationException, RepositoryException; + + /** + * Returns the access control entries that are effective at the node at + * absPath. + *

+ * This method is intended for information purpose only and should allow + * the user to determine which entries are currently used for access control + * evaluation. + *

+ * If an implementation is not able to determine the effective entries + * present at the given node it returns null in order to indicate + * that entries exists but the implementation cannot find them. If there + * are no entries present at the given node an empty array will be returned. + * + * @param absPath an absolute path + * @return the access control entries that are currently effective at the + * node at absPath or null if the + * implementation is not able to determine the effective entries. + * @throws PathNotFoundException if no node at absPath exists + * or the session does not have privilege to retrieve the node. + * @throws AccessDeniedException if the session lacks + * READ_ACCESS_CONTROL privilege for the + * absPath node. + * @throws UnsupportedRepositoryOperationException if access control + * is not supported. + * @throws RepositoryException if another error occurs. + */ + public AccessControlEntry[] getEffectiveAccessControlEntries(String absPath) + throws PathNotFoundException, AccessDeniedException, UnsupportedRepositoryOperationException, RepositoryException; + + + /** + * Adds the access control entry consisting of the specified + * principal and the specified privileges to the + * node at absPath. + *

+ * This method returns the AccessControlEntry object constructed from the + * specified principal and contains at least the given privileges. + * An implementation may return a resulting ACE that combines the given privileges + * with those added by a previous call to addAccessControlEntry for the same + * Principal. However, a call to addAccessControlEntry for a given + * Principal can never remove a Privilege added by a previous call + * to addAccessControlEntry. + *

+ * The access control entry does not take effect until a save + * is performed. + *

+ * This method is guaranteed to affect only the privileges of the specified + * principal. + *

+ * This method may affect the privileges granted to that principal with + * respect to nodes other than that specified. However, if it does, it is + * guaranteed to only affect the privileges of those other nodes in the + * same way as it affects the privileges of the specified node. + * + * @param absPath an absolute path. + * @param principal a Principal. + * @param privileges an array of Privileges. + * @return the AccessControlEntry object constructed from the + * specified principal and privileges. + * @throws PathNotFoundException if no node at absPath exists + * or the session does not have privilege to retrieve the node. + * @throws AccessControlException if the specified principal does not exist, + * if any of the specified privileges is not supported at + * absPath or if some other access control related + * exception occurs. + * @throws AccessDeniedException if the session lacks + * MODIFY_ACCESS_CONTROL privilege for the + * absPath node. + * @throws UnsupportedRepositoryOperationException if access control + * is not supported. + * @throws RepositoryException if another error occurs. + */ + public AccessControlEntry addAccessControlEntry(String absPath, + Principal principal, + Privilege[] privileges) + throws PathNotFoundException, AccessControlException, + AccessDeniedException, UnsupportedRepositoryOperationException, + RepositoryException; + + /** + * Removes the specified AccessControlEntry from the node at + * absPath. + *

+ * This method is guaranteed to affect only the privileges of the principal + * defined within the passed AccessControlEntry. + *

+ * This method may affect the privileges granted to that principal + * with respect to nodes other than that specified. However, if it does, + * it is guaranteed to only affect the privileges of those other nodes in + * the same way as it affects the privileges of the specified node. + *

+ * Only exactly those entries obtained through + * getAccessControlEntries can be removed. The effect of the + * removal only takes effect upon Session.save(). + * + * @param absPath an absolute path. + * @param ace the access control entry to be removed. + * @throws PathNotFoundException if no node at absPath exists + * or the session does not have privilege to retrieve the node. + * @throws AccessControlException + * if the specified entry is not + * present on the specified node. + * @throws AccessDeniedException if the session lacks + * MODIFY_ACCESS_CONTROL privilege for the + * absPath node. + * @throws UnsupportedRepositoryOperationException if access control + * is not supported. + * @throws RepositoryException if another error occurs. + */ + public void removeAccessControlEntry(String absPath, AccessControlEntry ace) + throws PathNotFoundException, AccessControlException, + AccessDeniedException, UnsupportedRepositoryOperationException, + RepositoryException; +} Propchange: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/jsr283/security/AccessControlManager.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/jsr283/security/AccessControlManager.java ------------------------------------------------------------------------------ svn:keywords = author date id revision url Added: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/jsr283/security/AccessControlPolicy.java URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/jsr283/security/AccessControlPolicy.java?rev=638834&view=auto ============================================================================== --- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/jsr283/security/AccessControlPolicy.java (added) +++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/jsr283/security/AccessControlPolicy.java Wed Mar 19 06:56:13 2008 @@ -0,0 +1,34 @@ +package org.apache.jackrabbit.core.security.jsr283.security; + +import javax.jcr.RepositoryException; + +/** + * An AccessControlPolicy is an object with a name and an optional + * description. Examples of possible AccessControlPolicy + * implementations include access control lists or role-responsibility + * assignments. + * + * @since JCR 2.0 + */ +public interface AccessControlPolicy { + /** + * Returns the name of the access control policy, which should be unique + * among the choices applicable to any particular node. + * It is presented to provide an easily identifiable choice for + * users choosing a policy to assign to a node. + * + * @return the name of the access control policy. + * @throws RepositoryException if an error occurs. + */ + public String getName() throws RepositoryException; + + /** + * Returns a human readable description of the access control policy which + * should be sufficient for allowing end users to chose between different + * policies to apply to a node. + * + * @return a human readable description of the access control policy. + * @throws RepositoryException if an error occurs. + */ + public String getDescription() throws RepositoryException; +} Propchange: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/jsr283/security/AccessControlPolicy.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/jsr283/security/AccessControlPolicy.java ------------------------------------------------------------------------------ svn:keywords = author date id revision url Added: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/jsr283/security/AccessControlPolicyIterator.java URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/jsr283/security/AccessControlPolicyIterator.java?rev=638834&view=auto ============================================================================== --- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/jsr283/security/AccessControlPolicyIterator.java (added) +++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/jsr283/security/AccessControlPolicyIterator.java Wed Mar 19 06:56:13 2008 @@ -0,0 +1,23 @@ +package org.apache.jackrabbit.core.security.jsr283.security; + +import javax.jcr.RangeIterator; + +/** + * Allows easy iteration through a list of AccessControlPolicys + * with nextAccessControlPolicy as well as a skip + * method inherited from RangeIterator. + * + * @since JCR 2.0 + */ +public interface AccessControlPolicyIterator extends RangeIterator { + + /** + * Returns the next AccessControlPolicy in the iteration. + * + * @return the next AccessControlPolicy in the iteration. + * @throws java.util.NoSuchElementException if iteration has no more + * AccessControlPolicys. + */ + public AccessControlPolicy nextAccessControlPolicy(); + +} Propchange: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/jsr283/security/AccessControlPolicyIterator.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/jsr283/security/AccessControlPolicyIterator.java ------------------------------------------------------------------------------ svn:keywords = author date id revision url Added: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/jsr283/security/Privilege.java URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/jsr283/security/Privilege.java?rev=638834&view=auto ============================================================================== --- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/jsr283/security/Privilege.java (added) +++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/jsr283/security/Privilege.java Wed Mar 19 06:56:13 2008 @@ -0,0 +1,131 @@ +package org.apache.jackrabbit.core.security.jsr283.security; + +/** + * A privilege represents the capability of performing a particular set + * of operations on items in the JCR repository. Each privilege is identified + * by a NAME that is unique across the set of privileges supported by a + * repository. JCR defines a set of standard privileges in the jcr + * namespace. Implementations may add additional privileges in namespaces other + * than jcr. + *

+ * A privilege may be an aggregate privilege. Aggregate privileges are sets of + * other privileges. Granting, denying, or testing an aggregate privilege is + * equivalent to individually granting, denying, or testing each privilege it + * contains. The privileges contained by an aggregate privilege may themselves + * be aggregate privileges if the resulting privilege graph is acyclic. + *

+ * A privilege may be an abstract privilege. Abstract privileges cannot + * themselves be granted or denied, but can be composed into aggregate privileges + * which are granted or denied. + *

+ * A privilege can be both aggregate and abstract. + * + * @since JCR 2.0 + */ +public interface Privilege { + + /** + * A constant representing READ, the privilege to retrieve + * a node and get its properties and their values. + */ + public static final String READ = "javax.jcr.security.Privilege.READ"; + + /** + * A constant representing MODIFY_PROPERTIES, the privilege + * to create, modify and remove the properties of a node. + */ + public static final String MODIFY_PROPERTIES = "javax.jcr.security.Privilege.MODIFY_PROPERTIES"; + + /** + * A constant representing ADD_CHILD_NODES, the privilege + * to create child nodes of a node. + */ + public static final String ADD_CHILD_NODES = "javax.jcr.security.Privilege.ADD_CHILD_NODES"; + + /** + * A constant representing REMOVE_CHILD_NODES, the privilege + * to remove child nodes of a node. + */ + public static final String REMOVE_CHILD_NODES = "javax.jcr.security.Privilege.REMOVE_CHILD_NODES"; + + /** + * A constant representing WRITE, an aggregate privilege that contains: + *

    + *
  • MODIFY_PROPERTIES
  • + *
  • ADD_CHILD_NODES
  • + *
  • REMOVE_CHILD_NODES
  • + *
+ */ + public static final String WRITE = "javax.jcr.security.Privilege.WRITE"; + + /** + * A constant representing READ_ACCESS_CONTROL, the privilege + * to get the access control policy of a node. + */ + public static final String READ_ACCESS_CONTROL = "javax.jcr.security.Privilege.READ_ACCESS_CONTROL"; + + /** + * A constant representing MODIFY_ACCESS_CONTROL, the privilege + * to modify the access control policies of a node. + */ + public static final String MODIFY_ACCESS_CONTROL = "javax.jcr.security.Privilege.MODIFY_ACCESS_CONTROL"; + + /** + * A constant representing ALL, an aggregate privilege that contains + * all predefined privileges: + *
    + *
  • READ
  • + *
  • WRITE
  • + *
  • READ_ACCESS_CONTROL
  • + *
  • MODIFY_ACCESS_CONTROL
  • + *
+ * It should in addition include all implementation-defined privileges. + */ + public static final String ALL = "javax.jcr.security.Privilege.ALL"; + + /** + * Returns the name of this privilege. + * + * @return the name of this privilege. + */ + public String getName(); + + /** + * Returns a description of this privilege. + * + * @return a description of this privilege. + */ + public String getDescription(); + + /** + * Returns whether this privilege is an abstract privilege. + * @return true if this privilege is an abstract privilege; + * false otherwise. + */ + public boolean isAbstract(); + + /** + * Returns whether this privilege is an aggregate privilege. + * @return true if this privilege is an aggregate privilege; + * false otherwise. + */ + public boolean isAggregate(); + + /** + * If this privilege is an aggregate privilege, returns the privileges directly + * contained by the aggregate privilege. Otherwise returns an empty array. + * + * @return an array of Privileges + */ + public Privilege[] getDeclaredAggregatePrivileges(); + + /** + * If this privilege is an aggregate privilege, returns the privileges it + * contains, the privileges contained by any aggregate privileges among + * those, and so on (the transitive closure of privileges contained by this + * privilege). Otherwise returns an empty array. + * + * @return an array of Privileges + */ + public Privilege[] getAggregatePrivileges(); +} Propchange: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/jsr283/security/Privilege.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/jsr283/security/Privilege.java ------------------------------------------------------------------------------ svn:keywords = author date id revision url