View Javadoc

1   /* Copyright 2004, 2005 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.providers.UsernamePasswordAuthenticationToken;
22  
23  import org.acegisecurity.ui.WebAuthenticationDetails;
24  
25  import javax.servlet.FilterConfig;
26  import javax.servlet.ServletException;
27  import javax.servlet.http.HttpServletRequest;
28  
29  
30  /***
31   * Extends Acegi's AuthenticationProcessingFilter to pick up Netegrity
32   * Siteminder's headers.
33   * 
34   * <P>
35   * Also provides a backup form-based authentication and the ability set source
36   * key names.
37   * </p>
38   * 
39   * <P>
40   * <B>Siteminder</B> must present two <B>headers</B> to this filter, a username
41   * and password. You must set the header keys before this filter is used for
42   * authentication, otherwise Siteminder checks will be skipped. If the
43   * Siteminder check is unsuccessful (i.e. if the headers are not found), then
44   * the form parameters will be checked (see next paragraph). This allows
45   * applications to optionally function even when their Siteminder
46   * infrastructure is unavailable, as is often the case during development.
47   * </p>
48   * 
49   * <P>
50   * <B>Login forms</B> must present two <B>parameters</B> to this filter: a
51   * username and password. If not specified, the parameter names to use are
52   * contained in the static fields {@link #ACEGI_SECURITY_FORM_USERNAME_KEY}
53   * and {@link #ACEGI_SECURITY_FORM_PASSWORD_KEY}.
54   * </p>
55   * 
56   * <P>
57   * <B>Do not use this class directly.</B> Instead, configure
58   * <code>web.xml</code> to use the {@link
59   * org.acegisecurity.util.FilterToBeanProxy}.
60   * </p>
61   */
62  public class SiteminderAuthenticationProcessingFilter
63      extends AuthenticationProcessingFilter {
64      //~ Instance fields ========================================================
65  
66      /*** Form password request key. */
67      private String formPasswordParameterKey = null;
68  
69      /*** Form username request key. */
70      private String formUsernameParameterKey = null;
71  
72      /*** Siteminder password header key. */
73      private String siteminderPasswordHeaderKey = null;
74  
75      /*** Siteminder username header key. */
76      private String siteminderUsernameHeaderKey = null;
77  
78      //~ Constructors ===========================================================
79  
80      /***
81       * Basic constructor.
82       */
83      public SiteminderAuthenticationProcessingFilter() {
84          super();
85      }
86  
87      //~ Methods ================================================================
88  
89      /***
90       * @see org.acegisecurity.ui.AbstractProcessingFilter#attemptAuthentication(javax.servlet.http.HttpServletRequest)
91       */
92      public Authentication attemptAuthentication(HttpServletRequest request)
93          throws AuthenticationException {
94          String username = null;
95          String password = null;
96  
97          // Check the Siteminder headers for authentication info
98          if ((siteminderUsernameHeaderKey != null)
99              && (siteminderUsernameHeaderKey.length() > 0)
100             && (siteminderPasswordHeaderKey != null)
101             && (siteminderPasswordHeaderKey.length() > 0)) {
102             username = request.getHeader(siteminderUsernameHeaderKey);
103             password = request.getHeader(siteminderPasswordHeaderKey);
104         }
105 
106         // If the Siteminder authentication info wasn't available, then get it
107         // from the form parameters
108         if ((username == null) || (username.length() == 0)
109             || (password == null) || (password.length() == 0)) {
110             if (logger.isDebugEnabled()) {
111                 logger.debug(
112                     "Siteminder headers not found for authentication, so trying to use form values");
113             }
114 
115             if ((formUsernameParameterKey != null)
116                 && (formUsernameParameterKey.length() > 0)) {
117                 username = request.getParameter(formUsernameParameterKey);
118             } else {
119                 username = request.getParameter(ACEGI_SECURITY_FORM_USERNAME_KEY);
120             }
121 
122             password = obtainPassword(request);
123         }
124 
125         // Convert username and password to upper case. This is normally not a
126         // good practice but we do it here because Siteminder gives us the username
127         // in lower case, while most backing systems store it in upper case.
128         if (username != null) {
129             username = username.toUpperCase();
130         } else {
131             // If username is null, set to blank to avoid a NPE.
132             username = "";
133         }
134 
135         if (password != null) {
136             password = password.toUpperCase();
137         } else {
138             // If password is null, set to blank to avoid a NPE.
139             password = "";
140         }
141 
142         UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username,
143                 password);
144 
145         // Allow subclasses to set the "details" property
146         setDetails(request, authRequest);
147 
148         // Place the last username attempted into HttpSession for views
149         request.getSession()
150                .setAttribute(ACEGI_SECURITY_LAST_USERNAME_KEY, username);
151 
152         return this.getAuthenticationManager().authenticate(authRequest);
153     }
154 
155     /***
156      * This filter by default responds to <code>/j_acegi_security_check</code>.
157      *
158      * @return the default
159      */
160     public String getDefaultFilterProcessesUrl() {
161         return "/j_acegi_security_check";
162     }
163 
164     /***
165      * Returns the form password parameter key.
166      *
167      * @return The form password parameter key.
168      */
169     public String getFormPasswordParameterKey() {
170         return formPasswordParameterKey;
171     }
172 
173     /***
174      * Returns the form username parameter key.
175      *
176      * @return The form username parameter key.
177      */
178     public String getFormUsernameParameterKey() {
179         return formUsernameParameterKey;
180     }
181 
182     /***
183      * Returns the Siteminder password header key.
184      *
185      * @return The Siteminder password header key.
186      */
187     public String getSiteminderPasswordHeaderKey() {
188         return siteminderPasswordHeaderKey;
189     }
190 
191     /***
192      * Returns the Siteminder username header key.
193      *
194      * @return The Siteminder username header key.
195      */
196     public String getSiteminderUsernameHeaderKey() {
197         return siteminderUsernameHeaderKey;
198     }
199 
200     /***
201      * @see javax.servlet.Filter#init(javax.servlet.FilterConfig)
202      */
203     public void init(FilterConfig filterConfig) throws ServletException {}
204 
205     /***
206      * Enables subclasses to override the composition of the password, such as
207      * by including additional values and a separator.
208      * 
209      * <p>
210      * This might be used for example if a postcode/zipcode was required in
211      * addition to the password. A delimiter such as a pipe (|) should be used
212      * to separate the password and extended value(s). The
213      * <code>AuthenticationDao</code> will need to generate the expected
214      * password in a corresponding manner.
215      * </p>
216      *
217      * @param request so that request attributes can be retrieved
218      *
219      * @return the password that will be presented in the
220      *         <code>Authentication</code> request token to the
221      *         <code>AuthenticationManager</code>
222      */
223     protected String obtainPassword(HttpServletRequest request) {
224         if ((formPasswordParameterKey != null)
225             && (formPasswordParameterKey.length() > 0)) {
226             return request.getParameter(formPasswordParameterKey);
227         } else {
228             return request.getParameter(ACEGI_SECURITY_FORM_PASSWORD_KEY);
229         }
230     }
231 
232     /***
233      * Provided so that subclasses may configure what is put into the
234      * authentication request's details property. The default implementation
235      * simply constructs {@link WebAuthenticationDetails}.
236      *
237      * @param request that an authentication request is being created for
238      * @param authRequest the authentication request object that should have
239      *        its details set
240      */
241     protected void setDetails(HttpServletRequest request,
242         UsernamePasswordAuthenticationToken authRequest) {
243         authRequest.setDetails(new WebAuthenticationDetails(request));
244     }
245 
246     /***
247      * Sets the form password parameter key.
248      *
249      * @param key The form password parameter key.
250      */
251     public void setFormPasswordParameterKey(final String key) {
252         this.formPasswordParameterKey = key;
253     }
254 
255     /***
256      * Sets the form username parameter key.
257      *
258      * @param key The form username parameter key.
259      */
260     public void setFormUsernameParameterKey(final String key) {
261         this.formUsernameParameterKey = key;
262     }
263 
264     /***
265      * Sets the Siteminder password header key.
266      *
267      * @param key The Siteminder password header key.
268      */
269     public void setSiteminderPasswordHeaderKey(final String key) {
270         this.siteminderPasswordHeaderKey = key;
271     }
272 
273     /***
274      * Sets the Siteminder username header key.
275      *
276      * @param key The Siteminder username header key.
277      */
278     public void setSiteminderUsernameHeaderKey(final String key) {
279         this.siteminderUsernameHeaderKey = key;
280     }
281 }