hadoop-common-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From t...@apache.org
Subject [2/2] git commit: HADOOP-10868. AuthenticationFilter should support externalizing the secret for signing and provide rotation support. (rkanter via tucu)
Date Tue, 16 Sep 2014 00:13:32 GMT
HADOOP-10868. AuthenticationFilter should support externalizing the secret for signing and provide rotation support. (rkanter via tucu)


Project: http://git-wip-us.apache.org/repos/asf/hadoop/repo
Commit: http://git-wip-us.apache.org/repos/asf/hadoop/commit/932ae036
Tree: http://git-wip-us.apache.org/repos/asf/hadoop/tree/932ae036
Diff: http://git-wip-us.apache.org/repos/asf/hadoop/diff/932ae036

Branch: refs/heads/trunk
Commit: 932ae036acb96634c5dd435d57ba02ce4d5e8918
Parents: 0ac760a
Author: Alejandro Abdelnur <tucu@apache.org>
Authored: Mon Sep 15 17:05:42 2014 -0700
Committer: Alejandro Abdelnur <tucu@apache.org>
Committed: Mon Sep 15 17:05:42 2014 -0700

----------------------------------------------------------------------
 hadoop-common-project/hadoop-auth/pom.xml       |  13 +
 .../server/AuthenticationFilter.java            | 152 ++++--
 .../util/RandomSignerSecretProvider.java        |   4 +-
 .../util/RolloverSignerSecretProvider.java      |   7 +-
 .../util/SignerSecretProvider.java              |   9 +-
 .../util/StringSignerSecretProvider.java        |  15 +-
 .../util/ZKSignerSecretProvider.java            | 503 +++++++++++++++++++
 .../src/site/apt/Configuration.apt.vm           | 148 +++++-
 .../hadoop-auth/src/site/apt/index.apt.vm       |   5 +
 .../server/TestAuthenticationFilter.java        | 117 ++++-
 .../util/TestJaasConfiguration.java             |  55 ++
 .../util/TestRandomSignerSecretProvider.java    |   2 +-
 .../util/TestRolloverSignerSecretProvider.java  |   2 +-
 .../authentication/util/TestSigner.java         |  23 +-
 .../util/TestStringSignerSecretProvider.java    |   9 +-
 .../util/TestZKSignerSecretProvider.java        | 270 ++++++++++
 hadoop-common-project/hadoop-common/CHANGES.txt |   3 +
 .../hadoop/fs/http/server/TestHttpFSServer.java |   8 +-
 hadoop-project/pom.xml                          |  11 +
 19 files changed, 1259 insertions(+), 97 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/hadoop/blob/932ae036/hadoop-common-project/hadoop-auth/pom.xml
----------------------------------------------------------------------
diff --git a/hadoop-common-project/hadoop-auth/pom.xml b/hadoop-common-project/hadoop-auth/pom.xml
index 564518c..5f7d774 100644
--- a/hadoop-common-project/hadoop-auth/pom.xml
+++ b/hadoop-common-project/hadoop-auth/pom.xml
@@ -130,6 +130,19 @@
           </exclusion>
         </exclusions>
     </dependency>
+    <dependency>
+      <groupId>org.apache.zookeeper</groupId>
+      <artifactId>zookeeper</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.curator</groupId>
+      <artifactId>curator-framework</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.curator</groupId>
+      <artifactId>curator-test</artifactId>
+      <scope>test</scope>
+    </dependency>
   </dependencies>
 
   <build>

http://git-wip-us.apache.org/repos/asf/hadoop/blob/932ae036/hadoop-common-project/hadoop-auth/src/main/java/org/apache/hadoop/security/authentication/server/AuthenticationFilter.java
----------------------------------------------------------------------
diff --git a/hadoop-common-project/hadoop-auth/src/main/java/org/apache/hadoop/security/authentication/server/AuthenticationFilter.java b/hadoop-common-project/hadoop-auth/src/main/java/org/apache/hadoop/security/authentication/server/AuthenticationFilter.java
index 9330444..47cf54c 100644
--- a/hadoop-common-project/hadoop-auth/src/main/java/org/apache/hadoop/security/authentication/server/AuthenticationFilter.java
+++ b/hadoop-common-project/hadoop-auth/src/main/java/org/apache/hadoop/security/authentication/server/AuthenticationFilter.java
@@ -22,6 +22,7 @@ import org.apache.hadoop.security.authentication.util.SignerException;
 import org.apache.hadoop.security.authentication.util.RandomSignerSecretProvider;
 import org.apache.hadoop.security.authentication.util.SignerSecretProvider;
 import org.apache.hadoop.security.authentication.util.StringSignerSecretProvider;
+import org.apache.hadoop.security.authentication.util.ZKSignerSecretProvider;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -42,7 +43,7 @@ import java.util.*;
 
 /**
  * The {@link AuthenticationFilter} enables protecting web application resources with different (pluggable)
- * authentication mechanisms.
+ * authentication mechanisms and signer secret providers.
  * <p/>
  * Out of the box it provides 2 authentication mechanisms: Pseudo and Kerberos SPNEGO.
  * <p/>
@@ -60,10 +61,13 @@ import java.util.*;
  * <li>[#PREFIX#.]type: simple|kerberos|#CLASS#, 'simple' is short for the
  * {@link PseudoAuthenticationHandler}, 'kerberos' is short for {@link KerberosAuthenticationHandler}, otherwise
  * the full class name of the {@link AuthenticationHandler} must be specified.</li>
- * <li>[#PREFIX#.]signature.secret: the secret used to sign the HTTP cookie value. The default value is a random
- * value. Unless multiple webapp instances need to share the secret the random value is adequate.</li>
- * <li>[#PREFIX#.]token.validity: time -in seconds- that the generated token is valid before a
- * new authentication is triggered, default value is <code>3600</code> seconds.</li>
+ * <li>[#PREFIX#.]signature.secret: when signer.secret.provider is set to
+ * "string" or not specified, this is the value for the secret used to sign the
+ * HTTP cookie.</li>
+ * <li>[#PREFIX#.]token.validity: time -in seconds- that the generated token is
+ * valid before a new authentication is triggered, default value is
+ * <code>3600</code> seconds. This is also used for the rollover interval for
+ * the "random" and "zookeeper" SignerSecretProviders.</li>
  * <li>[#PREFIX#.]cookie.domain: domain to use for the HTTP cookie that stores the authentication token.</li>
  * <li>[#PREFIX#.]cookie.path: path to use for the HTTP cookie that stores the authentication token.</li>
  * </ul>
@@ -72,6 +76,49 @@ import java.util.*;
  * {@link AuthenticationFilter} will take all the properties that start with the prefix #PREFIX#, it will remove
  * the prefix from it and it will pass them to the the authentication handler for initialization. Properties that do
  * not start with the prefix will not be passed to the authentication handler initialization.
+ * <p/>
+ * Out of the box it provides 3 signer secret provider implementations:
+ * "string", "random", and "zookeeper"
+ * <p/>
+ * Additional signer secret providers are supported via the
+ * {@link SignerSecretProvider} class.
+ * <p/>
+ * For the HTTP cookies mentioned above, the SignerSecretProvider is used to
+ * determine the secret to use for signing the cookies. Different
+ * implementations can have different behaviors.  The "string" implementation
+ * simply uses the string set in the [#PREFIX#.]signature.secret property
+ * mentioned above.  The "random" implementation uses a randomly generated
+ * secret that rolls over at the interval specified by the
+ * [#PREFIX#.]token.validity mentioned above.  The "zookeeper" implementation
+ * is like the "random" one, except that it synchronizes the random secret
+ * and rollovers between multiple servers; it's meant for HA services.
+ * <p/>
+ * The relevant configuration properties are:
+ * <ul>
+ * <li>signer.secret.provider: indicates the name of the SignerSecretProvider
+ * class to use. Possible values are: "string", "random", "zookeeper", or a
+ * classname. If not specified, the "string" implementation will be used with
+ * [#PREFIX#.]signature.secret; and if that's not specified, the "random"
+ * implementation will be used.</li>
+ * <li>[#PREFIX#.]signature.secret: When the "string" implementation is
+ * specified, this value is used as the secret.</li>
+ * <li>[#PREFIX#.]token.validity: When the "random" or "zookeeper"
+ * implementations are specified, this value is used as the rollover
+ * interval.</li>
+ * </ul>
+ * <p/>
+ * The "zookeeper" implementation has additional configuration properties that
+ * must be specified; see {@link ZKSignerSecretProvider} for details.
+ * <p/>
+ * For subclasses of AuthenticationFilter that want additional control over the
+ * SignerSecretProvider, they can use the following attribute set in the
+ * ServletContext:
+ * <ul>
+ * <li>signer.secret.provider.object: A SignerSecretProvider implementation can
+ * be passed here that will be used instead of the signer.secret.provider
+ * configuration property. Note that the class should already be
+ * initialized.</li>
+ * </ul>
  */
 
 @InterfaceAudience.Private
