Author: angela
Date: Mon Feb  9 11:03:21 2015
New Revision: 1658358

URL: http://svn.apache.org/r1658358
Log:
OAK-2485 : Add validating commit hook for login tokens
OAK-2486 : TokenProviderImpl.createToken should always create valid node

Added:
    
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/token/CommitMarker.java
    
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/token/TokenConstants.java
    
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/token/TokenValidatorProvider.java
    
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authentication/token/TokenValidatorTest.java
      - copied, changed from r1656763, 
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authentication/token/TokenProviderImplTest.java
Modified:
    
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/token/TokenConfigurationImpl.java
    
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/token/TokenProviderImpl.java
    
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authentication/token/AbstractTokenTest.java
    
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authentication/token/TokenInfoTest.java
    
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authentication/token/TokenProviderImplTest.java
    
jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/query/QueryTest.java

Added: 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/token/CommitMarker.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/token/CommitMarker.java?rev=1658358&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/token/CommitMarker.java
 (added)
+++ 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/token/CommitMarker.java
 Mon Feb  9 11:03:21 2015
@@ -0,0 +1,48 @@
+/*
+ * 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.authentication.token;
+
+import java.util.Collections;
+import java.util.Map;
+
+import javax.annotation.Nonnull;
+
+import org.apache.jackrabbit.oak.spi.commit.CommitInfo;
+
+/**
+ * Marker object to help the {@link 
org.apache.jackrabbit.oak.security.authentication.token.TokenValidatorProvider.TokenValidator}
+ * identifying if login tokens have been issued and updated by the {@code 
TokenProvider}
+ * implementation provided by this package and not through regular write
+ * operations on the Oak API which doesn't enforce the protected status
+ * of the login tokens as defined by this implementation.
+ */
+final class CommitMarker {
+
+    private static final String KEY = CommitMarker.class.getName();
+
+    private static final CommitMarker INSTANCE = new CommitMarker();
+
+    static Map<String, Object> asCommitAttributes() {
+        return Collections.<String, Object>singletonMap(CommitMarker.KEY, 
CommitMarker.INSTANCE);
+    }
+
+    static boolean isValidCommitInfo(@Nonnull CommitInfo commitInfo) {
+        return CommitMarker.INSTANCE == 
commitInfo.getInfo().get(CommitMarker.KEY);
+    }
+
+    private CommitMarker() {}
+}
\ No newline at end of file

Modified: 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/token/TokenConfigurationImpl.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/token/TokenConfigurationImpl.java?rev=1658358&r1=1658357&r2=1658358&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/token/TokenConfigurationImpl.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/token/TokenConfigurationImpl.java
 Mon Feb  9 11:03:21 2015
@@ -16,15 +16,21 @@
  */
 package org.apache.jackrabbit.oak.security.authentication.token;
 
+import java.security.Principal;
+import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import javax.annotation.Nonnull;
 
+import com.google.common.collect.ImmutableList;
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
 import org.apache.felix.scr.annotations.Properties;
 import org.apache.felix.scr.annotations.Property;
 import org.apache.felix.scr.annotations.Service;
 import org.apache.jackrabbit.oak.api.Root;
+import org.apache.jackrabbit.oak.spi.commit.MoveTracker;
+import org.apache.jackrabbit.oak.spi.commit.ValidatorProvider;
 import org.apache.jackrabbit.oak.spi.security.ConfigurationBase;
 import org.apache.jackrabbit.oak.spi.security.ConfigurationParameters;
 import org.apache.jackrabbit.oak.spi.security.SecurityConfiguration;
@@ -82,6 +88,13 @@ public class TokenConfigurationImpl exte
         return NAME;
     }
 
+    @Nonnull
+    @Override
+    public List<? extends ValidatorProvider> getValidators(String 
workspaceName, Set<Principal> principals, MoveTracker moveTracker) {
+        ValidatorProvider vp = new 
TokenValidatorProvider(getSecurityProvider().getParameters(UserConfiguration.NAME));
+        return ImmutableList.of(vp);
+    }
+
     //-------------------------------------------------< TokenConfiguration 
>---
     /**
      * Returns a new instance of {@link 
org.apache.jackrabbit.oak.spi.security.authentication.token.TokenProvider}.

Added: 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/token/TokenConstants.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/token/TokenConstants.java?rev=1658358&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/token/TokenConstants.java
 (added)
+++ 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/token/TokenConstants.java
 Mon Feb  9 11:03:21 2015
@@ -0,0 +1,45 @@
+/*
+ * 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.authentication.token;
+
+import java.util.Set;
+
+import com.google.common.collect.ImmutableSet;
+import org.apache.jackrabbit.oak.plugins.nodetype.NodeTypeConstants;
+
+interface TokenConstants {
+
+    /**
+     * Constant for the token attribute passed with valid simple credentials to
+     * trigger the generation of a new token.
+     */
+    String TOKEN_ATTRIBUTE = ".token";
+    String TOKEN_ATTRIBUTE_EXPIRY = "rep:token.exp";
+    String TOKEN_ATTRIBUTE_KEY = "rep:token.key";
+
+    String TOKENS_NODE_NAME = ".tokens";
+    String TOKENS_NT_NAME = NodeTypeConstants.NT_REP_UNSTRUCTURED;
+
+    String TOKEN_NT_NAME = "rep:Token";
+
+    Set<String> RESERVED_ATTRIBUTES = ImmutableSet.of(
+            TOKEN_ATTRIBUTE,
+            TOKEN_ATTRIBUTE_EXPIRY,
+            TOKEN_ATTRIBUTE_KEY);
+
+    Set<String> TOKEN_PROPERTY_NAMES = ImmutableSet.of(TOKEN_ATTRIBUTE_EXPIRY, 
TOKEN_ATTRIBUTE_KEY);
+}
\ No newline at end of file

Modified: 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/token/TokenProviderImpl.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/token/TokenProviderImpl.java?rev=1658358&r1=1658357&r2=1658358&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/token/TokenProviderImpl.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/token/TokenProviderImpl.java
 Mon Feb  9 11:03:21 2015
@@ -26,9 +26,7 @@ import java.util.Collections;
 import java.util.Date;
 import java.util.GregorianCalendar;
 import java.util.HashMap;
-import java.util.HashSet;
 import java.util.Map;
-import java.util.Set;
 import java.util.UUID;
 import javax.annotation.CheckForNull;
 import javax.annotation.Nonnull;
@@ -46,10 +44,9 @@ import org.apache.jackrabbit.oak.api.Com
 import org.apache.jackrabbit.oak.api.PropertyState;
 import org.apache.jackrabbit.oak.api.Root;
 import org.apache.jackrabbit.oak.api.Tree;
-import org.apache.jackrabbit.oak.plugins.identifier.IdentifierManager;
 import org.apache.jackrabbit.oak.namepath.NamePathMapper;
+import org.apache.jackrabbit.oak.plugins.identifier.IdentifierManager;
 import org.apache.jackrabbit.oak.plugins.name.NamespaceConstants;
-import org.apache.jackrabbit.oak.plugins.nodetype.NodeTypeConstants;
 import org.apache.jackrabbit.oak.spi.security.ConfigurationParameters;
 import 
org.apache.jackrabbit.oak.spi.security.authentication.ImpersonationCredentials;
 import org.apache.jackrabbit.oak.spi.security.authentication.token.TokenInfo;
@@ -93,35 +90,17 @@ import static org.apache.jackrabbit.oak.
  * interface will not validate the node type of the token node associated with
  * a given token.
  */
-class TokenProviderImpl implements TokenProvider {
+class TokenProviderImpl implements TokenProvider, TokenConstants {
 
     private static final Logger log = 
LoggerFactory.getLogger(TokenProviderImpl.class);
 
     /**
-     * Constant for the token attribute passed with valid simple credentials to
-     * trigger the generation of a new token.
-     */
-    private static final String TOKEN_ATTRIBUTE = ".token";
-    private static final String TOKEN_ATTRIBUTE_EXPIRY = "rep:token.exp";
-    private static final String TOKEN_ATTRIBUTE_KEY = "rep:token.key";
-    private static final String TOKENS_NODE_NAME = ".tokens";
-    private static final String TOKENS_NT_NAME = 
NodeTypeConstants.NT_REP_UNSTRUCTURED;
-    private static final String TOKEN_NT_NAME = "rep:Token";
-
-    /**
      * Default expiration time in ms for login tokens is 2 hours.
      */
     private static final long DEFAULT_TOKEN_EXPIRATION = 2 * 3600 * 1000;
     private static final int DEFAULT_KEY_SIZE = 8;
     private static final char DELIM = '_';
 
-    private static final Set<String> RESERVED_ATTRIBUTES = new HashSet(3);
-    static {
-        RESERVED_ATTRIBUTES.add(TOKEN_ATTRIBUTE);
-        RESERVED_ATTRIBUTES.add(TOKEN_ATTRIBUTE_EXPIRY);
-        RESERVED_ATTRIBUTES.add(TOKEN_ATTRIBUTE_KEY);
-    }
-
     private final Root root;
     private final ConfigurationParameters options;
 
@@ -133,7 +112,7 @@ class TokenProviderImpl implements Token
         this.root = root;
         this.options = options;
 
-        this.tokenExpiration = options.getConfigValue(PARAM_TOKEN_EXPIRATION, 
Long.valueOf(DEFAULT_TOKEN_EXPIRATION));
+        this.tokenExpiration = options.getConfigValue(PARAM_TOKEN_EXPIRATION, 
DEFAULT_TOKEN_EXPIRATION);
         this.userManager = userConfiguration.getUserManager(root, 
NamePathMapper.DEFAULT);
         this.identifierManager = new IdentifierManager(root);
     }
@@ -213,16 +192,6 @@ class TokenProviderImpl implements Token
             try {
                 String id = user.getID();
                 long creationTime = new Date().getTime();
-                NodeUtil tokenNode = createTokenNode(tokenParent, 
creationTime);
-                tokenNode.setString(JcrConstants.JCR_UUID, 
IdentifierManager.generateUUID());
-
-                String key = 
generateKey(options.getConfigValue(PARAM_TOKEN_LENGTH, DEFAULT_KEY_SIZE));
-                String nodeId = getIdentifier(tokenNode.getTree());
-                String token = new 
StringBuilder(nodeId).append(DELIM).append(key).toString();
-
-                String keyHash = 
PasswordUtil.buildPasswordHash(getKeyValue(key, id), options);
-                tokenNode.setString(TOKEN_ATTRIBUTE_KEY, keyHash);
-
                 long exp;
                 if (attributes.containsKey(PARAM_TOKEN_EXPIRATION)) {
                     exp = 
Long.parseLong(attributes.get(PARAM_TOKEN_EXPIRATION).toString());
@@ -230,16 +199,21 @@ class TokenProviderImpl implements Token
                     exp = tokenExpiration;
                 }
                 long expTime = createExpirationTime(creationTime, exp);
-                tokenNode.setDate(TOKEN_ATTRIBUTE_EXPIRY, expTime);
+                String uuid = UUID.randomUUID().toString();
 
-                for (String name : attributes.keySet()) {
-                    if (!RESERVED_ATTRIBUTES.contains(name)) {
-                        String attr = attributes.get(name).toString();
-                        tokenNode.setString(name, attr);
-                    }
+                TokenInfo tokenInfo;
+                try {
+                    String tokenName = generateTokenName(creationTime);
+                    tokenInfo = createTokenNode(tokenParent, tokenName, 
expTime, uuid, id, attributes);
+                    root.commit(CommitMarker.asCommitAttributes());
+                } catch (CommitFailedException e) {
+                    // conflict while creating token node -> retry
+                    log.debug("Failed to create token node. Using random name 
as fallback.");
+                    root.refresh();
+                    tokenInfo = createTokenNode(tokenParent, 
UUID.randomUUID().toString(), expTime, uuid, id, attributes);
+                    root.commit(CommitMarker.asCommitAttributes());
                 }
-                root.commit();
-                return new TokenInfoImpl(tokenNode, token, id);
+                return tokenInfo;
             } catch (NoSuchAlgorithmException e) {
                 // error while generating login token
                 log.error(error, e.getMessage());
@@ -326,7 +300,7 @@ class TokenProviderImpl implements Token
         return key + userId;
     }
 
-    private static boolean isValidTokenTree(Tree tokenTree) {
+    private static boolean isValidTokenTree(@CheckForNull Tree tokenTree) {
         if (tokenTree == null || !tokenTree.exists()) {
             return false;
         } else {
@@ -335,8 +309,15 @@ class TokenProviderImpl implements Token
         }
     }
 
+    @Nonnull
+    private static String generateTokenName(long creationTime) {
+        Calendar creation = GregorianCalendar.getInstance();
+        creation.setTimeInMillis(creationTime);
+        return Text.replace(ISO8601.format(creation), ":", ".");
+    }
+
     @CheckForNull
-    private Tree getTokenTree(TokenInfo tokenInfo) {
+    private Tree getTokenTree(@Nonnull TokenInfo tokenInfo) {
         if (tokenInfo instanceof TokenInfoImpl) {
             return root.getTree(((TokenInfoImpl) tokenInfo).tokenPath);
         } else {
@@ -345,7 +326,7 @@ class TokenProviderImpl implements Token
     }
 
     @CheckForNull
-    private String getUserId(Tree tokenTree) {
+    private String getUserId(@CheckForNull Tree tokenTree) {
         if (tokenTree != null && tokenTree.exists()) {
             try {
                 String userPath = Text.getRelativeParent(tokenTree.getPath(), 
2);
@@ -361,7 +342,7 @@ class TokenProviderImpl implements Token
     }
 
     @CheckForNull
-    private User getUser(String userId) {
+    private User getUser(@Nonnull String userId) {
         try {
             Authorizable user = userManager.getAuthorizable(userId);
             if (user != null && !user.isGroup()) {
@@ -385,13 +366,12 @@ class TokenProviderImpl implements Token
         String parentPath = null;
         try {
             String userPath = user.getPath();
+            parentPath = userPath + '/' + TOKENS_NODE_NAME;
+
             NodeUtil userNode = new NodeUtil(root.getTree(userPath));
-            tokenParent = userNode.getChild(TOKENS_NODE_NAME);
-            if (tokenParent == null) {
-                tokenParent = userNode.addChild(TOKENS_NODE_NAME, 
TOKENS_NT_NAME);
-                parentPath = userPath + '/' + TOKENS_NODE_NAME;
-                root.commit();
-            }
+            tokenParent = userNode.getOrAddChild(TOKENS_NODE_NAME, 
TOKENS_NT_NAME);
+
+            root.commit();
         } catch (RepositoryException e) {
             // error while creating token node.
             log.debug("Error while creating token node ", e.getMessage());
@@ -400,11 +380,9 @@ class TokenProviderImpl implements Token
             // try to get the tree from the updated root.
             log.debug("Conflict while creating token store -> retrying", 
e.getMessage());
             root.refresh();
-            if (parentPath != null) {
-                Tree parentTree = root.getTree(parentPath);
-                if (parentTree.exists()) {
-                    tokenParent = new NodeUtil(parentTree);
-                }
+            Tree parentTree = root.getTree(parentPath);
+            if (parentTree.exists()) {
+                tokenParent = new NodeUtil(parentTree);
             }
         }
         return tokenParent;
@@ -414,25 +392,38 @@ class TokenProviderImpl implements Token
      * Create a new token node below the specified {@code parent}.
      *
      * @param parent The parent node.
-     * @param creationTime The creation time that is used as name hint.
-     * @return The new token node
-     * @throws AccessDeniedException
+     * @param expTime The expiration time of the new token.
+     * @param uuid The uuid of the token node.
+     * @param id The id of the user that issues the token.
+     * @param attributes The additional attributes of the token to be created.
+     * @return The new token info
+     * @throws AccessDeniedException If the editing session cannot access the
+     * new token node.
+     *
      */
-    private NodeUtil createTokenNode(@Nonnull NodeUtil parent, @Nonnull long 
creationTime) throws AccessDeniedException {
-        Calendar creation = GregorianCalendar.getInstance();
-        creation.setTimeInMillis(creationTime);
-        String tokenName = Text.replace(ISO8601.format(creation), ":", ".");
-        NodeUtil tokenNode;
-        try {
-            tokenNode = parent.addChild(tokenName, TOKEN_NT_NAME);
-            root.commit();
-        } catch (CommitFailedException e) {
-            // conflict while creating token node -> retry
-            log.debug("Failed to create token node " + tokenName + ". Using 
random name as fallback.");
-            root.refresh();
-            tokenNode = parent.addChild(UUID.randomUUID().toString(), 
TOKEN_NT_NAME);
+    private TokenInfo createTokenNode(@Nonnull NodeUtil parent, @Nonnull 
String tokenName,
+                                      long expTime, @Nonnull String uuid,
+                                      @Nonnull String id, Map<String, ?> 
attributes)
+            throws AccessDeniedException, UnsupportedEncodingException, 
NoSuchAlgorithmException {
+
+        NodeUtil tokenNode = parent.addChild(tokenName, TOKEN_NT_NAME);
+        tokenNode.setString(JcrConstants.JCR_UUID, uuid);
+
+        String key = generateKey(options.getConfigValue(PARAM_TOKEN_LENGTH, 
DEFAULT_KEY_SIZE));
+        String nodeId = getIdentifier(tokenNode.getTree());
+        String token = nodeId + DELIM + key;
+
+        String keyHash = PasswordUtil.buildPasswordHash(getKeyValue(key, id), 
options);
+        tokenNode.setString(TOKEN_ATTRIBUTE_KEY, keyHash);
+        tokenNode.setDate(TOKEN_ATTRIBUTE_EXPIRY, expTime);
+
+        for (String name : attributes.keySet()) {
+            if (!RESERVED_ATTRIBUTES.contains(name)) {
+                String attr = attributes.get(name).toString();
+                tokenNode.setString(name, attr);
+            }
         }
-        return tokenNode;
+        return new TokenInfoImpl(tokenNode, token, id);
     }
 
     
//--------------------------------------------------------------------------
@@ -509,7 +500,7 @@ class TokenProviderImpl implements Token
                     try {
                         long expTime = createExpirationTime(loginTime, 
tokenExpiration);
                         tokenNode.setDate(TOKEN_ATTRIBUTE_EXPIRY, expTime);
-                        root.commit();
+                        root.commit(CommitMarker.asCommitAttributes());
                         log.debug("Successfully reset token expiration time.");
                         return true;
                     } catch (CommitFailedException e) {
@@ -527,7 +518,7 @@ class TokenProviderImpl implements Token
             if (tokenTree != null && tokenTree.exists()) {
                 try {
                     if (tokenTree.remove()) {
-                        root.commit();
+                        root.commit(CommitMarker.asCommitAttributes());
                         return true;
                     }
                 } catch (CommitFailedException e) {

Added: 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/token/TokenValidatorProvider.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/token/TokenValidatorProvider.java?rev=1658358&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/token/TokenValidatorProvider.java
 (added)
+++ 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/token/TokenValidatorProvider.java
 Mon Feb  9 11:03:21 2015
@@ -0,0 +1,201 @@
+/*
+ * 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.authentication.token;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.apache.jackrabbit.JcrConstants;
+import org.apache.jackrabbit.oak.api.CommitFailedException;
+import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.api.Tree;
+import org.apache.jackrabbit.oak.api.Type;
+import org.apache.jackrabbit.oak.plugins.tree.TreeFactory;
+import org.apache.jackrabbit.oak.spi.commit.CommitInfo;
+import org.apache.jackrabbit.oak.spi.commit.DefaultValidator;
+import org.apache.jackrabbit.oak.spi.commit.Validator;
+import org.apache.jackrabbit.oak.spi.commit.ValidatorProvider;
+import org.apache.jackrabbit.oak.spi.commit.VisibleValidator;
+import org.apache.jackrabbit.oak.spi.security.ConfigurationParameters;
+import org.apache.jackrabbit.oak.spi.security.user.UserConstants;
+import org.apache.jackrabbit.oak.spi.security.user.util.PasswordUtil;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+import org.apache.jackrabbit.oak.util.TreeUtil;
+import org.apache.jackrabbit.util.Text;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+class TokenValidatorProvider extends ValidatorProvider implements 
TokenConstants {
+
+    private static final Logger log = 
LoggerFactory.getLogger(TokenValidatorProvider.class);
+
+    private final String userRootPath;
+
+    TokenValidatorProvider(@Nonnull ConfigurationParameters userConfig) {
+        userRootPath = 
userConfig.getConfigValue(UserConstants.PARAM_USER_PATH, 
UserConstants.DEFAULT_USER_PATH);
+    }
+
+    @Override
+    protected Validator getRootValidator(NodeState before, NodeState after, 
CommitInfo commitInfo) {
+        return new TokenValidator(before, after, commitInfo);
+    }
+
+    private static CommitFailedException constraintViolation(int code, 
@Nonnull String message) {
+        return new CommitFailedException(CommitFailedException.CONSTRAINT, 
code, message);
+    }
+
+    private final class TokenValidator extends DefaultValidator implements 
TokenConstants {
+
+        private final Tree parentBefore;
+        private final Tree parentAfter;
+        private final CommitInfo commitInfo;
+
+        TokenValidator(@Nonnull NodeState parentBefore, @Nonnull NodeState 
parentAfter, @Nonnull CommitInfo commitInfo) {
+            this(TreeFactory.createReadOnlyTree(parentBefore), 
TreeFactory.createReadOnlyTree(parentAfter), commitInfo);
+        }
+
+        private TokenValidator(@Nullable Tree parentBefore, @Nonnull Tree 
parentAfter, @Nonnull CommitInfo commitInfo) {
+            this.parentBefore = parentBefore;
+            this.parentAfter = parentAfter;
+            this.commitInfo = commitInfo;
+        }
+
+        //------------------------------------------------------< Validator 
>---
+
+        @Override
+        public void propertyAdded(PropertyState after) throws 
CommitFailedException {
+            String name = after.getName();
+            if (TOKEN_PROPERTY_NAMES.contains(name)) {
+                // ensure that token specific properties are managed by the 
token provider.
+                verifyCommitInfo();
+                // make sure they are not solely located with a token node.
+                if (!isTokenTree(parentAfter)) {
+                    String msg = "Attempt to create reserved token property " 
+ name;
+                    throw constraintViolation(60, msg);
+                }
+            }
+        }
+
+        @Override
+        public void propertyChanged(PropertyState before, PropertyState after) 
throws CommitFailedException {
+            String beforeName = after.getName();
+            if (TOKEN_ATTRIBUTE_KEY.equals(beforeName)) {
+                String msg = "Attempt to change reserved token property " + 
beforeName;
+                throw constraintViolation(61, msg);
+            } else if (TOKEN_ATTRIBUTE_EXPIRY.equals(beforeName)) {
+                verifyCommitInfo();
+            } else if (JcrConstants.JCR_PRIMARYTYPE.equals(beforeName)) {
+                if (TOKEN_NT_NAME.equals(after.getValue(Type.STRING))) {
+                    throw constraintViolation(62, "Changing primary type of 
existing node to the reserved token node type.");
+                }
+                if (isTokensParent(parentAfter) && 
TOKENS_NT_NAME.equals(before.getValue(Type.STRING))) {
+                    throw constraintViolation(69, "Cannot change the primary 
type of an existing .tokens node.");
+                }
+            }
+        }
+
+        @Override
+        public Validator childNodeAdded(String name, NodeState after) throws 
CommitFailedException {
+            Tree tree = checkNotNull(parentAfter.getChild(name));
+
+            if (isTokenTree(tree)) {
+                validateTokenTree(tree);
+                // no further validation required
+                return null;
+            } else if (isTokensParent(tree)) {
+                validateTokensParent(tree);
+            }
+            return new VisibleValidator(new TokenValidator(null, tree, 
commitInfo), true, true);
+        }
+
+        @Override
+        public Validator childNodeChanged(String name, NodeState before, 
NodeState after) throws CommitFailedException {
+            Tree beforeTree = (parentBefore == null) ? null : 
parentBefore.getChild(name);
+            Tree afterTree = parentAfter.getChild(name);
+
+            if (isTokenTree(beforeTree) || isTokenTree(afterTree)) {
+                validateTokenTree(afterTree);
+            } else if (isTokensParent(beforeTree) || 
isTokensParent(afterTree)) {
+                validateTokensParent(afterTree);
+            }
+
+            return new VisibleValidator(new TokenValidator(beforeTree, 
afterTree, commitInfo), true, true);
+        }
+
+        //--------------------------------------------------------< private 
>---
+        private void verifyCommitInfo() throws CommitFailedException {
+            if (!CommitMarker.isValidCommitInfo(commitInfo)) {
+                throw constraintViolation(63, "Attempt to manually create or 
change a token node or it's parent.");
+            }
+        }
+
+        private void verifyHierarchy(@Nonnull String path) throws 
CommitFailedException {
+            if (!Text.isDescendant(userRootPath, path)) {
+                String msg = "Attempt to create a token (or it's parent) 
outside of configured scope " + path;
+                throw constraintViolation(64, msg);
+            }
+        }
+
+        private boolean isTokenTree(@CheckForNull Tree tree) {
+            return tree != null && 
TOKEN_NT_NAME.equals(TreeUtil.getPrimaryTypeName(tree));
+        }
+
+        private void validateTokenTree(@Nonnull Tree tokenTree) throws 
CommitFailedException {
+            // enforce changing being made by the TokenProvider implementation
+            verifyCommitInfo();
+
+            verifyHierarchy(tokenTree.getPath());
+
+            Tree parent = tokenTree.getParent();
+            if (!TOKENS_NODE_NAME.equals(parent.getName()) || 
!UserConstants.NT_REP_USER.equals(TreeUtil.getPrimaryTypeName(parent.getParent())))
 {
+                throw constraintViolation(65, "Invalid location of token 
node.");
+            }
+
+            // assert mandatory properties are present
+            String key = TreeUtil.getString(tokenTree, TOKEN_ATTRIBUTE_KEY);
+            if (PasswordUtil.isPlainTextPassword(key)) {
+                throw constraintViolation(66, "Invalid token key.");
+            }
+
+            if (TreeUtil.getString(tokenTree, TOKEN_ATTRIBUTE_EXPIRY) == null) 
{
+                throw constraintViolation(67, "Mandatory token expiration 
missing.");
+            }
+        }
+
+        private boolean isTokensParent(@CheckForNull Tree tree) {
+            return tree != null && TOKENS_NODE_NAME.equals(tree.getName());
+        }
+
+        private void validateTokensParent(@Nonnull Tree tokensParent) throws 
CommitFailedException {
+
+            verifyHierarchy(tokensParent.getPath());
+
+            Tree userTree = tokensParent.getParent();
+            if 
(!UserConstants.NT_REP_USER.equals(TreeUtil.getPrimaryTypeName(userTree))) {
+                throw constraintViolation(68, "Invalid location of .tokens 
node.");
+            }
+
+            String nt = TreeUtil.getPrimaryTypeName(tokensParent);
+            if (!TOKENS_NT_NAME.equals(nt)) {
+                log.debug("Unexpected node type of .tokens node " + nt + '.');
+            }
+        }
+    }
+}
\ No newline at end of file

Modified: 
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authentication/token/AbstractTokenTest.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authentication/token/AbstractTokenTest.java?rev=1658358&r1=1658357&r2=1658358&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authentication/token/AbstractTokenTest.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authentication/token/AbstractTokenTest.java
 Mon Feb  9 11:03:21 2015
@@ -16,14 +16,23 @@
  */
 package org.apache.jackrabbit.oak.security.authentication.token;
 
+import javax.annotation.CheckForNull;
+import javax.annotation.Nonnull;
+import javax.jcr.AccessDeniedException;
+
+import org.apache.jackrabbit.JcrConstants;
 import org.apache.jackrabbit.oak.AbstractSecurityTest;
+import org.apache.jackrabbit.oak.api.Tree;
+import org.apache.jackrabbit.oak.plugins.identifier.IdentifierManager;
 import org.apache.jackrabbit.oak.spi.security.ConfigurationParameters;
+import org.apache.jackrabbit.oak.spi.security.authentication.token.TokenInfo;
+import org.apache.jackrabbit.oak.util.NodeUtil;
 import org.junit.Before;
 
 /**
  * AbstractTokenTest...
  */
-public abstract class AbstractTokenTest extends AbstractSecurityTest {
+public abstract class AbstractTokenTest extends AbstractSecurityTest 
implements TokenConstants {
 
     TokenProviderImpl tokenProvider;
 
@@ -35,6 +44,33 @@ public abstract class AbstractTokenTest
         tokenProvider = new TokenProviderImpl(root,
                 ConfigurationParameters.EMPTY,
                 getUserConfiguration());
-        root.commit();
+    }
+
+    @Override
+    public void after() throws Exception {
+        try {
+            root.refresh();
+        } finally {
+            super.after();
+        }
+    }
+
+    @CheckForNull
+    Tree getTokenTree(@Nonnull TokenInfo info) {
+        String token = info.getToken();
+        int pos = token.indexOf('_');
+        String nodeId = (pos == -1) ? token : token.substring(0, pos);
+        return new IdentifierManager(root).getTree(nodeId);
+    }
+
+    @Nonnull
+    Tree createTokenTree(@Nonnull TokenInfo base, @Nonnull NodeUtil parent,
+                         @Nonnull String ntName) throws AccessDeniedException {
+        Tree tokenTree = getTokenTree(base);
+        Tree tree = parent.addChild("token", ntName).getTree();
+        tree.setProperty(tokenTree.getProperty(JcrConstants.JCR_UUID));
+        tree.setProperty(tokenTree.getProperty(TOKEN_ATTRIBUTE_KEY));
+        tree.setProperty(tokenTree.getProperty(TOKEN_ATTRIBUTE_EXPIRY));
+        return tree;
     }
 }
\ No newline at end of file

Modified: 
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authentication/token/TokenInfoTest.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authentication/token/TokenInfoTest.java?rev=1658358&r1=1658357&r2=1658358&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authentication/token/TokenInfoTest.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authentication/token/TokenInfoTest.java
 Mon Feb  9 11:03:21 2015
@@ -103,9 +103,9 @@ public class TokenInfoTest extends Abstr
     @Test
     public void testGetAttributes() {
         Map<String, String> reserved = new HashMap<String, String>();
-        reserved.put(".token", "value");
-        reserved.put("rep:token.key", "value");
-        reserved.put("rep:token.exp", "value");
+        reserved.put(TOKEN_ATTRIBUTE, "value");
+        reserved.put(TOKEN_ATTRIBUTE_KEY, "value");
+        reserved.put(TOKEN_ATTRIBUTE_EXPIRY, "value");
 
         Map<String, String> privateAttributes = new HashMap<String, String>();
         privateAttributes.put(".token_exp", "value");
@@ -160,7 +160,7 @@ public class TokenInfoTest extends Abstr
         TokenInfo info = tokenProvider.createToken(userId, 
Collections.<String, Object>emptyMap());
 
         Tree userTree = 
root.getTree(getUserManager(root).getAuthorizable(userId).getPath());
-        Tree tokens = userTree.getChild(".tokens");
+        Tree tokens = userTree.getChild(TOKENS_NODE_NAME);
         String tokenNodePath = 
tokens.getChildren().iterator().next().getPath();
 
         info.remove();

Modified: 
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authentication/token/TokenProviderImplTest.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authentication/token/TokenProviderImplTest.java?rev=1658358&r1=1658357&r2=1658358&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authentication/token/TokenProviderImplTest.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authentication/token/TokenProviderImplTest.java
 Mon Feb  9 11:03:21 2015
@@ -27,7 +27,6 @@ import java.util.concurrent.Callable;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.concurrent.Future;
-import javax.jcr.AccessDeniedException;
 import javax.jcr.Credentials;
 import javax.jcr.GuestCredentials;
 import javax.jcr.SimpleCredentials;
@@ -39,7 +38,6 @@ import org.apache.jackrabbit.oak.api.Pro
 import org.apache.jackrabbit.oak.api.Root;
 import org.apache.jackrabbit.oak.api.Tree;
 import org.apache.jackrabbit.oak.api.Type;
-import org.apache.jackrabbit.oak.plugins.identifier.IdentifierManager;
 import 
org.apache.jackrabbit.oak.spi.security.authentication.ImpersonationCredentials;
 import 
org.apache.jackrabbit.oak.spi.security.authentication.token.TokenConfiguration;
 import org.apache.jackrabbit.oak.spi.security.authentication.token.TokenInfo;
@@ -132,9 +130,9 @@ public class TokenProviderImplTest exten
     @Test
     public void testTokenNode() throws Exception {
         Map<String, String> reserved = new HashMap<String, String>();
-        reserved.put(".token", "value");
-        reserved.put("rep:token.key", "value");
-        reserved.put("rep:token.exp", "value");
+        reserved.put(TOKEN_ATTRIBUTE, "value");
+        reserved.put(TOKEN_ATTRIBUTE_KEY, "value");
+        reserved.put(TOKEN_ATTRIBUTE_EXPIRY, "value");
 
         Map<String, String> privateAttributes = new HashMap<String, String>();
         privateAttributes.put(".token_exp", "value");
@@ -152,11 +150,11 @@ public class TokenProviderImplTest exten
 
         TokenInfo info = tokenProvider.createToken(userId, attributes);
         Tree tokenTree = getTokenTree(info);
-        PropertyState prop = tokenTree.getProperty("rep:token.key");
+        PropertyState prop = tokenTree.getProperty(TOKEN_ATTRIBUTE_KEY);
         assertNotNull(prop);
         assertEquals(Type.STRING, prop.getType());
 
-        prop = tokenTree.getProperty("rep:token.exp");
+        prop = tokenTree.getProperty(TOKEN_ATTRIBUTE_EXPIRY);
         assertNotNull(prop);
         assertEquals(Type.DATE, prop.getType());
 
@@ -201,16 +199,15 @@ public class TokenProviderImplTest exten
 
         assertNotNull(tokenProvider.getTokenInfo(info.getToken()));
 
-        NodeUtil node = new NodeUtil(root.getTree("/")).addChild("testNode", 
"nt:unstructured");
+        NodeUtil node = new NodeUtil(root.getTree("/")).addChild("testNode", 
JcrConstants.NT_UNSTRUCTURED);
         try {
-            createTokenTree(info, node, "rep:Token");
+            createTokenTree(info, node, TOKEN_NT_NAME);
             tokenTree.remove();
-            root.commit();
 
             assertNull(tokenProvider.getTokenInfo(info.getToken()));
         } finally {
             node.getTree().remove();
-            root.commit();
+            root.commit(CommitMarker.asCommitAttributes());
         }
     }
 
@@ -222,16 +219,14 @@ public class TokenProviderImplTest exten
         assertNotNull(tokenProvider.getTokenInfo(info.getToken()));
 
         Tree userTree = 
root.getTree(getUserManager(root).getAuthorizable(userId).getPath());
-        NodeUtil node = new NodeUtil(userTree).addChild("testNode", 
"nt:unstructured");
+        NodeUtil node = new NodeUtil(userTree).addChild("testNode", 
JcrConstants.NT_UNSTRUCTURED);
         try {
-            createTokenTree(info, node, "rep:Token");
+            createTokenTree(info, node, TOKEN_NT_NAME);
             tokenTree.remove();
-            root.commit();
 
             assertNull(tokenProvider.getTokenInfo(info.getToken()));
         } finally {
-            node.getTree().remove();
-            root.commit();
+            root.refresh();
         }
     }
 
@@ -243,16 +238,14 @@ public class TokenProviderImplTest exten
         assertNotNull(tokenProvider.getTokenInfo(info.getToken()));
 
         Tree userTree = 
root.getTree(getUserManager(root).getAuthorizable(userId).getPath());
-        NodeUtil node = new NodeUtil(userTree.getChild(".tokens"));
+        NodeUtil node = new NodeUtil(userTree.getChild(TOKENS_NODE_NAME));
         try {
-            createTokenTree(info, node, "nt:unstructured");
+            createTokenTree(info, node, JcrConstants.NT_UNSTRUCTURED);
             tokenTree.remove();
-            root.commit();
 
             assertNull(tokenProvider.getTokenInfo(info.getToken()));
         } finally {
-            node.getTree().remove();
-            root.commit();
+            root.refresh();
         }
     }
 
@@ -266,17 +259,14 @@ public class TokenProviderImplTest exten
         TokenInfo info2 = null;
         try {
             Tree adminTree = 
root.getTree(getUserManager(root).getAuthorizable(adminSession.getAuthInfo().getUserID()).getPath());
-            NodeUtil node = new NodeUtil(adminTree).getOrAddChild(".tokens", 
"nt:unstructured");
-            assertTrue(root.move(tokenTree.getPath(), node.getTree().getPath() 
+ "/" + tokenTree.getName()));
-            root.commit();
+            NodeUtil node = new 
NodeUtil(adminTree).getOrAddChild(TOKENS_NODE_NAME, 
JcrConstants.NT_UNSTRUCTURED);
+            assertTrue(root.move(tokenTree.getPath(), node.getTree().getPath() 
+ '/' + tokenTree.getName()));
 
             info2 = tokenProvider.getTokenInfo(info.getToken());
             assertNotNull(info2);
             assertFalse(info2.matches(new TokenCredentials(info.getToken())));
         } finally {
-            Tree t = getTokenTree(info2);
-            t.remove();
-            root.commit();
+            root.refresh();
         }
     }
 
@@ -361,7 +351,7 @@ public class TokenProviderImplTest exten
     }
 
     /**
-     * @see OAK-1985
+     * @see <a 
href="https://issues.apache.org/jira/browse/OAK-1985";>OAK-1985</a>
      */
     @Test
     public void testTokenValidationIsCaseInsensitive() throws Exception {
@@ -381,6 +371,21 @@ public class TokenProviderImplTest exten
         assertEquals(userId, info.getUserId());
     }
 
+    @Test
+    public void testTokenNodeName() throws Exception {
+        TokenInfo info = tokenProvider.createToken(userId, 
Collections.<String, Object>emptyMap());
+        Tree tokenTree = getTokenTree(info);
+
+        // name must not be a uuid which is only used in case of conflict 
during
+        // creation which is not expected here.
+        try {
+            UUID.fromString(tokenTree.getName());
+            fail("UUID-name should only be used in case of conflict");
+        } catch (IllegalArgumentException e) {
+            // success
+        }
+    }
+
     
//--------------------------------------------------------------------------
     private static void assertTokenInfo(TokenInfo info, String userId) {
         assertNotNull(info);
@@ -388,21 +393,6 @@ public class TokenProviderImplTest exten
         assertEquals(userId, info.getUserId());
         assertFalse(info.isExpired(new Date().getTime()));
     }
-
-    private Tree getTokenTree(TokenInfo info) {
-        String token = info.getToken();
-        int pos = token.indexOf('_');
-        String nodeId = (pos == -1) ? token : token.substring(0, pos);
-        return new IdentifierManager(root).getTree(nodeId);
-    }
-
-    private void createTokenTree(TokenInfo base, NodeUtil parent, String 
ntName) throws AccessDeniedException {
-        Tree tokenTree = getTokenTree(base);
-        Tree tree = parent.addChild("token", ntName).getTree();
-        tree.setProperty(tokenTree.getProperty(JcrConstants.JCR_UUID));
-        tree.setProperty(tokenTree.getProperty("rep:token.key"));
-        tree.setProperty(tokenTree.getProperty("rep:token.exp"));
-    }
     
     private static class DataFuture {
         public Future<TokenInfo> future;

Copied: 
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authentication/token/TokenValidatorTest.java
 (from r1656763, 
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authentication/token/TokenProviderImplTest.java)
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authentication/token/TokenValidatorTest.java?p2=jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authentication/token/TokenValidatorTest.java&p1=jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authentication/token/TokenProviderImplTest.java&r1=1656763&r2=1658358&rev=1658358&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authentication/token/TokenProviderImplTest.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authentication/token/TokenValidatorTest.java
 Mon Feb  9 11:03:21 2015
@@ -16,49 +16,25 @@
  */
 package org.apache.jackrabbit.oak.security.authentication.token;
 
-import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Date;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.UUID;
-import java.util.concurrent.Callable;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.Future;
-import javax.jcr.AccessDeniedException;
-import javax.jcr.Credentials;
-import javax.jcr.GuestCredentials;
-import javax.jcr.SimpleCredentials;
 
 import org.apache.jackrabbit.JcrConstants;
-import 
org.apache.jackrabbit.api.security.authentication.token.TokenCredentials;
-import org.apache.jackrabbit.oak.api.ContentSession;
-import org.apache.jackrabbit.oak.api.PropertyState;
-import org.apache.jackrabbit.oak.api.Root;
+import org.apache.jackrabbit.oak.api.CommitFailedException;
 import org.apache.jackrabbit.oak.api.Tree;
 import org.apache.jackrabbit.oak.api.Type;
-import org.apache.jackrabbit.oak.plugins.identifier.IdentifierManager;
-import 
org.apache.jackrabbit.oak.spi.security.authentication.ImpersonationCredentials;
-import 
org.apache.jackrabbit.oak.spi.security.authentication.token.TokenConfiguration;
+import org.apache.jackrabbit.oak.plugins.nodetype.NodeTypeConstants;
 import org.apache.jackrabbit.oak.spi.security.authentication.token.TokenInfo;
-import 
org.apache.jackrabbit.oak.spi.security.authentication.token.TokenProvider;
+import org.apache.jackrabbit.oak.spi.security.user.util.PasswordUtil;
 import org.apache.jackrabbit.oak.util.NodeUtil;
 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.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
-/**
- * TokenProviderImplTest...
- */
-public class TokenProviderImplTest extends AbstractTokenTest {
+public class TokenValidatorTest extends AbstractTokenTest {
 
     private String userId;
 
@@ -70,356 +46,325 @@ public class TokenProviderImplTest exten
     }
 
     @Test
-    public void testDoCreateToken() throws Exception {
-        assertFalse(tokenProvider.doCreateToken(new GuestCredentials()));
-        assertFalse(tokenProvider.doCreateToken(new 
TokenCredentials("token")));
-        assertFalse(tokenProvider.doCreateToken(getAdminCredentials()));
-
-        SimpleCredentials sc = new SimpleCredentials("uid", 
"pw".toCharArray());
-        assertFalse(tokenProvider.doCreateToken(sc));
-
-        sc.setAttribute("any_attribute", "value");
-        assertFalse(tokenProvider.doCreateToken(sc));
-
-        sc.setAttribute("rep:token_key", "value");
-        assertFalse(tokenProvider.doCreateToken(sc));
-
-        sc.setAttribute(".token", "existing");
-        assertFalse(tokenProvider.doCreateToken(sc));
-
-        sc.setAttribute(".token", "");
-        assertTrue(tokenProvider.doCreateToken(sc));
+    public void testCreateReservedKeyProperty() throws Exception {
+        NodeUtil node = new NodeUtil(root.getTree("/")).addChild("testNode", 
JcrConstants.NT_UNSTRUCTURED);
+        try {
+            node.setString(TOKEN_ATTRIBUTE_KEY, "anyValue");
+            root.commit(CommitMarker.asCommitAttributes());
+            fail("The reserved token key property must not used with other 
node types.");
+        } catch (CommitFailedException e) {
+            assertEquals(60, e.getCode());
+        } finally {
+            node.getTree().remove();
+            root.commit();
+        }
     }
 
     @Test
-    public void testCreateTokenFromInvalidCredentials() throws Exception {
-        List<Credentials> invalid = new ArrayList<Credentials>();
-        invalid.add(new GuestCredentials());
-        invalid.add(new TokenCredentials("sometoken"));
-        invalid.add(new ImpersonationCredentials(new GuestCredentials(), 
null));
-        invalid.add(new SimpleCredentials("unknownUserId", new char[0]));
-
-        for (Credentials creds : invalid) {
-            assertNull(tokenProvider.createToken(creds));
+    public void testCreateReservedKeyProperty2() throws Exception {
+        NodeUtil node = new NodeUtil(root.getTree("/")).addChild("testNode", 
JcrConstants.NT_UNSTRUCTURED);
+        try {
+            node.setString(TOKEN_ATTRIBUTE_KEY, "anyValue");
+            root.commit();
+            fail("The reserved token key property must only be created by the 
TokenProvider.");
+        } catch (CommitFailedException e) {
+            assertEquals(63, e.getCode());
+        } finally {
+            node.getTree().remove();
+            root.commit();
         }
     }
 
     @Test
-    public void testCreateTokenFromCredentials() throws Exception {
-        SimpleCredentials sc = new SimpleCredentials(userId, new char[0]);
-        List<Credentials> valid = new ArrayList<Credentials>();
-        valid.add(sc);
-        valid.add(new ImpersonationCredentials(sc, null));
+    public void testChangingTokenKey() throws Exception {
+        TokenInfo info = tokenProvider.createToken(userId, 
Collections.<String, Object>emptyMap());
+        NodeUtil tokenTree = new NodeUtil(getTokenTree(info));
 
-        for (Credentials creds : valid) {
-            TokenInfo info = tokenProvider.createToken(creds);
-            assertTokenInfo(info, userId);
+        try {
+            tokenTree.setString(TOKEN_ATTRIBUTE_KEY, 
PasswordUtil.buildPasswordHash("anotherValue"));
+            root.commit(CommitMarker.asCommitAttributes());
+            fail("The token key must never be modified.");
+        } catch (CommitFailedException e) {
+            assertEquals(61, e.getCode());
         }
     }
 
     @Test
-    public void testCreateTokenFromInvalidUserId() throws Exception {
-        TokenInfo info = tokenProvider.createToken("unknownUserId", 
Collections.<String, Object>emptyMap());
-        assertNull(info);
-    }
-
-    @Test
-    public void testCreateTokenFromUserId() throws Exception {
+    public void testPlaintextTokenKey() throws Exception {
         TokenInfo info = tokenProvider.createToken(userId, 
Collections.<String, Object>emptyMap());
-        assertTokenInfo(info, userId);
+        NodeUtil tokenTree = new NodeUtil(getTokenTree(info));
+
+        try {
+            tokenTree.setString(TOKEN_ATTRIBUTE_KEY, "anotherValue");
+            root.commit(CommitMarker.asCommitAttributes());
+            fail("The token key must not be plaintext.");
+        } catch (CommitFailedException e) {
+            assertEquals(66, e.getCode());
+        }
     }
 
     @Test
-    public void testTokenNode() throws Exception {
-        Map<String, String> reserved = new HashMap<String, String>();
-        reserved.put(".token", "value");
-        reserved.put("rep:token.key", "value");
-        reserved.put("rep:token.exp", "value");
-
-        Map<String, String> privateAttributes = new HashMap<String, String>();
-        privateAttributes.put(".token_exp", "value");
-        privateAttributes.put(".tokenTest", "value");
-        privateAttributes.put(".token_something", "value");
-
-        Map<String, String> publicAttributes = new HashMap<String, String>();
-        publicAttributes.put("any", "value");
-        publicAttributes.put("another", "value");
-
-        Map<String, String> attributes = new HashMap<String, String>();
-        attributes.putAll(reserved);
-        attributes.putAll(publicAttributes);
-        attributes.putAll(privateAttributes);
+    public void testManuallyModifyExpirationDate() throws Exception {
+        TokenInfo info = tokenProvider.createToken(userId, 
Collections.<String, Object>emptyMap());
+        NodeUtil tokenTree = new NodeUtil(getTokenTree(info));
 
-        TokenInfo info = tokenProvider.createToken(userId, attributes);
-        Tree tokenTree = getTokenTree(info);
-        PropertyState prop = tokenTree.getProperty("rep:token.key");
-        assertNotNull(prop);
-        assertEquals(Type.STRING, prop.getType());
-
-        prop = tokenTree.getProperty("rep:token.exp");
-        assertNotNull(prop);
-        assertEquals(Type.DATE, prop.getType());
-
-        for (String key : reserved.keySet()) {
-            PropertyState p = tokenTree.getProperty(key);
-            if (p != null) {
-                assertFalse(reserved.get(key).equals(p.getValue(Type.STRING)));
-            }
+        try {
+            tokenTree.setDate(TOKEN_ATTRIBUTE_EXPIRY, new Date().getTime());
+            root.commit();
+            fail("The token expiry must not manually be changed");
+        } catch (CommitFailedException e) {
+            assertEquals(63, e.getCode());
         }
+    }
 
-        for (String key : privateAttributes.keySet()) {
-            assertEquals(privateAttributes.get(key), 
tokenTree.getProperty(key).getValue(Type.STRING));
-        }
+    @Test
+    public void testModifyExpirationDate() throws Exception {
+        TokenInfo info = tokenProvider.createToken(userId, 
Collections.<String, Object>emptyMap());
+        NodeUtil tokenTree = new NodeUtil(getTokenTree(info));
 
-        for (String key : publicAttributes.keySet()) {
-            assertEquals(publicAttributes.get(key), 
tokenTree.getProperty(key).getValue(Type.STRING));
-        }
+        tokenTree.setDate(TOKEN_ATTRIBUTE_EXPIRY, new Date().getTime());
+        root.commit(CommitMarker.asCommitAttributes());
     }
 
     @Test
-    public void testGetTokenInfoFromInvalidToken() throws Exception {
-        List<String> invalid = new ArrayList<String>();
-        invalid.add("/invalid");
-        invalid.add(UUID.randomUUID().toString());
+    public void testCreateTokenAtInvalidLocationBelowTestNode() throws 
Exception {
+        TokenInfo info = tokenProvider.createToken(userId, 
Collections.<String, Object>emptyMap());
+        Tree tokenTree = getTokenTree(info);
 
-        for (String token : invalid) {
-            TokenInfo info = tokenProvider.getTokenInfo(token);
-            assertNull(info);
-        }
+        assertNotNull(tokenProvider.getTokenInfo(info.getToken()));
 
+        NodeUtil node = new NodeUtil(root.getTree("/")).addChild("testNode", 
JcrConstants.NT_UNSTRUCTURED);
         try {
-            assertNull(tokenProvider.getTokenInfo("invalidToken"));
-        } catch (Exception e) {
-            // success
+            createTokenTree(info, node, TOKEN_NT_NAME);
+            tokenTree.remove();
+            root.commit(CommitMarker.asCommitAttributes());
+
+            fail("Creating a new token not  at '/testNode' must fail.");
+        } catch (CommitFailedException e) {
+            assertEquals(64, e.getCode());
+        } finally {
+            node.getTree().remove();
+            root.commit(CommitMarker.asCommitAttributes());
         }
     }
 
     @Test
-    public void testGetTokenInfoFromInvalidLocation() throws Exception {
+    public void testCreateTokenAtInvalidLocationInsideUser() throws Exception {
         TokenInfo info = tokenProvider.createToken(userId, 
Collections.<String, Object>emptyMap());
         Tree tokenTree = getTokenTree(info);
 
         assertNotNull(tokenProvider.getTokenInfo(info.getToken()));
 
-        NodeUtil node = new NodeUtil(root.getTree("/")).addChild("testNode", 
"nt:unstructured");
+        Tree userTree = 
root.getTree(getUserManager(root).getAuthorizable(userId).getPath());
+        NodeUtil node = new NodeUtil(userTree).addChild("testNode", 
JcrConstants.NT_UNSTRUCTURED);
         try {
-            createTokenTree(info, node, "rep:Token");
+            createTokenTree(info, node, TOKEN_NT_NAME);
             tokenTree.remove();
-            root.commit();
+            root.commit(CommitMarker.asCommitAttributes());
 
-            assertNull(tokenProvider.getTokenInfo(info.getToken()));
+            fail("Creating a new token '" + node.getTree().getPath() + "' must 
fail.");
+        } catch (CommitFailedException e) {
+            assertEquals(65, e.getCode());
         } finally {
             node.getTree().remove();
-            root.commit();
+            root.commit(CommitMarker.asCommitAttributes());
         }
     }
 
     @Test
-    public void testGetTokenInfoFromInvalidLocation2() throws Exception {
+    public void testCreateTokenAtInvalidLocationInsideUser2() throws Exception 
{
         TokenInfo info = tokenProvider.createToken(userId, 
Collections.<String, Object>emptyMap());
         Tree tokenTree = getTokenTree(info);
 
         assertNotNull(tokenProvider.getTokenInfo(info.getToken()));
 
         Tree userTree = 
root.getTree(getUserManager(root).getAuthorizable(userId).getPath());
-        NodeUtil node = new NodeUtil(userTree).addChild("testNode", 
"nt:unstructured");
+        NodeUtil node = new NodeUtil(userTree).getOrAddChild(TOKENS_NODE_NAME, 
TOKENS_NT_NAME);
         try {
-            createTokenTree(info, node, "rep:Token");
+            node = node.addChild("invalid", JcrConstants.NT_UNSTRUCTURED);
+            createTokenTree(info, node, TOKEN_NT_NAME);
             tokenTree.remove();
-            root.commit();
+            root.commit(CommitMarker.asCommitAttributes());
 
-            assertNull(tokenProvider.getTokenInfo(info.getToken()));
+            fail("Creating a new token '" + node.getTree().getPath() + "' must 
fail.");
+        } catch (CommitFailedException e) {
+            assertEquals(65, e.getCode());
         } finally {
             node.getTree().remove();
-            root.commit();
+            root.commit(CommitMarker.asCommitAttributes());
         }
     }
 
     @Test
-    public void testGetTokenInfoFromInvalidLocation3() throws Exception {
+    public void testManuallyCreateToken() throws Exception {
         TokenInfo info = tokenProvider.createToken(userId, 
Collections.<String, Object>emptyMap());
         Tree tokenTree = getTokenTree(info);
 
         assertNotNull(tokenProvider.getTokenInfo(info.getToken()));
 
-        Tree userTree = 
root.getTree(getUserManager(root).getAuthorizable(userId).getPath());
-        NodeUtil node = new NodeUtil(userTree.getChild(".tokens"));
+        NodeUtil tokensNode = new NodeUtil(tokenTree.getParent());
         try {
-            createTokenTree(info, node, "nt:unstructured");
+            // create a valid token node using the test root
+            createTokenTree(info, tokensNode, TOKEN_NT_NAME);
             tokenTree.remove();
             root.commit();
 
-            assertNull(tokenProvider.getTokenInfo(info.getToken()));
+            fail("Manually creating a token node must fail.");
+        } catch (CommitFailedException e) {
+            assertEquals(63, e.getCode());
         } finally {
-            node.getTree().remove();
+            root.refresh();
             root.commit();
         }
     }
 
     @Test
-    public void testGetTokenInfoFromInvalidLocation4() throws Exception {
+    public void testCreateTokenWithInvalidNodeType() throws Exception {
         TokenInfo info = tokenProvider.createToken(userId, 
Collections.<String, Object>emptyMap());
         Tree tokenTree = getTokenTree(info);
 
         assertNotNull(tokenProvider.getTokenInfo(info.getToken()));
 
-        TokenInfo info2 = null;
+        Tree userTree = 
root.getTree(getUserManager(root).getAuthorizable(userId).getPath());
+        NodeUtil node = new NodeUtil(userTree.getChild(TOKENS_NODE_NAME));
+        Tree t = null;
         try {
-            Tree adminTree = 
root.getTree(getUserManager(root).getAuthorizable(adminSession.getAuthInfo().getUserID()).getPath());
-            NodeUtil node = new NodeUtil(adminTree).getOrAddChild(".tokens", 
"nt:unstructured");
-            assertTrue(root.move(tokenTree.getPath(), node.getTree().getPath() 
+ "/" + tokenTree.getName()));
-            root.commit();
+            t = createTokenTree(info, node, JcrConstants.NT_UNSTRUCTURED);
+            tokenTree.remove();
+            root.commit(CommitMarker.asCommitAttributes());
 
-            info2 = tokenProvider.getTokenInfo(info.getToken());
-            assertNotNull(info2);
-            assertFalse(info2.matches(new TokenCredentials(info.getToken())));
+            fail("The token node must be of type rep:Token.");
+        } catch (CommitFailedException e) {
+            assertEquals(60, e.getCode());
         } finally {
-            Tree t = getTokenTree(info2);
-            t.remove();
-            root.commit();
+            if (t != null) {
+                t.remove();
+                root.commit(CommitMarker.asCommitAttributes());
+            }
         }
     }
 
     @Test
-    public void testGetTokenInfo() throws Exception {
-        String token = tokenProvider.createToken(userId, Collections.<String, 
Object>emptyMap()).getToken();
-        TokenInfo info = tokenProvider.getTokenInfo(token);
-        assertTokenInfo(info, userId);
+    public void testRemoveTokenNode() throws Exception {
+        TokenInfo info = tokenProvider.createToken(userId, 
Collections.<String, Object>emptyMap());
+        getTokenTree(info).remove();
+        root.commit();
     }
 
     @Test
-    public void testCreateTokenWithExpirationParam() throws Exception {
-        SimpleCredentials sc = new SimpleCredentials(userId, new char[0]);
-        sc.setAttribute(TokenProvider.PARAM_TOKEN_EXPIRATION, 100000);
+    public void testInvalidTokenParentNode() throws Exception {
+        Tree userTree = 
root.getTree(getUserManager(root).getAuthorizable(userId).getPath());
+        NodeUtil node = new NodeUtil(userTree).addChild("testNode", 
JcrConstants.NT_UNSTRUCTURED);
+        try {
+            // Invalid node type of '.tokens' node
+            node.addChild(TOKENS_NODE_NAME, JcrConstants.NT_UNSTRUCTURED);
+            root.commit(CommitMarker.asCommitAttributes());
+
+            fail("Creating a new token '" + node.getTree().getPath() + "' must 
fail.");
+        } catch (CommitFailedException e) {
+            assertEquals(68, e.getCode());
+        } finally {
+            node.getTree().remove();
+            root.commit(CommitMarker.asCommitAttributes());
+        }
+    }
 
-        TokenInfo info = tokenProvider.createToken(sc);
-        assertTokenInfo(info, userId);
+    @Test
+    public void testManuallyCreateTokenParent() throws Exception {
+        Tree userTree = 
root.getTree(getUserManager(root).getAuthorizable(userId).getPath());
+        NodeUtil node = new NodeUtil(userTree);
 
-        Tree tokenTree = getTokenTree(info);
-        assertNotNull(tokenTree);
-        assertTrue(tokenTree.exists());
-        
assertTrue(tokenTree.hasProperty(TokenProvider.PARAM_TOKEN_EXPIRATION));
-        assertEquals(100000, 
tokenTree.getProperty(TokenProvider.PARAM_TOKEN_EXPIRATION).getValue(Type.LONG).longValue());
+        node.addChild(TOKENS_NODE_NAME, TOKENS_NT_NAME);
+        root.commit();
     }
 
     @Test
-    public void testCreateTokenWithInvalidExpirationParam() throws Exception {
-        SimpleCredentials sc = new SimpleCredentials(userId, new char[0]);
-        sc.setAttribute(TokenProvider.PARAM_TOKEN_EXPIRATION, "invalid");
+    public void testManuallyCreateTokenParentWithNtUnstructured() throws 
Exception {
+        Tree userTree = 
root.getTree(getUserManager(root).getAuthorizable(userId).getPath());
+        NodeUtil node = new NodeUtil(userTree);
 
-        try {
-            tokenProvider.createToken(sc);
-            fail();
-        } catch (NumberFormatException e) {
-            // success
-        }
+        node.addChild(TOKENS_NODE_NAME, JcrConstants.NT_UNSTRUCTURED);
+        root.commit();
     }
 
-    /**
-     *@see <a 
href="https://issues.apache.org/jira/browse/OAK-1697";>OAK-1697</a>
-     */
     @Test
-    public void testValidTokenCredentialsWithConflict() throws Exception {
-        ExecutorService pool = Executors.newFixedThreadPool(10);
-        List<ContentSession> sessions = new ArrayList<ContentSession>();
-
+    public void testTokensNodeBelowRoot() throws Exception {
+        NodeUtil rootNode = new NodeUtil(root.getTree("/"));
+        NodeUtil n = null;
         try {
-            TokenConfiguration tc = getSecurityProvider().getConfiguration(
-                    TokenConfiguration.class);
-            SimpleCredentials sc = (SimpleCredentials) getAdminCredentials();
-
-            List<TokenProvider> tokenProviders = new 
ArrayList<TokenProvider>();
+            // Invalid node type of '.tokens' node
+            n = rootNode.addChild(TOKENS_NODE_NAME, TOKENS_NT_NAME);
+            root.commit();
 
-            for (int i = 0; i < 10; i++) {
-                ContentSession session = login(getAdminCredentials());
-                Root r = session.getLatestRoot();
-                tokenProviders.add(tc.getTokenProvider(r));
-                sessions.add(session);
+            fail("The token parent node must be located below the configured 
user root.");
+        } catch (CommitFailedException e) {
+            assertEquals(64, e.getCode());
+        } finally {
+            if (n != null) {
+                n.getTree().remove();
+                root.commit(CommitMarker.asCommitAttributes());
             }
+        }
+    }
 
-            ArrayList<DataFuture> list = new ArrayList<DataFuture>();
-
-            for (TokenProvider tokenProvider : tokenProviders) {
-                list.add(createDataFuture(pool, tokenProvider, sc.getUserID(),
-                        Collections.<String, Object> emptyMap()));
-            }
+    @Test
+    public void testTokensNodeAtInvalidPathBelowUser() throws Exception {
+        Tree userTree = 
root.getTree(getUserManager(root).getAuthorizable(userId).getPath());
+        NodeUtil userNode = new NodeUtil(userTree);
+        NodeUtil n = null;
+        try {
+            // Invalid node type of '.tokens' node
+            n = userNode.addChild("test", JcrConstants.NT_UNSTRUCTURED);
+            n.addChild(TOKENS_NODE_NAME, TOKENS_NT_NAME);
+            root.commit();
 
-            for (DataFuture df : list) {
-                assertNotNull(df.future.get());
-            }
+            fail("The token parent node must be located below the user home 
node.");
+        } catch (CommitFailedException e) {
+            assertEquals(68, e.getCode());
         } finally {
-            for (ContentSession session : sessions) {
-                if (session != null) {
-                    session.close();
-                }
-            }
-
-            if (pool != null) {
-                pool.shutdown();
+            if (n != null) {
+                n.getTree().remove();
+                root.commit(CommitMarker.asCommitAttributes());
             }
         }
     }
 
-    /**
-     * @see OAK-1985
-     */
     @Test
-    public void testTokenValidationIsCaseInsensitive() throws Exception {
-        Root root = adminSession.getLatestRoot();
-        TokenConfiguration tokenConfig = 
getSecurityProvider().getConfiguration(TokenConfiguration.class);
-        TokenProvider tp = tokenConfig.getTokenProvider(root);
-
-        String userId = ((SimpleCredentials) 
getAdminCredentials()).getUserID();
-        TokenInfo info = tp.createToken(userId.toUpperCase(), 
Collections.<String, Object>emptyMap());
-
-        assertTrue(info.matches(new TokenCredentials(info.getToken())));
-        assertEquals(userId, info.getUserId());
-
-        info = tp.getTokenInfo(info.getToken());
+    public void testChangeTokenParentPrimaryTypeToRepUnstructured() throws 
Exception {
+        Tree userTree = 
root.getTree(getUserManager(root).getAuthorizable(userId).getPath());
+        NodeUtil node = new NodeUtil(userTree);
 
-        assertTrue(info.matches(new TokenCredentials(info.getToken())));
-        assertEquals(userId, info.getUserId());
-    }
+        node = node.addChild(TOKENS_NODE_NAME, JcrConstants.NT_UNSTRUCTURED);
+        root.commit();
 
-    
//--------------------------------------------------------------------------
-    private static void assertTokenInfo(TokenInfo info, String userId) {
-        assertNotNull(info);
-        assertNotNull(info.getToken());
-        assertEquals(userId, info.getUserId());
-        assertFalse(info.isExpired(new Date().getTime()));
+        node.setName(JcrConstants.JCR_PRIMARYTYPE, TOKENS_NT_NAME);
+        root.commit();
     }
 
-    private Tree getTokenTree(TokenInfo info) {
-        String token = info.getToken();
-        int pos = token.indexOf('_');
-        String nodeId = (pos == -1) ? token : token.substring(0, pos);
-        return new IdentifierManager(root).getTree(nodeId);
-    }
+    @Test
+    public void testChangeTokenParentPrimaryType() throws Exception {
+        TokenInfo info = tokenProvider.createToken(userId, 
Collections.<String, Object>emptyMap());
 
-    private void createTokenTree(TokenInfo base, NodeUtil parent, String 
ntName) throws AccessDeniedException {
-        Tree tokenTree = getTokenTree(base);
-        Tree tree = parent.addChild("token", ntName).getTree();
-        tree.setProperty(tokenTree.getProperty(JcrConstants.JCR_UUID));
-        tree.setProperty(tokenTree.getProperty("rep:token.key"));
-        tree.setProperty(tokenTree.getProperty("rep:token.exp"));
-    }
-    
-    private static class DataFuture {
-        public Future<TokenInfo> future;
+        try {
+            Tree tokensTree = getTokenTree(info).getParent();
+            tokensTree.setProperty(JcrConstants.JCR_PRIMARYTYPE, 
JcrConstants.NT_UNSTRUCTURED, Type.NAME);
 
-        public DataFuture(Future<TokenInfo> future) {
-            super();
-            this.future = future;
+            root.commit();
+            fail("The primary type of the token parent must not be changed 
from rep:Unstructured to another type.");
+        } catch (CommitFailedException e) {
+            assertEquals(69, e.getCode());
+        } finally {
+            root.refresh();
         }
     }
-    
-    private DataFuture createDataFuture(ExecutorService pool , final 
TokenProvider tp,final String userId, final Map<String, ?> attributes){
-        Future<TokenInfo> future = pool.submit(new Callable<TokenInfo>() {
-            @Override
-            public TokenInfo call() throws Exception {
-                return tp.createToken(userId, attributes);
-            }
-        });
-        return new DataFuture(future);
+
+    @Test
+    public void testChangeRegularRepUnstructuredPrimaryType() throws Exception 
{
+        Tree userTree = 
root.getTree(getUserManager(root).getAuthorizable(userId).getPath());
+        NodeUtil n = new NodeUtil(userTree).getOrAddChild("test", 
NodeTypeConstants.NT_REP_UNSTRUCTURED);
+        root.commit();
+
+        n.setName(JcrConstants.JCR_PRIMARYTYPE, JcrConstants.NT_UNSTRUCTURED);
+        root.commit();
     }
 }
\ No newline at end of file

Modified: 
jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/query/QueryTest.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/query/QueryTest.java?rev=1658358&r1=1658357&r2=1658358&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/query/QueryTest.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/query/QueryTest.java
 Mon Feb  9 11:03:21 2015
@@ -21,7 +21,6 @@ package org.apache.jackrabbit.oak.jcr.qu
 import static com.google.common.collect.Sets.newHashSet;
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertTrue;
-import static org.apache.jackrabbit.JcrConstants.NT_FOLDER;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.fail;
 
@@ -49,6 +48,10 @@ import javax.jcr.query.QueryResult;
 import javax.jcr.query.Row;
 import javax.jcr.query.RowIterator;
 
+import org.apache.jackrabbit.api.JackrabbitSession;
+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.commons.JcrUtils;
 import org.apache.jackrabbit.commons.cnd.CndImporter;
 import org.apache.jackrabbit.oak.jcr.AbstractRepositoryTest;
@@ -582,10 +585,12 @@ public class QueryTest extends AbstractR
     public void xpathEscapeTest() throws RepositoryException {
         Session writer = createAdminSession();
         Session reader = createAdminSession();
+
+        UserManager uMgr = ((JackrabbitSession) writer).getUserManager();
+        String uid = "testUser";
         try {
-            Node rootNode = writer.getRootNode();
-            Node node = rootNode.addNode("test", "nt:unstructured");
-            node.addNode(".tokens");
+            User user = uMgr.createUser("testUser", "pw");
+            writer.getNode(user.getPath()).addNode(".tokens", 
"rep:Unstructured");
             writer.save();
 
             QueryManager qm = reader.getWorkspace().getQueryManager();
@@ -593,6 +598,11 @@ public class QueryTest extends AbstractR
             NodeIterator res = q.execute().getNodes();
             assertEquals(1, res.getSize());
         } finally {
+            Authorizable a = uMgr.getAuthorizable(uid);
+            if (a != null) {
+                a.remove();
+                writer.save();
+            }
             if (reader != null) {
                 reader.logout();
             }


Reply via email to