/* * Copyright (c) 1998-2011 Caucho Technology -- all rights reserved * * This file is part of Resin(R) Open Source * * Each copy or derived work must preserve the copyright notice and this * notice unmodified. * * Resin Open Source is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Resin Open Source is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty * of NON-INFRINGEMENT. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License * along with Resin Open Source; if not, write to the * Free SoftwareFoundation, Inc. * 59 Temple Place, Suite 330 * Boston, MA 02111-1307 USA * * @author Scott Ferguson */ package com.caucho.security; import java.io.IOException; import java.security.Principal; import java.security.acl.Group; import java.util.Enumeration; import java.util.HashMap; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; import javax.annotation.PostConstruct; 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.LoginException; import javax.security.auth.spi.LoginModule; import javax.servlet.ServletException; import com.caucho.config.Config; import com.caucho.config.ConfigException; import com.caucho.config.types.InitParam; import com.caucho.server.security.RolePrincipal; import com.caucho.util.L10N; /** * The JAAS authenticator uses an existing JAAS LoginModule. Applications * which have existing JAAS modules can use the JaasAuthenticator to * log users in based on the old login. * * <code><pre> * <authenticator url="jaas:"> * <init login-module="example.MyLogin"/> * </authenticator> */ @SuppressWarnings("serial") public class JaasAuthenticator extends AbstractAuthenticator { private static final L10N L = new L10N(JaasAuthenticator.class); private static final Logger log = Logger.getLogger(JaasAuthenticator.class.getName()); private Class<?> _loginModuleClass; private HashMap<String,String> _options = new HashMap<String,String>(); public JaasAuthenticator() { setPasswordDigest(null); } /** * Sets the JAAS spi login module class. */ public void setLoginModule(Class<?> loginModuleClass) throws ConfigException { _loginModuleClass = loginModuleClass; Config.checkCanInstantiate(loginModuleClass); if (! LoginModule.class.isAssignableFrom(loginModuleClass)) throw new ConfigException(L.l("'{0}' must implement javax.security.auth.spi.LoginModule", loginModuleClass.getName())); } public void setInitParam(InitParam init) { _options.putAll(init.getParameters()); } public void setOptions(InitParam init) { _options.putAll(init.getParameters()); } /** * Initialize the authenticator. */ @PostConstruct public void init() throws ServletException { super.init(); if (_loginModuleClass == null) throw new ServletException(L.l("JaasAuthenticator requires login-module")); } /** * Authenticate (login) the user. */ @Override protected Principal authenticate(Principal principal, PasswordCredentials cred, Object details) { try { String userName = principal.getName(); String password = new String(cred.getPassword()); Set<Principal> principals = getPrincipals(userName, password); if (principals == null || principals.size() == 0) return null; Principal userPrincipal = null; Group roles = null; for (Principal loginPrincipal : principals) { if ("roles".equals(loginPrincipal.getName()) && loginPrincipal instanceof Group) { roles = (Group) loginPrincipal; } else if (userPrincipal == null) userPrincipal = loginPrincipal; } if (userPrincipal == null && roles != null) userPrincipal = roles; if (userPrincipal instanceof RolePrincipal) return userPrincipal; else if (userPrincipal != null) return new JaasPrincipal(userPrincipal, roles); else return null; } catch (RuntimeException e) { throw e; } catch (Exception e) { throw new RuntimeException(e); } } /** * Returns true if the user plays the named role. * * @param request the servlet request * @param user the user to test * @param role the role to test */ @Override public boolean isUserInRole(Principal principal, String role) { if (principal == null) return false; if (principal instanceof RolePrincipal) return ((RolePrincipal) principal).isUserInRole(role); else return "user".equals(role); } private Set<Principal> getPrincipals(String userName, String password) { try { LoginModule login = (LoginModule) _loginModuleClass.newInstance(); Subject subject = new Subject(); HashMap<String,String> state = new HashMap<String,String>(); state.put("javax.security.auth.login.name", userName); state.put("javax.security.auth.login.password", password); login.initialize(subject, new Handler(userName, password), state, _options); try { login.login(); } catch (Exception e) { login.abort(); } login.commit(); Set<Principal> principals = subject.getPrincipals(); return principals; } catch (LoginException e) { log.log(Level.FINE, e.toString(), e); return null; } catch (RuntimeException e) { throw e; } catch (Exception e) { throw new RuntimeException(e); } } private static class JaasPrincipal implements RolePrincipal { private Principal _principal; private Group _roles; JaasPrincipal(Principal principal, Group roles) { _principal = principal; _roles = roles; } @Override public String getName() { return _principal.getName(); } @Override public boolean isUserInRole(String role) { if (_roles == null) return "user".equals(role); Enumeration<? extends Principal> e = _roles.members(); while (e.hasMoreElements()) { Principal principal = e.nextElement(); if (role.equals(principal.getName())) return true; } return false; } @Override public int hashCode() { return _principal.hashCode(); } @Override public boolean equals(Object obj) { if (! (obj instanceof JaasPrincipal)) return false; JaasPrincipal principal = (JaasPrincipal) obj; return getName().equals(principal.getName()); } public String toString() { return _principal.toString(); } } static class Handler implements CallbackHandler { private String _userName; private String _password; Handler(String userName, String password) { _userName = userName; _password = password; } public void handle(Callback []callbacks) throws IOException, UnsupportedCallbackException { for (int i = 0; i < callbacks.length; i++) { Callback cb = callbacks[i]; if (cb instanceof NameCallback) { NameCallback name = (NameCallback) cb; name.setName(_userName); } else if (cb instanceof PasswordCallback) { PasswordCallback password = (PasswordCallback) cb; password.setPassword(_password.toCharArray()); } } } } }