@@ -112,20 +159,23 @@ public class AuthenticationFilter implements Filter {
 
   /**
    * Constant for the configuration property that indicates the name of the
-   * SignerSecretProvider class to use.  If not specified, SIGNATURE_SECRET
-   * will be used or a random secret.
+   * SignerSecretProvider class to use.
+   * Possible values are: "string", "random", "zookeeper", or a classname.
+   * If not specified, the "string" implementation will be used with
+   * SIGNATURE_SECRET; and if that's not specified, the "random" implementation
+   * will be used.
    */
-  public static final String SIGNER_SECRET_PROVIDER_CLASS =
+  public static final String SIGNER_SECRET_PROVIDER =
           "signer.secret.provider";
 
   /**
-   * Constant for the attribute that can be used for providing a custom
-   * object that subclasses the SignerSecretProvider.  Note that this should be
-   * set in the ServletContext and the class should already be initialized.  
-   * If not specified, SIGNER_SECRET_PROVIDER_CLASS will be used.
+   * Constant for the ServletContext attribute that can be used for providing a
+   * custom implementation of the SignerSecretProvider. Note that the class
+   * should already be initialized. If not specified, SIGNER_SECRET_PROVIDER
+   * will be used.
    */
-  public static final String SIGNATURE_PROVIDER_ATTRIBUTE =
-      "org.apache.hadoop.security.authentication.util.SignerSecretProvider";
+  public static final String SIGNER_SECRET_PROVIDER_ATTRIBUTE =
+      "signer.secret.provider.object";
 
   private Properties config;
   private Signer signer;
@@ -138,7 +188,7 @@ public class AuthenticationFilter implements Filter {
   private String cookiePath;
 
   /**
-   * Initializes the authentication filter.
+   * Initializes the authentication filter and signer secret provider.
    * <p/>
    * It instantiates and initializes the specified {@link AuthenticationHandler}.
    * <p/>
@@ -184,35 +234,19 @@ public class AuthenticationFilter implements Filter {
     validity = Long.parseLong(config.getProperty(AUTH_TOKEN_VALIDITY, "36000"))
         * 1000; //10 hours
     secretProvider = (SignerSecretProvider) filterConfig.getServletContext().
-        getAttribute(SIGNATURE_PROVIDER_ATTRIBUTE);
+        getAttribute(SIGNER_SECRET_PROVIDER_ATTRIBUTE);
     if (secretProvider == null) {
-      String signerSecretProviderClassName =
-          config.getProperty(configPrefix + SIGNER_SECRET_PROVIDER_CLASS, null);
-      if (signerSecretProviderClassName == null) {
-        String signatureSecret =
-            config.getProperty(configPrefix + SIGNATURE_SECRET, null);
-        if (signatureSecret != null) {
-          secretProvider = new StringSignerSecretProvider(signatureSecret);
-        } else {
-          secretProvider = new RandomSignerSecretProvider();
-          randomSecret = true;
-        }
-      } else {
-        try {
-          Class<?> klass = Thread.currentThread().getContextClassLoader().
-              loadClass(signerSecretProviderClassName);
-          secretProvider = (SignerSecretProvider) klass.newInstance();
-          customSecretProvider = true;
-        } catch (ClassNotFoundException ex) {
-          throw new ServletException(ex);
-        } catch (InstantiationException ex) {
-          throw new ServletException(ex);
-        } catch (IllegalAccessException ex) {
-          throw new ServletException(ex);
-        }
+      Class<? extends SignerSecretProvider> providerClass
+              = getProviderClass(config);
+      try {
+        secretProvider = providerClass.newInstance();
+      } catch (InstantiationException ex) {
+        throw new ServletException(ex);
+      } catch (IllegalAccessException ex) {
+        throw new ServletException(ex);
       }
       try {
-        secretProvider.init(config, validity);
+        secretProvider.init(config, filterConfig.getServletContext(), validity);
       } catch (Exception ex) {
         throw new ServletException(ex);
       }
@@ -225,6 +259,42 @@ public class AuthenticationFilter implements Filter {
     cookiePath = config.getProperty(COOKIE_PATH, null);
   }
 
+  @SuppressWarnings("unchecked")
+  private Class<? extends SignerSecretProvider> getProviderClass(Properties config)
+          throws ServletException {
+    String providerClassName;
+    String signerSecretProviderName
+            = config.getProperty(SIGNER_SECRET_PROVIDER, null);
+    // fallback to old behavior
+    if (signerSecretProviderName == null) {
+      String signatureSecret = config.getProperty(SIGNATURE_SECRET, null);
+      if (signatureSecret != null) {
+        providerClassName = StringSignerSecretProvider.class.getName();
+      } else {
+        providerClassName = RandomSignerSecretProvider.class.getName();
+        randomSecret = true;
+      }
+    } else {
+      if ("random".equals(signerSecretProviderName)) {
+        providerClassName = RandomSignerSecretProvider.class.getName();
+        randomSecret = true;
+      } else if ("string".equals(signerSecretProviderName)) {
+        providerClassName = StringSignerSecretProvider.class.getName();
+      } else if ("zookeeper".equals(signerSecretProviderName)) {
+        providerClassName = ZKSignerSecretProvider.class.getName();
+      } else {
+        providerClassName = signerSecretProviderName;
+        customSecretProvider = true;
+      }
+    }
+    try {
+      return (Class<? extends SignerSecretProvider>) Thread.currentThread().
+              getContextClassLoader().loadClass(providerClassName);
+    } catch (ClassNotFoundException ex) {
+      throw new ServletException(ex);
+    }
+  }
+
   /**
    * Returns the configuration properties of the {@link AuthenticationFilter}
    * without the prefix. The returned properties are the same that the

http://git-wip-us.apache.org/repos/asf/hadoop/blob/932ae036/hadoop-common-project/hadoop-auth/src/main/java/org/apache/hadoop/security/authentication/util/RandomSignerSecretProvider.java
----------------------------------------------------------------------
diff --git a/hadoop-common-project/hadoop-auth/src/main/java/org/apache/hadoop/security/authentication/util/RandomSignerSecretProvider.java b/hadoop-common-project/hadoop-auth/src/main/java/org/apache/hadoop/security/authentication/util/RandomSignerSecretProvider.java
index 5491a86..29e5661 100644
--- a/hadoop-common-project/hadoop-auth/src/main/java/org/apache/hadoop/security/authentication/util/RandomSignerSecretProvider.java
+++ b/hadoop-common-project/hadoop-auth/src/main/java/org/apache/hadoop/security/authentication/util/RandomSignerSecretProvider.java
@@ -13,12 +13,13 @@
  */
 package org.apache.hadoop.security.authentication.util;
 
+import com.google.common.annotations.VisibleForTesting;
 import java.util.Random;
 import org.apache.hadoop.classification.InterfaceAudience;
 import org.apache.hadoop.classification.InterfaceStability;
 
 /**
- * A SignerSecretProvider that uses a random number as it's secret.  It rolls
+ * A SignerSecretProvider that uses a random number as its secret.  It rolls
  * the secret at a regular interval.
  */
 @InterfaceStability.Unstable
@@ -37,6 +38,7 @@ public class RandomSignerSecretProvider extends RolloverSignerSecretProvider {
    * is meant for testing.
    * @param seed the seed for the random number generator
    */
+  @VisibleForTesting
   public RandomSignerSecretProvider(long seed) {
     super();
     rand = new Random(seed);

http://git-wip-us.apache.org/repos/asf/hadoop/blob/932ae036/hadoop-common-project/hadoop-auth/src/main/java/org/apache/hadoop/security/authentication/util/RolloverSignerSecretProvider.java
----------------------------------------------------------------------
diff --git a/hadoop-common-project/hadoop-auth/src/main/java/org/apache/hadoop/security/authentication/util/RolloverSignerSecretProvider.java b/hadoop-common-project/hadoop-auth/src/main/java/org/apache/hadoop/security/authentication/util/RolloverSignerSecretProvider.java
index ec6e601..bdca3e4 100644
--- a/hadoop-common-project/hadoop-auth/src/main/java/org/apache/hadoop/security/authentication/util/RolloverSignerSecretProvider.java
+++ b/hadoop-common-project/hadoop-auth/src/main/java/org/apache/hadoop/security/authentication/util/RolloverSignerSecretProvider.java
@@ -17,6 +17,7 @@ import java.util.Properties;
 import java.util.concurrent.Executors;
 import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.TimeUnit;
+import javax.servlet.ServletContext;
 import org.apache.hadoop.classification.InterfaceAudience;
 import org.apache.hadoop.classification.InterfaceStability;
 import org.slf4j.Logger;
@@ -57,12 +58,14 @@ public abstract class RolloverSignerSecretProvider
    * Initialize the SignerSecretProvider.  It initializes the current secret
    * and starts the scheduler for the rollover to run at an interval of
    * tokenValidity.
-   * @param config filter configuration
+   * @param config configuration properties
+   * @param servletContext servlet context
    * @param tokenValidity The amount of time a token is valid for
    * @throws Exception
    */
   @Override
-  public void init(Properties config, long tokenValidity) throws Exception {
+  public void init(Properties config, ServletContext servletContext,
+          long tokenValidity) throws Exception {
     initSecrets(generateNewSecret(), null);
     startScheduler(tokenValidity, tokenValidity);
   }

http://git-wip-us.apache.org/repos/asf/hadoop/blob/932ae036/hadoop-common-project/hadoop-auth/src/main/java/org/apache/hadoop/security/authentication/util/SignerSecretProvider.java
----------------------------------------------------------------------
diff --git a/hadoop-common-project/hadoop-auth/src/main/java/org/apache/hadoop/security/authentication/util/SignerSecretProvider.java b/hadoop-common-project/hadoop-auth/src/main/java/org/apache/hadoop/security/authentication/util/SignerSecretProvider.java
index a4d98d7..2e0b985 100644
--- a/hadoop-common-project/hadoop-auth/src/main/java/org/apache/hadoop/security/authentication/util/SignerSecretProvider.java
+++ b/hadoop-common-project/hadoop-auth/src/main/java/org/apache/hadoop/security/authentication/util/SignerSecretProvider.java
@@ -14,6 +14,7 @@
 package org.apache.hadoop.security.authentication.util;
 
 import java.util.Properties;
+import javax.servlet.ServletContext;
 import org.apache.hadoop.classification.InterfaceAudience;
 import org.apache.hadoop.classification.InterfaceStability;
 
@@ -30,13 +31,13 @@ public abstract class SignerSecretProvider {
 
   /**
    * Initialize the SignerSecretProvider
-   * @param config filter configuration
+   * @param config configuration properties
+   * @param servletContext servlet context
    * @param tokenValidity The amount of time a token is valid for
    * @throws Exception
    */
-  public abstract void init(Properties config, long tokenValidity)
-      throws Exception;
-
+  public abstract void init(Properties config, ServletContext servletContext,
+          long tokenValidity) throws Exception;
   /**
    * Will be called on shutdown; subclasses should perform any cleanup here.
    */

http://git-wip-us.apache.org/repos/asf/hadoop/blob/932ae036/hadoop-common-project/hadoop-auth/src/main/java/org/apache/hadoop/security/authentication/util/StringSignerSecretProvider.java
----------------------------------------------------------------------
diff --git a/hadoop-common-project/hadoop-auth/src/main/java/org/apache/hadoop/security/authentication/util/StringSignerSecretProvider.java b/hadoop-common-project/hadoop-auth/src/main/java/org/apache/hadoop/security/authentication/util/StringSignerSecretProvider.java
index 230059b..7aaccd2 100644
--- a/hadoop-common-project/hadoop-auth/src/main/java/org/apache/hadoop/security/authentication/util/StringSignerSecretProvider.java
+++ b/hadoop-common-project/hadoop-auth/src/main/java/org/apache/hadoop/security/authentication/util/StringSignerSecretProvider.java
@@ -14,8 +14,10 @@
 package org.apache.hadoop.security.authentication.util;
 
 import java.util.Properties;
+import javax.servlet.ServletContext;
 import org.apache.hadoop.classification.InterfaceAudience;
 import org.apache.hadoop.classification.InterfaceStability;
+import org.apache.hadoop.security.authentication.server.AuthenticationFilter;
 
 /**
  * A SignerSecretProvider that simply creates a secret based on a given String.
@@ -27,14 +29,15 @@ public class StringSignerSecretProvider extends SignerSecretProvider {
   private byte[] secret;
   private byte[][] secrets;
 
-  public StringSignerSecretProvider(String secretStr) {
-    secret = secretStr.getBytes();
-    secrets = new byte[][]{secret};
-  }
+  public StringSignerSecretProvider() {}
 
   @Override
-  public void init(Properties config, long tokenValidity) throws Exception {
-    // do nothing
+  public void init(Properties config, ServletContext servletContext,
+          long tokenValidity) throws Exception {
+    String signatureSecret = config.getProperty(
+            AuthenticationFilter.SIGNATURE_SECRET, null);
+    secret = signatureSecret.getBytes();
+    secrets = new byte[][]{secret};
   }
 
   @Override

http://git-wip-us.apache.org/repos/asf/hadoop/blob/932ae036/hadoop-common-project/hadoop-auth/src/main/java/org/apache/hadoop/security/authentication/util/ZKSignerSecretProvider.java
----------------------------------------------------------------------
diff --git a/hadoop-common-project/hadoop-auth/src/main/java/org/apache/hadoop/security/authentication/util/ZKSignerSecretProvider.java b/hadoop-common-project/hadoop-auth/src/main/java/org/apache/hadoop/security/authentication/util/ZKSignerSecretProvider.java
new file mode 100644
index 0000000..45d4d65
--- /dev/null
+++ b/hadoop-common-project/hadoop-auth/src/main/java/org/apache/hadoop/security/authentication/util/ZKSignerSecretProvider.java
@@ -0,0 +1,503 @@
+/**
+ * Licensed 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. See accompanying LICENSE file.
+ */
+package org.apache.hadoop.security.authentication.util;
+
+import com.google.common.annotations.VisibleForTesting;
+import java.nio.ByteBuffer;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Random;
+import javax.security.auth.login.AppConfigurationEntry;
+import javax.security.auth.login.Configuration;
+import javax.servlet.ServletContext;
+import org.apache.curator.RetryPolicy;
+import org.apache.curator.framework.CuratorFramework;
+import org.apache.curator.framework.CuratorFrameworkFactory;
+import org.apache.curator.framework.api.ACLProvider;
+import org.apache.curator.framework.imps.DefaultACLProvider;
+import org.apache.curator.retry.ExponentialBackoffRetry;
+import org.apache.hadoop.classification.InterfaceAudience;
+import org.apache.hadoop.classification.InterfaceStability;
+import org.apache.zookeeper.KeeperException;
+import org.apache.zookeeper.ZooDefs.Perms;
+import org.apache.zookeeper.client.ZooKeeperSaslClient;
+import org.apache.zookeeper.data.ACL;
+import org.apache.zookeeper.data.Id;
+import org.apache.zookeeper.data.Stat;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A SignerSecretProvider that synchronizes a rolling random secret between
+ * multiple servers using ZooKeeper.
+ * <p/>
+ * It works by storing the secrets and next rollover time in a ZooKeeper znode.
+ * All ZKSignerSecretProviders looking at that znode will use those
+ * secrets and next rollover time to ensure they are synchronized.  There is no
+ * "leader" -- any of the ZKSignerSecretProviders can choose the next secret;
+ * which one is indeterminate.  Kerberos-based ACLs can also be enforced to
+ * prevent a malicious third-party from getting or setting the secrets.  It uses
+ * its own CuratorFramework client for talking to ZooKeeper.  If you want to use
+ * your own Curator client, you can pass it to ZKSignerSecretProvider; see
+ * {@link org.apache.hadoop.security.authentication.server.AuthenticationFilter}
+ * for more details.
+ * <p/>
+ * The supported configuration properties are:
+ * <ul>
+ * <li>signer.secret.provider.zookeeper.connection.string: indicates the
+ * ZooKeeper connection string to connect with.</li>
+ * <li>signer.secret.provider.zookeeper.path: indicates the ZooKeeper path
+ * to use for storing and retrieving the secrets.  All ZKSignerSecretProviders
+ * that need to coordinate should point to the same path.</li>
+ * <li>signer.secret.provider.zookeeper.auth.type: indicates the auth type to
+ * use.  Supported values are "none" and "sasl".  The default value is "none"
+ * </li>
+ * <li>signer.secret.provider.zookeeper.kerberos.keytab: set this to the path
+ * with the Kerberos keytab file.  This is only required if using Kerberos.</li>
+ * <li>signer.secret.provider.zookeeper.kerberos.principal: set this to the
+ * Kerberos principal to use.  This only required if using Kerberos.</li>
+ * <li>signer.secret.provider.zookeeper.disconnect.on.close: when set to "true",
+ * ZKSignerSecretProvider will close the ZooKeeper connection on shutdown.  The
+ * default is "true". Only set this to "false" if a custom Curator client is
+ * being provided and the disconnection is being handled elsewhere.</li>
+ * </ul>
+ *
+ * The following attribute in the ServletContext can also be set if desired:
+ * <li>signer.secret.provider.zookeeper.curator.client: A CuratorFramework
+ * client object can be passed here. If given, the "zookeeper" implementation
+ * will use this Curator client instead of creating its own, which is useful if
+ * you already have a Curator client or want more control over its
+ * configuration.</li>
+ */
+@InterfaceStability.Unstable
+@InterfaceAudience.Private
+public class ZKSignerSecretProvider extends RolloverSignerSecretProvider {
+
+  private static final String CONFIG_PREFIX =
+          "signer.secret.provider.zookeeper.";
+
+  /**
+   * Constant for the property that specifies the ZooKeeper connection string.
+   */
+  public static final String ZOOKEEPER_CONNECTION_STRING =
+          CONFIG_PREFIX + "connection.string";
+
+  /**
+   * Constant for the property that specifies the ZooKeeper path.
+   */
+  public static final String ZOOKEEPER_PATH = CONFIG_PREFIX + "path";
+
+  /**
+   * Constant for the property that specifies the auth type to use.  Supported
+   * values are "none" and "sasl".  The default value is "none".
+   */
+  public static final String ZOOKEEPER_AUTH_TYPE = CONFIG_PREFIX + "auth.type";
+
+  /**
+   * Constant for the property that specifies the Kerberos keytab file.
+   */
+  public static final String ZOOKEEPER_KERBEROS_KEYTAB =
+          CONFIG_PREFIX + "kerberos.keytab";
+
+  /**
+   * Constant for the property that specifies the Kerberos principal.
+   */
+  public static final String ZOOKEEPER_KERBEROS_PRINCIPAL =
+          CONFIG_PREFIX + "kerberos.principal";
+
+  /**
+   * Constant for the property that specifies whether or not the Curator client
+   * should disconnect from ZooKeeper on shutdown.  The default is "true".  Only
+   * set this to "false" if a custom Curator client is being provided and the
+   * disconnection is being handled elsewhere.
+   */
+  public static final String DISCONNECT_FROM_ZOOKEEPER_ON_SHUTDOWN =
+          CONFIG_PREFIX + "disconnect.on.shutdown";
+
+  /**
+   * Constant for the ServletContext attribute that can be used for providing a
+   * custom CuratorFramework client. If set ZKSignerSecretProvider will use this
+   * Curator client instead of creating a new one. The providing class is
+   * responsible for creating and configuring the Curator client (including
+   * security and ACLs) in this case.
+   */
+  public static final String
+      ZOOKEEPER_SIGNER_SECRET_PROVIDER_CURATOR_CLIENT_ATTRIBUTE =
+      CONFIG_PREFIX + "curator.client";
+
+  private static Logger LOG = LoggerFactory.getLogger(
+          ZKSignerSecretProvider.class);
+  private String path;
+  /**
+   * Stores the next secret that will be used after the current one rolls over.
+   * We do this to help with rollover performance by actually deciding the next
+   * secret at the previous rollover.  This allows us to switch to the next
+   * secret very quickly.  Afterwards, we have plenty of time to decide on the
+   * next secret.
+   */
+  private volatile byte[] nextSecret;
+  private final Random rand;
+  /**
+   * Stores the current version of the znode.
+   */
+  private int zkVersion;
+  /**
+   * Stores the next date that the rollover will occur.  This is only used
+   * for allowing new servers joining later to synchronize their rollover
+   * with everyone else.
+   */
+  private long nextRolloverDate;
+  private long tokenValidity;
+  private CuratorFramework client;
+  private boolean shouldDisconnect;
+  private static int INT_BYTES = Integer.SIZE / Byte.SIZE;
+  private static int LONG_BYTES = Long.SIZE / Byte.SIZE;
+  private static int DATA_VERSION = 0;
+
+  public ZKSignerSecretProvider() {
+    super();
+    rand = new Random();
+  }
+
+  /**
+   * This constructor lets you set the seed of the Random Number Generator and
+   * is meant for testing.
+   * @param seed the seed for the random number generator
+   */
+  @VisibleForTesting
+  public ZKSignerSecretProvider(long seed) {
+    super();
+    rand = new Random(seed);
+  }
+
+  @Override
+  public void init(Properties config, ServletContext servletContext,
+          long tokenValidity) throws Exception {
+    Object curatorClientObj = servletContext.getAttribute(
+            ZOOKEEPER_SIGNER_SECRET_PROVIDER_CURATOR_CLIENT_ATTRIBUTE);
+    if (curatorClientObj != null
+            && curatorClientObj instanceof CuratorFramework) {
+      client = (CuratorFramework) curatorClientObj;
+    } else {
+      client = createCuratorClient(config);
+    }
+    this.tokenValidity = tokenValidity;
+    shouldDisconnect = Boolean.parseBoolean(
+            config.getProperty(DISCONNECT_FROM_ZOOKEEPER_ON_SHUTDOWN, "true"));
+    path = config.getProperty(ZOOKEEPER_PATH);
+    if (path == null) {
+      throw new IllegalArgumentException(ZOOKEEPER_PATH
+              + " must be specified");
+    }
+    try {
+      nextRolloverDate = System.currentTimeMillis() + tokenValidity;
+      // everyone tries to do this, only one will succeed and only when the
+      // znode doesn't already exist.  Everyone else will synchronize on the
+      // data from the znode
+      client.create().creatingParentsIfNeeded()
+              .forPath(path, generateZKData(generateRandomSecret(),
+              generateRandomSecret(), null));
+      zkVersion = 0;
+      LOG.info("Creating secret znode");
+    } catch (KeeperException.NodeExistsException nee) {
+      LOG.info("The secret znode already exists, retrieving data");
+    }
+    // Synchronize on the data from the znode
+    // passing true tells it to parse out all the data for initing
+    pullFromZK(true);
+    long initialDelay = nextRolloverDate - System.currentTimeMillis();
+    // If it's in the past, try to find the next interval that we should
+    // be using
+    if (initialDelay < 1l) {
+      int i = 1;
+      while (initialDelay < 1l) {
+        initialDelay = nextRolloverDate + tokenValidity * i
+                - System.currentTimeMillis();
+        i++;
+      }
+    }
+    super.startScheduler(initialDelay, tokenValidity);
+  }
+
+  /**
+   * Disconnects from ZooKeeper unless told not to.
+   */
+  @Override
+  public void destroy() {
+    if (shouldDisconnect && client != null) {
+      client.close();
+    }
+    super.destroy();
+  }
+
+  @Override
+  protected synchronized void rollSecret() {
+    super.rollSecret();
+    // Try to push the information to ZooKeeper with a potential next secret.
+    nextRolloverDate += tokenValidity;
+    byte[][] secrets = super.getAllSecrets();
+    pushToZK(generateRandomSecret(), secrets[0], secrets[1]);
+    // Pull info from ZooKeeper to get the decided next secret
+    // passing false tells it that we don't care about most of the data
+    pullFromZK(false);
+  }
+
+  @Override
+  protected byte[] generateNewSecret() {
+    // We simply return nextSecret because it's already been decided on
+    return nextSecret;
+  }
+
+  /**
+   * Pushes proposed data to ZooKeeper.  If a different server pushes its data
+   * first, it gives up.
+   * @param newSecret The new secret to use
+   * @param currentSecret The current secret
+   * @param previousSecret  The previous secret
+   */
+  private synchronized void pushToZK(byte[] newSecret, byte[] currentSecret,
+          byte[] previousSecret) {
+    byte[] bytes = generateZKData(newSecret, currentSecret, previousSecret);
+    try {
+      client.setData().withVersion(zkVersion).forPath(path, bytes);
+    } catch (KeeperException.BadVersionException bve) {
+      LOG.debug("Unable to push to znode; another server already did it");
+    } catch (Exception ex) {
+      LOG.error("An unexpected exception occured pushing data to ZooKeeper",
+              ex);
+    }
+  }
+
+  /**
+   * Serialize the data to attempt to push into ZooKeeper.  The format is this:
+   * <p>
+   * [DATA_VERSION, newSecretLength, newSecret, currentSecretLength, currentSecret, previousSecretLength, previousSecret, nextRolloverDate]
+   * <p>
+   * Only previousSecret can be null, in which case the format looks like this:
+   * <p>
+   * [DATA_VERSION, newSecretLength, newSecret, currentSecretLength, currentSecret, 0, nextRolloverDate]
+   * <p>
+   * @param newSecret The new secret to use
+   * @param currentSecret The current secret
+   * @param previousSecret The previous secret
+   * @return The serialized data for ZooKeeper
+   */
+  private synchronized byte[] generateZKData(byte[] newSecret,
+          byte[] currentSecret, byte[] previousSecret) {
+    int newSecretLength = newSecret.length;
+    int currentSecretLength = currentSecret.length;
+    int previousSecretLength = 0;
+    if (previousSecret != null) {
+      previousSecretLength = previousSecret.length;
+    }
+    ByteBuffer bb = ByteBuffer.allocate(INT_BYTES + INT_BYTES + newSecretLength
+        + INT_BYTES + currentSecretLength + INT_BYTES + previousSecretLength
+        + LONG_BYTES);
+    bb.putInt(DATA_VERSION);
+    bb.putInt(newSecretLength);
+    bb.put(newSecret);
+    bb.putInt(currentSecretLength);
+    bb.put(currentSecret);
+    bb.putInt(previousSecretLength);
+    if (previousSecretLength > 0) {
+      bb.put(previousSecret);
+    }
+    bb.putLong(nextRolloverDate);
+    return bb.array();
+  }
+
+  /**
+   * Pulls data from ZooKeeper.  If isInit is false, it will only parse the
+   * next secret and version.  If isInit is true, it will also parse the current
+   * and previous secrets, and the next rollover date; it will also init the
+   * secrets.  Hence, isInit should only be true on startup.
+   * @param isInit  see description above
+   */
+  private synchronized void pullFromZK(boolean isInit) {
+    try {
+      Stat stat = new Stat();
+      byte[] bytes = client.getData().storingStatIn(stat).forPath(path);
+      ByteBuffer bb = ByteBuffer.wrap(bytes);
+      int dataVersion = bb.getInt();
+      if (dataVersion > DATA_VERSION) {
+        throw new IllegalStateException("Cannot load data from ZooKeeper; it"
+                + "was written with a newer version");
+      }
+      int nextSecretLength = bb.getInt();
+      byte[] nextSecret = new byte[nextSecretLength];
+      bb.get(nextSecret);
+      this.nextSecret = nextSecret;
+      zkVersion = stat.getVersion();
+      if (isInit) {
+        int currentSecretLength = bb.getInt();
+        byte[] currentSecret = new byte[currentSecretLength];
+        bb.get(currentSecret);
+        int previousSecretLength = bb.getInt();
+        byte[] previousSecret = null;
+        if (previousSecretLength > 0) {
+          previousSecret = new byte[previousSecretLength];
+          bb.get(previousSecret);
+        }
+        super.initSecrets(currentSecret, previousSecret);
+        nextRolloverDate = bb.getLong();
+      }
+    } catch (Exception ex) {
+      LOG.error("An unexpected exception occurred while pulling data from"
+              + "ZooKeeper", ex);
+    }
+  }
+
+  private byte[] generateRandomSecret() {
+    return Long.toString(rand.nextLong()).getBytes();
+  }
+
+  /**
+   * This method creates the Curator client and connects to ZooKeeper.
+   * @param config configuration properties
+   * @return A Curator client
+   * @throws java.lang.Exception
+   */
+  protected CuratorFramework createCuratorClient(Properties config)
+          throws Exception {
+    String connectionString = config.getProperty(
+            ZOOKEEPER_CONNECTION_STRING, "localhost:2181");
+
+    RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);
+    ACLProvider aclProvider;
+    String authType = config.getProperty(ZOOKEEPER_AUTH_TYPE, "none");
+    if (authType.equals("sasl")) {
+      LOG.info("Connecting to ZooKeeper with SASL/Kerberos"
+              + "and using 'sasl' ACLs");
+      String principal = setJaasConfiguration(config);
+      System.setProperty(ZooKeeperSaslClient.LOGIN_CONTEXT_NAME_KEY,
+              "ZKSignerSecretProviderClient");
+      System.setProperty("zookeeper.authProvider.1",
+              "org.apache.zookeeper.server.auth.SASLAuthenticationProvider");
+      aclProvider = new SASLOwnerACLProvider(principal);
+    } else {  // "none"
+      LOG.info("Connecting to ZooKeeper without authentication");
+      aclProvider = new DefaultACLProvider();     // open to everyone
+    }
+    CuratorFramework cf = CuratorFrameworkFactory.builder()
+            .connectString(connectionString)
+            .retryPolicy(retryPolicy)
+            .aclProvider(aclProvider)
+            .build();
+    cf.start();
+    return cf;
+  }
+
+  private String setJaasConfiguration(Properties config) throws Exception {
+    String keytabFile = config.getProperty(ZOOKEEPER_KERBEROS_KEYTAB).trim();
+    if (keytabFile == null || keytabFile.length() == 0) {
+      throw new IllegalArgumentException(ZOOKEEPER_KERBEROS_KEYTAB
+              + " must be specified");
+    }
+    String principal = config.getProperty(ZOOKEEPER_KERBEROS_PRINCIPAL)
+            .trim();
+    if (principal == null || principal.length() == 0) {
+      throw new IllegalArgumentException(ZOOKEEPER_KERBEROS_PRINCIPAL
+              + " must be specified");
+    }
+
+    // This is equivalent to writing a jaas.conf file and setting the system
+    // property, "java.security.auth.login.config", to point to it
+    JaasConfiguration jConf =
+            new JaasConfiguration("Client", principal, keytabFile);
+    Configuration.setConfiguration(jConf);
+    return principal.split("[/@]")[0];
+  }
+
+  /**
+   * Simple implementation of an {@link ACLProvider} that simply returns an ACL
+   * that gives all permissions only to a single principal.
+   */
+  private static class SASLOwnerACLProvider implements ACLProvider {
+
+    private final List<ACL> saslACL;
+
+    private SASLOwnerACLProvider(String principal) {
+      this.saslACL = Collections.singletonList(
+              new ACL(Perms.ALL, new Id("sasl", principal)));
+    }
+
+    @Override
+    public List<ACL> getDefaultAcl() {
+      return saslACL;
+    }
+
+    @Override
+    public List<ACL> getAclForPath(String path) {
+      return saslACL;
+    }
+  }
+
+  /**
+   * Creates a programmatic version of a jaas.conf file. This can be used
+   * instead of writing a jaas.conf file and setting the system property,
+   * "java.security.auth.login.config", to point to that file. It is meant to be
+   * used for connecting to ZooKeeper.
+   */
+  @InterfaceAudience.Private
+  public static class JaasConfiguration extends Configuration {
+
+    private static AppConfigurationEntry[] entry;
+    private String entryName;
+
+    /**
+     * Add an entry to the jaas configuration with the passed in name,
+     * principal, and keytab. The other necessary options will be set for you.
+     *
+     * @param entryName The name of the entry (e.g. "Client")
+     * @param principal The principal of the user
+     * @param keytab The location of the keytab
+     */
+    public JaasConfiguration(String entryName, String principal, String keytab) {
+      this.entryName = entryName;
+      Map<String, String> options = new HashMap<String, String>();
+      options.put("keyTab", keytab);
+      options.put("principal", principal);
+      options.put("useKeyTab", "true");
+      options.put("storeKey", "true");
+      options.put("useTicketCache", "false");
+      options.put("refreshKrb5Config", "true");
+      String jaasEnvVar = System.getenv("HADOOP_JAAS_DEBUG");
+      if (jaasEnvVar != null && "true".equalsIgnoreCase(jaasEnvVar)) {
+        options.put("debug", "true");
+      }
+      entry = new AppConfigurationEntry[]{
+                  new AppConfigurationEntry(getKrb5LoginModuleName(),
+                  AppConfigurationEntry.LoginModuleControlFlag.REQUIRED,
+                  options)};
+    }
+
+    @Override
+    public AppConfigurationEntry[] getAppConfigurationEntry(String name) {
+      return (entryName.equals(name)) ? entry : null;
+    }
+
+    private String getKrb5LoginModuleName() {
+      String krb5LoginModuleName;
+      if (System.getProperty("java.vendor").contains("IBM")) {
+        krb5LoginModuleName = "com.ibm.security.auth.module.Krb5LoginModule";
+      } else {
+        krb5LoginModuleName = "com.sun.security.auth.module.Krb5LoginModule";
+      }
+      return krb5LoginModuleName;
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/hadoop/blob/932ae036/hadoop-common-project/hadoop-auth/src/site/apt/Configuration.apt.vm
----------------------------------------------------------------------
diff --git a/hadoop-common-project/hadoop-auth/src/site/apt/Configuration.apt.vm b/hadoop-common-project/hadoop-auth/src/site/apt/Configuration.apt.vm
index 6377393..88248e5 100644
--- a/hadoop-common-project/hadoop-auth/src/site/apt/Configuration.apt.vm
+++ b/hadoop-common-project/hadoop-auth/src/site/apt/Configuration.apt.vm
@@ -45,14 +45,14 @@ Configuration
   * <<<[PREFIX.]type>>>: the authentication type keyword (<<<simple>>> or
     <<<kerberos>>>) or a Authentication handler implementation.
 
-  * <<<[PREFIX.]signature.secret>>>: The secret to SHA-sign the generated
-    authentication tokens. If a secret is not provided a random secret is
-    generated at start up time. If using multiple web application instances
-    behind a load-balancer a secret must be set for the application to work
-    properly.
+  * <<<[PREFIX.]signature.secret>>>: When <<<signer.secret.provider>>> is set to
+    <<<string>>> or not specified, this is the value for the secret used to sign
+    the HTTP cookie.
 
   * <<<[PREFIX.]token.validity>>>: The validity -in seconds- of the generated
-    authentication token. The default value is <<<3600>>> seconds.
+    authentication token. The default value is <<<3600>>> seconds. This is also
+    used for the rollover interval when <<<signer.secret.provider>>> is set to
+    <<<random>>> or <<<zookeeper>>>.
 
   * <<<[PREFIX.]cookie.domain>>>: domain to use for the HTTP cookie that stores
     the authentication token.
@@ -60,6 +60,12 @@ Configuration
   * <<<[PREFIX.]cookie.path>>>: path to use for the HTTP cookie that stores the
     authentication token.
 
+  * <<<signer.secret.provider>>>: indicates the name of the SignerSecretProvider
+    class to use. Possible values are: <<<string>>>, <<<random>>>,
+    <<<zookeeper>>>, or a classname. If not specified, the <<<string>>>
+    implementation will be used; and failing that, the <<<random>>>
+    implementation will be used.
+
 ** Kerberos Configuration
 
   <<IMPORTANT>>: A KDC must be configured and running.
@@ -239,3 +245,133 @@ Configuration
     ...
 </web-app>
 +---+
+
+** SignerSecretProvider Configuration
+
+  The SignerSecretProvider is used to provide more advanced behaviors for the
+  secret used for signing the HTTP Cookies.
+
+  These are the relevant configuration properties:
+
+    * <<<signer.secret.provider>>>: indicates the name of the
+      SignerSecretProvider class to use. Possible values are: "string",
+      "random", "zookeeper", or a classname. If not specified, the "string"
+      implementation will be used; and failing that, the "random" implementation
+      will be used.
+
+    * <<<[PREFIX.]signature.secret>>>: When <<<signer.secret.provider>>> is set
+      to <<<string>>> or not specified, this is the value for the secret used to
+      sign the HTTP cookie.
+
+    * <<<[PREFIX.]token.validity>>>: The validity -in seconds- of the generated
+      authentication token. The default value is <<<3600>>> seconds. This is
+      also used for the rollover interval when <<<signer.secret.provider>>> is
+      set to <<<random>>> or <<<zookeeper>>>.
+
+  The following configuration properties are specific to the <<<zookeeper>>>
+  implementation:
+
+    * <<<signer.secret.provider.zookeeper.connection.string>>>: Indicates the
+      ZooKeeper connection string to connect with.
+
+    * <<<signer.secret.provider.zookeeper.path>>>: Indicates the ZooKeeper path
+      to use for storing and retrieving the secrets.  All servers
+      that need to coordinate their secret should point to the same path
+
+    * <<<signer.secret.provider.zookeeper.auth.type>>>: Indicates the auth type
+      to use.  Supported values are <<<none>>> and <<<sasl>>>.  The default
+      value is <<<none>>>.
+
+    * <<<signer.secret.provider.zookeeper.kerberos.keytab>>>: Set this to the
+      path with the Kerberos keytab file.  This is only required if using
+      Kerberos.
+
+    * <<<signer.secret.provider.zookeeper.kerberos.principal>>>: Set this to the
+      Kerberos principal to use.  This only required if using Kerberos.
+
+  <<Example>>:
+
++---+
+<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee">
+    ...
+
+    <filter>
+        <!-- AuthenticationHandler configs not shown -->
+        <init-param>
+            <param-name>signer.secret.provider</param-name>
+            <param-value>string</param-value>
+        </init-param>
+        <init-param>
+            <param-name>signature.secret</param-name>
+            <param-value>my_secret</param-value>
+        </init-param>
+    </filter>
+
+    ...
+</web-app>
++---+
+
+  <<Example>>:
+
++---+
+<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee">
+    ...
+
+    <filter>
+        <!-- AuthenticationHandler configs not shown -->
+        <init-param>
+            <param-name>signer.secret.provider</param-name>
+            <param-value>random</param-value>
+        </init-param>
+        <init-param>
+            <param-name>token.validity</param-name>
+            <param-value>30</param-value>
+        </init-param>
+    </filter>
+
+    ...
+</web-app>
++---+
+
+  <<Example>>:
+
++---+
+<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee">
+    ...
+
+    <filter>
+        <!-- AuthenticationHandler configs not shown -->
+        <init-param>
+            <param-name>signer.secret.provider</param-name>
+            <param-value>zookeeper</param-value>
+        </init-param>
+        <init-param>
+            <param-name>token.validity</param-name>
+            <param-value>30</param-value>
+        </init-param>
+        <init-param>
+            <param-name>signer.secret.provider.zookeeper.connection.string</param-name>
+            <param-value>zoo1:2181,zoo2:2181,zoo3:2181</param-value>
+        </init-param>
+        <init-param>
+            <param-name>signer.secret.provider.zookeeper.path</param-name>
+            <param-value>/myapp/secrets</param-value>
+        </init-param>
+        <init-param>
+            <param-name>signer.secret.provider.zookeeper.use.kerberos.acls</param-name>
+            <param-value>true</param-value>
+        </init-param>
+        <init-param>
+            <param-name>signer.secret.provider.zookeeper.kerberos.keytab</param-name>
+            <param-value>/tmp/auth.keytab</param-value>
+        </init-param>
+        <init-param>
+            <param-name>signer.secret.provider.zookeeper.kerberos.principal</param-name>
+            <param-value>HTTP/localhost@LOCALHOST</param-value>
+        </init-param>
+    </filter>
+
+    ...
+</web-app>
++---+
+

http://git-wip-us.apache.org/repos/asf/hadoop/blob/932ae036/hadoop-common-project/hadoop-auth/src/site/apt/index.apt.vm
----------------------------------------------------------------------
diff --git a/hadoop-common-project/hadoop-auth/src/site/apt/index.apt.vm b/hadoop-common-project/hadoop-auth/src/site/apt/index.apt.vm
index 6051f8c..bf85f7f 100644
--- a/hadoop-common-project/hadoop-auth/src/site/apt/index.apt.vm
+++ b/hadoop-common-project/hadoop-auth/src/site/apt/index.apt.vm
@@ -44,6 +44,11 @@ Hadoop Auth, Java HTTP SPNEGO ${project.version}
   Subsequent HTTP client requests presenting the signed HTTP Cookie have access
   to the protected resources until the HTTP Cookie expires.
 
+  The secret used to sign the HTTP Cookie has multiple implementations that
+  provide different behaviors, including a hardcoded secret string, a rolling
+  randomly generated secret, and a rolling randomly generated secret
+  synchronized between multiple servers using ZooKeeper.
+
 * User Documentation
 
   * {{{./Examples.html}Examples}}

http://git-wip-us.apache.org/repos/asf/hadoop/blob/932ae036/hadoop-common-project/hadoop-auth/src/test/java/org/apache/hadoop/security/authentication/server/TestAuthenticationFilter.java
----------------------------------------------------------------------
diff --git a/hadoop-common-project/hadoop-auth/src/test/java/org/apache/hadoop/security/authentication/server/TestAuthenticationFilter.java b/hadoop-common-project/hadoop-auth/src/test/java/org/apache/hadoop/security/authentication/server/TestAuthenticationFilter.java
index a9a5e8c..5d93fcf 100644
--- a/hadoop-common-project/hadoop-auth/src/test/java/org/apache/hadoop/security/authentication/server/TestAuthenticationFilter.java
+++ b/hadoop-common-project/hadoop-auth/src/test/java/org/apache/hadoop/security/authentication/server/TestAuthenticationFilter.java
@@ -162,7 +162,8 @@ public class TestAuthenticationFilter {
                                  AuthenticationFilter.AUTH_TOKEN_VALIDITY)).elements());
       ServletContext context = Mockito.mock(ServletContext.class);
       Mockito.when(context.getAttribute(
-          AuthenticationFilter.SIGNATURE_PROVIDER_ATTRIBUTE)).thenReturn(null);
+              AuthenticationFilter.SIGNER_SECRET_PROVIDER_ATTRIBUTE))
+              .thenReturn(null);
       Mockito.when(config.getServletContext()).thenReturn(context);
       filter.init(config);
       Assert.assertEquals(PseudoAuthenticationHandler.class, filter.getAuthenticationHandler().getClass());
@@ -186,7 +187,8 @@ public class TestAuthenticationFilter {
                                  AuthenticationFilter.SIGNATURE_SECRET)).elements());
       ServletContext context = Mockito.mock(ServletContext.class);
       Mockito.when(context.getAttribute(
-          AuthenticationFilter.SIGNATURE_PROVIDER_ATTRIBUTE)).thenReturn(null);
+              AuthenticationFilter.SIGNER_SECRET_PROVIDER_ATTRIBUTE))
+              .thenReturn(null);
       Mockito.when(config.getServletContext()).thenReturn(context);
       filter.init(config);
       Assert.assertFalse(filter.isRandomSecret());
