/* * JOSSO: Java Open Single Sign-On * * Copyright 2004-2009, Atricore, Inc. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software 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. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. * */ package org.josso.auth.scheme; import org.josso.auth.CredentialProvider; import org.josso.auth.Credential; import org.josso.auth.SimplePrincipal; import org.josso.auth.util.CipherUtil; import org.josso.auth.exceptions.SSOAuthenticationException; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import javax.security.auth.Subject; import javax.crypto.NoSuchPaddingException; import javax.crypto.IllegalBlockSizeException; import javax.crypto.BadPaddingException; import java.net.URLDecoder; import java.net.URLEncoder; import java.security.Principal; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.util.Properties; import java.io.InputStream; import java.io.IOException; import java.io.UnsupportedEncodingException; /** * This authentication scheme uses symetric encrypton to compare a received token with * a username stored in the configured identity store.. * * Subclasses may provide different mechanisms to store / retrieve tokens instead of enrcyption * * Created by IntelliJ IDEA. * User: sgonzalez * Date: Nov 10, 2008 * Time: 2:36:29 PM * @org.apache.xbean.XBean element="rememberme-auth-scheme" */ public class RememberMeAuthScheme extends AbstractAuthenticationScheme { public static final String USERNAME_CREDENTIAL_NAME="username"; public static final String REMEMBER_ME_TOKEN_CREDENTIAL_NAME="remembermeToken"; private static final Log logger = LogFactory.getLog(RememberMeAuthScheme.class); private String base64Key; public RememberMeAuthScheme() { this.setName("rememberme-authentication"); Properties authProps = new Properties(); InputStream is = null; try { is = this.getClass().getResourceAsStream("/josso-auth.properties"); if (is == null) throw new IOException("Cannot find resource /josso-auth.properties. Make sure this file is installed with JOSSO Gateway!"); authProps.load(is); this.base64Key = authProps.getProperty("josso.rememberme.authscheme.key"); // Just check that the key is not the one provided with the archetype ... if (this.base64Key.equals("5FvzKCtKKjeqakdm4c89WA\\=\\=")) logger.warn("Please, replace josso-auth.properties key! Do not use the one provided with the Gateway Archetype!"); } catch (IOException e) { logger.error("Cannot load auth properties : " + e.getMessage(), e); } finally { if (is != null) try { is.close(); } catch (IOException e) { /**/} } } public String getName() { return _name; } protected CredentialProvider doMakeCredentialProvider() { return this; } @Override public Credential newCredential(String name, Object value) { if (name.equalsIgnoreCase(REMEMBER_ME_TOKEN_CREDENTIAL_NAME)) { return new RememberMeCredential(value); } if (name.equalsIgnoreCase(USERNAME_CREDENTIAL_NAME)) { return new UsernameCredential(value); } // Don't know how to handle this name ... if (logger.isDebugEnabled()) logger.debug("Unknown credential name : " + name); return null; } public boolean authenticate() throws SSOAuthenticationException { setAuthenticated(false); String remembermeToken = getRemembermeToken(_inputCredentials); // Check if all credentials are present. if (remembermeToken == null || remembermeToken.length() == 0) { if (logger.isDebugEnabled()) { logger.debug("RememberMe Token" + (remembermeToken == null || remembermeToken.length() == 0 ? " not" : "") + " provided. "); } // We don't support empty values ! return false; } String username = getUsername(_inputCredentials); if (username == null || username.length() == 0) { if (logger.isDebugEnabled()) { logger.debug("Username not " + (remembermeToken == null || remembermeToken.length() == 0 ? " not" : "") + " provided. "); } // We don't support empty values ! return false; } // Get known gredentials Credential[] knownCredentials = getKnownCredentials(); String knownUsername = getUsername(knownCredentials); String knownRemembermeToken = getRemembermeToken(knownCredentials); // Validate user identity ... if (!validateUsername(username, knownUsername) || !validateRememberMeToken(remembermeToken, knownRemembermeToken)) { return false; } if (logger.isDebugEnabled()) logger.debug("[authenticate()], Rememberme Token : " + remembermeToken); // We have successfully authenticated this user. setAuthenticated(true); return true; } /** * This implementation will retrieve some credentials from the identity store but will also use input some credentials as known credentials * * @throws SSOAuthenticationException */ @Override protected Credential[] getKnownCredentials() throws SSOAuthenticationException { // Load username credential from store Credential[] creds = super.getKnownCredentials(); // Add remember me token to credential array Credential[] newCreds = new Credential[creds.length+1]; for (int i = 0; i < creds.length; i++) { Credential cred = creds[i]; newCreds[i] = creds[i]; } newCreds[newCreds.length - 1] = getRememberMeCredential(_inputCredentials); return newCreds; } public Principal getPrincipal() { return new SimplePrincipal(getUsername(_inputCredentials)); } public Principal getPrincipal(Credential[] credentials) { return new SimplePrincipal(getUsername(credentials)); } public Credential[] getPrivateCredentials() { Credential c = getRememberMeCredential(_inputCredentials); if (c == null) return new Credential[0]; Credential[] r = {c}; return r; } public Credential[] getPublicCredentials() { Credential c = getRememberMeCredential(_inputCredentials); if (c == null) return new Credential[0]; Credential[] r = {c}; return r; } /** * This implementation will * @param userCredentials * @param s */ @Override public void initialize(Credential[] userCredentials, Subject s) { super.initialize(userCredentials, s); RememberMeCredential rememberMe = getRememberMeCredential(userCredentials); if (rememberMe == null) { logger.warn("No remember me credential recevied"); return; } // Add a new input credential with the username associated to the remember me token String remembermeToken = (String) rememberMe.getValue(); String username = getUsernameForToken(remembermeToken); if (username == null) { logger.debug("Username not provided, skiping UsernameCredential injection"); return; } // Now, inject username credential Credential usernameCred = doMakeCredentialProvider().newCredential(USERNAME_CREDENTIAL_NAME, username); this._inputCredentials = new Credential[_inputCredentials.length+1]; for (int i = 0; i < userCredentials.length; i++) { Credential userCredential = userCredentials[i]; _inputCredentials[i] = userCredential; } _inputCredentials[_inputCredentials.length - 1] = usernameCred; } /** * This will decrypt a remember me token into a username * * If the token is not symmetric, this should return null. * @param remembermeToken * @return */ public String getUsernameForToken(String remembermeToken) { try { String msg = CipherUtil.decryptAES(URLDecoder.decode(remembermeToken, "UTF-8"), base64Key); return msg.substring("josso:".length()); } catch (UnsupportedEncodingException e) { logger.debug(e.getMessage(), e); } catch (InvalidKeyException e) { logger.debug(e.getMessage(), e); } catch (NoSuchAlgorithmException e) { logger.debug(e.getMessage(), e); } catch (NoSuchPaddingException e) { logger.debug(e.getMessage(), e); } catch (IllegalBlockSizeException e) { logger.debug(e.getMessage(), e); } catch (BadPaddingException e) { logger.debug(e.getMessage(), e); } return null; } /** * This will encrypt a username into a remember me token * @param username * @return */ public String getRemembermeTokenForUser(String username) { try { String token = CipherUtil.encryptAES("josso:" + username, base64Key); return URLEncoder.encode(token, "UTF-8"); } catch (UnsupportedEncodingException e) { logger.error(e.getMessage(), e); } catch (NoSuchAlgorithmException e) { logger.error(e.getMessage(), e); } catch (NoSuchPaddingException e) { logger.error(e.getMessage(), e); } catch (InvalidKeyException e) { logger.error(e.getMessage(), e); } catch (IllegalBlockSizeException e) { logger.error(e.getMessage(), e); } catch (BadPaddingException e) { logger.error(e.getMessage(), e); } return ""; } // ---------------------------------------------------------- protected boolean validateUsername(String username, String knownUsername) { return username != null && knownUsername != null && username.length() > 0 && knownUsername.length() > 0 && username.equals(knownUsername); } protected boolean validateRememberMeToken(String rememberMeToken, String knownRememberMeToken) { return rememberMeToken != null && knownRememberMeToken != null && rememberMeToken.length() > 0 && knownRememberMeToken.length() > 0 && rememberMeToken.equals(knownRememberMeToken); } /** * Finds the credential instance that is a RememberMeCredential in the given set */ protected RememberMeCredential getRememberMeCredential(Credential[] credentials) { for (int i = 0; i < credentials.length; i++) { if (credentials[i] instanceof RememberMeCredential) { return (RememberMeCredential) credentials[i]; } } return null; } /** * Finds the credential instance that is a UsernameCredential in the given set */ protected UsernameCredential getUsernameCredential(Credential[] credentials) { for (int i = 0; i < credentials.length; i++) { if (credentials[i] instanceof UsernameCredential) { return (UsernameCredential) credentials[i]; } } return null; } /** * Gets a username based on a credential set * * @see #getUsernameCredential(org.josso.auth.Credential[]) */ protected String getUsername(Credential[] creds) { UsernameCredential cred = getUsernameCredential(creds); if (cred == null) return null; return cred.getValue().toString(); } /** * Gets a remember me token based on a credential set * @see #getRememberMeCredential(org.josso.auth.Credential[]) */ protected String getRemembermeToken(Credential[] creds) { RememberMeCredential cred = getRememberMeCredential(creds); if (cred == null) return null; return cred.getValue().toString(); } }