| Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||||||
| SiteminderAuthenticationProcessingFilter |
|
| 2.0;2 |
| 1 | /* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited |
|
| 2 | * |
|
| 3 | * Licensed under the Apache License, Version 2.0 (the "License"); |
|
| 4 | * you may not use this file except in compliance with the License. |
|
| 5 | * You may obtain a copy of the License at |
|
| 6 | * |
|
| 7 | * http://www.apache.org/licenses/LICENSE-2.0 |
|
| 8 | * |
|
| 9 | * Unless required by applicable law or agreed to in writing, software |
|
| 10 | * distributed under the License is distributed on an "AS IS" BASIS, |
|
| 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
| 12 | * See the License for the specific language governing permissions and |
|
| 13 | * limitations under the License. |
|
| 14 | */ |
|
| 15 | ||
| 16 | package org.acegisecurity.ui.webapp; |
|
| 17 | ||
| 18 | import org.acegisecurity.Authentication; |
|
| 19 | import org.acegisecurity.AuthenticationException; |
|
| 20 | ||
| 21 | import org.acegisecurity.context.HttpSessionContextIntegrationFilter; |
|
| 22 | import org.acegisecurity.context.SecurityContext; |
|
| 23 | ||
| 24 | import org.acegisecurity.providers.UsernamePasswordAuthenticationToken; |
|
| 25 | ||
| 26 | import org.apache.commons.logging.Log; |
|
| 27 | import org.apache.commons.logging.LogFactory; |
|
| 28 | ||
| 29 | import javax.servlet.http.HttpServletRequest; |
|
| 30 | import javax.servlet.http.HttpServletResponse; |
|
| 31 | ||
| 32 | ||
| 33 | /** |
|
| 34 | * Extends Acegi's AuthenticationProcessingFilter to pick up CA/Netegrity Siteminder headers.<P>Also provides a |
|
| 35 | * backup form-based authentication and the ability set source key names.</p> |
|
| 36 | * <P><B>Siteminder</B> must present two <B>headers</B> to this filter, a username and password. You must set the |
|
| 37 | * header keys before this filter is used for authentication, otherwise Siteminder checks will be skipped. If the |
|
| 38 | * Siteminder check is unsuccessful (i.e. if the headers are not found), then the form parameters will be checked (see |
|
| 39 | * next paragraph). This allows applications to optionally function even when their Siteminder infrastructure is |
|
| 40 | * unavailable, as is often the case during development.</p> |
|
| 41 | * <P><B>Login forms</B> must present two <B>parameters</B> to this filter: a username and password. If not |
|
| 42 | * specified, the parameter names to use are contained in the static fields {@link #ACEGI_SECURITY_FORM_USERNAME_KEY} |
|
| 43 | * and {@link #ACEGI_SECURITY_FORM_PASSWORD_KEY}.</p> |
|
| 44 | * <P><B>Do not use this class directly.</B> Instead, configure <code>web.xml</code> to use the {@link |
|
| 45 | * org.acegisecurity.util.FilterToBeanProxy}.</p> |
|
| 46 | */ |
|
| 47 | public class SiteminderAuthenticationProcessingFilter extends AuthenticationProcessingFilter { |
|
| 48 | //~ Static fields/initializers ===================================================================================== |
|
| 49 | ||
| 50 | /** Log instance for debugging */ |
|
| 51 | 2 | private static final Log logger = LogFactory.getLog(SiteminderAuthenticationProcessingFilter.class); |
| 52 | ||
| 53 | //~ Instance fields ================================================================================================ |
|
| 54 | ||
| 55 | /** Form password request key. */ |
|
| 56 | 6 | private String formPasswordParameterKey = null; |
| 57 | ||
| 58 | /** Form username request key. */ |
|
| 59 | 6 | private String formUsernameParameterKey = null; |
| 60 | ||
| 61 | /** Siteminder password header key. */ |
|
| 62 | 6 | private String siteminderPasswordHeaderKey = null; |
| 63 | ||
| 64 | /** Siteminder username header key. */ |
|
| 65 | 6 | private String siteminderUsernameHeaderKey = null; |
| 66 | ||
| 67 | //~ Constructors =================================================================================================== |
|
| 68 | ||
| 69 | /** |
|
| 70 | * Basic constructor. |
|
| 71 | */ |
|
| 72 | public SiteminderAuthenticationProcessingFilter() { |
|
| 73 | 6 | super(); |
| 74 | 6 | } |
| 75 | ||
| 76 | //~ Methods ======================================================================================================== |
|
| 77 | ||
| 78 | /** |
|
| 79 | * |
|
| 80 | * @see org.acegisecurity.ui.AbstractProcessingFilter#attemptAuthentication(javax.servlet.http.HttpServletRequest) |
|
| 81 | */ |
|
| 82 | public Authentication attemptAuthentication(HttpServletRequest request) |
|
| 83 | throws AuthenticationException { |
|
| 84 | 4 | String username = null; |
| 85 | 4 | String password = null; |
| 86 | ||
| 87 | // Check the Siteminder headers for authentication info |
|
| 88 | 4 | if ((siteminderUsernameHeaderKey != null) && (siteminderUsernameHeaderKey.length() > 0) |
| 89 | && (siteminderPasswordHeaderKey != null) && (siteminderPasswordHeaderKey.length() > 0)) { |
|
| 90 | 0 | username = request.getHeader(siteminderUsernameHeaderKey); |
| 91 | 0 | password = request.getHeader(siteminderPasswordHeaderKey); |
| 92 | } |
|
| 93 | ||
| 94 | // If the Siteminder authentication info wasn't available, then get it |
|
| 95 | // from the form parameters |
|
| 96 | 4 | if ((username == null) || (username.length() == 0) || (password == null) || (password.length() == 0)) { |
| 97 | 4 | if (logger.isDebugEnabled()) { |
| 98 | 0 | logger.debug("Siteminder headers not found for authentication, so trying to use form values"); |
| 99 | } |
|
| 100 | ||
| 101 | 4 | if ((formUsernameParameterKey != null) && (formUsernameParameterKey.length() > 0)) { |
| 102 | 0 | username = request.getParameter(formUsernameParameterKey); |
| 103 | } else { |
|
| 104 | 4 | username = request.getParameter(ACEGI_SECURITY_FORM_USERNAME_KEY); |
| 105 | } |
|
| 106 | ||
| 107 | 4 | password = obtainPassword(request); |
| 108 | } |
|
| 109 | ||
| 110 | // Convert username and password to upper case. This is normally not a |
|
| 111 | // good practice but we do it here because Siteminder gives us the username |
|
| 112 | // in lower case, while most backing systems store it in upper case. |
|
| 113 | 4 | if (username != null) { |
| 114 | 2 | username = username.toUpperCase(); |
| 115 | } else { |
|
| 116 | // If username is null, set to blank to avoid a NPE. |
|
| 117 | 2 | username = ""; |
| 118 | } |
|
| 119 | ||
| 120 | 4 | if (password != null) { |
| 121 | 2 | password = password.toUpperCase(); |
| 122 | } else { |
|
| 123 | // If password is null, set to blank to avoid a NPE. |
|
| 124 | 2 | password = ""; |
| 125 | } |
|
| 126 | ||
| 127 | 4 | UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password); |
| 128 | ||
| 129 | // Allow subclasses to set the "details" property |
|
| 130 | 4 | setDetails(request, authRequest); |
| 131 | ||
| 132 | // Place the last username attempted into HttpSession for views |
|
| 133 | 4 | request.getSession().setAttribute(ACEGI_SECURITY_LAST_USERNAME_KEY, username); |
| 134 | ||
| 135 | 4 | return this.getAuthenticationManager().authenticate(authRequest); |
| 136 | } |
|
| 137 | ||
| 138 | /** |
|
| 139 | * Returns the form password parameter key. |
|
| 140 | * |
|
| 141 | * @return The form password parameter key. |
|
| 142 | */ |
|
| 143 | public String getFormPasswordParameterKey() { |
|
| 144 | 0 | return formPasswordParameterKey; |
| 145 | } |
|
| 146 | ||
| 147 | /** |
|
| 148 | * Returns the form username parameter key. |
|
| 149 | * |
|
| 150 | * @return The form username parameter key. |
|
| 151 | */ |
|
| 152 | public String getFormUsernameParameterKey() { |
|
| 153 | 1 | return formUsernameParameterKey; |
| 154 | } |
|
| 155 | ||
| 156 | /** |
|
| 157 | * Returns the Siteminder password header key. |
|
| 158 | * |
|
| 159 | * @return The Siteminder password header key. |
|
| 160 | */ |
|
| 161 | public String getSiteminderPasswordHeaderKey() { |
|
| 162 | 0 | return siteminderPasswordHeaderKey; |
| 163 | } |
|
| 164 | ||
| 165 | /** |
|
| 166 | * Returns the Siteminder username header key. |
|
| 167 | * |
|
| 168 | * @return The Siteminder username header key. |
|
| 169 | */ |
|
| 170 | public String getSiteminderUsernameHeaderKey() { |
|
| 171 | 1 | return siteminderUsernameHeaderKey; |
| 172 | } |
|
| 173 | ||
| 174 | /** |
|
| 175 | * Overridden method to obtain different value depending on whether Siteminder or form validation is being |
|
| 176 | * performed. |
|
| 177 | * |
|
| 178 | * @param request so that request attributes can be retrieved |
|
| 179 | * |
|
| 180 | * @return the password that will be presented in the <code>Authentication</code> request token to the |
|
| 181 | * <code>AuthenticationManager</code> |
|
| 182 | */ |
|
| 183 | protected String obtainPassword(HttpServletRequest request) { |
|
| 184 | 4 | if ((formPasswordParameterKey != null) && (formPasswordParameterKey.length() > 0)) { |
| 185 | 0 | return request.getParameter(formPasswordParameterKey); |
| 186 | } else { |
|
| 187 | 4 | return request.getParameter(ACEGI_SECURITY_FORM_PASSWORD_KEY); |
| 188 | } |
|
| 189 | } |
|
| 190 | ||
| 191 | /** |
|
| 192 | * Overridden to perform authentication not only on j_security_check, but also on requests for the default |
|
| 193 | * target URL when the user isn't already authenticated.<p>Thank you Paul Garvey for providing a |
|
| 194 | * straightforward solution (and code) for this!</p> |
|
| 195 | * |
|
| 196 | * @see org.acegisecurity.ui.AbstractProcessingFilter#requiresAuthentication(javax.servlet.http.HttpServletRequest, |
|
| 197 | * javax.servlet.http.HttpServletResponse) |
|
| 198 | */ |
|
| 199 | protected boolean requiresAuthentication(final HttpServletRequest request, final HttpServletResponse response) { |
|
| 200 | 3 | String uri = request.getRequestURI(); |
| 201 | 3 | int pathParamIndex = uri.indexOf(';'); |
| 202 | ||
| 203 | 3 | if (pathParamIndex > 0) { |
| 204 | // strip everything after the first semi-colon |
|
| 205 | 0 | uri = uri.substring(0, pathParamIndex); |
| 206 | } |
|
| 207 | ||
| 208 | //attempt authentication if j_secuity_check is present or if the getDefaultTargetUrl() |
|
| 209 | //is present and user is not already authenticated. |
|
| 210 | 3 | boolean bAuthenticated = false; |
| 211 | 3 | SecurityContext context = (SecurityContext) |
| 212 | request.getSession().getAttribute(HttpSessionContextIntegrationFilter.ACEGI_SECURITY_CONTEXT_KEY); |
|
| 213 | ||
| 214 | 3 | if (context != null) { |
| 215 | 0 | Authentication auth = context.getAuthentication(); |
| 216 | ||
| 217 | 0 | if ((auth != null) && auth instanceof UsernamePasswordAuthenticationToken) { |
| 218 | 0 | UsernamePasswordAuthenticationToken token = (UsernamePasswordAuthenticationToken) auth; |
| 219 | 0 | bAuthenticated = token.isAuthenticated(); |
| 220 | } |
|
| 221 | } |
|
| 222 | ||
| 223 | // if true is returned then authentication will be attempted. |
|
| 224 | 3 | boolean bAttemptAuthentication = (uri.endsWith(request.getContextPath() + getFilterProcessesUrl())) |
| 225 | || ((getDefaultTargetUrl() != null) && uri.endsWith(getDefaultTargetUrl()) && !bAuthenticated); |
|
| 226 | ||
| 227 | 3 | if (logger.isDebugEnabled()) { |
| 228 | 0 | logger.debug("Authentication attempted for the following URI ==> " + uri + " is " + bAttemptAuthentication); |
| 229 | } |
|
| 230 | ||
| 231 | 3 | return bAttemptAuthentication; |
| 232 | } |
|
| 233 | ||
| 234 | /** |
|
| 235 | * Sets the form password parameter key. |
|
| 236 | * |
|
| 237 | * @param key The form password parameter key. |
|
| 238 | */ |
|
| 239 | public void setFormPasswordParameterKey(final String key) { |
|
| 240 | 0 | this.formPasswordParameterKey = key; |
| 241 | 0 | } |
| 242 | ||
| 243 | /** |
|
| 244 | * Sets the form username parameter key. |
|
| 245 | * |
|
| 246 | * @param key The form username parameter key. |
|
| 247 | */ |
|
| 248 | public void setFormUsernameParameterKey(final String key) { |
|
| 249 | 1 | this.formUsernameParameterKey = key; |
| 250 | 1 | } |
| 251 | ||
| 252 | /** |
|
| 253 | * Sets the Siteminder password header key. |
|
| 254 | * |
|
| 255 | * @param key The Siteminder password header key. |
|
| 256 | */ |
|
| 257 | public void setSiteminderPasswordHeaderKey(final String key) { |
|
| 258 | 0 | this.siteminderPasswordHeaderKey = key; |
| 259 | 0 | } |
| 260 | ||
| 261 | /** |
|
| 262 | * Sets the Siteminder username header key. |
|
| 263 | * |
|
| 264 | * @param key The Siteminder username header key. |
|
| 265 | */ |
|
| 266 | public void setSiteminderUsernameHeaderKey(final String key) { |
|
| 267 | 3 | this.siteminderUsernameHeaderKey = key; |
| 268 | 3 | } |
| 269 | } |