You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by md...@apache.org on 2010/11/12 16:23:45 UTC

svn commit: r1034419 - 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/...

Author: mduerig
Date: Fri Nov 12 15:23:45 2010
New Revision: 1034419

URL: http://svn.apache.org/viewvc?rev=1034419&view=rev
Log:
JCR-2800: Implement search facility for users and groups
added condition for matching user/principal name
work in progress

Modified:
    jackrabbit/trunk/jackrabbit-api/src/main/java/org/apache/jackrabbit/api/security/user/QueryBuilder.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/XPathQueryBuilder.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/XPathQueryEvaluator.java
    jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/api/security/user/UserManagerSearchTest.java

Modified: jackrabbit/trunk/jackrabbit-api/src/main/java/org/apache/jackrabbit/api/security/user/QueryBuilder.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-api/src/main/java/org/apache/jackrabbit/api/security/user/QueryBuilder.java?rev=1034419&r1=1034418&r2=1034419&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-api/src/main/java/org/apache/jackrabbit/api/security/user/QueryBuilder.java (original)
+++ jackrabbit/trunk/jackrabbit-api/src/main/java/org/apache/jackrabbit/api/security/user/QueryBuilder.java Fri Nov 12 15:23:45 2010
@@ -104,6 +104,20 @@ public interface QueryBuilder<T> {
     void setLimit(long offset, long maxCount);
 
     /**
+     * Create a condition which holds iff the name of the {@link Authorizable}
+     * matches a <code>pattern</code>.
+     * The percent character Ô%Õ represents any string of zero or more characters and the
+     * underscore character Ô_Õ represents any single character. Any literal use of these characters
+     * and the backslash character Ô\Õ must be escaped with a backslash character.
+     * The pattern is matched against the {@link Authorizable#getID() id} and the
+     * {@link Authorizable#getPrincipal() principal}.
+     *
+     * @param pattern  Pattern to match the property at <code>relPath</code> against
+     * @return  A condition
+     */
+    T nameMatches(String pattern);
+
+    /**
      * Create a condition which holds iff the node of an {@link Authorizable} has a
      * property at <code>relPath</code> which is not equal to <code>value</code>.
      * The format of the <code>relPath</code> argument is the same as in XPath:

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/XPathQueryBuilder.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/XPathQueryBuilder.java?rev=1034419&r1=1034418&r2=1034419&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/XPathQueryBuilder.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/XPathQueryBuilder.java Fri Nov 12 15:23:45 2010
@@ -59,6 +59,7 @@ public class XPathQueryBuilder implement
     }
 
     interface ConditionVisitor {
+        void visit(NodeCondition nodeCondition) throws RepositoryException;
         void visit(PropertyCondition condition) throws RepositoryException;
         void visit(ContainsCondition condition);
         void visit(ImpersonationCondition condition);
@@ -149,6 +150,10 @@ public class XPathQueryBuilder implement
         return new PropertyCondition(relPath, op, value);
     }
 
+    public Condition nameMatches(String pattern) {
+        return new NodeCondition(pattern);
+    }
+
     public Condition neq(String relPath, Value value) {
         return new PropertyCondition(relPath, RelationOp.NE, value);
     }
@@ -203,6 +208,22 @@ public class XPathQueryBuilder implement
 
     //------------------------------------------< private >---
 
+    static class NodeCondition implements Condition {
+        private final String pattern;
+
+        public NodeCondition(String pattern) {
+            this.pattern = pattern;
+        }
+
+        public String getPattern() {
+            return pattern;
+        }
+
+        public void accept(ConditionVisitor visitor) throws RepositoryException {
+            visitor.visit(this);
+        }
+    }
+
     static class PropertyCondition implements Condition {
         private final String relPath;
         private final RelationOp op;

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/XPathQueryEvaluator.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/XPathQueryEvaluator.java?rev=1034419&r1=1034418&r2=1034419&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/XPathQueryEvaluator.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/XPathQueryEvaluator.java Fri Nov 12 15:23:45 2010
@@ -29,6 +29,7 @@ import org.apache.jackrabbit.spi.commons
 import org.apache.jackrabbit.spi.commons.iterator.Predicate;
 import org.apache.jackrabbit.spi.commons.iterator.Predicates;
 import org.apache.jackrabbit.spi.commons.iterator.Transformer;
+import org.apache.jackrabbit.test.api.util.Text;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -118,6 +119,22 @@ public class XPathQueryEvaluator impleme
 
     //------------------------------------------< ConditionVisitor >---
 
+    public void visit(XPathQueryBuilder.NodeCondition condition) throws RepositoryException {
+        String repPrincipal = session.getJCRName(UserConstants.P_PRINCIPAL_NAME);
+
+        xPath.append('(')
+             .append("jcr:like(")
+             .append(repPrincipal)
+             .append(",'")
+             .append(condition.getPattern())
+             .append("')")
+             .append(" or ")
+             .append("jcr:like(fn:name(.),'")
+             .append(escape(condition.getPattern()))
+             .append("')")
+             .append(')');
+    }
+
     public void visit(XPathQueryBuilder.PropertyCondition condition) throws RepositoryException {
         RelationOp relOp = condition.getOp();
         if (relOp == RelationOp.EX) {
@@ -183,6 +200,37 @@ public class XPathQueryEvaluator impleme
 
     //------------------------------------------< private >---
 
+    /**
+     * Escape <code>string</code> for matching in jcr escaped node names
+     * @param string  string to escape
+     * @return  escaped string
+     */
+    public static String escape(String string) {
+        StringBuilder result = new StringBuilder();
+
+        int k = 0;
+        int j;
+        do {
+            j = string.indexOf('%', k); // split on %
+            if (j < 0) {
+                // jcr escape trail
+                result.append(Text.escapeIllegalJcrChars(string.substring(k)));
+            }
+            else if (j > 0 && string.charAt(j - 1) == '\\') {
+                // literal occurrence of % -> jcr escape
+                result.append(Text.escapeIllegalJcrChars(string.substring(k, j) + '%'));
+            }
+            else {
+                // wildcard occurrence of % -> jcr escape all but %
+                result.append(Text.escapeIllegalJcrChars(string.substring(k, j))).append('%');
+            }
+
+            k = j + 1;
+        } while (j >= 0);
+
+        return result.toString();
+    }
+
     private String getNtName(Class<? extends Authorizable> selector) throws RepositoryException {
         if (User.class.isAssignableFrom(selector)) {
             return session.getJCRName(UserConstants.NT_REP_USER);

Modified: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/api/security/user/UserManagerSearchTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/api/security/user/UserManagerSearchTest.java?rev=1034419&r1=1034418&r2=1034419&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/api/security/user/UserManagerSearchTest.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/api/security/user/UserManagerSearchTest.java Fri Nov 12 15:23:45 2010
@@ -23,6 +23,7 @@ import org.apache.jackrabbit.spi.commons
 
 import javax.jcr.RepositoryException;
 import javax.jcr.Value;
+import java.security.Principal;
 import java.util.*;
 
 public class UserManagerSearchTest extends AbstractUserTest {
@@ -69,10 +70,25 @@ public class UserManagerSearchTest exten
     private final Set<Group> groups = new HashSet<Group>();
     private final Set<Authorizable> authorizables = new HashSet<Authorizable>();
 
+    private final Set<Authorizable> systemDefined = new HashSet<Authorizable>();
+
     @Override
     public void setUp() throws Exception {
         super.setUp();
 
+        // todo P_PRINCIPAL_NAME
+        Iterator<Authorizable> systemAuthorizables = userMgr.findAuthorizables("rep:principalName", null);
+        while (systemAuthorizables.hasNext()) {
+            Authorizable authorizable =  systemAuthorizables.next();
+            if (authorizable.isGroup()) {
+                groups.add((Group) authorizable);
+            }
+            else {
+                users.add((User) authorizable);
+            }
+            systemDefined.add(authorizable);
+        }
+
         // Create a zoo. Please excuse my ignorance in zoology ;-)
         animals = createGroup("animals");
             invertebrates = createGroup("invertebrates");
@@ -161,7 +177,9 @@ public class UserManagerSearchTest exten
     @Override
     public void tearDown() throws Exception {
         for (Authorizable authorizable : authorizables) {
-            authorizable.remove();
+            if (!systemDefined.contains(authorizable)) {
+                authorizable.remove();
+            }
         }
         authorizables.clear();
         groups.clear();
@@ -179,7 +197,7 @@ public class UserManagerSearchTest exten
             public <T> void build(QueryBuilder<T> builder) { /* any */ }
         });
 
-        assertContainsAll(result, authorizables.iterator());
+        assertSameElements(result, authorizables.iterator());
     }
 
     public void testSelector() throws RepositoryException {
@@ -196,13 +214,13 @@ public class UserManagerSearchTest exten
             });
 
             if (User.class.isAssignableFrom(s)) {
-                assertContainsAll(result, users.iterator());
+                assertSameElements(result, users.iterator());
             }
             else if (Group.class.isAssignableFrom(s)) {
-                assertContainsAll(result, groups.iterator());
+                assertSameElements(result, groups.iterator());
             }
             else {
-                assertContainsAll(result, authorizables.iterator());
+                assertSameElements(result, authorizables.iterator());
             }
         }
     }
