jackrabbit-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ang...@apache.org
Subject svn commit: r1537027 [1/2] - in /jackrabbit/trunk/jackrabbit-core/src: main/java/org/apache/jackrabbit/core/ main/java/org/apache/jackrabbit/core/security/authentication/ main/java/org/apache/jackrabbit/core/security/authentication/token/ main/resource...
Date Wed, 30 Oct 2013 09:59:29 GMT
Author: angela
Date: Wed Oct 30 09:59:28 2013
New Revision: 1537027

URL: http://svn.apache.org/r1537027
Log:
JCR-3687 : Backport improvements made to token based auth in OAK

Added:
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authentication/token/CompatTokenProvider.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authentication/token/TokenInfo.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authentication/token/TokenProvider.java
    jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authentication/token/TokenBasedAuthenticationCompatTest.java   (contents, props changed)
      - copied, changed from r1535075, jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authentication/token/TokenBasedAuthenticationTest.java
Modified:
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/ProtectedItemModifier.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authentication/DefaultLoginModule.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authentication/token/TokenBasedAuthentication.java
    jackrabbit/trunk/jackrabbit-core/src/main/resources/org/apache/jackrabbit/core/nodetype/builtin_nodetypes.cnd
    jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authentication/token/TestAll.java
    jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authentication/token/TokenBasedAuthenticationTest.java

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/ProtectedItemModifier.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/ProtectedItemModifier.java?rev=1537027&r1=1537026&r2=1537027&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/ProtectedItemModifier.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/ProtectedItemModifier.java Wed Oct 30 09:59:28 2013
@@ -16,10 +16,17 @@
  */
 package org.apache.jackrabbit.core;
 
+import javax.jcr.AccessDeniedException;
+import javax.jcr.ItemExistsException;
+import javax.jcr.Property;
+import javax.jcr.RepositoryException;
+import javax.jcr.Value;
+
 import org.apache.jackrabbit.core.id.NodeId;
 import org.apache.jackrabbit.core.nodetype.NodeTypeImpl;
 import org.apache.jackrabbit.core.retention.RetentionManagerImpl;
 import org.apache.jackrabbit.core.security.AccessManager;
+import org.apache.jackrabbit.core.security.authentication.token.TokenProvider;
 import org.apache.jackrabbit.core.security.authorization.Permission;
 import org.apache.jackrabbit.core.security.authorization.acl.ACLEditor;
 import org.apache.jackrabbit.core.security.user.UserManagerImpl;
@@ -30,12 +37,6 @@ import org.apache.jackrabbit.core.value.
 import org.apache.jackrabbit.spi.Name;
 import org.apache.jackrabbit.spi.Path;
 
-import javax.jcr.AccessDeniedException;
-import javax.jcr.ItemExistsException;
-import javax.jcr.Property;
-import javax.jcr.RepositoryException;
-import javax.jcr.Value;
-
 /**
  * <code>ProtectedItemModifier</code>: An abstract helper class to allow classes
  * residing outside of the core package to modify and remove protected items.
@@ -58,6 +59,7 @@ public abstract class ProtectedItemModif
         if (!(UserManagerImpl.class.isAssignableFrom(cl) ||
               RetentionManagerImpl.class.isAssignableFrom(cl) ||
               ACLEditor.class.isAssignableFrom(cl) ||
+              TokenProvider.class.isAssignableFrom(cl) ||
               org.apache.jackrabbit.core.security.authorization.principalbased.ACLEditor.class.isAssignableFrom(cl))) {
             throw new IllegalArgumentException("Only UserManagerImpl, RetentionManagerImpl and ACLEditor may extend from the ProtectedItemModifier");
         }

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authentication/DefaultLoginModule.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authentication/DefaultLoginModule.java?rev=1537027&r1=1537026&r2=1537027&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authentication/DefaultLoginModule.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authentication/DefaultLoginModule.java Wed Oct 30 09:59:28 2013
@@ -16,29 +16,25 @@
  */
 package org.apache.jackrabbit.core.security.authentication;
 
+import java.security.Principal;
+import java.util.Map;
+import javax.jcr.Credentials;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.security.auth.Subject;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.login.FailedLoginException;
+import javax.security.auth.login.LoginException;
+
 import org.apache.jackrabbit.api.security.authentication.token.TokenCredentials;
-import org.apache.jackrabbit.api.security.principal.ItemBasedPrincipal;
 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.core.NodeImpl;
 import org.apache.jackrabbit.core.SessionImpl;
 import org.apache.jackrabbit.core.security.authentication.token.TokenBasedAuthentication;
