View Javadoc

1   /* Copyright 2004 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.adapters.cas;
17  
18  import edu.yale.its.tp.cas.auth.PasswordHandler;
19  
20  import org.apache.commons.logging.Log;
21  import org.apache.commons.logging.LogFactory;
22  
23  import org.springframework.context.ApplicationContext;
24  
25  import org.springframework.web.context.support.WebApplicationContextUtils;
26  
27  import java.util.Map;
28  
29  import javax.servlet.ServletRequest;
30  import javax.servlet.http.HttpServletRequest;
31  
32  
33  /***
34   * Enables CAS to use the Acegi Security System for authentication.
35   * 
36   * <P>
37   * This class works along with {@link CasPasswordHandler} to enable users to
38   * easily migrate from stand-alone Acegi Security System deployments to
39   * enterprise-wide CAS deployments.
40   * </p>
41   * 
42   * <p>
43   * It should be noted that the Acegi Security System will operate as a CAS
44   * client irrespective of the <code>PasswordHandler</code> used on the CAS
45   * server. In other words, this class need <B>not</B> be used on the CAS
46   * server if not desired. It exists solely for the convenience of users
47   * wishing have CAS delegate to an Acegi Security System-based
48   * <code>AuthenticationManager</code>.
49   * </p>
50   * 
51   * <p>
52   * This class works requires a properly configured
53   * <code>CasPasswordHandler</code>. On the first authentication request, the
54   * class will use Spring's {@link
55   * WebApplicationContextUtils#getWebApplicationContext(ServletContext sc)}
56   * method to obtain an <code>ApplicationContext</code> instance, inside which
57   * must be a configured <code>CasPasswordHandler</code> instance. The
58   * <code>CasPasswordHandlerProxy</code> will then delegate authentication
59   * requests to that instance.
60   * </p>
61   * 
62   * <p>
63   * To configure CAS to use this class, edit CAS' <code>web.xml</code> and
64   * define the <code>edu.yale.its.tp.cas.authHandler</code> context parameter
65   * with the value
66   * <code>org.acegisecurity.adapters.cas.CasPasswordHandlerProxy</code>.
67   * </p>
68   *
69   * @author Ben Alex
70   * @version $Id: CasPasswordHandlerProxy.java,v 1.2 2005/11/17 00:56:28 benalex Exp $
71   */
72  public class CasPasswordHandlerProxy implements PasswordHandler {
73      //~ Static fields/initializers =============================================
74  
75      private static final Log logger = LogFactory.getLog(CasPasswordHandlerProxy.class);
76  
77      //~ Instance fields ========================================================
78  
79      private ApplicationContext ctx;
80      private CasPasswordHandler handler;
81  
82      //~ Methods ================================================================
83  
84      /***
85       * Called by CAS when authentication is required.
86       * 
87       * <P>
88       * Delegates to the <code>CasPasswordHandler</code>.
89       * </p>
90       *
91       * @param request as provided by CAS
92       * @param username provided to CAS
93       * @param password provided to CAS
94       *
95       * @return whether authentication was successful or not
96       *
97       * @throws IllegalArgumentException if the application context does not
98       *         contain a <code>CasPasswordHandler</code> or the
99       *         <code>ServletRequest</code> was not of type
100      *         <code>HttpServletRequest</code>
101      */
102     public boolean authenticate(ServletRequest request, String username,
103         String password) {
104         if (ctx == null) {
105             if (!(request instanceof HttpServletRequest)) {
106                 throw new IllegalArgumentException(
107                     "Can only process HttpServletRequest");
108             }
109 
110             HttpServletRequest httpRequest = (HttpServletRequest) request;
111 
112             ctx = this.getContext(httpRequest);
113         }
114 
115         if (handler == null) {
116             Map beans = ctx.getBeansOfType(CasPasswordHandler.class, true, true);
117 
118             if (beans.size() == 0) {
119                 throw new IllegalArgumentException(
120                     "Bean context must contain at least one bean of type CasPasswordHandler");
121             }
122 
123             String beanName = (String) beans.keySet().iterator().next();
124             handler = (CasPasswordHandler) beans.get(beanName);
125         }
126 
127         return handler.authenticate(request, username, password);
128     }
129 
130     /***
131      * Allows test cases to override where application context obtained from.
132      *
133      * @param httpRequest which can be used to find the
134      *        <code>ServletContext</code>
135      *
136      * @return the Spring application context
137      */
138     protected ApplicationContext getContext(HttpServletRequest httpRequest) {
139         return WebApplicationContextUtils.getRequiredWebApplicationContext(httpRequest.getSession()
140                                                                                       .getServletContext());
141     }
142 }