@@ -291,6 +309,30 @@ public class UserManagerSearchTest exten
         }
     }
 
+    public void testNameMatch() throws RepositoryException {
+        Iterator<Authorizable> result = userMgr.findAuthorizables(new Query() {
+            public <T> void build(QueryBuilder<T> builder) {
+                builder.setCondition(builder.nameMatches("a%"));
+            }
+        });
+
+        Iterator<Authorizable> expected = Iterators.filterIterator(authorizables.iterator(), new Predicate<Authorizable>() {
+            public boolean evaluate(Authorizable authorizable) {
+                try {
+                    String name = authorizable.getID();
+                    Principal principal = authorizable.getPrincipal();
+                    return name.startsWith("a") || principal != null && principal.getName().startsWith("a");
+                } catch (RepositoryException e) {
+                    fail(e.getMessage());
+                }
+                return false;
+            }
+        });
+
+        assertTrue(result.hasNext());
+        assertSameElements(result, expected);
+    }
+
     public void testFindProperty1() throws RepositoryException {
         Iterator<Authorizable> result = userMgr.findAuthorizables(new Query() {
             public <T> void build(QueryBuilder<T> builder) {
@@ -404,7 +446,7 @@ public class UserManagerSearchTest exten
                     }
                     else {
                         String value = food[0].getString();
-                        return value.length() > 0 && value.charAt(0) == 'm';
+                        return value.startsWith("m");
                     }
                 } catch (RepositoryException e) {
                     fail(e.getMessage());
@@ -608,6 +650,8 @@ public class UserManagerSearchTest exten
 
     public void testSetBound() throws RepositoryException {
         List<User> sortedUsers = new ArrayList<User>(users);
+        sortedUsers.removeAll(systemDefined);  // remove system defined users: no @weight  
+
         Comparator<? super User> comp = new Comparator<User>() {
             public int compare(User user1, User user2) {
                 try {
@@ -688,15 +732,6 @@ public class UserManagerSearchTest exten
         }
     }
 
-    private static <T> void assertContainsAll(Iterator<? extends T> it1, Iterator<? extends T> it2) {
-        Set<? extends T> set1 = toSet(it1);
-        Set<? extends T> set2 = toSet(it2);
-        set2.removeAll(set1);
-        if (!set2.isEmpty()) {
-            fail("Missing elements in query result: " + set2);
-        }
-    }
-
     private static <T> void assertSameElements(Iterator<? extends T> it1, Iterator<? extends T> it2) {
         Set<? extends T> set1 = toSet(it1);
         Set<? extends T> set2 = toSet(it2);