jackrabbit-oak-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ang...@apache.org
Subject svn commit: r1431450 - in /jackrabbit/oak/trunk: oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/ oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/query/ oak-core/src/test/java/org/apache/jackrabbit/oak/security/user/query/...
Date Thu, 10 Jan 2013 15:53:36 GMT
Author: angela
Date: Thu Jan 10 15:53:35 2013
New Revision: 1431450

URL: http://svn.apache.org/viewvc?rev=1431450&view=rev
Log:
OAK-480 : Authorizable Query (WIP)

Added:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/query/GroupPredicate.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/query/UserQueryManager.java
      - copied, changed from r1431265, jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/UserQueryManager.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/query/XPathConditionVisitor.java
      - copied, changed from r1431265, jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/query/XPathQueryEvaluator.java
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/user/query/
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/user/query/UserQueryManagerTest.java
Removed:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/UserQueryManager.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/query/XPathQueryEvaluator.java
Modified:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/UserManagerImpl.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/query/QueryUtil.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/query/ResultRowToAuthorizable.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/query/XPathQueryBuilder.java
    jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/security/user/UserTest.java

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/UserManagerImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/UserManagerImpl.java?rev=1431450&r1=1431449&r2=1431450&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/UserManagerImpl.java
(original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/UserManagerImpl.java
Thu Jan 10 15:53:35 2013
@@ -23,6 +23,7 @@ import java.util.Iterator;
 import java.util.List;
 import javax.annotation.CheckForNull;
 import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
 import javax.jcr.RepositoryException;
 import javax.jcr.UnsupportedRepositoryOperationException;
 
@@ -37,6 +38,7 @@ import org.apache.jackrabbit.oak.api.Tre
 import org.apache.jackrabbit.oak.namepath.NamePathMapper;
 import org.apache.jackrabbit.oak.plugins.nodetype.ReadOnlyNodeTypeManager;
 import org.apache.jackrabbit.oak.security.principal.PrincipalImpl;
+import org.apache.jackrabbit.oak.security.user.query.UserQueryManager;
 import org.apache.jackrabbit.oak.spi.security.ConfigurationParameters;
 import org.apache.jackrabbit.oak.spi.security.SecurityProvider;
 import org.apache.jackrabbit.oak.spi.security.principal.EveryonePrincipal;
@@ -124,7 +126,7 @@ public class UserManagerImpl implements 
     @Override
     public Iterator<Authorizable> findAuthorizables(Query query) throws RepositoryException
{
         checkIsLive();
-        return getQueryManager().find(query);
+        return getQueryManager().findAuthorizables(query);
     }
 
     @Override
@@ -134,7 +136,8 @@ public class UserManagerImpl implements 
     }
 
     @Override
