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);