jackrabbit-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ang...@apache.org
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 GMT
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 <code>this</code> Authorizable.
+     * Returns the names of properties present with <code>this</code>
+     * Authorizable not taking possible relative paths into consideration.
+     * Same as {@link #getPropertyNames(String)} where the specified string
+     * is &quot;.&quot;.
      *
      * @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<String> getPropertyNames() throws RepositoryException;
 
     /**
+     * Returns the names of properties present with <code>this</code>
+     * Authorizable at the specified relative path.
+     *
+     * @return names of properties.
+     * @throws RepositoryException If an error occurs.
+     * @see #getProperty(String)
+     * @see #hasProperty(String)
+     */
+    Iterator<String> 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 <code>true</code> 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 <code>Authorizable</code>.
      *
-     * @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 <code>Authorizable</code>.
      *
-     * @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
      * <code>null</code>.
      *
-     * @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 <code>null</code>
      *         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 <code>Authorizable</code>s that have
-     * {@link Authorizable#getProperty(String) property} with the given name and
-     * that Property equals the given value.
+     * Returns all <code>Authorizable</code>s that have a
+     * {@link Authorizable#getProperty(String) property} with the given relative
+     * path (or name) that matches the specified value.
+     * <p/>
+     * 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 <code>Authorizable</code>s that have a property with the given
      * name exactly matching the given value.
      * @throws RepositoryException If an error occurs.
      * @see Authorizable#getProperty(String)
      */
-    Iterator<Authorizable> findAuthorizables(String propertyName, String value) throws RepositoryException;
+    Iterator<Authorizable> findAuthorizables(String relPath, String value) throws RepositoryException;
 
     /**
-     * Returns all <code>Authorizable</code>s that have
-     * {@link Authorizable#getProperty(String) property} with the given name and
-     * that Property equals the given value. In contrast to
+     * Returns all <code>Authorizable</code>s 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.
+     * <p/>
+     * 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:
      * <ul>
@@ -98,7 +108,7 @@ public interface UserManager {
      * @return An iterator of <code>Authorizable</code>.
      * @throws RepositoryException If an error occurs.
      */
-    Iterator<Authorizable> findAuthorizables(String propertyName, String value, int searchType) throws RepositoryException;
+    Iterator<Authorizable> 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<String> l = new ArrayList<String>();
         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<String> 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<String> l = new ArrayList<String>();
+            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 <code>true</code> if the given property is defined
      * by the rep:authorizable node type or one of it's sub-node types;
      * <code>false</code> 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<Name> 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<Name> 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<Name> 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<String> authorizableIDs;
+
+        private ResultFilteringNodeIterator(NodeIterator base, AuthorizableTypePredicate filter) {
+            super(base, filter);
+        }
+
+        @Override
+        protected Node seekNext() {
+            if (authorizableIDs == null) {
+                authorizableIDs = new HashSet<String>();
+            }
+            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<p>
- * 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<Node> matchSet = new HashSet<Node>();
+                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<Node> matchSet = new HashSet<Node>();
+                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<String, Node> matchingNodes = new HashMap<String, Node>();
+                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<Name> props, Name ntName,
-                                      NodeIterator nodes, boolean exact,
-                                      long maxSize) {
-        Set<Node> matchSet = new HashSet<Node>();
-        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<String, Node> 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<Authorizable> findAuthorizables(String propertyName, String value) throws RepositoryException {
-        return findAuthorizables(propertyName, value, SEARCH_TYPE_AUTHORIZABLE);
+    public Iterator<Authorizable> findAuthorizables(String relPath, String value) throws RepositoryException {
+        return findAuthorizables(relPath, value, SEARCH_TYPE_AUTHORIZABLE);
     }
 
     /**
      * @see UserManager#findAuthorizables(String,String, int)
      */
-    public Iterator<Authorizable> findAuthorizables(String propertyName, String value, int searchType)
+    public Iterator<Authorizable> 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;
 
 /**
  * <code>UserTest</code>...
@@ -70,10 +73,24 @@ public class AuthorizableTest extends Ab
                 found = propName.equals(it.next());
             }
             assertTrue(found);
+
+            found = false;
+            for (Iterator<String> 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<String> 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<String> relPaths = new ArrayList<String>();
+        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<String> invalidPaths = new ArrayList<String>();
+        // 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<String> wrongPaths = new ArrayList<String>();
+        wrongPaths.add("../jcr:primaryType");
+        wrongPaths.add("../../jcr:primaryType");
+        wrongPaths.add("../testing/jcr:primaryType");
+        for (String path : wrongPaths) {
+            assertNull(auth.getProperty(path));
+        }
+
+        List<String> invalidPaths = new ArrayList<String>();
+        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<String> wrongPaths = new ArrayList<String>();
+        wrongPaths.add("../jcr:primaryType");
+        wrongPaths.add("../../jcr:primaryType");
+        wrongPaths.add("../testing/jcr:primaryType");
+        for (String path : wrongPaths) {
+            assertFalse(auth.hasProperty(path));
+        }
+
+
+        List<String> invalidPaths = new ArrayList<String>();
+        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<String> it = auth.getPropertyNames(); it.hasNext();) {
+                String name = it.next();
+                assertFalse("Fullname".equals(name));
+            }
+
+            for (Iterator<String> 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<String> 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<String> invalidPaths = new ArrayList<String>();
+        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<String> 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<String> 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;
 /** <code>NodeResolverTest</code>... */
 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<Authorizable> findAuthorizables(String propertyName, String value) throws RepositoryException {
+                public Iterator<Authorizable> findAuthorizables(String relPath, String value) throws RepositoryException {
                     return null;
                 }
 
-                public Iterator<Authorizable> findAuthorizables(String propertyName, String value, int searchType) throws RepositoryException {
+                public Iterator<Authorizable> 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



Mime
View raw message