diff --git a/core/src/main/java/org/acegisecurity/ui/webapp/SiteminderAuthenticationProcessingFilter.java b/core/src/main/java/org/acegisecurity/ui/webapp/SiteminderAuthenticationProcessingFilter.java index f5f1a0c8ed..caa81e11d4 100644 --- a/core/src/main/java/org/acegisecurity/ui/webapp/SiteminderAuthenticationProcessingFilter.java +++ b/core/src/main/java/org/acegisecurity/ui/webapp/SiteminderAuthenticationProcessingFilter.java @@ -10,48 +10,43 @@ import net.sf.acegisecurity.providers.UsernamePasswordAuthenticationToken; import net.sf.acegisecurity.ui.WebAuthenticationDetails; /** - * Extends Acegi's AuthenticationProcessingFilter to use Siteminder's headers for identification. + * Extends Acegi's AuthenticationProcessingFilter to pick up Netegrity + * Siteminder's headers. * *
- * Provides the ability set all source key names and also provides form-based authentication as a backup. - *
- * - *- * You must set the Siteminder header keys, otherwise Siteminder checks will be skipped (there - * are no defaults). If the Siteminder check is unsuccessful or if the headers are not - * defined/found in the HTTP request, then the form parameters will be checked (see below). - * This allows applications to function even when their Siteminder infrastructure - * is unavailable, as is often the case during development. If you do not wish to use backup - * form-based authentication, then set the form parameter keys to null/blank. + * Also provides a backup form-based authentication and the ability set source + * key names. *
* *
- * Siteminder must present at least one HTTP header to this filter - typically
- * containing a unique identifier such a username, an employee number or a national ID.
- * This makes sense because Siteminder has already authenticated the user prior to
- * getting to this filter, so we're really only using it for identification and not authentication.
- * Set the siteminderUsernameHeaderKey
value to tell the filter where to greb the "username"
- * value. You'll typically also set the siteminderPasswordHeaderKey
to the same header key.
- * Just remember to modify your AuthenticationDAO so that it can handle identity-only requests!
+ * Siteminder must present two headers to this filter, a
+ * username and password. You must set the header keys before this filter is
+ * used for authentication, otherwise Siteminder checks will be skipped. If the
+ * Siteminder check is unsuccessful (i.e. if the headers are not found), then
+ * the form parameters will be checked (see next paragraph). This allows
+ * applications to optionally function even when their Siteminder infrastructure
+ * is unavailable, as is often the case during development.
*
- * Forms must present two parameters to this filter: a username and a password. - * If not specified, the parameter names to use are defaulted to the static fields - * {@link #ACEGI_SECURITY_FORM_USERNAME_KEY} and {@link #ACEGI_SECURITY_FORM_PASSWORD_KEY}. + * Login forms must present two parameters to this filter: a + * username and password. If not specified, the parameter names to use are + * contained in the static fields {@link #ACEGI_SECURITY_FORM_USERNAME_KEY} and + * {@link #ACEGI_SECURITY_FORM_PASSWORD_KEY}. *
* *
- * Do not use this class directly. Instead, configure web.xml
- * to use the {@link net.sf.acegisecurity.util.FilterChainProxy} and include this filter.
+ * Do not use this class directly. Instead, configure
+ * web.xml
to use the
+ * {@link net.sf.acegisecurity.util.FilterToBeanProxy}.
*
/j_acegi_security_check
.
- *
- * @return the default
- */
+ /***************************************************************************
+ * This filter by default responds to /j_acegi_security_check
.
+ *
+ * @return the default
+ */
public String getDefaultFilterProcessesUrl() {
return "/j_acegi_security_check";
}
- public Authentication attemptAuthentication(HttpServletRequest request) throws AuthenticationException {
+ /**
+ * @see net.sf.acegisecurity.ui.AbstractProcessingFilter#attemptAuthentication(javax.servlet.http.HttpServletRequest)
+ */
+ public Authentication attemptAuthentication(HttpServletRequest request)
+ throws AuthenticationException {
String username = null;
String password = null;
// Check the Siteminder headers for authentication info
if (siteminderUsernameHeaderKey != null
- && siteminderUsernameHeaderKey.length() > 0
- && siteminderPasswordHeaderKey != null
- && siteminderPasswordHeaderKey.length() > 0) {
+ && siteminderUsernameHeaderKey.length() > 0
+ && siteminderPasswordHeaderKey != null
+ && siteminderPasswordHeaderKey.length() > 0) {
username = request.getHeader(siteminderUsernameHeaderKey);
password = request.getHeader(siteminderPasswordHeaderKey);
}
- // If the Siteminder authentication info wasn't available, then get it from the form parameters
- if (username == null || username.length() == 0 || password == null || password.length() == 0) {
+ // If the Siteminder authentication info wasn't available, then get it
+ // from the form parameters
+ if (username == null || username.length() == 0 || password == null
+ || password.length() == 0) {
- //System.out.println("Siteminder headers not found for authentication");
+ System.out
+ .println("Siteminder headers not found for authentication, so trying to use form values");
- if (formUsernameParameterKey != null && formUsernameParameterKey.length() > 0) {
+ if (formUsernameParameterKey != null
+ && formUsernameParameterKey.length() > 0) {
username = request.getParameter(formUsernameParameterKey);
- }
- else {
- username = request.getParameter(ACEGI_SECURITY_FORM_USERNAME_KEY);
+ } else {
+ username = request
+ .getParameter(ACEGI_SECURITY_FORM_USERNAME_KEY);
}
password = obtainPassword(request);
}
- // If either are null, set them to blank to avoid a NPE.
- if (username == null) {
+ // Convert username and password to upper case. This is normally not a
+ // good practice but we do it here because Siteminder gives us the username
+ // in lower case, while most backing systems store it in upper case.
+ if (username != null) {
+ username = username.toUpperCase();
+ } else {
+ // If username is null, set to blank to avoid a NPE.
username = "";
}
- if (password == null) {
+ if (password != null) {
+ password = password.toUpperCase();
+ } else {
+ // If password is null, set to blank to avoid a NPE.
password = "";
}
- UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
+ UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
+ username, password);
// Allow subclasses to set the "details" property
setDetails(request, authRequest);
// Place the last username attempted into HttpSession for views
- request.getSession().setAttribute(ACEGI_SECURITY_LAST_USERNAME_KEY, username);
+ request.getSession().setAttribute(ACEGI_SECURITY_LAST_USERNAME_KEY,
+ username);
return this.getAuthenticationManager().authenticate(authRequest);
+
}
+ /**
+ * @see javax.servlet.Filter#init(javax.servlet.FilterConfig)
+ */
public void init(FilterConfig filterConfig) throws ServletException {
}
- /***
- * Provided so that subclasses may configure what is put into the
- * authentication request's details property. The default implementation
- * simply constructs {@link WebAuthenticationDetails}.
- *
- * @param request that an authentication request is being created for
- * @param authRequest the authentication request object that should have
- * its details set
- */
- protected void setDetails(HttpServletRequest request, UsernamePasswordAuthenticationToken authRequest) {
+ /***************************************************************************
+ * Provided so that subclasses may configure what is put into the
+ * authentication request's details property. The default implementation
+ * simply constructs {@link WebAuthenticationDetails}.
+ *
+ * @param request that an authentication request is being created for
+ * @param authRequest the authentication request object that should have its details set
+ */
+ protected void setDetails(HttpServletRequest request,
+ UsernamePasswordAuthenticationToken authRequest) {
authRequest.setDetails(new WebAuthenticationDetails(request));
}
- /***
- * Enables subclasses to override the composition of the password, such as
- * by including additional values and a separator.
- *
- *
- * This might be used for example if a postcode/zipcode was required in
- * addition to the password. A delimiter such as a pipe (|) should be used
- * to separate the password and extended value(s). The
- * AuthenticationDao
will need to generate the expected
- * password in a corresponding manner.
- *
Authentication
request token to the
- * AuthenticationManager
- */
+ /***************************************************************************
+ * Enables subclasses to override the composition of the password, such as
+ * by including additional values and a separator.
+ *
+ *
+ * This might be used for example if a postcode/zipcode was required in
+ * addition to the password. A delimiter such as a pipe (|) should be used
+ * to separate the password and extended value(s). The
+ * AuthenticationDao
will need to generate the expected
+ * password in a corresponding manner.
+ *
Authentication
request token to the
+ * AuthenticationManager
+ */
protected String obtainPassword(HttpServletRequest request) {
- if (formPasswordParameterKey != null && formPasswordParameterKey.length() > 0) {
+ if (formPasswordParameterKey != null
+ && formPasswordParameterKey.length() > 0) {
return request.getParameter(formPasswordParameterKey);
- }
- else {
+ } else {
return request.getParameter(ACEGI_SECURITY_FORM_PASSWORD_KEY);
}
diff --git a/core/src/test/java/org/acegisecurity/ui/webapp/SiteminderAuthenticationProcessingFilterTests.java b/core/src/test/java/org/acegisecurity/ui/webapp/SiteminderAuthenticationProcessingFilterTests.java
new file mode 100644
index 0000000000..aff5e0e7a4
--- /dev/null
+++ b/core/src/test/java/org/acegisecurity/ui/webapp/SiteminderAuthenticationProcessingFilterTests.java
@@ -0,0 +1,164 @@
+package net.sf.acegisecurity.ui.webapp;
+
+import junit.framework.TestCase;
+import net.sf.acegisecurity.Authentication;
+import net.sf.acegisecurity.MockAuthenticationManager;
+import net.sf.acegisecurity.ui.WebAuthenticationDetails;
+
+import org.springframework.mock.web.MockHttpServletRequest;
+
+/**
+ * Tests SiteminderAuthenticationProcessingFilter.
+ *
+ * @author Ben Alex
+ * @author Scott McCrory
+ * @version CVS $Id$
+ */
+public class SiteminderAuthenticationProcessingFilterTests extends TestCase {
+
+ /**
+ * Basic constructor.
+ */
+ public SiteminderAuthenticationProcessingFilterTests() {
+ super();
+ }
+
+ /**
+ * Argument constructor.
+ *
+ * @param arg0
+ */
+ public SiteminderAuthenticationProcessingFilterTests(String arg0) {
+ super(arg0);
+ }
+
+ /**
+ * @see junit.framework.TestCase#setUp()
+ */
+ public final void setUp() throws Exception {
+ super.setUp();
+ }
+
+ /**
+ * Runs the tests as a command-line program.
+ *
+ * @param args
+ */
+ public static void main(String[] args) {
+ junit.textui.TestRunner
+ .run(SiteminderAuthenticationProcessingFilterTests.class);
+ }
+
+ /**
+ * Tests the class' getters.
+ */
+ public void testGetters() {
+ SiteminderAuthenticationProcessingFilter filter = new SiteminderAuthenticationProcessingFilter();
+ assertEquals("/j_acegi_security_check", filter
+ .getDefaultFilterProcessesUrl());
+ }
+
+ /**
+ * Tests normal form processing.
+ *
+ * @throws Exception
+ */
+ public void testFormNormalOperation() throws Exception {
+
+ MockHttpServletRequest request = new MockHttpServletRequest();
+ request
+ .addParameter(
+ SiteminderAuthenticationProcessingFilter.ACEGI_SECURITY_FORM_USERNAME_KEY,
+ "marissa");
+ request
+ .addParameter(
+ SiteminderAuthenticationProcessingFilter.ACEGI_SECURITY_FORM_PASSWORD_KEY,
+ "koala");
+
+ MockAuthenticationManager authMgr = new MockAuthenticationManager(true);
+
+ SiteminderAuthenticationProcessingFilter filter = new SiteminderAuthenticationProcessingFilter();
+ filter.setAuthenticationManager(authMgr);
+ filter.init(null);
+
+ Authentication result = filter.attemptAuthentication(request);
+ assertTrue(result != null);
+ assertEquals("127.0.0.1", ((WebAuthenticationDetails) result
+ .getDetails()).getRemoteAddress());
+
+ }
+
+ /**
+ * Tests form null password handling.
+ *
+ * @throws Exception
+ */
+ public void testFormNullPasswordHandledGracefully() throws Exception {
+
+ MockHttpServletRequest request = new MockHttpServletRequest();
+ request
+ .addParameter(
+ SiteminderAuthenticationProcessingFilter.ACEGI_SECURITY_FORM_USERNAME_KEY,
+ "marissa");
+
+ MockAuthenticationManager authMgr = new MockAuthenticationManager(true);
+
+ SiteminderAuthenticationProcessingFilter filter = new SiteminderAuthenticationProcessingFilter();
+ filter.setAuthenticationManager(authMgr);
+ filter.init(null);
+
+ Authentication result = filter.attemptAuthentication(request);
+ assertTrue(result != null);
+
+ }
+
+ /**
+ * Tests form null username handling.
+ *
+ * @throws Exception
+ */
+ public void testFormNullUsernameHandledGracefully() throws Exception {
+
+ MockHttpServletRequest request = new MockHttpServletRequest();
+ request
+ .addParameter(
+ SiteminderAuthenticationProcessingFilter.ACEGI_SECURITY_FORM_PASSWORD_KEY,
+ "koala");
+
+ MockAuthenticationManager authMgr = new MockAuthenticationManager(true);
+
+ SiteminderAuthenticationProcessingFilter filter = new SiteminderAuthenticationProcessingFilter();
+ filter.setAuthenticationManager(authMgr);
+ filter.init(null);
+
+ Authentication result = filter.attemptAuthentication(request);
+ assertTrue(result != null);
+
+ }
+
+ /**
+ * Tests normal Siteminder header processing.
+ *
+ * @throws Exception
+ */
+ public void testSiteminderNormalOperation() throws Exception {
+
+ MockHttpServletRequest request = new MockHttpServletRequest();
+ request.addHeader("SM_USER", "E099544");
+
+ MockAuthenticationManager authMgr = new MockAuthenticationManager(true);
+
+ SiteminderAuthenticationProcessingFilter filter = new SiteminderAuthenticationProcessingFilter();
+ filter.setAuthenticationManager(authMgr);
+ filter.setSiteminderUsernameHeaderKey("SM_USER");
+ filter.setSiteminderPasswordHeaderKey("SM_USER");
+ filter.init(null);
+
+ Authentication result = filter.attemptAuthentication(request);
+ assertTrue(result != null);
+ assertEquals("127.0.0.1", ((WebAuthenticationDetails) result
+ .getDetails()).getRemoteAddress());
+
+ }
+
+}