@@ -206,10 +208,11 @@ public class TestAuthenticationFilter {
                                  AuthenticationFilter.SIGNATURE_SECRET)).elements());
       ServletContext context = Mockito.mock(ServletContext.class);
       Mockito.when(context.getAttribute(
-          AuthenticationFilter.SIGNATURE_PROVIDER_ATTRIBUTE)).thenReturn(
+          AuthenticationFilter.SIGNER_SECRET_PROVIDER_ATTRIBUTE)).thenReturn(
             new SignerSecretProvider() {
               @Override
-              public void init(Properties config, long tokenValidity) {
+              public void init(Properties config, ServletContext servletContext,
+                      long tokenValidity) {
               }
               @Override
               public byte[] getCurrentSecret() {
@@ -241,7 +244,8 @@ public class TestAuthenticationFilter {
                                  AuthenticationFilter.COOKIE_PATH)).elements());
       ServletContext context = Mockito.mock(ServletContext.class);
       Mockito.when(context.getAttribute(
-          AuthenticationFilter.SIGNATURE_PROVIDER_ATTRIBUTE)).thenReturn(null);
+              AuthenticationFilter.SIGNER_SECRET_PROVIDER_ATTRIBUTE))
+              .thenReturn(null);
       Mockito.when(config.getServletContext()).thenReturn(context);
       filter.init(config);
       Assert.assertEquals(".foo.com", filter.getCookieDomain());
