Return-Path: Delivered-To: apmail-jackrabbit-commits-archive@www.apache.org Received: (qmail 15664 invoked from network); 20 Aug 2010 09:35:35 -0000 Received: from unknown (HELO mail.apache.org) (140.211.11.3) by 140.211.11.9 with SMTP; 20 Aug 2010 09:35:35 -0000 Received: (qmail 67782 invoked by uid 500); 20 Aug 2010 09:35:35 -0000 Delivered-To: apmail-jackrabbit-commits-archive@jackrabbit.apache.org Received: (qmail 67693 invoked by uid 500); 20 Aug 2010 09:35:33 -0000 Mailing-List: contact commits-help@jackrabbit.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@jackrabbit.apache.org Delivered-To: mailing list commits@jackrabbit.apache.org Received: (qmail 67686 invoked by uid 99); 20 Aug 2010 09:35:32 -0000 Received: from Unknown (HELO nike.apache.org) (192.87.106.230) by apache.org (qpsmtpd/0.29) with ESMTP; Fri, 20 Aug 2010 09:35:32 +0000 X-ASF-Spam-Status: No, hits=-2000.0 required=10.0 tests=ALL_TRUSTED X-Spam-Check-By: apache.org Received: from [140.211.11.4] (HELO eris.apache.org) (140.211.11.4) by apache.org (qpsmtpd/0.29) with ESMTP; Fri, 20 Aug 2010 09:35:11 +0000 Received: by eris.apache.org (Postfix, from userid 65534) id 94004238899C; Fri, 20 Aug 2010 09:33:51 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r987435 - in /jackrabbit/trunk: jackrabbit-api/src/main/java/org/apache/jackrabbit/api/security/user/ jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/ jackrabbit-core/src/test/java/org/apache/jackrabbit/api/security/u... Date: Fri, 20 Aug 2010 09:33:51 -0000 To: commits@jackrabbit.apache.org From: angela@apache.org X-Mailer: svnmailer-1.0.8 Message-Id: <20100820093351.94004238899C@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org Author: angela Date: Fri Aug 20 09:33:50 2010 New Revision: 987435 URL: http://svn.apache.org/viewvc?rev=987435&view=rev Log: JCR-2620 - Authorizable#getProperty and #setProperty should deal with relativePath Added: jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/commons/iterator/FilteringNodeIterator.java (with props) Modified: jackrabbit/trunk/jackrabbit-api/src/main/java/org/apache/jackrabbit/api/security/user/Authorizable.java jackrabbit/trunk/jackrabbit-api/src/main/java/org/apache/jackrabbit/api/security/user/UserManager.java jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/AuthorizableImpl.java jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/IndexNodeResolver.java jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/NodeResolver.java jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/TraversingNodeResolver.java jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/UserManagerImpl.java jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/api/security/user/AuthorizableTest.java jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authentication/SimpleCredentialsAuthenticationTest.java jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/user/AuthorizableImplTest.java jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/user/IndexNodeResolverTest.java jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/user/NodeResolverTest.java jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/user/TraversingNodeResolverTest.java jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/user/UserImporterTest.java Modified: jackrabbit/trunk/jackrabbit-api/src/main/java/org/apache/jackrabbit/api/security/user/Authorizable.java URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-api/src/main/java/org/apache/jackrabbit/api/security/user/Authorizable.java?rev=987435&r1=987434&r2=987435&view=diff ============================================================================== --- jackrabbit/trunk/jackrabbit-api/src/main/java/org/apache/jackrabbit/api/security/user/Authorizable.java (original) +++ jackrabbit/trunk/jackrabbit-api/src/main/java/org/apache/jackrabbit/api/security/user/Authorizable.java Fri Aug 20 09:33:50 2010 @@ -104,61 +104,76 @@ public interface Authorizable { void remove() throws RepositoryException; /** - * Returns the names of properties present with this Authorizable. + * Returns the names of properties present with this + * Authorizable not taking possible relative paths into consideration. + * Same as {@link #getPropertyNames(String)} where the specified string + * is ".". * * @return names of properties. * @throws RepositoryException If an error occurs. - * @see #getProperty(String) + * @see #getProperty(String) where the specified relative path is simply an + * name. * @see #hasProperty(String) */ Iterator getPropertyNames() throws RepositoryException; /** + * Returns the names of properties present with this + * Authorizable at the specified relative path. + * + * @return names of properties. + * @throws RepositoryException If an error occurs. + * @see #getProperty(String) + * @see #hasProperty(String) + */ + Iterator getPropertyNames(String relPath) throws RepositoryException; + + /** * Tests if a the property with specified name exists. * - * @param name The name of the property to be tested. + * @param relPath The relative path to the property to be tested. * @return true if a property with the given name exists. * @throws RepositoryException If an error occurs. * @see #getProperty(String) */ - boolean hasProperty(String name) throws RepositoryException; + boolean hasProperty(String relPath) throws RepositoryException; /** * Set an arbitrary property to this Authorizable. * - * @param name The name of the property to be added or modified. + * @param relPath The relative path of the property to be added or modified. * @param value The desired value. * @throws RepositoryException If the specified property could not be set. */ - void setProperty(String name, Value value) throws RepositoryException; + void setProperty(String relPath, Value value) throws RepositoryException; /** * Set an arbitrary property to this Authorizable. * - * @param name The name of the property to be added or modified. + * @param relPath The relative path of the property to be added or modified. * @param value The desired property values. * @throws RepositoryException If the specified property could not be set. */ - void setProperty(String name, Value[] value) throws RepositoryException; + void setProperty(String relPath, Value[] value) throws RepositoryException; /** * Returns the values for the properties with the specified name or * null. * - * @param name The name of the property to be retrieved. + * @param relPath Relative path of the property to be retrieved. * @return value of the property with the given name or null * if no such property exists. * @throws RepositoryException If an error occurs. */ - Value[] getProperty(String name) throws RepositoryException; + Value[] getProperty(String relPath) throws RepositoryException; /** * Removes the property with the given name. * - * @param name The name of the property to be removed. - * @return true If the property with the specified name was successfully + * @param relPath Relative path (or name) of the property to be removed. + * @return true If the property at the specified relPath was successfully * removed; false if no such property was present. * @throws RepositoryException If an error occurs. */ - boolean removeProperty(String name) throws RepositoryException; + boolean removeProperty(String relPath) throws RepositoryException; } Modified: jackrabbit/trunk/jackrabbit-api/src/main/java/org/apache/jackrabbit/api/security/user/UserManager.java URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-api/src/main/java/org/apache/jackrabbit/api/security/user/UserManager.java?rev=987435&r1=987434&r2=987435&view=diff ============================================================================== --- jackrabbit/trunk/jackrabbit-api/src/main/java/org/apache/jackrabbit/api/security/user/UserManager.java (original) +++ jackrabbit/trunk/jackrabbit-api/src/main/java/org/apache/jackrabbit/api/security/user/UserManager.java Fri Aug 20 09:33:50 2010 @@ -67,27 +67,37 @@ public interface UserManager { Authorizable getAuthorizable(Principal principal) throws RepositoryException; /** - * Returns all Authorizables that have - * {@link Authorizable#getProperty(String) property} with the given name and - * that Property equals the given value. + * Returns all Authorizables that have a + * {@link Authorizable#getProperty(String) property} with the given relative + * path (or name) that matches the specified value. + *