-    public User createUser(String userID, String password, Principal principal, String intermediatePath)
throws RepositoryException {
+    public User createUser(String userID, String password, Principal principal,
+                           @Nullable String intermediatePath) throws RepositoryException
{
         checkIsLive();
         checkValidID(userID);
         checkValidPrincipal(principal, false);
@@ -167,12 +170,12 @@ public class UserManagerImpl implements 
     }
 
     @Override
-    public Group createGroup(Principal principal, String intermediatePath) throws RepositoryException
{
+    public Group createGroup(Principal principal, @Nullable String intermediatePath) throws
RepositoryException {
         return createGroup(principal.getName(), principal, intermediatePath);
     }
 
     @Override
-    public Group createGroup(String groupID, Principal principal, String intermediatePath)
throws RepositoryException {
+    public Group createGroup(String groupID, Principal principal, @Nullable String intermediatePath)
throws RepositoryException {
         checkIsLive();
         checkValidID(groupID);
         checkValidPrincipal(principal, true);
@@ -384,7 +387,7 @@ public class UserManagerImpl implements 
 
     private UserQueryManager getQueryManager() {
         if (queryManager == null) {
-            queryManager = new UserQueryManager(this, root);
+            queryManager = new UserQueryManager(this, namePathMapper, config, root.getQueryEngine());
         }
         return queryManager;
     }

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/query/GroupPredicate.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/query/GroupPredicate.java?rev=1431450&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/query/GroupPredicate.java
(added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/query/GroupPredicate.java
Thu Jan 10 15:53:35 2013
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.oak.security.user.query;
+
+import javax.annotation.Nullable;
+import javax.jcr.RepositoryException;
+
+import com.google.common.base.Predicate;
+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.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * GroupPredicate... TODO
+ */
+class GroupPredicate implements Predicate<Authorizable> {
+
+    static final Logger log = LoggerFactory.getLogger(GroupPredicate.class);
+
+    private final Group group;
+    private final boolean declaredMembersOnly;
+
+    GroupPredicate(UserManager userManager, String groupId, boolean declaredMembersOnly)
throws RepositoryException {
+        Authorizable authorizable = userManager.getAuthorizable(groupId);
+        group = (authorizable == null || !authorizable.isGroup()) ? null : (Group) authorizable;
+        this.declaredMembersOnly = declaredMembersOnly;
+    }
+
+    @Override
+    public boolean apply(@Nullable Authorizable authorizable) {
+        if (group != null && authorizable != null) {
+            try {
+                return (declaredMembersOnly) ? group.isDeclaredMember(authorizable) : group.isMember(authorizable);
+            } catch (RepositoryException e) {
+                log.debug("Cannot determine group membership for {}", authorizable, e.getMessage());
+            }
+        }
+        return false;
+    }
+}
\ No newline at end of file

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/query/QueryUtil.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/query/QueryUtil.java?rev=1431450&r1=1431449&r2=1431450&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/query/QueryUtil.java
(original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/query/QueryUtil.java
Thu Jan 10 15:53:35 2013
@@ -17,14 +17,19 @@
 package org.apache.jackrabbit.oak.security.user.query;
 
 import javax.annotation.Nonnull;
+import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
+import javax.jcr.Value;
 
+import org.apache.jackrabbit.api.security.user.QueryBuilder;
 import org.apache.jackrabbit.oak.spi.security.ConfigurationParameters;
 import org.apache.jackrabbit.oak.spi.security.user.AuthorizableType;
 import org.apache.jackrabbit.oak.spi.security.user.UserConstants;
 import org.apache.jackrabbit.oak.spi.security.user.util.UserUtility;
+import org.apache.jackrabbit.util.Text;
 
 /**
- * QueryUtil... TODO
+ * Common utilities used for user/group queries.
  */
 public final class QueryUtil {
 
@@ -69,4 +74,82 @@ public final class QueryUtil {
             return UserConstants.NT_REP_AUTHORIZABLE;
         }
     }
+
+    /**
+     * Escape {@code string} for matching in jcr escaped node names
+     *
+     * @param string string to escape
+     * @return escaped string
+     */
+    @Nonnull
+    public static String escapeNodeName(String string) {
+        StringBuilder result = new StringBuilder();
+
+        int k = 0;
+        int j;
+        do {
+            j = string.indexOf('%', k);
+            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();
+    }
+
+    @Nonnull
+    public static String format(Value value) throws RepositoryException {
+        switch (value.getType()) {
+            case PropertyType.STRING:
+            case PropertyType.BOOLEAN:
+                return '\'' + value.getString() + '\'';
+
+            case PropertyType.LONG:
+            case PropertyType.DOUBLE:
+                return value.getString();
+
+            case PropertyType.DATE:
+                return "xs:dateTime('" + value.getString() + "')";
+
+            default:
+                throw new RepositoryException("Property of type " + PropertyType.nameFromValue(value.getType())
+ " not supported");
+        }
+    }
+
+    @Nonnull
+    public static String escapeForQuery(String value) {
+        StringBuilder ret = new StringBuilder();
+        for (int i = 0; i < value.length(); i++) {
+            char c = value.charAt(i);
+            if (c == '\\') {
+                ret.append("\\\\");
+            } else if (c == '\'') {
+                ret.append("''");
+            } else {
+                ret.append(c);
+            }
+        }
+        return ret.toString();
+    }
+
+    @Nonnull
+    public static RelationOp getCollation(QueryBuilder.Direction direction) throws RepositoryException
{
+        switch (direction) {
+            case ASCENDING:
+                return RelationOp.GT;
+            case DESCENDING:
+                return RelationOp.LT;
+            default:
+                throw new RepositoryException("Unknown sort order " + direction);
+        }
+    }
 }

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/query/ResultRowToAuthorizable.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/query/ResultRowToAuthorizable.java?rev=1431450&r1=1431449&r2=1431450&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/query/ResultRowToAuthorizable.java
(original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/query/ResultRowToAuthorizable.java
Thu Jan 10 15:53:35 2013
@@ -28,13 +28,13 @@ import org.slf4j.LoggerFactory;
 /**
  * ResultRowToAuthorizable... TODO
  */
-public class ResultRowToAuthorizable implements Function<ResultRow, Authorizable> {
+class ResultRowToAuthorizable implements Function<ResultRow, Authorizable> {
 
     private static final Logger log = LoggerFactory.getLogger(ResultRowToAuthorizable.class);
 
     private final UserManager userManager;
 
-    public ResultRowToAuthorizable(UserManager userManager) {
+    ResultRowToAuthorizable(UserManager userManager) {
         this.userManager = userManager;
     }
 

Copied: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/query/UserQueryManager.java
(from r1431265, jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/UserQueryManager.java)
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/query/UserQueryManager.java?p2=jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/query/UserQueryManager.java&p1=jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/UserQueryManager.java&r1=1431265&r2=1431450&rev=1431450&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/UserQueryManager.java
(original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/query/UserQueryManager.java
Thu Jan 10 15:53:35 2013
@@ -14,24 +14,25 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.jackrabbit.oak.security.user;
+package org.apache.jackrabbit.oak.security.user.query;
 
 import java.text.ParseException;
 import java.util.Iterator;
 import javax.annotation.Nonnull;
 import javax.jcr.RepositoryException;
+import javax.jcr.Value;
 
+import com.google.common.base.Predicate;
 import com.google.common.base.Predicates;
 import com.google.common.collect.Iterators;
 import org.apache.jackrabbit.api.security.user.Authorizable;
 import org.apache.jackrabbit.api.security.user.Query;
+import org.apache.jackrabbit.api.security.user.QueryBuilder;
+import org.apache.jackrabbit.api.security.user.UserManager;
 import org.apache.jackrabbit.oak.api.QueryEngine;
-import org.apache.jackrabbit.oak.api.Result;
-import org.apache.jackrabbit.oak.api.Root;
-import org.apache.jackrabbit.oak.security.user.query.QueryUtil;
-import org.apache.jackrabbit.oak.security.user.query.ResultRowToAuthorizable;
-import org.apache.jackrabbit.oak.security.user.query.XPathQueryBuilder;
-import org.apache.jackrabbit.oak.security.user.query.XPathQueryEvaluator;
+import org.apache.jackrabbit.oak.api.ResultRow;
+import org.apache.jackrabbit.oak.namepath.NamePathMapper;
+import org.apache.jackrabbit.oak.spi.security.ConfigurationParameters;
 import org.apache.jackrabbit.oak.spi.security.user.AuthorizableType;
 import org.apache.jackrabbit.util.ISO9075;
 import org.apache.jackrabbit.util.Text;
@@ -40,73 +41,154 @@ import org.slf4j.LoggerFactory;
 
 /**
  * UserQueryManager... TODO
+ *
+ * TODO OAK-253: replace usage of XPATH
  */
-class UserQueryManager {
+public class UserQueryManager {
 
     private static final Logger log = LoggerFactory.getLogger(UserQueryManager.class);
 
-    private final UserManagerImpl userManager;
-    private final Root root;
+    private final UserManager userManager;
+    private final NamePathMapper namePathMapper;
+    private final ConfigurationParameters config;
+    private final QueryEngine queryEngine;
 
-    UserQueryManager(UserManagerImpl userManager, Root root) {
+    public UserQueryManager(UserManager userManager, NamePathMapper namePathMapper,
+                            ConfigurationParameters config, QueryEngine queryEngine) {
         this.userManager = userManager;
-        this.root = root;
+        this.namePathMapper = namePathMapper;
+        this.config = config;
+        this.queryEngine = queryEngine;
     }
 
+    /**
+     * Find the authorizables matching the specified user {@code Query}.
+     *
+     * @param query A query object.
+     * @return An iterator of authorizables that match the specified query.
+     * @throws RepositoryException If an error occurs.
+     */
     @Nonnull
-    Iterator<Authorizable> find(Query query) throws RepositoryException {
-        // TODO OAK-253: replace usage of XPATH
+    public Iterator<Authorizable> findAuthorizables(Query query) throws RepositoryException
{
         XPathQueryBuilder builder = new XPathQueryBuilder();
         query.build(builder);
-        return new XPathQueryEvaluator(builder, userManager, root, userManager.getNamePathMapper(),
userManager.getConfig()).eval();
+
+        if (builder.getMaxCount() == 0) {
+            return Iterators.emptyIterator();
+        }
+
+        Value bound = builder.getBound();
+        Condition condition = builder.getCondition();
+        String sortCol = builder.getSortProperty();
+        QueryBuilder.Direction sortDir = builder.getSortDirection();
+        if (bound != null) {
+            if (sortCol == null) {
+                log.warn("Ignoring bound {} since no sort order is specified");
+            } else {
+                Condition boundCondition = builder.property(sortCol, QueryUtil.getCollation(sortDir),
bound);
+                if (condition == null) {
+                    condition = boundCondition;
+                } else {
+                    condition = builder.and(condition, boundCondition);
+                }
+            }
+        }
+
+        StringBuilder statement = new StringBuilder();
+        ConditionVisitor visitor = new XPathConditionVisitor(statement, namePathMapper);
+
+        String searchRoot = QueryUtil.getSearchRoot(builder.getSelectorType(), config);
+        String ntName = QueryUtil.getNodeTypeName(builder.getSelectorType());
+        statement.append(searchRoot).append("//element(*,").append(ntName).append(')');
+
+        if (condition != null) {
+            statement.append('[');
+            condition.accept(visitor);
+            statement.append(']');
+        }
+
+        if (sortCol != null) {
+            boolean ignoreCase = builder.getSortIgnoreCase();
+            statement.append(" order by ")
+                    .append(ignoreCase ? "" : "fn:lower-case(")
+                    .append(sortCol)
+                    .append(ignoreCase ? " " : ") ")
+                    .append(sortDir.getDirection());
+        }
+
+        if (builder.getGroupName() == null) {
+            long offset = builder.getOffset();
+            if (bound != null && offset > 0) {
+                log.warn("Found bound {} and offset {} in limit. Discarding offset.", bound,
offset);
+                offset = 0;
+            }
+            return findAuthorizables(statement.toString(), builder.getMaxCount(), offset);
+        } else {
+            // filtering by group name included in query -> enforce offset
+            // and limit on the result set.
+            Iterator<Authorizable> result = findAuthorizables(statement.toString(),
Long.MAX_VALUE, 0);
+            Predicate groupFilter = new GroupPredicate(userManager,
+                    builder.getGroupName(),
+                    builder.isDeclaredMembersOnly());
+            return ResultIterator.create(builder.getOffset(), builder.getMaxCount(),
+                    Iterators.filter(result, groupFilter));
+        }
     }
 
+    /**
+     * Find the authorizables matching the following search parameters within
+     * the sub-tree defined by an authorizable tree:
+     *
+     * @param relPath A relative path (or a name) pointing to properties within
+     * the tree defined by a given authorizable node.
+     * @param value The property value to look for.
+     * @param authorizableType Filter the search results to only return authorizable
+     * trees of a given type. Passing {@link org.apache.jackrabbit.oak.spi.security.user.AuthorizableType#AUTHORIZABLE}
indicates that
+     * no filtering for a specific authorizable type is desired. However, properties
+     * might still be search in the complete sub-tree of authorizables depending
+     * on the other query parameters.
+     * @return An iterator of authorizable trees that match the specified
+     * search parameters and filters or an empty iterator if no result can be
+     * found.
+     * @throws javax.jcr.RepositoryException If an error occurs.
+     */
     @Nonnull
-    Iterator<Authorizable> findAuthorizables(String relativePath, String value,
-                                             AuthorizableType authorizableType) throws RepositoryException
{
-        return findAuthorizables(relativePath, value, true, authorizableType);
+    public Iterator<Authorizable> findAuthorizables(String relPath, String value,
+                                                    AuthorizableType authorizableType) throws
RepositoryException {
+        return findAuthorizables(relPath, value, authorizableType, true);
     }
 
     /**
-     * Find the authorizable trees matching the following search parameters within
+     * Find the authorizables matching the following search parameters within
      * the sub-tree defined by an authorizable tree:
      *
+     *
      * @param relPath A relative path (or a name) pointing to properties within
      * the tree defined by a given authorizable node.
      * @param value The property value to look for.
-     * @param exact A boolean flag indicating if the value must match exactly or not.s
-     * @param type Filter the search results to only return authorizable
+     * @param authorizableType Filter the search results to only return authorizable
      * trees of a given type. Passing {@link org.apache.jackrabbit.oak.spi.security.user.AuthorizableType#AUTHORIZABLE}
indicates that
      * no filtering for a specific authorizable type is desired. However, properties
      * might still be search in the complete sub-tree of authorizables depending
      * on the other query parameters.
+     * @param exact A boolean flag indicating if the value must match exactly or not.s
      * @return An iterator of authorizable trees that match the specified
      * search parameters and filters or an empty iterator if no result can be
      * found.
      * @throws javax.jcr.RepositoryException If an error occurs.
      */
     @Nonnull
-    Iterator<Authorizable> findAuthorizables(String relPath, String value,
-                                             boolean exact, AuthorizableType type) throws
RepositoryException {
-        // TODO OAK-480: fix authorizable query
-        // TODO OAK-253: replace usage of XPATH
-        String statement = buildXPathStatement(relPath, value, exact, type);
-        QueryEngine queryEngine = root.getQueryEngine();
-        try {
-            Result result = queryEngine.executeQuery(statement, javax.jcr.query.Query.XPATH,
Long.MAX_VALUE, 0, null, userManager.getNamePathMapper());
-            Iterator<Authorizable> authorizables = Iterators.transform(result.getRows().iterator(),
new ResultRowToAuthorizable(userManager));
-            return Iterators.filter(authorizables, Predicates.<Object>notNull());
-        } catch (ParseException e) {
-            log.warn("Invalid user query: " + statement, e);
-            throw new RepositoryException(e);
-        }
+    public Iterator<Authorizable> findAuthorizables(String relPath, String value,
+                                                    AuthorizableType authorizableType, boolean
exact) throws RepositoryException {
+        String statement = buildXPathStatement(relPath, value, authorizableType, exact);
+        return findAuthorizables(statement, Long.MAX_VALUE, 0);
     }
 
     //------------------------------------------------------------< private >---
     @Nonnull
-    private String buildXPathStatement(String relPath, String value, boolean exact, AuthorizableType
type) {
+    private String buildXPathStatement(String relPath, String value, AuthorizableType type,
boolean exact) {
         StringBuilder stmt = new StringBuilder();
-        String searchRoot = QueryUtil.getSearchRoot(type, userManager.getConfig());
+        String searchRoot = QueryUtil.getSearchRoot(type, config);
         if (!"/".equals(searchRoot)) {
             stmt.append(searchRoot);
         }
@@ -116,12 +198,12 @@ class UserQueryManager {
         String ntName;
         if (relPath.indexOf('/') == -1) {
             // search for properties somewhere below an authorizable node
-            propName = userManager.getNamePathMapper().getOakName(relPath);
+            propName = namePathMapper.getOakName(relPath);
             path = null;
             ntName = null;
         } else {
             // FIXME: proper normalization of the relative path
-            String oakPath = userManager.getNamePathMapper().getOakPath(relPath);
+            String oakPath = namePathMapper.getOakPath(relPath);
             propName = Text.getName(oakPath);
             path = Text.getRelativeParent(oakPath, 1);
             ntName = QueryUtil.getNodeTypeName(type);
@@ -149,7 +231,7 @@ class UserQueryManager {
                 stmt.append('\'');
             } else {
                 stmt.append(",'%");
-                stmt.append(escapeForQuery(value));
+                stmt.append(QueryUtil.escapeForQuery(value));
                 stmt.append("%')");
             }
             stmt.append(']');
@@ -158,18 +240,15 @@ class UserQueryManager {
     }
 
     @Nonnull
-    private static String escapeForQuery(String value) {
-        StringBuilder ret = new StringBuilder();
-        for (int i = 0; i < value.length(); i++) {
-            char c = value.charAt(i);
-            if (c == '\\') {
-                ret.append("\\\\");
-            } else if (c == '\'') {
-                ret.append("''");
-            } else {
-                ret.append(c);
-            }
+    private Iterator<Authorizable> findAuthorizables(String statement, long limit,
+                                                    long offset) throws RepositoryException
{
+        try {
+            Iterable<? extends ResultRow> resultRows = queryEngine.executeQuery(statement,
javax.jcr.query.Query.XPATH, limit, offset, null, namePathMapper).getRows();
+            Iterator<Authorizable> authorizables = Iterators.transform(resultRows.iterator(),
new ResultRowToAuthorizable(userManager));
+            return Iterators.filter(authorizables, Predicates.<Object>notNull());
+        } catch (ParseException e) {
+            log.warn("Invalid user query: " + statement, e);
+            throw new RepositoryException(e);
         }
-        return ret.toString();
     }
 }
\ No newline at end of file

Copied: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/query/XPathConditionVisitor.java
(from r1431265, jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/query/XPathQueryEvaluator.java)
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/query/XPathConditionVisitor.java?p2=jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/query/XPathConditionVisitor.java&p1=jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/query/XPathQueryEvaluator.java&r1=1431265&r2=1431450&rev=1431450&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/query/XPathQueryEvaluator.java
(original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/query/XPathConditionVisitor.java
Thu Jan 10 15:53:35 2013
@@ -16,114 +16,26 @@
  */
 package org.apache.jackrabbit.oak.security.user.query;
 
-import java.text.ParseException;
-import java.util.Iterator;
-import javax.annotation.Nonnull;
-import javax.jcr.PropertyType;
 import javax.jcr.RepositoryException;
-import javax.jcr.Value;
-import javax.jcr.query.Query;
 
-import com.google.common.base.Predicate;
-import com.google.common.base.Predicates;
-import com.google.common.collect.Iterators;
-import org.apache.jackrabbit.api.security.user.Authorizable;
-import org.apache.jackrabbit.api.security.user.Group;
-import org.apache.jackrabbit.api.security.user.QueryBuilder;
-import org.apache.jackrabbit.api.security.user.UserManager;
-import org.apache.jackrabbit.oak.api.ResultRow;
-import org.apache.jackrabbit.oak.api.Root;
 import org.apache.jackrabbit.oak.namepath.NamePathMapper;
-import org.apache.jackrabbit.oak.spi.security.ConfigurationParameters;
 import org.apache.jackrabbit.oak.spi.security.user.UserConstants;
-import org.apache.jackrabbit.util.Text;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 /**
- * XPATH based evaluation of {@link org.apache.jackrabbit.api.security.user.Query}s.
+ * XPATH based condition visitor.
  */
-public class XPathQueryEvaluator implements ConditionVisitor {
+class XPathConditionVisitor implements ConditionVisitor {
 
-    static final Logger log = LoggerFactory.getLogger(XPathQueryEvaluator.class);
+    static final Logger log = LoggerFactory.getLogger(XPathConditionVisitor.class);
 
-    private final XPathQueryBuilder builder;
-    private final UserManager userManager;
-    private final Root root;
+    private final StringBuilder statement;
     private final NamePathMapper namePathMapper;
-    private final ConfigurationParameters config;
 
-    private final StringBuilder statement = new StringBuilder();
-
-    public XPathQueryEvaluator(XPathQueryBuilder builder, UserManager userManager,
-                               Root root, NamePathMapper namePathMapper,
-                               ConfigurationParameters config) {
-        this.builder = builder;
-        this.userManager = userManager;
-        this.root = root;
+    XPathConditionVisitor(StringBuilder statement, NamePathMapper namePathMapper) {
+        this.statement = statement;
         this.namePathMapper = namePathMapper;
-        this.config = config;
-    }
-
-    public Iterator<Authorizable> eval() throws RepositoryException {
-        if (builder.getMaxCount() == 0) {
-            return Iterators.emptyIterator();
-        }
-
-        Value bound = builder.getBound();
-        Condition condition = builder.getCondition();
-        String sortCol = builder.getSortProperty();
-        QueryBuilder.Direction sortDir = builder.getSortDirection();
-        if (bound != null) {
-            if (sortCol == null) {
-                log.warn("Ignoring bound {} since no sort order is specified");
-            } else {
-                Condition boundCondition = builder.property(sortCol, getCollation(sortDir),
bound);
-                if (condition == null) {
-                    condition = boundCondition;
-                } else {
-                    condition = builder.and(condition, boundCondition);
-                }
-            }
-        }
-
-        String searchRoot = QueryUtil.getSearchRoot(builder.getSelectorType(), config);
-        String ntName = QueryUtil.getNodeTypeName(builder.getSelectorType());
-        statement.append(searchRoot).append("//element(*,").append(ntName).append(')');
-
-        if (condition != null) {
-            statement.append('[');
-            condition.accept(this);
-            statement.append(']');
-        }
-
-        if (sortCol != null) {
-            boolean ignoreCase = builder.getSortIgnoreCase();
-            statement.append(" order by ")
-                    .append(ignoreCase ? "" : "fn:lower-case(")
-                    .append(sortCol)
-                    .append(ignoreCase ? " " : ") ")
-                    .append(sortDir.getDirection());
-        }
-
-        try {
-            if (builder.getGroupName() == null) {
-                long offset = builder.getOffset();
-                if (bound != null && offset > 0) {
-                    log.warn("Found bound {} and offset {} in limit. Discarding offset.",
bound, offset);
-                    offset = 0;
-                }
-                return findAuthorizables(builder.getMaxCount(), offset);
-            } else {
-                // filtering by group name included in query -> enforce offset
-                // and limit on the result set.
-                Iterator<Authorizable> result = findAuthorizables(Long.MAX_VALUE, 0);
-                Iterator<Authorizable> filtered = filter(result, builder.getGroupName(),
builder.isDeclaredMembersOnly());
-                return ResultIterator.create(builder.getOffset(), builder.getMaxCount(),
filtered);
-            }
-        } catch (ParseException e) {
-            throw new RepositoryException(e);
-        }
     }
 
     //---------------------------------------------------< ConditionVisitor >---
@@ -137,7 +49,7 @@ public class XPathQueryEvaluator impleme
                 .append("')")
                 .append(" or ")
                 .append("jcr:like(fn:name(),'")
-                .append(escape(condition.getPattern()))
+                .append(QueryUtil.escapeNodeName(condition.getPattern()))
                 .append("')")
                 .append(')');
     }
@@ -156,7 +68,7 @@ public class XPathQueryEvaluator impleme
         } else {
             statement.append(condition.getRelPath())
                     .append(condition.getOp().getOp())
-                    .append(format(condition.getValue()));
+                    .append(QueryUtil.format(condition.getValue()));
         }
     }
 
@@ -208,99 +120,4 @@ public class XPathQueryEvaluator impleme
             statement.append(')');
         }
     }
-
-    //------------------------------------------------------------< private >---
-    /**
-     * Escape {@code string} for matching in jcr escaped node names
-     *
-     * @param string string to escape
-     * @return escaped string
-     */
-    @Nonnull
-    public static String escape(String string) {
-        StringBuilder result = new StringBuilder();
-
-        int k = 0;
-        int j;
-        do {
-            j = string.indexOf('%', k);
-            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();
-    }
-
-
-    @Nonnull
-    private static String format(Value value) throws RepositoryException {
-        switch (value.getType()) {
-            case PropertyType.STRING:
-            case PropertyType.BOOLEAN:
-                return '\'' + value.getString() + '\'';
-
-            case PropertyType.LONG:
-            case PropertyType.DOUBLE:
-                return value.getString();
-
-            case PropertyType.DATE:
-                return "xs:dateTime('" + value.getString() + "')";
-
-            default:
-                throw new RepositoryException("Property of type " + PropertyType.nameFromValue(value.getType())
+ " not supported");
-        }
-    }
-
-    @Nonnull
-    private static RelationOp getCollation(QueryBuilder.Direction direction) throws RepositoryException
{
-        switch (direction) {
-            case ASCENDING:
-                return RelationOp.GT;
-            case DESCENDING:
-                return RelationOp.LT;
-            default:
-                throw new RepositoryException("Unknown sort order " + direction);
-        }
-    }
-
-    @Nonnull
-    private Iterator<Authorizable> findAuthorizables(long limit, long offset) throws
ParseException {
-        Iterable<? extends ResultRow> resultRows = root.getQueryEngine().executeQuery(statement.toString(),
Query.XPATH, limit, offset, null, namePathMapper).getRows();
-        Iterator<Authorizable> authorizables = Iterators.transform(resultRows.iterator(),
new ResultRowToAuthorizable(userManager));
-        return Iterators.filter(authorizables, Predicates.<Object>notNull());
-    }
-
-    @Nonnull
-    private Iterator<Authorizable> filter(Iterator<Authorizable> authorizables,
-                                          String groupName,
-                                          final boolean declaredMembersOnly) throws RepositoryException
{
-        Predicate<Authorizable> predicate;
-        Authorizable authorizable = userManager.getAuthorizable(groupName);
-        if (authorizable == null || !authorizable.isGroup()) {
-            predicate = Predicates.alwaysFalse();
-        } else {
-            final Group group = (Group) authorizable;
-            predicate = new Predicate<Authorizable>() {
-                public boolean apply(Authorizable authorizable) {
-                    try {
-                        return (declaredMembersOnly) ? group.isDeclaredMember(authorizable)
: group.isMember(authorizable);
-                    } catch (RepositoryException e) {
-                        log.debug("Cannot determine group membership for {}", authorizable,
e.getMessage());
-                        return false;
-                    }
-                }
-            };
-        }
-        return Iterators.filter(authorizables, predicate);
-    }
 }
\ No newline at end of file

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/query/XPathQueryBuilder.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/query/XPathQueryBuilder.java?rev=1431450&r1=1431449&r2=1431450&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/query/XPathQueryBuilder.java
(original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/query/XPathQueryBuilder.java
Thu Jan 10 15:53:35 2013
@@ -24,7 +24,7 @@ import org.apache.jackrabbit.api.securit
 import org.apache.jackrabbit.api.security.user.User;
 import org.apache.jackrabbit.oak.spi.security.user.AuthorizableType;
 
-public class XPathQueryBuilder implements QueryBuilder<Condition> {
+class XPathQueryBuilder implements QueryBuilder<Condition> {
 
     private AuthorizableType selectorType = AuthorizableType.AUTHORIZABLE;
     private String groupName;

Added: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/user/query/UserQueryManagerTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/user/query/UserQueryManagerTest.java?rev=1431450&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/user/query/UserQueryManagerTest.java
(added)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/user/query/UserQueryManagerTest.java
Thu Jan 10 15:53:35 2013
@@ -0,0 +1,142 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.oak.security.user.query;
+
+import java.util.Iterator;
+import javax.jcr.Value;
+import javax.jcr.ValueFactory;
+
+import org.apache.jackrabbit.api.security.user.Authorizable;
+import org.apache.jackrabbit.api.security.user.User;
+import org.apache.jackrabbit.api.security.user.UserManager;
+import org.apache.jackrabbit.oak.api.Root;
+import org.apache.jackrabbit.oak.namepath.NamePathMapper;
+import org.apache.jackrabbit.oak.plugins.value.ValueFactoryImpl;
+import org.apache.jackrabbit.oak.security.AbstractSecurityTest;
+import org.apache.jackrabbit.oak.spi.security.user.AuthorizableType;
+import org.apache.jackrabbit.oak.spi.security.user.UserConfiguration;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * UserQueryManagerTest provides test cases for {@link UserQueryManager}.
+ * This class include the original jr2.x test-cases provided by
+ * {@code NodeResolverTest} and {@code IndexNodeResolverTest}.
+ */
+public class UserQueryManagerTest extends AbstractSecurityTest {
+
+    private Root root;
+    private ValueFactory valueFactory;
+    private UserQueryManager queryMgr;
+    private User user;
+    private String propertyName;
+
+    @Before
+    public void before() throws Exception {
+        super.before();
+
+        UserConfiguration uc = securityProvider.getUserConfiguration();
+        NamePathMapper npMapper = NamePathMapper.DEFAULT;
+
+        root = admin.getLatestRoot();
+
+        UserManager userMgr = uc.getUserManager(root, npMapper);
+        user = userMgr.createUser("testUser", "pw");
+        root.commit();
+
+        queryMgr = new UserQueryManager(userMgr, npMapper, uc.getConfigurationParameters(),
root.getQueryEngine());
+
+        valueFactory = new ValueFactoryImpl(root.getBlobFactory(), npMapper);
+        propertyName = "testProperty";
+    }
+
+    /**
+     * @since oak
+     */
+    @Test
+    public void testFindNodesExact() throws Exception {
+        Value vs = valueFactory.createValue("value \\, containing backslash");
+        user.setProperty(propertyName, vs);
+        root.commit();
+
+        try {
+            Iterator<Authorizable> result = queryMgr.findAuthorizables(propertyName,
"value \\, containing backslash", AuthorizableType.USER, true);
+            assertTrue("expected result", result.hasNext());
+            assertEquals(user.getID(), result.next().getID());
+            assertFalse("expected no more results", result.hasNext());
+        } finally {
+            user.removeProperty(propertyName);
+            root.commit();
+        }
+    }
+
+    @Test
+    public void testFindNodesNonExact() throws Exception {
+        Value vs = valueFactory.createValue("value \\, containing backslash");
+        user.setProperty(propertyName, vs);
+        root.commit();
+
+        try {
+            Iterator<Authorizable> result = queryMgr.findAuthorizables(propertyName,
"value \\, containing backslash", AuthorizableType.USER, false);
+            assertTrue("expected result", result.hasNext());
+            assertEquals(user.getID(), result.next().getID());
+            assertFalse("expected no more results", result.hasNext());
+        } finally {
+            user.removeProperty(propertyName);
+            root.commit();
+        }
+    }
+
+    @Test
+    public void testFindNodesNonExactWithApostrophe() throws Exception {
+        Value vs = valueFactory.createValue("value ' with apostrophe");
+        try {
+            user.setProperty(propertyName, vs);
+            root.commit();
+
+            Iterator<Authorizable> result = queryMgr.findAuthorizables(propertyName,
"value ' with apostrophe", AuthorizableType.USER, false);
+
+            assertTrue("expected result", result.hasNext());
+            assertEquals(user.getID(), result.next().getID());
+            assertFalse("expected no more results", result.hasNext());
+        } finally {
+            user.removeProperty(propertyName);
+            root.commit();
+        }
+    }
+
+    @Test
+    public void testFindNodesExactWithApostrophe() throws Exception {
+        Value vs = valueFactory.createValue("value ' with apostrophe");
+        try {
+            user.setProperty(propertyName, vs);
+            root.commit();
+
+            Iterator<Authorizable> result = queryMgr.findAuthorizables(propertyName,
"value ' with apostrophe", AuthorizableType.USER, true);
+            assertTrue("expected result", result.hasNext());
+            assertEquals(user.getID(), result.next().getID());
+            assertFalse("expected no more results", result.hasNext());
+        } finally {
+            user.removeProperty(propertyName);
+            root.commit();
+        }
+    }
+}
\ No newline at end of file

Modified: jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/security/user/UserTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/security/user/UserTest.java?rev=1431450&r1=1431449&r2=1431450&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/security/user/UserTest.java
(original)
+++ jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/security/user/UserTest.java
Thu Jan 10 15:53:35 2013
@@ -127,7 +127,7 @@ public class UserTest extends AbstractUs
         }
     }
 
-      @Test
+    @Test
     public void testLoginAfterChangePassword() throws RepositoryException {
         user.changePassword("changed");
         superuser.save();
@@ -266,6 +266,7 @@ public class UserTest extends AbstractUs
         }
     }
 
+    @Test
     public void testLoginWithGetCredentials() throws RepositoryException, NotExecutableException
{
         try {
             Credentials creds = user.getCredentials();



Mime
View raw message