View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
4   //  ------------------------------------------------------------------------
5   //  All rights reserved. This program and the accompanying materials
6   //  are made available under the terms of the Eclipse Public License v1.0
7   //  and Apache License v2.0 which accompanies this distribution.
8   //
9   //      The Eclipse Public License is available at
10  //      http://www.eclipse.org/legal/epl-v10.html
11  //
12  //      The Apache License v2.0 is available at
13  //      http://www.opensource.org/licenses/apache2.0.php
14  //
15  //  You may elect to redistribute this code under either of these licenses.
16  //  ========================================================================
17  //
18  
19  package org.eclipse.jetty.plus.jaas;
20  
21  import java.io.IOException;
22  import java.security.Principal;
23  import java.util.ArrayList;
24  import java.util.Arrays;
25  import java.util.Collection;
26  import java.util.LinkedHashSet;
27  import java.util.Set;
28  
29  import javax.security.auth.Subject;
30  import javax.security.auth.callback.Callback;
31  import javax.security.auth.callback.CallbackHandler;
32  import javax.security.auth.callback.NameCallback;
33  import javax.security.auth.callback.PasswordCallback;
34  import javax.security.auth.callback.UnsupportedCallbackException;
35  import javax.security.auth.login.LoginContext;
36  import javax.security.auth.login.LoginException;
37  
38  import org.eclipse.jetty.plus.jaas.callback.ObjectCallback;
39  import org.eclipse.jetty.plus.jaas.callback.RequestParameterCallback;
40  import org.eclipse.jetty.security.DefaultIdentityService;
41  import org.eclipse.jetty.security.IdentityService;
42  import org.eclipse.jetty.security.LoginService;
43  import org.eclipse.jetty.server.AbstractHttpConnection;
44  import org.eclipse.jetty.server.Request;
45  import org.eclipse.jetty.server.UserIdentity;
46  import org.eclipse.jetty.util.Loader;
47  import org.eclipse.jetty.util.component.AbstractLifeCycle;
48  import org.eclipse.jetty.util.log.Log;
49  import org.eclipse.jetty.util.log.Logger;
50  
51  /* ---------------------------------------------------- */
52  /** JAASLoginService
53   * 
54   * @org.apache.xbean.XBean element="jaasUserRealm" description="Creates a UserRealm suitable for use with JAAS"
55   */
56  public class JAASLoginService extends AbstractLifeCycle implements LoginService
57  {
58      private static final Logger LOG = Log.getLogger(JAASLoginService.class);
59  
60      public static String DEFAULT_ROLE_CLASS_NAME = "org.eclipse.jetty.plus.jaas.JAASRole";
61      public static String[] DEFAULT_ROLE_CLASS_NAMES = {DEFAULT_ROLE_CLASS_NAME};
62  	
63      protected String[] _roleClassNames = DEFAULT_ROLE_CLASS_NAMES;
64      protected String _callbackHandlerClass;
65      protected String _realmName;
66      protected String _loginModuleName;
67      protected JAASUserPrincipal _defaultUser = new JAASUserPrincipal(null, null, null);
68      protected IdentityService _identityService;
69   
70      /* ---------------------------------------------------- */
71      /**
72       * Constructor.
73       *
74       */
75      public JAASLoginService()
76      {
77      }
78      
79  
80      /* ---------------------------------------------------- */
81      /**
82       * Constructor.
83       *
84       * @param name the name of the realm
85       */
86      public JAASLoginService(String name)
87      {
88          this();
89          _realmName = name;
90          _loginModuleName = name;
91      }
92  
93  
94      /* ---------------------------------------------------- */
95      /**
96       * Get the name of the realm.
97       *
98       * @return name or null if not set.
99       */
100     public String getName()
101     {
102         return _realmName;
103     }
104 
105 
106     /* ---------------------------------------------------- */
107     /**
108      * Set the name of the realm
109      *
110      * @param name a <code>String</code> value
111      */
112     public void setName (String name)
113     {
114         _realmName = name;
115     }
116 
117     /* ------------------------------------------------------------ */
118     /** Get the identityService.
119      * @return the identityService
120      */
121     public IdentityService getIdentityService()
122     {
123         return _identityService;
124     }
125 
126     /* ------------------------------------------------------------ */
127     /** Set the identityService.
128      * @param identityService the identityService to set
129      */
130     public void setIdentityService(IdentityService identityService)
131     {
132         _identityService = identityService;
133     }
134 
135     /* ------------------------------------------------------------ */
136     /**
137      * Set the name to use to index into the config
138      * file of LoginModules.
139      *
140      * @param name a <code>String</code> value
141      */
142     public void setLoginModuleName (String name)
143     {
144         _loginModuleName = name;
145     }
146 
147     /* ------------------------------------------------------------ */
148     public void setCallbackHandlerClass (String classname)
149     {
150         _callbackHandlerClass = classname;
151     }
152 
153     /* ------------------------------------------------------------ */
154     public void setRoleClassNames (String[] classnames)
155     {
156         ArrayList<String> tmp = new ArrayList<String>();
157         
158         if (classnames != null)
159             tmp.addAll(Arrays.asList(classnames));
160          
161         if (!tmp.contains(DEFAULT_ROLE_CLASS_NAME))
162             tmp.add(DEFAULT_ROLE_CLASS_NAME);
163         _roleClassNames = tmp.toArray(new String[tmp.size()]);
164     }
165 
166     /* ------------------------------------------------------------ */
167     public String[] getRoleClassNames()
168     {
169         return _roleClassNames;
170     }
171 
172     /* ------------------------------------------------------------ */
173     /**
174      * @see org.eclipse.jetty.util.component.AbstractLifeCycle#doStart()
175      */
176     protected void doStart() throws Exception
177     {
178         if (_identityService==null)
179             _identityService=new DefaultIdentityService();
180         super.doStart();
181     }
182 
183     /* ------------------------------------------------------------ */
184     public UserIdentity login(final String username,final Object credentials)
185     {
186         try
187         {
188             CallbackHandler callbackHandler = null;
189             
190             
191             if (_callbackHandlerClass == null)
192             {
193                 callbackHandler = new CallbackHandler()
194                 {
195                     public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException
196                     {
197                         for (Callback callback: callbacks)
198                         {
199                             if (callback instanceof NameCallback)
200                             {
201                                 ((NameCallback)callback).setName(username);
202                             }
203                             else if (callback instanceof PasswordCallback)
204                             {
205                                 ((PasswordCallback)callback).setPassword((char[]) credentials.toString().toCharArray());
206                             }
207                             else if (callback instanceof ObjectCallback)
208                             {
209                                 ((ObjectCallback)callback).setObject(credentials);
210                             }
211                             else if (callback instanceof RequestParameterCallback)
212                             {
213                                 AbstractHttpConnection connection = AbstractHttpConnection.getCurrentConnection();
214                                 Request request = (connection == null? null : connection.getRequest());
215                                 
216                                 if (request != null)
217                                 {
218                                     RequestParameterCallback rpc = (RequestParameterCallback)callback;
219                                     rpc.setParameterValues(Arrays.asList(request.getParameterValues(rpc.getParameterName())));
220                                 }
221                             }
222                             else 
223                                 throw new UnsupportedCallbackException(callback);
224                         }
225                     }
226                 };
227             }
228             else
229             {
230                 Class clazz = Loader.loadClass(getClass(), _callbackHandlerClass);
231                 callbackHandler = (CallbackHandler)clazz.newInstance();
232             }
233             //set up the login context
234             //TODO jaspi requires we provide the Configuration parameter
235             Subject subject = new Subject();
236             LoginContext loginContext = new LoginContext(_loginModuleName, subject, callbackHandler);
237 
238             loginContext.login();
239 
240             //login success
241             JAASUserPrincipal userPrincipal = new JAASUserPrincipal(getUserName(callbackHandler), subject, loginContext);
242             subject.getPrincipals().add(userPrincipal);
243             
244             return _identityService.newUserIdentity(subject,userPrincipal,getGroups(subject));
245         }
246         catch (LoginException e)
247         {
248             LOG.debug(e);
249         }
250         catch (IOException e)
251         {
252             LOG.info(e.getMessage());
253             LOG.debug(e);
254         }
255         catch (UnsupportedCallbackException e)
256         {
257             LOG.info(e.getMessage());
258             LOG.debug(e);
259         }
260         catch (InstantiationException e)
261         {
262             LOG.info(e.getMessage());
263             LOG.debug(e);
264         }
265         catch (IllegalAccessException e)
266         {
267             LOG.info(e.getMessage());
268             LOG.debug(e);
269         }
270         catch (ClassNotFoundException e)
271         {
272             LOG.info(e.getMessage());
273             LOG.debug(e);
274         }
275         return null;
276     }
277 
278     /* ------------------------------------------------------------ */
279     public boolean validate(UserIdentity user)
280     {
281         // TODO optionally check user is still valid
282         return true;
283     }
284 
285     /* ------------------------------------------------------------ */
286     private String getUserName(CallbackHandler callbackHandler) throws IOException, UnsupportedCallbackException
287     {
288         NameCallback nameCallback = new NameCallback("foo");
289         callbackHandler.handle(new Callback[] {nameCallback});
290         return nameCallback.getName();
291     }
292 
293     /* ------------------------------------------------------------ */
294     public void logout(UserIdentity user)
295     {
296         Set<JAASUserPrincipal> userPrincipals = user.getSubject().getPrincipals(JAASUserPrincipal.class);
297         LoginContext loginContext = userPrincipals.iterator().next().getLoginContext();
298         try
299         {
300             loginContext.logout();
301         }
302         catch (LoginException e)
303         {
304             LOG.warn(e);
305         }
306     }
307 
308 
309     /* ------------------------------------------------------------ */
310     @SuppressWarnings({ "unchecked", "rawtypes" })
311     private String[] getGroups (Subject subject)
312     {
313         //get all the roles of the various types
314         String[] roleClassNames = getRoleClassNames();
315         Collection<String> groups = new LinkedHashSet<String>();
316         try
317         {
318             for (String roleClassName : roleClassNames)
319             {
320                 Class load_class = Thread.currentThread().getContextClassLoader().loadClass(roleClassName);
321                 Set<Principal> rolesForType = subject.getPrincipals(load_class);
322                 for (Principal principal : rolesForType)
323                 {
324                     groups.add(principal.getName());
325                 }
326             }
327             
328             return groups.toArray(new String[groups.size()]);
329         }
330         catch (ClassNotFoundException e)
331         {
332             throw new RuntimeException(e);
333         }
334     }
335 
336 }