+ * If a relative path with more than one segment is specified only properties + * exactly matching that patch will be returned. If, however, a name is + * specified all properties that may be retrieved using + * {@link Authorizable#getProperty(String)} will be searched for a match. * - * @param propertyName + * @param relPath A relative property path or name. * @param value * @return All Authorizables that have a property with the given * name exactly matching the given value. * @throws RepositoryException If an error occurs. * @see Authorizable#getProperty(String) */ - Iterator findAuthorizables(String propertyName, String value) throws RepositoryException; + Iterator findAuthorizables(String relPath, String value) throws RepositoryException; /** - * Returns all Authorizables that have - * {@link Authorizable#getProperty(String) property} with the given name and - * that Property equals the given value. In contrast to + * Returns all Authorizables that have a + * {@link Authorizable#getProperty(String) property} with the given relative + * path (or name) that matches the specified value. In contrast to * {@link #findAuthorizables(String, String)} the type of authorizable is * respected while executing the search. + *

+ * If a relative path with more than one segment is specified only properties + * exactly matching that path will be returned. If, however, a name is + * specified all properties that may be retrieved using + * {@link Authorizable#getProperty(String)} will be searched for a match. * - * @param propertyName + * @param relPath A relative property path or name. * @param value * @param searchType Any of the following constants: *

    @@ -98,7 +108,7 @@ public interface UserManager { * @return An iterator of Authorizable. * @throws RepositoryException If an error occurs. */ - Iterator findAuthorizables(String propertyName, String value, int searchType) throws RepositoryException; + Iterator findAuthorizables(String relPath, String value, int searchType) throws RepositoryException; /** * Creates an User for the given userID / password pair; neither of the Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/AuthorizableImpl.java URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/AuthorizableImpl.java?rev=987435&r1=987434&r2=987435&view=diff ============================================================================== --- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/AuthorizableImpl.java (original) +++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/AuthorizableImpl.java Fri Aug 20 09:33:50 2010 @@ -30,6 +30,7 @@ import org.apache.jackrabbit.util.Text; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import javax.jcr.Node; import javax.jcr.Property; import javax.jcr.PropertyIterator; import javax.jcr.RepositoryException; @@ -100,7 +101,7 @@ abstract class AuthorizableImpl implemen List l = new ArrayList(); for (PropertyIterator it = node.getProperties(); it.hasNext();) { Property prop = it.nextProperty(); - if (isAuthorizableProperty(prop)) { + if (isAuthorizableProperty(prop, false)) { l.add(prop.getName()); } } @@ -108,20 +109,42 @@ abstract class AuthorizableImpl implemen } /** + * @see Authorizable#getPropertyNames(String) + */ + public Iterator getPropertyNames(String relPath) throws RepositoryException { + Node n = node.getNode(relPath); + if (n.isSame(node)) { + // same as #getPropertyNames() + return getPropertyNames(); + } else if (Text.isDescendant(node.getPath(), n.getPath())) { + List l = new ArrayList(); + for (PropertyIterator it = n.getProperties(); it.hasNext();) { + Property prop = it.nextProperty(); + if (isAuthorizableProperty(prop, false)) { + l.add(prop.getName()); + } + } + return l.iterator(); + } else { + throw new IllegalArgumentException("Relative path " + relPath + " refers to items outside of scope of authorizable " + getID()); + } + } + + /** * @see #getProperty(String) */ - public boolean hasProperty(String name) throws RepositoryException { - return node.hasProperty(name) && isAuthorizableProperty(node.getProperty(name)); + public boolean hasProperty(String relPath) throws RepositoryException { + return node.hasProperty(relPath) && isAuthorizableProperty(node.getProperty(relPath), true); } /** * @see #hasProperty(String) * @see Authorizable#getProperty(String) */ - public Value[] getProperty(String name) throws RepositoryException { - if (hasProperty(name)) { - Property prop = node.getProperty(name); - if (isAuthorizableProperty(prop)) { + public Value[] getProperty(String relPath) throws RepositoryException { + if (node.hasProperty(relPath)) { + Property prop = node.getProperty(relPath); + if (isAuthorizableProperty(prop, true)) { if (prop.isMultiple()) { return prop.getValues(); } else { @@ -136,26 +159,29 @@ abstract class AuthorizableImpl implemen * Sets the Value for the given name. If a value existed, it is replaced, * if not it is created. * - * @param name The property name. + * @param relPath The relative path to the property or the property name. * @param value The property value. * @throws RepositoryException If the specified name defines a property * that needs to be modified by this user API or setting the corresponding * JCR property fails. * @see Authorizable#setProperty(String, Value) */ - public synchronized void setProperty(String name, Value value) throws RepositoryException { + public synchronized void setProperty(String relPath, Value value) throws RepositoryException { + String name = Text.getName(relPath); + String intermediate = (relPath.equals(name)) ? null : Text.getRelativeParent(relPath, 1); checkProtectedProperty(name); try { + Node n = getOrCreateTargetNode(intermediate); // check if the property has already been created as multi valued // property before -> in this case remove in order to avoid // ValueFormatException. - if (node.hasProperty(name)) { - Property p = node.getProperty(name); + if (n.hasProperty(name)) { + Property p = n.getProperty(name); if (p.isMultiple()) { p.remove(); } } - node.setProperty(name, value); + n.setProperty(name, value); if (userManager.isAutoSave()) { node.save(); } @@ -170,26 +196,29 @@ abstract class AuthorizableImpl implemen * Sets the Value[] for the given name. If a value existed, it is replaced, * if not it is created. * - * @param name The property name. + * @param relPath The relative path to the property or the property name. * @param values The property values. * @throws RepositoryException If the specified name defines a property * that needs to be modified by this user API or setting the corresponding * JCR property fails. * @see Authorizable#setProperty(String, Value[]) */ - public synchronized void setProperty(String name, Value[] values) throws RepositoryException { + public synchronized void setProperty(String relPath, Value[] values) throws RepositoryException { + String name = Text.getName(relPath); + String intermediate = (relPath.equals(name)) ? null : Text.getRelativeParent(relPath, 1); checkProtectedProperty(name); try { + Node n = getOrCreateTargetNode(intermediate); // check if the property has already been created as single valued // property before -> in this case remove in order to avoid // ValueFormatException. - if (node.hasProperty(name)) { - Property p = node.getProperty(name); + if (n.hasProperty(name)) { + Property p = n.getProperty(name); if (!p.isMultiple()) { p.remove(); } } - node.setProperty(name, values); + n.setProperty(name, values); if (userManager.isAutoSave()) { node.save(); } @@ -203,26 +232,24 @@ abstract class AuthorizableImpl implemen /** * @see Authorizable#removeProperty(String) */ - public synchronized boolean removeProperty(String name) throws RepositoryException { + public synchronized boolean removeProperty(String relPath) throws RepositoryException { + String name = Text.getName(relPath); checkProtectedProperty(name); try { - if (node.hasProperty(name)) { - // 'node' is protected -> use setValue instead of Property.remove() - Property p = node.getProperty(name); - if (p.isMultiple()) { - p.setValue((Value[]) null); - } else { - p.setValue((Value) null); - } - if (userManager.isAutoSave()) { - node.save(); + if (node.hasProperty(relPath)) { + Property p = node.getProperty(relPath); + if (isAuthorizableProperty(p, true)) { + p.remove(); + if (userManager.isAutoSave()) { + node.save(); + } + return true; } - return true; - } else { - return false; } + // no such property or wasn't a property of this authorizable. + return false; } catch (RepositoryException e) { - log.warn("Failed to remove Property " + name + " from " + this, e); + log.warn("Failed to remove Property " + relPath + " from " + this, e); node.refresh(false); throw e; } @@ -335,21 +362,34 @@ abstract class AuthorizableImpl implemen /** * Returns true if the given property of the authorizable node is one of the - * non-protected properties defined by the rep:authorizable. + * non-protected properties defined by the rep:Authorizable node type or a + * some other descendant of the authorizable node. * * @param prop Property to be tested. + * @param verifyAncestor If true the property is tested to be a descendant + * of the node of this authorizable; otherwise it is expected that this + * test has been executed by the caller. * @return true if the given property is defined * by the rep:authorizable node type or one of it's sub-node types; * false otherwise. * @throws RepositoryException If the property definition cannot be retrieved. */ - private static boolean isAuthorizableProperty(Property prop) throws RepositoryException { + private boolean isAuthorizableProperty(Property prop, boolean verifyAncestor) throws RepositoryException { + if (verifyAncestor && !Text.isDescendant(node.getPath(), prop.getPath())) { + log.debug("Attempt to access property outside of authorizable scope."); + return false; + } + PropertyDefinition def = prop.getDefinition(); if (def.isProtected()) { return false; - } else { + } else if (node.isSame(prop.getParent())) { NodeTypeImpl declaringNt = (NodeTypeImpl) prop.getDefinition().getDeclaringNodeType(); return declaringNt.isNodeType(UserConstants.NT_REP_AUTHORIZABLE); + } else { + // another non-protected property somewhere in the subtree of this + // authorizable node -> is a property that can be set using #setProperty. + return true; } } @@ -394,6 +434,37 @@ abstract class AuthorizableImpl implemen } } + /** + * + * @param relPath + * @return + * @throws RepositoryException + */ + private Node getOrCreateTargetNode(String relPath) throws RepositoryException { + Node n; + if (relPath != null) { + if (node.hasNode(relPath)) { + n = node.getNode(relPath); + } else { + n = node; + for (String segment : Text.explode(relPath, '/')) { + if (n.hasNode(segment)) { + n = n.getNode(segment); + } else { + n = n.addNode(segment); + } + } + } + if (!Text.isDescendantOrEqual(node.getPath(), n.getPath())) { + node.refresh(false); + throw new RepositoryException("Relative path " + relPath + " outside of scope of " + this); + } + } else { + n = node; + } + return n; + } + //-------------------------------------------------------------------------- /** * Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/IndexNodeResolver.java URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/IndexNodeResolver.java?rev=987435&r1=987434&r2=987435&view=diff ============================================================================== --- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/IndexNodeResolver.java (original) +++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/IndexNodeResolver.java Fri Aug 20 09:33:50 2010 @@ -16,9 +16,14 @@ */ package org.apache.jackrabbit.core.security.user; +import org.apache.jackrabbit.commons.iterator.FilteringNodeIterator; +import org.apache.jackrabbit.core.NodeImpl; 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.ISO9075; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import javax.jcr.Node; import javax.jcr.NodeIterator; @@ -27,6 +32,7 @@ import javax.jcr.Session; import javax.jcr.query.Query; import javax.jcr.query.QueryManager; import java.util.Collections; +import java.util.HashSet; import java.util.Set; /** @@ -34,6 +40,8 @@ import java.util.Set; */ class IndexNodeResolver extends NodeResolver { + private static Logger log = LoggerFactory.getLogger(IndexNodeResolver.class); + private final QueryManager queryManager; IndexNodeResolver(Session session, NamePathResolver resolver) throws RepositoryException { @@ -48,6 +56,7 @@ class IndexNodeResolver extends NodeReso @Override public Node findNode(Name nodeName, Name ntName) throws RepositoryException { Query query = buildQuery(nodeName, ntName); + query.setLimit(1); NodeIterator res = query.execute().getNodes(); if (res.hasNext()) { return res.nextNode(); @@ -69,9 +78,8 @@ class IndexNodeResolver extends NodeReso } /** - * Search nodes. Take the arguments as search criteria. - * The queried value has to be a string fragment of one of the Properties - * contained in the given set. And the node have to be of a requested nodetype + * Search authorizable nodes of the specified node type having the specified + * properties with the specified value. * * @param propertyNames * @param value @@ -87,6 +95,20 @@ class IndexNodeResolver extends NodeReso return query.execute().getNodes(); } + @Override + public NodeIterator findNodes(Path relPath, String value, int authorizableType, boolean exact, long maxSize) throws RepositoryException { + Query query; + if (relPath.getLength() == 1) { + Set names = Collections.singleton(relPath.getNameElement().getName()); + // search without nt-restriction in order not to limit the query to the + // authorizable nodes and filter non-matching results later. + query = buildQuery(value, names, null, exact, maxSize, getSearchRoot(authorizableType)); + } else { + query = buildQuery(value, relPath, exact, maxSize, getSearchRoot(authorizableType)); + } + return new ResultFilteringNodeIterator(query.execute().getNodes(), getAuthorizableTypePredicate(authorizableType, false)); + } + //-------------------------------------------------------------------------- /** * @@ -95,8 +117,7 @@ class IndexNodeResolver extends NodeReso * @return * @throws RepositoryException */ - private Query buildQuery(Name nodeName, Name ntName) - throws RepositoryException { + private Query buildQuery(Name nodeName, Name ntName) throws RepositoryException { StringBuilder stmt = new StringBuilder("/jcr:root"); stmt.append(getSearchRoot(ntName)); stmt.append("//element("); @@ -108,25 +129,44 @@ class IndexNodeResolver extends NodeReso } /** + * + * @param value + * @param props + * @param ntName + * @param exact + * @param maxSize + * @return + * @throws RepositoryException + */ + private Query buildQuery(String value, Set props, Name ntName, + boolean exact, long maxSize) throws RepositoryException { + String searchRoot = getSearchRoot(ntName); + return buildQuery(value, props, ntName, exact, maxSize, searchRoot); + } + + /** * * @param value * @param props * @param ntName * @param exact - * @param maxSize Currently ignored! + * @param maxSize * @return * @throws RepositoryException */ private Query buildQuery(String value, Set props, Name ntName, - boolean exact, long maxSize) - throws RepositoryException { + boolean exact, long maxSize, String searchRoot) throws RepositoryException { StringBuilder stmt = new StringBuilder("/jcr:root"); - String searchRoot = getSearchRoot(ntName); if (!"/".equals(searchRoot)) { stmt.append(searchRoot); } - stmt.append("//element(*,"); - stmt.append(getNamePathResolver().getJCRName(ntName)); + + if (ntName != null) { + stmt.append("//element(*,"); + stmt.append(getNamePathResolver().getJCRName(ntName)); + } else { + stmt.append("//element(*"); + } if (value == null) { stmt.append(")"); @@ -157,6 +197,47 @@ class IndexNodeResolver extends NodeReso return q; } + /** + * + * @param value + * @param relPath + * @param exact + * @param maxSize + * @return + * @throws RepositoryException + */ + private Query buildQuery(String value, Path relPath, boolean exact, long maxSize, String searchRoot) + throws RepositoryException { + StringBuilder stmt = new StringBuilder("/jcr:root"); + if (!"/".equals(searchRoot)) { + stmt.append(searchRoot); + } + + String p = getNamePathResolver().getJCRPath(relPath.getAncestor(1)); + stmt.append("//").append(p); + + if (value != null) { + stmt.append("["); + Name prop = relPath.getNameElement().getName(); + stmt.append((exact) ? "@" : "jcr:like(@"); + String pName = getNamePathResolver().getJCRName(prop); + stmt.append(ISO9075.encode(pName)); + if (exact) { + stmt.append("='"); + stmt.append(value.replaceAll("'", "''")); + stmt.append("'"); + } else { + stmt.append(",'%"); + stmt.append(escapeForQuery(value)); + stmt.append("%')"); + } + stmt.append("]"); + } + Query q = queryManager.createQuery(stmt.toString(), Query.XPATH); + q.setLimit(maxSize); + return q; + } + private static String escapeForQuery(String value) { StringBuilder ret = new StringBuilder(); for (int i = 0; i < value.length(); i++) { @@ -171,4 +252,38 @@ class IndexNodeResolver extends NodeReso } return ret.toString(); } + + //-------------------------------------------------------------------------- + /** + * + */ + private class ResultFilteringNodeIterator extends FilteringNodeIterator { + + private Set authorizableIDs; + + private ResultFilteringNodeIterator(NodeIterator base, AuthorizableTypePredicate filter) { + super(base, filter); + } + + @Override + protected Node seekNext() { + if (authorizableIDs == null) { + authorizableIDs = new HashSet(); + } + Node n = null; + while (n == null && base.hasNext()) { + NodeImpl nextRes = (NodeImpl) base.nextNode(); + Node authorizableNode = ((AuthorizableTypePredicate) filter).getAuthorizableNode(nextRes); + try { + if (authorizableNode != null && authorizableIDs.add(authorizableNode.getIdentifier())) { + n = authorizableNode; + } + } catch (RepositoryException e) { + log.warn(e.getMessage()); + } + } + return n; + } + + } } Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/NodeResolver.java URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/NodeResolver.java?rev=987435&r1=987434&r2=987435&view=diff ============================================================================== --- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/NodeResolver.java (original) +++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/NodeResolver.java Fri Aug 20 09:33:50 2010 @@ -24,17 +24,25 @@ import javax.jcr.NodeIterator; import javax.jcr.RepositoryException; import javax.jcr.Session; +import org.apache.jackrabbit.api.security.user.UserManager; +import org.apache.jackrabbit.commons.predicate.Predicate; +import org.apache.jackrabbit.core.NodeImpl; +import org.apache.jackrabbit.core.nodetype.NodeTypeImpl; 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; /** - * Resolver: searches for Principals stored in Nodes of a {@link javax.jcr.Workspace} - * which match a certain criteria

    - * The principalNames are assumed to be stored in properties. + * Resolver: searches for user and/or groups stored in Nodes of a {@link javax.jcr.Workspace} + * which match a certain criteria */ abstract class NodeResolver { + private static Logger log = LoggerFactory.getLogger(NodeResolver.class); + private final Session session; private final NamePathResolver resolver; @@ -109,7 +117,7 @@ abstract class NodeResolver { } /** - * Search nodes. Take the arguments as search cirteria. + * Search nodes. Take the arguments as search criteria. * The queried value has to be a string fragment of one of the Properties * contained in the given set. And the node have to be of a requested nodetype * @@ -126,6 +134,23 @@ abstract class NodeResolver { throws RepositoryException; /** + * Search all properties underneath an authorizable of the specified type + * that match the specified value and relative path. If the relative path + * consists of a single name element the path constraint is omitted. + * + * @param relPath + * @param value + * @param authorizableType + * @param exact + * @param maxSize + * @return + * @throws RepositoryException + */ + public abstract NodeIterator findNodes(Path relPath, String value, + int authorizableType, boolean exact, + long maxSize) throws RepositoryException; + + /** * @return Session this instance has been constructed with. */ Session getSession() { @@ -156,6 +181,93 @@ abstract class NodeResolver { } return searchRoot; } + + /** + * @param authorizableType + * @return The path of search root for the specified authorizable type. + */ + String getSearchRoot(int authorizableType) { + switch (authorizableType) { + case UserManager.SEARCH_TYPE_USER: + return userSearchRoot; + case UserManager.SEARCH_TYPE_GROUP: + return groupSearchRoot; + default: + return authorizableSearchRoot; + } + } + + /** + * + * @param authorizableType + * @param exact If exact is true, the predicate only evaluates to true if the + * passed node is of the required authorizable node type. Otherwise, all + * ancestors are taken into account as well. + * @return a new AuthorizableTypePredicate instance. + */ + AuthorizableTypePredicate getAuthorizableTypePredicate(int authorizableType, boolean exact) { + return new AuthorizableTypePredicate(authorizableType, exact); + } + + //-------------------------------------------------------------------------- + /** + * + */ + class AuthorizableTypePredicate implements Predicate { + + private final int authorizableType; + private final boolean exact; + + private AuthorizableTypePredicate(int authorizableType, boolean exact) { + this.authorizableType = authorizableType; + this.exact = exact; + } + + /** + * @see Predicate#evaluate(Object) + */ + public boolean evaluate(Object object) { + if (object instanceof NodeImpl) { + Node n = getAuthorizableNode((NodeImpl) object); + return n != null; + } + return false; + } + + Node getAuthorizableNode(NodeImpl n) { + try { + if (matches(n)) { + return n; + } + + if (!exact) { + // walk up the node hierarchy to verify it is a child node + // of an authorizable node of the expected type. + while (n.getDepth() > 0) { + n = (NodeImpl) n.getParent(); + if (matches(n)) { + return n; + } + } + } + } catch (RepositoryException e) { + log.debug(e.getMessage()); + } + return null; + } + + private boolean matches(NodeImpl result) throws RepositoryException { + Name ntName = ((NodeTypeImpl) result.getPrimaryNodeType()).getQName(); + switch (authorizableType) { + case UserManager.SEARCH_TYPE_GROUP: + return UserConstants.NT_REP_GROUP.equals(ntName); + case UserManager.SEARCH_TYPE_USER: + return UserConstants.NT_REP_USER.equals(ntName); + default: + return UserConstants.NT_REP_USER.equals(ntName) || UserConstants.NT_REP_GROUP.equals(ntName); + } + } + } } Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/TraversingNodeResolver.java URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/TraversingNodeResolver.java?rev=987435&r1=987434&r2=987435&view=diff ============================================================================== --- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/TraversingNodeResolver.java (original) +++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/TraversingNodeResolver.java Fri Aug 20 09:33:50 2010 @@ -17,9 +17,13 @@ package org.apache.jackrabbit.core.security.user; +import org.apache.jackrabbit.api.security.user.UserManager; import org.apache.jackrabbit.commons.iterator.NodeIteratorAdapter; +import org.apache.jackrabbit.commons.predicate.Predicate; import org.apache.jackrabbit.core.NodeImpl; +import org.apache.jackrabbit.core.SessionImpl; import org.apache.jackrabbit.spi.Name; +import org.apache.jackrabbit.spi.Path; import org.apache.jackrabbit.spi.commons.conversion.NamePathResolver; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -33,8 +37,10 @@ import javax.jcr.Session; import javax.jcr.Value; import java.util.Collection; import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; +import java.util.Map; import java.util.Set; import java.util.regex.PatternSyntaxException; @@ -85,11 +91,12 @@ class TraversingNodeResolver extends Nod if (getSession().nodeExists(sr)) { try { Node root = getSession().getNode(sr); - NodeIterator nodes = collectNodes(value, - Collections.singleton(propertyName), ntName, - root.getNodes(), true, 1); - if (nodes.hasNext()) { - return nodes.nextNode(); + Set matchSet = new HashSet(); + collectNodes(value, Collections.singleton(propertyName), ntName, root.getNodes(), matchSet, true, 1); + + NodeIterator it = new NodeIteratorAdapter(matchSet); + if (it.hasNext()) { + return it.nextNode(); } } catch (PathNotFoundException e) { // should not get here @@ -109,7 +116,33 @@ class TraversingNodeResolver extends Nod if (getSession().nodeExists(sr)) { try { Node root = getSession().getNode(sr); - return collectNodes(value, propertyNames, ntName, root.getNodes(), exact, maxSize); + Set matchSet = new HashSet(); + collectNodes(value, propertyNames, ntName, root.getNodes(), matchSet, exact, maxSize); + return new NodeIteratorAdapter(matchSet); + } catch (PathNotFoundException e) { + // should not get here + log.warn("Error while retrieving node " + sr); + } + } // else: searchRoot does not exist yet -> omit the search + return NodeIteratorAdapter.EMPTY; + } + + /** + * @inheritDoc + */ + @Override + public NodeIterator findNodes(Path relPath, String value, int authorizableType, boolean exact, long maxSize) throws RepositoryException { + String sr = getSearchRoot(authorizableType); + if (getSession().nodeExists(sr)) { + try { + String path = getNamePathResolver().getJCRPath(relPath); + AuthorizableTypePredicate pred = getAuthorizableTypePredicate(authorizableType, relPath.getLength() > 1); + + Node root = getSession().getNode(sr); + Map matchingNodes = new HashMap(); + collectNodes(value, path, pred, root.getNodes(), matchingNodes, exact, maxSize); + + return new NodeIteratorAdapter(matchingNodes.values()); } catch (PathNotFoundException e) { // should not get here log.warn("Error while retrieving node " + sr); @@ -144,33 +177,13 @@ class TraversingNodeResolver extends Nod } /** - * searches the given value in the range of the given NodeIterator. - * recurses unitll all matching values in all configured props are found. - * - * @param value the value to be found in the nodes - * @param props property to be searched, or null if {@link javax.jcr.Item#getName()} - * @param ntName to filter search - * @param nodes range of nodes and descendants to be searched - * @param exact if set to true the value has to match exactly else a - * substring is searched - * @param maxSize - * @return - */ - private NodeIterator collectNodes(String value, Set props, Name ntName, - NodeIterator nodes, boolean exact, - long maxSize) { - Set matchSet = new HashSet(); - collectNodes(value, props, ntName, nodes, matchSet, exact, maxSize); - return new NodeIteratorAdapter(matchSet); - } - - /** - * searches the given value in the range of the given NodeIterator. - * recurses unitll all matching values in all configured properties are found. + * Searches the given value in the range of the given NodeIterator. + * This method is called recursively to look within the complete tree + * of authorizable nodes. * * @param value the value to be found in the nodes * @param propertyNames property to be searched, or null if {@link javax.jcr.Item#getName()} - * @param nodeTypeName name of nodetypes to search + * @param nodeTypeName name of node types to search * @param itr range of nodes and descendants to be searched * @param matchSet Set of found matches to append results * @param exact if set to true the value has to match exact @@ -196,6 +209,25 @@ class TraversingNodeResolver extends Nod } } + private void collectNodes(String value, String relPath, + AuthorizableTypePredicate predicate, NodeIterator itr, + Map matchingNodes, boolean exact, long maxSize) { + while (itr.hasNext()) { + NodeImpl node = (NodeImpl) itr.nextNode(); + try { + Node authNode = getMatchingNode(node, predicate, relPath, value, exact); + if (authNode != null) { + matchingNodes.put(authNode.getIdentifier(), authNode); + maxSize--; + } else if (node.hasNodes() && maxSize > 0) { + collectNodes(value, relPath, predicate, node.getNodes(), matchingNodes, exact, maxSize); + } + } catch (RepositoryException e) { + log.warn("Internal error while accessing node", e); + } + } + } + /** * * @param node @@ -245,6 +277,39 @@ class TraversingNodeResolver extends Nod return match; } + /** + * + * @param node + * @param predicate + * @param relPath + * @param value + * @param exact + * @return + * @throws RepositoryException + */ + private static Node getMatchingNode(NodeImpl node, AuthorizableTypePredicate predicate, + String relPath, String value, + boolean exact) throws RepositoryException { + boolean match = false; + Node authNode = predicate.getAuthorizableNode(node); + if (authNode != null && node.hasProperty(relPath)) { + try { + Property prop = node.getProperty(relPath); + if (prop.isMultiple()) { + Value[] values = prop.getValues(); + for (int i = 0; i < values.length && !match; i++) { + match = matches(value, values[i].getString(), exact); + } + } else { + match = matches(value, prop.getString(), exact); + } + } catch (PatternSyntaxException pe) { + log.debug("couldn't search for {}, pattern invalid: {}", value, pe.getMessage()); + } + } + return (match) ? authNode : null; + } + private static boolean matches(String value, String toMatch, boolean exact) { return (exact) ? toMatch.equals(value) : toMatch.matches(".*"+value+".*"); } Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/UserManagerImpl.java URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/UserManagerImpl.java?rev=987435&r1=987434&r2=987435&view=diff ============================================================================== --- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/UserManagerImpl.java (original) +++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/UserManagerImpl.java Fri Aug 20 09:33:50 2010 @@ -32,6 +32,7 @@ import org.apache.jackrabbit.core.securi import org.apache.jackrabbit.core.security.principal.PrincipalImpl; import org.apache.jackrabbit.core.session.SessionOperation; import org.apache.jackrabbit.spi.Name; +import org.apache.jackrabbit.spi.Path; import org.apache.jackrabbit.util.Text; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -50,6 +51,7 @@ import javax.jcr.version.VersionExceptio import java.io.UnsupportedEncodingException; import java.security.Principal; +import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.NoSuchElementException; @@ -443,30 +445,46 @@ public class UserManagerImpl extends Pro /** * @see UserManager#findAuthorizables(String,String) */ - public Iterator findAuthorizables(String propertyName, String value) throws RepositoryException { - return findAuthorizables(propertyName, value, SEARCH_TYPE_AUTHORIZABLE); + public Iterator findAuthorizables(String relPath, String value) throws RepositoryException { + return findAuthorizables(relPath, value, SEARCH_TYPE_AUTHORIZABLE); } /** * @see UserManager#findAuthorizables(String,String, int) */ - public Iterator findAuthorizables(String propertyName, String value, int searchType) + public Iterator findAuthorizables(String relPath, String value, int searchType) throws RepositoryException { - Name name = session.getQName(propertyName); - Name ntName; - switch (searchType) { - case SEARCH_TYPE_AUTHORIZABLE: - ntName = NT_REP_AUTHORIZABLE; - break; - case SEARCH_TYPE_GROUP: - ntName = NT_REP_GROUP; - break; - case SEARCH_TYPE_USER: - ntName = NT_REP_USER; - break; - default: throw new IllegalArgumentException("Invalid search type " + searchType); + if (searchType < SEARCH_TYPE_USER || searchType > SEARCH_TYPE_AUTHORIZABLE) { + throw new IllegalArgumentException("Invalid search type " + searchType); + } + + Path path = session.getQPath(relPath); + NodeIterator nodes; + if (relPath.indexOf('/') == -1) { + // search for properties somewhere below an authorizable node + nodes = authResolver.findNodes(path, value, searchType, true, Long.MAX_VALUE); + } else { + path = path.getNormalizedPath(); + if (path.getLength() == 1) { + // only search below the authorizable node + Name ntName; + switch (searchType) { + case SEARCH_TYPE_GROUP: + ntName = NT_REP_GROUP; + break; + case SEARCH_TYPE_USER: + ntName = NT_REP_USER; + break; + default: + ntName = NT_REP_AUTHORIZABLE; + } + nodes = authResolver.findNodes(path.getNameElement().getName(), value, ntName, true); + } else { + // search below authorizable nodes but take some path constraints + // into account. + nodes = authResolver.findNodes(path, value, searchType, true, Long.MAX_VALUE); + } } - NodeIterator nodes = authResolver.findNodes(name, value, ntName, true); return new AuthorizableIterator(nodes); } Modified: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/api/security/user/AuthorizableTest.java URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/api/security/user/AuthorizableTest.java?rev=987435&r1=987434&r2=987435&view=diff ============================================================================== --- jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/api/security/user/AuthorizableTest.java (original) +++ jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/api/security/user/AuthorizableTest.java Fri Aug 20 09:33:50 2010 @@ -18,13 +18,16 @@ package org.apache.jackrabbit.api.securi import org.apache.jackrabbit.api.JackrabbitSession; import org.apache.jackrabbit.test.NotExecutableException; +import org.apache.jackrabbit.util.Text; import javax.jcr.RepositoryException; import javax.jcr.Session; import javax.jcr.Value; import java.security.Principal; +import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; +import java.util.List; /** * UserTest... @@ -70,10 +73,24 @@ public class AuthorizableTest extends Ab found = propName.equals(it.next()); } assertTrue(found); + + found = false; + for (Iterator it = auth.getPropertyNames("."); it.hasNext() && !found;) { + found = propName.equals(it.next()); + } + assertTrue(found); + assertTrue(auth.hasProperty(propName)); + assertTrue(auth.hasProperty("./" + propName)); + assertTrue(auth.getProperty(propName).length == 1); + assertEquals(v, auth.getProperty(propName)[0]); + assertEquals(v, auth.getProperty("./" + propName)[0]); + assertTrue(auth.removeProperty(propName)); + assertFalse(auth.hasProperty(propName)); + save(superuser); } finally { // try to remove the property again even if previous calls failed. @@ -101,9 +118,22 @@ public class AuthorizableTest extends Ab found = propName.equals(it.next()); } assertTrue(found); + + found = false; + for (Iterator it = auth.getPropertyNames("."); it.hasNext() && !found;) { + found = propName.equals(it.next()); + } + assertTrue(found); + assertTrue(auth.hasProperty(propName)); + assertTrue(auth.hasProperty("./" + propName)); + assertEquals(Arrays.asList(v), Arrays.asList(auth.getProperty(propName))); + assertEquals(Arrays.asList(v), Arrays.asList(auth.getProperty("./" + propName))); + assertTrue(auth.removeProperty(propName)); + assertFalse(auth.hasProperty(propName)); + save(superuser); } finally { // try to remove the property again even if previous calls failed. @@ -112,6 +142,105 @@ public class AuthorizableTest extends Ab } } + public void testSetPropertyByRelPath() throws NotExecutableException, RepositoryException { + Authorizable auth = getTestUser(superuser); + Value[] v = new Value[] {superuser.getValueFactory().createValue("Super User")}; + + List relPaths = new ArrayList(); + relPaths.add("testing/Fullname"); + relPaths.add("testing/Email"); + relPaths.add("testing/testing/testing/Fullname"); + relPaths.add("testing/testing/testing/Email"); + + for (String relPath : relPaths) { + try { + auth.setProperty(relPath, v); + save(superuser); + + assertTrue(auth.hasProperty(relPath)); + String propName = Text.getName(relPath); + assertFalse(auth.hasProperty(propName)); + } finally { + // try to remove the property even if previous calls failed. + auth.removeProperty(relPath); + save(superuser); + } + } + } + + public void testSetPropertyInvalidRelativePath() throws NotExecutableException, RepositoryException { + Authorizable auth = getTestUser(superuser); + Value[] v = new Value[] {superuser.getValueFactory().createValue("Super User")}; + + List invalidPaths = new ArrayList(); + // try setting outside of tree defined by the user. + invalidPaths.add("../testing/Fullname"); + invalidPaths.add("../../testing/Fullname"); + invalidPaths.add("testing/testing/../../../Fullname"); + // try absolute path -> must fail + invalidPaths.add("/testing/Fullname"); + + for (String invalidRelPath : invalidPaths) { + try { + auth.setProperty(invalidRelPath, v); + fail("Modifications outside of the scope of the authorizable must fail."); + } catch (Exception e) { + // success. + } + } + } + + public void testGetPropertyByInvalidRelativePath() throws NotExecutableException, RepositoryException { + Authorizable auth = getTestUser(superuser); + + List wrongPaths = new ArrayList(); + wrongPaths.add("../jcr:primaryType"); + wrongPaths.add("../../jcr:primaryType"); + wrongPaths.add("../testing/jcr:primaryType"); + for (String path : wrongPaths) { + assertNull(auth.getProperty(path)); + } + + List invalidPaths = new ArrayList(); + invalidPaths.add("/testing/jcr:primaryType"); + invalidPaths.add(".."); + invalidPaths.add("."); + invalidPaths.add(null); + for (String invalidPath : invalidPaths) { + try { + assertNull(auth.getProperty(invalidPath)); + } catch (Exception e) { + // success + } + } + } + + public void testHasPropertyByInvalidRelativePath() throws NotExecutableException, RepositoryException { + Authorizable auth = getTestUser(superuser); + + List wrongPaths = new ArrayList(); + wrongPaths.add("../jcr:primaryType"); + wrongPaths.add("../../jcr:primaryType"); + wrongPaths.add("../testing/jcr:primaryType"); + for (String path : wrongPaths) { + assertFalse(auth.hasProperty(path)); + } + + + List invalidPaths = new ArrayList(); + invalidPaths.add(".."); + invalidPaths.add("."); + invalidPaths.add(null); + + for (String invalidPath : invalidPaths) { + try { + assertFalse(auth.hasProperty(invalidPath)); + } catch (Exception e) { + // success + } + } + } + public void testGetPropertyNames() throws NotExecutableException, RepositoryException { Authorizable auth = getTestUser(superuser); @@ -138,6 +267,72 @@ public class AuthorizableTest extends Ab } } + public void testGetPropertyNamesByRelPath() throws NotExecutableException, RepositoryException { + Authorizable auth = getTestUser(superuser); + + // TODO: retrieve propname and value from config + String relPath = "testing/Fullname"; + Value v = superuser.getValueFactory().createValue("Super User"); + try { + auth.setProperty(relPath, v); + save(superuser); + } catch (RepositoryException e) { + throw new NotExecutableException("Cannot test 'Authorizable.setProperty'."); + } + + try { + for (Iterator it = auth.getPropertyNames(); it.hasNext();) { + String name = it.next(); + assertFalse("Fullname".equals(name)); + } + + for (Iterator it = auth.getPropertyNames("testing"); it.hasNext();) { + String name = it.next(); + String rp = "testing/" + name; + + assertFalse(auth.hasProperty(name)); + assertNull(auth.getProperty(name)); + + assertTrue(auth.hasProperty(rp)); + assertNotNull(auth.getProperty(rp)); + } + for (Iterator it = auth.getPropertyNames("./testing"); it.hasNext();) { + String name = it.next(); + String rp = "testing/" + name; + + assertFalse(auth.hasProperty(name)); + assertNull(auth.getProperty(name)); + + assertTrue(auth.hasProperty(rp)); + assertNotNull(auth.getProperty(rp)); + } + } finally { + // try to remove the property again even if previous calls failed. + auth.removeProperty(relPath); + save(superuser); + } + } + + public void testGetPropertyNamesByInvalidRelPath() throws NotExecutableException, RepositoryException { + Authorizable auth = getTestUser(superuser); + + List invalidPaths = new ArrayList(); + invalidPaths.add("../"); + invalidPaths.add("../../"); + invalidPaths.add("../testing"); + invalidPaths.add("/testing"); + invalidPaths.add(null); + + for (String invalidRelPath : invalidPaths) { + try { + auth.getPropertyNames(invalidRelPath); + fail("Calling Authorizable#getPropertyNames with " + invalidRelPath + " must fail."); + } catch (Exception e) { + // success + } + } + } + public void testGetNotExistingProperty() throws RepositoryException, NotExecutableException { Authorizable auth = getTestUser(superuser); String hint = "Fullname"; Modified: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authentication/SimpleCredentialsAuthenticationTest.java URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authentication/SimpleCredentialsAuthenticationTest.java?rev=987435&r1=987434&r2=987435&view=diff ============================================================================== --- jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authentication/SimpleCredentialsAuthenticationTest.java (original) +++ jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authentication/SimpleCredentialsAuthenticationTest.java Fri Aug 20 09:33:50 2010 @@ -172,6 +172,10 @@ public class SimpleCredentialsAuthentica return null; } + public Iterator getPropertyNames(String relPath) throws RepositoryException { + return null; + } + public boolean hasProperty(String name) throws RepositoryException { return false; } Modified: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/user/AuthorizableImplTest.java URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/user/AuthorizableImplTest.java?rev=987435&r1=987434&r2=987435&view=diff ============================================================================== --- jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/user/AuthorizableImplTest.java (original) +++ jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/user/AuthorizableImplTest.java Fri Aug 20 09:33:50 2010 @@ -351,6 +351,10 @@ public class AuthorizableImplTest extend return user.getPropertyNames(); } + public Iterator getPropertyNames(String relPath) throws RepositoryException { + return user.getPropertyNames(relPath); + } + public boolean hasProperty(String name) throws RepositoryException { return user.hasProperty(name); } @@ -375,4 +379,4 @@ public class AuthorizableImplTest extend assertFalse(user.equals(user3)); assertTrue(s.add(user3)); } -} \ No newline at end of file +} Modified: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/user/IndexNodeResolverTest.java URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/user/IndexNodeResolverTest.java?rev=987435&r1=987434&r2=987435&view=diff ============================================================================== --- jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/user/IndexNodeResolverTest.java (original) +++ jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/user/IndexNodeResolverTest.java Fri Aug 20 09:33:50 2010 @@ -31,6 +31,7 @@ public class IndexNodeResolverTest exten private static Logger log = LoggerFactory.getLogger(IndexNodeResolver.class); + @Override protected NodeResolver createNodeResolver(SessionImpl session) throws RepositoryException, NotExecutableException { return new IndexNodeResolver(session, session); } Modified: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/user/NodeResolverTest.java URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/user/NodeResolverTest.java?rev=987435&r1=987434&r2=987435&view=diff ============================================================================== --- jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/user/NodeResolverTest.java (original) +++ jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/user/NodeResolverTest.java Fri Aug 20 09:33:50 2010 @@ -17,18 +17,17 @@ package org.apache.jackrabbit.core.security.user; import org.apache.jackrabbit.api.JackrabbitSession; -import org.apache.jackrabbit.api.security.user.UserManager; import org.apache.jackrabbit.api.security.user.Authorizable; import org.apache.jackrabbit.api.security.user.Group; +import org.apache.jackrabbit.api.security.user.UserManager; import org.apache.jackrabbit.core.SessionImpl; import org.apache.jackrabbit.spi.Name; +import org.apache.jackrabbit.spi.Path; import org.apache.jackrabbit.spi.commons.name.NameConstants; import org.apache.jackrabbit.spi.commons.name.NameFactoryImpl; import org.apache.jackrabbit.test.AbstractJCRTest; import org.apache.jackrabbit.test.NotExecutableException; import org.apache.jackrabbit.util.Text; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import javax.jcr.Node; import javax.jcr.NodeIterator; @@ -40,14 +39,13 @@ import java.util.Iterator; /** NodeResolverTest... */ public abstract class NodeResolverTest extends AbstractJCRTest { - private static Logger log = LoggerFactory.getLogger(NodeResolverTest.class); - NodeResolver nodeResolver; UserManager umgr; String usersPath = UserConstants.USERS_PATH; String groupsPath = UserConstants.GROUPS_PATH; String authorizablesPath = UserConstants.AUTHORIZABLES_PATH; + @Override protected void setUp() throws Exception { super.setUp(); @@ -232,6 +230,60 @@ public abstract class NodeResolverTest e } } + /** + * + * @throws NotExecutableException + * @throws RepositoryException + */ + public void testFindNodesByRelPathProperties() throws NotExecutableException, RepositoryException { + Value[] vs = new Value[] { + superuser.getValueFactory().createValue("blub"), + superuser.getValueFactory().createValue("blib") + }; + + String relPath = "relPath/" + propertyName1; + String relPath2 = "another/" + propertyName1; + String relPath3 = "relPath/relPath/" + propertyName1; + UserImpl currentUser = getCurrentUser(); + currentUser.setProperty(relPath, vs); + currentUser.setProperty(relPath2, vs); + currentUser.setProperty(relPath3, vs); + save(); + + Path relQPath = ((SessionImpl) superuser).getQPath(relPath); + Path relQName = ((SessionImpl) superuser).getQPath(propertyName1); + + try { + NodeResolver nr = createNodeResolver(currentUser.getNode().getSession()); + // 1) findNodes(QPath..... + // relPath : "prop1" -> should find the currentuserNode + NodeIterator result = nr.findNodes(relQName, "blub", UserManager.SEARCH_TYPE_USER, false, Long.MAX_VALUE); + assertTrue("expected result", result.hasNext()); + Node n = result.nextNode(); + assertEquals(currentUser.getNode().getPath(), n.getPath()); + assertFalse("expected no more results", result.hasNext()); + + // relPath : "relPath/prop1" -> should find the currentuserNode + result = nr.findNodes(relQPath, "blub", UserManager.SEARCH_TYPE_USER, false, Long.MAX_VALUE); + assertTrue("expected result", result.hasNext()); + assertEquals(currentUser.getNode().getPath(), result.nextNode().getPath()); + assertFalse("expected no more results", result.hasNext()); + + // 2) findNodes(Name.......) + // search by Name -> should not find deep property + Name propName = ((SessionImpl) superuser).getQName(propertyName1); + result = nr.findNodes(propName, "blub", UserConstants.NT_REP_USER, false); + assertFalse("should not find result", result.hasNext()); + + } finally { + currentUser.removeProperty(relPath); + currentUser.removeProperty(relPath2); + currentUser.removeProperty(relPath3); + save(); + } + } + + public void testFindNodesWithNonExistingSearchRoot() throws NotExecutableException, RepositoryException { String searchRoot = nodeResolver.getSearchRoot(UserConstants.NT_REP_AUTHORIZABLE); if (superuser.nodeExists(searchRoot)) { Modified: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/user/TraversingNodeResolverTest.java URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/user/TraversingNodeResolverTest.java?rev=987435&r1=987434&r2=987435&view=diff ============================================================================== --- jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/user/TraversingNodeResolverTest.java (original) +++ jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/user/TraversingNodeResolverTest.java Fri Aug 20 09:33:50 2010 @@ -28,6 +28,7 @@ public class TraversingNodeResolverTest private static Logger log = LoggerFactory.getLogger(TraversingNodeResolverTest.class); + @Override protected NodeResolver createNodeResolver(SessionImpl session) throws RepositoryException, NotExecutableException { return new TraversingNodeResolver(session, session); } Modified: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/user/UserImporterTest.java URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/user/UserImporterTest.java?rev=987435&r1=987434&r2=987435&view=diff ============================================================================== --- jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/user/UserImporterTest.java (original) +++ jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/user/UserImporterTest.java Fri Aug 20 09:33:50 2010 @@ -1397,11 +1397,11 @@ public class UserImporterTest extends Ab return null; } - public Iterator findAuthorizables(String propertyName, String value) throws RepositoryException { + public Iterator findAuthorizables(String relPath, String value) throws RepositoryException { return null; } - public Iterator findAuthorizables(String propertyName, String value, int searchType) throws RepositoryException { + public Iterator findAuthorizables(String relPath, String value, int searchType) throws RepositoryException { return null; } Added: jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/commons/iterator/FilteringNodeIterator.java URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/commons/iterator/FilteringNodeIterator.java?rev=987435&view=auto ============================================================================== --- jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/commons/iterator/FilteringNodeIterator.java (added) +++ jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/commons/iterator/FilteringNodeIterator.java Fri Aug 20 09:33:50 2010 @@ -0,0 +1,120 @@ +/* + * 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.commons.iterator; + +import org.apache.jackrabbit.commons.predicate.Predicate; + +import javax.jcr.Node; +import javax.jcr.NodeIterator; +import java.util.NoSuchElementException; + +/** + * A wrapper around a NodeIterator filtering out nodes from the base iterator + * that don't match the specified {@link Predicate filter}. Due to the nature + * of the filter mechanism the {@link #getSize() size} if the iterator may + * shrink upon iteration. + */ +public class FilteringNodeIterator implements NodeIterator { + + protected final NodeIterator base; + protected final Predicate filter; + + private Node next; + private long position; + + public FilteringNodeIterator(NodeIterator base, Predicate filter) { + this.base = base; + this.filter = filter; + next = seekNext(); + } + + //-----------------------------------------------------------< Iterator >--- + /** + * @see java.util.Iterator#hasNext() + */ + public boolean hasNext() { + return next != null; + } + + /** + * @see java.util.Iterator#next() + */ + public Object next() { + return nextNode(); + } + + /** + * @see java.util.Iterator#remove() + */ + public void remove() { + throw new UnsupportedOperationException(); + } + + //-------------------------------------------------------< NodeIterator >--- + /** + * @see javax.jcr.NodeIterator#nextNode() + */ + public Node nextNode() { + Node n = next; + if (n == null) { + throw new NoSuchElementException(); + } + next = seekNext(); + position++; + return n; + } + + //------------------------------------------------------< RangeIterator >--- + /** + * @see javax.jcr.RangeIterator#skip(long) + */ + public void skip(long skipNum) { + while (skipNum-- > 0) { + next(); + } + } + + /** + * @see javax.jcr.RangeIterator#getSize() + */ + public long getSize() { + return -1; + } + + /** + * @see javax.jcr.RangeIterator#getPosition() + */ + public long getPosition() { + return position; + } + + //-------------------------------------------------------------------------- + /** + * + * @return + */ + protected Node seekNext() { + Node n = null; + while (n == null && base.hasNext()) { + Node nextRes = base.nextNode(); + if (filter.evaluate(nextRes)) { + n = nextRes; + } + } + return n; + } +} \ No newline at end of file Propchange: jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/commons/iterator/FilteringNodeIterator.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/commons/iterator/FilteringNodeIterator.java ------------------------------------------------------------------------------ svn:keywords = Author Date Id Revision Rev URL