@@ -265,7 +269,8 @@ public class TestAuthenticationFilter {
                         "management.operation.return")).elements());
       ServletContext context = Mockito.mock(ServletContext.class);
       Mockito.when(context.getAttribute(
-          AuthenticationFilter.SIGNATURE_PROVIDER_ATTRIBUTE)).thenReturn(null);
+              AuthenticationFilter.SIGNER_SECRET_PROVIDER_ATTRIBUTE))
+              .thenReturn(null);
       Mockito.when(config.getServletContext()).thenReturn(context);
       filter.init(config);
       Assert.assertTrue(DummyAuthenticationHandler.init);
@@ -304,7 +309,8 @@ public class TestAuthenticationFilter {
               AuthenticationFilter.AUTH_TOKEN_VALIDITY)).elements());
       ServletContext context = Mockito.mock(ServletContext.class);
       Mockito.when(context.getAttribute(
-          AuthenticationFilter.SIGNATURE_PROVIDER_ATTRIBUTE)).thenReturn(null);
+              AuthenticationFilter.SIGNER_SECRET_PROVIDER_ATTRIBUTE))
+              .thenReturn(null);
       Mockito.when(config.getServletContext()).thenReturn(context);
 
       filter.init(config);
@@ -330,7 +336,8 @@ public class TestAuthenticationFilter {
                         "management.operation.return")).elements());
       ServletContext context = Mockito.mock(ServletContext.class);
       Mockito.when(context.getAttribute(
-          AuthenticationFilter.SIGNATURE_PROVIDER_ATTRIBUTE)).thenReturn(null);
+              AuthenticationFilter.SIGNER_SECRET_PROVIDER_ATTRIBUTE))
+              .thenReturn(null);
       Mockito.when(config.getServletContext()).thenReturn(context);
       filter.init(config);
 
