// ======================================================================== // Copyright (c) 2003-2009 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // You may elect to redistribute this code under either of these licenses. // ======================================================================== package org.eclipse.jetty.plus.jaas; import java.io.IOException; import java.security.Principal; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.LinkedHashSet; import java.util.Set; import javax.security.auth.Subject; import javax.security.auth.callback.Callback; import javax.security.auth.callback.CallbackHandler; import javax.security.auth.callback.NameCallback; import javax.security.auth.callback.PasswordCallback; import javax.security.auth.callback.UnsupportedCallbackException; import javax.security.auth.login.LoginContext; import javax.security.auth.login.LoginException; import org.eclipse.jetty.plus.jaas.callback.ObjectCallback; import org.eclipse.jetty.security.DefaultIdentityService; import org.eclipse.jetty.security.IdentityService; import org.eclipse.jetty.security.LoginService; import org.eclipse.jetty.server.UserIdentity; import org.eclipse.jetty.util.Loader; import org.eclipse.jetty.util.component.AbstractLifeCycle; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; /* ---------------------------------------------------- */ /** JAASLoginService * * @org.apache.xbean.XBean element="jaasUserRealm" description="Creates a UserRealm suitable for use with JAAS" */ public class JAASLoginService extends AbstractLifeCycle implements LoginService { private static final Logger LOG = Log.getLogger(JAASLoginService.class); public static String DEFAULT_ROLE_CLASS_NAME = "org.eclipse.jetty.plus.jaas.JAASRole"; public static String[] DEFAULT_ROLE_CLASS_NAMES = {DEFAULT_ROLE_CLASS_NAME}; protected String[] _roleClassNames = DEFAULT_ROLE_CLASS_NAMES; protected String _callbackHandlerClass; protected String _realmName; protected String _loginModuleName; protected JAASUserPrincipal _defaultUser = new JAASUserPrincipal(null, null, null); protected IdentityService _identityService; /* ---------------------------------------------------- */ /** * Constructor. * */ public JAASLoginService() { } /* ---------------------------------------------------- */ /** * Constructor. * * @param name the name of the realm */ public JAASLoginService(String name) { this(); _realmName = name; _loginModuleName = name; } /* ---------------------------------------------------- */ /** * Get the name of the realm. * * @return name or null if not set. */ public String getName() { return _realmName; } /* ---------------------------------------------------- */ /** * Set the name of the realm * * @param name a <code>String</code> value */ public void setName (String name) { _realmName = name; } /* ------------------------------------------------------------ */ /** Get the identityService. * @return the identityService */ public IdentityService getIdentityService() { return _identityService; } /* ------------------------------------------------------------ */ /** Set the identityService. * @param identityService the identityService to set */ public void setIdentityService(IdentityService identityService) { _identityService = identityService; } /* ------------------------------------------------------------ */ /** * Set the name to use to index into the config * file of LoginModules. * * @param name a <code>String</code> value */ public void setLoginModuleName (String name) { _loginModuleName = name; } /* ------------------------------------------------------------ */ public void setCallbackHandlerClass (String classname) { _callbackHandlerClass = classname; } /* ------------------------------------------------------------ */ public void setRoleClassNames (String[] classnames) { ArrayList<String> tmp = new ArrayList<String>(); if (classnames != null) tmp.addAll(Arrays.asList(classnames)); if (!tmp.contains(DEFAULT_ROLE_CLASS_NAME)) tmp.add(DEFAULT_ROLE_CLASS_NAME); _roleClassNames = tmp.toArray(new String[tmp.size()]); } /* ------------------------------------------------------------ */ public String[] getRoleClassNames() { return _roleClassNames; } /* ------------------------------------------------------------ */ /** * @see org.eclipse.jetty.util.component.AbstractLifeCycle#doStart() */ protected void doStart() throws Exception { if (_identityService==null) _identityService=new DefaultIdentityService(); super.doStart(); } /* ------------------------------------------------------------ */ public UserIdentity login(final String username,final Object credentials) { try { CallbackHandler callbackHandler = null; if (_callbackHandlerClass == null) { callbackHandler = new CallbackHandler() { public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { for (Callback callback: callbacks) { if (callback instanceof NameCallback) { ((NameCallback)callback).setName(username); } else if (callback instanceof PasswordCallback) { ((PasswordCallback)callback).setPassword((char[]) credentials.toString().toCharArray()); } else if (callback instanceof ObjectCallback) { ((ObjectCallback)callback).setObject(credentials); } } } }; } else { Class clazz = Loader.loadClass(getClass(), _callbackHandlerClass); callbackHandler = (CallbackHandler)clazz.newInstance(); } //set up the login context //TODO jaspi requires we provide the Configuration parameter Subject subject = new Subject(); LoginContext loginContext = new LoginContext(_loginModuleName, subject, callbackHandler); loginContext.login(); //login success JAASUserPrincipal userPrincipal = new JAASUserPrincipal(getUserName(callbackHandler), subject, loginContext); subject.getPrincipals().add(userPrincipal); return _identityService.newUserIdentity(subject,userPrincipal,getGroups(subject)); } catch (LoginException e) { LOG.warn(e); } catch (IOException e) { LOG.warn(e); } catch (UnsupportedCallbackException e) { LOG.warn(e); } catch (InstantiationException e) { LOG.warn(e); } catch (IllegalAccessException e) { LOG.warn(e); } catch (ClassNotFoundException e) { LOG.warn(e); } return null; } /* ------------------------------------------------------------ */ public boolean validate(UserIdentity user) { // TODO optionally check user is still valid return true; } /* ------------------------------------------------------------ */ private String getUserName(CallbackHandler callbackHandler) throws IOException, UnsupportedCallbackException { NameCallback nameCallback = new NameCallback("foo"); callbackHandler.handle(new Callback[] {nameCallback}); return nameCallback.getName(); } /* ------------------------------------------------------------ */ public void logout(UserIdentity user) { Set<JAASUserPrincipal> userPrincipals = user.getSubject().getPrincipals(JAASUserPrincipal.class); LoginContext loginContext = userPrincipals.iterator().next().getLoginContext(); try { loginContext.logout(); } catch (LoginException e) { LOG.warn(e); } } /* ------------------------------------------------------------ */ @SuppressWarnings({ "unchecked", "rawtypes" }) private String[] getGroups (Subject subject) { //get all the roles of the various types String[] roleClassNames = getRoleClassNames(); Collection<String> groups = new LinkedHashSet<String>(); try { for (String roleClassName : roleClassNames) { Class load_class = Thread.currentThread().getContextClassLoader().loadClass(roleClassName); Set<Principal> rolesForType = subject.getPrincipals(load_class); for (Principal principal : rolesForType) { groups.add(principal.getName()); } } return groups.toArray(new String[groups.size()]); } catch (ClassNotFoundException e) { throw new RuntimeException(e); } } }