blob: bf5283425b055a6f5b3e8425d2bedef9dcf328ea [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.realm;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jsecurity.authc.*;
import org.jsecurity.authc.credential.AllowAllCredentialsMatcher;
import org.jsecurity.authc.credential.CredentialsMatcher;
import org.jsecurity.authc.credential.SimpleCredentialsMatcher;
import org.jsecurity.cache.CacheManager;
import org.jsecurity.subject.PrincipalCollection;
/**
* A top-level abstract implementation of the <tt>Realm</tt> interface that only implements authentication support
* (log-in) operations and leaves authorization (access control) behavior to subclasses.
*
* <p>Since a Realm provides both authentication <em>and</em> authorization operations, the implementation approach for
* this class could have been reversed. That is, authorization support could have been implemented here and
* authentication support left to subclasses.
*
* <p>The reason the existing implementation is in place though
* (authentication support) is that most authentication operations are fairly common across the large majority of
* applications, whereas authorization operations are more so heavily dependent upon the application's data model, which
* can vary widely.
*
* <p>By providing the most common authentication operations here and leaving data-model specific authorization checks
* to subclasses, a top-level abstract class for most common authentication behavior is more useful as an extension
* point for most applications.
*
* @author Les Hazlewood
* @author Jeremy Haile
* @since 0.2
*/
public abstract class AuthenticatingRealm extends CachingRealm implements LogoutAware {
private static final Log log = LogFactory.getLog(AuthenticatingRealm.class);
/**
* Password matcher used to determine if the provided password matches
* the password stored in the data store.
*/
private CredentialsMatcher credentialsMatcher = new SimpleCredentialsMatcher();
/**
* The class that this realm supports for authentication tokens. This is used by the
* default implementation of the {@link Realm#supports(org.jsecurity.authc.AuthenticationToken)} method to
* determine whether or not the given authentication token is supported by this realm.
*/
private Class<? extends AuthenticationToken> authenticationTokenClass = UsernamePasswordToken.class;
/*--------------------------------------------
| C O N S T R U C T O R S |
============================================*/
public AuthenticatingRealm() {
}
public AuthenticatingRealm(CacheManager cacheManager) {
setCacheManager(cacheManager);
}
public AuthenticatingRealm(CredentialsMatcher matcher) {
setCredentialsMatcher(matcher);
}
public AuthenticatingRealm(CacheManager cacheManager, CredentialsMatcher matcher) {
setCacheManager(cacheManager);
setCredentialsMatcher(matcher);
}
/*--------------------------------------------
| A C C E S S O R S / M O D I F I E R S |
============================================*/
/**
* Returns the <code>CredentialsMatcher</code> used during an authentication attempt to verify submitted
* credentials with those stored in the system.
*
* <p>Unless overridden by the {@link #setCredentialsMatcher setCredentialsMatcher} method, the default
* value is a {@link org.jsecurity.authc.credential.SimpleCredentialsMatcher SimpleCredentialsMatcher} instance.
*
* @return the <code>CredentialsMatcher</code> used during an authentication attempt to verify submitted
* credentials with those stored in the system.
*/
public CredentialsMatcher getCredentialsMatcher() {
return credentialsMatcher;
}
/**
* Sets the CrendialsMatcher used during an authentication attempt to verify submitted credentials with those
* stored in the system. The implementation of this matcher can be switched via configuration to
* support any number of schemes, including plain text comparisons, hashing comparisons, and others.
*
* <p>Unless overridden by this method, the default value is a
* {@link org.jsecurity.authc.credential.SimpleCredentialsMatcher} instance.
*
* @param credentialsMatcher the matcher to use.
*/
public void setCredentialsMatcher(CredentialsMatcher credentialsMatcher) {
this.credentialsMatcher = credentialsMatcher;
}
/**
* Returns the authenticationToken class supported by this realm.
*
* <p>The default value is <tt>{@link UsernamePasswordToken UsernamePasswordToken.class}</tt>, since
* about 90% of realms use username/password authentication, regardless of their protocol (e.g. over jdbc, ldap,
* kerberos, http, etc).
*
* <p>If subclasses haven't already overridden the {@link Realm#supports Realm.supports(AuthenticationToken)} method,
* they must {@link #setAuthenticationTokenClass(Class) set a new class} if they won't support
* <tt>UsernamePasswordToken</tt> authentication token submissions.
*
* @return the authenticationToken class supported by this realm.
* @see #setAuthenticationTokenClass
*/
public Class getAuthenticationTokenClass() {
return authenticationTokenClass;
}
/**
* Sets the authenticationToken class supported by this realm.
*
* <p>Unless overridden by this method, the default value is
* {@link UsernamePasswordToken UsernamePasswordToken.class} to support the majority of applications.
*
* @param authenticationTokenClass the class of authentication token instances supported by this realm.
* @see #getAuthenticationTokenClass getAuthenticationTokenClass() for more explanation.
*/
public void setAuthenticationTokenClass(Class<? extends AuthenticationToken> authenticationTokenClass) {
this.authenticationTokenClass = authenticationTokenClass;
}
/*--------------------------------------------
| M E T H O D S |
============================================*/
/**
* Convenience implementation that returns
* <tt>getAuthenticationTokenClass().isAssignableFrom( token.getClass() );</tt>. Can be overridden
* by subclasses for more complex token checking.
* <p>Most configurations will only need to set a different class via
* {@link #setAuthenticationTokenClass}, as opposed to overriding this method.
*
* @param token the token being submitted for authentication.
* @return true if this authentication realm can process the submitted token instance of the class, false otherwise.
*/
public boolean supports(AuthenticationToken token) {
return token != null && getAuthenticationTokenClass().isAssignableFrom(token.getClass());
}
public final AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
AuthenticationInfo info = doGetAuthenticationInfo(token);
if (info == null) {
if (log.isDebugEnabled()) {
String msg = "No authentication information found for submitted authentication token [" + token + "]. " +
"Returning null.";
log.debug(msg);
}
return null;
}
CredentialsMatcher cm = getCredentialsMatcher();
if (cm != null) {
if (!cm.doCredentialsMatch(token, info)) {
String msg = "The credentials provided for account [" + token +
"] did not match the expected credentials.";
throw new IncorrectCredentialsException(msg);
}
} else {
throw new AuthenticationException("A CredentialsMatcher must be configured in order to verify " +
"credentials during authentication. If you do not wish for credentials to be examined, you " +
"can configure an " + AllowAllCredentialsMatcher.class.getName() + " instance.");
}
return info;
}
/**
* Retrieves authentication data from an implementation-specific datasource (RDBMS, LDAP, etc) for the given
* authentication token.
*
* <p>For most datasources, this means just 'pulling' authentication data for an associated subject/user and nothing
* more and letting JSecurity do the rest. But in some systems, this method could actually perform EIS specific
* log-in logic in addition to just retrieving data - it is up to the Realm implementation.
*
* <p>A <tt>null</tt> return value means that no account could be associated with the specified token.
*
* @param token the authentication token containing the user's principal and credentials.
* @return an {@link AuthenticationInfo} object containing account data resulting from the
* authentication ONLY if the lookup is successful (i.e. account exists and is valid, etc.)
* @throws org.jsecurity.authc.AuthenticationException
* if there is an error acquiring data or performing
* realm-specific authentication logic for the specified <tt>token</tt>
*/
protected abstract AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException;
/**
* Default implementation that does nothing (no-op) and exists as a convenience mechanism in case subclasses
* wish to override it to implement realm-specific logout logic for the given user account logging out.</p>
*
* <p>In a single-realm JSecurity configuration (most applications), the <code>principals</code> method
* argument will be the same as that which is contained in the <code>AuthenticationInfo</code> object returned by the
* {@link #doGetAuthenticationInfo} method (that is, {@link AuthenticationInfo#getPrincipals info.getPrincipals()}).
*
* <p>In a multi-realm JSecurity configuration, the given <code>principals</code> method
* argument could contain principals returned by many realms. Therefore the subclass implementation would need
* to know how to extract the principal(s) relevant to only itself and ignore other realms' principals.</p>
*
* @param principals the application-specific Subject/user identifier that is logging out.
*/
public void onLogout(PrincipalCollection principals) {
//no-op, here for subclass override if desired.
}
}