View Javadoc

1   /* 
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *     http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.jetspeed.security.impl;
18  
19  import java.security.Principal;
20  import java.util.List;
21  import java.util.Map;
22  
23  import javax.security.auth.Subject;
24  import javax.security.auth.callback.Callback;
25  import javax.security.auth.callback.CallbackHandler;
26  import javax.security.auth.callback.NameCallback;
27  import javax.security.auth.callback.PasswordCallback;
28  import javax.security.auth.login.FailedLoginException;
29  import javax.security.auth.login.LoginException;
30  import javax.security.auth.spi.LoginModule;
31  
32  import org.apache.jetspeed.security.LoginModuleProxy;
33  import org.apache.jetspeed.security.RolePrincipal;
34  import org.apache.jetspeed.security.SecurityHelper;
35  import org.apache.jetspeed.security.User;
36  import org.apache.jetspeed.security.UserManager;
37  import org.apache.jetspeed.security.UserPrincipal;
38  
39  /***
40   * <p>LoginModule implementation that authenticates a user
41   * against a relational database. OJB based implementation.</p>
42   * <p>When a user is successfully authenticated, the user principal
43   * are added to the current subject.</p>
44   * <p>The LoginModule also recognizes the debug option.</p>
45   * <p>Configuration files should provide:</p>
46   * <pre><code>
47   * Jetspeed {
48   *   org.apache.jetspeed.security.impl.DefaultLoginModule required debug=true;
49   * };
50   * </code></pre>
51   * @author <a href="mailto:dlestrat@apache.org">David Le Strat</a>
52   */
53  public class DefaultLoginModule implements LoginModule
54  {
55  
56      /*** <p>LoginModule debug mode is turned off by default.</p> */
57      protected boolean debug;
58  
59      /*** <p>The authentication status.</p> */
60      protected boolean success;
61  
62      /*** <p>The commit status.</p> */
63      protected boolean commitSuccess;
64  
65      /*** <p>The Subject to be authenticated.</p> */
66      protected Subject subject;
67  
68      /*** <p>A CallbackHandler for communicating with the end user (prompting for usernames and passwords, for example).</p> */
69      protected CallbackHandler callbackHandler;
70  
71      /*** <p>State shared with other configured LoginModules.</p> */
72      protected Map sharedState;
73  
74      /*** <p>Options specified in the login Configuration for this particular LoginModule.</p> */
75      protected Map options;
76  
77      /*** <p>InternalUserPrincipal manager service.</p> */
78      protected UserManager ums;
79  
80      /*** The portal user role. */
81      protected String portalUserRole;
82  
83      /*** <p>The user name.</p> */
84      protected String username;
85  
86      
87      /***
88       * <p>The default login module constructor.</p>
89       */
90      public DefaultLoginModule()
91      {
92          LoginModuleProxy loginModuleProxy = LoginModuleProxyImpl.loginModuleProxy;
93          if (loginModuleProxy != null)
94          {
95              this.ums = loginModuleProxy.getUserManager();
96              this.portalUserRole = loginModuleProxy.getPortalUserRole();
97          }
98          debug = false;
99          success = false;
100         commitSuccess = false;
101         username = null;
102     }
103 
104     
105     /***
106      * Create a new login module that uses the given user manager.
107      * @param userManager the user manager to use
108      * @param portalUserRole the portal user role to use
109      */
110     protected DefaultLoginModule (UserManager userManager, String portalUserRole) 
111     {
112         this.ums = userManager;
113         this.portalUserRole = portalUserRole;
114         debug = false;
115         success = false;
116         commitSuccess = false;
117         username = null;
118     }
119     protected DefaultLoginModule (UserManager userManager) 
120     {
121         this(userManager, LoginModuleProxy.DEFAULT_PORTAL_USER_ROLE_NAME);
122     }
123     
124     /***
125      * @see javax.security.auth.spi.LoginModule#abort()
126      */
127     public boolean abort() throws LoginException
128     {
129         // Clean out state
130         success = false;
131         commitSuccess = false;
132         username = null;
133         if (callbackHandler instanceof PassiveCallbackHandler)
134         {
135             ((PassiveCallbackHandler) callbackHandler).clearPassword();
136         }
137         logout();
138         return true;
139     }
140 
141     protected void refreshProxy()
142     {
143         if (this.ums == null)
144         {
145             LoginModuleProxy loginModuleProxy = LoginModuleProxyImpl.loginModuleProxy;
146             if (loginModuleProxy != null)
147             {
148                 this.ums = loginModuleProxy.getUserManager();
149             }
150         }        
151     }
152     
153     /***
154      * @see javax.security.auth.spi.LoginModule#commit()
155      */
156     public boolean commit() throws LoginException
157     {
158         if (success)
159         {
160             if (subject.isReadOnly())
161             {
162                 throw new LoginException("Subject is Readonly");
163             }
164             try
165             {
166                 // TODO We should get the user profile here and had it in cache so that we do not have to retrieve it again.
167                 // TODO Ideally the User should be available from the session.  Need discussion around this.
168                 refreshProxy();
169                 commitPrincipals(subject, ums.getUser(username));
170 
171                 username = null;
172                 commitSuccess = true;
173 
174                 if (callbackHandler instanceof PassiveCallbackHandler)
175                 {
176                     ((PassiveCallbackHandler) callbackHandler).clearPassword();
177                 }
178 
179             }
180             catch (Exception ex)
181             {
182                 ex.printStackTrace(System.out);
183                 throw new LoginException(ex.getMessage());
184             }
185         }
186 
187         return commitSuccess;
188     }
189 
190     /***
191      * @see javax.security.auth.spi.LoginModule#login()
192      */
193     public boolean login() throws LoginException
194     {
195         if (callbackHandler == null)
196         {
197             throw new LoginException("Error: no CallbackHandler available " + "to garner authentication information from the user");
198         }
199         try
200         {
201             // Setup default callback handlers.
202             Callback[] callbacks = new Callback[] { new NameCallback("Username: "), new PasswordCallback("Password: ", false)};
203 
204             callbackHandler.handle(callbacks);
205 
206             username = ((NameCallback) callbacks[0]).getName();
207             String password = new String(((PasswordCallback) callbacks[1]).getPassword());
208 
209             ((PasswordCallback) callbacks[1]).clearPassword();
210 
211             refreshProxy();            
212             success = ums.authenticate(this.username, password);
213 
214             callbacks[0] = null;
215             callbacks[1] = null;
216             if (!success)
217             {
218                 throw new FailedLoginException("Authentication failed: Password does not match");
219             }
220 
221             return (true);
222         }
223         catch (LoginException ex)
224         {
225             throw ex;
226         }
227         catch (Exception ex)
228         {
229             success = false;
230             throw new LoginException(ex.getMessage());
231         }
232     }
233 
234     /***
235      * @see javax.security.auth.spi.LoginModule#logout()
236      */
237     public boolean logout() throws LoginException
238     {
239         // TODO Can we set subject to null?
240         subject.getPrincipals().clear();
241         subject.getPrivateCredentials().clear();
242         subject.getPublicCredentials().clear();
243         success = false;
244         commitSuccess = false;
245 
246         return true;
247     }
248 
249     /***
250      * @see javax.security.auth.spi.LoginModule#initialize(javax.security.auth.Subject, javax.security.auth.callback.CallbackHandler, java.util.Map, java.util.Map)
251      */
252     public void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options)
253     {
254         this.subject = subject;
255         this.callbackHandler = callbackHandler;
256         this.sharedState = sharedState;
257         this.options = options;
258 
259         // Initialize debug mode if configure option.
260         if (options.containsKey("debug"))
261         {
262             debug = "true".equalsIgnoreCase((String) options.get("debug"));
263         }
264     }
265 
266     
267     protected Principal getUserPrincipal(User user)
268     {
269         return SecurityHelper.getPrincipal(user.getSubject(),UserPrincipal.class);
270     }
271     
272     protected List getUserRoles(User user)
273     {
274         return SecurityHelper.getPrincipals(user.getSubject(),RolePrincipal.class);
275     }
276     
277     /***
278      * Default setup of the logged on Subject Principals for Tomcat
279      * @param subject
280      * @param user
281      */
282     protected void commitPrincipals(Subject subject, User user)
283     {
284         // add user specific portal user name and roles
285         subject.getPrincipals().add(getUserPrincipal(user));
286         subject.getPrincipals().addAll(getUserRoles(user));
287 
288         // add portal user role: used in web.xml authorization to
289         // detect authenticated portal users
290         subject.getPrincipals().add(new RolePrincipalImpl(portalUserRole));        
291     }
292 }