View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2016 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.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  import javax.servlet.ServletRequest;
38  
39  import org.eclipse.jetty.jaas.callback.ObjectCallback;
40  import org.eclipse.jetty.jaas.callback.RequestParameterCallback;
41  import org.eclipse.jetty.security.DefaultIdentityService;
42  import org.eclipse.jetty.security.IdentityService;
43  import org.eclipse.jetty.security.LoginService;
44  import org.eclipse.jetty.server.UserIdentity;
45  import org.eclipse.jetty.util.Loader;
46  import org.eclipse.jetty.util.component.AbstractLifeCycle;
47  import org.eclipse.jetty.util.log.Log;
48  import org.eclipse.jetty.util.log.Logger;
49  
50  /* ---------------------------------------------------- */
51  /** 
52   * JAASLoginService
53   *
54   */
55  public class JAASLoginService extends AbstractLifeCycle implements LoginService
56  {
57      private static final Logger LOG = Log.getLogger(JAASLoginService.class);
58  
59      public static final String DEFAULT_ROLE_CLASS_NAME = "org.eclipse.jetty.jaas.JAASRole";
60      public static final String[] DEFAULT_ROLE_CLASS_NAMES = {DEFAULT_ROLE_CLASS_NAME};
61  
62      protected String[] _roleClassNames = DEFAULT_ROLE_CLASS_NAMES;
63      protected String _callbackHandlerClass;
64      protected String _realmName;
65      protected String _loginModuleName;
66      protected JAASUserPrincipal _defaultUser = new JAASUserPrincipal(null, null, null);
67      protected IdentityService _identityService;
68  
69      /* ---------------------------------------------------- */
70      /**
71       * Constructor.
72       *
73       */
74      public JAASLoginService()
75      {
76      }
77  
78  
79      /* ---------------------------------------------------- */
80      /**
81       * Constructor.
82       *
83       * @param name the name of the realm
84       */
85      public JAASLoginService(String name)
86      {
87          this();
88          _realmName = name;
89          _loginModuleName = name;
90      }
91  
92  
93      /* ---------------------------------------------------- */
94      /**
95       * Get the name of the realm.
96       *
97       * @return name or null if not set.
98       */
99      public String getName()
100     {
101         return _realmName;
102     }
103 
104 
105     /* ---------------------------------------------------- */
106     /**
107      * Set the name of the realm
108      *
109      * @param name a <code>String</code> value
110      */
111     public void setName (String name)
112     {
113         _realmName = name;
114     }
115 
116     /* ------------------------------------------------------------ */
117     /** Get the identityService.
118      * @return the identityService
119      */
120     public IdentityService getIdentityService()
121     {
122         return _identityService;
123     }
124 
125     /* ------------------------------------------------------------ */
126     /** Set the identityService.
127      * @param identityService the identityService to set
128      */
129     public void setIdentityService(IdentityService identityService)
130     {
131         _identityService = identityService;
132     }
133 
134     /* ------------------------------------------------------------ */
135     /**
136      * Set the name to use to index into the config
137      * file of LoginModules.
138      *
139      * @param name a <code>String</code> value
140      */
141     public void setLoginModuleName (String name)
142     {
143         _loginModuleName = name;
144     }
145 
146     /* ------------------------------------------------------------ */
147     public void setCallbackHandlerClass (String classname)
148     {
149         _callbackHandlerClass = classname;
150     }
151 
152     /* ------------------------------------------------------------ */
153     public void setRoleClassNames (String[] classnames)
154     {
155         ArrayList<String> tmp = new ArrayList<String>();
156 
157         if (classnames != null)
158             tmp.addAll(Arrays.asList(classnames));
159 
160         if (!tmp.contains(DEFAULT_ROLE_CLASS_NAME))
161             tmp.add(DEFAULT_ROLE_CLASS_NAME);
162         _roleClassNames = tmp.toArray(new String[tmp.size()]);
163     }
164 
165     /* ------------------------------------------------------------ */
166     public String[] getRoleClassNames()
167     {
168         return _roleClassNames;
169     }
170 
171     /* ------------------------------------------------------------ */
172     /**
173      * @see org.eclipse.jetty.util.component.AbstractLifeCycle#doStart()
174      */
175     protected void doStart() throws Exception
176     {
177         if (_identityService==null)
178             _identityService=new DefaultIdentityService();
179         super.doStart();
180     }
181 
182     /* ------------------------------------------------------------ */
183     @Override
184     public UserIdentity login(final String username,final Object credentials, final ServletRequest request)
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                                 RequestParameterCallback rpc = (RequestParameterCallback)callback;
214                                 if (request!=null)
215                                     rpc.setParameterValues(Arrays.asList(request.getParameterValues(rpc.getParameterName())));
216                             }
217                             else
218                                 throw new UnsupportedCallbackException(callback);
219                         }
220                     }
221                 };
222             }
223             else
224             {
225                 Class<?> clazz = Loader.loadClass(getClass(), _callbackHandlerClass);
226                 callbackHandler = (CallbackHandler)clazz.newInstance();
227             }
228             //set up the login context
229             //TODO jaspi requires we provide the Configuration parameter
230             Subject subject = new Subject();
231             LoginContext loginContext = new LoginContext(_loginModuleName, subject, callbackHandler);
232 
233             loginContext.login();
234 
235             //login success
236             JAASUserPrincipal userPrincipal = new JAASUserPrincipal(getUserName(callbackHandler), subject, loginContext);
237             subject.getPrincipals().add(userPrincipal);
238 
239             return _identityService.newUserIdentity(subject,userPrincipal,getGroups(subject));
240         }
241         catch (LoginException e)
242         {
243             LOG.warn(e);
244         }
245         catch (IOException e)
246         {
247             LOG.warn(e);
248         }
249         catch (UnsupportedCallbackException e)
250         {
251            LOG.warn(e);
252         }
253         catch (InstantiationException e)
254         {
255             LOG.warn(e);
256         }
257         catch (IllegalAccessException e)
258         {
259             LOG.warn(e);
260         }
261         catch (ClassNotFoundException e)
262         {
263             LOG.warn(e);
264         }
265         return null;
266     }
267 
268     /* ------------------------------------------------------------ */
269     public boolean validate(UserIdentity user)
270     {
271         // TODO optionally check user is still valid
272         return true;
273     }
274 
275     /* ------------------------------------------------------------ */
276     private String getUserName(CallbackHandler callbackHandler) throws IOException, UnsupportedCallbackException
277     {
278         NameCallback nameCallback = new NameCallback("foo");
279         callbackHandler.handle(new Callback[] {nameCallback});
280         return nameCallback.getName();
281     }
282 
283     /* ------------------------------------------------------------ */
284     public void logout(UserIdentity user)
285     {
286         Set<JAASUserPrincipal> userPrincipals = user.getSubject().getPrincipals(JAASUserPrincipal.class);
287         LoginContext loginContext = userPrincipals.iterator().next().getLoginContext();
288         try
289         {
290             loginContext.logout();
291         }
292         catch (LoginException e)
293         {
294             LOG.warn(e);
295         }
296     }
297 
298 
299     /* ------------------------------------------------------------ */
300     @SuppressWarnings({ "unchecked", "rawtypes" })
301     private String[] getGroups (Subject subject)
302     {
303         //get all the roles of the various types
304         String[] roleClassNames = getRoleClassNames();
305         Collection<String> groups = new LinkedHashSet<String>();
306         try
307         {
308             for (String roleClassName : roleClassNames)
309             {
310                 Class load_class = Thread.currentThread().getContextClassLoader().loadClass(roleClassName);
311                 Set<Principal> rolesForType = subject.getPrincipals(load_class);
312                 for (Principal principal : rolesForType)
313                 {
314                     groups.add(principal.getName());
315                 }
316             }
317 
318             return groups.toArray(new String[groups.size()]);
319         }
320         catch (ClassNotFoundException e)
321         {
322             throw new RuntimeException(e);
323         }
324     }
325 
326 }