@@ -361,13 +368,20 @@ public class TestAuthenticationFilter {
                         "management.operation.return")).elements());
       ServletContext context = Mockito.mock(ServletContext.class);
       Mockito.when(context.getAttribute(
-          AuthenticationFilter.SIGNATURE_PROVIDER_ATTRIBUTE)).thenReturn(null);
+              AuthenticationFilter.SIGNER_SECRET_PROVIDER_ATTRIBUTE))
+              .thenReturn(null);
       Mockito.when(config.getServletContext()).thenReturn(context);
       filter.init(config);
 
       AuthenticationToken token = new AuthenticationToken("u", "p", DummyAuthenticationHandler.TYPE);
       token.setExpires(System.currentTimeMillis() + TOKEN_VALIDITY_SEC);
-      Signer signer = new Signer(new StringSignerSecretProvider("secret"));
+      StringSignerSecretProvider secretProvider
+              = new StringSignerSecretProvider();
+      Properties secretProviderProps = new Properties();
+      secretProviderProps.setProperty(
+              AuthenticationFilter.SIGNATURE_SECRET, "secret");
+      secretProvider.init(secretProviderProps, null, TOKEN_VALIDITY_SEC);
+      Signer signer = new Signer(secretProvider);
       String tokenSigned = signer.sign(token.toString());
 
       Cookie cookie = new Cookie(AuthenticatedURL.AUTH_COOKIE, tokenSigned);
@@ -398,14 +412,21 @@ public class TestAuthenticationFilter {
                         "management.operation.return")).elements());
       ServletContext context = Mockito.mock(ServletContext.class);
       Mockito.when(context.getAttribute(
-          AuthenticationFilter.SIGNATURE_PROVIDER_ATTRIBUTE)).thenReturn(null);
+              AuthenticationFilter.SIGNER_SECRET_PROVIDER_ATTRIBUTE))
+              .thenReturn(null);
       Mockito.when(config.getServletContext()).thenReturn(context);
       filter.init(config);
 
       AuthenticationToken token =
           new AuthenticationToken("u", "p", DummyAuthenticationHandler.TYPE);
       token.setExpires(System.currentTimeMillis() - TOKEN_VALIDITY_SEC);
-      Signer signer = new Signer(new StringSignerSecretProvider("secret"));
+      StringSignerSecretProvider secretProvider
+              = new StringSignerSecretProvider();
+      Properties secretProviderProps = new Properties();
+      secretProviderProps.setProperty(
+              AuthenticationFilter.SIGNATURE_SECRET, "secret");
+      secretProvider.init(secretProviderProps, null, TOKEN_VALIDITY_SEC);
+      Signer signer = new Signer(secretProvider);
       String tokenSigned = signer.sign(token.toString());
 
       Cookie cookie = new Cookie(AuthenticatedURL.AUTH_COOKIE, tokenSigned);
@@ -443,13 +464,20 @@ public class TestAuthenticationFilter {
                         "management.operation.return")).elements());
       ServletContext context = Mockito.mock(ServletContext.class);
       Mockito.when(context.getAttribute(
-          AuthenticationFilter.SIGNATURE_PROVIDER_ATTRIBUTE)).thenReturn(null);
+              AuthenticationFilter.SIGNER_SECRET_PROVIDER_ATTRIBUTE))
+              .thenReturn(null);
       Mockito.when(config.getServletContext()).thenReturn(context);
       filter.init(config);
 
       AuthenticationToken token = new AuthenticationToken("u", "p", "invalidtype");
       token.setExpires(System.currentTimeMillis() + TOKEN_VALIDITY_SEC);
-      Signer signer = new Signer(new StringSignerSecretProvider("secret"));
+      StringSignerSecretProvider secretProvider
+              = new StringSignerSecretProvider();
+      Properties secretProviderProps = new Properties();
+      secretProviderProps.setProperty(
+              AuthenticationFilter.SIGNATURE_SECRET, "secret");
+      secretProvider.init(secretProviderProps, null, TOKEN_VALIDITY_SEC);
+      Signer signer = new Signer(secretProvider);
       String tokenSigned = signer.sign(token.toString());
 
       Cookie cookie = new Cookie(AuthenticatedURL.AUTH_COOKIE, tokenSigned);
@@ -485,7 +513,8 @@ public class TestAuthenticationFilter {
                         "management.operation.return")).elements());
       ServletContext context = Mockito.mock(ServletContext.class);
       Mockito.when(context.getAttribute(
-          AuthenticationFilter.SIGNATURE_PROVIDER_ATTRIBUTE)).thenReturn(null);
+              AuthenticationFilter.SIGNER_SECRET_PROVIDER_ATTRIBUTE))
+              .thenReturn(null);
       Mockito.when(config.getServletContext()).thenReturn(context);
       filter.init(config);
 
@@ -538,7 +567,8 @@ public class TestAuthenticationFilter {
             ".return", "expired.token")).elements());
       ServletContext context = Mockito.mock(ServletContext.class);
       Mockito.when(context.getAttribute(
-          AuthenticationFilter.SIGNATURE_PROVIDER_ATTRIBUTE)).thenReturn(null);
+              AuthenticationFilter.SIGNER_SECRET_PROVIDER_ATTRIBUTE))
+              .thenReturn(null);
       Mockito.when(config.getServletContext()).thenReturn(context);
 
     if (withDomainPath) {
@@ -593,7 +623,13 @@ public class TestAuthenticationFilter {
         Mockito.verify(chain).doFilter(Mockito.any(ServletRequest.class),
                 Mockito.any(ServletResponse.class));
 
-        Signer signer = new Signer(new StringSignerSecretProvider("secret"));
+        StringSignerSecretProvider secretProvider
+                = new StringSignerSecretProvider();
+        Properties secretProviderProps = new Properties();
+        secretProviderProps.setProperty(
+                AuthenticationFilter.SIGNATURE_SECRET, "secret");
+        secretProvider.init(secretProviderProps, null, TOKEN_VALIDITY_SEC);
+        Signer signer = new Signer(secretProvider);
         String value = signer.verifyAndExtract(v);
         AuthenticationToken token = AuthenticationToken.parse(value);
         assertThat(token.getExpires(), not(0L));
@@ -662,7 +698,8 @@ public class TestAuthenticationFilter {
                         "management.operation.return")).elements());
       ServletContext context = Mockito.mock(ServletContext.class);
       Mockito.when(context.getAttribute(
-          AuthenticationFilter.SIGNATURE_PROVIDER_ATTRIBUTE)).thenReturn(null);
+              AuthenticationFilter.SIGNER_SECRET_PROVIDER_ATTRIBUTE))
+              .thenReturn(null);
       Mockito.when(config.getServletContext()).thenReturn(context);
       filter.init(config);
 
@@ -671,7 +708,13 @@ public class TestAuthenticationFilter {
 
       AuthenticationToken token = new AuthenticationToken("u", "p", "t");
       token.setExpires(System.currentTimeMillis() + TOKEN_VALIDITY_SEC);
-      Signer signer = new Signer(new StringSignerSecretProvider("secret"));
+      StringSignerSecretProvider secretProvider
+              = new StringSignerSecretProvider();
+      Properties secretProviderProps = new Properties();
+      secretProviderProps.setProperty(
+              AuthenticationFilter.SIGNATURE_SECRET, "secret");
+      secretProvider.init(secretProviderProps, null, TOKEN_VALIDITY_SEC);
+      Signer signer = new Signer(secretProvider);
       String tokenSigned = signer.sign(token.toString());
 
       Cookie cookie = new Cookie(AuthenticatedURL.AUTH_COOKIE, tokenSigned);
@@ -716,7 +759,8 @@ public class TestAuthenticationFilter {
                         "management.operation.return")).elements());
       ServletContext context = Mockito.mock(ServletContext.class);
       Mockito.when(context.getAttribute(
-          AuthenticationFilter.SIGNATURE_PROVIDER_ATTRIBUTE)).thenReturn(null);
+              AuthenticationFilter.SIGNER_SECRET_PROVIDER_ATTRIBUTE))
+              .thenReturn(null);
       Mockito.when(config.getServletContext()).thenReturn(context);
       filter.init(config);
 
@@ -783,7 +827,8 @@ public class TestAuthenticationFilter {
                         "management.operation.return")).elements());
       ServletContext context = Mockito.mock(ServletContext.class);
       Mockito.when(context.getAttribute(
-          AuthenticationFilter.SIGNATURE_PROVIDER_ATTRIBUTE)).thenReturn(null);
+              AuthenticationFilter.SIGNER_SECRET_PROVIDER_ATTRIBUTE))
+              .thenReturn(null);
       Mockito.when(config.getServletContext()).thenReturn(context);
       filter.init(config);
 
@@ -792,7 +837,13 @@ public class TestAuthenticationFilter {
 
       AuthenticationToken token = new AuthenticationToken("u", "p", DummyAuthenticationHandler.TYPE);
       token.setExpires(System.currentTimeMillis() - TOKEN_VALIDITY_SEC);
-      Signer signer = new Signer(new StringSignerSecretProvider(secret));
+      StringSignerSecretProvider secretProvider
+              = new StringSignerSecretProvider();
+      Properties secretProviderProps = new Properties();
+      secretProviderProps.setProperty(
+              AuthenticationFilter.SIGNATURE_SECRET, secret);
+      secretProvider.init(secretProviderProps, null, TOKEN_VALIDITY_SEC);
+      Signer signer = new Signer(secretProvider);
       String tokenSigned = signer.sign(token.toString());
 
       Cookie cookie = new Cookie(AuthenticatedURL.AUTH_COOKIE, tokenSigned);
@@ -854,7 +905,8 @@ public class TestAuthenticationFilter {
                         "management.operation.return")).elements());
       ServletContext context = Mockito.mock(ServletContext.class);
       Mockito.when(context.getAttribute(
-          AuthenticationFilter.SIGNATURE_PROVIDER_ATTRIBUTE)).thenReturn(null);
+              AuthenticationFilter.SIGNER_SECRET_PROVIDER_ATTRIBUTE))
+              .thenReturn(null);
       Mockito.when(config.getServletContext()).thenReturn(context);
       filter.init(config);
 
@@ -863,7 +915,13 @@ public class TestAuthenticationFilter {
 
       AuthenticationToken token = new AuthenticationToken("u", "p", "invalidtype");
       token.setExpires(System.currentTimeMillis() + TOKEN_VALIDITY_SEC);
-      Signer signer = new Signer(new StringSignerSecretProvider(secret));
+      StringSignerSecretProvider secretProvider
+              = new StringSignerSecretProvider();
+      Properties secretProviderProps = new Properties();
+      secretProviderProps.setProperty(
+              AuthenticationFilter.SIGNATURE_SECRET, secret);
+      secretProvider.init(secretProviderProps, null, TOKEN_VALIDITY_SEC);
+      Signer signer = new Signer(secretProvider);
       String tokenSigned = signer.sign(token.toString());
 
       Cookie cookie = new Cookie(AuthenticatedURL.AUTH_COOKIE, tokenSigned);
@@ -893,7 +951,8 @@ public class TestAuthenticationFilter {
                         "management.operation.return")).elements());
       ServletContext context = Mockito.mock(ServletContext.class);
       Mockito.when(context.getAttribute(
-          AuthenticationFilter.SIGNATURE_PROVIDER_ATTRIBUTE)).thenReturn(null);
+              AuthenticationFilter.SIGNER_SECRET_PROVIDER_ATTRIBUTE))
+              .thenReturn(null);
       Mockito.when(config.getServletContext()).thenReturn(context);
       filter.init(config);
 
