1 /*
2 * JBoss, the OpenSource WebOS
3 *
4 * Distributable under LGPL license.
5 * See terms of license at gnu.org.
6 *
7 * This software is derived from works developed by the
8 * Apache Software Foundation (http://www.apache.org/), and its
9 * redistribution and use are further subject to the terms of the
10 * Apache Software License (see below), which is herein incorporated
11 * by reference.
12 *
13 * ====================================================================
14 *
15 * The Apache Software License, Version 1.1
16 *
17 * Copyright (c) 1999-2001 The Apache Software Foundation. All rights
18 * reserved.
19 *
20 * Redistribution and use in source and binary forms, with or without
21 * modification, are permitted provided that the following conditions
22 * are met:
23 *
24 * 1. Redistributions of source code must retain the above copyright
25 * notice, this list of conditions and the following disclaimer.
26 *
27 * 2. Redistributions in binary form must reproduce the above copyright
28 * notice, this list of conditions and the following disclaimer in
29 * the documentation and/or other materials provided with the
30 * distribution.
31 *
32 * 3. The end-user documentation included with the redistribution, if
33 * any, must include the following acknowlegement:
34 * "This product includes software developed by the
35 * Apache Software Foundation (http://www.apache.org/)."
36 * Alternately, this acknowlegement may appear in the software itself,
37 * if and wherever such third-party acknowlegements normally appear.
38 *
39 * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
40 * Foundation" must not be used to endorse or promote products derived
41 * from this software without prior written permission. For written
42 * permission, please contact apache@apache.org.
43 *
44 * 5. Products derived from this software may not be called "Apache"
45 * nor may "Apache" appear in their names without prior written
46 * permission of the Apache Group.
47 *
48 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
49 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
50 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
51 * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
52 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
53 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
54 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
55 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
56 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
57 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
58 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
59 * SUCH DAMAGE.
60 * ====================================================================
61 *
62 * This software consists of voluntary contributions made by many
63 * individuals on behalf of the Apache Software Foundation. For more
64 * information on the Apache Software Foundation, please see
65 * <http://www.apache.org/>.
66 *
67 * [Additional notices, if required by prior licensing conditions]
68 *
69 */
70 package org.jboss.web.tomcat.tc4.authenticator;
71
72
73 import java.lang.reflect.Method;
74 import java.security.Principal;
75
76 import javax.servlet.http.Cookie;
77 import javax.servlet.http.HttpServletResponse;
78
79 import org.apache.catalina.Container;
80 import org.apache.catalina.HttpRequest;
81 import org.apache.catalina.HttpResponse;
82 import org.apache.catalina.LifecycleException;
83 import org.apache.catalina.Pipeline;
84 import org.apache.catalina.Realm;
85 import org.apache.catalina.Session;
86 import org.apache.catalina.Valve;
87 import org.apache.catalina.authenticator.Constants;
88
89
90 /**
91 * Overrides the superclass version by using class
92 * <code>org.jboss.web.tomcat.tc4.authenticator.SingleSignOn</code> instead
93 * of <code>org.apache.catalina.authenticator.SingleSignOn</code> as its
94 * method expected single sign-on valve. This class also differs from
95 * the standard Tomcat version in its implementation of method
96 * {@link #register reqister}.
97 * <p>
98 * Basic implementation of the <b>Valve</b> interface that enforces the
99 * <code><security-constraint></code> elements in the web application
100 * deployment descriptor. This functionality is implemented as a Valve
101 * so that it can be ommitted in environments that do not require these
102 * features. Individual implementations of each supported authentication
103 * method can subclass this base class as required.
104 * <p>
105 * <b>USAGE CONSTRAINT</b>: When this class is utilized, the Context to
106 * which it is attached (or a parent Container in a hierarchy) must have an
107 * associated Realm that can be used for authenticating users and enumerating
108 * the roles to which they have been assigned.
109 * <p>
110 * <b>USAGE CONSTRAINT</b>: This Valve is only useful when processing HTTP
111 * requests. Requests of any other type will simply be passed through.
112 *
113 * @see #register
114 * @see SingleSignOn
115 * @see org.apache.catalina.authenticator.AuthenticatorBase
116 *
117 * @author B Stansberry, based on the work of Craig R. McClanahan
118 * @version $Revision: 1.1.2.1 $ $Date: 2003/11/22 08:12:44 $
119 */
120 public abstract class AuthenticatorBase
121 extends org.apache.catalina.authenticator.AuthenticatorBase
122 {
123
124
125 // ----------------------------------------------------- Instance Variables
126
127
128 /**
129 * Descriptive information about this implementation.
130 */
131 protected static final String info =
132 AuthenticatorBase.class.getName() + "/1.0";
133
134 /**
135 * The SingleSignOn implementation in our request processing chain,
136 * if there is one.
137 * <p>
138 * <b>NOTE:</b> This is an instance of
139 * <code>org.jboss.web.tomcat.tc4.authenticator.SingleSignOn</code>,
140 * not <code>org.apache.catalina.authenticator.SingleSignOn</code>.
141 *
142 */
143 protected SingleSignOn ourSSO = null;
144
145
146 // ------------------------------------------------------------- Properties
147
148
149 // --------------------------------------------------------- Public Methods
150
151
152 // ------------------------------------------------------ Protected Methods
153
154
155
156 /**
157 * Associate the specified single sign on identifier with the
158 * specified Session.
159 * <p>
160 * <b>IMPLEMENTATION NOTE:</b> Overrides the superclass version solely by
161 * using a <code>org.jboss.web.tomcat.tc4.authenticator.SingleSignOn</code>
162 * instead of an <code>org.apache.catalina.authenticator.SingleSignOn</code>
163 *
164 * @param ssoId Single sign on identifier
165 * @param session Session to be associated
166 */
167 protected void associate(String ssoId, Session session)
168 {
169
170 if (ourSSO == null)
171 return;
172 ourSSO.associate(ssoId, session);
173
174 }
175
176
177 /**
178 * Attempts reauthentication to the <code>Realm</code> using
179 * the credentials included in argument <code>entry</code>.
180 *
181 * @param ssoId identifier of SingleSignOn session with which the
182 * caller is associated
183 * @param request the request that needs to be authenticated
184 */
185 protected boolean reauthenticateFromSSO(String ssoId, HttpRequest request)
186 {
187
188 if (ourSSO == null || ssoId == null)
189 return false;
190
191 boolean reauthenticated = false;
192
193 SingleSignOnEntry entry = ourSSO.lookup(ssoId);
194 if (entry != null && entry.getCanReauthenticate())
195 {
196 Principal reauthPrincipal = null;
197 Container parent = getContainer();
198 if (parent != null)
199 {
200 Realm realm = getContainer().getRealm();
201 String username = entry.getUsername();
202 if (realm != null && username != null)
203 {
204 reauthPrincipal =
205 realm.authenticate(username, entry.getPassword());
206 }
207 }
208
209 if (reauthPrincipal != null)
210 {
211 associate(ssoId, getSession(request, true));
212 request.setAuthType(entry.getAuthType());
213 request.setUserPrincipal(reauthPrincipal);
214
215 reauthenticated = true;
216 if (debug >= 1)
217 {
218 log(" Reauthenticated cached principal '" +
219 entry.getPrincipal().getName() + "' with auth type '" +
220 entry.getAuthType() + "'");
221 }
222 }
223 }
224
225 return reauthenticated;
226 }
227
228
229 /**
230 * Register an authenticated Principal and authentication type in our
231 * request, in the current session (if there is one), and with our
232 * SingleSignOn valve, if there is one. Set the appropriate cookie
233 * to be returned.
234 * <p>
235 * <b>IMPLEMENTATION NOTE:</b> Differs from the standard Tomcat
236 * implementation in checking if any <code>SingleSignOn</code> valve
237 * has added a note to the request. If it has, it does not call
238 * <code>SingleSignOn.register</code>, instead calling
239 * <code>SingleSignOn.update</code>. This behavior supports
240 * authenticators like <code>SSLAuthenticator</code> that may attempt
241 * to re-register with every request.
242 *
243 * @param request The servlet request we are processing
244 * @param response The servlet response we are generating
245 * @param principal The authenticated Principal to be registered
246 * @param authType The authentication type to be registered
247 * @param username Username used to authenticate (if any)
248 * @param password Password used to authenticate (if any)
249 */
250 protected void register(HttpRequest request, HttpResponse response,
251 Principal principal, String authType,
252 String username, String password)
253 {
254
255 if (debug >= 1)
256 log("Authenticated '" + principal.getName() + "' with type '" +
257 authType + "'");
258
259 // Cache the authentication information in our request
260 request.setAuthType(authType);
261 request.setUserPrincipal(principal);
262
263 Session session = getSession(request, false);
264 // Cache the authentication information in our session, if any
265 if (cache)
266 {
267 if (session != null)
268 {
269 session.setAuthType(authType);
270 session.setPrincipal(principal);
271 if (username != null)
272 session.setNote(Constants.SESS_USERNAME_NOTE, username);
273 else
274 session.removeNote(Constants.SESS_USERNAME_NOTE);
275 if (password != null)
276 session.setNote(Constants.SESS_PASSWORD_NOTE, password);
277 else
278 session.removeNote(Constants.SESS_PASSWORD_NOTE);
279 }
280 }
281
282 // Construct a cookie to be returned to the client
283 if (ourSSO == null)
284 return;
285
286 // Only create a new SSO entry if the SSO did not already set a note
287 // for an existing entry (as it would do with subsequent requests
288 // for DIGEST and SSL authenticated contexts)
289 String ssoId = (String) request.getNote(Constants.REQ_SSOID_NOTE);
290 if (ssoId == null)
291 {
292 // Construct a cookie to be returned to the client
293 HttpServletResponse hres =
294 (HttpServletResponse) response.getResponse();
295 ssoId = generateSessionId();
296 Cookie cookie = new Cookie(Constants.SINGLE_SIGN_ON_COOKIE, ssoId);
297 cookie.setMaxAge(-1);
298 cookie.setPath("/");
299 hres.addCookie(cookie);
300
301 // Register this principal with our SSO valve
302 ourSSO.register(ssoId, principal, authType, username, password);
303 request.setNote(Constants.REQ_SSOID_NOTE, ssoId);
304
305 }
306 else
307 {
308 // Update the SSO session with the latest authentication data
309 ourSSO.update(ssoId, principal, authType, username, password);
310 }
311
312 // Fix for Tomcat Bug 10040
313 // Always associate a session with a new SSO reqistration.
314 // SSO entries are only removed from the SSO registry map when
315 // associated sessions are destroyed; if a new SSO entry is created
316 // above for this request and the user never revisits the context, the
317 // SSO entry will never be cleared if we don't associate the session
318 if (session == null)
319 session = getSession(request, true);
320 ourSSO.associate(ssoId, session);
321
322 }
323
324
325 // ------------------------------------------------------ Lifecycle Methods
326
327
328 /**
329 * Prepare for the beginning of active use of the public methods of this
330 * component. This method should be called after <code>configure()</code>,
331 * and before any of the public methods of the component are utilized.
332 * <p>
333 * <b>IMPLEMENTATION NOTE:</b> Overrides the superclass version solely by
334 * using a <code>org.jboss.web.tomcat.tc4.authenticator.SingleSignOn</code>
335 * instead of an <code>org.apache.catalina.authenticator.SingleSignOn</code>
336 *
337 *
338 * @exception LifecycleException if this component detects a fatal error
339 * that prevents this component from being used
340 */
341 public void start() throws LifecycleException
342 {
343
344 // Validate and update our current component state
345 if (started)
346 {
347 throw new LifecycleException
348 (sm.getString("authenticator.alreadyStarted"));
349 }
350 lifecycle.fireLifecycleEvent(START_EVENT, null);
351 if ("org.apache.catalina.core.StandardContext".equals
352 (context.getClass().getName()))
353 {
354 try
355 {
356 Class paramTypes[] = new Class[0];
357 Object paramValues[] = new Object[0];
358 Method method =
359 context.getClass().getMethod("getDebug", paramTypes);
360 Integer result = (Integer) method.invoke(context, paramValues);
361 setDebug(result.intValue());
362 }
363 catch (Exception e)
364 {
365 log("Exception getting debug value", e);
366 }
367 }
368 started = true;
369
370 // Look up the SingleSignOn implementation in our request processing
371 // path, if there is one
372 Container parent = context.getParent();
373 while ((ourSSO == null) && (parent != null))
374 {
375 if (!(parent instanceof Pipeline))
376 {
377 parent = parent.getParent();
378 continue;
379 }
380 Valve valves[] = ((Pipeline) parent).getValves();
381 for (int i = 0; i < valves.length; i++)
382 {
383 if (valves[i] instanceof SingleSignOn)
384 {
385 ourSSO = (SingleSignOn) valves[i];
386 break;
387 }
388 }
389 if (ourSSO == null)
390 parent = parent.getParent();
391 }
392 if (debug >= 1)
393 {
394 if (ourSSO != null)
395 {
396 log("Found SingleSignOn Valve at " + ourSSO);
397 }
398 else
399 {
400 log("No SingleSignOn Valve is present");
401 }
402 }
403
404 }
405
406
407 /**
408 * Gracefully terminate the active use of the public methods of this
409 * component. This method should be the last one called on a given
410 * instance of this component.
411 * <p>
412 * <b>IMPLEMENTATION NOTE:</b> Overrides the superclass version solely by
413 * using a <code>org.jboss.web.tomcat.tc4.authenticator.SingleSignOn</code>
414 * instead of an <code>org.apache.catalina.authenticator.SingleSignOn</code>
415 *
416 * @exception LifecycleException if this component detects a fatal error
417 * that needs to be reported
418 */
419 public void stop() throws LifecycleException
420 {
421
422 // Validate and update our current component state
423 if (!started)
424 {
425 throw new LifecycleException
426 (sm.getString("authenticator.notStarted"));
427 }
428 lifecycle.fireLifecycleEvent(STOP_EVENT, null);
429 started = false;
430
431 ourSSO = null;
432
433 }
434
435
436 }