blob: 16325acd913fd7c5ab7fd79bd4a20656b54ef7ae [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jsecurity.subject;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jsecurity.authc.AuthenticationException;
import org.jsecurity.authc.AuthenticationInfo;
import org.jsecurity.authc.AuthenticationToken;
import org.jsecurity.authc.RememberMeAuthenticationToken;
import org.jsecurity.codec.Base64;
import org.jsecurity.codec.Hex;
import org.jsecurity.crypto.BlowfishCipher;
import org.jsecurity.crypto.Cipher;
import org.jsecurity.io.DefaultSerializer;
import org.jsecurity.io.SerializationException;
import org.jsecurity.io.Serializer;
/**
* Abstract implementation of the <code>RememberMeManager</code> interface that handles
* {@link #setSerializer(org.jsecurity.io.Serializer) serialization} and
* {@link #setCipher(org.jsecurity.crypto.Cipher) encryption} of the remembered user identity.
*
* <p>The remembered identity storage location is implementation-specific.
*
* @author Les Hazlewood
* @author Jeremy Haile
* @since 0.9
*/
public abstract class AbstractRememberMeManager implements RememberMeManager {
private static final Log log = LogFactory.getLog(AbstractRememberMeManager.class);
private Serializer serializer = new DefaultSerializer();
private Cipher cipher = new BlowfishCipher();
private byte[] encryptionCipherKey = null;
private byte[] decryptionCipherKey = null;
public AbstractRememberMeManager() {
}
public Serializer getSerializer() {
return serializer;
}
public void setSerializer(Serializer serializer) {
this.serializer = serializer;
}
public Cipher getCipher() {
return cipher;
}
public void setCipher(Cipher cipher) {
this.cipher = cipher;
}
public byte[] getEncryptionCipherKey() {
return encryptionCipherKey;
}
public void setEncryptionCipherKey(byte[] encryptionCipherKey) {
this.encryptionCipherKey = encryptionCipherKey;
}
public void setEncryptionCipherKeyHex(String hex) {
setEncryptionCipherKey(Hex.decode(hex));
}
public void setEncryptionCipherKeyBase64(String base64) {
setEncryptionCipherKey(Base64.decode(base64));
}
public byte[] getDecryptionCipherKey() {
return decryptionCipherKey;
}
public void setDecryptionCipherKey(byte[] decryptionCipherKey) {
this.decryptionCipherKey = decryptionCipherKey;
}
public void setDecryptionCipherKeyHex(String hex) {
setDecryptionCipherKey(Hex.decode(hex));
}
public void setDecryptionCipherKeyBase64(String base64) {
setDecryptionCipherKey(Base64.decode(base64));
}
public byte[] getCipherKey() {
//Since this method should only be used with symmetric ciphers
//(where the enc and dec keys are the same), either is fine, just return one of them:
return getEncryptionCipherKey();
}
public void setCipherKey(byte[] cipherKey) {
//Since this method should only be used in symmetric ciphers
//(where the enc and dec keys are the same), set it on both:
setEncryptionCipherKey(cipherKey);
setDecryptionCipherKey(cipherKey);
}
public void setCipherKeyHex(String hex) {
setCipherKey(Hex.decode(hex));
}
public void setCipherKeyBase64(String base64) {
setCipherKey(Base64.decode(base64));
}
// Abstract methods to be implemented by subclasses
protected abstract void rememberSerializedIdentity(byte[] serialized);
protected abstract byte[] getSerializedRememberedIdentity();
protected abstract void forgetIdentity();
protected boolean isRememberMe(AuthenticationToken token) {
return token != null && (token instanceof RememberMeAuthenticationToken) &&
((RememberMeAuthenticationToken) token).isRememberMe();
}
public void onSuccessfulLogin(AuthenticationToken token, AuthenticationInfo info) {
//always clear any previous identity:
forgetIdentity(token);
//reset it if necessary:
if (isRememberMe(token)) {
rememberIdentity(token, info);
} else {
if (log.isDebugEnabled()) {
log.debug("AuthenticationToken did not indicate RememberMe is requested. " +
"RememberMe functionality will not be executed for corresponding account.");
}
}
}
public void rememberIdentity(AuthenticationToken submittedToken, AuthenticationInfo successfullyAuthenticated) {
rememberIdentity(successfullyAuthenticated);
}
public void rememberIdentity(AuthenticationInfo successfullyAuthenticated) {
PrincipalCollection principals = getIdentityToRemember(successfullyAuthenticated);
rememberIdentity(principals);
}
protected PrincipalCollection getIdentityToRemember(AuthenticationInfo info) {
return info.getPrincipals();
}
protected void rememberIdentity(PrincipalCollection accountPrincipals) {
try {
byte[] bytes = serialize(accountPrincipals);
if (getCipher() != null) {
bytes = encrypt(bytes);
}
rememberSerializedIdentity(bytes);
} catch (SerializationException se) {
if (log.isWarnEnabled()) {
log.warn("Unable to serialize account principals [" + accountPrincipals + "]. Identity " +
"cannot be remembered! This is a non fatal exception as RememberMe identity services " +
"are not considered critical and execution can continue as normal. But please " +
"investigate and resolve to prevent seeing this message again.", se);
}
}
}
public PrincipalCollection getRememberedPrincipals() {
try {
PrincipalCollection principals = null;
byte[] bytes = getSerializedRememberedIdentity();
if (bytes != null) {
if (getCipher() != null) {
bytes = decrypt(bytes);
}
try {
principals = deserialize(bytes);
} catch (SerializationException e) {
if (log.isWarnEnabled()) {
log.warn("Unable to deserialize stored identity byte array. Remembered identity " +
"cannot be reconstituted! This is a non fatal exception as RememberMe identity services " +
"are not considered critical and execution can continue as normal, but please " +
"investigate and resolve to prevent seeing this message again.", e);
}
}
}
return principals;
} catch( Exception e ) {
return onRememberedPrincipalFailure( e );
}
}
/**
* Called when an exception is thrown while trying to retrieve principals. The default implementation logs a
* warning and forgets the problem identity. This most commonly would occur when an encryption key is
* updated and old principals are retrieved that have been encrypted with the previous key.
* @param e the exception that was thrown.
* @return the principal collection to be returned.
*/
protected PrincipalCollection onRememberedPrincipalFailure(Exception e) {
if(log.isWarnEnabled() ) {
log.warn("There was a failure while trying to retrieve remembered principals. This could be due to a " +
"configuration problem or corrupted principals. This could also be due to a recently " +
"changed encryption key. The remembered identity will be forgotten and not used for this " +
"request.", e);
}
forgetIdentity();
return null;
}
protected byte[] encrypt(byte[] serialized) {
byte[] value = serialized;
Cipher cipher = getCipher();
if (cipher != null) {
value = cipher.encrypt(serialized, getEncryptionCipherKey());
}
return value;
}
protected byte[] decrypt(byte[] encrypted) {
byte[] serialized = encrypted;
Cipher cipher = getCipher();
if (cipher != null) {
serialized = cipher.decrypt(encrypted, getDecryptionCipherKey());
}
return serialized;
}
protected byte[] serialize(PrincipalCollection principals) {
return getSerializer().serialize(principals);
}
protected PrincipalCollection deserialize(byte[] serializedIdentity) {
return (PrincipalCollection) getSerializer().deserialize(serializedIdentity);
}
public void onFailedLogin(AuthenticationToken token, AuthenticationException ae) {
forgetIdentity(token, ae);
}
public void onLogout(PrincipalCollection subjectPrincipals) {
forgetIdentity();
}
protected void forgetIdentity(AuthenticationToken token, AuthenticationException ae) {
forgetIdentity(token);
}
protected void forgetIdentity(AuthenticationToken token) {
forgetIdentity();
}
}