@@ -914,7 +973,13 @@ public class TestAuthenticationFilter {
 
       AuthenticationToken token = new AuthenticationToken("u", "p", "t");
       token.setExpires(System.currentTimeMillis() + TOKEN_VALIDITY_SEC);
-      Signer signer = new Signer(new StringSignerSecretProvider("secret"));
+      StringSignerSecretProvider secretProvider
+              = new StringSignerSecretProvider();
+      Properties secretProviderProps = new Properties();
+      secretProviderProps.setProperty(
+              AuthenticationFilter.SIGNATURE_SECRET, "secret");
+      secretProvider.init(secretProviderProps, null, TOKEN_VALIDITY_SEC);
+      Signer signer = new Signer(secretProvider);
       String tokenSigned = signer.sign(token.toString());
       Cookie cookie = new Cookie(AuthenticatedURL.AUTH_COOKIE, tokenSigned);
       Mockito.when(request.getCookies()).thenReturn(new Cookie[]{cookie});

http://git-wip-us.apache.org/repos/asf/hadoop/blob/932ae036/hadoop-common-project/hadoop-auth/src/test/java/org/apache/hadoop/security/authentication/util/TestJaasConfiguration.java
----------------------------------------------------------------------
diff --git a/hadoop-common-project/hadoop-auth/src/test/java/org/apache/hadoop/security/authentication/util/TestJaasConfiguration.java b/hadoop-common-project/hadoop-auth/src/test/java/org/apache/hadoop/security/authentication/util/TestJaasConfiguration.java
new file mode 100644
index 0000000..2b70135
--- /dev/null
+++ b/hadoop-common-project/hadoop-auth/src/test/java/org/apache/hadoop/security/authentication/util/TestJaasConfiguration.java
@@ -0,0 +1,55 @@
+/**
+ * Licensed 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. See accompanying LICENSE file.
+ */
+package org.apache.hadoop.security.authentication.util;
+
+import java.util.Map;
+import javax.security.auth.login.AppConfigurationEntry;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class TestJaasConfiguration {
+
+  // We won't test actually using it to authenticate because that gets messy and
+  // may conflict with other tests; but we can test that it otherwise behaves
+  // correctly
+  @Test
+  public void test() throws Exception {
+    String krb5LoginModuleName;
+    if (System.getProperty("java.vendor").contains("IBM")) {
+      krb5LoginModuleName = "com.ibm.security.auth.module.Krb5LoginModule";
+    } else {
+      krb5LoginModuleName = "com.sun.security.auth.module.Krb5LoginModule";
+    }
+
+    ZKSignerSecretProvider.JaasConfiguration jConf =
+            new ZKSignerSecretProvider.JaasConfiguration("foo", "foo/localhost",
+            "/some/location/foo.keytab");
+    AppConfigurationEntry[] entries = jConf.getAppConfigurationEntry("bar");
+    Assert.assertNull(entries);
+    entries = jConf.getAppConfigurationEntry("foo");
+    Assert.assertEquals(1, entries.length);
+    AppConfigurationEntry entry = entries[0];
+    Assert.assertEquals(AppConfigurationEntry.LoginModuleControlFlag.REQUIRED,
+            entry.getControlFlag());
+    Assert.assertEquals(krb5LoginModuleName, entry.getLoginModuleName());
+    Map<String, ?> options = entry.getOptions();
+    Assert.assertEquals("/some/location/foo.keytab", options.get("keyTab"));
+    Assert.assertEquals("foo/localhost", options.get("principal"));
+    Assert.assertEquals("true", options.get("useKeyTab"));
+    Assert.assertEquals("true", options.get("storeKey"));
+    Assert.assertEquals("false", options.get("useTicketCache"));
+    Assert.assertEquals("true", options.get("refreshKrb5Config"));
+    Assert.assertEquals(6, options.size());
+  }
+}

http://git-wip-us.apache.org/repos/asf/hadoop/blob/932ae036/hadoop-common-project/hadoop-auth/src/test/java/org/apache/hadoop/security/authentication/util/TestRandomSignerSecretProvider.java
----------------------------------------------------------------------
diff --git a/hadoop-common-project/hadoop-auth/src/test/java/org/apache/hadoop/security/authentication/util/TestRandomSignerSecretProvider.java b/hadoop-common-project/hadoop-auth/src/test/java/org/apache/hadoop/security/authentication/util/TestRandomSignerSecretProvider.java
index c3384ad..41d4967 100644
--- a/hadoop-common-project/hadoop-auth/src/test/java/org/apache/hadoop/security/authentication/util/TestRandomSignerSecretProvider.java
+++ b/hadoop-common-project/hadoop-auth/src/test/java/org/apache/hadoop/security/authentication/util/TestRandomSignerSecretProvider.java
@@ -31,7 +31,7 @@ public class TestRandomSignerSecretProvider {
     RandomSignerSecretProvider secretProvider =
         new RandomSignerSecretProvider(seed);
     try {
-      secretProvider.init(null, rolloverFrequency);
+      secretProvider.init(null, null, rolloverFrequency);
 
       byte[] currentSecret = secretProvider.getCurrentSecret();
       byte[][] allSecrets = secretProvider.getAllSecrets();

http://git-wip-us.apache.org/repos/asf/hadoop/blob/932ae036/hadoop-common-project/hadoop-auth/src/test/java/org/apache/hadoop/security/authentication/util/TestRolloverSignerSecretProvider.java
----------------------------------------------------------------------
diff --git a/hadoop-common-project/hadoop-auth/src/test/java/org/apache/hadoop/security/authentication/util/TestRolloverSignerSecretProvider.java b/hadoop-common-project/hadoop-auth/src/test/java/org/apache/hadoop/security/authentication/util/TestRolloverSignerSecretProvider.java
index 2a2986a..1e40c42 100644
--- a/hadoop-common-project/hadoop-auth/src/test/java/org/apache/hadoop/security/authentication/util/TestRolloverSignerSecretProvider.java
+++ b/hadoop-common-project/hadoop-auth/src/test/java/org/apache/hadoop/security/authentication/util/TestRolloverSignerSecretProvider.java
@@ -28,7 +28,7 @@ public class TestRolloverSignerSecretProvider {
         new TRolloverSignerSecretProvider(
             new byte[][]{secret1, secret2, secret3});
     try {
-      secretProvider.init(null, rolloverFrequency);
+      secretProvider.init(null, null, rolloverFrequency);
 
       byte[] currentSecret = secretProvider.getCurrentSecret();
       byte[][] allSecrets = secretProvider.getAllSecrets();

http://git-wip-us.apache.org/repos/asf/hadoop/blob/932ae036/hadoop-common-project/hadoop-auth/src/test/java/org/apache/hadoop/security/authentication/util/TestSigner.java
----------------------------------------------------------------------
diff --git a/hadoop-common-project/hadoop-auth/src/test/java/org/apache/hadoop/security/authentication/util/TestSigner.java b/hadoop-common-project/hadoop-auth/src/test/java/org/apache/hadoop/security/authentication/util/TestSigner.java
index 1e2c960..c6a7710 100644
--- a/hadoop-common-project/hadoop-auth/src/test/java/org/apache/hadoop/security/authentication/util/TestSigner.java
+++ b/hadoop-common-project/hadoop-auth/src/test/java/org/apache/hadoop/security/authentication/util/TestSigner.java
@@ -14,6 +14,8 @@
 package org.apache.hadoop.security.authentication.util;
 
 import java.util.Properties;
+import javax.servlet.ServletContext;
+import org.apache.hadoop.security.authentication.server.AuthenticationFilter;
 import org.junit.Assert;
 import org.junit.Test;
 
@@ -21,7 +23,7 @@ public class TestSigner {
 
   @Test
   public void testNullAndEmptyString() throws Exception {
-    Signer signer = new Signer(new StringSignerSecretProvider("secret"));
+    Signer signer = new Signer(createStringSignerSecretProvider());
     try {
       signer.sign(null);
       Assert.fail();
@@ -42,7 +44,7 @@ public class TestSigner {
 
   @Test
   public void testSignature() throws Exception {
-    Signer signer = new Signer(new StringSignerSecretProvider("secret"));
+    Signer signer = new Signer(createStringSignerSecretProvider());
     String s1 = signer.sign("ok");
     String s2 = signer.sign("ok");
     String s3 = signer.sign("wrong");
@@ -52,7 +54,7 @@ public class TestSigner {
 
   @Test
   public void testVerify() throws Exception {
-    Signer signer = new Signer(new StringSignerSecretProvider("secret"));
+    Signer signer = new Signer(createStringSignerSecretProvider());
     String t = "test";
     String s = signer.sign(t);
     String e = signer.verifyAndExtract(s);
@@ -61,7 +63,7 @@ public class TestSigner {
 
   @Test
   public void testInvalidSignedText() throws Exception {
-    Signer signer = new Signer(new StringSignerSecretProvider("secret"));
+    Signer signer = new Signer(createStringSignerSecretProvider());
     try {
       signer.verifyAndExtract("test");
       Assert.fail();
@@ -74,7 +76,7 @@ public class TestSigner {
 
   @Test
   public void testTampering() throws Exception {
-    Signer signer = new Signer(new StringSignerSecretProvider("secret"));
+    Signer signer = new Signer(createStringSignerSecretProvider());
     String t = "test";
     String s = signer.sign(t);
     s += "x";
@@ -88,6 +90,14 @@ public class TestSigner {
     }
   }
 
+  private StringSignerSecretProvider createStringSignerSecretProvider() throws Exception {
+      StringSignerSecretProvider secretProvider = new StringSignerSecretProvider();
+      Properties secretProviderProps = new Properties();
+      secretProviderProps.setProperty(AuthenticationFilter.SIGNATURE_SECRET, "secret");
+      secretProvider.init(secretProviderProps, null, -1);
+      return secretProvider;
+  }
+
   @Test
   public void testMultipleSecrets() throws Exception {
     TestSignerSecretProvider secretProvider = new TestSignerSecretProvider();
@@ -128,7 +138,8 @@ public class TestSigner {
     private byte[] previousSecret;
 
     @Override
-    public void init(Properties config, long tokenValidity) {
+    public void init(Properties config, ServletContext servletContext,
+            long tokenValidity) {
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/hadoop/blob/932ae036/hadoop-common-project/hadoop-auth/src/test/java/org/apache/hadoop/security/authentication/util/TestStringSignerSecretProvider.java
----------------------------------------------------------------------
diff --git a/hadoop-common-project/hadoop-auth/src/test/java/org/apache/hadoop/security/authentication/util/TestStringSignerSecretProvider.java b/hadoop-common-project/hadoop-auth/src/test/java/org/apache/hadoop/security/authentication/util/TestStringSignerSecretProvider.java
index c117006..d8b044d 100644
--- a/hadoop-common-project/hadoop-auth/src/test/java/org/apache/hadoop/security/authentication/util/TestStringSignerSecretProvider.java
+++ b/hadoop-common-project/hadoop-auth/src/test/java/org/apache/hadoop/security/authentication/util/TestStringSignerSecretProvider.java
@@ -13,6 +13,8 @@
  */
 package org.apache.hadoop.security.authentication.util;
 
+import java.util.Properties;
+import org.apache.hadoop.security.authentication.server.AuthenticationFilter;
 import org.junit.Assert;
 import org.junit.Test;
 
@@ -22,8 +24,11 @@ public class TestStringSignerSecretProvider {
   public void testGetSecrets() throws Exception {
     String secretStr = "secret";
     StringSignerSecretProvider secretProvider
-        = new StringSignerSecretProvider(secretStr);
-    secretProvider.init(null, -1);
+            = new StringSignerSecretProvider();
+    Properties secretProviderProps = new Properties();
+    secretProviderProps.setProperty(
+            AuthenticationFilter.SIGNATURE_SECRET, "secret");
+    secretProvider.init(secretProviderProps, null, -1);
     byte[] secretBytes = secretStr.getBytes();
     Assert.assertArrayEquals(secretBytes, secretProvider.getCurrentSecret());
     byte[][] allSecrets = secretProvider.getAllSecrets();


Mime
View raw message