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 [6/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/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=689499&r1=689498&r2=689499&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/combined/CombinedEditor.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/combined/CombinedEditor.java Wed Aug 27 08:12:04 2008
@@ -17,363 +17,117 @@
 package org.apache.jackrabbit.core.security.authorization.combined;
 
 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.core.NodeImpl;
-import org.apache.jackrabbit.core.SessionImpl;
-import org.apache.jackrabbit.core.security.authorization.AccessControlConstants;
+import org.apache.jackrabbit.api.jsr283.security.AccessControlPolicy;
 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.PrivilegeRegistry;
-import org.apache.jackrabbit.core.security.authorization.acl.ACLEditor;
-import org.apache.jackrabbit.core.security.principal.ItemBasedPrincipal;
-import org.apache.jackrabbit.core.security.principal.PrincipalImpl;
-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.NodeIterator;
 import javax.jcr.PathNotFoundException;
-import javax.jcr.PropertyType;
 import javax.jcr.RepositoryException;
-import javax.jcr.Value;
-import javax.jcr.ValueFactory;
 import java.security.Principal;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Arrays;
 
 /**
  * <code>CombinedEditor</code>...
  */
-class CombinedEditor extends ACLEditor {
+class CombinedEditor implements AccessControlEditor {
 
     private static Logger log = LoggerFactory.getLogger(CombinedEditor.class);
 
-    private final NamePathResolver systemResolver;
-    private final String acRootPath;
+    private final AccessControlEditor[] editors;
 
-    CombinedEditor(SessionImpl session, NamePathResolver systemResolver,
-                   Path acRootPath) throws RepositoryException {
-        super(session);
-        this.systemResolver = systemResolver;
-        this.acRootPath = session.getJCRPath(acRootPath);
-    }
-
-    PolicyTemplate getPolicyTemplate(Principal principal) throws RepositoryException {
-        if (!session.getPrincipalManager().hasPrincipal(principal.getName())) {
-            throw new AccessControlException("Unknown principal.");
-        }
-
-        String nPath = getPathToAcNode(principal);
-        if (session.nodeExists(nPath)) {
-            return getPolicyTemplate(nPath);
-        } else {
-            // no policy for the given principal
-            log.debug("No combined policy template for Principal " + principal.getName());
-            return null;
-        }
+    CombinedEditor(AccessControlEditor[] editors) {
+        this.editors = editors;
     }
 
     //------------------------------------------------< AccessControlEditor >---
     /**
-     * @see AccessControlEditor#getPolicyTemplate(String)
-     */
-    public PolicyTemplate getPolicyTemplate(String nodePath) throws AccessControlException, PathNotFoundException, RepositoryException {
-        checkProtectsNode(nodePath);
-
-        NodeImpl acNode = getAcNode(nodePath);
-        if (acNode != null) {
-            return createTemplate(acNode);
-        } else {
-            // nodeID not below rep:accesscontrol -> delegate to ACLEditor
-            return super.getPolicyTemplate(nodePath);
-        }
-    }
-
-    /**
-     * @see AccessControlEditor#editPolicyTemplate(String)
+     * @see AccessControlEditor#getPolicies(String)
      */
-    public PolicyTemplate editPolicyTemplate(String nodePath) throws AccessControlException, PathNotFoundException, RepositoryException {
-        checkProtectsNode(nodePath);
-
-        if (Text.isDescendant(acRootPath, nodePath)) {
-            NodeImpl acNode = getAcNode(nodePath);
-            if (acNode == null) {
-                // check validity and create the ac node
-                getPrincipal(nodePath);
-                acNode = createAcNode(nodePath);
+    public AccessControlPolicy[] getPolicies(String nodePath) throws AccessControlException, PathNotFoundException, RepositoryException {
+        List templates = new ArrayList(editors.length);
+        for (int i = 0; i < editors.length; i++) {
+            AccessControlPolicy[] ts = editors[i].getPolicies(nodePath);
+            if (ts.length > 0) {
+                templates.addAll(Arrays.asList(ts));
             }
-            return createTemplate(acNode);
-        } else {
-            // nodeID not below rep:accesscontrol -> delegate to ACLEditor
-            return super.editPolicyTemplate(nodePath);
         }
+        return (AccessControlPolicy[]) templates.toArray(new AccessControlPolicy[templates.size()]);
     }
 
     /**
-     * @see AccessControlEditor#editPolicyTemplate(Principal)
+     * @see AccessControlEditor#editAccessControlPolicies(String)
      */
-    public PolicyTemplate editPolicyTemplate(Principal principal) throws RepositoryException {
-        if (!session.getPrincipalManager().hasPrincipal(principal.getName())) {
-            throw new AccessControlException("Unknown principal.");
-        }
-        String nPath = getPathToAcNode(principal);
-        if (!session.nodeExists(nPath)) {
-            createAcNode(nPath);
-        }
-        return getPolicyTemplate(nPath);
-    }
-
-    /**
-     * @see AccessControlEditor#setPolicyTemplate(String,PolicyTemplate)
-     */
-    public void setPolicyTemplate(String nodePath, PolicyTemplate template) throws AccessControlException, PathNotFoundException, RepositoryException {
-        checkProtectsNode(nodePath);
-
-        if (template instanceof PolicyTemplateImpl) {
-            PolicyTemplateImpl at = (PolicyTemplateImpl) template;
-            if (!nodePath.equals(at.getPath())) {
-                throw new AccessControlException("Attempt to store PolicyTemplate to a wrong node.");
-            }
-            NodeImpl acNode = getAcNode(nodePath);
-            if (acNode == null) {
-                throw new PathNotFoundException("No such node " + nodePath);
+    public AccessControlPolicy[] editAccessControlPolicies(String nodePath) throws AccessControlException, PathNotFoundException, RepositoryException {
+        List templates = new ArrayList(editors.length);
+        for (int i = 0; i < editors.length; i++) {
+            try {
+                templates.addAll(Arrays.asList(editors[i].editAccessControlPolicies(nodePath)));
+            } catch (AccessControlException e) {
+                log.debug(e.getMessage());
+                // ignore.
             }
-
-            /*
-             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 */
-            PolicyEntry[] aces = (PolicyEntry[]) template.getEntries();
-            for (int i = 0; i < aces.length; i++) {
-                PolicyEntryImpl ace = (PolicyEntryImpl) 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[j] = vf.createValue(privs[j].getName());
-                }
-                setSecurityProperty(aceNode, P_PRIVILEGES, vs);
-
-                // remove local namespace remapping from the node path before
-                // storing the path value.
-                String pathValue = systemResolver.getJCRPath(session.getQPath(ace.getNodePath()));
-                setSecurityProperty(aceNode, P_NODE_PATH, vf.createValue(pathValue, PropertyType.PATH));
-
-                // TODO: TOBEFIXED respect namespace sensitive parts of the glob
-                setSecurityProperty(aceNode, P_GLOB, vf.createValue(ace.getGlob()));
-            }
-        } else {
-            // try super class
-            super.setPolicyTemplate(nodePath, template);
         }
+        return (AccessControlPolicy[]) templates.toArray(new AccessControlPolicy[templates.size()]);
     }
 
     /**
-     * @see AccessControlEditor#removePolicyTemplate(String)
-     * @param nodePath
+     * @see AccessControlEditor#editAccessControlPolicies(Principal)
      */
-    public PolicyTemplate removePolicyTemplate(String nodePath) throws AccessControlException, PathNotFoundException, RepositoryException {
-        checkProtectsNode(nodePath);
-
-        NodeImpl acNode = getAcNode(nodePath);
-        if (acNode != null) {
-            if (isAccessControlled(acNode)) {
-                // build the template in order to have a return value
-                PolicyTemplate tmpl = createTemplate(acNode);
-                removeSecurityItem(acNode.getNode(N_POLICY));
-                return tmpl;
-            } else {
-                log.debug("No policy present to remove at " + nodePath);
-                return null;
+    public AccessControlPolicy[] editAccessControlPolicies(Principal principal) throws RepositoryException {
+        List templates = new ArrayList();
+        for (int i = 0; i < editors.length; i++) {
+            try {
+                templates.addAll(Arrays.asList(editors[i].editAccessControlPolicies(principal)));
+            } catch (AccessControlException e) {
+                log.debug(e.getMessage());
+                // ignore.
             }
-        } else {
-            // nodeID not below rep:accesscontrol -> delegate to ACLEditor
-            return super.removePolicyTemplate(nodePath);
         }
+        return (AccessControlPolicy[]) templates.toArray(new AccessControlPolicy[templates.size()]);
     }
 
-    //------------------------------------------------------------< private >---
     /**
-     *
-     * @param nodePath
-     * @return
-     * @throws PathNotFoundException
-     * @throws RepositoryException
+     * @see AccessControlEditor#setPolicy(String,AccessControlPolicy)
      */
-    private NodeImpl getAcNode(String nodePath) throws PathNotFoundException, RepositoryException {
-        if (Text.isDescendant(acRootPath, nodePath)) {
-            return (NodeImpl) session.getNode(nodePath);
-        } else {
-            // node outside of rep:accesscontrol tree -> not handled by this editor.
-            return null;
-        }
-    }
-
-    private NodeImpl createAcNode(String acPath) throws RepositoryException {
-        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);
+    public void setPolicy(String nodePath, AccessControlPolicy template) throws AccessControlException, PathNotFoundException, RepositoryException {
+        for (int i = 0; i < editors.length; i++) {
+            try {
+                // return as soon as the first editor successfully handled the
+                // specified template
+                editors[i].setPolicy(nodePath, template);
+                log.debug("Set template " + template + " using " + editors[i]);
+                return;
+            } catch (AccessControlException e) {
+                log.debug(e.getMessage());
+                // ignore and try next
             }
         }
-        return node;
-    }
 
-    /**
-     * Test if the Node identified by <code>id</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
-     * represents a ACL or ACE item.
-     * @throws RepositoryException
-     */
-    private void checkProtectsNode(String nodePath) throws RepositoryException {
-        if (session.nodeExists(nodePath)) {
-            NodeImpl n = (NodeImpl) session.getNode(nodePath);
-            if (n.isNodeType(NT_REP_ACL) || n.isNodeType(NT_REP_ACE)) {
-                throw new AccessControlException("Node " + nodePath + " defines ACL or ACE.");
-            }
-        }
-    }
-
-    /**
-     *
-     * @param principal
-     * @return
-     * @throws RepositoryException
-     */
-    private String getPathToAcNode(Principal principal) throws RepositoryException {
-        StringBuffer princPath = new StringBuffer(acRootPath);
-        if (principal instanceof ItemBasedPrincipal) {
-            princPath.append(((ItemBasedPrincipal) principal).getPath());
-        } else {
-            princPath.append("/");
-            princPath.append(Text.escapeIllegalJcrChars(principal.getName()));
-        }
-        return princPath.toString();
-    }
-
-    private Principal getPrincipal(String pathToACNode) throws RepositoryException {
-        String name = Text.unescapeIllegalJcrChars(Text.getName(pathToACNode));
-        PrincipalManager pMgr = session.getPrincipalManager();
-        if (!pMgr.hasPrincipal(name)) {
-            throw new AccessControlException("Unknown principal.");
-        }
-        return pMgr.getPrincipal(name);
-    }
-
-    /**
-     *
-     * @param node
-     * @return
-     * @throws RepositoryException
-     */
-    private boolean isAccessControlled(NodeImpl node) throws RepositoryException {
-        return node.isNodeType(NT_REP_ACCESS_CONTROL) && node.hasNode(N_POLICY);
+        // none accepted -> throw
+        throw new AccessControlException("None of the editors accepted policy " + template + " at " + nodePath);
     }
 
     /**
-     *
-     * @param acNode
-     * @return
-     * @throws RepositoryException
+     * @see AccessControlEditor#removePolicy(String,AccessControlPolicy)
      */
-    private PolicyTemplate createTemplate(NodeImpl acNode) throws RepositoryException {
-        if (!acNode.isNodeType(NT_REP_ACCESS_CONTROL)) {
-            throw new RepositoryException("Expected node of type rep:AccessControl.");
-        }
-
-        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);
-        }
-
-        // build the list of policy entries;
-        List entries = new ArrayList();
-        if (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 = createEntry(aceNode, principal);
-                if (ace != null) {
-                    entries.add(ace);
-                }
+    public void removePolicy(String nodePath,
+                             AccessControlPolicy policy) throws AccessControlException, PathNotFoundException, RepositoryException {
+        for (int i = 0; i < editors.length; i++) {
+            try {
+                // return as soon as the first editor successfully handled the
+                // specified template
+                editors[i].removePolicy(nodePath, policy);
+                log.debug("Removed template " + policy + " using " + editors[i]);
+                return;
+            } catch (AccessControlException e) {
+                log.debug(e.getMessage());
+                // ignore and try next
             }
         }
-        return new PolicyTemplateImpl(entries, principal, acNode.getPath());
-    }
-
-    /**
-     *
-     * @param node
-     * @param principal
-     * @return
-     * @throws RepositoryException
-     */
-    private PolicyEntryImpl createEntry(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 pV = node.getProperty(P_NODE_PATH).getString();
-        String nodePath = session.getJCRPath(systemResolver.getQPath(pV));
-
-        // TODO: make sure local namespace remappings are respected.
-        String glob = node.getProperty(P_GLOB).getString();
-
-        return new PolicyEntryImpl(principal, privileges, allow, nodePath, glob);
+        // neither of the editors was able to remove a policy at nodePath
+        throw new AccessControlException("Unable to remove template " + policy);
     }
 }
\ No newline at end of file

Modified: 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=689499&r1=689498&r2=689499&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/combined/CombinedProvider.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/combined/CombinedProvider.java Wed Aug 27 08:12:04 2008
@@ -16,71 +16,60 @@
  */
 package org.apache.jackrabbit.core.security.authorization.combined;
 
-import org.apache.jackrabbit.api.jsr283.security.AccessControlEntry;
-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.observation.SynchronousEventListener;
-import org.apache.jackrabbit.core.security.SecurityConstants;
 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.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.principal.PrincipalImpl;
+import org.apache.jackrabbit.core.security.authorization.AccessControlUtils;
+import org.apache.jackrabbit.core.security.authorization.AbstractCompiledPermissions;
+import org.apache.jackrabbit.core.security.authorization.principalbased.ACLProvider;
+import org.apache.jackrabbit.core.ItemImpl;
 import org.apache.jackrabbit.spi.Path;
 import org.apache.jackrabbit.spi.commons.name.PathFactoryImpl;
-import org.apache.jackrabbit.util.Text;
+import org.apache.jackrabbit.api.jsr283.security.AccessControlPolicy;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import javax.jcr.Item;
-import javax.jcr.Node;
 import javax.jcr.RepositoryException;
 import javax.jcr.Session;
-import javax.jcr.observation.Event;
-import javax.jcr.observation.EventIterator;
-import javax.jcr.observation.EventListener;
-import java.security.Principal;
+import javax.jcr.ItemNotFoundException;
 import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.HashMap;
+import java.util.Arrays;
+import java.util.Iterator;
 
 /**
  * <code>CombinedProvider</code>...
  */
-public class CombinedProvider extends AbstractAccessControlProvider implements AccessControlConstants {
+public class CombinedProvider extends AbstractAccessControlProvider {
 
     private static Logger log = LoggerFactory.getLogger(CombinedProvider.class);
 
-    // TODO: add means to show effective-policy to a user.
-    // TODO: TOBEFIXED proper evaluation of permissions respecting resource-based ACLs.
-    // TODO: TOBEFIXED assert proper evaluation order of group/non-group principal-ACLs
+    private AccessControlProvider[] providers;
 
-    private CombinedEditor editor;
-    private NodeImpl acRoot;
-
-    public CombinedProvider() {
-        super("Combined AC policy", "Policy evaluating user-based and resource-based ACLs.");
+    //-------------------------------------------------< AccessControlUtils >---
+    /**
+     * @see AccessControlUtils#isAcItem(Path)
+     */
+    public boolean isAcItem(Path absPath) throws RepositoryException {
+        for (int i = 0; i < providers.length; i++) {
+            if (providers[i] instanceof AccessControlUtils && ((AccessControlUtils) providers[i]).isAcItem(absPath)) {
+                return true;
+            }
+        }
+        return false;
     }
 
-    //--------------------------------------< AbstractAccessControlProvider >---
     /**
-     * @see AbstractAccessControlProvider#isAcItem(Path)
+     * @see AccessControlUtils#isAcItem(ItemImpl)
      */
-    protected 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())) {
+    public boolean isAcItem(ItemImpl item) throws RepositoryException {
+        for (int i = 0; i < providers.length; i++) {
+            if (providers[i] instanceof AccessControlUtils && ((AccessControlUtils) providers[i]).isAcItem(item)) {
                 return true;
             }
         }
@@ -89,65 +78,50 @@
 
     //----------------------------------------------< AccessControlProvider >---
     /**
-     * @see AccessControlProvider#init(javax.jcr.Session, java.util.Map)
+     * @see AccessControlProvider#close()
      */
-    public void init(Session systemSession, Map options) throws RepositoryException {
-        super.init(systemSession, options);
-
-        NodeImpl root = (NodeImpl) session.getRootNode();
-        if (root.hasNode(N_ACCESSCONTROL)) {
-            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);
+    public void close() {
+        for (int i = 0; i < providers.length; i++) {
+            providers[i].close();
         }
+        super.close();
+    }
 
-        editor = new CombinedEditor(session, resolver, resolver.getQPath(acRoot.getPath()));
-        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);
-            }
+    /**
+     * @see AccessControlProvider#init(javax.jcr.Session, java.util.Map)
+     */
+    public void init(Session systemSession, Map configuration) throws RepositoryException {
+        super.init(systemSession, configuration);
 
-            String glob = GlobPattern.WILDCARD_ALL;
-            PolicyTemplate pt = editor.editPolicyTemplate(administrators);
-            pt.setEntry(new PolicyEntryImpl(administrators, PrivilegeRegistry.ALL, true, root.getPath(), glob));
-            editor.setPolicyTemplate(pt.getPath(), pt);
-
-            Principal everyone = pMgr.getEveryone();
-            log.info("... Privilege.READ for everyone.");
-            pt = editor.editPolicyTemplate(everyone);
-            pt.setEntry(new PolicyEntryImpl(everyone, PrivilegeRegistry.READ, true, root.getPath(), glob));
-            editor.setPolicyTemplate(pt.getPath(), 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;
-        }
+        // this provider combines the result of 2 (currently hardcoded) AC-providers
+        // TODO: make this configurable
+        providers = new AccessControlProvider[2];
+
+        // 1) a resource-based ACL provider, that is not inited with default
+        //    permissions and should only be used to overrule the permissions
+        //    granted or denied by the default provider (see 2).
+        providers[0] = new org.apache.jackrabbit.core.security.authorization.acl.ACLProvider();
+        Map config = new HashMap(configuration);
+        config.put(PARAM_OMIT_DEFAULT_PERMISSIONS, Boolean.TRUE);
+        providers[0].init(systemSession, config);
+
+        // 2) the principal-base ACL provider which is intended to provide
+        //    the default/standard permissions present at an item for a given
+        //    set of principals.
+        providers[1] = new ACLProvider();
+        providers[1].init(systemSession, configuration);
     }
 
     /**
-     * @see AccessControlProvider#getAccessControlEntries(Path)
-     * @param absPath
-     */
-    public AccessControlEntry[] getAccessControlEntries(Path absPath) throws RepositoryException {
-        checkInitialized();
-        // TODO: TOBEFIXED
-        return new AccessControlEntry[0];
+     * @see AccessControlProvider#getEffectivePolicies(Path)
+     */
+    public AccessControlPolicy[] getEffectivePolicies(Path absPath)
+            throws ItemNotFoundException, RepositoryException {
+        List l = new ArrayList();
+        for (int i = 0; i < providers.length; i++) {
+            l.addAll(Arrays.asList(providers[i].getEffectivePolicies(absPath)));
+        }
+        return (AccessControlPolicy[]) l.toArray(new AccessControlPolicy[l.size()]);
     }
 
     /**
@@ -155,18 +129,21 @@
      */
     public AccessControlEditor getEditor(Session editingSession) {
         checkInitialized();
-        if (editingSession instanceof SessionImpl) {
+        List editors = new ArrayList();
+        for (int i = 0; i < providers.length; i++) {
             try {
-                return new CombinedEditor((SessionImpl) editingSession,
-                        session, session.getQPath(acRoot.getPath()));
+                editors.add(providers[i].getEditor(editingSession));
             } catch (RepositoryException e) {
-                // should never get here
-                log.error("Internal error:", e.getMessage());
+                log.debug(e.getMessage());
+                // ignore.
             }
         }
-
-        log.debug("Unable to build access control editor " + CombinedEditor.class.getName() + ".");
-        return null;
+        if (!editors.isEmpty()) {
+            return new CombinedEditor((AccessControlEditor[]) editors.toArray(new AccessControlEditor[editors.size()]));
+        } else {
+            log.debug("None of the derived access control providers supports editing.");
+            return null;
+        }
     }
 
     /**
@@ -176,8 +153,6 @@
         checkInitialized();
         if (isAdminOrSystem(principals)) {
             return getAdminPermissions();
-        } else if (isReadOnly(principals)) {
-            return getReadOnlyPermissions();
         } else {
             return new CompiledPermissionImpl(principals);
         }
@@ -191,7 +166,13 @@
         if (isAdminOrSystem(principals)) {
             return true;
         } else {
-            return new CompiledPermissionImpl(principals, false).grants(PathFactoryImpl.getInstance().getRootPath(), Permission.READ);
+            CompiledPermissions cp = new CompiledPermissionImpl(principals);
+            try {
+                Path rootPath = PathFactoryImpl.getInstance().getRootPath();
+                return cp.grants(rootPath, Permission.READ);
+            } finally {
+                cp.close();
+            }
         }
     }
 
@@ -199,39 +180,24 @@
     /**
      *
      */
-    private class CompiledPermissionImpl extends AbstractCompiledPermissions
-            implements SynchronousEventListener {
+    private class CompiledPermissionImpl extends AbstractCompiledPermissions  {
 
-        private final Set principals;
-        private final Set acPaths;
-        private Entries entries;
+        private final List cPermissions;
 
         /**
          * @param principals
-         * @throws RepositoryException
          */
-        private CompiledPermissionImpl(Set principals) throws RepositoryException {
-            this(principals, true);
-        }
-
-        /**
-         * @param principals
-         * @throws RepositoryException
-         */
-        private CompiledPermissionImpl(Set principals, boolean listenToEvents) throws RepositoryException {
-
-            this.principals = principals;
-            acPaths = new HashSet(principals.size());
-            entries = reload();
-
-            // TODO: describe
-            if (listenToEvents) {
-                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)
-                };
-                observationMgr.addEventListener(this, events, acRoot.getPath(), true, null, ntNames, false);
+        private CompiledPermissionImpl(Set principals) throws
+                RepositoryException {
+            this.cPermissions = new ArrayList();
+            for (int i = 0; i < providers.length; i++) {
+                CompiledPermissions cp = providers[i].compilePermissions(principals);
+                if (cp instanceof AbstractCompiledPermissions) {
+                    cPermissions.add(cp);
+                } else {
+                    // TODO: deal with other impls.
+                    log.warn("AbstractCompiledPermissions expected. Found " + cp.getClass().getName() + " -> ignore.");
+                }
             }
         }
 
@@ -239,214 +205,36 @@
         /**
          * @see AbstractCompiledPermissions#buildResult(Path)
          */
-        protected synchronized Result buildResult(Path absPath) throws RepositoryException {
-            if (!absPath.isAbsolute()) {
-                throw new RepositoryException("Absolute path expected.");
-            }
-
-            /* 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
-             */
-            boolean isAclItem = isAcItem(absPath);
-            String jcrPath = session.getJCRPath(absPath);            
-            int permissions;
-            if (session.itemExists(jcrPath)) {
-                permissions = entries.getPermissions(session.getItem(jcrPath), isAclItem);
-            } else {
-                Node parent = null;
-                String parentPath = jcrPath;
-                while (parent == null) {
-                    parentPath = Text.getRelativeParent(parentPath, 1);
-                    if (parentPath.length() == 0) {
-                        // root node reached
-                        parent = session.getRootNode();
-                    } else if (session.nodeExists(parentPath)) {
-                        parent = session.getNode(parentPath);
-                    }
-                }
-                String relPath = jcrPath.substring(parent.getPath().length() + 1);
-                permissions = entries.getPermissions(parent, relPath, isAclItem);
-            }
-            /* TODO: privileges can only be determined for nodes. */
-            int privileges = entries.getPrivileges(jcrPath);
-            return new Result(permissions, privileges);
-        }
-
-        //--------------------------------------------< CompiledPermissions >---
-        /**
-         * @see CompiledPermissions#close()
-         */
-        public void close() {
-            try {
-                observationMgr.removeEventListener(this);
-            } catch (RepositoryException e) {
-                log.debug("Unable to unregister listener: ", e.getMessage());
-            }
-            super.close();
-        }
-
-        //--------------------------------------------------< EventListener >---
-        /**
-         * @see EventListener#onEvent(EventIterator)
-         */
-        public synchronized void onEvent(EventIterator events) {
-            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) {
-                    clearCache();
-                    // reload the acl
-                    entries = reload();
-                }
-            } catch (RepositoryException e) {
-                // should never get here
-                log.warn("Internal error: ", e.getMessage());
-            }
-        }
-
-        /**
-         *
-         * @return
-         * @throws RepositoryException
-         */
-        private Entries reload() throws RepositoryException {
-            // reload the paths
-            acPaths.clear();
-
-            // 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.
-            List allACEs = new ArrayList();
-            // build acl-hierarchy assuming that principal-order determines the
-            // acl-inheritance.
-            for (Iterator it = principals.iterator(); it.hasNext();) {
-                Principal princ = (Principal) it.next();
-                PolicyTemplate at = editor.getPolicyTemplate(princ);
-                if (at == null || at.isEmpty()) {
-                    log.debug("No matching ACL node found for principal " + princ.getName() + " -> principal ignored.");
-                } else {
-                    // retrieve the ACEs from the node
-                    PolicyEntry[] aces = (PolicyEntry[]) at.getEntries();
-                    allACEs.addAll(Arrays.asList(aces));
-                    acPaths.add(at.getPath());
-                }
+        protected Result buildResult(Path absPath) throws RepositoryException {
+            Result res = null;
+            for (Iterator it = cPermissions.iterator(); it.hasNext();) {
+                AbstractCompiledPermissions acp = (AbstractCompiledPermissions) it.next();
+                Result other = acp.getResult(absPath);
+                res = (res == null) ? other : res.combine(other);
             }
-            return new Entries(allACEs);
-        }
-    }
-
-    //--------------------------------------------------------------------------
-
-    private static class Entries {
-
-        private final List entries;
-
-        private Entries(List entries) {
-            this.entries = entries;
+            return res;
         }
 
         /**
-         * Loop over all entries and evaluate allows/denies for those matching
-         * the given jcrPath.
-         *
-         * @param target Existing target item for which the permissions will be
-         * evaluated.
-         * @param protectsACL
-         * @return
-         * @throws RepositoryException
+         * @see AbstractCompiledPermissions#getResult(Path)
          */
-        private int getPermissions(Item target, boolean protectsACL) throws RepositoryException {
-            int allows = 0;
-            int denies = 0;
-            for (Iterator it = entries.iterator(); it.hasNext() && allows != Permission.ALL;) {
-                PolicyEntryImpl entr = (PolicyEntryImpl) it.next();
-                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;
+        public Result getResult(Path absPath) throws RepositoryException {
+            // TODO: missing caching
+            return buildResult(absPath);
         }
 
+        //--------------------------------------------< CompiledPermissions >---
         /**
-         * loop over all entries and evaluate allows/denies for those matching
-         * the given jcrPath.
-         *
-         * @param parent Existing parent of the target to be evaluated.
-         * @param relPath relative path to a non-existing child item to calculate the
-         * permissions for.
-         * @param protectsACL
-         * @return
-         * @throws RepositoryException
+         * @see CompiledPermissions#close()
          */
-        private int getPermissions(Node parent, String relPath, boolean protectsACL) throws RepositoryException {
-            int allows = 0;
-            int denies = 0;
-            String jcrPath = parent.getPath() + "/" + relPath;
-
-            for (Iterator it = entries.iterator(); it.hasNext() && allows != Permission.ALL;) {
-                PolicyEntryImpl entr = (PolicyEntryImpl) it.next();
-                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);
-                    }
-                }
+        public synchronized void close() {
+            // close all c-permissions retained in the list and clear the list.
+            for (Iterator it = cPermissions.iterator(); it.hasNext();) {
+                CompiledPermissions cp = (CompiledPermissions) it.next();
+                cp.close();
+                it.remove();
             }
-            return allows;
-        }
-
-        private int getPrivileges(String nodePath) throws RepositoryException {
-            // TODO: improve. avoid duplicate evaluation ...            
-            int allows = 0;
-            int denies = 0;
-            for (Iterator it = entries.iterator(); it.hasNext() && allows != Permission.ALL;) {
-                PolicyEntryImpl entr = (PolicyEntryImpl) it.next();
-                // loop over all entries and evaluate allows/denies for those
-                // matching the given jcrPath
-                // TODO: check again which ACEs must be respected.
-                // TODO: maybe ancestor-defs only if glob = *?
-                String np = entr.getNodePath();
-                // TODO: TOBEFIXED Text.isDescendant that returns false if np==root-path
-                if (np.equals(nodePath) || "/".equals(np) || Text.isDescendant(np, nodePath)) {
-                    if (entr.isAllow()) {
-                        allows |= PrivilegeRegistry.diff(entr.getPrivilegeBits(), denies);
-                    } else {
-                        denies |= PrivilegeRegistry.diff(entr.getPrivilegeBits(), allows);
-                    }
-                }
-            }
-            return allows;
+            super.close();
         }
     }
 }
\ No newline at end of file

Added: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/principalbased/ACLEditor.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/principalbased/ACLEditor.java?rev=689499&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/principalbased/ACLEditor.java (added)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/principalbased/ACLEditor.java Wed Aug 27 08:12:04 2008
@@ -0,0 +1,369 @@
+/*
+ * $Id$
+ *
+ * Copyright 1997-2005 Day Management AG
+ * Barfuesserplatz 6, 4001 Basel, Switzerland
+ * All Rights Reserved.
+ *
+ * This software is the confidential and proprietary information of
+ * Day Management AG, ("Confidential Information"). You shall not
+ * disclose such Confidential Information and shall use it only in
+ * accordance with the terms of the license agreement you entered into
+ * with Day.
+ */
+package org.apache.jackrabbit.core.security.authorization.principalbased;
+
+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.AccessControlPolicy;
+import org.apache.jackrabbit.api.security.principal.PrincipalManager;
+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.JackrabbitAccessControlEntry;
+import org.apache.jackrabbit.core.security.principal.ItemBasedPrincipal;
+import org.apache.jackrabbit.core.security.principal.PrincipalImpl;
+import org.apache.jackrabbit.spi.Name;
+import org.apache.jackrabbit.spi.Path;
+import org.apache.jackrabbit.spi.commons.conversion.NameException;
+import org.apache.jackrabbit.spi.commons.conversion.NameParser;
+import org.apache.jackrabbit.util.Text;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.jcr.Node;
+import javax.jcr.PathNotFoundException;
+import javax.jcr.RepositoryException;
+import javax.jcr.Value;
+import javax.jcr.ValueFactory;
+import java.security.Principal;
+
+/**
+ * <code>CombinedEditor</code>...
+ */
+public class ACLEditor extends SecurityItemModifier implements AccessControlEditor, AccessControlConstants {
+
+    private static Logger log = LoggerFactory.getLogger(ACLEditor.class);
+    /**
+     * Default name for ace nodes
+     */
+    private static final String DEFAULT_ACE_NAME = "ace";
+
+    /**
+     * the editing session
+     */
+    private final SessionImpl session;
+    private final String acRootPath;
+
+    ACLEditor(SessionImpl session, Path acRootPath) throws RepositoryException {
+        this.session = session;
+        this.acRootPath = session.getJCRPath(acRootPath);
+    }
+
+    ACLTemplate getACL(Principal principal) throws RepositoryException {
+        if (!session.getPrincipalManager().hasPrincipal(principal.getName())) {
+            throw new AccessControlException("Unknown principal.");
+        }
+        String nPath = getPathToAcNode(principal);
+        if (session.nodeExists(nPath)) {
+            return (ACLTemplate) getPolicies(nPath)[0];
+        } else {
+            // no policy for the given principal
+            log.debug("No combined policy template for Principal " + principal.getName());
+            return null;
+        }
+    }
+
+    //------------------------------------------------< AccessControlEditor >---
+    /**
+     * @see AccessControlEditor#getPolicies(String)
+     */
+    public AccessControlPolicy[] getPolicies(String nodePath) throws AccessControlException, PathNotFoundException, RepositoryException {
+        checkProtectsNode(nodePath);
+
+        NodeImpl acNode = getAcNode(nodePath);
+        if (acNode != null) {
+            return new AccessControlPolicy[] {createTemplate(acNode)};
+        } else {
+            return new AccessControlPolicy[0];
+        }
+    }
+
+    /**
+     * @see AccessControlEditor#editAccessControlPolicies(String)
+     */
+    public AccessControlPolicy[] editAccessControlPolicies(String nodePath) throws AccessControlException, PathNotFoundException, RepositoryException {
+        checkProtectsNode(nodePath);
+
+        if (Text.isDescendant(acRootPath, nodePath)) {
+            NodeImpl acNode = getAcNode(nodePath);
+            if (acNode == null) {
+                // check validity and create the ac node
+                getPrincipal(nodePath);
+                acNode = createAcNode(nodePath);
+            }
+            return new AccessControlPolicy[] {createTemplate(acNode)};
+        } else {
+            // nodePath not below rep:accesscontrol -> not editable
+            return new AccessControlPolicy[0];
+        }
+    }
+
+    /**
+     * @see AccessControlEditor#editAccessControlPolicies(Principal)
+     */
+    public AccessControlPolicy[] editAccessControlPolicies(Principal principal) throws RepositoryException {
+        if (!session.getPrincipalManager().hasPrincipal(principal.getName())) {
+            throw new AccessControlException("Unknown principal.");
+        }
+        String nPath = getPathToAcNode(principal);
+        if (!session.nodeExists(nPath)) {
+            createAcNode(nPath);
+        }
+        return getPolicies(nPath);
+    }
+
+    /**
+     * @see AccessControlEditor#setPolicy(String,AccessControlPolicy)
+     */
+    public void setPolicy(String nodePath, AccessControlPolicy policy) throws AccessControlException, PathNotFoundException, RepositoryException {
+        checkProtectsNode(nodePath);
+        checkValidPolicy(nodePath, policy);
+
+        ACLTemplate acl = (ACLTemplate) policy;
+        NodeImpl acNode = getAcNode(nodePath);
+        if (acNode == null) {
+            throw new PathNotFoundException("No such node " + nodePath);
+        }
+        // write the entries to the node
+        /*
+         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 */
+        AccessControlEntry[] aces = acl.getAccessControlEntries();
+        for (int i = 0; i < aces.length; i++) {
+            JackrabbitAccessControlEntry ace = (JackrabbitAccessControlEntry) 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[j] = vf.createValue(privs[j].getName());
+            }
+            setSecurityProperty(aceNode, P_PRIVILEGES, vs);
+
+            // store the restrictions:
+            String[] restrNames = ace.getRestrictionNames();
+            for (int rnIndex = 0; rnIndex < restrNames.length; rnIndex++) {
+                Name pName = session.getQName(restrNames[rnIndex]);
+                Value value = ace.getRestriction(restrNames[rnIndex]);
+                setSecurityProperty(aceNode, pName, value);
+            }
+        }
+    }
+
+    /**
+     * @see AccessControlEditor#removePolicy(String,AccessControlPolicy)
+     */
+    public void removePolicy(String nodePath, AccessControlPolicy policy) throws AccessControlException, PathNotFoundException, RepositoryException {
+        checkProtectsNode(nodePath);
+        checkValidPolicy(nodePath, policy);
+
+        NodeImpl acNode = getAcNode(nodePath);
+        if (acNode != null) {
+            if (isAccessControlled(acNode)) {
+                // build the template in order to have a return value
+                AccessControlPolicy tmpl = createTemplate(acNode);
+                if (tmpl.equals(policy)) {
+                    removeSecurityItem(acNode.getNode(N_POLICY));
+                    return;
+                }
+            }
+        }
+        // node either not access-controlled or the passed policy didn't apply
+        // to the node at 'nodePath' -> throw exception.no policy was removed
+        throw new AccessControlException("Policy " + policy + " does not apply to " + nodePath);
+    }
+
+    //------------------------------------------------------------< private >---
+    /**
+     *
+     * @param nodePath
+     * @return
+     * @throws PathNotFoundException
+     * @throws RepositoryException
+     */
+    private NodeImpl getAcNode(String nodePath) throws PathNotFoundException, RepositoryException {
+        if (Text.isDescendant(acRootPath, nodePath)) {
+            return (NodeImpl) session.getNode(nodePath);
+        } else {
+            // node outside of rep:accesscontrol tree -> not handled by this editor.
+            return null;
+        }
+    }
+
+    private NodeImpl createAcNode(String acPath) throws RepositoryException {
+        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;
+    }
+
+    /**
+     * Check if the Node identified by <code>id</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
+     * represents a ACL or ACE item.
+     * @throws RepositoryException
+     */
+    private void checkProtectsNode(String nodePath) throws RepositoryException {
+        if (session.nodeExists(nodePath)) {
+            NodeImpl n = (NodeImpl) session.getNode(nodePath);
+            if (n.isNodeType(NT_REP_ACL) || n.isNodeType(NT_REP_ACE)) {
+                throw new AccessControlException("Node " + nodePath + " defines ACL or ACE.");
+            }
+        }
+    }
+
+    /**
+     * Check if the specified policy can be set or removed at nodePath.
+     *
+     * @param nodePath
+     * @param policy
+     * @throws AccessControlException
+     */
+    private 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 + " is not applicable or does not apply to the node at " + nodePath);
+        }
+    }
+
+    /**
+     *
+     * @param principal
+     * @return
+     * @throws RepositoryException
+     */
+    String getPathToAcNode(Principal principal) throws RepositoryException {
+        StringBuffer princPath = new StringBuffer(acRootPath);
+        if (principal instanceof ItemBasedPrincipal) {
+            princPath.append(((ItemBasedPrincipal) principal).getPath());
+        } else {
+            princPath.append("/");
+            princPath.append(Text.escapeIllegalJcrChars(principal.getName()));
+        }
+        return princPath.toString();
+    }
+
+    private Principal getPrincipal(String pathToACNode) throws RepositoryException {
+        String name = Text.unescapeIllegalJcrChars(Text.getName(pathToACNode));
+        PrincipalManager pMgr = session.getPrincipalManager();
+        if (!pMgr.hasPrincipal(name)) {
+            throw new AccessControlException("Unknown principal.");
+        }
+        return pMgr.getPrincipal(name);
+    }
+
+    /**
+     *
+     * @param node
+     * @return
+     * @throws RepositoryException
+     */
+    private boolean isAccessControlled(NodeImpl node) throws RepositoryException {
+        return node.isNodeType(NT_REP_ACCESS_CONTROL) && node.hasNode(N_POLICY);
+    }
+
+    /**
+     *
+     * @param acNode
+     * @return
+     * @throws RepositoryException
+     */
+    private AccessControlPolicy createTemplate(NodeImpl acNode) throws RepositoryException {
+        if (!acNode.isNodeType(NT_REP_ACCESS_CONTROL)) {
+            throw new RepositoryException("Expected node of type rep:AccessControl.");
+        }
+
+        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 ACLTemplate(principal, acNode);
+    }
+
+    /**
+     * 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_ACE_NAME} is taken
+     * @return
+     * @throws RepositoryException
+     */
+    protected static Name getUniqueNodeName(Node node, String name) throws RepositoryException {
+        if (name == null) {
+            name = DEFAULT_ACE_NAME;
+        } else {
+            try {
+                NameParser.checkFormat(name);
+            } catch (NameException e) {
+                name = DEFAULT_ACE_NAME;
+                log.debug("Invalid path name for Permission: " + name + ".");
+            }
+        }
+        int i=0;
+        String check = name;
+        while (node.hasNode(check)) {
+            check = name + i;
+            i++;
+        }
+        return ((SessionImpl) node.getSession()).getQName(check);
+    }
+}

Propchange: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/principalbased/ACLEditor.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/principalbased/ACLEditor.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision url

Added: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/principalbased/ACLProvider.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/principalbased/ACLProvider.java?rev=689499&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/principalbased/ACLProvider.java (added)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/principalbased/ACLProvider.java Wed Aug 27 08:12:04 2008
@@ -0,0 +1,441 @@
+/*
+ * $Id$
+ *
+ * Copyright 1997-2005 Day Management AG
+ * Barfuesserplatz 6, 4001 Basel, Switzerland
+ * All Rights Reserved.
+ *
+ * This software is the confidential and proprietary information of
+ * Day Management AG, ("Confidential Information"). You shall not
+ * disclose such Confidential Information and shall use it only in
+ * accordance with the terms of the license agreement you entered into
+ * with Day.
+ */
+package org.apache.jackrabbit.core.security.authorization.principalbased;
+
+import org.apache.jackrabbit.core.security.authorization.AbstractAccessControlProvider;
+import org.apache.jackrabbit.core.security.authorization.AccessControlConstants;
+import org.apache.jackrabbit.core.security.authorization.AccessControlProvider;
+import org.apache.jackrabbit.core.security.authorization.PrivilegeRegistry;
+import org.apache.jackrabbit.core.security.authorization.AccessControlEditor;
+import org.apache.jackrabbit.core.security.authorization.CompiledPermissions;
+import org.apache.jackrabbit.core.security.authorization.Permission;
+import org.apache.jackrabbit.core.security.authorization.AbstractCompiledPermissions;
+import org.apache.jackrabbit.core.security.authorization.AccessControlUtils;
+import org.apache.jackrabbit.core.security.authorization.UnmodifiableAccessControlList;
+import org.apache.jackrabbit.core.security.SecurityConstants;
+import org.apache.jackrabbit.core.security.principal.PrincipalImpl;
+import org.apache.jackrabbit.core.NodeImpl;
+import org.apache.jackrabbit.core.SessionImpl;
+import org.apache.jackrabbit.core.ItemImpl;
+import org.apache.jackrabbit.core.observation.SynchronousEventListener;
+import org.apache.jackrabbit.spi.Path;
+import org.apache.jackrabbit.spi.commons.name.PathFactoryImpl;
+import org.apache.jackrabbit.api.security.principal.PrincipalManager;
+import org.apache.jackrabbit.api.jsr283.security.AccessControlPolicy;
+import org.apache.jackrabbit.api.jsr283.security.AccessControlEntry;
+import org.apache.jackrabbit.api.jsr283.security.Privilege;
+import org.apache.jackrabbit.api.jsr283.security.AccessControlManager;
+import org.apache.jackrabbit.util.Text;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.Item;
+import javax.jcr.ItemNotFoundException;
+import javax.jcr.PropertyType;
+import javax.jcr.ValueFactory;
+import javax.jcr.observation.Event;
+import javax.jcr.observation.EventListener;
+import javax.jcr.observation.EventIterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.HashSet;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.security.Principal;
+
+/**
+ * <code>CombinedProvider</code>...
+ */
+public class ACLProvider extends AbstractAccessControlProvider implements AccessControlConstants {
+
+    private static Logger log = LoggerFactory.getLogger(ACLProvider.class);
+
+    // TODO: add means to show effective-policy to a user.
+
+    private ACLEditor editor;
+    private NodeImpl acRoot;
+
+    //-------------------------------------------------< AccessControlUtils >---
+    /**
+     * @see AccessControlUtils#isAcItem(Path)
+     */
+    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())) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * @see AccessControlUtils#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(javax.jcr.Session, java.util.Map)
+     */
+    public void init(Session systemSession, Map configuration) throws RepositoryException {
+        super.init(systemSession, configuration);
+
+        NodeImpl root = (NodeImpl) session.getRootNode();
+        if (root.hasNode(N_ACCESSCONTROL)) {
+            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);
+        }
+
+        editor = new ACLEditor(session, resolver.getQPath(acRoot.getPath()));
+        if (!configuration.containsKey(PARAM_OMIT_DEFAULT_PERMISSIONS)) {
+            try {
+                log.info("Install initial permissions: ...");
+
+                ValueFactory vf = session.getValueFactory();
+                Map restrictions = new HashMap();
+                restrictions.put(session.getJCRName(ACLTemplate.P_NODE_PATH), vf.createValue(root.getPath(), PropertyType.PATH));
+                restrictions.put(session.getJCRName(ACLTemplate.P_GLOB), vf.createValue(GlobPattern.WILDCARD_ALL));
+
+                PrincipalManager pMgr = session.getPrincipalManager();
+                AccessControlManager acMgr = session.getAccessControlManager();
+                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);
+                }
+                AccessControlPolicy[] acls = editor.editAccessControlPolicies(administrators);
+                ACLTemplate acl = (ACLTemplate) acls[0];
+                if (acl.isEmpty()) {
+                    log.info("... Privilege.ALL for administrators principal.");
+                    acl.addEntry(administrators,
+                            new Privilege[] {acMgr.privilegeFromName(Privilege.JCR_ALL)},
+                            true, restrictions);
+                    editor.setPolicy(acl.getPath(), acl);
+                } else {
+                    log.info("... policy for administrators principal already present.");
+                }
+
+                Principal everyone = pMgr.getEveryone();
+                acls = editor.editAccessControlPolicies(everyone);
+                acl = (ACLTemplate) acls[0];
+                if (acl.isEmpty()) {
+                    log.info("... Privilege.READ for everyone principal.");
+                    acl.addEntry(everyone,
+                            new Privilege[] {acMgr.privilegeFromName(Privilege.JCR_READ)},
+                            true, restrictions);
+                    editor.setPolicy(acl.getPath(), acl);
+                } else {
+                    log.info("... policy for everyone principal already present.");
+                }
+
+                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;
+            }
+        }
+    }
+
+    /**
+     * @see AccessControlProvider#getEffectivePolicies(Path)
+     */
+    public AccessControlPolicy[] getEffectivePolicies(Path absPath)
+            throws ItemNotFoundException, RepositoryException {
+        AccessControlPolicy[] tmpls = editor.getPolicies(session.getJCRPath(absPath));
+        AccessControlPolicy[] effectives = new AccessControlPolicy[tmpls.length];
+        for (int i = 0; i < tmpls.length; i++) {
+            effectives[i] = new UnmodifiableAccessControlList((ACLTemplate) tmpls[i]);
+        }
+        return effectives;
+    }
+
+    /**
+     * @see AccessControlProvider#getEditor(Session)
+     */
+    public AccessControlEditor getEditor(Session editingSession) {
+        checkInitialized();
+        if (editingSession instanceof SessionImpl) {
+            try {
+                return new ACLEditor((SessionImpl) editingSession, session.getQPath(acRoot.getPath()));
+            } catch (RepositoryException e) {
+                // should never get here
+                log.error("Internal error: ", e.getMessage());
+            }
+        }
+
+        log.debug("Unable to build access control editor " + ACLEditor.class.getName() + ".");
+        return null;
+    }
+
+    /**
+     * @see AccessControlProvider#compilePermissions(Set)
+     */
+    public CompiledPermissions compilePermissions(Set principals) throws RepositoryException {
+        checkInitialized();
+        if (isAdminOrSystem(principals)) {
+            return getAdminPermissions();
+        } else if (isReadOnly(principals)) {
+            return getReadOnlyPermissions();
+        } else {
+            return new ACLProvider.CompiledPermissionImpl(principals);
+        }
+    }
+
+    /**
+     * @see AccessControlProvider#canAccessRoot(Set)
+     */
+    public boolean canAccessRoot(Set principals) throws RepositoryException {
+        checkInitialized();
+        if (isAdminOrSystem(principals)) {
+            return true;
+        } else {
+            CompiledPermissions cp = new CompiledPermissionImpl(principals, false);
+            return cp.grants(PathFactoryImpl.getInstance().getRootPath(), Permission.READ);
+        }
+    }
+
+    //-----------------------------------------------------< CompiledPolicy >---
+    /**
+     *
+     */
+    private class CompiledPermissionImpl extends AbstractCompiledPermissions
+            implements SynchronousEventListener {
+
+        private final Set principals;
+        private final Set acPaths;
+        private ACLProvider.Entries entries;
+
+        /**
+         * @param principals
+         * @throws RepositoryException
+         */
+        private CompiledPermissionImpl(Set principals) throws RepositoryException {
+            this(principals, true);
+        }
+
+        /**
+         * @param principals
+         * @throws RepositoryException
+         */
+        private CompiledPermissionImpl(Set principals, boolean listenToEvents) throws RepositoryException {
+
+            this.principals = principals;
+            acPaths = new HashSet(principals.size());
+            entries = reload();
+
+            // TODO: describe
+            if (listenToEvents) {
+                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)
+                };
+                observationMgr.addEventListener(this, events, acRoot.getPath(), true, null, ntNames, false);
+            }
+        }
+
+        //------------------------------------< AbstractCompiledPermissions >---
+        /**
+         * @see AbstractCompiledPermissions#buildResult(Path)
+         */
+        protected synchronized Result buildResult(Path absPath) throws RepositoryException {
+            if (!absPath.isAbsolute()) {
+                throw new RepositoryException("Absolute path expected.");
+            }
+
+            boolean isAcItem = isAcItem(absPath);
+            String jcrPath = session.getJCRPath(absPath);
+
+            // retrieve principal-based permissions and privileges
+            Result result;
+            if (session.itemExists(jcrPath)) {
+                Item item = session.getItem(jcrPath);
+                result = entries.getResult(item, item.getPath(), isAcItem);
+            } else {
+                result = entries.getResult(null, jcrPath, isAcItem);
+            }
+            return result;
+        }
+
+        //--------------------------------------------< CompiledPermissions >---
+        /**
+         * @see CompiledPermissions#close()
+         */
+        public void close() {
+            try {
+                observationMgr.removeEventListener(this);
+            } catch (RepositoryException e) {
+                log.debug("Unable to unregister listener: ", e.getMessage());
+            }
+            super.close();
+        }
+
+        //--------------------------------------------------< EventListener >---
+        /**
+         * @see EventListener#onEvent(EventIterator)
+         */
+        public synchronized void onEvent(EventIterator events) {
+            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
+                            break;
+                    }
+                }
+                // eventually reload the ACL and clear the cache
+                if (reload) {
+                    clearCache();
+                    // reload the acl
+                    entries = reload();
+                }
+            } catch (RepositoryException e) {
+                // should never get here
+                log.warn("Internal error: ", e.getMessage());
+            }
+        }
+
+        /**
+         *
+         * @return
+         * @throws RepositoryException
+         */
+        private ACLProvider.Entries reload() throws RepositoryException {
+            // reload the paths
+            acPaths.clear();
+
+            // 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.
+            List allACEs = new ArrayList();
+            // build acl-hierarchy assuming that principal-order determines the
+            // acl-inheritance.
+            for (Iterator it = principals.iterator(); it.hasNext();) {
+                Principal princ = (Principal) it.next();
+                ACLTemplate acl = editor.getACL(princ);
+                if (acl == null || acl.isEmpty()) {
+                    acPaths.add(editor.getPathToAcNode(princ));
+                } else {
+                    // retrieve the ACEs from the node
+                    AccessControlEntry[] aces = (AccessControlEntry[]) acl.getAccessControlEntries();
+                    allACEs.addAll(Arrays.asList(aces));
+                    acPaths.add(acl.getPath());
+                }
+            }
+
+            return new ACLProvider.Entries(allACEs);
+        }
+    }
+
+    //--------------------------------------------------------------------------
+    /**
+     * Utility class that raps a list of access control entries and evaluates
+     * them for a specified item/path.
+     */
+    private class Entries {
+
+        private final List entries;
+
+        /**
+         *
+         * @param entries
+         */
+        private Entries(List entries) {
+            this.entries = entries;
+        }
+
+        /**
+         * Loop over all entries and evaluate allows/denies for those matching
+         * the given jcrPath.
+         *
+         * @param target Existing target item for which the permissions will be
+         * evaluated or <code>null</code>.
+         * @param targetPath Path used for the evaluation; pointing to an
+         * existing or non-existing item.
+         * @param isAcItem
+         * @return
+         * @throws RepositoryException
+         */
+        private AbstractCompiledPermissions.Result getResult(Item target,
+                                                             String targetPath,
+                                                             boolean isAcItem) throws RepositoryException {
+            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;
+
+            String parentPath = Text.getRelativeParent(targetPath, 1);
+            for (Iterator it = entries.iterator(); it.hasNext() && allows != Permission.ALL;) {
+                ACLTemplate.Entry entr = (ACLTemplate.Entry) it.next();
+                int privs = entr.getPrivilegeBits();
+
+                if (!"".equals(parentPath) && entr.matches(parentPath)) {
+                    if (entr.isAllow()) {
+                        parentAllows |= Permission.diff(privs, parentDenies);
+                    } else {
+                        parentDenies |= Permission.diff(privs, parentAllows);
+                    }
+                }
+
+                boolean matches = (target != null) ? entr.matches(target) : entr.matches(targetPath);
+                if (matches) {
+                    if (entr.isAllow()) {
+                        allowPrivileges |= Permission.diff(privs, denyPrivileges);
+                        int permissions = Permission.calculatePermissions(allowPrivileges, parentAllows, true, isAcItem);
+                        allows |= Permission.diff(permissions, denies);
+                    } else {
+                        denyPrivileges |= Permission.diff(privs, allowPrivileges);
+                        int permissions = Permission.calculatePermissions(denyPrivileges, parentDenies, false, isAcItem);
+                        denies |= Permission.diff(permissions, allows);
+                    }
+                }
+            }
+            return new AbstractCompiledPermissions.Result(allows, denies, allowPrivileges, denyPrivileges);
+        }
+    }
+}

Propchange: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/principalbased/ACLProvider.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/principalbased/ACLProvider.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision url