Author: tucu
Date: Tue Aug 5 21:25:22 2014
New Revision: 1616006
URL: http://svn.apache.org/r1616006
Log:
HADOOP-10791. AuthenticationFilter should support externalizing the secret for
signing and provide rotation support. (rkanter via tucu)
Added:
hadoop/common/branches/branch-2/hadoop-common-project/hadoop-auth/dev-support/
- copied from r1616005,
hadoop/common/trunk/hadoop-common-project/hadoop-auth/dev-support/
hadoop/common/branches/branch-2/hadoop-common-project/hadoop-auth/src/main/java/org/apache/hadoop/security/authentication/util/RandomSignerSecretProvider.java
- copied unchanged from r1616005,
hadoop/common/trunk/hadoop-common-project/hadoop-auth/src/main/java/org/apache/hadoop/security/authentication/util/RandomSignerSecretProvider.java
hadoop/common/branches/branch-2/hadoop-common-project/hadoop-auth/src/main/java/org/apache/hadoop/security/authentication/util/RolloverSignerSecretProvider.java
- copied unchanged from r1616005,
hadoop/common/trunk/hadoop-common-project/hadoop-auth/src/main/java/org/apache/hadoop/security/authentication/util/RolloverSignerSecretProvider.java
hadoop/common/branches/branch-2/hadoop-common-project/hadoop-auth/src/main/java/org/apache/hadoop/security/authentication/util/SignerSecretProvider.java
- copied unchanged from r1616005,
hadoop/common/trunk/hadoop-common-project/hadoop-auth/src/main/java/org/apache/hadoop/security/authentication/util/SignerSecretProvider.java
hadoop/common/branches/branch-2/hadoop-common-project/hadoop-auth/src/main/java/org/apache/hadoop/security/authentication/util/StringSignerSecretProvider.java
- copied unchanged from r1616005,
hadoop/common/trunk/hadoop-common-project/hadoop-auth/src/main/java/org/apache/hadoop/security/authentication/util/StringSignerSecretProvider.java
hadoop/common/branches/branch-2/hadoop-common-project/hadoop-auth/src/test/java/org/apache/hadoop/security/authentication/util/TestRandomSignerSecretProvider.java
- copied unchanged from r1616005,
hadoop/common/trunk/hadoop-common-project/hadoop-auth/src/test/java/org/apache/hadoop/security/authentication/util/TestRandomSignerSecretProvider.java
hadoop/common/branches/branch-2/hadoop-common-project/hadoop-auth/src/test/java/org/apache/hadoop/security/authentication/util/TestRolloverSignerSecretProvider.java
- copied unchanged from r1616005,
hadoop/common/trunk/hadoop-common-project/hadoop-auth/src/test/java/org/apache/hadoop/security/authentication/util/TestRolloverSignerSecretProvider.java
hadoop/common/branches/branch-2/hadoop-common-project/hadoop-auth/src/test/java/org/apache/hadoop/security/authentication/util/TestStringSignerSecretProvider.java
- copied unchanged from r1616005,
hadoop/common/trunk/hadoop-common-project/hadoop-auth/src/test/java/org/apache/hadoop/security/authentication/util/TestStringSignerSecretProvider.java
Modified:
hadoop/common/branches/branch-2/hadoop-common-project/hadoop-auth/pom.xml
hadoop/common/branches/branch-2/hadoop-common-project/hadoop-auth/src/main/java/org/apache/hadoop/security/authentication/server/AuthenticationFilter.java
hadoop/common/branches/branch-2/hadoop-common-project/hadoop-auth/src/main/java/org/apache/hadoop/security/authentication/util/Signer.java
hadoop/common/branches/branch-2/hadoop-common-project/hadoop-auth/src/test/java/org/apache/hadoop/security/authentication/server/TestAuthenticationFilter.java
hadoop/common/branches/branch-2/hadoop-common-project/hadoop-auth/src/test/java/org/apache/hadoop/security/authentication/util/TestSigner.java
hadoop/common/branches/branch-2/hadoop-common-project/hadoop-common/CHANGES.txt
Modified:
hadoop/common/branches/branch-2/hadoop-common-project/hadoop-auth/pom.xml
URL:
http://svn.apache.org/viewvc/hadoop/common/branches/branch-2/hadoop-common-project/hadoop-auth/pom.xml?rev=1616006&r1=1616005&r2=1616006&view=diff
==============================================================================
--- hadoop/common/branches/branch-2/hadoop-common-project/hadoop-auth/pom.xml
(original)
+++ hadoop/common/branches/branch-2/hadoop-common-project/hadoop-auth/pom.xml
Tue Aug 5 21:25:22 2014
@@ -155,6 +155,13 @@
</execution>
</executions>
</plugin>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>findbugs-maven-plugin</artifactId>
+ <configuration>
+
<excludeFilterFile>${basedir}/dev-support/findbugsExcludeFile.xml</excludeFilterFile>
+ </configuration>
+ </plugin>
</plugins>
</build>
Modified:
hadoop/common/branches/branch-2/hadoop-common-project/hadoop-auth/src/main/java/org/apache/hadoop/security/authentication/server/AuthenticationFilter.java
URL:
http://svn.apache.org/viewvc/hadoop/common/branches/branch-2/hadoop-common-project/hadoop-auth/src/main/java/org/apache/hadoop/security/authentication/server/AuthenticationFilter.java?rev=1616006&r1=1616005&r2=1616006&view=diff
==============================================================================
---
hadoop/common/branches/branch-2/hadoop-common-project/hadoop-auth/src/main/java/org/apache/hadoop/security/authentication/server/AuthenticationFilter.java
(original)
+++
hadoop/common/branches/branch-2/hadoop-common-project/hadoop-auth/src/main/java/org/apache/hadoop/security/authentication/server/AuthenticationFilter.java
Tue Aug 5 21:25:22 2014
@@ -19,6 +19,9 @@ import org.apache.hadoop.security.authen
import
org.apache.hadoop.security.authentication.client.AuthenticationException;
import org.apache.hadoop.security.authentication.util.Signer;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -107,11 +110,28 @@ public class AuthenticationFilter implem
*/
public static final String COOKIE_PATH = "cookie.path";
- private static final Random RAN = new Random();
+ /**
+ * 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.
+ */
+ public static final String SIGNER_SECRET_PROVIDER_CLASS =
+ "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.
+ */
+ public static final String SIGNATURE_PROVIDER_ATTRIBUTE =
+ "org.apache.hadoop.security.authentication.util.SignerSecretProvider";
private Signer signer;
+ private SignerSecretProvider secretProvider;
private AuthenticationHandler authHandler;
private boolean randomSecret;
+ private boolean customSecretProvider;
private long validity;
private String cookieDomain;
private String cookiePath;
@@ -159,14 +179,46 @@ public class AuthenticationFilter implem
} catch (IllegalAccessException ex) {
throw new ServletException(ex);
}
- String signatureSecret = config.getProperty(configPrefix +
SIGNATURE_SECRET);
- if (signatureSecret == null) {
- signatureSecret = Long.toString(RAN.nextLong());
- randomSecret = true;
- LOG.warn("'signature.secret' configuration not set, using a random value
as secret");
+
+ validity = Long.parseLong(config.getProperty(AUTH_TOKEN_VALIDITY, "36000"))
+ * 1000; //10 hours
+ secretProvider = (SignerSecretProvider) filterConfig.getServletContext().
+ getAttribute(SIGNATURE_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);
+ }
+ }
+ try {
+ secretProvider.init(config, validity);
+ } catch (Exception ex) {
+ throw new ServletException(ex);
+ }
+ } else {
+ customSecretProvider = true;
}
- signer = new Signer(signatureSecret.getBytes());
- validity = Long.parseLong(config.getProperty(AUTH_TOKEN_VALIDITY,
"36000")) * 1000; //10 hours
+ signer = new Signer(secretProvider);
cookieDomain = config.getProperty(COOKIE_DOMAIN, null);
cookiePath = config.getProperty(COOKIE_PATH, null);
@@ -191,6 +243,15 @@ public class AuthenticationFilter implem
}
/**
+ * Returns if a custom implementation of a SignerSecretProvider is being
used.
+ *
+ * @return if a custom implementation of a SignerSecretProvider is being
used.
+ */
+ protected boolean isCustomSignerSecretProvider() {
+ return customSecretProvider;
+ }
+
+ /**
* Returns the validity time of the generated tokens.
*
* @return the validity time of the generated tokens, in seconds.
@@ -228,6 +289,9 @@ public class AuthenticationFilter implem
authHandler.destroy();
authHandler = null;
}
+ if (secretProvider != null) {
+ secretProvider.destroy();
+ }
}
/**
Modified:
hadoop/common/branches/branch-2/hadoop-common-project/hadoop-auth/src/main/java/org/apache/hadoop/security/authentication/util/Signer.java
URL:
http://svn.apache.org/viewvc/hadoop/common/branches/branch-2/hadoop-common-project/hadoop-auth/src/main/java/org/apache/hadoop/security/authentication/util/Signer.java?rev=1616006&r1=1616005&r2=1616006&view=diff
==============================================================================
---
hadoop/common/branches/branch-2/hadoop-common-project/hadoop-auth/src/main/java/org/apache/hadoop/security/authentication/util/Signer.java
(original)
+++
hadoop/common/branches/branch-2/hadoop-common-project/hadoop-auth/src/main/java/org/apache/hadoop/security/authentication/util/Signer.java
Tue Aug 5 21:25:22 2014
@@ -24,18 +24,19 @@ import java.security.NoSuchAlgorithmExce
public class Signer {
private static final String SIGNATURE = "&s=";
- private byte[] secret;
+ private SignerSecretProvider secretProvider;
/**
- * Creates a Signer instance using the specified secret.
+ * Creates a Signer instance using the specified SignerSecretProvider. The
+ * SignerSecretProvider should already be initialized.
*
- * @param secret secret to use for creating the digest.
+ * @param secretProvider The SignerSecretProvider to use
*/
- public Signer(byte[] secret) {
- if (secret == null) {
- throw new IllegalArgumentException("secret cannot be NULL");
+ public Signer(SignerSecretProvider secretProvider) {
+ if (secretProvider == null) {
+ throw new IllegalArgumentException("secretProvider cannot be NULL");
}
- this.secret = secret.clone();
+ this.secretProvider = secretProvider;
}
/**
@@ -47,11 +48,12 @@ public class Signer {
*
* @return the signed string.
*/
- public String sign(String str) {
+ public synchronized String sign(String str) {
if (str == null || str.length() == 0) {
throw new IllegalArgumentException("NULL or empty string to sign");
}
- String signature = computeSignature(str);
+ byte[] secret = secretProvider.getCurrentSecret();
+ String signature = computeSignature(secret, str);
return str + SIGNATURE + signature;
}
@@ -71,21 +73,19 @@ public class Signer {
}
String originalSignature = signedStr.substring(index + SIGNATURE.length());
String rawValue = signedStr.substring(0, index);
- String currentSignature = computeSignature(rawValue);
- if (!originalSignature.equals(currentSignature)) {
- throw new SignerException("Invalid signature");
- }
+ checkSignatures(rawValue, originalSignature);
return rawValue;
}
/**
* Returns then signature of a string.
*
+ * @param secret The secret to use
* @param str string to sign.
*
* @return the signature for the string.
*/
- protected String computeSignature(String str) {
+ protected String computeSignature(byte[] secret, String str) {
try {
MessageDigest md = MessageDigest.getInstance("SHA");
md.update(str.getBytes());
@@ -97,4 +97,22 @@ public class Signer {
}
}
+ protected void checkSignatures(String rawValue, String originalSignature)
+ throws SignerException {
+ boolean isValid = false;
+ byte[][] secrets = secretProvider.getAllSecrets();
+ for (int i = 0; i < secrets.length; i++) {
+ byte[] secret = secrets[i];
+ if (secret != null) {
+ String currentSignature = computeSignature(secret, rawValue);
+ if (originalSignature.equals(currentSignature)) {
+ isValid = true;
+ break;
+ }
+ }
+ }
+ if (!isValid) {
+ throw new SignerException("Invalid signature");
+ }
+ }
}
Modified:
hadoop/common/branches/branch-2/hadoop-common-project/hadoop-auth/src/test/java/org/apache/hadoop/security/authentication/server/TestAuthenticationFilter.java
URL:
http://svn.apache.org/viewvc/hadoop/common/branches/branch-2/hadoop-common-project/hadoop-auth/src/test/java/org/apache/hadoop/security/authentication/server/TestAuthenticationFilter.java?rev=1616006&r1=1616005&r2=1616006&view=diff
==============================================================================
---
hadoop/common/branches/branch-2/hadoop-common-project/hadoop-auth/src/test/java/org/apache/hadoop/security/authentication/server/TestAuthenticationFilter.java
(original)
+++
hadoop/common/branches/branch-2/hadoop-common-project/hadoop-auth/src/test/java/org/apache/hadoop/security/authentication/server/TestAuthenticationFilter.java
Tue Aug 5 21:25:22 2014
@@ -23,6 +23,7 @@ import java.util.Vector;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
+import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
@@ -33,6 +34,8 @@ import javax.servlet.http.HttpServletRes
import org.apache.hadoop.security.authentication.client.AuthenticatedURL;
import
org.apache.hadoop.security.authentication.client.AuthenticationException;
import org.apache.hadoop.security.authentication.util.Signer;
+import org.apache.hadoop.security.authentication.util.SignerSecretProvider;
+import
org.apache.hadoop.security.authentication.util.StringSignerSecretProvider;
import org.junit.Assert;
import org.junit.Test;
import org.mockito.Mockito;
@@ -157,9 +160,14 @@ public class TestAuthenticationFilter {
Mockito.when(config.getInitParameterNames()).thenReturn(
new Vector<String>(Arrays.asList(AuthenticationFilter.AUTH_TYPE,
AuthenticationFilter.AUTH_TOKEN_VALIDITY)).elements());
+ ServletContext context = Mockito.mock(ServletContext.class);
+ Mockito.when(context.getAttribute(
+ AuthenticationFilter.SIGNATURE_PROVIDER_ATTRIBUTE)).thenReturn(null);
+ Mockito.when(config.getServletContext()).thenReturn(context);
filter.init(config);
Assert.assertEquals(PseudoAuthenticationHandler.class,
filter.getAuthenticationHandler().getClass());
Assert.assertTrue(filter.isRandomSecret());
+ Assert.assertFalse(filter.isCustomSignerSecretProvider());
Assert.assertNull(filter.getCookieDomain());
Assert.assertNull(filter.getCookiePath());
Assert.assertEquals(TOKEN_VALIDITY_SEC, filter.getValidity());
@@ -167,6 +175,26 @@ public class TestAuthenticationFilter {
filter.destroy();
}
+ // string secret
+ filter = new AuthenticationFilter();
+ try {
+ FilterConfig config = Mockito.mock(FilterConfig.class);
+
Mockito.when(config.getInitParameter(AuthenticationFilter.AUTH_TYPE)).thenReturn("simple");
+
Mockito.when(config.getInitParameter(AuthenticationFilter.SIGNATURE_SECRET)).thenReturn("secret");
+ Mockito.when(config.getInitParameterNames()).thenReturn(
+ new Vector<String>(Arrays.asList(AuthenticationFilter.AUTH_TYPE,
+
AuthenticationFilter.SIGNATURE_SECRET)).elements());
+ ServletContext context = Mockito.mock(ServletContext.class);
+ Mockito.when(context.getAttribute(
+ AuthenticationFilter.SIGNATURE_PROVIDER_ATTRIBUTE)).thenReturn(null);
+ Mockito.when(config.getServletContext()).thenReturn(context);
+ filter.init(config);
+ Assert.assertFalse(filter.isRandomSecret());
+ Assert.assertFalse(filter.isCustomSignerSecretProvider());
+ } finally {
+ filter.destroy();
+ }
+
// custom secret
filter = new AuthenticationFilter();
try {
@@ -176,8 +204,26 @@ public class TestAuthenticationFilter {
Mockito.when(config.getInitParameterNames()).thenReturn(
new Vector<String>(Arrays.asList(AuthenticationFilter.AUTH_TYPE,
AuthenticationFilter.SIGNATURE_SECRET)).elements());
+ ServletContext context = Mockito.mock(ServletContext.class);
+ Mockito.when(context.getAttribute(
+ AuthenticationFilter.SIGNATURE_PROVIDER_ATTRIBUTE)).thenReturn(
+ new SignerSecretProvider() {
+ @Override
+ public void init(Properties config, long tokenValidity) {
+ }
+ @Override
+ public byte[] getCurrentSecret() {
+ return null;
+ }
+ @Override
+ public byte[][] getAllSecrets() {
+ return null;
+ }
+ });
+ Mockito.when(config.getServletContext()).thenReturn(context);
filter.init(config);
Assert.assertFalse(filter.isRandomSecret());
+ Assert.assertTrue(filter.isCustomSignerSecretProvider());
} finally {
filter.destroy();
}
@@ -193,6 +239,10 @@ public class TestAuthenticationFilter {
new Vector<String>(Arrays.asList(AuthenticationFilter.AUTH_TYPE,
AuthenticationFilter.COOKIE_DOMAIN,
AuthenticationFilter.COOKIE_PATH)).elements());
+ ServletContext context = Mockito.mock(ServletContext.class);
+ Mockito.when(context.getAttribute(
+ AuthenticationFilter.SIGNATURE_PROVIDER_ATTRIBUTE)).thenReturn(null);
+ Mockito.when(config.getServletContext()).thenReturn(context);
filter.init(config);
Assert.assertEquals(".foo.com", filter.getCookieDomain());
Assert.assertEquals("/bar", filter.getCookiePath());
@@ -213,6 +263,10 @@ public class TestAuthenticationFilter {
new Vector<String>(
Arrays.asList(AuthenticationFilter.AUTH_TYPE,
"management.operation.return")).elements());
+ ServletContext context = Mockito.mock(ServletContext.class);
+ Mockito.when(context.getAttribute(
+ AuthenticationFilter.SIGNATURE_PROVIDER_ATTRIBUTE)).thenReturn(null);
+ Mockito.when(config.getServletContext()).thenReturn(context);
filter.init(config);
Assert.assertTrue(DummyAuthenticationHandler.init);
} finally {
@@ -248,6 +302,10 @@ public class TestAuthenticationFilter {
Mockito.when(config.getInitParameterNames()).thenReturn(
new Vector<String>(Arrays.asList(AuthenticationFilter.AUTH_TYPE,
AuthenticationFilter.AUTH_TOKEN_VALIDITY)).elements());
+ ServletContext context = Mockito.mock(ServletContext.class);
+ Mockito.when(context.getAttribute(
+ AuthenticationFilter.SIGNATURE_PROVIDER_ATTRIBUTE)).thenReturn(null);
+ Mockito.when(config.getServletContext()).thenReturn(context);
filter.init(config);
Assert.assertEquals(PseudoAuthenticationHandler.class,
@@ -270,6 +328,10 @@ public class TestAuthenticationFilter {
new Vector<String>(
Arrays.asList(AuthenticationFilter.AUTH_TYPE,
"management.operation.return")).elements());
+ ServletContext context = Mockito.mock(ServletContext.class);
+ Mockito.when(context.getAttribute(
+ AuthenticationFilter.SIGNATURE_PROVIDER_ATTRIBUTE)).thenReturn(null);
+ Mockito.when(config.getServletContext()).thenReturn(context);
filter.init(config);
HttpServletRequest request = Mockito.mock(HttpServletRequest.class);
@@ -297,11 +359,15 @@ public class TestAuthenticationFilter {
Arrays.asList(AuthenticationFilter.AUTH_TYPE,
AuthenticationFilter.SIGNATURE_SECRET,
"management.operation.return")).elements());
+ ServletContext context = Mockito.mock(ServletContext.class);
+ Mockito.when(context.getAttribute(
+ AuthenticationFilter.SIGNATURE_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("secret".getBytes());
+ Signer signer = new Signer(new StringSignerSecretProvider("secret"));
String tokenSigned = signer.sign(token.toString());
Cookie cookie = new Cookie(AuthenticatedURL.AUTH_COOKIE, tokenSigned);
@@ -330,12 +396,16 @@ public class TestAuthenticationFilter {
Arrays.asList(AuthenticationFilter.AUTH_TYPE,
AuthenticationFilter.SIGNATURE_SECRET,
"management.operation.return")).elements());
+ ServletContext context = Mockito.mock(ServletContext.class);
+ Mockito.when(context.getAttribute(
+ AuthenticationFilter.SIGNATURE_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("secret".getBytes());
+ Signer signer = new Signer(new StringSignerSecretProvider("secret"));
String tokenSigned = signer.sign(token.toString());
Cookie cookie = new Cookie(AuthenticatedURL.AUTH_COOKIE, tokenSigned);
@@ -371,11 +441,15 @@ public class TestAuthenticationFilter {
Arrays.asList(AuthenticationFilter.AUTH_TYPE,
AuthenticationFilter.SIGNATURE_SECRET,
"management.operation.return")).elements());
+ ServletContext context = Mockito.mock(ServletContext.class);
+ Mockito.when(context.getAttribute(
+ AuthenticationFilter.SIGNATURE_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("secret".getBytes());
+ Signer signer = new Signer(new StringSignerSecretProvider("secret"));
String tokenSigned = signer.sign(token.toString());
Cookie cookie = new Cookie(AuthenticatedURL.AUTH_COOKIE, tokenSigned);
@@ -409,6 +483,10 @@ public class TestAuthenticationFilter {
new Vector<String>(
Arrays.asList(AuthenticationFilter.AUTH_TYPE,
"management.operation.return")).elements());
+ ServletContext context = Mockito.mock(ServletContext.class);
+ Mockito.when(context.getAttribute(
+ AuthenticationFilter.SIGNATURE_PROVIDER_ATTRIBUTE)).thenReturn(null);
+ Mockito.when(config.getServletContext()).thenReturn(context);
filter.init(config);
HttpServletRequest request = Mockito.mock(HttpServletRequest.class);
@@ -458,6 +536,10 @@ public class TestAuthenticationFilter {
AuthenticationFilter.AUTH_TOKEN_VALIDITY,
AuthenticationFilter.SIGNATURE_SECRET, "management.operation" +
".return", "expired.token")).elements());
+ ServletContext context = Mockito.mock(ServletContext.class);
+ Mockito.when(context.getAttribute(
+ AuthenticationFilter.SIGNATURE_PROVIDER_ATTRIBUTE)).thenReturn(null);
+ Mockito.when(config.getServletContext()).thenReturn(context);
if (withDomainPath) {
Mockito.when(config.getInitParameter(AuthenticationFilter
@@ -511,7 +593,7 @@ public class TestAuthenticationFilter {
Mockito.verify(chain).doFilter(Mockito.any(ServletRequest.class),
Mockito.any(ServletResponse.class));
- Signer signer = new Signer("secret".getBytes());
+ Signer signer = new Signer(new StringSignerSecretProvider("secret"));
String value = signer.verifyAndExtract(v);
AuthenticationToken token = AuthenticationToken.parse(value);
assertThat(token.getExpires(), not(0L));
@@ -578,6 +660,10 @@ public class TestAuthenticationFilter {
new Vector<String>(
Arrays.asList(AuthenticationFilter.AUTH_TYPE,
"management.operation.return")).elements());
+ ServletContext context = Mockito.mock(ServletContext.class);
+ Mockito.when(context.getAttribute(
+ AuthenticationFilter.SIGNATURE_PROVIDER_ATTRIBUTE)).thenReturn(null);
+ Mockito.when(config.getServletContext()).thenReturn(context);
filter.init(config);
HttpServletRequest request = Mockito.mock(HttpServletRequest.class);
@@ -585,7 +671,7 @@ public class TestAuthenticationFilter {
AuthenticationToken token = new AuthenticationToken("u", "p", "t");
token.setExpires(System.currentTimeMillis() + TOKEN_VALIDITY_SEC);
- Signer signer = new Signer("secret".getBytes());
+ Signer signer = new Signer(new StringSignerSecretProvider("secret"));
String tokenSigned = signer.sign(token.toString());
Cookie cookie = new Cookie(AuthenticatedURL.AUTH_COOKIE, tokenSigned);
@@ -628,6 +714,10 @@ public class TestAuthenticationFilter {
new Vector<String>(
Arrays.asList(AuthenticationFilter.AUTH_TYPE,
"management.operation.return")).elements());
+ ServletContext context = Mockito.mock(ServletContext.class);
+ Mockito.when(context.getAttribute(
+ AuthenticationFilter.SIGNATURE_PROVIDER_ATTRIBUTE)).thenReturn(null);
+ Mockito.when(config.getServletContext()).thenReturn(context);
filter.init(config);
HttpServletRequest request = Mockito.mock(HttpServletRequest.class);
@@ -691,6 +781,10 @@ public class TestAuthenticationFilter {
Arrays.asList(AuthenticationFilter.AUTH_TYPE,
AuthenticationFilter.SIGNATURE_SECRET,
"management.operation.return")).elements());
+ ServletContext context = Mockito.mock(ServletContext.class);
+ Mockito.when(context.getAttribute(
+ AuthenticationFilter.SIGNATURE_PROVIDER_ATTRIBUTE)).thenReturn(null);
+ Mockito.when(config.getServletContext()).thenReturn(context);
filter.init(config);
HttpServletRequest request = Mockito.mock(HttpServletRequest.class);
@@ -698,7 +792,7 @@ public class TestAuthenticationFilter {
AuthenticationToken token = new AuthenticationToken("u", "p",
DummyAuthenticationHandler.TYPE);
token.setExpires(System.currentTimeMillis() - TOKEN_VALIDITY_SEC);
- Signer signer = new Signer(secret.getBytes());
+ Signer signer = new Signer(new StringSignerSecretProvider(secret));
String tokenSigned = signer.sign(token.toString());
Cookie cookie = new Cookie(AuthenticatedURL.AUTH_COOKIE, tokenSigned);
@@ -758,6 +852,10 @@ public class TestAuthenticationFilter {
Arrays.asList(AuthenticationFilter.AUTH_TYPE,
AuthenticationFilter.SIGNATURE_SECRET,
"management.operation.return")).elements());
+ ServletContext context = Mockito.mock(ServletContext.class);
+ Mockito.when(context.getAttribute(
+ AuthenticationFilter.SIGNATURE_PROVIDER_ATTRIBUTE)).thenReturn(null);
+ Mockito.when(config.getServletContext()).thenReturn(context);
filter.init(config);
HttpServletRequest request = Mockito.mock(HttpServletRequest.class);
@@ -765,7 +863,7 @@ public class TestAuthenticationFilter {
AuthenticationToken token = new AuthenticationToken("u", "p",
"invalidtype");
token.setExpires(System.currentTimeMillis() + TOKEN_VALIDITY_SEC);
- Signer signer = new Signer(secret.getBytes());
+ Signer signer = new Signer(new StringSignerSecretProvider(secret));
String tokenSigned = signer.sign(token.toString());
Cookie cookie = new Cookie(AuthenticatedURL.AUTH_COOKIE, tokenSigned);
@@ -793,6 +891,10 @@ public class TestAuthenticationFilter {
new Vector<String>(
Arrays.asList(AuthenticationFilter.AUTH_TYPE,
"management.operation.return")).elements());
+ ServletContext context = Mockito.mock(ServletContext.class);
+ Mockito.when(context.getAttribute(
+ AuthenticationFilter.SIGNATURE_PROVIDER_ATTRIBUTE)).thenReturn(null);
+ Mockito.when(config.getServletContext()).thenReturn(context);
filter.init(config);
HttpServletRequest request = Mockito.mock(HttpServletRequest.class);
@@ -812,7 +914,7 @@ public class TestAuthenticationFilter {
AuthenticationToken token = new AuthenticationToken("u", "p", "t");
token.setExpires(System.currentTimeMillis() + TOKEN_VALIDITY_SEC);
- Signer signer = new Signer("secret".getBytes());
+ Signer signer = new Signer(new StringSignerSecretProvider("secret"));
String tokenSigned = signer.sign(token.toString());
Cookie cookie = new Cookie(AuthenticatedURL.AUTH_COOKIE, tokenSigned);
Mockito.when(request.getCookies()).thenReturn(new Cookie[]{cookie});
Modified:
hadoop/common/branches/branch-2/hadoop-common-project/hadoop-auth/src/test/java/org/apache/hadoop/security/authentication/util/TestSigner.java
URL:
http://svn.apache.org/viewvc/hadoop/common/branches/branch-2/hadoop-common-project/hadoop-auth/src/test/java/org/apache/hadoop/security/authentication/util/TestSigner.java?rev=1616006&r1=1616005&r2=1616006&view=diff
==============================================================================
---
hadoop/common/branches/branch-2/hadoop-common-project/hadoop-auth/src/test/java/org/apache/hadoop/security/authentication/util/TestSigner.java
(original)
+++
hadoop/common/branches/branch-2/hadoop-common-project/hadoop-auth/src/test/java/org/apache/hadoop/security/authentication/util/TestSigner.java
Tue Aug 5 21:25:22 2014
@@ -13,24 +13,15 @@
*/
package org.apache.hadoop.security.authentication.util;
+import java.util.Properties;
import org.junit.Assert;
import org.junit.Test;
public class TestSigner {
@Test
- public void testNoSecret() throws Exception {
- try {
- new Signer(null);
- Assert.fail();
- }
- catch (IllegalArgumentException ex) {
- }
- }
-
- @Test
public void testNullAndEmptyString() throws Exception {
- Signer signer = new Signer("secret".getBytes());
+ Signer signer = new Signer(new StringSignerSecretProvider("secret"));
try {
signer.sign(null);
Assert.fail();
@@ -51,17 +42,17 @@ public class TestSigner {
@Test
public void testSignature() throws Exception {
- Signer signer = new Signer("secret".getBytes());
+ Signer signer = new Signer(new StringSignerSecretProvider("secret"));
String s1 = signer.sign("ok");
String s2 = signer.sign("ok");
String s3 = signer.sign("wrong");
Assert.assertEquals(s1, s2);
- Assert.assertNotSame(s1, s3);
+ Assert.assertNotEquals(s1, s3);
}
@Test
public void testVerify() throws Exception {
- Signer signer = new Signer("secret".getBytes());
+ Signer signer = new Signer(new StringSignerSecretProvider("secret"));
String t = "test";
String s = signer.sign(t);
String e = signer.verifyAndExtract(s);
@@ -70,7 +61,7 @@ public class TestSigner {
@Test
public void testInvalidSignedText() throws Exception {
- Signer signer = new Signer("secret".getBytes());
+ Signer signer = new Signer(new StringSignerSecretProvider("secret"));
try {
signer.verifyAndExtract("test");
Assert.fail();
@@ -83,7 +74,7 @@ public class TestSigner {
@Test
public void testTampering() throws Exception {
- Signer signer = new Signer("secret".getBytes());
+ Signer signer = new Signer(new StringSignerSecretProvider("secret"));
String t = "test";
String s = signer.sign(t);
s += "x";
@@ -96,4 +87,66 @@ public class TestSigner {
Assert.fail();
}
}
+
+ @Test
+ public void testMultipleSecrets() throws Exception {
+ TestSignerSecretProvider secretProvider = new TestSignerSecretProvider();
+ Signer signer = new Signer(secretProvider);
+ secretProvider.setCurrentSecret("secretB");
+ String t1 = "test";
+ String s1 = signer.sign(t1);
+ String e1 = signer.verifyAndExtract(s1);
+ Assert.assertEquals(t1, e1);
+ secretProvider.setPreviousSecret("secretA");
+ String t2 = "test";
+ String s2 = signer.sign(t2);
+ String e2 = signer.verifyAndExtract(s2);
+ Assert.assertEquals(t2, e2);
+ Assert.assertEquals(s1, s2); //check is using current secret for signing
+ secretProvider.setCurrentSecret("secretC");
+ secretProvider.setPreviousSecret("secretB");
+ String t3 = "test";
+ String s3 = signer.sign(t3);
+ String e3 = signer.verifyAndExtract(s3);
+ Assert.assertEquals(t3, e3);
+ Assert.assertNotEquals(s1, s3); //check not using current secret for
signing
+ String e1b = signer.verifyAndExtract(s1);
+ Assert.assertEquals(t1, e1b); // previous secret still valid
+ secretProvider.setCurrentSecret("secretD");
+ secretProvider.setPreviousSecret("secretC");
+ try {
+ signer.verifyAndExtract(s1); // previous secret no longer valid
+ Assert.fail();
+ } catch (SignerException ex) {
+ // Expected
+ }
+ }
+
+ class TestSignerSecretProvider extends SignerSecretProvider {
+
+ private byte[] currentSecret;
+ private byte[] previousSecret;
+
+ @Override
+ public void init(Properties config, long tokenValidity) {
+ }
+
+ @Override
+ public byte[] getCurrentSecret() {
+ return currentSecret;
+ }
+
+ @Override
+ public byte[][] getAllSecrets() {
+ return new byte[][]{currentSecret, previousSecret};
+ }
+
+ public void setCurrentSecret(String secretStr) {
+ currentSecret = secretStr.getBytes();
+ }
+
+ public void setPreviousSecret(String previousSecretStr) {
+ previousSecret = previousSecretStr.getBytes();
+ }
+ }
}
Modified:
hadoop/common/branches/branch-2/hadoop-common-project/hadoop-common/CHANGES.txt
URL:
http://svn.apache.org/viewvc/hadoop/common/branches/branch-2/hadoop-common-project/hadoop-common/CHANGES.txt?rev=1616006&r1=1616005&r2=1616006&view=diff
==============================================================================
---
hadoop/common/branches/branch-2/hadoop-common-project/hadoop-common/CHANGES.txt
(original)
+++
hadoop/common/branches/branch-2/hadoop-common-project/hadoop-common/CHANGES.txt
Tue Aug 5 21:25:22 2014
@@ -60,6 +60,9 @@ Release 2.6.0 - UNRELEASED
HADOOP-10903. Enhance hadoop classpath command to expand wildcards or write
classpath into jar manifest. (cnauroth)
+ HADOOP-10791. AuthenticationFilter should support externalizing the
+ secret for signing and provide rotation support. (rkanter via tucu)
+
OPTIMIZATIONS
BUG FIXES