-import org.apache.jackrabbit.core.security.user.UserImpl;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import javax.jcr.Credentials;
-import javax.jcr.Node;
-import javax.jcr.RepositoryException;
-import javax.jcr.Session;
-import javax.security.auth.Subject;
-import javax.security.auth.callback.CallbackHandler;
-import javax.security.auth.login.FailedLoginException;
-import javax.security.auth.login.LoginException;
-import java.security.Principal;
-import java.util.Map;
-
 /**
  * The <code>DefaultLoginModule</code> authenticates Credentials related to
  * a {@link User} of the Repository<br>
@@ -229,20 +225,7 @@ public class DefaultLoginModule extends 
             // special token based login
             tokenCredentials = ((TokenCredentials) credentials);
             try {
-                Node n = TokenBasedAuthentication.getTokenNode(tokenCredentials, session);
-                final NodeImpl userNode = (NodeImpl) n.getParent().getParent();
-                final String principalName = userNode.getProperty(UserImpl.P_PRINCIPAL_NAME).getString();
-                if (userNode.isNodeType(UserImpl.NT_REP_USER)) {
-                    Authorizable a = userManager.getAuthorizable(new ItemBasedPrincipal() {
-                        public String getPath() throws RepositoryException {
-                            return userNode.getPath();
-                        }
-                        public String getName() {
-                            return principalName;
-                        }
-                    });
-                    return a.getID();
-                }
+                return TokenBasedAuthentication.getUserId(tokenCredentials, session);
             } catch (RepositoryException e) {
                 if (log.isDebugEnabled()) {
                     log.warn("Failed to retrieve UserID from token-based credentials", e);

Added: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authentication/token/CompatTokenProvider.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authentication/token/CompatTokenProvider.java?rev=1537027&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authentication/token/CompatTokenProvider.java (added)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authentication/token/CompatTokenProvider.java Wed Oct 30 09:59:28 2013
@@ -0,0 +1,427 @@
+/*
+ * 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.core.security.authentication.token;
+
+import java.io.UnsupportedEncodingException;
+import java.security.NoSuchAlgorithmException;
+import java.security.Principal;
+import java.security.SecureRandom;
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.HashMap;
+import java.util.Map;
+import javax.jcr.Node;
+import javax.jcr.Property;
+import javax.jcr.PropertyIterator;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.SimpleCredentials;
+
+import org.apache.jackrabbit.api.JackrabbitSession;
+import org.apache.jackrabbit.api.security.authentication.token.TokenCredentials;
+import org.apache.jackrabbit.api.security.principal.ItemBasedPrincipal;
+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.core.NodeImpl;
+import org.apache.jackrabbit.core.SessionImpl;
+import org.apache.jackrabbit.core.id.NodeId;
+import org.apache.jackrabbit.core.id.NodeIdFactory;
+import org.apache.jackrabbit.core.security.SecurityConstants;
+import org.apache.jackrabbit.core.security.user.UserImpl;
+import org.apache.jackrabbit.spi.Name;
+import org.apache.jackrabbit.util.ISO8601;
+import org.apache.jackrabbit.util.Text;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Backport of the TokenProvider implementation present with OAK adjusted to
+ * match some subtle differences in jackrabbit token login.
+ */
+class CompatTokenProvider {
+
+    private static final Logger log = LoggerFactory.getLogger(CompatTokenProvider.class);
+
+    private static final String TOKEN_ATTRIBUTE = ".token";
+    private static final String TOKEN_ATTRIBUTE_EXPIRY = TOKEN_ATTRIBUTE + ".exp";
+    private static final String TOKEN_ATTRIBUTE_KEY = TOKEN_ATTRIBUTE + ".key";
+    private static final String TOKENS_NODE_NAME = ".tokens";
+    private static final String TOKENS_NT_NAME = "nt:unstructured"; // TODO: configurable
+
+    private static final char DELIM = '_';
+
+    private final SessionImpl session;
+    private final UserManager userManager;
+    private final long tokenExpiration;
+
+    CompatTokenProvider(SessionImpl session, long tokenExpiration) throws RepositoryException {
+        this.session = session;
+        this.userManager = session.getUserManager();
+        this.tokenExpiration = tokenExpiration;
+    }
+
+    /**
+     * Create a separate token node underneath a dedicated token store within
+     * the user home node. That token node contains the hashed token, the
+     * expiration time and additional mandatory attributes that will be verified
+     * during login.
+     *
+     * @param user
+     * @param sc The current simple credentials.
+     * @return A new {@code TokenInfo} or {@code null} if the token could not
+     *         be created.
+     */
+    public TokenInfo createToken(User user, SimpleCredentials sc) throws RepositoryException {
+        String userPath = null;
+        Principal pr = user.getPrincipal();
+        if (pr instanceof ItemBasedPrincipal) {
+            userPath = ((ItemBasedPrincipal) pr).getPath();
+        }
+
+        TokenCredentials tokenCredentials;
+        if (userPath != null && session.nodeExists(userPath)) {
+            Node userNode = session.getNode(userPath);
+            Node tokenParent;
+            if (!userNode.hasNode(TOKENS_NODE_NAME)) {
+                userNode.addNode(TOKENS_NODE_NAME, TOKENS_NT_NAME);
+                try {
+                    session.save();
+                } catch (RepositoryException e) {
+                    // may happen when .tokens node is created concurrently
+                    session.refresh(false);
+                }
+            }
+            tokenParent = userNode.getNode(TOKENS_NODE_NAME);
+
+            long creationTime = new Date().getTime();
+            long expirationTime = creationTime + tokenExpiration;
+
+            Calendar cal = GregorianCalendar.getInstance();
+            cal.setTimeInMillis(creationTime);
+
+            // generate key part of the login token
+            String key = generateKey(8);
+
+            // create the token node
+            String tokenName = Text.replace(ISO8601.format(cal), ":", ".");
+            Node tokenNode;
+            // avoid usage of sequential nodeIDs
+            if (System.getProperty(NodeIdFactory.SEQUENTIAL_NODE_ID) == null) {
+                tokenNode = tokenParent.addNode(tokenName);
+            } else {
+                tokenNode = ((NodeImpl) tokenParent).addNodeWithUuid(tokenName, NodeId.randomId().toString());
+            }
+
+            StringBuilder sb = new StringBuilder(tokenNode.getIdentifier());
+            sb.append(DELIM).append(key);
+
+            String token = sb.toString();
+            tokenCredentials = new TokenCredentials(token);
+            sc.setAttribute(TOKEN_ATTRIBUTE, token);
+
+            // add key property
+            tokenNode.setProperty(TOKEN_ATTRIBUTE_KEY, getDigestedKey(key));
+
+            // add expiration time property
+            cal.setTimeInMillis(expirationTime);
+            tokenNode.setProperty(TOKEN_ATTRIBUTE_EXPIRY, session.getValueFactory().createValue(cal));
+
+            // add additional attributes passed in by the credentials.
+            for (String name : sc.getAttributeNames()) {
+                if (!TOKEN_ATTRIBUTE.equals(name)) {
+                    String value = sc.getAttribute(name).toString();
+                    tokenNode.setProperty(name, value);
+                    tokenCredentials.setAttribute(name, value);
+                }
+            }
+            session.save();
+            return new CompatModeInfo(token, tokenNode);
+        } else {
+            throw new RepositoryException("Cannot create login token: No corresponding node for User " + user.getID() +" in workspace '" + session.getWorkspace().getName() + "'.");
+        }
+    }
+
+    /**
+     * Retrieves the token information associated with the specified login
+     * token. If no accessible {@code Tree} exists for the given token or if
+     * the token is not associated with a valid user this method returns {@code null}.
+     *
+     * @param token A valid login token.
+     * @return The {@code TokenInfo} associated with the specified token or
+     *         {@code null} of the corresponding information does not exist or is not
+     *         associated with a valid user.
+     */
+    public TokenInfo getTokenInfo(String token) throws RepositoryException {
+        if (token == null) {
+            return null;
+        }
+        NodeImpl tokenNode = (NodeImpl) getTokenNode(token, session);
+        String userId = getUserId(tokenNode, userManager);
+        if (userId == null || !isValidTokenTree(tokenNode)) {
+            return null;
+        } else {
+            return new CompatModeInfo(token);
+        }
+    }
+
+    static Node getTokenNode(String token, Session session) throws RepositoryException {
+        int pos = token.indexOf(DELIM);
+        String id = (pos == -1) ? token : token.substring(0, pos);
+        return session.getNodeByIdentifier(id);
+    }
+
+    public static String getUserId(TokenCredentials tokenCredentials, Session session) throws RepositoryException {
+        if (!(session instanceof JackrabbitSession)) {
+            throw new RepositoryException("JackrabbitSession expected");
+        }
+        NodeImpl n = (NodeImpl) getTokenNode(tokenCredentials.getToken(), session);
+        return getUserId(n, ((JackrabbitSession) session).getUserManager());
+    }
+
+    private static String getUserId(NodeImpl tokenNode, UserManager userManager) throws RepositoryException {
+        if (tokenNode != null) {
+            final NodeImpl userNode = (NodeImpl) tokenNode.getParent().getParent();
+            final String principalName = userNode.getProperty(UserImpl.P_PRINCIPAL_NAME).getString();
+            if (userNode.isNodeType(UserImpl.NT_REP_USER)) {
+                Authorizable a = userManager.getAuthorizable(new ItemBasedPrincipal() {
+                    public String getPath() throws RepositoryException {
+                        return userNode.getPath();
+                    }
+
+                    public String getName() {
+                        return principalName;
+                    }
+                });
+                if (a != null && !a.isGroup() && !((User)a).isDisabled()) {
+                    return a.getID();
+                }
+            } else {
+                throw new RepositoryException("Failed to calculate userId from token credentials");
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Returns {@code true} if the specified {@code attributeName}
+     * starts with or equals {@link #TOKEN_ATTRIBUTE}.
+     *
+     * @param attributeName The attribute name.
+     * @return {@code true} if the specified {@code attributeName}
+     *         starts with or equals {@link #TOKEN_ATTRIBUTE}.
+     */
+    static boolean isMandatoryAttribute(String attributeName) {
+        return attributeName != null && attributeName.startsWith(TOKEN_ATTRIBUTE);
+    }
+
+    /**
+     * Returns <code>false</code> if the specified attribute name doesn't have
+     * a 'jcr' or 'rep' namespace prefix; <code>true</code> otherwise. This is
+     * a lazy evaluation in order to avoid testing the defining node type of
+     * the associated jcr property.
+     *
+     * @param propertyName
+     * @return <code>true</code> if the specified property name doesn't seem
+     * to represent repository internal information.
+     */
+    private static boolean isInfoAttribute(String propertyName) {
+        String prefix = Text.getNamespacePrefix(propertyName);
+        return !Name.NS_JCR_PREFIX.equals(prefix) && !Name.NS_REP_PREFIX.equals(prefix);
+    }
+
+    private static boolean isValidTokenTree(NodeImpl tokenNode) throws RepositoryException {
+        if (tokenNode == null) {
+            return false;
+        } else {
+            return TOKENS_NODE_NAME.equals(tokenNode.getParent().getName());
+        }
+    }
+
+    private static String generateKey(int size) {
+        SecureRandom random = new SecureRandom();
+        byte key[] = new byte[size];
+        random.nextBytes(key);
+
+        StringBuffer res = new StringBuffer(key.length * 2);
+        for (byte b : key) {
+            res.append(Text.hexTable[(b >> 4) & 15]);
+            res.append(Text.hexTable[b & 15]);
+        }
+        return res.toString();
+    }
+
+    private static String getDigestedKey(TokenCredentials tc) throws RepositoryException {
+        String tk = tc.getToken();
+        int pos = tk.indexOf(DELIM);
+        if (pos > -1) {
+            return getDigestedKey(tk.substring(pos+1));
+        }
+        return null;
+    }
+
+    private static String getDigestedKey(String key) throws RepositoryException {
+        try {
+            StringBuilder sb = new StringBuilder();
+            sb.append("{").append(SecurityConstants.DEFAULT_DIGEST).append("}");
+            sb.append(Text.digest(SecurityConstants.DEFAULT_DIGEST, key, "UTF-8"));
+            return sb.toString();
+        } catch (NoSuchAlgorithmException e) {
+            throw new RepositoryException("Failed to generate login token.");
+        } catch (UnsupportedEncodingException e) {
+            throw new RepositoryException("Failed to generate login token.");
+        }
+    }
+
+    private final class CompatModeInfo implements TokenInfo {
+
+        private final String token;
+
+        private final Map<String, String> attributes;
+        private final Map<String, String> info;
+        private final long expiry;
+        private final String key;
+
+        private CompatModeInfo(String token) throws RepositoryException {
+            this(token, getTokenNode(token, session));
+        }
+
+        private CompatModeInfo(String token, Node n) throws RepositoryException {
+            this.token = token;
+            long expTime = Long.MAX_VALUE;
+            String keyV = null;
+            if (token != null) {
+                attributes = new HashMap<String, String>();
+                info = new HashMap<String, String>();
+
+                PropertyIterator it = n.getProperties();
+                while (it.hasNext()) {
+                    Property p = it.nextProperty();
+                    String name = p.getName();
+                    if (TOKEN_ATTRIBUTE_EXPIRY.equals(name)) {
+                        expTime = p.getLong();
+                    } else if (TOKEN_ATTRIBUTE_KEY.equals(name)) {
+                        keyV = p.getString();
+                    } else if (isMandatoryAttribute(name)) {
+                        attributes.put(name, p.getString());
+                    } else if (isInfoAttribute(name)) {
+                        info.put(name, p.getString());
+                    } // else: jcr property -> ignore
+                }
+            } else {
+                attributes = Collections.emptyMap();
+                info = Collections.emptyMap();
+            }
+            expiry = expTime;
+            key = keyV;
+        }
+
+        public String getToken() {
+            return token;
+        }
+
+        public boolean isExpired(long loginTime) {
+            return expiry < loginTime;
+        }
+
+        public boolean remove() {
+            Session s = null;
+            try {
+                s = ((SessionImpl) session).createSession(session.getWorkspace().getName());
+                Node tokenNode = getTokenNode(token, s);
+
+                tokenNode.remove();
+                s.save();
+                return true;
+            } catch (RepositoryException e) {
+                log.warn("Internal error while removing token node.", e);
+            } finally {
+                if (s != null) {
+                    s.logout();
+                }
+            }
+            return false;
+        }
+
+        public boolean matches(TokenCredentials tokenCredentials) throws RepositoryException {
+            // test for matching key
+            if (key != null && !key.equals(getDigestedKey(tokenCredentials))) {
+                return false;
+            }
+
+            // check if all other required attributes match
+            for (String name : attributes.keySet()) {
+                if (!attributes.get(name).equals(tokenCredentials.getAttribute(name))) {
+                    // no match -> login fails.
+                    return false;
+                }
+            }
+
+            // update set of informative attributes on the credentials
+            // based on the properties present on the token node.
+            Collection<String> attrNames = Arrays.asList(tokenCredentials.getAttributeNames());
+            for (String key : info.keySet()) {
+                if (!attrNames.contains(key)) {
+                    tokenCredentials.setAttribute(key, info.get(key));
+                }
+            }
+
+            return true;
+        }
+
+        public boolean resetExpiration(long loginTime) throws RepositoryException {
+            Node tokenNode;
+            Session s = null;
+            try {
+                // expiry...
+                if (expiry - loginTime <= tokenExpiration/2) {
+                    long expirationTime = loginTime + tokenExpiration;
+                    Calendar cal = GregorianCalendar.getInstance();
+                    cal.setTimeInMillis(expirationTime);
+
+                    s = ((SessionImpl) session).createSession(session.getWorkspace().getName());
+                    tokenNode = getTokenNode(token, s);
+                    tokenNode.setProperty(TOKEN_ATTRIBUTE_EXPIRY, s.getValueFactory().createValue(cal));
+                    s.save();
+                    return true;
+                }
+            } catch (RepositoryException e) {
+                log.warn("Failed to update expiry or informative attributes of token node.", e);
+            } finally {
+                if (s != null) {
+                    s.logout();
+                }
+            }
+            return false;
+        }
+
+        public TokenCredentials getCredentials() {
+            TokenCredentials tc = new TokenCredentials(token);
+            for (String name : attributes.keySet()) {
+                tc.setAttribute(name, attributes.get(name));
+            }
+            for (String name : info.keySet()) {
+                tc.setAttribute(name, info.get(name));
+            }
+            return tc;
+        }
+    }
+}
\ No newline at end of file

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authentication/token/TokenBasedAuthentication.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authentication/token/TokenBasedAuthentication.java?rev=1537027&r1=1537026&r2=1537027&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authentication/token/TokenBasedAuthentication.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authentication/token/TokenBasedAuthentication.java Wed Oct 30 09:59:28 2013
@@ -16,41 +16,22 @@
  */
 package org.apache.jackrabbit.core.security.authentication.token;
 
+import java.util.Date;
+import javax.jcr.Credentials;
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.SimpleCredentials;
+
+import org.apache.jackrabbit.api.JackrabbitSession;
 import org.apache.jackrabbit.api.security.authentication.token.TokenCredentials;
-import org.apache.jackrabbit.api.security.principal.ItemBasedPrincipal;
 import org.apache.jackrabbit.api.security.user.User;
 import org.apache.jackrabbit.core.NodeImpl;
 import org.apache.jackrabbit.core.SessionImpl;
-import org.apache.jackrabbit.core.id.NodeId;
-import org.apache.jackrabbit.core.id.NodeIdFactory;
-import org.apache.jackrabbit.core.security.SecurityConstants;
 import org.apache.jackrabbit.core.security.authentication.Authentication;
-import org.apache.jackrabbit.spi.Name;
-import org.apache.jackrabbit.util.ISO8601;
-import org.apache.jackrabbit.util.Text;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import javax.jcr.Credentials;
-import javax.jcr.Node;
-import javax.jcr.Property;
-import javax.jcr.PropertyIterator;
-import javax.jcr.RepositoryException;
-import javax.jcr.Session;
-import javax.jcr.SimpleCredentials;
-import java.io.UnsupportedEncodingException;
-import java.security.NoSuchAlgorithmException;
-import java.security.Principal;
-import java.security.SecureRandom;
-import java.util.Arrays;
-import java.util.Calendar;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Date;
-import java.util.GregorianCalendar;
-import java.util.HashMap;
-import java.util.Map;
-
 /**
  * Authentication implementation that compares the tokens stored with a
  * given user node to the token present in the SimpleCredentials attributes.
@@ -72,60 +53,29 @@ public class TokenBasedAuthentication im
      */
     public static final String TOKEN_ATTRIBUTE = ".token";
 
-    private static final String TOKEN_ATTRIBUTE_EXPIRY = TOKEN_ATTRIBUTE + ".exp";
-    private static final String TOKEN_ATTRIBUTE_KEY = TOKEN_ATTRIBUTE + ".key";
-    private static final String TOKENS_NODE_NAME = ".tokens";
-    private static final String TOKENS_NT_NAME = "nt:unstructured"; // TODO: configurable
-
-    private static final char DELIM = '_';
-
-    private final String token;
-    private final long tokenExpiration;
-    private final Session session;
-
-    private final Map<String, String> attributes;
-    private final Map<String, String> info;
-    private final long expiry;
-    private final String key;
+    /**
+     * @deprecated This system parameter allows to enable backwards compatible
+     * behavior of the {@code TokenBasedAuthentication}. Note that as of OAK 1.0
+     * this flag will no be supported.
+     */
+    public static final String PARAM_COMPAT = "TokenCompatMode";
+
+    private final TokenInfo tokenInfo;
 
     public TokenBasedAuthentication(String token, long tokenExpiration, Session session) throws RepositoryException {
-        this.session = session;
-        this.tokenExpiration = tokenExpiration;
-        this.token = token;
-        long expTime = Long.MAX_VALUE;
-        String keyV = null;
-        if (token != null) {
-            attributes = new HashMap<String, String>();
-            info = new HashMap<String, String>();
-
-            Node n = getTokenNode(token, session);
-            PropertyIterator it = n.getProperties();
-            while (it.hasNext()) {
-                Property p = it.nextProperty();
-                String name = p.getName();
-                if (TOKEN_ATTRIBUTE_EXPIRY.equals(name)) {
-                    expTime = p.getLong();
-                } else if (TOKEN_ATTRIBUTE_KEY.equals(name)) {
-                    keyV = p.getString();
-                } else if (isMandatoryAttribute(name)) {
-                    attributes.put(name, p.getString());
-                } else if (isInfoAttribute(name)) {
-                    info.put(name, p.getString());
-                } // else: jcr property -> ignore
-            }
+        if (compatMode()) {
+            this.tokenInfo = new CompatTokenProvider((SessionImpl) session, tokenExpiration).getTokenInfo(token);
         } else {
-            attributes = Collections.emptyMap();
-            info = Collections.emptyMap();
+            this.tokenInfo = new TokenProvider((SessionImpl) session, tokenExpiration).getTokenInfo(token);
         }
-        expiry = expTime;
-        key = keyV;
+
     }
 
     /**
      * @see Authentication#canHandle(javax.jcr.Credentials)
      */
     public boolean canHandle(Credentials credentials) {
-        return token != null && isTokenBasedLogin(credentials);
+        return tokenInfo != null && isTokenBasedLogin(credentials);
     }
 
     /**
@@ -136,105 +86,31 @@ public class TokenBasedAuthentication im
             throw new RepositoryException("TokenCredentials expected. Cannot handle " + credentials.getClass().getName());
         }
         TokenCredentials tokenCredentials = (TokenCredentials) credentials;
+        return validateCredentials(tokenCredentials);
+    }
 
-        // credentials without userID -> check if attributes provide
-        // sufficient information for successful authentication.
-        if (token.equals(tokenCredentials.getToken())) {
-            long loginTime = new Date().getTime();
-            // test if the token has already expired
-            if (expiry < loginTime) {
-                // already expired -> login fails.
-                // ... remove the expired token node before aborting the login
-                removeToken();
-                return false;
-            }
-
-            // test for matching key
-            if (key != null && !key.equals(getDigestedKey(tokenCredentials))) {
-                return false;
-            }
-
-            // check if all other required attributes match
-            for (String name : attributes.keySet()) {
-                if (!attributes.get(name).equals(tokenCredentials.getAttribute(name))) {
-                    // no match -> login fails.
-                    return false;
-                }
-            }
-
-            // update set of informative attributes on the credentials
-            // based on the properties present on the token node.
-            Collection<String> attrNames = Arrays.asList(tokenCredentials.getAttributeNames());
-            for (String key : info.keySet()) {
-                if (!attrNames.contains(key)) {
-                    tokenCredentials.setAttribute(key, info.get(key));
-                }
-            }
+    private boolean validateCredentials(TokenCredentials tokenCredentials) throws RepositoryException {
+        if (tokenInfo == null) {
+            log.debug("No valid TokenInfo for token.");
+            return false;
+        }
 
-            // update token node if required: optionally resetting the expiration
-            updateTokenNode(expiry, loginTime);
+        long loginTime = new Date().getTime();
+        if (tokenInfo.isExpired(loginTime)) {
+            // token is expired
+            log.debug("Token is expired");
+            tokenInfo.remove();
+            return false;
+        }
 
+        if (tokenInfo.matches(tokenCredentials)) {
+            tokenInfo.resetExpiration(loginTime);
             return true;
         }
 
-        // wrong credentials that cannot be compared by this authentication
         return false;
     }
 
-    /**
-     * Performs the following checks/updates:
-     * <ol>
-     * <li>Reset the expiration if half of the expiration has passed in order to
-     * minimize write operations (avoid resetting upon each login).</li>
-     * </ol>
-     *
-     * @param tokenExpiry
-     * @param loginTime
-     */
-    private void updateTokenNode(long tokenExpiry, long loginTime) {
-        Node tokenNode;
-        Session s = null;
-        try {
-            // expiry...
-            if (tokenExpiry - loginTime <= tokenExpiration/2) {
-                long expirationTime = loginTime + tokenExpiration;
-                Calendar cal = GregorianCalendar.getInstance();
-                cal.setTimeInMillis(expirationTime);
-
-                s = ((SessionImpl) session).createSession(session.getWorkspace().getName());
-                tokenNode = getTokenNode(token, s);
-                tokenNode.setProperty(TOKEN_ATTRIBUTE_EXPIRY, s.getValueFactory().createValue(cal));
-                s.save();
-            }
-        } catch (RepositoryException e) {
-            log.warn("Failed to update expiry or informative attributes of token node.", e);
-        } finally {
-            if (s != null) {
-                s.logout();
-            }
-        }
-    }
-
-    /**
-     * Remove the node associated with the expired token defined by this TokenBasedAuthentication.
-     */
-    private void removeToken() {
-        Session s = null;
-        try {
-            s = ((SessionImpl) session).createSession(session.getWorkspace().getName());
-            Node tokenNode = getTokenNode(token, s);
-            
-            tokenNode.remove();
-            s.save();
-        } catch (RepositoryException e) {
-            log.warn("Internal error while removing token node.", e);
-        } finally {
-            if (s != null) {
-                s.logout();
-            }
-        }
-    }
-
     //--------------------------------------------------------------------------
     /**
      * Returns <code>true</code> if the given <code>credentials</code> object
@@ -251,28 +127,17 @@ public class TokenBasedAuthentication im
     /**
      * Returns <code>true</code> if the specified <code>attributeName</code>
      * starts with or equals {@link #TOKEN_ATTRIBUTE}.
-     *  
+     *
      * @param attributeName
      * @return <code>true</code> if the specified <code>attributeName</code>
      * starts with or equals {@link #TOKEN_ATTRIBUTE}.
      */
     public static boolean isMandatoryAttribute(String attributeName) {
-        return attributeName != null && attributeName.startsWith(TOKEN_ATTRIBUTE);
-    }
-
-    /**
-     * Returns <code>false</code> if the specified attribute name doesn't have
-     * a 'jcr' or 'rep' namespace prefix; <code>true</code> otherwise. This is
-     * a lazy evaluation in order to avoid testing the defining node type of
-     * the associated jcr property.
-     *
-     * @param propertyName
-     * @return <code>true</code> if the specified property name doesn't seem
-     * to represent repository internal information.
-     */
-    private static boolean isInfoAttribute(String propertyName) {
-        String prefix = Text.getNamespacePrefix(propertyName);
-        return !Name.NS_JCR_PREFIX.equals(prefix) && !Name.NS_REP_PREFIX.equals(prefix);
+        if (compatMode()) {
+            return CompatTokenProvider.isMandatoryAttribute(attributeName);
+        } else {
+            return TokenProvider.isMandatoryAttribute(attributeName);
+        }
     }
 
     /**
@@ -305,122 +170,48 @@ public class TokenBasedAuthentication im
      * creating the token node.
      */
     public static Credentials createToken(User user, SimpleCredentials credentials,
-                                                       long tokenExpiration, Session session) throws RepositoryException {
+                                          long tokenExpiration, Session session) throws RepositoryException {
         String workspaceName = session.getWorkspace().getName();
         if (user == null) {
             throw new RepositoryException("Cannot create login token: No corresponding node for 'null' user in workspace '" + workspaceName + "'.");
         }
-        String userPath = null;
-        Principal pr = user.getPrincipal();
-        if (pr instanceof ItemBasedPrincipal) {
-            userPath = ((ItemBasedPrincipal) pr).getPath();
-        }
-
-        TokenCredentials tokenCredentials;
-        if (userPath != null && session.nodeExists(userPath)) {
-            Node userNode = session.getNode(userPath);
-            Node tokenParent;
-            if (!userNode.hasNode(TOKENS_NODE_NAME)) {
-                userNode.addNode(TOKENS_NODE_NAME, TOKENS_NT_NAME);
-                try {
-                    session.save();
-                } catch (RepositoryException e) {
-                    // may happen when .tokens node is created concurrently
-                    session.refresh(false);
-                }
-            }
-            tokenParent = userNode.getNode(TOKENS_NODE_NAME);
-
-            long creationTime = new Date().getTime();
-            long expirationTime = creationTime + tokenExpiration;
-
-            Calendar cal = GregorianCalendar.getInstance();
-            cal.setTimeInMillis(creationTime);
 
-            // generate key part of the login token
-            String key = generateKey(8);
-
-            // create the token node
-            String tokenName = Text.replace(ISO8601.format(cal), ":", ".");
-            Node tokenNode;
-            // avoid usage of sequential nodeIDs
-            if (System.getProperty(NodeIdFactory.SEQUENTIAL_NODE_ID) == null) {
-                tokenNode = tokenParent.addNode(tokenName);
-            } else {
-                tokenNode = ((NodeImpl) tokenParent).addNodeWithUuid(tokenName, NodeId.randomId().toString());
-            }
-
-            StringBuilder sb = new StringBuilder(tokenNode.getIdentifier());
-            sb.append(DELIM).append(key);
+        TokenInfo ti;
+        if (compatMode()) {
+            ti = new CompatTokenProvider((SessionImpl) session, tokenExpiration).createToken(user, credentials);
+        } else {
+            ti = new TokenProvider((SessionImpl) session, tokenExpiration).createToken(user, credentials);
+        }
 
-            String token = sb.toString();
-            tokenCredentials = new TokenCredentials(token);
-            credentials.setAttribute(TOKEN_ATTRIBUTE, token);
-
-            // add key property
-            tokenNode.setProperty(TOKEN_ATTRIBUTE_KEY, getDigestedKey(key));
-
-            // add expiration time property
-            cal.setTimeInMillis(expirationTime);
-            tokenNode.setProperty(TOKEN_ATTRIBUTE_EXPIRY, session.getValueFactory().createValue(cal));
-
-            // add additional attributes passed in by the credentials.
-            for (String name : credentials.getAttributeNames()) {
-                if (!TOKEN_ATTRIBUTE.equals(name)) {
-                    String value = credentials.getAttribute(name).toString();
-                    tokenNode.setProperty(name, value);
-                    tokenCredentials.setAttribute(name, value);
-                }
-            }
-            session.save();
-            return tokenCredentials;
+        if (ti != null) {
+            return ti.getCredentials();
         } else {
-            throw new RepositoryException("Cannot create login token: No corresponding node for User " + user.getID() +" in workspace '" + workspaceName + "'.");
+            throw new RepositoryException("Cannot create login token.");
         }
     }
 
     public static Node getTokenNode(TokenCredentials credentials, Session session) throws RepositoryException {
-        return getTokenNode(credentials.getToken(), session);
+        if (compatMode()) {
+            return CompatTokenProvider.getTokenNode(credentials.getToken(), session);
+        } else {
+            return TokenProvider.getTokenNode(credentials.getToken(), session);
+        }
     }
 
-    private static Node getTokenNode(String token, Session session) throws RepositoryException {
-        int pos = token.indexOf(DELIM);
-        String id = (pos == -1) ? token : token.substring(0, pos);
-        return session.getNodeByIdentifier(id);
-    }
-
-    private static String generateKey(int size) {
-        SecureRandom random = new SecureRandom();
-        byte key[] = new byte[size];
-        random.nextBytes(key);
-
-        StringBuffer res = new StringBuffer(key.length * 2);
-        for (byte b : key) {
-            res.append(Text.hexTable[(b >> 4) & 15]);
-            res.append(Text.hexTable[b & 15]);
-        }
-        return res.toString();
-    }
-
-    private static String getDigestedKey(TokenCredentials tc) throws RepositoryException {
-        String tk = tc.getToken();
-        int pos = tk.indexOf(DELIM);
-        if (pos > -1) {
-            return getDigestedKey(tk.substring(pos+1));
-        }     
-        return null;
-    }
-
-    private static String getDigestedKey(String key) throws RepositoryException {
-        try {
-            StringBuilder sb = new StringBuilder();
-            sb.append("{").append(SecurityConstants.DEFAULT_DIGEST).append("}");
-            sb.append(Text.digest(SecurityConstants.DEFAULT_DIGEST, key, "UTF-8"));
-            return sb.toString();
-        } catch (NoSuchAlgorithmException e) {
-            throw new RepositoryException("Failed to generate login token.");
-        } catch (UnsupportedEncodingException e) {
-            throw new RepositoryException("Failed to generate login token.");
+
+    public static String getUserId(TokenCredentials tokenCredentials, Session session) throws RepositoryException {
+        if (compatMode()) {
+            return CompatTokenProvider.getUserId(tokenCredentials, session);
+        } else {
+            if (!(session instanceof JackrabbitSession)) {
+                throw new RepositoryException("JackrabbitSession expected");
+            }
+            NodeImpl n = (NodeImpl) getTokenNode(tokenCredentials, session);
+            return TokenProvider.getUserId(n, ((JackrabbitSession) session).getUserManager());
         }
     }
+
+    private static boolean compatMode() {
+        return Boolean.parseBoolean(System.getProperty(PARAM_COMPAT));
+    }
 }
\ No newline at end of file

Added: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authentication/token/TokenInfo.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authentication/token/TokenInfo.java?rev=1537027&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authentication/token/TokenInfo.java (added)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authentication/token/TokenInfo.java Wed Oct 30 09:59:28 2013
@@ -0,0 +1,40 @@
+/*
+ * 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.core.security.authentication.token;
+
+import javax.jcr.RepositoryException;
+
+import org.apache.jackrabbit.api.security.authentication.token.TokenCredentials;
+
+/**
+ * TokenInfo... TODO
+ */
+interface TokenInfo {
+
+    String getToken();
+
+
+    boolean isExpired(long loginTime);
+
+    boolean remove();
+
+    boolean matches(TokenCredentials tokenCredentials) throws RepositoryException;
+
+    boolean resetExpiration(long loginTime) throws RepositoryException;
+
+    TokenCredentials getCredentials();
+}
\ No newline at end of file

Added: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authentication/token/TokenProvider.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authentication/token/TokenProvider.java?rev=1537027&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authentication/token/TokenProvider.java (added)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authentication/token/TokenProvider.java Wed Oct 30 09:59:28 2013
@@ -0,0 +1,477 @@
+/*
+ * 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.core.security.authentication.token;
+
+import java.io.UnsupportedEncodingException;
+import java.security.NoSuchAlgorithmException;
+import java.security.Principal;
+import java.security.SecureRandom;
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.Collection;
+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 javax.jcr.AccessDeniedException;
+import javax.jcr.NamespaceRegistry;
+import javax.jcr.Node;
+import javax.jcr.Property;
+import javax.jcr.PropertyIterator;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.SimpleCredentials;
+import javax.jcr.ValueFactory;
+
+import org.apache.jackrabbit.api.security.authentication.token.TokenCredentials;
+import org.apache.jackrabbit.api.security.principal.ItemBasedPrincipal;
+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.core.NodeImpl;
+import org.apache.jackrabbit.core.ProtectedItemModifier;
+import org.apache.jackrabbit.core.SessionImpl;
+import org.apache.jackrabbit.core.id.NodeId;
+import org.apache.jackrabbit.core.security.user.PasswordUtility;
+import org.apache.jackrabbit.core.security.user.UserImpl;
+import org.apache.jackrabbit.spi.Name;
+import org.apache.jackrabbit.spi.commons.name.NameConstants;
+import org.apache.jackrabbit.util.ISO8601;
+import org.apache.jackrabbit.util.Text;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Backport of the TokenProvider implementation present with OAK adjusted to
+ * match some subtle differences in jackrabbit token login.
+ */
+public class TokenProvider extends ProtectedItemModifier {
+
+    private static final Logger log = LoggerFactory.getLogger(TokenProvider.class);
+
+    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 TOKEN_NT_NAME = "rep:Token";
+    private static final Name TOKENS_NT_NAME = NameConstants.NT_UNSTRUCTURED;
+
+    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 static final Collection<String> RESERVED_PREFIXES = Collections.unmodifiableList(Arrays.asList(
+            NamespaceRegistry.PREFIX_XML,
+            NamespaceRegistry.PREFIX_JCR,
+            NamespaceRegistry.PREFIX_NT,
+            NamespaceRegistry.PREFIX_MIX,
+            Name.NS_XMLNS_PREFIX,
+            Name.NS_REP_PREFIX,
+            Name.NS_SV_PREFIX
+    ));
+
+    private final SessionImpl session;
+    private final UserManager userManager;
+    private final long tokenExpiration;
+
+    TokenProvider(SessionImpl session, long tokenExpiration) throws RepositoryException {
+        this.session = session;
+        this.userManager = session.getUserManager();
+        this.tokenExpiration = tokenExpiration;
+    }
+
+    /**
+     * Create a separate token node underneath a dedicated token store within
+     * the user home node. That token node contains the hashed token, the
+     * expiration time and additional mandatory attributes that will be verified
+     * during login.
+     *
+     * @param user
+     * @param sc The current simple credentials.
+     * @return A new {@code TokenInfo} or {@code null} if the token could not
+     *         be created.
+     */
+    public TokenInfo createToken(User user, SimpleCredentials sc) throws RepositoryException {
+        TokenInfo tokenInfo = null;
+        if (sc != null && user != null && user.getID().equals(sc.getUserID())) {
+            String[] attrNames = sc.getAttributeNames();
+            Map<String, String> attributes = new HashMap<String, String>(attrNames.length);
+            for (String attrName : sc.getAttributeNames()) {
+                attributes.put(attrName, sc.getAttribute(attrName).toString());
+            }
+            tokenInfo = createToken(user, attributes);
+            if (tokenInfo != null) {
+                // also set the new token to the simple credentials.
+                sc.setAttribute(TOKEN_ATTRIBUTE, tokenInfo.getToken());
+            }
+        }
+
+        return tokenInfo;
+    }
+
+    /**
+     * Create a separate token node underneath a dedicated token store within
+     * the user home node. That token node contains the hashed token, the
+     * expiration time and additional mandatory attributes that will be verified
+     * during login.
+     *
+     * @param userId     The identifier of the user for which a new token should
+     *                   be created.
+     * @param attributes The attributes associated with the new token.
+     * @return A new {@code TokenInfo} or {@code null} if the token could not
+     *         be created.
+     */
+    private TokenInfo createToken(User user, Map<String, ?> attributes) throws RepositoryException {
+        String error = "Failed to create login token. ";
+        NodeImpl tokenParent = getTokenParent(user);
+        if (tokenParent != null) {
+            try {
+                ValueFactory vf = session.getValueFactory();
+                long creationTime = new Date().getTime();
+                Calendar creation = GregorianCalendar.getInstance();
+                creation.setTimeInMillis(creationTime);
+
+                Name tokenName = session.getQName(Text.replace(ISO8601.format(creation), ":", "."));
+                NodeImpl tokenNode = super.addNode(tokenParent, tokenName, session.getQName(TOKEN_NT_NAME), NodeId.randomId());
+
+                String key = generateKey(8);
+                String token = new StringBuilder(tokenNode.getId().toString()).append(DELIM).append(key).toString();
+
+                String keyHash = PasswordUtility.buildPasswordHash(getKeyValue(key, user.getID()));
+                setProperty(tokenNode, session.getQName(TOKEN_ATTRIBUTE_KEY), vf.createValue(keyHash));
+                setProperty(tokenNode, session.getQName(TOKEN_ATTRIBUTE_EXPIRY), vf.createValue(createExpirationValue(creationTime)));
+
+                for (String name : attributes.keySet()) {
+                    if (!RESERVED_ATTRIBUTES.contains(name)) {
+                        String attr = attributes.get(name).toString();
+                        setProperty(tokenNode, session.getQName(name), vf.createValue(attr));
+                    }
+                }
+                session.save();
+                return new TokenInfoImpl(tokenNode, token, user.getID());
+            } catch (NoSuchAlgorithmException e) {
+                // error while generating login token
+                log.error(error, e.getMessage());
+            } catch (UnsupportedEncodingException e) {
+                // error while generating login token
+                log.error(error, e.getMessage());
+            } catch (AccessDeniedException e) {
+                log.warn(error, e.getMessage());
+            }
+        } else {
+            log.warn("Unable to get/create token store for user " + user.getID());
+        }
+        return null;
+    }
+
+    private Calendar createExpirationValue(long creationTime) {
+        Calendar cal = Calendar.getInstance();
+        cal.setTimeInMillis(createExpirationTime(creationTime, tokenExpiration));
+        return cal;
+    }
+
+    /**
+     * Retrieves the token information associated with the specified login
+     * token. If no accessible {@code Tree} exists for the given token or if
+     * the token is not associated with a valid user this method returns {@code null}.
+     *
+     * @param token A valid login token.
+     * @return The {@code TokenInfo} associated with the specified token or
+     *         {@code null} of the corresponding information does not exist or is not
+     *         associated with a valid user.
+     */
+    public TokenInfo getTokenInfo(String token) throws RepositoryException {
+        if (token == null) {
+            return null;
+        }
+        NodeImpl tokenNode = (NodeImpl) getTokenNode(token, session);
+        String userId = getUserId(tokenNode, userManager);
+        if (userId == null || !isValidTokenTree(tokenNode)) {
+            return null;
+        } else {
+            return new TokenInfoImpl(tokenNode, token, userId);
+        }
+    }
+
+    static Node getTokenNode(String token, Session session) throws RepositoryException {
+        int pos = token.indexOf(DELIM);
+        String id = (pos == -1) ? token : token.substring(0, pos);
+        return session.getNodeByIdentifier(id);
+    }
+
+    static String getUserId(NodeImpl tokenNode, UserManager userManager) throws RepositoryException {
+        if (tokenNode != null) {
+            final NodeImpl userNode = (NodeImpl) tokenNode.getParent().getParent();
+            final String principalName = userNode.getProperty(UserImpl.P_PRINCIPAL_NAME).getString();
+            if (userNode.isNodeType(UserImpl.NT_REP_USER)) {
+                Authorizable a = userManager.getAuthorizable(new ItemBasedPrincipal() {
+                    public String getPath() throws RepositoryException {
+                        return userNode.getPath();
+                    }
+
+                    public String getName() {
+                        return principalName;
+                    }
+                });
+                if (a != null && !a.isGroup() && !((User)a).isDisabled()) {
+                    return a.getID();
+                }
+            } else {
+                throw new RepositoryException("Failed to calculate userId from token credentials");
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Returns {@code true} if the specified {@code attributeName}
+     * starts with or equals {@link #TOKEN_ATTRIBUTE}.
+     *
+     * @param attributeName The attribute name.
+     * @return {@code true} if the specified {@code attributeName}
+     *         starts with or equals {@link #TOKEN_ATTRIBUTE}.
+     */
+    static boolean isMandatoryAttribute(String attributeName) {
+        return attributeName != null && attributeName.startsWith(TOKEN_ATTRIBUTE);
+    }
+
+    /**
+     * Returns {@code false} if the specified attribute name doesn't have
+     * a 'jcr' or 'rep' namespace prefix; {@code true} otherwise. This is
+     * a lazy evaluation in order to avoid testing the defining node type of
+     * the associated jcr property.
+     *
+     * @param attributeName The attribute name.
+     * @return {@code true} if the specified property name doesn't seem
+     *         to represent repository internal information.
+     */
+    static  boolean isInfoAttribute(String attributeName) {
+        String prefix = Text.getNamespacePrefix(attributeName);
+        return !RESERVED_PREFIXES.contains(prefix);
+    }
+
+    private static long createExpirationTime(long creationTime, long tokenExpiration) {
+        return creationTime + tokenExpiration;
+    }
+
+    private static long getExpirationTime(NodeImpl tokenNode, long defaultValue) throws RepositoryException {
+        if (tokenNode.hasProperty(TOKEN_ATTRIBUTE_EXPIRY)) {
+            return tokenNode.getProperty(TOKEN_ATTRIBUTE_EXPIRY).getLong();
+        } else {
+            return defaultValue;
+        }
+    }
+
+    private static String generateKey(int size) {
+        SecureRandom random = new SecureRandom();
+        byte key[] = new byte[size];
+        random.nextBytes(key);
+
+        StringBuilder res = new StringBuilder(key.length * 2);
+        for (byte b : key) {
+            res.append(Text.hexTable[(b >> 4) & 15]);
+            res.append(Text.hexTable[b & 15]);
+        }
+        return res.toString();
+    }
+
+    private static String getKeyValue(String key, String userId) {
+        return key + userId;
+    }
+
+    private static boolean isValidTokenTree(NodeImpl tokenNode) throws RepositoryException {
+        if (tokenNode == null) {
+            return false;
+        } else {
+            return TOKENS_NODE_NAME.equals(tokenNode.getParent().getName()) &&
+                    TOKEN_NT_NAME.equals(tokenNode.getPrimaryNodeType().getName());
+        }
+    }
+
+    private NodeImpl getTokenParent(User user) throws RepositoryException {
+        NodeImpl tokenParent = null;
+        String parentPath = null;
+        try {
+            if (user != null) {
+                Principal pr = user.getPrincipal();
+                if (pr instanceof ItemBasedPrincipal) {
+                    String userPath = ((ItemBasedPrincipal) pr).getPath();
+                    NodeImpl userNode = (NodeImpl) session.getNode(userPath);
+                    if (userNode.hasNode(TOKENS_NODE_NAME)) {
+                        tokenParent = (NodeImpl) userNode.getNode(TOKENS_NODE_NAME);
+                    } else {
+                        tokenParent = userNode.addNode(session.getQName(TOKENS_NODE_NAME), TOKENS_NT_NAME, NodeId.randomId());
+                        parentPath = userPath + '/' + TOKENS_NODE_NAME;
+                        session.save();
+                    }
+                }
+            } else {
+                log.debug("Cannot create login token: No corresponding node for User " + user.getID() + '.');
+            }
+        } catch (RepositoryException e) {
+            // conflict while creating token store for this user -> refresh and
+            // try to get the tree from the updated root.
+            log.debug("Conflict while creating token store -> retrying", e.getMessage());
+            session.refresh(false);
+            if (parentPath != null && session.nodeExists(parentPath)) {
+                tokenParent = (NodeImpl) session.getNode(parentPath);
+            }
+        }
+        return tokenParent;
+    }
+
+    private class TokenInfoImpl implements TokenInfo {
+
+        private final String token;
+        private final String tokenPath;
+        private final String userId;
+
+        private final long expirationTime;
+        private final String key;
+
+        private final Map<String, String> mandatoryAttributes;
+        private final Map<String, String> publicAttributes;
+
+
+        private TokenInfoImpl(NodeImpl tokenNode, String token, String userId) throws RepositoryException {
+            this.token = token;
+            this.tokenPath = tokenNode.getPath();
+            this.userId = userId;
+
+            expirationTime = getExpirationTime(tokenNode, Long.MIN_VALUE);
+            key = tokenNode.getProperty(TOKEN_ATTRIBUTE_KEY).getString();
+
+            mandatoryAttributes = new HashMap<String, String>();
+            publicAttributes = new HashMap<String, String>();
+            PropertyIterator pit = tokenNode.getProperties();
+            while (pit.hasNext()) {
+                Property property = pit.nextProperty();
+                String name = property.getName();
+                String value = property.getString();
+                if (RESERVED_ATTRIBUTES.contains(name)) {
+                    continue;
+                }
+                if (isMandatoryAttribute(name)) {
+                    mandatoryAttributes.put(name, value);
+                } else if (isInfoAttribute(name)) {
+                    // info attribute
+                    publicAttributes.put(name, value);
+                } // else: jcr specific property
+            }
+        }
+
+        public String getToken() {
+            return token;
+        }
+
+        public boolean isExpired(long loginTime) {
+            return expirationTime < loginTime;
+        }
+
+        public boolean resetExpiration(long loginTime) throws RepositoryException {
+            if (isExpired(loginTime)) {
+                log.debug("Attempt to reset an expired token.");
+                return false;
+            }
+
+            Session s = null;
+            try {
+                if (expirationTime - loginTime <= tokenExpiration / 2) {
+                    s = session.createSession(session.getWorkspace().getName());
+                    s.getNode(tokenPath).setProperty(TOKEN_ATTRIBUTE_EXPIRY, createExpirationValue(loginTime));
+                    s.save();
+                    log.debug("Successfully reset token expiration time.");
+                    return true;
+                }
+            } catch (RepositoryException e) {
+                log.warn("Error while resetting token expiration", e.getMessage());
+            } finally {
+                if (s != null) {
+                    s.logout();
+                }
+            }
+            return false;
+        }
+
+        public boolean matches(TokenCredentials tokenCredentials) {
+            String tk = tokenCredentials.getToken();
+            int pos = tk.lastIndexOf(DELIM);
+            if (pos > -1) {
+                tk = tk.substring(pos + 1);
+            }
+            if (key == null || !PasswordUtility.isSame(key, getKeyValue(tk, userId))) {
+                return false;
+            }
+
+            for (String name : mandatoryAttributes.keySet()) {
+                String expectedValue = mandatoryAttributes.get(name);
+                if (!expectedValue.equals(tokenCredentials.getAttribute(name))) {
+                    return false;
+                }
+            }
+
+            // update set of informative attributes on the credentials
+            // based on the properties present on the token node.
+            Collection<String> attrNames = Arrays.asList(tokenCredentials.getAttributeNames());
+            for (String name : publicAttributes.keySet()) {
+                if (!attrNames.contains(name)) {
+                    tokenCredentials.setAttribute(name, publicAttributes.get(name).toString());
+
+                }
+            }
+            return true;
+        }
+
+        public boolean remove() {
+            Session s = null;
+            try {
+                s = session.createSession(session.getWorkspace().getName());
+                Node node = s.getNode(tokenPath);
+                node.remove();
+                s.save();
+                return true;
+            } catch (RepositoryException e) {
+                log.warn("Internal error while removing token node.", e);
+            } finally {
+                if (s != null) {
+                    s.logout();
+                }
+            }
+            return false;
+        }
+
+        public TokenCredentials getCredentials() {
+            TokenCredentials tc = new TokenCredentials(token);
+            for (String name : mandatoryAttributes.keySet()) {
+                tc.setAttribute(name, mandatoryAttributes.get(name));
+            }
+            for (String name : publicAttributes.keySet()) {
+                tc.setAttribute(name, publicAttributes.get(name));
+            }
+            return tc;
+        }
+
+    }
+}
\ No newline at end of file

Modified: jackrabbit/trunk/jackrabbit-core/src/main/resources/org/apache/jackrabbit/core/nodetype/builtin_nodetypes.cnd
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/resources/org/apache/jackrabbit/core/nodetype/builtin_nodetypes.cnd?rev=1537027&r1=1537026&r2=1537027&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/resources/org/apache/jackrabbit/core/nodetype/builtin_nodetypes.cnd (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/resources/org/apache/jackrabbit/core/nodetype/builtin_nodetypes.cnd Wed Oct 30 09:59:28 2013
@@ -627,3 +627,12 @@
   mixin
   - rep:hold (UNDEFINED) protected  multiple IGNORE
   - rep:retentionPolicy (UNDEFINED) protected IGNORE
+
+// -----------------------------------------------------------------------------
+// Token Management (backported from oak)
+// -----------------------------------------------------------------------------
+[rep:Token] > mix:referenceable
+  - rep:token.key (STRING) protected mandatory
+  - rep:token.exp (DATE) protected mandatory
+  - * (UNDEFINED) protected
+  - * (UNDEFINED) multiple protected

Modified: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authentication/token/TestAll.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authentication/token/TestAll.java?rev=1537027&r1=1537026&r2=1537027&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authentication/token/TestAll.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authentication/token/TestAll.java Wed Oct 30 09:59:28 2013
@@ -32,6 +32,7 @@ public class TestAll extends TestCase {
     public static Test suite() {
         TestSuite suite = new TestSuite("org.apache.jackrabbit.core.security.authentication.token tests");
 
+        suite.addTestSuite(TokenBasedAuthenticationCompatTest.class);
         suite.addTestSuite(TokenBasedAuthenticationTest.class);
         suite.addTestSuite(TokenBasedLoginTest.class);
 

Copied: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authentication/token/TokenBasedAuthenticationCompatTest.java (from r1535075, jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authentication/token/TokenBasedAuthenticationTest.java)
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authentication/token/TokenBasedAuthenticationCompatTest.java?p2=jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authentication/token/TokenBasedAuthenticationCompatTest.java&p1=jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authentication/token/TokenBasedAuthenticationTest.java&r1=1535075&r2=1537027&rev=1537027&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authentication/token/TokenBasedAuthenticationTest.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authentication/token/TokenBasedAuthenticationCompatTest.java Wed Oct 30 09:59:28 2013
@@ -16,9 +16,9 @@
  */
 package org.apache.jackrabbit.core.security.authentication.token;
 
-import org.apache.jackrabbit.api.security.authentication.token.TokenCredentials;
-import org.apache.jackrabbit.test.AbstractJCRTest;
-
+import java.util.Calendar;
+import java.util.Date;
+import java.util.UUID;
 import javax.jcr.Credentials;
 import javax.jcr.ItemNotFoundException;
 import javax.jcr.Node;
@@ -27,15 +27,22 @@ import javax.jcr.SimpleCredentials;
 import javax.jcr.lock.LockException;
 import javax.jcr.nodetype.ConstraintViolationException;
 import javax.jcr.version.VersionException;
-import java.util.Calendar;
-import java.util.Date;
+
+import org.apache.jackrabbit.api.security.authentication.token.TokenCredentials;
+import org.apache.jackrabbit.api.security.user.User;
+import org.apache.jackrabbit.core.SessionImpl;
+import org.apache.jackrabbit.test.AbstractJCRTest;
 
 /**
- * <code>TokenBasedAuthenticationTest</code>...
+ * <code>TokenBasedAuthenticationCompatTest</code>...
  */
-public class TokenBasedAuthenticationTest extends AbstractJCRTest {
+public class TokenBasedAuthenticationCompatTest extends AbstractJCRTest {
+
+    SessionImpl adminSession;
+    User testUser;
 
     Node tokenNode;
+    String token;
 
     TokenBasedAuthentication nullTokenAuth;
     TokenBasedAuthentication validTokenAuth;
@@ -48,24 +55,39 @@ public class TokenBasedAuthenticationTes
     protected void setUp() throws Exception {
         super.setUp();
 
-        tokenNode = testRootNode.addNode(nodeName1, "nt:unstructured");
-        tokenNode.setProperty(TokenBasedAuthentication.TOKEN_ATTRIBUTE +".exp", new Date().getTime()+TokenBasedAuthentication.TOKEN_EXPIRATION);
-        superuser.save();
+        System.setProperty(TokenBasedAuthentication.PARAM_COMPAT, Boolean.TRUE.toString());
+
+        adminSession = (SessionImpl) getHelper().getSuperuserSession("security");
+        testUser = adminSession.getUserManager().createUser(UUID.randomUUID().toString(), "pw");
+        adminSession.save();
+
+        SimpleCredentials sc = new SimpleCredentials(testUser.getID(), "pw".toCharArray());
+        sc.setAttribute(TokenBasedAuthentication.TOKEN_ATTRIBUTE, "");
+
+        CompatTokenProvider tp = new CompatTokenProvider(adminSession, TokenBasedAuthentication.TOKEN_EXPIRATION);
+        TokenInfo ti = tp.createToken(testUser, sc);
+        tokenNode = CompatTokenProvider.getTokenNode(ti.getToken(), adminSession);
 
-        String token = tokenNode.getIdentifier();
+        token = ti.getToken();
 
-        nullTokenAuth = new TokenBasedAuthentication(null, -1, superuser);
-        validTokenAuth = new TokenBasedAuthentication(token, 7200, superuser);
+        nullTokenAuth = new TokenBasedAuthentication(null, -1, adminSession);
+        validTokenAuth = new TokenBasedAuthentication(token, 7200, adminSession);
 
         tokenCreds = new TokenCredentials(token);
     }
 
+    @Override
+    protected void tearDown() throws Exception {
+        System.setProperty(TokenBasedAuthentication.PARAM_COMPAT, Boolean.FALSE.toString());
+        super.tearDown();
+    }
+
     private TokenBasedAuthentication expiredToken() throws RepositoryException, LockException, ConstraintViolationException, VersionException {
         Calendar cal = Calendar.getInstance();
-        cal.setTimeInMillis(new Date().getTime()-100);
+        cal.setTimeInMillis(new Date().getTime() - 100);
         tokenNode.setProperty(".token.exp", cal);
-        superuser.save();
-        return new TokenBasedAuthentication(tokenNode.getIdentifier(), TokenBasedAuthentication.TOKEN_EXPIRATION, superuser);
+        adminSession.save();
+        return new TokenBasedAuthentication(token, TokenBasedAuthentication.TOKEN_EXPIRATION, adminSession);
     }
 
     public void testCanHandle() throws RepositoryException {
@@ -96,7 +118,7 @@ public class TokenBasedAuthenticationTes
         assertFalse(expiredToken.authenticate(tokenCreds));
 
         try {
-            superuser.getNodeByIdentifier(identifier);
+            adminSession.getNodeByIdentifier(identifier);
             fail("expired token node should be removed.");
         } catch (ItemNotFoundException e) {
             // success
@@ -121,12 +143,12 @@ public class TokenBasedAuthenticationTes
 
     public void testAttributes() throws RepositoryException {
         tokenNode.setProperty(TokenBasedAuthentication.TOKEN_ATTRIBUTE +".any", "correct");
-        superuser.save();
-        TokenBasedAuthentication auth = new TokenBasedAuthentication(tokenNode.getIdentifier(), TokenBasedAuthentication.TOKEN_EXPIRATION, superuser);
+        adminSession.save();
+        TokenBasedAuthentication auth = new TokenBasedAuthentication(tokenNode.getIdentifier(), TokenBasedAuthentication.TOKEN_EXPIRATION, adminSession);
 
         assertFalse(auth.authenticate(tokenCreds));
 
-        tokenCreds.setAttribute(TokenBasedAuthentication.TOKEN_ATTRIBUTE +".any", "wrong");
+        tokenCreds.setAttribute(TokenBasedAuthentication.TOKEN_ATTRIBUTE + ".any", "wrong");
         assertFalse(auth.authenticate(tokenCreds));
 
         tokenCreds.setAttribute(TokenBasedAuthentication.TOKEN_ATTRIBUTE +".any", "correct");
@@ -134,8 +156,8 @@ public class TokenBasedAuthenticationTes
 
         // add informative property
         tokenNode.setProperty("noMatchRequired", "abc");
-        superuser.save();
-        auth = new TokenBasedAuthentication(tokenNode.getIdentifier(), TokenBasedAuthentication.TOKEN_EXPIRATION, superuser);
+        adminSession.save();
+        auth = new TokenBasedAuthentication(tokenNode.getIdentifier(), TokenBasedAuthentication.TOKEN_EXPIRATION, adminSession);
 
         assertTrue(auth.authenticate(tokenCreds));
     }
@@ -143,25 +165,25 @@ public class TokenBasedAuthenticationTes
     public void testUpdateAttributes() throws RepositoryException {
         tokenNode.setProperty(TokenBasedAuthentication.TOKEN_ATTRIBUTE +".any", "correct");
         tokenNode.setProperty("informative","value");
-        superuser.save();
+        adminSession.save();
 
         // token credentials must be updated to contain the additional attribute
         // present on the token node.
-        TokenBasedAuthentication auth = new TokenBasedAuthentication(tokenNode.getIdentifier(), TokenBasedAuthentication.TOKEN_EXPIRATION, superuser);
-        tokenCreds.setAttribute(TokenBasedAuthentication.TOKEN_ATTRIBUTE +".any", "correct");
+        TokenBasedAuthentication auth = new TokenBasedAuthentication(tokenNode.getIdentifier(), TokenBasedAuthentication.TOKEN_EXPIRATION, adminSession);
+        tokenCreds.setAttribute(TokenBasedAuthentication.TOKEN_ATTRIBUTE + ".any", "correct");
         assertTrue(auth.authenticate(tokenCreds));               
         assertEquals("value", tokenCreds.getAttribute("informative"));
 
         // additional informative property present on credentials upon subsequent
         // authentication -> the node must not be updated
-        auth = new TokenBasedAuthentication(tokenNode.getIdentifier(), TokenBasedAuthentication.TOKEN_EXPIRATION, superuser);
+        auth = new TokenBasedAuthentication(tokenNode.getIdentifier(), TokenBasedAuthentication.TOKEN_EXPIRATION, adminSession);
         tokenCreds.setAttribute("informative2", "value2");
         assertTrue(auth.authenticate(tokenCreds));
         assertFalse(tokenNode.hasProperty("informative2"));
 
         // modified informative property present on credentials upon subsequent
         // authentication -> the node must not be updated
-        auth = new TokenBasedAuthentication(tokenNode.getIdentifier(), TokenBasedAuthentication.TOKEN_EXPIRATION, superuser);
+        auth = new TokenBasedAuthentication(tokenNode.getIdentifier(), TokenBasedAuthentication.TOKEN_EXPIRATION, adminSession);
         tokenCreds.setAttribute("informative", "otherValue");
         assertTrue(auth.authenticate(tokenCreds));
         assertTrue(tokenNode.hasProperty("informative"));
@@ -169,7 +191,7 @@ public class TokenBasedAuthenticationTes
 
         // additional mandatory property on the credentials upon subsequent
         // authentication -> must be ignored
-        auth = new TokenBasedAuthentication(tokenNode.getIdentifier(), TokenBasedAuthentication.TOKEN_EXPIRATION, superuser);        
+        auth = new TokenBasedAuthentication(tokenNode.getIdentifier(), TokenBasedAuthentication.TOKEN_EXPIRATION, adminSession);
         tokenCreds.setAttribute(TokenBasedAuthentication.TOKEN_ATTRIBUTE +".toIgnore", "ignore");
         assertTrue(auth.authenticate(tokenCreds));
         assertFalse(tokenNode.hasProperty(TokenBasedAuthentication.TOKEN_ATTRIBUTE +".toIgnore"));

Propchange: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authentication/token/TokenBasedAuthenticationCompatTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authentication/token/TokenBasedAuthenticationCompatTest.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision Rev URL



Mime
View raw message