Return-Path: Delivered-To: apmail-jackrabbit-commits-archive@www.apache.org Received: (qmail 22909 invoked from network); 12 Nov 2010 15:24:35 -0000 Received: from unknown (HELO mail.apache.org) (140.211.11.3) by 140.211.11.9 with SMTP; 12 Nov 2010 15:24:35 -0000 Received: (qmail 21991 invoked by uid 500); 12 Nov 2010 15:25:07 -0000 Delivered-To: apmail-jackrabbit-commits-archive@jackrabbit.apache.org Received: (qmail 21819 invoked by uid 500); 12 Nov 2010 15:25:05 -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 21812 invoked by uid 99); 12 Nov 2010 15:25:04 -0000 Received: from nike.apache.org (HELO nike.apache.org) (192.87.106.230) by apache.org (qpsmtpd/0.29) with ESMTP; Fri, 12 Nov 2010 15:25:04 +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, 12 Nov 2010 15:25:01 +0000 Received: by eris.apache.org (Postfix, from userid 65534) id 16CDB23889DE; Fri, 12 Nov 2010 15:23:46 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Subject: 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/... Date: Fri, 12 Nov 2010 15:23:45 -0000 To: commits@jackrabbit.apache.org From: mduerig@apache.org X-Mailer: svnmailer-1.0.8 Message-Id: <20101112152346.16CDB23889DE@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org 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 { void setLimit(long offset, long maxCount); /** + * Create a condition which holds iff the name of the {@link Authorizable} + * matches a pattern. + * 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 relPath against + * @return A condition + */ + T nameMatches(String pattern); + + /** * Create a condition which holds iff the node of an {@link Authorizable} has a * property at relPath which is not equal to value. * The format of the relPath 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 string 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 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 groups = new HashSet(); private final Set authorizables = new HashSet(); + private final Set systemDefined = new HashSet(); + @Override public void setUp() throws Exception { super.setUp(); + // todo P_PRINCIPAL_NAME + Iterator 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 void build(QueryBuilder 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 result = userMgr.findAuthorizables(new Query() { + public void build(QueryBuilder builder) { + builder.setCondition(builder.nameMatches("a%")); + } + }); + + Iterator expected = Iterators.filterIterator(authorizables.iterator(), new Predicate() { + 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 result = userMgr.findAuthorizables(new Query() { public void build(QueryBuilder 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 sortedUsers = new ArrayList(users); + sortedUsers.removeAll(systemDefined); // remove system defined users: no @weight + Comparator comp = new Comparator() { public int compare(User user1, User user2) { try { @@ -688,15 +732,6 @@ public class UserManagerSearchTest exten } } - private static void assertContainsAll(Iterator it1, Iterator it2) { - Set set1 = toSet(it1); - Set set2 = toSet(it2); - set2.removeAll(set1); - if (!set2.isEmpty()) { - fail("Missing elements in query result: " + set2); - } - } - private static void assertSameElements(Iterator it1, Iterator it2) { Set set1 = toSet(it1); Set set2 = toSet(it2);