diff --git a/web/src/main/java/org/springframework/security/web/authentication/www/DigestAuthenticationFilter.java b/web/src/main/java/org/springframework/security/web/authentication/www/DigestAuthenticationFilter.java
index ced37900d2..a5e0e19be5 100644
--- a/web/src/main/java/org/springframework/security/web/authentication/www/DigestAuthenticationFilter.java
+++ b/web/src/main/java/org/springframework/security/web/authentication/www/DigestAuthenticationFilter.java
@@ -38,6 +38,7 @@ import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.SpringSecurityMessageSource;
import org.springframework.security.crypto.codec.Base64;
+import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserCache;
import org.springframework.security.core.userdetails.UserDetails;
@@ -49,46 +50,52 @@ import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.GenericFilterBean;
-
/**
* Processes a HTTP request's Digest authorization headers, putting the result into the
* SecurityContextHolder
.
*
- * For a detailed background on what this filter is designed to process, refer to
- * RFC 2617 (which superseded RFC 2069, although this
- * filter support clients that implement either RFC 2617 or RFC 2069).
+ * For a detailed background on what this filter is designed to process, refer to RFC 2617 (which superseded RFC 2069,
+ * although this filter support clients that implement either RFC 2617 or RFC 2069).
*
- * This filter can be used to provide Digest authentication services to both remoting protocol clients (such as
- * Hessian and SOAP) as well as standard user agents (such as Internet Explorer and FireFox).
+ * This filter can be used to provide Digest authentication services to both remoting
+ * protocol clients (such as Hessian and SOAP) as well as standard user agents (such as
+ * Internet Explorer and FireFox).
*
- * This Digest implementation has been designed to avoid needing to store session state between invocations.
- * All session management information is stored in the "nonce" that is sent to the client by the {@link
- * DigestAuthenticationEntryPoint}.
+ * This Digest implementation has been designed to avoid needing to store session state
+ * between invocations. All session management information is stored in the "nonce" that
+ * is sent to the client by the {@link DigestAuthenticationEntryPoint}.
*
- * If authentication is successful, the resulting {@link org.springframework.security.core.Authentication Authentication}
- * object will be placed into the SecurityContextHolder
.
+ * If authentication is successful, the resulting
+ * {@link org.springframework.security.core.Authentication Authentication} object will be
+ * placed into the SecurityContextHolder
.
*
- * If authentication fails, an {@link org.springframework.security.web.AuthenticationEntryPoint AuthenticationEntryPoint}
- * implementation is called. This must always be {@link DigestAuthenticationEntryPoint}, which will prompt the user
- * to authenticate again via Digest authentication.
+ * If authentication fails, an
+ * {@link org.springframework.security.web.AuthenticationEntryPoint
+ * AuthenticationEntryPoint} implementation is called. This must always be
+ * {@link DigestAuthenticationEntryPoint}, which will prompt the user to authenticate
+ * again via Digest authentication.
*
- * Note there are limitations to Digest authentication, although it is a more comprehensive and secure solution
- * than Basic authentication. Please see RFC 2617 section 4 for a full discussion on the advantages of Digest
- * authentication over Basic authentication, including commentary on the limitations that it still imposes.
+ * Note there are limitations to Digest authentication, although it is a more
+ * comprehensive and secure solution than Basic authentication. Please see RFC 2617
+ * section 4 for a full discussion on the advantages of Digest authentication over Basic
+ * authentication, including commentary on the limitations that it still imposes.
*
* @author Ben Alex
* @author Luke Taylor
* @since 1.0.0
*/
-public class DigestAuthenticationFilter extends GenericFilterBean implements MessageSourceAware {
- //~ Static fields/initializers =====================================================================================
-
+public class DigestAuthenticationFilter extends GenericFilterBean implements
+ MessageSourceAware {
+ // ~ Static fields/initializers
+ // =====================================================================================
private static final Log logger = LogFactory.getLog(DigestAuthenticationFilter.class);
- //~ Instance fields ================================================================================================
+ // ~ Instance fields
+ // ================================================================================================
- private AuthenticationDetailsSource authenticationDetailsSource = new WebAuthenticationDetailsSource();
+ private AuthenticationDetailsSource authenticationDetailsSource = new WebAuthenticationDetailsSource();
private DigestAuthenticationEntryPoint authenticationEntryPoint;
protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
private UserCache userCache = new NullUserCache();
@@ -96,12 +103,14 @@ public class DigestAuthenticationFilter extends GenericFilterBean implements Mes
private boolean passwordAlreadyEncoded = false;
private boolean createAuthenticatedToken = false;
- //~ Methods ========================================================================================================
+ // ~ Methods
+ // ========================================================================================================
@Override
public void afterPropertiesSet() {
Assert.notNull(userDetailsService, "A UserDetailsService is required");
- Assert.notNull(authenticationEntryPoint, "A DigestAuthenticationEntryPoint is required");
+ Assert.notNull(authenticationEntryPoint,
+ "A DigestAuthenticationEntryPoint is required");
}
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
@@ -118,14 +127,17 @@ public class DigestAuthenticationFilter extends GenericFilterBean implements Mes
}
if (logger.isDebugEnabled()) {
- logger.debug("Digest Authorization header received from user agent: " + header);
+ logger.debug("Digest Authorization header received from user agent: "
+ + header);
}
DigestData digestAuth = new DigestData(header);
try {
- digestAuth.validateAndDecode(authenticationEntryPoint.getKey(), authenticationEntryPoint.getRealmName());
- } catch (BadCredentialsException e) {
+ digestAuth.validateAndDecode(authenticationEntryPoint.getKey(),
+ authenticationEntryPoint.getRealmName());
+ }
+ catch (BadCredentialsException e) {
fail(request, response, e);
return;
@@ -151,38 +163,45 @@ public class DigestAuthenticationFilter extends GenericFilterBean implements Mes
userCache.putUserInCache(user);
}
- serverDigestMd5 = digestAuth.calculateServerDigest(user.getPassword(), request.getMethod());
+ serverDigestMd5 = digestAuth.calculateServerDigest(user.getPassword(),
+ request.getMethod());
// If digest is incorrect, try refreshing from backend and recomputing
if (!serverDigestMd5.equals(digestAuth.getResponse()) && cacheWasUsed) {
if (logger.isDebugEnabled()) {
- logger.debug(
- "Digest comparison failure; trying to refresh user from DAO in case password had changed");
+ logger.debug("Digest comparison failure; trying to refresh user from DAO in case password had changed");
}
user = userDetailsService.loadUserByUsername(digestAuth.getUsername());
userCache.putUserInCache(user);
- serverDigestMd5 = digestAuth.calculateServerDigest(user.getPassword(), request.getMethod());
+ serverDigestMd5 = digestAuth.calculateServerDigest(user.getPassword(),
+ request.getMethod());
}
- } catch (UsernameNotFoundException notFound) {
- fail(request, response,
- new BadCredentialsException(messages.getMessage("DigestAuthenticationFilter.usernameNotFound",
- new Object[]{digestAuth.getUsername()}, "Username {0} not found")));
+ }
+ catch (UsernameNotFoundException notFound) {
+ fail(request,
+ response,
+ new BadCredentialsException(messages.getMessage(
+ "DigestAuthenticationFilter.usernameNotFound",
+ new Object[] { digestAuth.getUsername() },
+ "Username {0} not found")));
return;
}
-
// If digest is still incorrect, definitely reject authentication attempt
if (!serverDigestMd5.equals(digestAuth.getResponse())) {
if (logger.isDebugEnabled()) {
- logger.debug("Expected response: '" + serverDigestMd5 + "' but received: '" + digestAuth.getResponse()
+ logger.debug("Expected response: '" + serverDigestMd5
+ + "' but received: '" + digestAuth.getResponse()
+ "'; is AuthenticationDao returning clear text passwords?");
}
- fail(request, response,
- new BadCredentialsException(messages.getMessage("DigestAuthenticationFilter.incorrectResponse",
+ fail(request,
+ response,
+ new BadCredentialsException(messages.getMessage(
+ "DigestAuthenticationFilter.incorrectResponse",
"Incorrect response")));
return;
}
@@ -192,8 +211,10 @@ public class DigestAuthenticationFilter extends GenericFilterBean implements Mes
// We do this last so we can direct the user agent its nonce is stale
// but the request was otherwise appearing to be valid
if (digestAuth.isNonceExpired()) {
- fail(request, response,
- new NonceExpiredException(messages.getMessage("DigestAuthenticationFilter.nonceExpired",
+ fail(request,
+ response,
+ new NonceExpiredException(messages.getMessage(
+ "DigestAuthenticationFilter.nonceExpired",
"Nonce has expired/timed out")));
return;
@@ -204,27 +225,34 @@ public class DigestAuthenticationFilter extends GenericFilterBean implements Mes
+ "' with response: '" + digestAuth.getResponse() + "'");
}
- SecurityContextHolder.getContext().setAuthentication(createSuccessfulAuthentication(request, user));
+ Authentication authentication = createSuccessfulAuthentication(request, user);
+ SecurityContext context = SecurityContextHolder.createEmptyContext();
+ context.setAuthentication(authentication);
+ SecurityContextHolder.setContext(context);
chain.doFilter(request, response);
}
- private Authentication createSuccessfulAuthentication(HttpServletRequest request, UserDetails user) {
+ private Authentication createSuccessfulAuthentication(HttpServletRequest request,
+ UserDetails user) {
UsernamePasswordAuthenticationToken authRequest;
if (createAuthenticatedToken) {
- authRequest = new UsernamePasswordAuthenticationToken(user, user.getPassword(), user.getAuthorities());
+ authRequest = new UsernamePasswordAuthenticationToken(user,
+ user.getPassword(), user.getAuthorities());
}
else {
- authRequest = new UsernamePasswordAuthenticationToken(user, user.getPassword());
+ authRequest = new UsernamePasswordAuthenticationToken(user,
+ user.getPassword());
}
- authRequest.setDetails(authenticationDetailsSource.buildDetails((HttpServletRequest) request));
+ authRequest.setDetails(authenticationDetailsSource
+ .buildDetails((HttpServletRequest) request));
return authRequest;
}
- private void fail(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed)
- throws IOException, ServletException {
+ private void fail(HttpServletRequest request, HttpServletResponse response,
+ AuthenticationException failed) throws IOException, ServletException {
SecurityContextHolder.getContext().setAuthentication(null);
if (logger.isDebugEnabled()) {
@@ -246,12 +274,15 @@ public class DigestAuthenticationFilter extends GenericFilterBean implements Mes
return userDetailsService;
}
- public void setAuthenticationDetailsSource(AuthenticationDetailsSource authenticationDetailsSource) {
- Assert.notNull(authenticationDetailsSource, "AuthenticationDetailsSource required");
+ public void setAuthenticationDetailsSource(
+ AuthenticationDetailsSource authenticationDetailsSource) {
+ Assert.notNull(authenticationDetailsSource,
+ "AuthenticationDetailsSource required");
this.authenticationDetailsSource = authenticationDetailsSource;
}
- public void setAuthenticationEntryPoint(DigestAuthenticationEntryPoint authenticationEntryPoint) {
+ public void setAuthenticationEntryPoint(
+ DigestAuthenticationEntryPoint authenticationEntryPoint) {
this.authenticationEntryPoint = authenticationEntryPoint;
}
@@ -271,18 +302,16 @@ public class DigestAuthenticationFilter extends GenericFilterBean implements Mes
this.userDetailsService = userDetailsService;
}
-
/**
- * If you set this property, the Authentication object, which is
- * created after the successful digest authentication will be marked
- * as authenticated and filled with the authorities loaded by
- * the UserDetailsService. It therefore will not be re-authenticated
- * by your AuthenticationProvider. This means, that only the password
+ * If you set this property, the Authentication object, which is created after the
+ * successful digest authentication will be marked as authenticated and filled
+ * with the authorities loaded by the UserDetailsService. It therefore will not be
+ * re-authenticated by your AuthenticationProvider. This means, that only the password
* of the user is checked, but not the flags like isEnabled() or
- * isAccountNonExpired(). You will save some time by enabling this flag,
- * as otherwise your UserDetailsService will be called twice. A more secure
- * option would be to introduce a cache around your UserDetailsService, but
- * if you don't use these flags, you can also safely enable this option.
+ * isAccountNonExpired(). You will save some time by enabling this flag, as otherwise
+ * your UserDetailsService will be called twice. A more secure option would be to
+ * introduce a cache around your UserDetailsService, but if you don't use these flags,
+ * you can also safely enable this option.
*
* @param createAuthenticatedToken default is false
*/
@@ -304,8 +333,10 @@ public class DigestAuthenticationFilter extends GenericFilterBean implements Mes
DigestData(String header) {
section212response = header.substring(7);
- String[] headerEntries = DigestAuthUtils.splitIgnoringQuotes(section212response, ',');
- Map headerMap = DigestAuthUtils.splitEachArrayElementAndCreateMap(headerEntries, "=", "\"");
+ String[] headerEntries = DigestAuthUtils.splitIgnoringQuotes(
+ section212response, ',');
+ Map headerMap = DigestAuthUtils
+ .splitEachArrayElementAndCreateMap(headerEntries, "=", "\"");
username = headerMap.get("username");
realm = headerMap.get("realm");
@@ -317,69 +348,87 @@ public class DigestAuthenticationFilter extends GenericFilterBean implements Mes
cnonce = headerMap.get("cnonce"); // RFC 2617 extension
if (logger.isDebugEnabled()) {
- logger.debug("Extracted username: '" + username + "'; realm: '" + realm + "'; nonce: '"
- + nonce + "'; uri: '" + uri + "'; response: '" + response + "'");
+ logger.debug("Extracted username: '" + username + "'; realm: '" + realm
+ + "'; nonce: '" + nonce + "'; uri: '" + uri + "'; response: '"
+ + response + "'");
}
}
- void validateAndDecode(String entryPointKey, String expectedRealm) throws BadCredentialsException {
+ void validateAndDecode(String entryPointKey, String expectedRealm)
+ throws BadCredentialsException {
// Check all required parameters were supplied (ie RFC 2069)
- if ((username == null) || (realm == null) || (nonce == null) || (uri == null) || (response == null)) {
- throw new BadCredentialsException(messages.getMessage("DigestAuthenticationFilter.missingMandatory",
- new Object[]{section212response}, "Missing mandatory digest value; received header {0}"));
+ if ((username == null) || (realm == null) || (nonce == null) || (uri == null)
+ || (response == null)) {
+ throw new BadCredentialsException(messages.getMessage(
+ "DigestAuthenticationFilter.missingMandatory",
+ new Object[] { section212response },
+ "Missing mandatory digest value; received header {0}"));
}
// Check all required parameters for an "auth" qop were supplied (ie RFC 2617)
if ("auth".equals(qop)) {
if ((nc == null) || (cnonce == null)) {
if (logger.isDebugEnabled()) {
- logger.debug("extracted nc: '" + nc + "'; cnonce: '" + cnonce + "'");
+ logger.debug("extracted nc: '" + nc + "'; cnonce: '" + cnonce
+ + "'");
}
- throw new BadCredentialsException(messages.getMessage("DigestAuthenticationFilter.missingAuth",
- new Object[]{section212response}, "Missing mandatory digest value; received header {0}"));
+ throw new BadCredentialsException(messages.getMessage(
+ "DigestAuthenticationFilter.missingAuth",
+ new Object[] { section212response },
+ "Missing mandatory digest value; received header {0}"));
}
}
// Check realm name equals what we expected
if (!expectedRealm.equals(realm)) {
- throw new BadCredentialsException(messages.getMessage("DigestAuthenticationFilter.incorrectRealm",
- new Object[]{realm, expectedRealm},
- "Response realm name '{0}' does not match system realm name of '{1}'"));
+ throw new BadCredentialsException(
+ messages.getMessage("DigestAuthenticationFilter.incorrectRealm",
+ new Object[] { realm, expectedRealm },
+ "Response realm name '{0}' does not match system realm name of '{1}'"));
}
// Check nonce was Base64 encoded (as sent by DigestAuthenticationEntryPoint)
if (!Base64.isBase64(nonce.getBytes())) {
- throw new BadCredentialsException(messages.getMessage("DigestAuthenticationFilter.nonceEncoding",
- new Object[]{nonce}, "Nonce is not encoded in Base64; received nonce {0}"));
+ throw new BadCredentialsException(messages.getMessage(
+ "DigestAuthenticationFilter.nonceEncoding",
+ new Object[] { nonce },
+ "Nonce is not encoded in Base64; received nonce {0}"));
}
// Decode nonce from Base64
// format of nonce is:
// base64(expirationTime + ":" + md5Hex(expirationTime + ":" + key))
String nonceAsPlainText = new String(Base64.decode(nonce.getBytes()));
- String[] nonceTokens = StringUtils.delimitedListToStringArray(nonceAsPlainText, ":");
+ String[] nonceTokens = StringUtils.delimitedListToStringArray(
+ nonceAsPlainText, ":");
if (nonceTokens.length != 2) {
- throw new BadCredentialsException(messages.getMessage("DigestAuthenticationFilter.nonceNotTwoTokens",
- new Object[]{nonceAsPlainText}, "Nonce should have yielded two tokens but was {0}"));
+ throw new BadCredentialsException(messages.getMessage(
+ "DigestAuthenticationFilter.nonceNotTwoTokens",
+ new Object[] { nonceAsPlainText },
+ "Nonce should have yielded two tokens but was {0}"));
}
// Extract expiry time from nonce
try {
nonceExpiryTime = new Long(nonceTokens[0]).longValue();
- } catch (NumberFormatException nfe) {
- throw new BadCredentialsException(messages.getMessage("DigestAuthenticationFilter.nonceNotNumeric",
- new Object[]{nonceAsPlainText},
+ }
+ catch (NumberFormatException nfe) {
+ throw new BadCredentialsException(
+ messages.getMessage("DigestAuthenticationFilter.nonceNotNumeric",
+ new Object[] { nonceAsPlainText },
"Nonce token should have yielded a numeric first token, but was {0}"));
}
// Check signature of nonce matches this expiry time
- String expectedNonceSignature = DigestAuthUtils.md5Hex(nonceExpiryTime + ":" + entryPointKey);
+ String expectedNonceSignature = DigestAuthUtils.md5Hex(nonceExpiryTime + ":"
+ + entryPointKey);
if (!expectedNonceSignature.equals(nonceTokens[1])) {
- new BadCredentialsException(messages.getMessage("DigestAuthenticationFilter.nonceCompromised",
- new Object[]{nonceAsPlainText}, "Nonce token compromised {0}"));
+ new BadCredentialsException(messages.getMessage(
+ "DigestAuthenticationFilter.nonceCompromised",
+ new Object[] { nonceAsPlainText }, "Nonce token compromised {0}"));
}
}
@@ -387,8 +436,8 @@ public class DigestAuthenticationFilter extends GenericFilterBean implements Mes
// Compute the expected response-digest (will be in hex form)
// Don't catch IllegalArgumentException (already checked validity)
- return DigestAuthUtils.generateDigest(passwordAlreadyEncoded, username, realm, password,
- httpMethod, uri, qop, nonce, nc, cnonce);
+ return DigestAuthUtils.generateDigest(passwordAlreadyEncoded, username,
+ realm, password, httpMethod, uri, qop, nonce, nc, cnonce);
}
boolean isNonceExpired() {
diff --git a/web/src/test/java/org/springframework/security/web/authentication/www/DigestAuthenticationFilterTests.java b/web/src/test/java/org/springframework/security/web/authentication/www/DigestAuthenticationFilterTests.java
index 1d79c11f8b..ef7265310c 100644
--- a/web/src/test/java/org/springframework/security/web/authentication/www/DigestAuthenticationFilterTests.java
+++ b/web/src/test/java/org/springframework/security/web/authentication/www/DigestAuthenticationFilterTests.java
@@ -15,11 +15,20 @@
package org.springframework.security.web.authentication.www;
-import static org.junit.Assert.*;
-import static org.mockito.Mockito.*;
+import static org.fest.assertions.Assertions.*;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
import java.io.IOException;
-import java.util.*;
+import java.util.Map;
+
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
@@ -32,7 +41,9 @@ import org.junit.Before;
import org.junit.Test;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
+import org.springframework.security.authentication.TestingAuthenticationToken;
import org.springframework.security.core.authority.AuthorityUtils;
+import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
@@ -41,7 +52,6 @@ import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.core.userdetails.cache.NullUserCache;
import org.springframework.util.StringUtils;
-
/**
* Tests {@link DigestAuthenticationFilter}.
*
@@ -49,7 +59,8 @@ import org.springframework.util.StringUtils;
* @author Luke Taylor
*/
public class DigestAuthenticationFilterTests {
- //~ Static fields/initializers =====================================================================================
+ // ~ Static fields/initializers
+ // =====================================================================================
private static final String NC = "00000002";
private static final String CNONCE = "c822c727a648aba7";
@@ -65,23 +76,26 @@ public class DigestAuthenticationFilterTests {
*/
private static final String NONCE = generateNonce(60);
- //~ Instance fields ================================================================================================
+ // ~ Instance fields
+ // ================================================================================================
- // private ApplicationContext ctx;
+ // private ApplicationContext ctx;
private DigestAuthenticationFilter filter;
private MockHttpServletRequest request;
+ // ~ Methods
+ // ========================================================================================================
- //~ Methods ========================================================================================================
-
- private String createAuthorizationHeader(String username, String realm, String nonce, String uri,
- String responseDigest, String qop, String nc, String cnonce) {
- return "Digest username=\"" + username + "\", realm=\"" + realm + "\", nonce=\"" + nonce + "\", uri=\"" + uri
- + "\", response=\"" + responseDigest + "\", qop=" + qop + ", nc=" + nc + ", cnonce=\"" + cnonce + "\"";
+ private String createAuthorizationHeader(String username, String realm, String nonce,
+ String uri, String responseDigest, String qop, String nc, String cnonce) {
+ return "Digest username=\"" + username + "\", realm=\"" + realm + "\", nonce=\""
+ + nonce + "\", uri=\"" + uri + "\", response=\"" + responseDigest
+ + "\", qop=" + qop + ", nc=" + nc + ", cnonce=\"" + cnonce + "\"";
}
- private MockHttpServletResponse executeFilterInContainerSimulator(Filter filter, final ServletRequest request,
- final boolean expectChainToProceed) throws ServletException, IOException {
+ private MockHttpServletResponse executeFilterInContainerSimulator(Filter filter,
+ final ServletRequest request, final boolean expectChainToProceed)
+ throws ServletException, IOException {
final MockHttpServletResponse response = new MockHttpServletResponse();
final FilterChain chain = mock(FilterChain.class);
@@ -111,8 +125,10 @@ public class DigestAuthenticationFilterTests {
// Create User Details Service
UserDetailsService uds = new UserDetailsService() {
- public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
- return new User("rod,ok", "koala", AuthorityUtils.createAuthorityList("ROLE_ONE","ROLE_TWO"));
+ public UserDetails loadUserByUsername(String username)
+ throws UsernameNotFoundException {
+ return new User("rod,ok", "koala", AuthorityUtils.createAuthorityList(
+ "ROLE_ONE", "ROLE_TWO"));
}
};
@@ -129,25 +145,28 @@ public class DigestAuthenticationFilterTests {
}
@Test
- public void testExpiredNonceReturnsForbiddenWithStaleHeader()
- throws Exception {
+ public void testExpiredNonceReturnsForbiddenWithStaleHeader() throws Exception {
String nonce = generateNonce(0);
- String responseDigest = DigestAuthUtils.generateDigest(false, USERNAME, REALM, PASSWORD, "GET",
- REQUEST_URI, QOP, nonce, NC, CNONCE);
+ String responseDigest = DigestAuthUtils.generateDigest(false, USERNAME, REALM,
+ PASSWORD, "GET", REQUEST_URI, QOP, nonce, NC, CNONCE);
- request.addHeader("Authorization",
- createAuthorizationHeader(USERNAME, REALM, nonce, REQUEST_URI, responseDigest, QOP, NC, CNONCE));
+ request.addHeader(
+ "Authorization",
+ createAuthorizationHeader(USERNAME, REALM, nonce, REQUEST_URI,
+ responseDigest, QOP, NC, CNONCE));
Thread.sleep(1000); // ensures token expired
- MockHttpServletResponse response = executeFilterInContainerSimulator(filter, request, false);
+ MockHttpServletResponse response = executeFilterInContainerSimulator(filter,
+ request, false);
assertNull(SecurityContextHolder.getContext().getAuthentication());
assertEquals(401, response.getStatus());
String header = response.getHeader("WWW-Authenticate").toString().substring(7);
String[] headerEntries = StringUtils.commaDelimitedListToStringArray(header);
- Map headerMap = DigestAuthUtils.splitEachArrayElementAndCreateMap(headerEntries, "=", "\"");
+ Map headerMap = DigestAuthUtils
+ .splitEachArrayElementAndCreateMap(headerEntries, "=", "\"");
assertEquals("true", headerMap.get("stale"));
}
@@ -175,13 +194,14 @@ public class DigestAuthenticationFilterTests {
}
@Test
- public void testInvalidDigestAuthorizationTokenGeneratesError()
- throws Exception {
+ public void testInvalidDigestAuthorizationTokenGeneratesError() throws Exception {
String token = "NOT_A_VALID_TOKEN_AS_MISSING_COLON";
- request.addHeader("Authorization", "Digest " + new String(Base64.encodeBase64(token.getBytes())));
+ request.addHeader("Authorization",
+ "Digest " + new String(Base64.encodeBase64(token.getBytes())));
- MockHttpServletResponse response = executeFilterInContainerSimulator(filter, request, false);
+ MockHttpServletResponse response = executeFilterInContainerSimulator(filter,
+ request, false);
assertEquals(401, response.getStatus());
assertNull(SecurityContextHolder.getContext().getAuthentication());
@@ -191,7 +211,8 @@ public class DigestAuthenticationFilterTests {
public void testMalformedHeaderReturnsForbidden() throws Exception {
request.addHeader("Authorization", "Digest scsdcsdc");
- MockHttpServletResponse response = executeFilterInContainerSimulator(filter, request, false);
+ MockHttpServletResponse response = executeFilterInContainerSimulator(filter,
+ request, false);
assertNull(SecurityContextHolder.getContext().getAuthentication());
assertEquals(401, response.getStatus());
@@ -201,28 +222,36 @@ public class DigestAuthenticationFilterTests {
public void testNonBase64EncodedNonceReturnsForbidden() throws Exception {
String nonce = "NOT_BASE_64_ENCODED";
- String responseDigest = DigestAuthUtils.generateDigest(false, USERNAME, REALM, PASSWORD, "GET",
- REQUEST_URI, QOP, nonce, NC, CNONCE);
+ String responseDigest = DigestAuthUtils.generateDigest(false, USERNAME, REALM,
+ PASSWORD, "GET", REQUEST_URI, QOP, nonce, NC, CNONCE);
- request.addHeader("Authorization",
- createAuthorizationHeader(USERNAME, REALM, nonce, REQUEST_URI, responseDigest, QOP, NC, CNONCE));
+ request.addHeader(
+ "Authorization",
+ createAuthorizationHeader(USERNAME, REALM, nonce, REQUEST_URI,
+ responseDigest, QOP, NC, CNONCE));
- MockHttpServletResponse response = executeFilterInContainerSimulator(filter, request, false);
+ MockHttpServletResponse response = executeFilterInContainerSimulator(filter,
+ request, false);
assertNull(SecurityContextHolder.getContext().getAuthentication());
assertEquals(401, response.getStatus());
}
@Test
- public void testNonceWithIncorrectSignatureForNumericFieldReturnsForbidden() throws Exception {
- String nonce = new String(Base64.encodeBase64("123456:incorrectStringPassword".getBytes()));
- String responseDigest = DigestAuthUtils.generateDigest(false, USERNAME, REALM, PASSWORD, "GET",
- REQUEST_URI, QOP, nonce, NC, CNONCE);
+ public void testNonceWithIncorrectSignatureForNumericFieldReturnsForbidden()
+ throws Exception {
+ String nonce = new String(Base64.encodeBase64("123456:incorrectStringPassword"
+ .getBytes()));
+ String responseDigest = DigestAuthUtils.generateDigest(false, USERNAME, REALM,
+ PASSWORD, "GET", REQUEST_URI, QOP, nonce, NC, CNONCE);
- request.addHeader("Authorization",
- createAuthorizationHeader(USERNAME, REALM, nonce, REQUEST_URI, responseDigest, QOP, NC, CNONCE));
+ request.addHeader(
+ "Authorization",
+ createAuthorizationHeader(USERNAME, REALM, nonce, REQUEST_URI,
+ responseDigest, QOP, NC, CNONCE));
- MockHttpServletResponse response = executeFilterInContainerSimulator(filter, request, false);
+ MockHttpServletResponse response = executeFilterInContainerSimulator(filter,
+ request, false);
assertNull(SecurityContextHolder.getContext().getAuthentication());
assertEquals(401, response.getStatus());
@@ -230,29 +259,38 @@ public class DigestAuthenticationFilterTests {
@Test
public void testNonceWithNonNumericFirstElementReturnsForbidden() throws Exception {
- String nonce = new String(Base64.encodeBase64("hello:ignoredSecondElement".getBytes()));
- String responseDigest = DigestAuthUtils.generateDigest(false, USERNAME, REALM, PASSWORD, "GET",
- REQUEST_URI, QOP, nonce, NC, CNONCE);
+ String nonce = new String(Base64.encodeBase64("hello:ignoredSecondElement"
+ .getBytes()));
+ String responseDigest = DigestAuthUtils.generateDigest(false, USERNAME, REALM,
+ PASSWORD, "GET", REQUEST_URI, QOP, nonce, NC, CNONCE);
- request.addHeader("Authorization",
- createAuthorizationHeader(USERNAME, REALM, nonce, REQUEST_URI, responseDigest, QOP, NC, CNONCE));
+ request.addHeader(
+ "Authorization",
+ createAuthorizationHeader(USERNAME, REALM, nonce, REQUEST_URI,
+ responseDigest, QOP, NC, CNONCE));
- MockHttpServletResponse response = executeFilterInContainerSimulator(filter, request, false);
+ MockHttpServletResponse response = executeFilterInContainerSimulator(filter,
+ request, false);
assertNull(SecurityContextHolder.getContext().getAuthentication());
assertEquals(401, response.getStatus());
}
@Test
- public void testNonceWithoutTwoColonSeparatedElementsReturnsForbidden() throws Exception {
- String nonce = new String(Base64.encodeBase64("a base 64 string without a colon".getBytes()));
- String responseDigest = DigestAuthUtils.generateDigest(false, USERNAME, REALM, PASSWORD, "GET",
- REQUEST_URI, QOP, nonce, NC, CNONCE);
+ public void testNonceWithoutTwoColonSeparatedElementsReturnsForbidden()
+ throws Exception {
+ String nonce = new String(Base64.encodeBase64("a base 64 string without a colon"
+ .getBytes()));
+ String responseDigest = DigestAuthUtils.generateDigest(false, USERNAME, REALM,
+ PASSWORD, "GET", REQUEST_URI, QOP, nonce, NC, CNONCE);
- request.addHeader("Authorization",
- createAuthorizationHeader(USERNAME, REALM, nonce, REQUEST_URI, responseDigest, QOP, NC, CNONCE));
+ request.addHeader(
+ "Authorization",
+ createAuthorizationHeader(USERNAME, REALM, nonce, REQUEST_URI,
+ responseDigest, QOP, NC, CNONCE));
- MockHttpServletResponse response = executeFilterInContainerSimulator(filter, request, false);
+ MockHttpServletResponse response = executeFilterInContainerSimulator(filter,
+ request, false);
assertNull(SecurityContextHolder.getContext().getAuthentication());
assertEquals(401, response.getStatus());
@@ -260,58 +298,67 @@ public class DigestAuthenticationFilterTests {
@Test
public void testNormalOperationWhenPasswordIsAlreadyEncoded() throws Exception {
- String encodedPassword = DigestAuthUtils.encodePasswordInA1Format(USERNAME, REALM, PASSWORD);
- String responseDigest = DigestAuthUtils.generateDigest(true, USERNAME, REALM, encodedPassword, "GET",
- REQUEST_URI, QOP, NONCE, NC, CNONCE);
+ String encodedPassword = DigestAuthUtils.encodePasswordInA1Format(USERNAME,
+ REALM, PASSWORD);
+ String responseDigest = DigestAuthUtils.generateDigest(true, USERNAME, REALM,
+ encodedPassword, "GET", REQUEST_URI, QOP, NONCE, NC, CNONCE);
- request.addHeader("Authorization",
- createAuthorizationHeader(USERNAME, REALM, NONCE, REQUEST_URI, responseDigest, QOP, NC, CNONCE));
+ request.addHeader(
+ "Authorization",
+ createAuthorizationHeader(USERNAME, REALM, NONCE, REQUEST_URI,
+ responseDigest, QOP, NC, CNONCE));
executeFilterInContainerSimulator(filter, request, true);
assertNotNull(SecurityContextHolder.getContext().getAuthentication());
- assertEquals(USERNAME,
- ((UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getUsername());
+ assertEquals(USERNAME, ((UserDetails) SecurityContextHolder.getContext()
+ .getAuthentication().getPrincipal()).getUsername());
}
@Test
public void testNormalOperationWhenPasswordNotAlreadyEncoded() throws Exception {
- String responseDigest = DigestAuthUtils.generateDigest(false, USERNAME, REALM, PASSWORD, "GET",
- REQUEST_URI, QOP, NONCE, NC, CNONCE);
+ String responseDigest = DigestAuthUtils.generateDigest(false, USERNAME, REALM,
+ PASSWORD, "GET", REQUEST_URI, QOP, NONCE, NC, CNONCE);
- request.addHeader("Authorization",
- createAuthorizationHeader(USERNAME, REALM, NONCE, REQUEST_URI, responseDigest, QOP, NC, CNONCE));
+ request.addHeader(
+ "Authorization",
+ createAuthorizationHeader(USERNAME, REALM, NONCE, REQUEST_URI,
+ responseDigest, QOP, NC, CNONCE));
executeFilterInContainerSimulator(filter, request, true);
assertNotNull(SecurityContextHolder.getContext().getAuthentication());
- assertEquals(USERNAME,
- ((UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getUsername());
- assertFalse(SecurityContextHolder.getContext().getAuthentication().isAuthenticated());
+ assertEquals(USERNAME, ((UserDetails) SecurityContextHolder.getContext()
+ .getAuthentication().getPrincipal()).getUsername());
+ assertFalse(SecurityContextHolder.getContext().getAuthentication()
+ .isAuthenticated());
}
@Test
- public void testNormalOperationWhenPasswordNotAlreadyEncodedAndWithoutReAuthentication() throws Exception {
- String responseDigest = DigestAuthUtils.generateDigest(false, USERNAME, REALM, PASSWORD, "GET",
- REQUEST_URI, QOP, NONCE, NC, CNONCE);
+ public void testNormalOperationWhenPasswordNotAlreadyEncodedAndWithoutReAuthentication()
+ throws Exception {
+ String responseDigest = DigestAuthUtils.generateDigest(false, USERNAME, REALM,
+ PASSWORD, "GET", REQUEST_URI, QOP, NONCE, NC, CNONCE);
- request.addHeader("Authorization",
- createAuthorizationHeader(USERNAME, REALM, NONCE, REQUEST_URI, responseDigest, QOP, NC, CNONCE));
+ request.addHeader(
+ "Authorization",
+ createAuthorizationHeader(USERNAME, REALM, NONCE, REQUEST_URI,
+ responseDigest, QOP, NC, CNONCE));
filter.setCreateAuthenticatedToken(true);
executeFilterInContainerSimulator(filter, request, true);
assertNotNull(SecurityContextHolder.getContext().getAuthentication());
- assertEquals(USERNAME,
- ((UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getUsername());
- assertTrue(SecurityContextHolder.getContext().getAuthentication().isAuthenticated());
- assertEquals(AuthorityUtils.createAuthorityList("ROLE_ONE","ROLE_TWO"),
+ assertEquals(USERNAME, ((UserDetails) SecurityContextHolder.getContext()
+ .getAuthentication().getPrincipal()).getUsername());
+ assertTrue(SecurityContextHolder.getContext().getAuthentication()
+ .isAuthenticated());
+ assertEquals(AuthorityUtils.createAuthorityList("ROLE_ONE", "ROLE_TWO"),
SecurityContextHolder.getContext().getAuthentication().getAuthorities());
}
@Test
- public void otherAuthorizationSchemeIsIgnored()
- throws Exception {
+ public void otherAuthorizationSchemeIsIgnored() throws Exception {
request.addHeader("Authorization", "SOME_OTHER_AUTHENTICATION_SCHEME");
executeFilterInContainerSimulator(filter, request, true);
@@ -319,14 +366,14 @@ public class DigestAuthenticationFilterTests {
assertNull(SecurityContextHolder.getContext().getAuthentication());
}
- @Test(expected=IllegalArgumentException.class)
+ @Test(expected = IllegalArgumentException.class)
public void startupDetectsMissingAuthenticationEntryPoint() throws Exception {
DigestAuthenticationFilter filter = new DigestAuthenticationFilter();
filter.setUserDetailsService(mock(UserDetailsService.class));
filter.afterPropertiesSet();
}
- @Test(expected=IllegalArgumentException.class)
+ @Test(expected = IllegalArgumentException.class)
public void startupDetectsMissingUserDetailsService() throws Exception {
DigestAuthenticationFilter filter = new DigestAuthenticationFilter();
filter.setAuthenticationEntryPoint(new DigestAuthenticationEntryPoint());
@@ -334,26 +381,32 @@ public class DigestAuthenticationFilterTests {
}
@Test
- public void successfulLoginThenFailedLoginResultsInSessionLosingToken() throws Exception {
- String responseDigest = DigestAuthUtils.generateDigest(false, USERNAME, REALM, PASSWORD, "GET",
- REQUEST_URI, QOP, NONCE, NC, CNONCE);
+ public void successfulLoginThenFailedLoginResultsInSessionLosingToken()
+ throws Exception {
+ String responseDigest = DigestAuthUtils.generateDigest(false, USERNAME, REALM,
+ PASSWORD, "GET", REQUEST_URI, QOP, NONCE, NC, CNONCE);
- request.addHeader("Authorization",
- createAuthorizationHeader(USERNAME, REALM, NONCE, REQUEST_URI, responseDigest, QOP, NC, CNONCE));
+ request.addHeader(
+ "Authorization",
+ createAuthorizationHeader(USERNAME, REALM, NONCE, REQUEST_URI,
+ responseDigest, QOP, NC, CNONCE));
executeFilterInContainerSimulator(filter, request, true);
assertNotNull(SecurityContextHolder.getContext().getAuthentication());
// Now retry, giving an invalid nonce
- responseDigest = DigestAuthUtils.generateDigest(false, USERNAME, REALM, "WRONG_PASSWORD", "GET",
- REQUEST_URI, QOP, NONCE, NC, CNONCE);
+ responseDigest = DigestAuthUtils.generateDigest(false, USERNAME, REALM,
+ "WRONG_PASSWORD", "GET", REQUEST_URI, QOP, NONCE, NC, CNONCE);
request = new MockHttpServletRequest();
- request.addHeader("Authorization",
- createAuthorizationHeader(USERNAME, REALM, NONCE, REQUEST_URI, responseDigest, QOP, NC, CNONCE));
+ request.addHeader(
+ "Authorization",
+ createAuthorizationHeader(USERNAME, REALM, NONCE, REQUEST_URI,
+ responseDigest, QOP, NC, CNONCE));
- MockHttpServletResponse response = executeFilterInContainerSimulator(filter, request, false);
+ MockHttpServletResponse response = executeFilterInContainerSimulator(filter,
+ request, false);
// Check we lost our previous authentication
assertNull(SecurityContextHolder.getContext().getAuthentication());
@@ -364,13 +417,16 @@ public class DigestAuthenticationFilterTests {
public void wrongCnonceBasedOnDigestReturnsForbidden() throws Exception {
String cnonce = "NOT_SAME_AS_USED_FOR_DIGEST_COMPUTATION";
- String responseDigest = DigestAuthUtils.generateDigest(false, USERNAME, REALM, PASSWORD, "GET",
- REQUEST_URI, QOP, NONCE, NC, "DIFFERENT_CNONCE");
+ String responseDigest = DigestAuthUtils.generateDigest(false, USERNAME, REALM,
+ PASSWORD, "GET", REQUEST_URI, QOP, NONCE, NC, "DIFFERENT_CNONCE");
- request.addHeader("Authorization",
- createAuthorizationHeader(USERNAME, REALM, NONCE, REQUEST_URI, responseDigest, QOP, NC, cnonce));
+ request.addHeader(
+ "Authorization",
+ createAuthorizationHeader(USERNAME, REALM, NONCE, REQUEST_URI,
+ responseDigest, QOP, NC, cnonce));
- MockHttpServletResponse response = executeFilterInContainerSimulator(filter, request, false);
+ MockHttpServletResponse response = executeFilterInContainerSimulator(filter,
+ request, false);
assertNull(SecurityContextHolder.getContext().getAuthentication());
assertEquals(401, response.getStatus());
@@ -379,13 +435,16 @@ public class DigestAuthenticationFilterTests {
@Test
public void wrongDigestReturnsForbidden() throws Exception {
String password = "WRONG_PASSWORD";
- String responseDigest = DigestAuthUtils.generateDigest(false, USERNAME, REALM, password, "GET",
- REQUEST_URI, QOP, NONCE, NC, CNONCE);
+ String responseDigest = DigestAuthUtils.generateDigest(false, USERNAME, REALM,
+ password, "GET", REQUEST_URI, QOP, NONCE, NC, CNONCE);
- request.addHeader("Authorization",
- createAuthorizationHeader(USERNAME, REALM, NONCE, REQUEST_URI, responseDigest, QOP, NC, CNONCE));
+ request.addHeader(
+ "Authorization",
+ createAuthorizationHeader(USERNAME, REALM, NONCE, REQUEST_URI,
+ responseDigest, QOP, NC, CNONCE));
- MockHttpServletResponse response = executeFilterInContainerSimulator(filter, request, false);
+ MockHttpServletResponse response = executeFilterInContainerSimulator(filter,
+ request, false);
assertNull(SecurityContextHolder.getContext().getAuthentication());
assertEquals(401, response.getStatus());
@@ -394,13 +453,16 @@ public class DigestAuthenticationFilterTests {
@Test
public void wrongRealmReturnsForbidden() throws Exception {
String realm = "WRONG_REALM";
- String responseDigest = DigestAuthUtils.generateDigest(false, USERNAME, realm, PASSWORD, "GET",
- REQUEST_URI, QOP, NONCE, NC, CNONCE);
+ String responseDigest = DigestAuthUtils.generateDigest(false, USERNAME, realm,
+ PASSWORD, "GET", REQUEST_URI, QOP, NONCE, NC, CNONCE);
- request.addHeader("Authorization",
- createAuthorizationHeader(USERNAME, realm, NONCE, REQUEST_URI, responseDigest, QOP, NC, CNONCE));
+ request.addHeader(
+ "Authorization",
+ createAuthorizationHeader(USERNAME, realm, NONCE, REQUEST_URI,
+ responseDigest, QOP, NC, CNONCE));
- MockHttpServletResponse response = executeFilterInContainerSimulator(filter, request, false);
+ MockHttpServletResponse response = executeFilterInContainerSimulator(filter,
+ request, false);
assertNull(SecurityContextHolder.getContext().getAuthentication());
assertEquals(401, response.getStatus());
@@ -408,15 +470,41 @@ public class DigestAuthenticationFilterTests {
@Test
public void wrongUsernameReturnsForbidden() throws Exception {
- String responseDigest = DigestAuthUtils.generateDigest(false, "NOT_A_KNOWN_USER", REALM, PASSWORD,
- "GET", REQUEST_URI, QOP, NONCE, NC, CNONCE);
+ String responseDigest = DigestAuthUtils.generateDigest(false, "NOT_A_KNOWN_USER",
+ REALM, PASSWORD, "GET", REQUEST_URI, QOP, NONCE, NC, CNONCE);
- request.addHeader("Authorization",
- createAuthorizationHeader(USERNAME, REALM, NONCE, REQUEST_URI, responseDigest, QOP, NC, CNONCE));
+ request.addHeader(
+ "Authorization",
+ createAuthorizationHeader(USERNAME, REALM, NONCE, REQUEST_URI,
+ responseDigest, QOP, NC, CNONCE));
- MockHttpServletResponse response = executeFilterInContainerSimulator(filter, request, false);
+ MockHttpServletResponse response = executeFilterInContainerSimulator(filter,
+ request, false);
assertNull(SecurityContextHolder.getContext().getAuthentication());
assertEquals(401, response.getStatus());
}
+
+ // SEC-3108
+ @Test
+ public void authenticationCreatesEmptyContext() throws Exception {
+ SecurityContext existingContext = SecurityContextHolder.createEmptyContext();
+ TestingAuthenticationToken existingAuthentication = new TestingAuthenticationToken("existingauthenitcated", "pass", "ROLE_USER");
+ existingContext.setAuthentication(existingAuthentication);
+
+ SecurityContextHolder.setContext(existingContext);
+
+ String responseDigest = DigestAuthUtils.generateDigest(false, USERNAME, REALM,
+ PASSWORD, "GET", REQUEST_URI, QOP, NONCE, NC, CNONCE);
+
+ request.addHeader(
+ "Authorization",
+ createAuthorizationHeader(USERNAME, REALM, NONCE, REQUEST_URI,
+ responseDigest, QOP, NC, CNONCE));
+
+ filter.setCreateAuthenticatedToken(true);
+ executeFilterInContainerSimulator(filter, request, true);
+
+ assertThat(existingAuthentication).isSameAs(existingContext.getAuthentication());
+ }
}