You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by an...@apache.org on 2010/12/21 16:15:21 UTC
svn commit: r1051522 - 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/
test/java/org/apach...
Author: angela
Date: Tue Dec 21 15:15:20 2010
New Revision: 1051522
URL: http://svn.apache.org/viewvc?rev=1051522&view=rev
Log:
JCR-2851 : Authentication Mechanism Based on Login Token (work in progress)
Added:
jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authentication/token/
jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authentication/token/TokenBasedAuthentication.java (with props)
jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authentication/DefaultLoginModuleTest.java
- copied, changed from r1050064, jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authentication/LoginModuleTest.java
jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authentication/token/
jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authentication/token/TestAll.java (with props)
jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authentication/token/TokenBasedAuthenticationTest.java (with props)
jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authentication/token/TokenBasedLoginTest.java (with props)
Modified:
jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/RepositoryImpl.java
jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authentication/DefaultLoginModule.java
jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authentication/TestAll.java
Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/RepositoryImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/RepositoryImpl.java?rev=1051522&r1=1051521&r2=1051522&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/RepositoryImpl.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/RepositoryImpl.java Tue Dec 21 15:15:20 2010
@@ -57,6 +57,7 @@ import org.apache.commons.collections.ma
import org.apache.commons.io.IOUtils;
import org.apache.jackrabbit.api.JackrabbitRepository;
import org.apache.jackrabbit.api.management.RepositoryManager;
+import org.apache.jackrabbit.api.security.authentication.token.TokenCredentials;
import org.apache.jackrabbit.commons.AbstractRepository;
import org.apache.jackrabbit.core.cluster.ClusterContext;
import org.apache.jackrabbit.core.cluster.ClusterException;
@@ -1473,6 +1474,11 @@ public class RepositoryImpl extends Abst
for (String name : sc.getAttributeNames()) {
session.setAttribute(name, sc.getAttribute(name));
}
+ } else if (credentials instanceof TokenCredentials) {
+ TokenCredentials tc = (TokenCredentials) credentials;
+ for (String name : tc.getAttributeNames()) {
+ session.setAttribute(name, tc.getAttribute(name));
+ }
}
log.debug("User {} logged in to workspace {}",
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=1051522&r1=1051521&r2=1051522&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 Tue Dec 21 15:15:20 2010
@@ -16,14 +16,20 @@
*/
package org.apache.jackrabbit.core.security.authentication;
+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;
@@ -37,10 +43,16 @@ import java.util.Map;
* The <code>DefaultLoginModule</code> authenticates Credentials related to
* a {@link User} of the Repository<br>
* In any other case it is marked to be ignored.<p>
- * This Module can deal only with <code>SimpleCredentials</code> since it
- * uses by default the {@link SimpleCredentialsAuthentication}. Impersonation is
- * delegated to the <code>User</code>'s {@link User#getImpersonation()
- * Impersonation} object
+ * This Module can deal with the following credentials
+ * <ul>
+ * <li><code>SimpleCredentials</code> -> handled by {@link SimpleCredentialsAuthentication}.</li>
+ * <li><code>TokenCredentials</code> -> handled by {@link TokenBasedAuthentication}.</li>
+ * </ul>
+ * In both cases the login is successful if the system contains a non-disabled,
+ * valid user that matches the given credentials.
+ * <p/>
+ * Correspondingly impersonation is delegated to the <code>User</code>'s
+ * {@link User#getImpersonation() Impersonation} object.
*
* @see AbstractLoginModule
*/
@@ -48,10 +60,65 @@ public class DefaultLoginModule extends
private static final Logger log = LoggerFactory.getLogger(DefaultLoginModule.class);
+ /**
+ * Flag indicating if Token-based authentication is disabled by the
+ * LoginModule configuration.
+ */
+ private boolean disableTokenAuth;
+
+ /**
+ * The expiration time for login tokens as set by the LoginModule configuration.
+ */
+ private long tokenExpiration = TokenBasedAuthentication.TOKEN_EXPIRATION;
+
+ /**
+ * The user object retrieved during the authentication process.
+ */
protected User user;
+ private SessionImpl session;
private UserManager userManager;
/**
+ * The login token extracted from TokenCredentials or null in case of
+ * another credentials.
+ */
+ private String loginToken;
+
+ //--------------------------------------------------------< LoginModule >---
+ /**
+ * @see javax.security.auth.spi.LoginModule#commit()
+ */
+ @Override
+ public boolean commit() throws LoginException {
+ boolean success = super.commit();
+ if (success && !disableTokenAuth && TokenBasedAuthentication.doCreateToken(credentials)) {
+ Session s = null;
+ try {
+ /*
+ use a different session instance to create the token
+ node in order to prevent concurrent modifications with
+ the shared system session.
+ */
+ s = session.createSession(session.getWorkspace().getName());
+ Credentials tc = TokenBasedAuthentication.createToken(user, credentials, tokenExpiration, s);
+ if (tc != null) {
+ subject.getPublicCredentials().add(tc);
+ }
+ } catch (RepositoryException e) {
+ LoginException le = new LoginException("Failed to commit: " + e.getMessage());
+ le.initCause(e);
+ throw le;
+ } finally {
+ if (s != null) {
+ s.logout();
+ }
+ }
+ }
+ return success;
+ }
+
+ //------------------------------------------------< AbstractLoginModule >---
+ /**
* Retrieves the user manager from the specified session. If this fails
* this login modules initialization must fail.
*
@@ -63,7 +130,8 @@ public class DefaultLoginModule extends
throw new LoginException("Unable to initialize LoginModule: SessionImpl expected.");
}
try {
- userManager = ((SessionImpl) session).getUserManager();
+ this.session = (SessionImpl) session;
+ userManager = this.session.getUserManager();
log.debug("- UserManager -> '" + userManager.getClass().getName() + "'");
} catch (RepositoryException e) {
throw new LoginException("Unable to initialize LoginModule: " + e.getMessage());
@@ -103,10 +171,78 @@ public class DefaultLoginModule extends
}
/**
+ * @see AbstractLoginModule#supportsCredentials(javax.jcr.Credentials)
+ */
+ @Override
+ protected boolean supportsCredentials(Credentials creds) {
+ if (creds instanceof TokenCredentials) {
+ return !disableTokenAuth;
+ } else {
+ return super.supportsCredentials(creds);
+ }
+ }
+
+ /**
+ * @see AbstractLoginModule#getUserID(javax.jcr.Credentials)
+ */
+ @Override
+ protected String getUserID(Credentials credentials) {
+ // shortcut to avoid duplicate evaluation.
+ if (user != null) {
+ try {
+ return user.getID();
+ } catch (RepositoryException e) {
+ log.warn("Failed to retrieve userID from user", e);
+ // ignore and re-evaluate credentials.
+ }
+ }
+
+ // handle TokenCredentials
+ if (!disableTokenAuth && TokenBasedAuthentication.isTokenBasedLogin(credentials)) {
+ // special token based login
+ loginToken = ((TokenCredentials) credentials).getToken();
+ try {
+ Node n = session.getNodeByIdentifier(loginToken);
+ 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();
+ }
+ } catch (RepositoryException e) {
+ if (log.isDebugEnabled()) {
+ log.warn("Failed to retrieve UserID from token-based credentials", e);
+ } else {
+ log.warn("Failed to retrieve UserID from token-based credentials: {}", e.toString());
+ }
+ }
+ // failed to retrieve the user from loginToken.
+ return null;
+ } else {
+ // regular login -> extraction of userID is handled by the super class.
+ return super.getUserID(credentials);
+ }
+ }
+
+ /**
* @see AbstractLoginModule#getAuthentication(Principal, Credentials)
*/
@Override
protected Authentication getAuthentication(Principal principal, Credentials creds) throws RepositoryException {
+ if (!disableTokenAuth && loginToken != null) {
+ Authentication authentication = new TokenBasedAuthentication(loginToken, tokenExpiration, session);
+ if (authentication.canHandle(creds)) {
+ return authentication;
+ }
+ }
+
if (user != null) {
Authentication authentication = new SimpleCredentialsAuthentication(user);
if (authentication.canHandle(creds)) {
@@ -147,4 +283,42 @@ public class DefaultLoginModule extends
return false;
}
}
+
+ //--------------------------------------------------------------------------
+ // methods used for token based login
+ //--------------------------------------------------------------------------
+ /**
+ * Return a flag indicating if token based authentication is disabled.
+ *
+ * @return <code>true</code> if token based authentication is disabled;
+ * <code>false</code> otherwise.
+ */
+ public boolean isDisableTokenAuth() {
+ return disableTokenAuth;
+ }
+
+ /**
+ * Set a flag indicating if token based authentication is disabled.
+ *
+ * @param disableTokenAuth <code>true</code> to disable token based
+ * authentication; <code>false</code> otherwise
+ */
+ public void setDisableTokenAuth(boolean disableTokenAuth) {
+ this.disableTokenAuth = disableTokenAuth;
+ }
+
+ /**
+ * @return The configured expiration time for login tokens in milliseconds.
+ */
+ public long getTokenExpiration() {
+ return tokenExpiration;
+ }
+
+ /**
+ * @param tokenExpiration Sets the configured expiration time (in milliseconds)
+ * of login tokens.
+ */
+ public void setTokenExpiration(long tokenExpiration) {
+ this.tokenExpiration = tokenExpiration;
+ }
}
\ No newline at end of file
Added: 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=1051522&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authentication/token/TokenBasedAuthentication.java (added)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authentication/token/TokenBasedAuthentication.java Tue Dec 21 15:15:20 2010
@@ -0,0 +1,290 @@
+/*
+ * 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 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.SessionImpl;
+import org.apache.jackrabbit.core.security.authentication.Authentication;
+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.security.Principal;
+import java.util.Calendar;
+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.
+ * Authentication succeeds if the login token refers to a non-expired
+ * token node and if all other credential attributes are equal to the
+ * corresponding properties.
+ */
+public class TokenBasedAuthentication implements Authentication {
+
+ private static final Logger log = LoggerFactory.getLogger(TokenBasedAuthentication.class);
+
+ /**
+ * Default expiration time for login tokens is 2 hours.
+ */
+ public static final long TOKEN_EXPIRATION = 2 * 3600 * 1000;
+
+ /**
+ * The name of the login token attribute.
+ */
+ public static final String TOKEN_ATTRIBUTE = ".token";
+
+ private static final String TOKEN_ATTRIBUTE_EXPIRY = TOKEN_ATTRIBUTE + ".exp";
+ private static final String TOKENS_NODE_NAME = ".tokens";
+ private static final String TOKENS_NT_NAME = "nt:unstructured"; // TODO: configurable
+
+
+ private final String token;
+ private final long tokenExpiration;
+ private final Session session;
+
+ private final Map<String, String> attributes;
+ private final long expiry;
+
+ public TokenBasedAuthentication(String token, long tokenExpiration, Session session) throws RepositoryException {
+ this.session = session;
+ this.tokenExpiration = tokenExpiration;
+ this.token = token;
+ long expTime = Long.MAX_VALUE;
+ if (token != null) {
+ attributes = new HashMap<String, String>();
+ Node n = session.getNodeByIdentifier(token);
+ PropertyIterator it = n.getProperties();
+ while (it.hasNext()) {
+ Property p = it.nextProperty();
+ String name = p.getName();
+ if (!name.startsWith(TOKEN_ATTRIBUTE)) {
+ continue;
+ }
+ if (TOKEN_ATTRIBUTE_EXPIRY.equals(name)) {
+ expTime = p.getLong();
+ } else {
+ attributes.put(p.getName(), p.getString());
+ }
+ }
+ } else {
+ attributes = Collections.emptyMap();
+ }
+ expiry = expTime;
+ }
+
+ /**
+ * @see Authentication#canHandle(javax.jcr.Credentials)
+ */
+ public boolean canHandle(Credentials credentials) {
+ return token != null && isTokenBasedLogin(credentials);
+ }
+
+ /**
+ * @see Authentication#authenticate(javax.jcr.Credentials)
+ */
+ public boolean authenticate(Credentials credentials) throws RepositoryException {
+ if (!(credentials instanceof TokenCredentials)) {
+ throw new RepositoryException("TokenCredentials expected. Cannot handle " + credentials.getClass().getName());
+ }
+ TokenCredentials tokenCredentials = (TokenCredentials) credentials;
+ // 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;
+ }
+ // 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;
+ }
+ }
+
+ // token matches and none of the additional constraints was
+ // violated -> authentication succeeded.
+ resetExpiry(expiry, loginTime);
+ return true;
+ }
+
+ // wrong credentials that cannot be compared by this authentication
+ return false;
+ }
+
+ /**
+ * Reset the expiration if half of the expiration has passed in order to
+ * minimize write operations (avoid resetting upon each login).
+ *
+ * @param tokenExpiry
+ * @param loginTime
+ */
+ private void resetExpiry(long tokenExpiry, long loginTime) {
+ if (tokenExpiry - loginTime <= tokenExpiration/2) {
+
+ long expirationTime = loginTime + tokenExpiration;
+ Calendar cal = GregorianCalendar.getInstance();
+ cal.setTimeInMillis(expirationTime);
+
+ Session s = null;
+ try {
+ // use another session to reset the expiration time in order
+ // to avoid concurrent write operations with the shared systemsession.
+ s = ((SessionImpl) session).createSession(session.getWorkspace().getName());
+
+ Node tokenNode = s.getNodeByIdentifier(token);
+ tokenNode.setProperty(TOKEN_ATTRIBUTE_EXPIRY, s.getValueFactory().createValue(cal));
+ s.save();
+ } catch (RepositoryException e) {
+ log.warn("Internal error while resetting expiry of the login token.", 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 {
+ // use another session to remove the token node
+ // to avoid concurrent write operations with the shared systemsession.
+ s = ((SessionImpl) session).createSession(session.getWorkspace().getName());
+
+ Node tokenNode = s.getNodeByIdentifier(token);
+ tokenNode.remove();
+ s.save();
+ } catch (RepositoryException e) {
+ log.warn("Internal error while resetting expiry of the login token.", e);
+ } finally {
+ if (s != null) {
+ s.logout();
+ }
+ }
+ }
+
+ //--------------------------------------------------------------------------
+ /**
+ *
+ * @param credentials
+ * @return
+ */
+ public static boolean isTokenBasedLogin(Credentials credentials) {
+ return credentials instanceof TokenCredentials;
+ }
+
+ /**
+ *
+ * @param credentials
+ * @return
+ */
+ public static boolean doCreateToken(Credentials credentials) {
+ if (credentials instanceof SimpleCredentials) {
+ Object attr = ((SimpleCredentials) credentials).getAttribute(TOKEN_ATTRIBUTE);
+ return (attr != null && "".equals(attr.toString()));
+ }
+ return false;
+ }
+
+ /**
+ * Create a new token node for the specified user.
+ *
+ * @param user
+ * @param credentials
+ * @param tokenExpiration
+ * @param session
+ * @return A new instance of <code>TokenCredentials</code> to be used for
+ * further login actions against this Authentication implementation.
+ * @throws RepositoryException If there is no node corresponding to the
+ * specified user in the current workspace or if an error occurs while
+ * creating the token node.
+ */
+ public synchronized static Credentials createToken(User user, SimpleCredentials credentials,
+ 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)) {
+ tokenParent = userNode.getNode(TOKENS_NODE_NAME);
+ } else {
+ tokenParent = userNode.addNode(TOKENS_NODE_NAME, TOKENS_NT_NAME);
+ }
+
+ long creationTime = new Date().getTime();
+ long expirationTime = creationTime + tokenExpiration;
+
+ Calendar cal = GregorianCalendar.getInstance();
+ cal.setTimeInMillis(creationTime);
+
+ String tokenName = Text.replace(ISO8601.format(cal), ":", ".");
+ Node tokenNode = tokenParent.addNode(tokenName);
+
+ tokenCredentials = new TokenCredentials(tokenNode.getIdentifier());
+
+ // 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;
+ } else {
+ throw new RepositoryException("Cannot create login token: No corresponding node for User " + user.getID() +" in workspace '" + workspaceName + "'.");
+ }
+ }
+}
\ No newline at end of file
Propchange: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authentication/token/TokenBasedAuthentication.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authentication/token/TokenBasedAuthentication.java
------------------------------------------------------------------------------
svn:keywords = Author Date Id Revision Rev URL
Copied: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authentication/DefaultLoginModuleTest.java (from r1050064, jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authentication/LoginModuleTest.java)
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authentication/DefaultLoginModuleTest.java?p2=jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authentication/DefaultLoginModuleTest.java&p1=jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authentication/LoginModuleTest.java&r1=1050064&r2=1051522&rev=1051522&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authentication/LoginModuleTest.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authentication/DefaultLoginModuleTest.java Tue Dec 21 15:15:20 2010
@@ -16,10 +16,23 @@
*/
package org.apache.jackrabbit.core.security.authentication;
-import java.security.Principal;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
+import org.apache.jackrabbit.api.security.authentication.token.TokenCredentials;
+import org.apache.jackrabbit.core.RepositoryImpl;
+import org.apache.jackrabbit.core.config.ConfigurationEntityResolver;
+import org.apache.jackrabbit.core.config.ConfigurationErrorHandler;
+import org.apache.jackrabbit.core.config.ConfigurationException;
+import org.apache.jackrabbit.core.config.LoginModuleConfig;
+import org.apache.jackrabbit.core.config.RepositoryConfig;
+import org.apache.jackrabbit.core.config.RepositoryConfigurationParser;
+import org.apache.jackrabbit.core.security.authentication.token.TokenBasedAuthentication;
+import org.apache.jackrabbit.core.security.principal.FallbackPrincipalProvider;
+import org.apache.jackrabbit.core.security.principal.ProviderRegistryImpl;
+import org.apache.jackrabbit.test.AbstractJCRTest;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
import javax.jcr.Credentials;
import javax.jcr.RepositoryException;
@@ -27,119 +40,127 @@ import javax.jcr.Session;
import javax.jcr.SimpleCredentials;
import javax.security.auth.Subject;
import javax.security.auth.callback.CallbackHandler;
-import javax.security.auth.login.AppConfigurationEntry;
-import javax.security.auth.login.Configuration;
-import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;
-
-import org.apache.jackrabbit.core.security.TestPrincipal;
-import org.apache.jackrabbit.core.security.principal.FallbackPrincipalProvider;
-import org.apache.jackrabbit.core.security.principal.ProviderRegistryImpl;
-import org.apache.jackrabbit.test.AbstractJCRTest;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import java.io.IOException;
+import java.io.StringReader;
+import java.util.Properties;
/**
- * <code>LoginModuleTest</code> checks if multiple login modules are properly
- * handled. More specifically, this test case sets up a configuration with
- * two login modules:
- * <ul>
- * <li>module 1: required. This module will always authenticate successfully</li>
- * <li>module 2: sufficient. This module will always indicate that it should be ignored.</li>
- * </ul>
- * See also JCR-2671.
+ * <code>DefaultLoginModuleTest</code>...
*/
-public class LoginModuleTest extends AbstractJCRTest {
-
- private static final String APP_NAME = LoginModuleTest.class.getName();
-
- public void testMultipleModules() throws Exception {
-
- CallbackHandler ch = new CallbackHandlerImpl(new SimpleCredentials("user", "pass".toCharArray()),
- superuser, new ProviderRegistryImpl(new FallbackPrincipalProvider()),
- "admin", "anonymous");
- LoginContext context = new LoginContext(
- APP_NAME, new Subject(), ch, new TestConfiguration());
- context.login();
- assertFalse("no principal set", context.getSubject().getPrincipals().isEmpty());
- }
-
- static class TestConfiguration extends Configuration {
-
- @Override
- public void refresh() {
- }
+public class DefaultLoginModuleTest extends AbstractJCRTest {
- @Override
- public AppConfigurationEntry[] getAppConfigurationEntry(String name) {
- return new AppConfigurationEntry[] {
- new TestAppConfigurationEntry(AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, false),
- new TestAppConfigurationEntry(AppConfigurationEntry.LoginModuleControlFlag.SUFFICIENT, true)
- };
+ private static final String DEFAULT_CONFIG =
+ "<Security appName=\"Jackrabbit\">" +
+ "<LoginModule class=\"org.apache.jackrabbit.core.security.authentication.DefaultLoginModule\">\n" +
+ " <param name=\"anonymousId\" value=\"anonymous\"/>\n" +
+ " <param name=\"adminId\" value=\"admin\"/>\n" +
+ "</LoginModule>" +
+ "</Security>";
+
+ private static final String DISABLE_TOKEN_CONFIG =
+ "<Security appName=\"Jackrabbit\">" +
+ "<LoginModule class=\"org.apache.jackrabbit.core.security.authentication.DefaultLoginModule\">\n" +
+ " <param name=\"anonymousId\" value=\"anonymous\"/>\n" +
+ " <param name=\"adminId\" value=\"admin\"/>\n" +
+ " <param name=\"disableTokenAuth\" value=\"true\"/>\n" +
+ "</LoginModule>" +
+ "</Security>";
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ RepositoryConfig rc = ((RepositoryImpl) superuser.getRepository()).getConfig();
+ String workspaceName = rc.getSecurityConfig().getSecurityManagerConfig().getWorkspaceName();
+ if (workspaceName == null) {
+ workspaceName = rc.getDefaultWorkspaceName();
}
+ securitySession = getHelper().getSuperuserSession(workspaceName);
}
- static class TestAppConfigurationEntry extends AppConfigurationEntry {
-
- private static final Map<String, Object> IGNORE = new HashMap<String, Object>();
-
- private static final Map<String, Object> EMPTY = Collections.emptyMap();
-
- static {
- IGNORE.put("ignore", "true");
- }
-
- public TestAppConfigurationEntry(LoginModuleControlFlag controlFlag,
- boolean ignore) {
- super(TestLoginModule.class.getName(), controlFlag, ignore ? IGNORE : EMPTY);
+ @Override
+ protected void cleanUp() throws Exception {
+ if (securitySession != null && securitySession.isLive()) {
+ securitySession.logout();
}
+ super.cleanUp();
}
- public static class TestLoginModule extends AbstractLoginModule {
+ private SimpleCredentials simpleCredentials = new SimpleCredentials("admin", "admin".toCharArray());
+ private Session securitySession;
- private boolean ignore = false;
+ public void testSimpleCredentialsLogin() throws Exception {
+ AuthContext ac = getAuthContext(simpleCredentials, DEFAULT_CONFIG);
+ ac.login();
+ ac.logout();
+ }
- @Override
- protected void doInit(CallbackHandler callbackHandler,
- Session session,
- Map options) throws LoginException {
- if (options.containsKey("ignore")) {
- ignore = true;
+ public void testTokenCredentials() throws Exception {
+ simpleCredentials.setAttribute(TokenBasedAuthentication.TOKEN_ATTRIBUTE, "");
+ try {
+ AuthContext ac = getAuthContext(simpleCredentials, DEFAULT_CONFIG);
+ ac.login();
+ Subject subj = ac.getSubject();
+ assertFalse(subj.getPublicCredentials(SimpleCredentials.class).isEmpty());
+ assertFalse(subj.getPublicCredentials(TokenCredentials.class).isEmpty());
+
+ TokenCredentials tokenCredentials = subj.getPublicCredentials(TokenCredentials.class).iterator().next();
+ ac.logout();
+
+ // test login with token credentials
+ ac = getAuthContext(tokenCredentials, DEFAULT_CONFIG);
+ ac.login();
+ ac.logout();
+
+ // test login with token credentials if token-auth is disabled.
+ try {
+ ac = getAuthContext(tokenCredentials, DISABLE_TOKEN_CONFIG);
+ ac.login();
+ ac.logout();
+ fail();
+ } catch (LoginException e) {
+ // success
}
- }
- @Override
- protected boolean impersonate(Principal principal,
- Credentials credentials)
- throws RepositoryException, LoginException {
- return false;
+ } finally {
+ simpleCredentials.removeAttribute(TokenBasedAuthentication.TOKEN_ATTRIBUTE);
}
+ }
- @Override
- protected Authentication getAuthentication(Principal principal,
- Credentials creds)
- throws RepositoryException {
- if (ignore) {
- return null;
- } else {
- return new Authentication() {
- public boolean canHandle(Credentials credentials) {
- return true;
- }
-
- public boolean authenticate(Credentials credentials)
- throws RepositoryException {
- return true;
- }
- };
- }
- }
+ private AuthContext getAuthContext(Credentials creds, String config) throws RepositoryException {
+ CallbackHandler ch = new CallbackHandlerImpl(creds,
+ securitySession, new ProviderRegistryImpl(new FallbackPrincipalProvider()),
+ "admin", "anonymous");
+ return new LocalAuthContext(getLoginModuleConfig(config), ch, null);
+ }
- @Override
- protected Principal getPrincipal(Credentials credentials) {
- if (ignore) {
- return null;
- } else {
- return new TestPrincipal(((SimpleCredentials) credentials).getUserID());
+ private static LoginModuleConfig getLoginModuleConfig(String config) throws ConfigurationException {
+ return new RepositoryConfigurationParser(new Properties()).parseLoginModuleConfig(parseXML(new InputSource(new StringReader(config)), false));
+ }
+
+ private static Element parseXML(InputSource xml, boolean validate) throws ConfigurationException {
+ try {
+ DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+ factory.setValidating(validate);
+ DocumentBuilder builder = factory.newDocumentBuilder();
+ if (validate) {
+ builder.setErrorHandler(new ConfigurationErrorHandler());
}
+ builder.setEntityResolver(ConfigurationEntityResolver.INSTANCE);
+ Document document = builder.parse(xml);
+ return document.getDocumentElement();
+ } catch (ParserConfigurationException e) {
+ throw new ConfigurationException("Unable to create configuration XML parser", e);
+ } catch (SAXParseException e) {
+ throw new ConfigurationException("Configuration file syntax error. (Line: " + e.getLineNumber() + " Column: " + e.getColumnNumber() + ")", e);
+ } catch (SAXException e) {
+ throw new ConfigurationException("Configuration file syntax error. ", e);
+ } catch (IOException e) {
+ throw new ConfigurationException("Configuration file could not be read.", e);
}
}
-}
+}
\ No newline at end of file
Modified: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authentication/TestAll.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authentication/TestAll.java?rev=1051522&r1=1051521&r2=1051522&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authentication/TestAll.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authentication/TestAll.java Tue Dec 21 15:15:20 2010
@@ -31,6 +31,7 @@ public class TestAll extends TestCase {
suite.addTestSuite(SimpleCredentialsAuthenticationTest.class);
suite.addTestSuite(CryptedSimpleCredentialsTest.class);
suite.addTestSuite(LoginModuleTest.class);
+ suite.addTestSuite(DefaultLoginModuleTest.class);
return suite;
}
Added: 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=1051522&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authentication/token/TestAll.java (added)
+++ jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authentication/token/TestAll.java Tue Dec 21 15:15:20 2010
@@ -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 junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+/**
+ * Test suite that includes all test cases for package org.apache.jackrabbit.core.security.authentication.token.
+ */
+public class TestAll extends TestCase {
+
+ /**
+ * Returns a <code>Test</code> suite that executes all tests inside this
+ * package.
+ */
+ public static Test suite() {
+ TestSuite suite = new TestSuite("org.apache.jackrabbit.core.security.authentication.token tests");
+
+ suite.addTestSuite(TokenBasedAuthenticationTest.class);
+ suite.addTestSuite(TokenBasedLoginTest.class);
+
+ return suite;
+ }
+}
Propchange: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authentication/token/TestAll.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authentication/token/TestAll.java
------------------------------------------------------------------------------
svn:keywords = Author Date Id Revision Rev URL
Added: 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/TokenBasedAuthenticationTest.java?rev=1051522&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authentication/token/TokenBasedAuthenticationTest.java (added)
+++ jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authentication/token/TokenBasedAuthenticationTest.java Tue Dec 21 15:15:20 2010
@@ -0,0 +1,142 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.core.security.authentication.token;
+
+import org.apache.jackrabbit.api.security.authentication.token.TokenCredentials;
+import org.apache.jackrabbit.test.AbstractJCRTest;
+
+import javax.jcr.Credentials;
+import javax.jcr.ItemNotFoundException;
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+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;
+
+/**
+ * <code>TokenBasedAuthenticationTest</code>...
+ */
+public class TokenBasedAuthenticationTest extends AbstractJCRTest {
+
+ Node tokenNode;
+
+ TokenBasedAuthentication nullTokenAuth;
+ TokenBasedAuthentication validTokenAuth;
+
+ TokenCredentials tokenCreds;
+ Credentials simpleCreds = new SimpleCredentials("uid", "pw".toCharArray());
+ Credentials creds = new Credentials() {};
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ tokenNode = testRootNode.addNode(nodeName1, "nt:unstructured");
+ tokenNode.setProperty(".token.exp", new Date().getTime()+TokenBasedAuthentication.TOKEN_EXPIRATION);
+ superuser.save();
+
+ String token = tokenNode.getIdentifier();
+
+ nullTokenAuth = new TokenBasedAuthentication(null, -1, superuser);
+ validTokenAuth = new TokenBasedAuthentication(token, 7200, superuser);
+
+ tokenCreds = new TokenCredentials(token);
+ }
+
+ private TokenBasedAuthentication expiredToken() throws RepositoryException, LockException, ConstraintViolationException, VersionException {
+ Calendar cal = Calendar.getInstance();
+ cal.setTimeInMillis(new Date().getTime()-100);
+ tokenNode.setProperty(".token.exp", cal);
+ superuser.save();
+ return new TokenBasedAuthentication(tokenNode.getIdentifier(), TokenBasedAuthentication.TOKEN_EXPIRATION, superuser);
+ }
+
+ public void testCanHandle() throws RepositoryException {
+ assertTrue(validTokenAuth.canHandle(tokenCreds));
+ assertFalse(nullTokenAuth.canHandle(tokenCreds));
+
+ assertFalse(validTokenAuth.canHandle(simpleCreds));
+ assertFalse(nullTokenAuth.canHandle(simpleCreds));
+
+ assertFalse(validTokenAuth.canHandle(creds));
+ assertFalse(nullTokenAuth.canHandle(creds));
+
+ TokenBasedAuthentication expiredToken = expiredToken();
+ assertTrue(expiredToken.canHandle(tokenCreds));
+ }
+
+ public void testExpiry() throws RepositoryException {
+ assertTrue(validTokenAuth.authenticate(tokenCreds));
+
+ TokenBasedAuthentication expiredToken = expiredToken();
+ assertFalse(expiredToken.authenticate(tokenCreds));
+ }
+
+ public void testRemoval() throws RepositoryException {
+ String identifier = tokenNode.getIdentifier();
+
+ TokenBasedAuthentication expiredToken = expiredToken();
+ assertFalse(expiredToken.authenticate(tokenCreds));
+
+ try {
+ superuser.getNodeByIdentifier(identifier);
+ fail("expired token node should be removed.");
+ } catch (ItemNotFoundException e) {
+ // success
+ }
+ }
+
+ public void testInvalidCredentials() throws RepositoryException {
+ try {
+ validTokenAuth.authenticate(creds);
+ fail("RepositoryException expected");
+ } catch (RepositoryException e) {
+ // success
+ }
+
+ try {
+ assertFalse(validTokenAuth.authenticate(simpleCreds));
+ fail("RepositoryException expected");
+ } catch (RepositoryException e) {
+ // success
+ }
+ }
+
+ public void testAttributes() throws RepositoryException {
+ tokenNode.setProperty(".token.any", "correct");
+ superuser.save();
+ TokenBasedAuthentication auth = new TokenBasedAuthentication(tokenNode.getIdentifier(), TokenBasedAuthentication.TOKEN_EXPIRATION, superuser);
+
+ assertFalse(auth.authenticate(tokenCreds));
+
+ tokenCreds.setAttribute(".token.any", "wrong");
+ assertFalse(auth.authenticate(tokenCreds));
+
+ tokenCreds.setAttribute(".token.any", "correct");
+ assertTrue(auth.authenticate(tokenCreds));
+
+ // add informative property
+ tokenNode.setProperty("noMatchRequired", "abc");
+ superuser.save();
+ auth = new TokenBasedAuthentication(tokenNode.getIdentifier(), TokenBasedAuthentication.TOKEN_EXPIRATION, superuser);
+
+ assertTrue(auth.authenticate(tokenCreds));
+ }
+}
\ No newline at end of file
Propchange: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authentication/token/TokenBasedAuthenticationTest.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authentication/token/TokenBasedAuthenticationTest.java
------------------------------------------------------------------------------
svn:keywords = Author Date Id Revision Rev URL
Added: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authentication/token/TokenBasedLoginTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authentication/token/TokenBasedLoginTest.java?rev=1051522&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authentication/token/TokenBasedLoginTest.java (added)
+++ jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authentication/token/TokenBasedLoginTest.java Tue Dec 21 15:15:20 2010
@@ -0,0 +1,356 @@
+/*
+ * 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 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.api.security.user.UserManager;
+import org.apache.jackrabbit.test.AbstractJCRTest;
+import org.apache.jackrabbit.test.NotExecutableException;
+
+import javax.jcr.Credentials;
+import javax.jcr.LoginException;
+import javax.jcr.Node;
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.SimpleCredentials;
+import java.security.Principal;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * <code>TokenBasedLoginTest</code>...
+ */
+public class TokenBasedLoginTest extends AbstractJCRTest {
+
+ private static final String TOKENS_NAME = ".tokens";
+ private static final String TOKEN_ATTRIBUTE = ".token";
+
+ private User testuser;
+ private String testuserPath;
+ private SimpleCredentials creds;
+
+ private boolean doSave;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ if (superuser instanceof JackrabbitSession) {
+ UserManager umgr = ((JackrabbitSession) superuser).getUserManager();
+ String uid = "test";
+ while (umgr.getAuthorizable(uid) != null) {
+ uid += "_";
+ }
+
+ testuser = umgr.createUser(uid, uid);
+ Principal p = testuser.getPrincipal();
+ if (p instanceof ItemBasedPrincipal) {
+ testuserPath = ((ItemBasedPrincipal) p).getPath();
+ if (!superuser.nodeExists(testuserPath)) {
+ throw new NotExecutableException();
+ }
+ } else {
+ throw new NotExecutableException();
+ }
+
+ creds = new SimpleCredentials(uid, uid.toCharArray());
+
+ if (!umgr.isAutoSave()) {
+ doSave = true;
+ superuser.save();
+ }
+ } else {
+ throw new NotExecutableException();
+ }
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ if (testuser != null) {
+ testuser.remove();
+ if (doSave) {
+ superuser.save();
+ }
+ }
+ super.tearDown();
+ }
+
+ public void testLogin() throws RepositoryException {
+ Repository repo = getHelper().getRepository();
+
+ // make sure regular simple login works.
+ Session s = repo.login(creds);
+ s.logout();
+
+ // test if token creation works.
+ creds.setAttribute(TOKEN_ATTRIBUTE, "");
+ // an additional attribute that must match
+ creds.setAttribute(TOKEN_ATTRIBUTE + ".any", "any");
+ // an attribute just for info purposes
+ creds.setAttribute("attr", "attr");
+
+ String token = null;
+
+ s = repo.login(creds);
+ try {
+ Node userNode = superuser.getNode(testuserPath);
+
+ assertTrue(userNode.hasNode(TOKENS_NAME));
+
+ Node tNode = userNode.getNode(TOKENS_NAME);
+ assertTrue(tNode.hasNodes());
+
+ Node ttNode = tNode.getNodes().nextNode();
+ assertTrue(ttNode.hasProperty("attr"));
+ assertEquals("attr", ttNode.getProperty("attr").getString());
+
+ assertTrue(ttNode.hasProperty(TOKEN_ATTRIBUTE + ".any"));
+ assertEquals("any", ttNode.getProperty(TOKEN_ATTRIBUTE + ".any").getString());
+
+ token = ttNode.getIdentifier();
+
+ } finally {
+ s.logout();
+ }
+
+ // login with token only must succeed as well.
+ TokenCredentials tokenOnly = new TokenCredentials(token);
+ tokenOnly.setAttribute(TOKEN_ATTRIBUTE + ".any", "any");
+
+ s = repo.login(tokenOnly);
+ try {
+ assertEquals(creds.getUserID(), s.getUserID());
+ } finally {
+ s.logout();
+ }
+
+ // the non-mandatory attribute may have any value if present with the creds.
+ tokenOnly.setAttribute("attr", "another");
+ s = repo.login(tokenOnly);
+ try {
+ assertEquals(creds.getUserID(), s.getUserID());
+ } finally {
+ s.logout();
+ tokenOnly.removeAttribute("attr");
+ }
+
+ // login with token but wrong mandatory attribute
+ tokenOnly.setAttribute(TOKEN_ATTRIBUTE + ".any", "another");
+ try {
+ s = repo.login(tokenOnly);
+ s.logout();
+ fail("The additional mandatory attr doesn't match. login must fail.");
+ } catch (LoginException e) {
+ // success
+ }
+
+ // login with token but missing the mandatory attribute
+ tokenOnly.removeAttribute(TOKEN_ATTRIBUTE + ".any");
+ try {
+ s = repo.login(tokenOnly);
+ s.logout();
+ fail("The additional mandatory attr is missing. login must fail.");
+ } catch (LoginException e) {
+ // success
+ }
+ }
+
+ /**
+ * Tests concurrent login on the Repository including token creation.
+ * Test copied and slightly adjusted from org.apache.jackrabbit.core.ConcurrentLoginTest
+ */
+ public void testConcurrentLogin() throws RepositoryException, NotExecutableException {
+ final Credentials creds = getHelper().getSuperuserCredentials();
+ if (creds instanceof SimpleCredentials) {
+ ((SimpleCredentials) creds).setAttribute(TokenBasedAuthentication.TOKEN_ATTRIBUTE, "");
+ } else {
+ throw new NotExecutableException();
+ }
+
+ final Exception[] exception = new Exception[1];
+ List<Thread> testRunner = new ArrayList<Thread>();
+ for (int i = 0; i < 10; i++) {
+ testRunner.add(new Thread(new Runnable() {
+ public void run() {
+ for (int i = 0; i < 100; i++) {
+ try {
+ Session s = getHelper().getRepository().login(creds);
+ s.logout();
+
+ assertNotNull(s.getAttribute(TokenBasedAuthentication.TOKEN_ATTRIBUTE).toString());
+ } catch (Exception e) {
+ exception[0] = e;
+ break;
+ }
+ }
+ }
+ }));
+ }
+
+ // start threads
+ for (Object aTestRunner : testRunner) {
+ ((Thread) aTestRunner).start();
+ }
+
+ // join threads
+ for (Object aTestRunner : testRunner) {
+ try {
+ ((Thread) aTestRunner).join();
+ } catch (InterruptedException e) {
+ fail(e.toString());
+ }
+ }
+
+ if (exception[0] != null) {
+ fail(exception[0].toString());
+ }
+ }
+
+ /**
+ * Tests concurrent login of 3 different users on the Repository including
+ * token creation.
+ * Test copied and slightly adjusted from org.apache.jackrabbit.core.ConcurrentLoginTest
+ */
+ public void testConcurrentLoginOfDifferentUsers() throws RepositoryException, NotExecutableException {
+ creds.setAttribute(TokenBasedAuthentication.TOKEN_ATTRIBUTE, "");
+
+ final Credentials adminCreds = getHelper().getSuperuserCredentials();
+ if (adminCreds instanceof SimpleCredentials) {
+ ((SimpleCredentials) adminCreds).setAttribute(TokenBasedAuthentication.TOKEN_ATTRIBUTE, "");
+ } else {
+ throw new NotExecutableException();
+ }
+
+ final Credentials readOnlyCreds = getHelper().getSuperuserCredentials();
+ if (readOnlyCreds instanceof SimpleCredentials) {
+ ((SimpleCredentials) readOnlyCreds).setAttribute(TokenBasedAuthentication.TOKEN_ATTRIBUTE, "");
+ } else {
+ throw new NotExecutableException();
+ }
+
+ final List<Credentials> credentials = new ArrayList<Credentials>(3);
+ credentials.add(creds);
+ credentials.add(adminCreds);
+ credentials.add(readOnlyCreds);
+
+ final Exception[] exception = new Exception[1];
+ List<Thread> testRunner = new ArrayList<Thread>();
+ for (int i = 0; i < 10; i++) {
+ testRunner.add(new Thread(new Runnable() {
+ public void run() {
+ for (int i = 0; i < 100; i++) {
+ try {
+ double rand = credentials.size() * Math.random();
+ int index = (int) Math.floor(rand);
+ Credentials c = credentials.get(index);
+ Session s = getHelper().getRepository().login(c);
+ s.logout();
+
+ assertNotNull(s.getAttribute(TokenBasedAuthentication.TOKEN_ATTRIBUTE).toString());
+ } catch (Exception e) {
+ exception[0] = e;
+ break;
+ }
+ }
+ }
+ }));
+ }
+
+ // start threads
+ for (Object aTestRunner : testRunner) {
+ ((Thread) aTestRunner).start();
+ }
+
+ // join threads
+ for (Object aTestRunner : testRunner) {
+ try {
+ ((Thread) aTestRunner).join();
+ } catch (InterruptedException e) {
+ fail(e.toString());
+ }
+ }
+
+ if (exception[0] != null) {
+ fail(exception[0].toString());
+ }
+ }
+
+ /**
+ * Tests concurrent login on the Repository including token creation.
+ * Test copied and slightly adjusted from org.apache.jackrabbit.core.ConcurrentLoginTest
+ */
+ public void testConcurrentLoginDifferentWorkspaces() throws RepositoryException, NotExecutableException {
+ final Credentials creds = getHelper().getSuperuserCredentials();
+ if (creds instanceof SimpleCredentials) {
+ ((SimpleCredentials) creds).setAttribute(TokenBasedAuthentication.TOKEN_ATTRIBUTE, "");
+ } else {
+ throw new NotExecutableException();
+ }
+
+ final List<String> wspNames = Arrays.asList(superuser.getWorkspace().getAccessibleWorkspaceNames());
+ if (wspNames.size() <= 1) {
+ throw new NotExecutableException();
+ }
+
+ final Exception[] exception = new Exception[1];
+ List<Thread> testRunner = new ArrayList<Thread>();
+ for (int i = 0; i < 10; i++) {
+ testRunner.add(new Thread(new Runnable() {
+ public void run() {
+ for (int i = 0; i < 100; i++) {
+ try {
+ double rand = wspNames.size() * Math.random();
+ int index = (int) Math.floor(rand);
+ String wspName = wspNames.get(index);
+
+ Session s = getHelper().getRepository().login(creds, wspName);
+ s.logout();
+
+ assertNotNull(s.getAttribute(TokenBasedAuthentication.TOKEN_ATTRIBUTE).toString());
+ } catch (Exception e) {
+ exception[0] = e;
+ break;
+ }
+ }
+ }
+ }));
+ }
+
+ // start threads
+ for (Object aTestRunner : testRunner) {
+ ((Thread) aTestRunner).start();
+ }
+
+ // join threads
+ for (Object aTestRunner : testRunner) {
+ try {
+ ((Thread) aTestRunner).join();
+ } catch (InterruptedException e) {
+ fail(e.toString());
+ }
+ }
+
+ if (exception[0] != null) {
+ fail(exception[0].toString());
+ }
+ }
+}
\ No newline at end of file
Propchange: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authentication/token/TokenBasedLoginTest.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authentication/token/TokenBasedLoginTest.java
------------------------------------------------------------------------------
svn:keywords = Author Date Id Revision Rev URL