Return-Path: X-Original-To: apmail-jackrabbit-oak-commits-archive@minotaur.apache.org Delivered-To: apmail-jackrabbit-oak-commits-archive@minotaur.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 1FC44D435 for ; Fri, 9 Nov 2012 09:37:26 +0000 (UTC) Received: (qmail 96767 invoked by uid 500); 9 Nov 2012 09:37:25 -0000 Delivered-To: apmail-jackrabbit-oak-commits-archive@jackrabbit.apache.org Received: (qmail 96707 invoked by uid 500); 9 Nov 2012 09:37:24 -0000 Mailing-List: contact oak-commits-help@jackrabbit.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: oak-dev@jackrabbit.apache.org Delivered-To: mailing list oak-commits@jackrabbit.apache.org Received: (qmail 96648 invoked by uid 99); 9 Nov 2012 09:37:21 -0000 Received: from nike.apache.org (HELO nike.apache.org) (192.87.106.230) by apache.org (qpsmtpd/0.29) with ESMTP; Fri, 09 Nov 2012 09:37:21 +0000 X-ASF-Spam-Status: No, hits=-2000.0 required=5.0 tests=ALL_TRUSTED X-Spam-Check-By: apache.org Received: from [140.211.11.4] (HELO eris.apache.org) (140.211.11.4) by apache.org (qpsmtpd/0.29) with ESMTP; Fri, 09 Nov 2012 09:37:15 +0000 Received: from eris.apache.org (localhost [127.0.0.1]) by eris.apache.org (Postfix) with ESMTP id 3867223888E4; Fri, 9 Nov 2012 09:36:53 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r1407399 - in /jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak: security/privilege/ spi/security/privilege/ Date: Fri, 09 Nov 2012 09:36:52 -0000 To: oak-commits@jackrabbit.apache.org From: angela@apache.org X-Mailer: svnmailer-1.0.8-patched Message-Id: <20121109093653.3867223888E4@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org Author: angela Date: Fri Nov 9 09:36:51 2012 New Revision: 1407399 URL: http://svn.apache.org/viewvc?rev=1407399&view=rev Log: OAK-64 : Privilege Management (WIP) Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/privilege/ReadOnlyPrivilegeManager.java Removed: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/privilege/PrivilegeDefinitionProviderImpl.java jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/privilege/PrivilegeDefinitionProvider.java Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/privilege/PrivilegeConfigurationImpl.java jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/privilege/PrivilegeDefinitionReader.java jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/privilege/PrivilegeDefinitionWriter.java jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/privilege/PrivilegeManagerImpl.java jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/privilege/PrivilegeMigrator.java jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/privilege/PrivilegeConfiguration.java Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/privilege/PrivilegeConfigurationImpl.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/privilege/PrivilegeConfigurationImpl.java?rev=1407399&r1=1407398&r2=1407399&view=diff ============================================================================== --- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/privilege/PrivilegeConfigurationImpl.java (original) +++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/privilege/PrivilegeConfigurationImpl.java Fri Nov 9 09:36:51 2012 @@ -28,22 +28,22 @@ import org.apache.jackrabbit.oak.spi.com import org.apache.jackrabbit.oak.spi.lifecycle.RepositoryInitializer; import org.apache.jackrabbit.oak.spi.security.SecurityConfiguration; import org.apache.jackrabbit.oak.spi.security.privilege.PrivilegeConfiguration; -import org.apache.jackrabbit.oak.spi.security.privilege.PrivilegeDefinitionProvider; /** * PrivilegeConfigurationImpl... TODO */ public class PrivilegeConfigurationImpl extends SecurityConfiguration.Default implements PrivilegeConfiguration { + @Nonnull @Override - public PrivilegeDefinitionProvider getPrivilegeDefinitionProvider(ContentSession contentSession, Root root) { - return new PrivilegeDefinitionProviderImpl(contentSession, root); + public PrivilegeManager getPrivilegeManager(Root root, NamePathMapper namePathMapper) { + return new ReadOnlyPrivilegeManager(root, namePathMapper); } @Nonnull @Override public PrivilegeManager getPrivilegeManager(ContentSession contentSession, Root root, NamePathMapper namePathMapper) { - return new PrivilegeManagerImpl(root, getPrivilegeDefinitionProvider(contentSession, root), namePathMapper); + return new PrivilegeManagerImpl(root, namePathMapper, contentSession); } @Nonnull @@ -52,9 +52,10 @@ public class PrivilegeConfigurationImpl return new PrivilegeInitializer(); } + @Nonnull @Override public List getValidatorProviders() { ValidatorProvider vp = new PrivilegeValidatorProvider(); return Collections.singletonList(vp); } -} \ No newline at end of file +} Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/privilege/PrivilegeDefinitionReader.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/privilege/PrivilegeDefinitionReader.java?rev=1407399&r1=1407398&r2=1407399&view=diff ============================================================================== --- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/privilege/PrivilegeDefinitionReader.java (original) +++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/privilege/PrivilegeDefinitionReader.java Fri Nov 9 09:36:51 2012 @@ -16,36 +16,15 @@ */ package org.apache.jackrabbit.oak.security.privilege; -import java.io.IOException; -import java.io.InputStream; -import java.util.ArrayList; import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.List; import java.util.Map; -import java.util.Set; import javax.annotation.CheckForNull; import javax.annotation.Nonnull; -import javax.jcr.NamespaceRegistry; -import javax.jcr.RepositoryException; -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.parsers.ParserConfigurationException; import org.apache.jackrabbit.oak.api.Root; import org.apache.jackrabbit.oak.api.Tree; import org.apache.jackrabbit.oak.spi.security.privilege.PrivilegeDefinition; import org.apache.jackrabbit.oak.util.NodeUtil; -import org.w3c.dom.Attr; -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.NamedNodeMap; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; -import org.xml.sax.InputSource; -import org.xml.sax.SAXException; -import org.xml.sax.helpers.DefaultHandler; import static org.apache.jackrabbit.oak.spi.security.privilege.PrivilegeConstants.PRIVILEGES_PATH; import static org.apache.jackrabbit.oak.spi.security.privilege.PrivilegeConstants.REP_AGGREGATES; @@ -95,177 +74,4 @@ class PrivilegeDefinitionReader { return new PrivilegeDefinitionImpl(name, isAbstract, declAggrNames); } - - /** - * Reads privilege definitions for the specified {@code InputStream}. The - * aim of this method is to provide backwards compatibility with - * custom privilege definitions of Jackrabbit 2.x repositories. The caller - * is in charge of migrating the definitions. - * - * @param customPrivileges - * @param nsRegistry - * @return - * @throws RepositoryException - * @throws IOException - */ - static PrivilegeDefinition[] readCustomDefinitons(InputStream customPrivileges, - NamespaceRegistry nsRegistry) throws RepositoryException, IOException { - Map definitions = new LinkedHashMap(); - InputSource src = new InputSource(customPrivileges); - for (PrivilegeDefinition def : PrivilegeXmlHandler.readDefinitions(src, nsRegistry)) { - String privName = def.getName(); - if (definitions.containsKey(privName)) { - throw new RepositoryException("Duplicate entry for custom privilege with name " + privName.toString()); - } - definitions.put(privName, def); - } - return definitions.values().toArray(new PrivilegeDefinition[definitions.size()]); - } - - //-------------------------------------------------------------------------- - /** - * The {@code PrivilegeXmlHandler} loads privilege definitions from a XML - * document using the following format: - *
-     *  <!DOCTYPE privileges [
-     *  <!ELEMENT privileges (privilege)+>
-     *  <!ELEMENT privilege (contains)+>
-     *  <!ATTLIST privilege abstract (true|false) false>
-     *  <!ATTLIST privilege name NMTOKEN #REQUIRED>
-     *  <!ELEMENT contains EMPTY>
-     *  <!ATTLIST contains name NMTOKEN #REQUIRED>
-     * ]>
-     * 
- */ - private static class PrivilegeXmlHandler { - - private static final String TEXT_XML = "text/xml"; - private static final String APPLICATION_XML = "application/xml"; - - private static final String XML_PRIVILEGES = "privileges"; - private static final String XML_PRIVILEGE = "privilege"; - private static final String XML_CONTAINS = "contains"; - - private static final String ATTR_NAME = "name"; - private static final String ATTR_ABSTRACT = "abstract"; - - private static final String ATTR_XMLNS = "xmlns:"; - - private static DocumentBuilderFactory DOCUMENT_BUILDER_FACTORY = createFactory(); - - private static DocumentBuilderFactory createFactory() { - DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); - factory.setNamespaceAware(true); - factory.setIgnoringComments(false); - factory.setIgnoringElementContentWhitespace(true); - return factory; - } - - private static PrivilegeDefinition[] readDefinitions(InputSource input, - NamespaceRegistry nsRegistry) throws RepositoryException, IOException { - try { - List defs = new ArrayList(); - - DocumentBuilder builder = createDocumentBuilder(); - Document doc = builder.parse(input); - Element root = doc.getDocumentElement(); - if (!XML_PRIVILEGES.equals(root.getNodeName())) { - throw new IllegalArgumentException("root element must be named 'privileges'"); - } - - updateNamespaceMapping(root, nsRegistry); - - NodeList nl = root.getElementsByTagName(XML_PRIVILEGE); - for (int i = 0; i < nl.getLength(); i++) { - Node n = nl.item(i); - PrivilegeDefinition def = parseDefinition(n, nsRegistry); - if (def != null) { - defs.add(def); - } - } - return defs.toArray(new PrivilegeDefinition[defs.size()]); - - } catch (SAXException e) { - throw new RepositoryException(e); - } catch (ParserConfigurationException e) { - throw new RepositoryException(e); - } - } - - /** - * Build a new {@code PrivilegeDefinition} from the given XML node. - * @param n the xml node storing the privilege definition. - * @param nsRegistry - * @return a new PrivilegeDefinition. - * @throws javax.jcr.RepositoryException - */ - private static PrivilegeDefinition parseDefinition(Node n, NamespaceRegistry nsRegistry) throws RepositoryException { - if (n.getNodeType() == Node.ELEMENT_NODE) { - Element elem = (Element) n; - - updateNamespaceMapping(elem, nsRegistry); - - String name = elem.getAttribute(ATTR_NAME); - boolean isAbstract = Boolean.parseBoolean(elem.getAttribute(ATTR_ABSTRACT)); - - Set aggrNames = new HashSet(); - NodeList nodeList = elem.getChildNodes(); - for (int i = 0; i < nodeList.getLength(); i++) { - Node contains = nodeList.item(i); - if (isElement(n) && XML_CONTAINS.equals(contains.getNodeName())) { - String aggrName = ((Element) contains).getAttribute(ATTR_NAME); - if (aggrName != null) { - aggrNames.add(aggrName); - } - } - } - return new PrivilegeDefinitionImpl(name, isAbstract, aggrNames); - } - - // could not parse into privilege definition - return null; - } - - /** - * Create a new {@code DocumentBuilder} - * - * @return a new {@code DocumentBuilder} - * @throws ParserConfigurationException - */ - private static DocumentBuilder createDocumentBuilder() throws ParserConfigurationException { - DocumentBuilder builder = DOCUMENT_BUILDER_FACTORY.newDocumentBuilder(); - builder.setErrorHandler(new DefaultHandler()); - return builder; - } - - /** - * Update the specified nsRegistry mappings with the nsRegistry declarations - * defined by the given XML element. - * - * @param elem - * @param nsRegistry - * @throws javax.jcr.RepositoryException - */ - private static void updateNamespaceMapping(Element elem, NamespaceRegistry nsRegistry) throws RepositoryException { - NamedNodeMap attributes = elem.getAttributes(); - for (int i = 0; i < attributes.getLength(); i++) { - Attr attr = (Attr) attributes.item(i); - if (attr.getName().startsWith(ATTR_XMLNS)) { - String prefix = attr.getName().substring(ATTR_XMLNS.length()); - String uri = attr.getValue(); - nsRegistry.registerNamespace(prefix, uri); - } - } - } - - /** - * Returns {@code true} if the given XML node is an element. - * - * @param n - * @return {@code true} if the given XML node is an element; {@code false} otherwise. - */ - private static boolean isElement(Node n) { - return n.getNodeType() == Node.ELEMENT_NODE; - } - } } \ No newline at end of file Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/privilege/PrivilegeDefinitionWriter.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/privilege/PrivilegeDefinitionWriter.java?rev=1407399&r1=1407398&r2=1407399&view=diff ============================================================================== --- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/privilege/PrivilegeDefinitionWriter.java (original) +++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/privilege/PrivilegeDefinitionWriter.java Fri Nov 9 09:36:51 2012 @@ -43,7 +43,7 @@ class PrivilegeDefinitionWriter implemen writeDefinitions(Collections.singleton(definition)); } - void writeDefinitions(Set definitions) throws RepositoryException { + void writeDefinitions(Iterable definitions) throws RepositoryException { try { // make sure the privileges path is defined Tree privilegesTree = root.getTree(PRIVILEGES_PATH); Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/privilege/PrivilegeManagerImpl.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/privilege/PrivilegeManagerImpl.java?rev=1407399&r1=1407398&r2=1407399&view=diff ============================================================================== --- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/privilege/PrivilegeManagerImpl.java (original) +++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/privilege/PrivilegeManagerImpl.java Fri Nov 9 09:36:51 2012 @@ -16,63 +16,37 @@ */ package org.apache.jackrabbit.oak.security.privilege; -import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.Set; import javax.jcr.InvalidItemStateException; import javax.jcr.NamespaceException; import javax.jcr.RepositoryException; -import javax.jcr.security.AccessControlException; import javax.jcr.security.Privilege; -import org.apache.jackrabbit.api.security.authorization.PrivilegeManager; +import org.apache.jackrabbit.oak.api.ContentSession; import org.apache.jackrabbit.oak.api.Root; import org.apache.jackrabbit.oak.namepath.NamePathMapper; import org.apache.jackrabbit.oak.spi.security.privilege.PrivilegeDefinition; -import org.apache.jackrabbit.oak.spi.security.privilege.PrivilegeDefinitionProvider; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** - * {@code PrivilegeManager} implementation operating on the specified - * {@code PrivilegeDefinitionProvider}. + * {@code PrivilegeManager} implementation reading from and storing privileges + * into the repository. */ -public class PrivilegeManagerImpl implements PrivilegeManager { +public class PrivilegeManagerImpl extends ReadOnlyPrivilegeManager { /** * logger instance */ private static final Logger log = LoggerFactory.getLogger(PrivilegeManagerImpl.class); - private final Root root; - private final NamePathMapper namePathMapper; + private final ContentSession contentSession; - private final PrivilegeDefinitionProvider provider; - - public PrivilegeManagerImpl(Root root, PrivilegeDefinitionProvider provider, NamePathMapper namePathMapper) { - this.root = root; - this.namePathMapper = namePathMapper; - this.provider = provider; - } - - @Override - public Privilege[] getRegisteredPrivileges() throws RepositoryException { - Set privileges = new HashSet(); - for (PrivilegeDefinition def : provider.getPrivilegeDefinitions()) { - privileges.add(new PrivilegeImpl(def)); - } - return privileges.toArray(new Privilege[privileges.size()]); - } - - @Override - public Privilege getPrivilege(String privilegeName) throws RepositoryException { - PrivilegeDefinition def = provider.getPrivilegeDefinition(getOakName(privilegeName)); - if (def == null) { - throw new AccessControlException("No such privilege " + privilegeName); - } else { - return new PrivilegeImpl(def); - } + public PrivilegeManagerImpl(Root root, NamePathMapper namePathMapper, ContentSession contentSession) { + super(root, namePathMapper); + this.contentSession = contentSession; } @Override @@ -89,16 +63,16 @@ public class PrivilegeManagerImpl implem throw new NamespaceException("Invalid privilege name " + privilegeName); } - PrivilegeDefinition def = provider.registerDefinition(oakName, isAbstract, getOakNames(declaredAggregateNames)); - return new PrivilegeImpl(def); + PrivilegeDefinition definition = new PrivilegeDefinitionImpl(oakName, isAbstract, getOakNames(declaredAggregateNames)); + PrivilegeDefinitionWriter writer = new PrivilegeDefinitionWriter(contentSession.getLatestRoot()); + writer.writeDefinition(definition); + + // refresh the current root to make sure the definition is visible + root.refresh(); + return getPrivilege(definition); } //------------------------------------------------------------< private >--- - - private String getOakName(String jcrName) { - return namePathMapper.getOakName(jcrName); - } - private Set getOakNames(String[] jcrNames) throws RepositoryException { Set oakNames; if (jcrNames == null || jcrNames.length == 0) { @@ -115,83 +89,4 @@ public class PrivilegeManagerImpl implem } return oakNames; } - - //-------------------------------------------------------------------------- - /** - * Privilege implementation based on a {@link PrivilegeDefinition}. - */ - private class PrivilegeImpl implements Privilege { - - private final PrivilegeDefinition definition; - - private PrivilegeImpl(PrivilegeDefinition definition) { - this.definition = definition; - } - - //------------------------------------------------------< Privilege >--- - @Override - public String getName() { - return getOakName(definition.getName()); - } - - @Override - public boolean isAbstract() { - return definition.isAbstract(); - } - - @Override - public boolean isAggregate() { - return !definition.getDeclaredAggregateNames().isEmpty(); - } - - @Override - public Privilege[] getDeclaredAggregatePrivileges() { - Set declaredAggregateNames = definition.getDeclaredAggregateNames(); - Set declaredAggregates = new HashSet(declaredAggregateNames.size()); - for (String pName : declaredAggregateNames) { - try { - declaredAggregates.add(getPrivilege(pName)); - } catch (RepositoryException e) { - log.warn("Error while retrieving privilege "+ pName +" contained in " + getName(), e.getMessage()); - } - } - return declaredAggregates.toArray(new Privilege[declaredAggregates.size()]); - } - - @Override - public Privilege[] getAggregatePrivileges() { - Set aggr = new HashSet(); - for (Privilege decl : getDeclaredAggregatePrivileges()) { - aggr.add(decl); - if (decl.isAggregate()) { - // TODO: defensive check to prevent circular aggregation that might occur with inconsistent repositories - aggr.addAll(Arrays.asList(decl.getAggregatePrivileges())); - } - } - return aggr.toArray(new Privilege[aggr.size()]); - } - - //---------------------------------------------------------< Object >--- - @Override - public int hashCode() { - return definition.hashCode(); - } - - @Override - public boolean equals(Object o) { - if (o == this) { - return true; - } - if (o instanceof PrivilegeImpl) { - return definition.equals(((PrivilegeImpl) o).definition); - } else { - return false; - } - } - - @Override - public String toString() { - return "Privilege " + definition.getName(); - } - } } \ No newline at end of file Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/privilege/PrivilegeMigrator.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/privilege/PrivilegeMigrator.java?rev=1407399&r1=1407398&r2=1407399&view=diff ============================================================================== --- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/privilege/PrivilegeMigrator.java (original) +++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/privilege/PrivilegeMigrator.java Fri Nov 9 09:36:51 2012 @@ -18,12 +18,32 @@ package org.apache.jackrabbit.oak.securi import java.io.IOException; import java.io.InputStream; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; import javax.jcr.NamespaceRegistry; import javax.jcr.RepositoryException; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; import org.apache.jackrabbit.oak.api.ContentSession; +import org.apache.jackrabbit.oak.api.Root; +import org.apache.jackrabbit.oak.api.Tree; +import org.apache.jackrabbit.oak.plugins.name.ReadWriteNamespaceRegistry; import org.apache.jackrabbit.oak.spi.security.privilege.PrivilegeDefinition; -import org.apache.jackrabbit.oak.util.TODO; +import org.w3c.dom.Attr; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.DefaultHandler; /** * PrivilegeMigrator is a utility to migrate custom privilege definitions from @@ -37,27 +57,28 @@ public class PrivilegeMigrator { this.contentSession = contentSession; } - /** - * - * @throws RepositoryException - */ public void migrateCustomPrivileges() throws RepositoryException { - PrivilegeDefinitionProviderImpl pr = new PrivilegeDefinitionProviderImpl(contentSession, contentSession.getLatestRoot()); + final Root root = contentSession.getLatestRoot(); + PrivilegeDefinitionWriter writer = new PrivilegeDefinitionWriter(root); InputStream stream = null; - // TODO: order custom privileges such that validation succeeds. // FIXME: user proper path to jr2 custom privileges stored in fs // jr2 used to be: // new FileSystemResource(fs, "/privileges/custom_privileges.xml").getInputStream() if (stream != null) { try { - // TODO: should get a proper namespace registry from somewhere - NamespaceRegistry nsRegistry = - TODO.dummyImplementation().returnValue(null); - PrivilegeDefinition[] custom = PrivilegeDefinitionReader.readCustomDefinitons(stream, nsRegistry); - - for (PrivilegeDefinition def : custom) { - pr.registerDefinition(def.getName(), def.isAbstract(), def.getDeclaredAggregateNames()); - } + NamespaceRegistry nsRegistry = new ReadWriteNamespaceRegistry() { + @Override + protected Root getWriteRoot() { + return root; + } + + @Override + protected Tree getReadTree() { + return root.getTree("/"); + } + }; + Iterable custom = readCustomDefinitons(stream, nsRegistry); + writer.writeDefinitions(custom); } catch (IOException e) { throw new RepositoryException(e); } finally { @@ -69,4 +90,177 @@ public class PrivilegeMigrator { } } } + + /** + * Reads privilege definitions for the specified {@code InputStream}. The + * aim of this method is to provide backwards compatibility with + * custom privilege definitions of Jackrabbit 2.x repositories. The caller + * is in charge of migrating the definitions. + * + * @param customPrivileges + * @param nsRegistry + * @return + * @throws RepositoryException + * @throws IOException + */ + private static Iterable readCustomDefinitons(InputStream customPrivileges, + NamespaceRegistry nsRegistry) throws RepositoryException, IOException { + Map definitions = new LinkedHashMap(); + InputSource src = new InputSource(customPrivileges); + for (PrivilegeDefinition def : PrivilegeXmlHandler.readDefinitions(src, nsRegistry)) { + String privName = def.getName(); + if (definitions.containsKey(privName)) { + throw new RepositoryException("Duplicate entry for custom privilege with name " + privName.toString()); + } + definitions.put(privName, def); + } + return definitions.values(); + } + + //-------------------------------------------------------------------------- + /** + * The {@code PrivilegeXmlHandler} loads privilege definitions from a XML + * document using the following format: + *
+     *  <!DOCTYPE privileges [
+     *  <!ELEMENT privileges (privilege)+>
+     *  <!ELEMENT privilege (contains)+>
+     *  <!ATTLIST privilege abstract (true|false) false>
+     *  <!ATTLIST privilege name NMTOKEN #REQUIRED>
+     *  <!ELEMENT contains EMPTY>
+     *  <!ATTLIST contains name NMTOKEN #REQUIRED>
+     * ]>
+     * 
+ */ + private static class PrivilegeXmlHandler { + + private static final String TEXT_XML = "text/xml"; + private static final String APPLICATION_XML = "application/xml"; + + private static final String XML_PRIVILEGES = "privileges"; + private static final String XML_PRIVILEGE = "privilege"; + private static final String XML_CONTAINS = "contains"; + + private static final String ATTR_NAME = "name"; + private static final String ATTR_ABSTRACT = "abstract"; + + private static final String ATTR_XMLNS = "xmlns:"; + + private static DocumentBuilderFactory DOCUMENT_BUILDER_FACTORY = createFactory(); + + private static DocumentBuilderFactory createFactory() { + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + factory.setNamespaceAware(true); + factory.setIgnoringComments(false); + factory.setIgnoringElementContentWhitespace(true); + return factory; + } + + private static PrivilegeDefinition[] readDefinitions(InputSource input, + NamespaceRegistry nsRegistry) throws RepositoryException, IOException { + try { + List defs = new ArrayList(); + + DocumentBuilder builder = createDocumentBuilder(); + Document doc = builder.parse(input); + Element root = doc.getDocumentElement(); + if (!XML_PRIVILEGES.equals(root.getNodeName())) { + throw new IllegalArgumentException("root element must be named 'privileges'"); + } + + updateNamespaceMapping(root, nsRegistry); + + NodeList nl = root.getElementsByTagName(XML_PRIVILEGE); + for (int i = 0; i < nl.getLength(); i++) { + Node n = nl.item(i); + PrivilegeDefinition def = parseDefinition(n, nsRegistry); + if (def != null) { + defs.add(def); + } + } + return defs.toArray(new PrivilegeDefinition[defs.size()]); + + } catch (SAXException e) { + throw new RepositoryException(e); + } catch (ParserConfigurationException e) { + throw new RepositoryException(e); + } + } + + /** + * Build a new {@code PrivilegeDefinition} from the given XML node. + * @param n the xml node storing the privilege definition. + * @param nsRegistry + * @return a new PrivilegeDefinition. + * @throws javax.jcr.RepositoryException + */ + private static PrivilegeDefinition parseDefinition(Node n, NamespaceRegistry nsRegistry) throws RepositoryException { + if (n.getNodeType() == Node.ELEMENT_NODE) { + Element elem = (Element) n; + + updateNamespaceMapping(elem, nsRegistry); + + String name = elem.getAttribute(ATTR_NAME); + boolean isAbstract = Boolean.parseBoolean(elem.getAttribute(ATTR_ABSTRACT)); + + Set aggrNames = new HashSet(); + NodeList nodeList = elem.getChildNodes(); + for (int i = 0; i < nodeList.getLength(); i++) { + Node contains = nodeList.item(i); + if (isElement(n) && XML_CONTAINS.equals(contains.getNodeName())) { + String aggrName = ((Element) contains).getAttribute(ATTR_NAME); + if (aggrName != null) { + aggrNames.add(aggrName); + } + } + } + return new PrivilegeDefinitionImpl(name, isAbstract, aggrNames); + } + + // could not parse into privilege definition + return null; + } + + /** + * Create a new {@code DocumentBuilder} + * + * @return a new {@code DocumentBuilder} + * @throws ParserConfigurationException + */ + private static DocumentBuilder createDocumentBuilder() throws ParserConfigurationException { + DocumentBuilder builder = DOCUMENT_BUILDER_FACTORY.newDocumentBuilder(); + builder.setErrorHandler(new DefaultHandler()); + return builder; + } + + /** + * Update the specified nsRegistry mappings with the nsRegistry declarations + * defined by the given XML element. + * + * @param elem + * @param nsRegistry + * @throws javax.jcr.RepositoryException + */ + private static void updateNamespaceMapping(Element elem, NamespaceRegistry nsRegistry) throws RepositoryException { + NamedNodeMap attributes = elem.getAttributes(); + for (int i = 0; i < attributes.getLength(); i++) { + Attr attr = (Attr) attributes.item(i); + if (attr.getName().startsWith(ATTR_XMLNS)) { + String prefix = attr.getName().substring(ATTR_XMLNS.length()); + String uri = attr.getValue(); + nsRegistry.registerNamespace(prefix, uri); + } + } + } + + /** + * Returns {@code true} if the given XML node is an element. + * + * @param n + * @return {@code true} if the given XML node is an element; {@code false} otherwise. + */ + private static boolean isElement(Node n) { + return n.getNodeType() == Node.ELEMENT_NODE; + } + } } \ No newline at end of file Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/privilege/ReadOnlyPrivilegeManager.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/privilege/ReadOnlyPrivilegeManager.java?rev=1407399&view=auto ============================================================================== --- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/privilege/ReadOnlyPrivilegeManager.java (added) +++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/privilege/ReadOnlyPrivilegeManager.java Fri Nov 9 09:36:51 2012 @@ -0,0 +1,204 @@ +/* + * 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.oak.security.privilege; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import javax.annotation.CheckForNull; +import javax.annotation.Nonnull; +import javax.jcr.RepositoryException; +import javax.jcr.UnsupportedRepositoryOperationException; +import javax.jcr.security.AccessControlException; +import javax.jcr.security.Privilege; + +import org.apache.jackrabbit.api.security.authorization.PrivilegeManager; +import org.apache.jackrabbit.oak.api.Root; +import org.apache.jackrabbit.oak.namepath.NamePathMapper; +import org.apache.jackrabbit.oak.spi.security.privilege.PrivilegeConstants; +import org.apache.jackrabbit.oak.spi.security.privilege.PrivilegeDefinition; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Read-Only {@code PrivilegeManager} implementation reading privilege definitions + * from the repository content. + * + * TODO: review if jcr:all should be present in the content as well (updated in the privilege commit validator) + */ +public class ReadOnlyPrivilegeManager implements PrivilegeManager { + + /** + * logger instance + */ + private static final Logger log = LoggerFactory.getLogger(ReadOnlyPrivilegeManager.class); + + final Root root; + final NamePathMapper namePathMapper; + + public ReadOnlyPrivilegeManager(Root root, NamePathMapper namePathMapper) { + this.root = root; + this.namePathMapper = namePathMapper; + } + + @Override + public Privilege[] getRegisteredPrivileges() throws RepositoryException { + Set privileges = new HashSet(); + for (PrivilegeDefinition def : getPrivilegeDefinitions()) { + privileges.add(getPrivilege(def)); + } + return privileges.toArray(new Privilege[privileges.size()]); + } + + @Override + public Privilege getPrivilege(String privilegeName) throws RepositoryException { + PrivilegeDefinition def = getPrivilegeDefinition(getOakName(privilegeName)); + if (def == null) { + throw new AccessControlException("No such privilege " + privilegeName); + } else { + return getPrivilege(def); + } + } + + @Override + public Privilege registerPrivilege(String privilegeName, boolean isAbstract, + String[] declaredAggregateNames) throws RepositoryException { + throw new UnsupportedRepositoryOperationException("ReadOnly PrivilegeManager: Privilege registration not supported"); + } + + //------------------------------------------------------------< private >--- + @CheckForNull + String getOakName(String jcrName) { + return namePathMapper.getOakName(jcrName); + } + + @Nonnull + Privilege getPrivilege(PrivilegeDefinition definition) { + return new PrivilegeImpl(definition); + } + + @Nonnull + private PrivilegeDefinition[] getPrivilegeDefinitions() { + Map definitions = getReader().readDefinitions(); + definitions.put(PrivilegeConstants.JCR_ALL, getJcrAllDefinition(definitions)); + return definitions.values().toArray(new PrivilegeDefinition[definitions.size()]); + } + + @CheckForNull + private PrivilegeDefinition getPrivilegeDefinition(String oakName) { + if (PrivilegeConstants.JCR_ALL.equals(oakName)) { + return getJcrAllDefinition(getReader().readDefinitions()); + } else { + return getReader().readDefinition(oakName); + } + } + + @Nonnull + private PrivilegeDefinitionReader getReader() { + return new PrivilegeDefinitionReader(root); + } + + @Nonnull + private static PrivilegeDefinition getJcrAllDefinition(Map definitions) { + return new PrivilegeDefinitionImpl(PrivilegeConstants.JCR_ALL, false, definitions.keySet()); + } + + //-------------------------------------------------------------------------- + /** + * Privilege implementation based on a {@link org.apache.jackrabbit.oak.spi.security.privilege.PrivilegeDefinition}. + */ + private class PrivilegeImpl implements Privilege { + + private final PrivilegeDefinition definition; + + private PrivilegeImpl(PrivilegeDefinition definition) { + this.definition = definition; + } + + //------------------------------------------------------< Privilege >--- + @Override + public String getName() { + return namePathMapper.getJcrName(definition.getName()); + } + + @Override + public boolean isAbstract() { + return definition.isAbstract(); + } + + @Override + public boolean isAggregate() { + return !definition.getDeclaredAggregateNames().isEmpty(); + } + + @Override + public Privilege[] getDeclaredAggregatePrivileges() { + Set declaredAggregateNames = definition.getDeclaredAggregateNames(); + Set declaredAggregates = new HashSet(declaredAggregateNames.size()); + for (String oakName : declaredAggregateNames) { + if (oakName.equals(definition.getName())) { + log.warn("Found cyclic privilege aggregation -> ignore declared aggregate " + oakName); + continue; + } + PrivilegeDefinition def = getPrivilegeDefinition(oakName); + if (def != null) { + declaredAggregates.add(getPrivilege(def)); + } else { + log.warn("Invalid privilege '{}' in declared aggregates of '{}'", oakName, getName()); + } + } + return declaredAggregates.toArray(new Privilege[declaredAggregates.size()]); + } + + @Override + public Privilege[] getAggregatePrivileges() { + Set aggr = new HashSet(); + for (Privilege decl : getDeclaredAggregatePrivileges()) { + aggr.add(decl); + if (decl.isAggregate()) { + // TODO: defensive check to prevent circular aggregation that might occur with inconsistent repositories + aggr.addAll(Arrays.asList(decl.getAggregatePrivileges())); + } + } + return aggr.toArray(new Privilege[aggr.size()]); + } + + //---------------------------------------------------------< Object >--- + @Override + public int hashCode() { + return definition.hashCode(); + } + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + if (o instanceof PrivilegeImpl) { + return definition.equals(((PrivilegeImpl) o).definition); + } else { + return false; + } + } + + @Override + public String toString() { + return "Privilege " + definition.getName(); + } + } +} Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/privilege/PrivilegeConfiguration.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/privilege/PrivilegeConfiguration.java?rev=1407399&r1=1407398&r2=1407399&view=diff ============================================================================== --- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/privilege/PrivilegeConfiguration.java (original) +++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/privilege/PrivilegeConfiguration.java Fri Nov 9 09:36:51 2012 @@ -30,8 +30,8 @@ import org.apache.jackrabbit.oak.spi.sec public interface PrivilegeConfiguration extends SecurityConfiguration { @Nonnull - PrivilegeDefinitionProvider getPrivilegeDefinitionProvider(ContentSession contentSession, Root root); + PrivilegeManager getPrivilegeManager(Root root, NamePathMapper namePathMapper); @Nonnull PrivilegeManager getPrivilegeManager(ContentSession contentSession, Root root, NamePathMapper namePathMapper); -} \ No newline at end of file +}