You are viewing a plain text version of this content. The canonical link for it is here.
Posted to oak-commits@jackrabbit.apache.org by an...@apache.org on 2013/05/10 16:15:07 UTC
svn commit: r1481018 - in
/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak:
security/authentication/ security/authentication/token/
spi/security/authentication/
Author: angela
Date: Fri May 10 14:15:07 2013
New Revision: 1481018
URL: http://svn.apache.org/r1481018
Log:
OAK-91 : Implement Authentication Support
Modified:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/AuthenticationConfigurationImpl.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/token/TokenProviderImpl.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/AuthenticationConfiguration.java
Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/AuthenticationConfigurationImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/AuthenticationConfigurationImpl.java?rev=1481018&r1=1481017&r2=1481018&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/AuthenticationConfigurationImpl.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/AuthenticationConfigurationImpl.java Fri May 10 14:15:07 2013
@@ -33,16 +33,25 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
- * AuthenticationConfigurationImpl... TODO
+ * Default implementation of the {@code AuthenticationConfiguration} with the
+ * following characteristics:
+ *
+ * <ul>
+ * <li>
+ * {@link LoginContextProvider}: Returns the default implementation of
+ * {@code LoginContextProvider} that handles standard JAAS based logins and
+ * deals with pre-authenticated subjects.</li>
+ * <li>
+ * {@link TokenProvider}: Returns the default implementation of the token
+ * provider interface that stores information in the content repository.
+ * </li>
+ * </ul>
+ *
*/
public class AuthenticationConfigurationImpl extends SecurityConfiguration.Default implements AuthenticationConfiguration {
private static final Logger log = LoggerFactory.getLogger(AuthenticationConfigurationImpl.class);
- private static final String DEFAULT_APP_NAME = "jackrabbit.oak";
-
- public static final String PARAM_TOKEN_OPTIONS = "org.apache.jackrabbit.oak.token.options";
-
private final SecurityProvider securityProvider;
private final ConfigurationParameters config;
@@ -51,6 +60,30 @@ public class AuthenticationConfiguration
this.config = securityProvider.getConfiguration(PARAM_AUTHENTICATION_OPTIONS);
}
+ /**
+ * Create a {@code LoginContextProvider} using standard
+ * {@link javax.security.auth.login.Configuration#getConfiguration() JAAS}
+ * functionality. In case no login configuration for the specified app name
+ * can be retrieve this implementation uses the default as defined by
+ * {@link ConfigurationUtil#getDefaultConfiguration(ConfigurationParameters)}.
+ * <p>
+ * The {@link LoginContextProvider} implementation is intended to be used with
+ * <ul>
+ * <li>Regular login using JAAS {@link javax.security.auth.spi.LoginModule} or</li>
+ * <li>Pre-authenticated subjects in which case any authentication
+ * related validation is omitted</li>
+ * </ul>
+ *
+ * <h4>Configuration Options</h4>
+ * <ul>
+ * <li>{@link #PARAM_APP_NAME}: The appName passed to
+ * {@code Configuration#getAppConfigurationEntry(String)}. The default
+ * value is {@link #DEFAULT_APP_NAME}.</li>
+ * </ul>
+ *
+ * @param contentRepository The content repository.
+ * @return An new instance of {@link LoginContextProviderImpl}.
+ */
@Nonnull
@Override
public LoginContextProvider getLoginContextProvider(ContentRepository contentRepository) {
@@ -60,20 +93,31 @@ public class AuthenticationConfiguration
loginConfig = Configuration.getConfiguration();
// FIXME: workaround for Java7 behavior. needs clean up (see OAK-497)
if (loginConfig.getAppConfigurationEntry(appName) == null) {
- log.debug("No login configuration available for {}; using default", appName);
loginConfig = null;
}
} catch (SecurityException e) {
log.info("Failed to retrieve login configuration: using default. " + e);
}
if (loginConfig == null) {
- // TODO: define configuration structure
- // TODO: review if having a default is desirable or if login should fail without valid login configuration.
+ log.debug("No login configuration available for {}; using default", appName);
loginConfig = ConfigurationUtil.getDefaultConfiguration(config);
}
return new LoginContextProviderImpl(appName, loginConfig, contentRepository, securityProvider);
}
+ /**
+ * Returns a new instance of {@link TokenProviderImpl}.
+ *
+ * <h4>Configuration Options</h4>
+ * <ul>
+ * <li>{@link #PARAM_TOKEN_OPTIONS}: The configuration parameters for
+ * the token provider which allows to change the default expiration time
+ * and the length of the generated token.</li>
+ * </ul>
+ *
+ * @param root The target root.
+ * @return A new instance of {@link TokenProviderImpl}.
+ */
@Nonnull
@Override
public TokenProvider getTokenProvider(Root root) {
Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/token/TokenProviderImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/token/TokenProviderImpl.java?rev=1481018&r1=1481017&r2=1481018&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/token/TokenProviderImpl.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/token/TokenProviderImpl.java Fri May 10 14:15:07 2013
@@ -16,8 +16,6 @@
*/
package org.apache.jackrabbit.oak.security.authentication.token;
-import static org.apache.jackrabbit.oak.api.Type.STRING;
-
import java.io.UnsupportedEncodingException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
@@ -31,7 +29,6 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
-
import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
import javax.jcr.Credentials;
@@ -62,21 +59,32 @@ import org.apache.jackrabbit.util.Text;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import static org.apache.jackrabbit.oak.api.Type.STRING;
+
/**
- * Default implementation of the {@code TokenProvider} interface with the
- * following characteristics.
+ * Default implementation of the {@code TokenProvider} interface that keeps login
+ * tokens in the content repository. As a precondition the configured the user
+ * management implementation must provide paths for all
+ * {@link org.apache.jackrabbit.api.security.user.User users} that refer to
+ * a valid {@link Tree} in the content repository.
*
- * <h3>doCreateToken</h3>
- * The {@link #doCreateToken(javax.jcr.Credentials)} returns {@code true} if
- * {@code SimpleCredentials} can be extracted from the specified credentials
- * object and that simple credentials object has a {@link #TOKEN_ATTRIBUTE}
- * attribute with an empty value.
+ * <h3>Backwards compatibility with Jackrabbit 2.x</h3>
+ * For security reasons the nodes storing the token information now have a
+ * dedicated node type (rep:Token) which has the following definition:
+ * <pre>
+ * [rep:Token] > nt:unstructured, mix:referenceable
+ * - rep:token.key (STRING) protected mandatory
+ * - rep:token.exp (STRING) protected mandatory
+ * </pre>
+ * Consequently the hash of the token and the expiration time of tokens generated
+ * by this provider can no longer be manipulated using regular JCR item
+ * modifications.<p>
*
- * <h3>createToken</h3>
- * This implementation of {@link #createToken(javax.jcr.Credentials)} will
- * create a separate token node underneath the user home node. That token
- * node contains the hashed token, the expiration time and additional
- * mandatory attributes that will be verified during login.
+ * Existing login tokens generated by Jackrabbit 2.x which are migrated to
+ * OAK will still be valid (unless they expire) due to the fact that
+ * {@link #getTokenInfo(String)} and the implementation of the {@link TokenInfo}
+ * interface will not validate the node type of the token node associated with
+ * a given token.
*/
public class TokenProviderImpl implements TokenProvider {
@@ -127,6 +135,18 @@ public class TokenProviderImpl implement
}
//------------------------------------------------------< TokenProvider >---
+
+ /**
+ * Returns {@code true} if {@code SimpleCredentials} can be extracted from
+ * the specified credentials object and that simple credentials object has
+ * a {@link #TOKEN_ATTRIBUTE} attribute with an empty value.
+ *
+ * @param credentials The current credentials.
+ * @return {@code true} if the specified credentials are {@link SimpleCredentials}
+ * or {@link ImpersonationCredentials} and if the (extracted) simple credentials
+ * object contain a {@link #TOKEN_ATTRIBUTE} attribute with an empty value;
+ * {@code false} otherwise.
+ */
@Override
public boolean doCreateToken(Credentials credentials) {
SimpleCredentials sc = extractSimpleCredentials(credentials);
@@ -138,6 +158,16 @@ public class TokenProviderImpl implement
}
}
+ /**
+ * 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 credentials The current credentials.
+ * @return A new {@code TokenInfo} or {@code null} if the token could not
+ * be created.
+ */
@Override
public TokenInfo createToken(Credentials credentials) {
SimpleCredentials sc = extractSimpleCredentials(credentials);
@@ -158,18 +188,24 @@ public class TokenProviderImpl implement
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.
+ */
@Override
public TokenInfo createToken(String userId, Map<String, ?> attributes) {
- String error = "Failed to create login token ";
- try {
- Authorizable user = userManager.getAuthorizable(userId);
- if (user != null && !user.isGroup()) {
- NodeUtil userNode = new NodeUtil(root.getTree(user.getPath()));
- NodeUtil tokenParent = userNode.getChild(TOKENS_NODE_NAME);
- if (tokenParent == null) {
- tokenParent = userNode.addChild(TOKENS_NODE_NAME, TOKENS_NT_NAME);
- }
-
+ String error = "Failed to create login token. ";
+ NodeUtil tokenParent = getTokenParent(userId);
+ if (tokenParent != null) {
+ try {
long creationTime = new Date().getTime();
Calendar creation = GregorianCalendar.getInstance();
creation.setTimeInMillis(creationTime);
@@ -194,25 +230,33 @@ public class TokenProviderImpl implement
}
}
root.commit();
-
return new TokenInfoImpl(tokenNode, token, userId);
- } else {
- log.debug("Cannot create login token: No corresponding node for User " + userId + '.');
+ } 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 (CommitFailedException e) {
+ // conflict while committing changes
+ log.warn(error, e.getMessage());
}
-
- } catch (NoSuchAlgorithmException e) {
- log.debug(error, e.getMessage());
- } catch (UnsupportedEncodingException e) {
- log.debug(error, e.getMessage());
- } catch (CommitFailedException e) {
- log.debug(error, e.getMessage());
- } catch (RepositoryException e) {
- log.debug(error, e.getMessage());
+ } else {
+ log.warn("Unable to get/create token store for user " + userId);
}
-
return null;
}
+ /**
+ * 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.
+ */
@Override
public TokenInfo getTokenInfo(String token) {
int pos = token.indexOf(DELIM);
@@ -331,6 +375,42 @@ public class TokenProviderImpl implement
return null;
}
+ @CheckForNull
+ private NodeUtil getTokenParent(String userId) {
+ NodeUtil tokenParent = null;
+ String parentPath = null;
+ try {
+ Authorizable user = userManager.getAuthorizable(userId);
+ if (user != null && !user.isGroup()) {
+ String userPath = user.getPath();
+ NodeUtil userNode = new NodeUtil(root.getTree(userPath));
+ tokenParent = userNode.getChild(TOKENS_NODE_NAME);
+ if (tokenParent == null) {
+ tokenParent = userNode.addChild(TOKENS_NODE_NAME, TOKENS_NT_NAME);
+ parentPath = userPath + '/' + TOKENS_NODE_NAME;
+ root.commit();
+ }
+ } else {
+ log.debug("Cannot create login token: No corresponding node for User " + userId + '.');
+ }
+ } catch (RepositoryException e) {
+ // error while accessing user.
+ log.debug("Error while accessing user " + userId + '.', e);
+ } catch (CommitFailedException 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());
+ root.refresh();
+ if (parentPath != null) {
+ Tree parentTree = root.getTree(parentPath);
+ if (parentTree.exists()) {
+ tokenParent = new NodeUtil(parentTree);
+ }
+ }
+ }
+ return tokenParent;
+ }
+
//--------------------------------------------------------------------------
/**
* TokenInfo
@@ -434,7 +514,7 @@ public class TokenProviderImpl implement
* Returns {@code true} if the specified {@code attributeName}
* starts with or equals {@link #TOKEN_ATTRIBUTE}.
*
- * @param attributeName
+ * @param attributeName The attribute name.
* @return {@code true} if the specified {@code attributeName}
* starts with or equals {@link #TOKEN_ATTRIBUTE}.
*/
@@ -448,12 +528,12 @@ public class TokenProviderImpl implement
* a lazy evaluation in order to avoid testing the defining node type of
* the associated jcr property.
*
- * @param propertyName
+ * @param attributeName The attribute name.
* @return {@code true} if the specified property name doesn't seem
* to represent repository internal information.
*/
- private static boolean isInfoAttribute(String propertyName) {
- String prefix = Text.getNamespacePrefix(propertyName);
+ private static boolean isInfoAttribute(String attributeName) {
+ String prefix = Text.getNamespacePrefix(attributeName);
return !NamespaceConstants.RESERVED_PREFIXES.contains(prefix);
}
}
Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/AuthenticationConfiguration.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/AuthenticationConfiguration.java?rev=1481018&r1=1481017&r2=1481018&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/AuthenticationConfiguration.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/AuthenticationConfiguration.java Fri May 10 14:15:07 2013
@@ -24,12 +24,15 @@ import org.apache.jackrabbit.oak.spi.sec
import org.apache.jackrabbit.oak.spi.security.authentication.token.TokenProvider;
/**
- * AuthenticationConfiguration... TODO
+ * Interface for the authentication setup.
*/
public interface AuthenticationConfiguration extends SecurityConfiguration {
String PARAM_AUTHENTICATION_OPTIONS = "org.apache.jackrabbit.oak.authentication.options";
+ String PARAM_TOKEN_OPTIONS = "org.apache.jackrabbit.oak.authentication.token.options";
+
String PARAM_APP_NAME = "org.apache.jackrabbit.oak.auth.appName";
+ String DEFAULT_APP_NAME = "jackrabbit.oak";
@Nonnull
LoginContextProvider getLoginContextProvider(ContentRepository contentRepository);