diff --git a/core/src/main/java/org/springframework/security/ui/preauth/AbstractPreAuthenticatedProcessingFilter.java b/core/src/main/java/org/springframework/security/ui/preauth/AbstractPreAuthenticatedProcessingFilter.java index f3f61dd7a5..e2fbc8c8ea 100755 --- a/core/src/main/java/org/springframework/security/ui/preauth/AbstractPreAuthenticatedProcessingFilter.java +++ b/core/src/main/java/org/springframework/security/ui/preauth/AbstractPreAuthenticatedProcessingFilter.java @@ -63,11 +63,11 @@ public abstract class AbstractPreAuthenticatedProcessingFilter extends SpringSec /** * Do the actual authentication for a pre-authenticated user. */ - private void doAuthenticate(HttpServletRequest httpRequest, HttpServletResponse httpResponse) { + private void doAuthenticate(HttpServletRequest request, HttpServletResponse response) { Authentication authResult = null; - Object principal = getPreAuthenticatedPrincipal(httpRequest); - Object credentials = getPreAuthenticatedCredentials(httpRequest); + Object principal = getPreAuthenticatedPrincipal(request); + Object credentials = getPreAuthenticatedCredentials(request); if (principal == null) { if (logger.isDebugEnabled()) { @@ -83,11 +83,11 @@ public abstract class AbstractPreAuthenticatedProcessingFilter extends SpringSec try { PreAuthenticatedAuthenticationToken authRequest = new PreAuthenticatedAuthenticationToken(principal, credentials); - authRequest.setDetails(authenticationDetailsSource.buildDetails(httpRequest)); + authRequest.setDetails(authenticationDetailsSource.buildDetails(request)); authResult = authenticationManager.authenticate(authRequest); - successfulAuthentication(httpRequest, httpResponse, authResult); + successfulAuthentication(request, response, authResult); } catch (AuthenticationException failed) { - unsuccessfulAuthentication(httpRequest, httpResponse, failed); + unsuccessfulAuthentication(request, response, failed); } } @@ -144,7 +144,7 @@ public abstract class AbstractPreAuthenticatedProcessingFilter extends SpringSec this.authenticationManager = authenticationManager; } - protected abstract Object getPreAuthenticatedPrincipal(HttpServletRequest httpRequest); + protected abstract Object getPreAuthenticatedPrincipal(HttpServletRequest request); - protected abstract Object getPreAuthenticatedCredentials(HttpServletRequest httpRequest); + protected abstract Object getPreAuthenticatedCredentials(HttpServletRequest request); } diff --git a/core/src/main/java/org/springframework/security/ui/preauth/PreAuthenticatedCredentialsNotFoundException.java b/core/src/main/java/org/springframework/security/ui/preauth/PreAuthenticatedCredentialsNotFoundException.java new file mode 100644 index 0000000000..a65b835378 --- /dev/null +++ b/core/src/main/java/org/springframework/security/ui/preauth/PreAuthenticatedCredentialsNotFoundException.java @@ -0,0 +1,11 @@ +package org.springframework.security.ui.preauth; + +import org.springframework.security.AuthenticationException; + +public class PreAuthenticatedCredentialsNotFoundException extends AuthenticationException { + + public PreAuthenticatedCredentialsNotFoundException(String msg) { + super(msg); + } + +} diff --git a/core/src/main/java/org/springframework/security/ui/preauth/header/RequestHeaderPreAuthenticatedProcessingFilter.java b/core/src/main/java/org/springframework/security/ui/preauth/header/RequestHeaderPreAuthenticatedProcessingFilter.java new file mode 100644 index 0000000000..65bd427583 --- /dev/null +++ b/core/src/main/java/org/springframework/security/ui/preauth/header/RequestHeaderPreAuthenticatedProcessingFilter.java @@ -0,0 +1,76 @@ +package org.springframework.security.ui.preauth.header; + +import javax.servlet.http.HttpServletRequest; + +import org.springframework.security.ui.FilterChainOrder; +import org.springframework.security.ui.preauth.AbstractPreAuthenticatedProcessingFilter; +import org.springframework.security.ui.preauth.PreAuthenticatedCredentialsNotFoundException; +import org.springframework.util.Assert; + +/** + * A simple pre-authenticated filter which obtains the username from a request header, for use with systems such as + * CA Siteminder. + *

+ * As with most pre-authenticated scenarios, it is essential that the external authentication system is set up + * correctly as this filter does no authentication whatsoever. All the protection is assumed to be provided externally + * and if this filter is included inappropriately in a configuration, it would be possible to assume the + * identity of a user merely by setting the correct header name. This also means it should not be used in combination + * with other Spring Security authentication mechanisms such as form login, as this would imply there was a means of + * bypassing the external system which would be risky. + *

+ * The property principalRequestHeader is the name of the request header that contains the username. It + * defaults to "SM_USER" for compatibility with Siteminder. + * + * + * @author Luke Taylor + * @version $Id$ + * @since 2.0 + */ +public class RequestHeaderPreAuthenticatedProcessingFilter extends AbstractPreAuthenticatedProcessingFilter { + private String principalRequestHeader = "SM_USER"; + private String credentialsRequestHeader; + + /** + * Read and returns the header named by principalRequestHeader from the request. + * + * @throws PreAuthenticatedCredentialsNotFoundException if the header is missing + */ + protected Object getPreAuthenticatedPrincipal(HttpServletRequest request) { + String principal = request.getHeader(principalRequestHeader); + + if (principal == null) { + throw new PreAuthenticatedCredentialsNotFoundException(principalRequestHeader + + " header not found in request."); + } + + return principal; + } + + /** + * Credentials aren't usually applicable, but if a credentialsRequestHeader is set, this + * will be read and used as the credentials value. Otherwise a dummy value will be used. + */ + protected Object getPreAuthenticatedCredentials(HttpServletRequest request) { + if (credentialsRequestHeader != null) { + String credentials = request.getHeader(credentialsRequestHeader); + + return credentials; + } + + return "N/A"; + } + + public void setPrincipalRequestHeader(String principalRequestHeader) { + Assert.hasText(principalRequestHeader, "principalRequestHeader must not be empty or null"); + this.principalRequestHeader = principalRequestHeader; + } + + public void setCredentialsRequestHeader(String credentialsRequestHeader) { + Assert.hasText(credentialsRequestHeader, "credentialsRequestHeader must not be empty or null"); + this.credentialsRequestHeader = credentialsRequestHeader; + } + + public int getOrder() { + return FilterChainOrder.PRE_AUTH_FILTER; + } +} diff --git a/core/src/test/java/org/springframework/security/ui/preauth/header/RequestHeaderPreAuthenticatedProcessingFilterTests.java b/core/src/test/java/org/springframework/security/ui/preauth/header/RequestHeaderPreAuthenticatedProcessingFilterTests.java new file mode 100644 index 0000000000..f575a6b3ff --- /dev/null +++ b/core/src/test/java/org/springframework/security/ui/preauth/header/RequestHeaderPreAuthenticatedProcessingFilterTests.java @@ -0,0 +1,83 @@ +package org.springframework.security.ui.preauth.header; + +import static org.junit.Assert.*; + +import org.junit.After; +import org.junit.Test; +import org.springframework.mock.web.MockFilterChain; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.springframework.security.MockAuthenticationManager; +import org.springframework.security.context.SecurityContextHolder; +import org.springframework.security.ui.preauth.PreAuthenticatedCredentialsNotFoundException; + +/** + * + * @author Luke Taylor + * @version $Id$ + */ +public class RequestHeaderPreAuthenticatedProcessingFilterTests { + + @After + public void clearContext() { + SecurityContextHolder.clearContext(); + } + + @Test(expected = PreAuthenticatedCredentialsNotFoundException.class) + public void rejectsMissingHeader() throws Exception { + MockHttpServletRequest request = new MockHttpServletRequest(); + MockHttpServletResponse response = new MockHttpServletResponse(); + MockFilterChain chain = new MockFilterChain(); + RequestHeaderPreAuthenticatedProcessingFilter filter = new RequestHeaderPreAuthenticatedProcessingFilter(); + filter.getOrder(); + + filter.doFilter(request, response, chain); + } + + @Test + public void defaultsToUsingSiteminderHeader() throws Exception { + MockHttpServletRequest request = new MockHttpServletRequest(); + request.addHeader("SM_USER", "cat"); + MockHttpServletResponse response = new MockHttpServletResponse(); + MockFilterChain chain = new MockFilterChain(); + RequestHeaderPreAuthenticatedProcessingFilter filter = new RequestHeaderPreAuthenticatedProcessingFilter(); + filter.setAuthenticationManager(new MockAuthenticationManager()); + + filter.doFilter(request, response, chain); + assertNotNull(SecurityContextHolder.getContext().getAuthentication()); + assertEquals("cat", SecurityContextHolder.getContext().getAuthentication().getName()); + assertEquals("N/A", SecurityContextHolder.getContext().getAuthentication().getCredentials()); + } + + @Test + public void alternativeHeaderNameIsSupported() throws Exception { + MockHttpServletRequest request = new MockHttpServletRequest(); + request.addHeader("myUsernameHeader", "wolfman"); + MockHttpServletResponse response = new MockHttpServletResponse(); + MockFilterChain chain = new MockFilterChain(); + RequestHeaderPreAuthenticatedProcessingFilter filter = new RequestHeaderPreAuthenticatedProcessingFilter(); + filter.setAuthenticationManager(new MockAuthenticationManager()); + filter.setPrincipalRequestHeader("myUsernameHeader"); + + filter.doFilter(request, response, chain); + assertNotNull(SecurityContextHolder.getContext().getAuthentication()); + assertEquals("wolfman", SecurityContextHolder.getContext().getAuthentication().getName()); + } + + @Test + public void credentialsAreRetrievedIfHeaderNameIsSet() throws Exception { + MockHttpServletRequest request = new MockHttpServletRequest(); + MockHttpServletResponse response = new MockHttpServletResponse(); + MockFilterChain chain = new MockFilterChain(); + RequestHeaderPreAuthenticatedProcessingFilter filter = new RequestHeaderPreAuthenticatedProcessingFilter(); + filter.setAuthenticationManager(new MockAuthenticationManager()); + filter.setCredentialsRequestHeader("myCredentialsHeader"); + request.addHeader("SM_USER", "cat"); + request.addHeader("myCredentialsHeader", "catspassword"); + + filter.doFilter(request, response, chain); + assertNotNull(SecurityContextHolder.getContext().getAuthentication()); + assertEquals("catspassword", SecurityContextHolder.getContext().getAuthentication().getCredentials()); + } + +}