1
0
mirror of synced 2026-08-05 17:57:15 +00:00

Initial commit.

This commit is contained in:
Ben Alex
2004-03-16 23:57:17 +00:00
commit 35fe1e7b73
267 changed files with 17812 additions and 0 deletions
@@ -0,0 +1,55 @@
/*
* The Acegi Security System for Spring is published under the terms
* of the Apache Software License.
*
* Visit http://acegisecurity.sourceforge.net for further details.
*/
package net.sf.acegisecurity;
import org.aopalliance.intercept.MethodInvocation;
/**
* Makes a final access control (authorization) decision.
*
* @author Ben Alex
* @version $Id$
*/
public interface AccessDecisionManager {
//~ Methods ================================================================
/**
* Resolves an access control decision for the passed parameters.
*
* @param authentication the caller invoking the method
* @param invocation the method being called
* @param config the configuration attributes associated with the method
* being invoked
*
* @throws AccessDeniedException if access is denied
*/
public void decide(Authentication authentication,
MethodInvocation invocation,
ConfigAttributeDefinition config)
throws AccessDeniedException;
/**
* Indicates whether this <code>AccessDecisionManager</code> is able to
* process authorization requests presented with the passed
* <code>ConfigAttribute</code>.
*
* <p>
* This allows the <code>SecurityInterceptor</code> to check every
* configuration attribute can be consumed by the configured
* <code>AccessDecisionManager</code> and/or <code>RunAsManager</code>.
* </p>
*
* @param attribute a configuration attribute that has been configured
* against the <code>SecurityInterceptor</code>
*
* @return true if this <code>AccessDecisionManager</code> can support the
* passed configuration attribute
*/
public boolean supports(ConfigAttribute attribute);
}
@@ -0,0 +1,40 @@
/*
* The Acegi Security System for Spring is published under the terms
* of the Apache Software License.
*
* Visit http://acegisecurity.sourceforge.net for further details.
*/
package net.sf.acegisecurity;
/**
* Thrown if an {@link Authentication} object does not hold a required
* authority.
*
* @author Ben Alex
* @version $Id$
*/
public class AccessDeniedException extends AcegiSecurityException {
//~ Constructors ===========================================================
/**
* Constructs an <code>AccessDeniedException</code> with the specified
* message.
*
* @param msg the detail message
*/
public AccessDeniedException(String msg) {
super(msg);
}
/**
* Constructs an <code>AccessDeniedException</code> with the specified
* message and root cause.
*
* @param msg the detail message
* @param t root cause
*/
public AccessDeniedException(String msg, Throwable t) {
super(msg, t);
}
}
@@ -0,0 +1,48 @@
/*
* The Acegi Security System for Spring is published under the terms
* of the Apache Software License.
*
* Visit http://acegisecurity.sourceforge.net for further details.
*/
package net.sf.acegisecurity;
import org.springframework.core.NestedRuntimeException;
/**
* Abstract superclass for all exceptions thrown in the security package and
* subpackages.
*
* <p>
* Note that this is a runtime (unchecked) exception. Security exceptions are
* usually fatal; there is no reason for them to be checked.
* </p>
*
* @author Ben Alex
* @version $Id$
*/
public abstract class AcegiSecurityException extends NestedRuntimeException {
//~ Constructors ===========================================================
/**
* Constructs an <code>AcegiSecurityException</code> with the specified
* message and root cause.
*
* @param msg the detail message
* @param t the root cause
*/
public AcegiSecurityException(String msg, Throwable t) {
super(msg, t);
}
/**
* Constructs an <code>AcegiSecurityException</code> with the specified
* message and no root cause.
*
* @param msg the detail message
*/
public AcegiSecurityException(String msg) {
super(msg);
}
}
@@ -0,0 +1,69 @@
/*
* The Acegi Security System for Spring is published under the terms
* of the Apache Software License.
*
* Visit http://acegisecurity.sourceforge.net for further details.
*/
package net.sf.acegisecurity;
/**
* Represents an authentication request.
*
* <p>
* An <code>Authentication</code> object is not considered authenticated until
* it is processed by an {@link AuthenticationManager}.
* </p>
*
* <p>
* Stored in a request {@link net.sf.acegisecurity.context.SecureContext}.
* </p>
*
* @author Ben Alex
* @version $Id$
*/
public interface Authentication {
//~ Methods ================================================================
public void setAuthenticated(boolean isAuthenticated);
/**
* Indicates whether or not authentication was attempted by the {@link
* net.sf.acegisecurity.SecurityInterceptor}. Note that classes should
* not rely on this value as being valid unless it has been set by a
* trusted <code>SecurityInterceptor</code>.
*
* @return true if authenticated by the <code>SecurityInterceptor</code>
*/
public boolean isAuthenticated();
/**
* Set by an <code>AuthenticationManager</code> to indicate the authorities
* that the principal has been granted. Note that classes should not rely
* on this value as being valid unless it has been set by a trusted
* <code>AuthenticationManager</code>.
*
* @return the authorities granted to the principal, or <code>null</code>
* if authentication has not been completed
*/
public GrantedAuthority[] getAuthorities();
/**
* The credentials that prove the principal is correct. This is usually a
* password, but could be anything relevant to the
* <code>AuthenticationManager</code>. Callers are expected to populate
* the credentials.
*
* @return the credentials that prove the identity of the
* <code>Principal</code>
*/
public Object getCredentials();
/**
* The identity of the principal being authenticated. This is usually a
* username. Callers are expected to populate the principal.
*
* @return the <code>Principal</code> being authenticated
*/
public Object getPrincipal();
}
@@ -0,0 +1,42 @@
/*
* The Acegi Security System for Spring is published under the terms
* of the Apache Software License.
*
* Visit http://acegisecurity.sourceforge.net for further details.
*/
package net.sf.acegisecurity;
/**
* Thrown if an authentication request is rejected because there is no {@link
* Authentication} object in the {@link
* net.sf.acegisecurity.context.SecureContext}.
*
* @author Ben Alex
* @version $Id$
*/
public class AuthenticationCredentialsNotFoundException
extends AuthenticationException {
//~ Constructors ===========================================================
/**
* Constructs an <code>AuthenticationCredentialsNotFoundException</code>
* with the specified message.
*
* @param msg the detail message
*/
public AuthenticationCredentialsNotFoundException(String msg) {
super(msg);
}
/**
* Constructs an <code>AuthenticationCredentialsNotFoundException</code>
* with the specified message and root cause.
*
* @param msg the detail message
* @param t root cause
*/
public AuthenticationCredentialsNotFoundException(String msg, Throwable t) {
super(msg, t);
}
}
@@ -0,0 +1,41 @@
/*
* The Acegi Security System for Spring is published under the terms
* of the Apache Software License.
*
* Visit http://acegisecurity.sourceforge.net for further details.
*/
package net.sf.acegisecurity;
/**
* Abstract superclass for all exceptions related to the {@link
* AuthenticationManager} being unable to authenticate an {@link
* Authentication} object.
*
* @author Ben Alex
* @version $Id$
*/
public abstract class AuthenticationException extends AcegiSecurityException {
//~ Constructors ===========================================================
/**
* Constructs an <code>AuthenticationException</code> with the specified
* message and root cause.
*
* @param msg the detail message
* @param t the root cause
*/
public AuthenticationException(String msg, Throwable t) {
super(msg, t);
}
/**
* Constructs an <code>AuthenticationException</code> with the specified
* message and no root cause.
*
* @param msg the detail message
*/
public AuthenticationException(String msg) {
super(msg);
}
}
@@ -0,0 +1,61 @@
/*
* The Acegi Security System for Spring is published under the terms
* of the Apache Software License.
*
* Visit http://acegisecurity.sourceforge.net for further details.
*/
package net.sf.acegisecurity;
/**
* Processes an {@link Authentication} request.
*
* @author Ben Alex
* @version $Id$
*/
public interface AuthenticationManager {
//~ Methods ================================================================
/**
* Attempts to authenticate the passed {@link Authentication} object,
* returning a fully populated <code>Authentication</code> object
* (including granted authorities) if successful.
*
* <p>
* An <code>AuthenticationManager</code> must honour the following contract
* concerning exceptions:
* </p>
*
* <p>
* A {@link DisabledException} must be thrown if an account is disabled and
* the <code>AuthenticationManager</code> can test for this state.
* </p>
*
* <p>
* A {@link LockedException} must be thrown if an account is locked and the
* <code>AuthenticationManager</code> can test for account locking.
* </p>
*
* <p>
* A {@link BadCredentialsException} must be thrown if incorrect
* credentials are presented. Whilst the above exceptions are optional, an
* <code>AuthenticationManager</code> must <B>always</B> test credentials.
* </p>
*
* <p>
* Exceptions should be tested for and if applicable thrown in the order
* expressed above (ie if an account is disabled or locked, the
* authentication request is immediately rejected and the credentials
* testing process is not performed). This prevents credentials being
* tested against disabled or locked accounts.
* </p>
*
* @param authentication the authentication request object
*
* @return a fully authenticated object including credentials
*
* @throws AuthenticationException if authentication fails
*/
public Authentication authenticate(Authentication authentication)
throws AuthenticationException;
}
@@ -0,0 +1,44 @@
/*
* The Acegi Security System for Spring is published under the terms
* of the Apache Software License.
*
* Visit http://acegisecurity.sourceforge.net for further details.
*/
package net.sf.acegisecurity;
/**
* Thrown if an authentication request could not be processed due to a system
* problem.
*
* <p>
* This might be thrown if a backend authentication repository is unavailable.
* </p>
*
* @author Ben Alex
* @version $Id$
*/
public class AuthenticationServiceException extends AuthenticationException {
//~ Constructors ===========================================================
/**
* Constructs an <code>AuthenticationServiceException</code> with the
* specified message.
*
* @param msg the detail message
*/
public AuthenticationServiceException(String msg) {
super(msg);
}
/**
* Constructs an <code>AuthenticationServiceException</code> with the
* specified message and root cause.
*
* @param msg the detail message
* @param t root cause
*/
public AuthenticationServiceException(String msg, Throwable t) {
super(msg, t);
}
}
@@ -0,0 +1,41 @@
/*
* The Acegi Security System for Spring is published under the terms
* of the Apache Software License.
*
* Visit http://acegisecurity.sourceforge.net for further details.
*/
package net.sf.acegisecurity;
/**
* Thrown if an authentication request is rejected because the credentials are
* invalid. For this exception to be thrown, it means the account is neither
* locked nor disabled.
*
* @author Ben Alex
* @version $Id$
*/
public class BadCredentialsException extends AuthenticationException {
//~ Constructors ===========================================================
/**
* Constructs a <code>BadCredentialsException</code> with the specified
* message.
*
* @param msg the detail message
*/
public BadCredentialsException(String msg) {
super(msg);
}
/**
* Constructs a <code>BadCredentialsException</code> with the specified
* message and root cause.
*
* @param msg the detail message
* @param t root cause
*/
public BadCredentialsException(String msg, Throwable t) {
super(msg, t);
}
}
@@ -0,0 +1,53 @@
/*
* The Acegi Security System for Spring is published under the terms
* of the Apache Software License.
*
* Visit http://acegisecurity.sourceforge.net for further details.
*/
package net.sf.acegisecurity;
/**
* Stores a security system related configuration attribute.
*
* <p>
* When the {@link SecurityInterceptor} is setup, a list of configuration
* attributes is defined for secure method patterns. These configuration
* attributes have special meaning to a {@link RunAsManager}, {@link
* AccessDecisionManager} or <code>AccessDecisionManager</code> delegate.
* </p>
*
* <P>
* Stored at runtime with other <code>ConfigAttribute</code>s for the same
* method within a {@link ConfigAttributeDefinition}.
* </p>
*
* @author <A HREF="mailto:ben.alex@fremerx.com">Ben Alex</A>
* @version $Id$
*/
public interface ConfigAttribute {
//~ Methods ================================================================
/**
* If the <code>ConfigAttribute</code> can be represented as a
* <code>String</code> and that <code>String</code> is sufficient in
* precision to be relied upon as a configuration parameter by a {@link
* RunAsManager}, {@link AccessDecisionManager} or
* <code>AccessDecisionManager</code> delegate, this method should return
* such a <code>String</code>.
*
* <p>
* If the <code>ConfigAttribute</code> cannot be expressed with sufficient
* precision as a <code>String</code>, <code>null</code> should be
* returned. Returning <code>null</code> will require an relying classes
* to specifically support the <code>ConfigAttribute</code>
* implementation, so returning <code>null</code> should be avoided
* unless actually required.
* </p>
*
* @return a representation of the configuration attribute (or
* <code>null</code> if the configuration attribute cannot be
* expressed as a <code>String</code> with sufficient precision).
*/
public String getAttribute();
}
@@ -0,0 +1,62 @@
/*
* The Acegi Security System for Spring is published under the terms
* of the Apache Software License.
*
* Visit http://acegisecurity.sourceforge.net for further details.
*/
package net.sf.acegisecurity;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
/**
* Holds a group of {@link ConfigAttribute}s that are associated with a given
* method.
*
* <p>
* All the <code>ConfigAttributeDefinition</code>s associated with a given
* <code>SecurityInterceptor</code> are stored in a {@link
* MethodDefinitionMap}.
* </p>
*
* @author Ben Alex
* @version $Id$
*/
public class ConfigAttributeDefinition {
//~ Instance fields ========================================================
private Set configAttributes = new HashSet();
//~ Constructors ===========================================================
public ConfigAttributeDefinition() {
super();
}
//~ Methods ================================================================
/**
* DOCUMENT ME!
*
* @return all the configuration attributes related to the method.
*/
public Iterator getConfigAttributes() {
return this.configAttributes.iterator();
}
/**
* Adds a <code>ConfigAttribute</code> that is related to the method.
*
* @param newConfigAttribute DOCUMENT ME!
*/
public void addConfigAttribute(ConfigAttribute newConfigAttribute) {
this.configAttributes.add(newConfigAttribute);
}
public String toString() {
return this.configAttributes.toString();
}
}
@@ -0,0 +1,39 @@
/*
* The Acegi Security System for Spring is published under the terms
* of the Apache Software License.
*
* Visit http://acegisecurity.sourceforge.net for further details.
*/
package net.sf.acegisecurity;
import org.springframework.util.StringUtils;
import java.beans.PropertyEditorSupport;
/**
* A property editor that can create a populated {@link
* ConfigAttributeDefinition} from a comma separated list of values.
*
* @author Ben Alex
* @version $Id$
*/
public class ConfigAttributeEditor extends PropertyEditorSupport {
//~ Methods ================================================================
public void setAsText(String s) throws IllegalArgumentException {
if ((s == null) || "".equals(s)) {
setValue(null);
} else {
String[] tokens = StringUtils.commaDelimitedListToStringArray(s);
ConfigAttributeDefinition configDefinition = new ConfigAttributeDefinition();
for (int i = 0; i < tokens.length; i++) {
configDefinition.addConfigAttribute(new SecurityConfig(tokens[i]));
}
setValue(configDefinition);
}
}
}
@@ -0,0 +1,40 @@
/*
* The Acegi Security System for Spring is published under the terms
* of the Apache Software License.
*
* Visit http://acegisecurity.sourceforge.net for further details.
*/
package net.sf.acegisecurity;
/**
* Thrown if an authentication request is rejected because the account is
* disabled. Makes no assertion as to whether or not the credentials were
* valid.
*
* @author Ben Alex
* @version $Id$
*/
public class DisabledException extends AuthenticationException {
//~ Constructors ===========================================================
/**
* Constructs a <code>DisabledException</code> with the specified message.
*
* @param msg the detail message
*/
public DisabledException(String msg) {
super(msg);
}
/**
* Constructs a <code>DisabledException</code> with the specified message
* and root cause.
*
* @param msg the detail message
* @param t root cause
*/
public DisabledException(String msg, Throwable t) {
super(msg, t);
}
}
@@ -0,0 +1,46 @@
/*
* The Acegi Security System for Spring is published under the terms
* of the Apache Software License.
*
* Visit http://acegisecurity.sourceforge.net for further details.
*/
package net.sf.acegisecurity;
/**
* Represents an authority granted to an {@link Authentication} object.
*
* <p>
* A <code>GrantedAuthority</code> must either represent itself as a
* <code>String</code> or be specifically supported by an {@link
* AccessDecisionManager}.
* </p>
*
* @author Ben Alex
* @version $Id$
*/
public interface GrantedAuthority {
//~ Methods ================================================================
/**
* If the <code>GrantedAuthority</code> can be represented as a
* <code>String</code> and that <code>String</code> is sufficient in
* precision to be relied upon for an access control decision by an {@link
* AccessDecisionManager} (or delegate), this method should return such a
* <code>String</code>.
*
* <p>
* If the <code>GrantedAuthority</code> cannot be expressed with sufficient
* precision as a <code>String</code>, <code>null</code> should be
* returned. Returning <code>null</code> will require an
* <code>AccessDecisionManager</code> (or delegate) to specifically
* support the <code>GrantedAuthority</code> implementation, so returning
* <code>null</code> should be avoided unless actually required.
* </p>
*
* @return a representation of the granted authority (or <code>null</code>
* if the granted authority cannot be expressed as a
* <code>String</code> with sufficient precision).
*/
public String getAuthority();
}
@@ -0,0 +1,64 @@
/*
* The Acegi Security System for Spring is published under the terms
* of the Apache Software License.
*
* Visit http://acegisecurity.sourceforge.net for further details.
*/
package net.sf.acegisecurity;
/**
* Basic concrete implementation of a {@link GrantedAuthority}.
*
* <p>
* Stores a <code>String</code> representation of an authority granted to the
* {@link Authentication} object.
* </p>
*
* @author Ben Alex
* @version $Id$
*/
public class GrantedAuthorityImpl implements GrantedAuthority {
//~ Instance fields ========================================================
private String role;
//~ Constructors ===========================================================
public GrantedAuthorityImpl(String role) {
super();
this.role = role;
}
private GrantedAuthorityImpl() {
super();
}
//~ Methods ================================================================
public String getAuthority() {
return this.role;
}
public boolean equals(Object obj) {
if (obj instanceof String) {
return obj.equals(this.role);
}
if (obj instanceof GrantedAuthority) {
GrantedAuthority attr = (GrantedAuthority) obj;
return this.role.equals(attr.getAuthority());
}
return false;
}
public int hashCode() {
return this.role.hashCode();
}
public String toString() {
return this.role;
}
}
@@ -0,0 +1,39 @@
/*
* The Acegi Security System for Spring is published under the terms
* of the Apache Software License.
*
* Visit http://acegisecurity.sourceforge.net for further details.
*/
package net.sf.acegisecurity;
/**
* Thrown if an authentication request is rejected because the account is
* locked. Makes no assertion as to whether or not the credentials were valid.
*
* @author Ben Alex
* @version $Id$
*/
public class LockedException extends AuthenticationException {
//~ Constructors ===========================================================
/**
* Constructs a <code>LockedException</code> with the specified message.
*
* @param msg the detail message.
*/
public LockedException(String msg) {
super(msg);
}
/**
* Constructs a <code>LockedException</code> with the specified message and
* root cause.
*
* @param msg the detail message.
* @param t root cause
*/
public LockedException(String msg, Throwable t) {
super(msg, t);
}
}
@@ -0,0 +1,116 @@
/*
* The Acegi Security System for Spring is published under the terms
* of the Apache Software License.
*
* Visit http://acegisecurity.sourceforge.net for further details.
*/
package net.sf.acegisecurity;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.metadata.Attributes;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Iterator;
/**
* Stores a {@link ConfigAttributeDefinition} for each method signature defined
* by Commons Attributes.
*
* @author Cameron Braid
* @author Ben Alex
* @version $Id$
*/
public class MethodDefinitionAttributes implements MethodDefinitionSource {
//~ Instance fields ========================================================
private Attributes attributes;
//~ Methods ================================================================
public void setAttributes(Attributes attributes) {
this.attributes = attributes;
}
public ConfigAttributeDefinition getAttributes(MethodInvocation invocation) {
ConfigAttributeDefinition definition = new ConfigAttributeDefinition();
Class interceptedClass = invocation.getMethod().getDeclaringClass();
// add the class level attributes for the implementing class
addClassAttributes(definition, interceptedClass);
// add the class level attributes for the implemented interfaces
addClassAttributes(definition, interceptedClass.getInterfaces());
// add the method level attributes for the implemented method
addMethodAttributes(definition, invocation.getMethod());
// add the method level attributes for the implemented intreface methods
addInterfaceMethodAttributes(definition, invocation.getMethod());
return definition;
}
public Iterator getConfigAttributeDefinitions() {
return null;
}
private void add(ConfigAttributeDefinition definition, Collection attribs) {
for (Iterator iter = attribs.iterator(); iter.hasNext();) {
Object o = (Object) iter.next();
if (o instanceof ConfigAttribute) {
definition.addConfigAttribute((ConfigAttribute) o);
}
}
}
private void addClassAttributes(ConfigAttributeDefinition definition,
Class clazz) {
addClassAttributes(definition, new Class[] {clazz});
}
private void addClassAttributes(ConfigAttributeDefinition definition,
Class[] clazz) {
for (int i = 0; i < clazz.length; i++) {
Collection classAttributes = attributes.getAttributes(clazz[i]);
if (classAttributes != null) {
add(definition, classAttributes);
}
}
}
private void addInterfaceMethodAttributes(ConfigAttributeDefinition definition,
Method method) {
Class[] interfaces = method.getDeclaringClass().getInterfaces();
for (int i = 0; i < interfaces.length; i++) {
Class clazz = interfaces[i];
try {
Method m = clazz.getDeclaredMethod(method.getName(),
method.getParameterTypes());
addMethodAttributes(definition, m);
} catch (Exception e) {
// this won't happen since we are getting a method from an interface that
// the declaring class implements
}
}
}
private void addMethodAttributes(ConfigAttributeDefinition definition,
Method method) {
// add the method level attributes
Collection methodAttributes = attributes.getAttributes(method);
if (methodAttributes != null) {
add(definition, methodAttributes);
}
}
}
@@ -0,0 +1,181 @@
/*
* The Acegi Security System for Spring is published under the terms
* of the Apache Software License.
*
* Visit http://acegisecurity.sourceforge.net for further details.
*/
package net.sf.acegisecurity;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
/**
* Stores a {@link ConfigAttributeDefinition} for each method signature defined
* in a bean context.
*
* @author Ben Alex
* @version $Id$
*/
public class MethodDefinitionMap implements MethodDefinitionSource {
//~ Static fields/initializers =============================================
private static final Log logger = LogFactory.getLog(MethodDefinitionMap.class);
//~ Instance fields ========================================================
/** Map from Method to ApplicationDefinition */
protected Map methodMap = new HashMap();
/** Map from Method to name pattern used for registration */
private Map nameMap = new HashMap();
//~ Methods ================================================================
public ConfigAttributeDefinition getAttributes(MethodInvocation invocation) {
return (ConfigAttributeDefinition) this.methodMap.get(invocation
.getMethod());
}
public Iterator getConfigAttributeDefinitions() {
return methodMap.values().iterator();
}
/**
* Add required authorities for a secure method. Method names can end or
* start with "" for matching multiple methods.
*
* @param method the method to be secured
* @param attr required authorities associated with the method
*/
public void addSecureMethod(Method method, ConfigAttributeDefinition attr) {
logger.info("Adding secure method [" + method + "] with attributes ["
+ attr + "]");
this.methodMap.put(method, attr);
}
/**
* Add required authorities for a secure method. Method names can end or
* start with "" for matching multiple methods.
*
* @param name class and method name, separated by a dot
* @param attr required authorities associated with the method
*
* @throws IllegalArgumentException DOCUMENT ME!
*/
public void addSecureMethod(String name, ConfigAttributeDefinition attr) {
int lastDotIndex = name.lastIndexOf(".");
if (lastDotIndex == -1) {
throw new IllegalArgumentException("'" + name
+ "' is not a valid method name: format is FQN.methodName");
}
String className = name.substring(0, lastDotIndex);
String methodName = name.substring(lastDotIndex + 1);
try {
Class clazz = Class.forName(className, true,
Thread.currentThread()
.getContextClassLoader());
addSecureMethod(clazz, methodName, attr);
} catch (ClassNotFoundException ex) {
throw new IllegalArgumentException("Class '" + className
+ "' not found");
}
}
/**
* Add required authorities for a secure method. Method names can end or
* start with "" for matching multiple methods.
*
* @param clazz target interface or class
* @param mappedName mapped method name
* @param attr required authorities associated with the method
*
* @throws IllegalArgumentException DOCUMENT ME!
*/
public void addSecureMethod(Class clazz, String mappedName,
ConfigAttributeDefinition attr) {
String name = clazz.getName() + '.' + mappedName;
if (logger.isDebugEnabled()) {
logger.debug("Adding secure method [" + name
+ "] with attributes [" + attr + "]");
}
Method[] methods = clazz.getDeclaredMethods();
List matchingMethods = new ArrayList();
for (int i = 0; i < methods.length; i++) {
if (methods[i].getName().equals(mappedName)
|| isMatch(methods[i].getName(), mappedName)) {
matchingMethods.add(methods[i]);
}
}
if (matchingMethods.isEmpty()) {
throw new IllegalArgumentException("Couldn't find method '"
+ mappedName + "' on " + clazz);
}
// register all matching methods
for (Iterator it = matchingMethods.iterator(); it.hasNext();) {
Method method = (Method) it.next();
String regMethodName = (String) this.nameMap.get(method);
if ((regMethodName == null)
|| (!regMethodName.equals(name)
&& (regMethodName.length() <= name.length()))) {
// no already registered method name, or more specific
// method name specification now -> (re-)register method
if (logger.isDebugEnabled() && (regMethodName != null)) {
logger.debug("Replacing attributes for secure method ["
+ method + "]: current name [" + name
+ "] is more specific than [" + regMethodName
+ "]");
}
this.nameMap.put(method, name);
addSecureMethod(method, attr);
} else {
if (logger.isDebugEnabled() && (regMethodName != null)) {
logger.debug("Keeping attributes for secure method ["
+ method + "]: current name [" + name
+ "] is not more specific than ["
+ regMethodName + "]");
}
}
}
}
/**
* Return if the given method name matches the mapped name. The default
* implementation checks for "xxx" and "xxx" matches.
*
* @param methodName the method name of the class
* @param mappedName the name in the descriptor
*
* @return if the names match
*/
private boolean isMatch(String methodName, String mappedName) {
return (mappedName.endsWith("*")
&& methodName.startsWith(mappedName.substring(0,
mappedName.length()
- 1)))
|| (mappedName.startsWith("*")
&& methodName.endsWith(mappedName.substring(1,
mappedName.length())));
}
}
@@ -0,0 +1,44 @@
/*
* The Acegi Security System for Spring is published under the terms
* of the Apache Software License.
*
* Visit http://acegisecurity.sourceforge.net for further details.
*/
package net.sf.acegisecurity;
import org.aopalliance.intercept.MethodInvocation;
import java.util.Iterator;
/**
* Implemented by classes that store {@link ConfigAttributeDefinition}s and can
* identify the appropriate <code>ConfigAttributeDefinition</code> that
* applies for the current method call.
*
* @author Ben Alex
* @version $Id$
*/
public interface MethodDefinitionSource {
//~ Methods ================================================================
/**
* DOCUMENT ME!
*
* @param invocation the method being called
*
* @return the <code>ConfigAttributeDefinition</code> that applies to the
* passed method call
*/
public ConfigAttributeDefinition getAttributes(MethodInvocation invocation);
/**
* If available, all of the <code>ConfigAttributeDefinition</code>s defined
* by the implementing class.
*
* @return an iterator over all the <code>ConfigAttributeDefinition</code>s
* or <code>null</code> if unsupported
*/
public Iterator getConfigAttributeDefinitions();
}
@@ -0,0 +1,70 @@
/*
* The Acegi Security System for Spring is published under the terms
* of the Apache Software License.
*
* Visit http://acegisecurity.sourceforge.net for further details.
*/
package net.sf.acegisecurity;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.propertyeditors.PropertiesEditor;
import java.beans.PropertyEditorSupport;
import java.util.Iterator;
import java.util.Properties;
/**
* Property editor to assist with the setup of {@link MethodDefinitionSource}.
*
* <p>
* The class creates and populates an {@link MethodDefinitionMap}.
* </p>
*
* @author Ben Alex
* @version $Id$
*/
public class MethodDefinitionSourceEditor extends PropertyEditorSupport {
//~ Static fields/initializers =============================================
private static final Log logger = LogFactory.getLog(MethodDefinitionSourceEditor.class);
//~ Methods ================================================================
public void setAsText(String s) throws IllegalArgumentException {
MethodDefinitionMap source = new MethodDefinitionMap();
if ((s == null) || "".equals(s)) {
// Leave value in property editor null
} else {
// Use properties editor to tokenize the string
PropertiesEditor propertiesEditor = new PropertiesEditor();
propertiesEditor.setAsText(s);
Properties props = (Properties) propertiesEditor.getValue();
// Now we have properties, process each one individually
ConfigAttributeEditor configAttribEd = new ConfigAttributeEditor();
for (Iterator iter = props.keySet().iterator(); iter.hasNext();) {
String name = (String) iter.next();
String value = props.getProperty(name);
// Convert value to series of security configuration attributes
configAttribEd.setAsText(value);
ConfigAttributeDefinition attr = (ConfigAttributeDefinition) configAttribEd
.getValue();
// Register name and attribute
source.addSecureMethod(name, attr);
}
}
setValue(source);
}
}
@@ -0,0 +1,90 @@
/*
* The Acegi Security System for Spring is published under the terms
* of the Apache Software License.
*
* Visit http://acegisecurity.sourceforge.net for further details.
*/
package net.sf.acegisecurity;
import org.aopalliance.intercept.MethodInvocation;
/**
* Creates a new temporary {@link Authentication} object for the current method
* invocation only.
*
* <P>
* This interface permits implementations to replace the
* <code>Authentication</code> object that applies to the current method
* invocation only. The {@link SecurityInterceptor} will replace the
* <code>Authentication</code> object held in the {@link
* net.sf.acegisecurity.context.SecureContext} for the duration of the method
* invocation only, returning it to the original <code>Authentication</code>
* object when the method invocation completes.
* </p>
*
* <P>
* This is provided so that systems with two layers of objects can be
* established. One layer is public facing and has normal secure methods with
* the granted authorities expected to be held by external callers. The other
* layer is private, and is only expected to be called by objects within the
* public facing layer. The objects in this private layer still need security
* (otherwise they would be public methods) and they also need security in
* such a manner that prevents them being called directly by external callers.
* The objects in the private layer would be configured to require granted
* authorities never granted to external callers. The
* <code>RunAsManager</code> interface provides a mechanism to elevate
* security in this manner.
* </p>
*
* <p>
* It is expected implementations will provide a corresponding concrete
* <code>Authentication</code> and <code>AuthenticationProvider</code> so that
* the replacement <code>Authentication</code> object can be authenticated.
* Some form of security will need to be implemented to prevent to ensure the
* <code>AuthenticationProvider</code> only accepts
* <code>Authentication</code> objects created by an authorized concrete
* implementation of <code>RunAsManager</code>.
* </p>
*
* @author Ben Alex
* @version $Id$
*/
public interface RunAsManager {
//~ Methods ================================================================
/**
* Returns a replacement <code>Authentication</code> object for the current
* method invocation, or <code>null</code> if replacement not required.
*
* @param authentication the caller invoking the method
* @param invocation the method being called
* @param config the configuration attributes associated with the method
* being invoked
*
* @return a replacement object to be used for duration of the method
* invocation
*/
public Authentication buildRunAs(Authentication authentication,
MethodInvocation invocation,
ConfigAttributeDefinition config);
/**
* Indicates whether this <code>RunAsManager</code> is able to process the
* passed <code>ConfigAttribute</code>.
*
* <p>
* This allows the <code>SecurityInterceptor</code> to check every
* configuration attribute can be consumed by the configured
* <code>AccessDecisionManager</code> and/or <code>RunAsManager</code>.
* </p>
*
* @param attribute a configuration attribute that has been configured
* against the <code>SecurityInterceptor</code>
*
* @return true if this <code>RunAsManager</code> can support the passed
* configuration attribute
*/
public boolean supports(ConfigAttribute attribute);
}
@@ -0,0 +1,58 @@
/*
* The Acegi Security System for Spring is published under the terms
* of the Apache Software License.
*
* Visit http://acegisecurity.sourceforge.net for further details.
*/
package net.sf.acegisecurity;
/**
* Stores a {@link ConfigAttribute} as a <code>String</code>.
*
* @author <A HREF="mailto:ben.alex@fremerx.com">Ben Alex</A>
* @version $Id$
*/
public class SecurityConfig implements ConfigAttribute {
//~ Instance fields ========================================================
private String attrib;
//~ Constructors ===========================================================
public SecurityConfig(String config) {
this.attrib = config;
}
private SecurityConfig() {
super();
}
//~ Methods ================================================================
public String getAttribute() {
return this.attrib;
}
public boolean equals(Object obj) {
if (obj instanceof String) {
return obj.equals(this.attrib);
}
if (obj instanceof ConfigAttribute) {
ConfigAttribute attr = (ConfigAttribute) obj;
return this.attrib.equals(attr.getAttribute());
}
return false;
}
public int hashCode() {
return this.attrib.hashCode();
}
public String toString() {
return this.attrib;
}
}
@@ -0,0 +1,303 @@
/*
* The Acegi Security System for Spring is published under the terms
* of the Apache Software License.
*
* Visit http://acegisecurity.sourceforge.net for further details.
*/
package net.sf.acegisecurity;
import net.sf.acegisecurity.context.Context;
import net.sf.acegisecurity.context.ContextHolder;
import net.sf.acegisecurity.context.SecureContext;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
/**
* Intercepts calls to an object and applies security.
*
* <p>
* A method is treated as public unless it has one or more configuration
* attributes defined via {@link
* #setMethodDefinitionSource(MethodDefinitionSource)}. If public, no
* authentication will be attempted, which means an unauthenticated {@link
* Authentication} object may be present in the {@link ContextHolder} (if any
* such an unauthenticated <code>Authentication</code> object exists, its
* {@link Authentication#isAuthenticated()} method will return
* <code>false</code> once the <code>SecurityInterceptor</code> has
* intercepted the public method).
* </p>
*
* <p>
* For those methods to be secured by the interceptor, one or more
* configuration attributes must be defined. These attributes are stored as
* {@link ConfigAttribute} objects.
* </p>
*
* <p>
* The presence of a configuration attribute for a given method will force
* authentication to be attempted via the {@link AuthenticationManager}
* configured against the interceptor. If successfully authenticated, the
* configured {@link AccessDecisionManager} will be passed the {@link
* ConfigAttributeDefinition} applicable for the method invocation, the
* method invocation itself, and the <code>Authentication</code> object. The
* <code>AccessDecisionManager</code> which will then make the authorization
* decision.
* </p>
*
* <p>
* There shouldn't be any requirement to customise the behaviour of the
* <code>SecurityInterceptor</code>, as all security decisions are made by the
* <code>AuthenticationProvider</code> and <code>AccessDecisionManager</code>
* interfaces, which can of course be replaced with different concrete
* implementations.
* </p>
*
* @author Ben Alex
* @version $Id$
*/
public class SecurityInterceptor implements MethodInterceptor, InitializingBean {
//~ Static fields/initializers =============================================
private static final Log logger = LogFactory.getLog(SecurityInterceptor.class);
//~ Instance fields ========================================================
private AccessDecisionManager accessDecisionManager;
private AuthenticationManager authenticationManager;
private MethodDefinitionSource methodDefinitionSource;
private RunAsManager runAsManager;
private boolean validateConfigAttributes = true;
//~ Methods ================================================================
public void setAccessDecisionManager(
AccessDecisionManager accessDecisionManager) {
this.accessDecisionManager = accessDecisionManager;
}
public AccessDecisionManager getAccessDecisionManager() {
return accessDecisionManager;
}
public void setAuthenticationManager(AuthenticationManager newManager) {
this.authenticationManager = newManager;
}
public AuthenticationManager getAuthenticationManager() {
return this.authenticationManager;
}
public void setMethodDefinitionSource(MethodDefinitionSource newSource) {
this.methodDefinitionSource = newSource;
}
public MethodDefinitionSource getMethodDefinitionSource() {
return this.methodDefinitionSource;
}
public void setRunAsManager(RunAsManager runAsManager) {
this.runAsManager = runAsManager;
}
public RunAsManager getRunAsManager() {
return runAsManager;
}
public void setValidateConfigAttributes(boolean validateConfigAttributes) {
this.validateConfigAttributes = validateConfigAttributes;
}
public boolean isValidateConfigAttributes() {
return validateConfigAttributes;
}
public void afterPropertiesSet() {
if (this.authenticationManager == null) {
throw new IllegalArgumentException(
"An AuthenticationManager is required");
}
if (this.accessDecisionManager == null) {
throw new IllegalArgumentException(
"An AccessDecisionManager is required");
}
if (this.runAsManager == null) {
throw new IllegalArgumentException("A RunAsManager is required");
}
if (this.methodDefinitionSource == null) {
throw new IllegalArgumentException(
"A MethodDefinitionSource is required");
}
if (this.validateConfigAttributes) {
Iterator iter = this.methodDefinitionSource
.getConfigAttributeDefinitions();
if (iter == null) {
if (logger.isWarnEnabled()) {
logger.warn(
"Could not validate configuration attributes as the MethodDefinitionSource did not return a ConfigAttributeDefinition Iterator");
}
return;
}
Set set = new HashSet();
while (iter.hasNext()) {
ConfigAttributeDefinition def = (ConfigAttributeDefinition) iter
.next();
Iterator attributes = def.getConfigAttributes();
while (attributes.hasNext()) {
ConfigAttribute attr = (ConfigAttribute) attributes.next();
if (!this.runAsManager.supports(attr)
&& !this.accessDecisionManager.supports(attr)) {
set.add(attr);
}
}
}
if (set.size() == 0) {
if (logger.isInfoEnabled()) {
logger.info("Validated configuration attributes");
}
} else {
throw new IllegalArgumentException(
"Unsupported configuration attributes: " + set.toString());
}
}
}
/**
* Does the work of authenticating and authorizing the request. Throws
* {@link AcegiSecurityException} and its subclasses.
*
* @param mi The method being invoked which requires a security decision
*
* @return The returned value from the method invocation
*
* @throws Throwable if any error occurs
* @throws AuthenticationCredentialsNotFoundException if the
* <code>ContextHolder</code> does not contain a valid
* <code>SecureContext</code> which in turn contains an
* <code>Authentication</code> object
*/
public Object invoke(MethodInvocation mi) throws Throwable {
ConfigAttributeDefinition attr = this.methodDefinitionSource
.getAttributes(mi);
if (attr != null) {
if (logger.isDebugEnabled()) {
logger.debug("Secure method configuration "
+ attr.getConfigAttributes().toString());
}
// Ensure ContextHolder presents a populated SecureContext
if ((ContextHolder.getContext() == null)
|| !(ContextHolder.getContext() instanceof SecureContext)) {
throw new AuthenticationCredentialsNotFoundException(
"A valid SecureContext was not provided in the RequestContext");
}
SecureContext context = (SecureContext) ContextHolder.getContext();
if (context.getAuthentication() == null) {
throw new AuthenticationCredentialsNotFoundException(
"Authentication credentials were not found in the SecureContext");
}
// Attempt authentication
Authentication authenticated = this.authenticationManager
.authenticate(context.getAuthentication());
authenticated.setAuthenticated(true);
logger.debug("Authenticated: " + authenticated.toString());
context.setAuthentication(authenticated);
ContextHolder.setContext((Context) context);
// Attempt authorization
this.accessDecisionManager.decide(authenticated, mi, attr);
if (logger.isDebugEnabled()) {
logger.debug("Authorization successful");
}
// Attempt to run as a different user
Authentication runAs = this.runAsManager.buildRunAs(authenticated,
mi, attr);
if (runAs == null) {
if (logger.isDebugEnabled()) {
logger.debug(
"RunAsManager did not change Authentication object");
}
Object ret = mi.proceed();
return ret;
} else {
if (logger.isDebugEnabled()) {
logger.debug("Switching to RunAs Authentication: "
+ runAs.toString());
}
context.setAuthentication(runAs);
ContextHolder.setContext((Context) context);
Object ret = mi.proceed();
if (logger.isDebugEnabled()) {
logger.debug("Reverting to original Authentication: "
+ authenticated.toString());
}
context.setAuthentication(authenticated);
ContextHolder.setContext((Context) context);
return ret;
}
} else {
if (logger.isDebugEnabled()) {
logger.debug("Public method - authentication not attempted");
}
// Set Authentication object (if it exists) to be unauthenticated
if ((ContextHolder.getContext() != null)
&& ContextHolder.getContext() instanceof SecureContext) {
SecureContext context = (SecureContext) ContextHolder
.getContext();
if (context.getAuthentication() != null) {
if (logger.isDebugEnabled()) {
logger.debug(
"Authentication object detected and tagged as unauthenticated");
}
Authentication authenticated = context.getAuthentication();
authenticated.setAuthenticated(false);
context.setAuthentication(authenticated);
ContextHolder.setContext((Context) context);
}
}
Object ret = mi.proceed();
return ret;
}
}
}
@@ -0,0 +1,100 @@
/*
* The Acegi Security System for Spring is published under the terms
* of the Apache Software License.
*
* Visit http://acegisecurity.sourceforge.net for further details.
*/
package net.sf.acegisecurity.adapters;
import net.sf.acegisecurity.GrantedAuthority;
import net.sf.acegisecurity.providers.AbstractAuthenticationToken;
/**
* Convenience superclass for {@link AuthByAdapter} implementations.
*
* @author Ben Alex
* @version $Id$
*/
public abstract class AbstractAdapterAuthenticationToken
extends AbstractAuthenticationToken implements AuthByAdapter {
//~ Instance fields ========================================================
private GrantedAuthority[] authorities;
private int keyHash;
//~ Constructors ===========================================================
protected AbstractAdapterAuthenticationToken() {
super();
}
/**
* The only way an <code>AbstractAdapterAuthentication</code> should be
* constructed.
*
* @param key the key that is hashed and made available via {@link
* #getKeyHash()}
* @param authorities the authorities granted to this principal
*/
protected AbstractAdapterAuthenticationToken(String key,
GrantedAuthority[] authorities) {
super();
this.keyHash = key.hashCode();
this.authorities = authorities;
}
//~ Methods ================================================================
/**
* Setting is ignored. Always considered authenticated.
*
* @param ignored DOCUMENT ME!
*/
public void setAuthenticated(boolean ignored) {
// ignored
}
/**
* Always returns <code>true</code>.
*
* @return DOCUMENT ME!
*/
public boolean isAuthenticated() {
return true;
}
public GrantedAuthority[] getAuthorities() {
return authorities;
}
public int getKeyHash() {
return this.keyHash;
}
/**
* Iterates the granted authorities and indicates whether or not the
* specified role is held.
*
* <p>
* Comparison is based on the <code>String</code> returned by {@link
* GrantedAuthority#getAuthority}.
* </p>
*
* @param role the role being searched for in this object's granted
* authorities list
*
* @return <code>true</code> if the granted authority is held, or
* <code>false</code> otherwise
*/
public boolean isUserInRole(String role) {
for (int i = 0; i < this.authorities.length; i++) {
if (role.equals(this.authorities[i].getAuthority())) {
return true;
}
}
return false;
}
}
@@ -0,0 +1,146 @@
/*
* The Acegi Security System for Spring is published under the terms
* of the Apache Software License.
*
* Visit http://acegisecurity.sourceforge.net for further details.
*/
package net.sf.acegisecurity.adapters;
import net.sf.acegisecurity.Authentication;
import net.sf.acegisecurity.context.Context;
import net.sf.acegisecurity.context.ContextHolder;
import net.sf.acegisecurity.context.SecureContext;
import net.sf.acegisecurity.context.SecureContextImpl;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
/**
* Automatically populates a {@link net.sf.acegisecurity.context.SecureContext}
* from a subclass-provided container source.
*
* <p>
* The container is expected to expose an {@link Authentication} object in a
* well-known location. The <code>Authentication</code> object will have been
* created by the container-specific Acegi Security System for Spring adapter.
* </p>
*
* <P>
* Once the <code>Authentication</code> object has been extracted from the
* well-known location, the interceptor handles putting it into the {@link
* ContextHolder}. It then removes it once the filter chain has completed.
* </p>
*
* <p>
* This interceptor will not operate if the container does not provide an
* <code>Authentication</code> object from its well-known location.
* </p>
*
* @author Ben Alex
* @version $Id$
*/
public abstract class AbstractIntegrationFilter implements Filter {
//~ Static fields/initializers =============================================
protected static final Log logger = LogFactory.getLog(AbstractIntegrationFilter.class);
//~ Methods ================================================================
public void destroy() {}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain)
throws IOException, ServletException {
// Populate authentication information
Object extracted = this.extractFromContainer(request);
if (extracted instanceof Authentication) {
if (logger.isDebugEnabled()) {
logger.debug("Authentication added to ContextHolder from container");
}
Authentication auth = (Authentication) extracted;
// Get or create existing SecureContext
SecureContext secureContext = null;
if ((ContextHolder.getContext() == null)
|| !(ContextHolder.getContext() instanceof SecureContext)) {
secureContext = new SecureContextImpl();
} else {
secureContext = (SecureContext) ContextHolder.getContext();
}
// Add Authentication to SecureContext, and save
secureContext.setAuthentication(auth);
ContextHolder.setContext((Context) secureContext);
} else {
if (logger.isDebugEnabled()) {
logger.debug("Authentication not added to ContextHolder (could not extract an authentication object from the container which is an instance of Authentication)");
}
}
// Proceed with chain
chain.doFilter(request, response);
// Remove authentication information
if ((ContextHolder.getContext() != null)
&& ContextHolder.getContext() instanceof SecureContext) {
if (logger.isDebugEnabled()) {
logger.debug("Removing Authentication from ContextHolder");
}
// Get context holder and remove authentication information
SecureContext secureContext = (SecureContext) ContextHolder
.getContext();
secureContext.setAuthentication(null);
ContextHolder.setContext((Context) secureContext);
} else {
if (logger.isDebugEnabled()) {
logger.debug("ContextHolder does not contain any authentication information");
}
}
}
/**
* Subclasses must override this method to provide the <code>Object</code>
* that contains the <code>Authentication</code> interface.
*
* <p>
* For convenience we have allowed any <code>Object</code> to be returned
* by subclasses, as the abstract class will ensure class casting safety
* and ignore objects that do not implement <code>Authentication</code>.
* </p>
*
* <p>
* If no authentication object is available, subclasses should return
* <code>null</code>.
* </p>
*
* <p>
* If the container can locate multiple authentication objects, subclasses
* should return the object that was created by the Acegi Security System
* for Spring adapter (ie that implements <code>Authentication</code>).
* </p>
*
* @param request the request, which may be of use in extracting the
* authentication object
*
* @return <code>null</code> or an object that implements
* <code>Authentication</code>
*/
public abstract Object extractFromContainer(ServletRequest request);
public void init(FilterConfig filterConfig) throws ServletException {}
}
@@ -0,0 +1,35 @@
/*
* The Acegi Security System for Spring is published under the terms
* of the Apache Software License.
*
* Visit http://acegisecurity.sourceforge.net for further details.
*/
package net.sf.acegisecurity.adapters;
import net.sf.acegisecurity.Authentication;
/**
* Indicates a specialized, immutable, server-side only {@link Authentication}
* class.
*
* <P>
* Automatically considered valid by the {@link AuthByAdapterProvider},
* provided the hash code presented by the implementation objects matches that
* expected by the <code>AuthByAdapterProvider</code>.
* </p>
*
* @author Ben Alex
* @version $Id$
*/
public interface AuthByAdapter extends Authentication {
//~ Methods ================================================================
/**
* DOCUMENT ME!
*
* @return the hash code of the key used when the object was created.
*/
public int getKeyHash();
}
@@ -0,0 +1,77 @@
/*
* The Acegi Security System for Spring is published under the terms
* of the Apache Software License.
*
* Visit http://acegisecurity.sourceforge.net for further details.
*/
package net.sf.acegisecurity.adapters;
import net.sf.acegisecurity.Authentication;
import net.sf.acegisecurity.AuthenticationException;
import net.sf.acegisecurity.BadCredentialsException;
import net.sf.acegisecurity.providers.AuthenticationProvider;
import org.springframework.beans.factory.InitializingBean;
/**
* An {@link AuthenticationProvider} implementation that can authenticate an
* {@link AuthByAdapter}.
*
* <P>
* Configured in the bean context with a key that should match the key used by
* adapters to generate <code>AuthByAdapter</code> instances. It treats as
* valid any such instance presenting a hash code that matches the
* <code>AuthByAdapterProvider</code>-configured key.
* </p>
*
* <P>
* If the key does not match, a <code>BadCredentialsException</code> is
* thrown.
* </p>
*
* @author Ben Alex
* @version $Id$
*/
public class AuthByAdapterProvider implements InitializingBean,
AuthenticationProvider {
//~ Instance fields ========================================================
private String key;
//~ Methods ================================================================
public void setKey(String key) {
this.key = key;
}
public String getKey() {
return key;
}
public void afterPropertiesSet() throws Exception {
if (key == null) {
throw new IllegalArgumentException("A Key is required and should match that configured for the adapters");
}
}
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
AuthByAdapter token = (AuthByAdapter) authentication;
if (token.getKeyHash() == key.hashCode()) {
return authentication;
} else {
throw new BadCredentialsException("The presented AuthByAdapter implementation does not contain the expected key");
}
}
public boolean supports(Class authentication) {
if (AuthByAdapter.class.isAssignableFrom(authentication)) {
return true;
} else {
return false;
}
}
}
@@ -0,0 +1,54 @@
/*
* The Acegi Security System for Spring is published under the terms
* of the Apache Software License.
*
* Visit http://acegisecurity.sourceforge.net for further details.
*/
package net.sf.acegisecurity.adapters;
import net.sf.acegisecurity.Authentication;
import net.sf.acegisecurity.adapters.jboss.JbossIntegrationFilter;
import org.jboss.security.SimplePrincipal;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
/**
* Detects the container and delegates to the appropriate {@link
* AbstractIntegrationFilter}.
*
* <p>
* This eases the creation of portable secured Spring applications, as the
* <code>web.xml</code> will not need to refer to a specific container
* integration filter.
* </p>
*
* <p>
* See {@link AbstractIntegrationFilter} for further information.
* </p>
*
* @author Ben Alex
* @version $Id$
*/
public class AutoIntegrationFilter extends AbstractIntegrationFilter {
//~ Methods ================================================================
public Object extractFromContainer(ServletRequest request) {
if (request instanceof HttpServletRequest) {
HttpServletRequest httpRequest = (HttpServletRequest) request;
if (httpRequest.getUserPrincipal() instanceof Authentication) {
return new HttpRequestIntegrationFilter().extractFromContainer(request);
}
if (httpRequest.getUserPrincipal() instanceof SimplePrincipal) {
return new JbossIntegrationFilter().extractFromContainer(request);
}
}
return null;
}
}
@@ -0,0 +1,42 @@
/*
* The Acegi Security System for Spring is published under the terms
* of the Apache Software License.
*
* Visit http://acegisecurity.sourceforge.net for further details.
*/
package net.sf.acegisecurity.adapters;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
/**
* Populates a {@link net.sf.acegisecurity.context.SecureContext} from the
* container's <code>HttpServletRequest.getUserPrincipal()</code>.
*
* <p>
* See {@link AbstractIntegrationFilter} for further information.
* </p>
*
* @author Ben Alex
* @version $Id$
*/
public class HttpRequestIntegrationFilter extends AbstractIntegrationFilter {
//~ Static fields/initializers =============================================
private static final Log logger = LogFactory.getLog(HttpRequestIntegrationFilter.class);
//~ Methods ================================================================
public Object extractFromContainer(ServletRequest request) {
if (request instanceof HttpServletRequest) {
return ((HttpServletRequest) request).getUserPrincipal();
} else {
return null;
}
}
}
@@ -0,0 +1,55 @@
/*
* The Acegi Security System for Spring is published under the terms
* of the Apache Software License.
*
* Visit http://acegisecurity.sourceforge.net for further details.
*/
package net.sf.acegisecurity.adapters;
import net.sf.acegisecurity.GrantedAuthority;
import java.security.Principal;
/**
* A {@link Principal} compatible {@link net.sf.acegisecurity.Authentication}
* object.
*
* @author Ben Alex
* @version $Id$
*/
public class PrincipalAcegiUserToken extends AbstractAdapterAuthenticationToken
implements Principal {
//~ Instance fields ========================================================
private String password;
private String username;
//~ Constructors ===========================================================
public PrincipalAcegiUserToken(String key, String username,
String password, GrantedAuthority[] authorities) {
super(key, authorities);
this.username = username;
this.password = password;
}
private PrincipalAcegiUserToken() {
super();
}
//~ Methods ================================================================
public Object getCredentials() {
return this.password;
}
public String getName() {
return this.username;
}
public Object getPrincipal() {
return this.username;
}
}
@@ -0,0 +1,8 @@
<html>
<body>
Provides "adapters" so that containers can authenticate with the
Acegi Security System for Spring.
<p>
</body>
</html>
@@ -0,0 +1,38 @@
/*
* The Acegi Security System for Spring is published under the terms
* of the Apache Software License.
*
* Visit http://acegisecurity.sourceforge.net for further details.
*/
package net.sf.acegisecurity.context;
import java.io.Serializable;
/**
* Holds objects that are needed on every request.
*
* <P>
* A <code>Context</code> will be sent between application tiers via a {@link
* ContextHolder}.
* </p>
*
* @author Ben Alex
* @version $Id$
*/
public interface Context extends Serializable {
//~ Methods ================================================================
/**
* Check the <code>Context</code> is properly configured.
*
* <P>
* This allows implementations to confirm they are valid, as this method
* is automatically called by the {@link ContextInterceptor}.
* </p>
*
* @throws ContextInvalidException if the <code>Context</code> is invalid.
*/
public void validate() throws ContextInvalidException;
}
@@ -0,0 +1,47 @@
/*
* The Acegi Security System for Spring is published under the terms
* of the Apache Software License.
*
* Visit http://acegisecurity.sourceforge.net for further details.
*/
package net.sf.acegisecurity.context;
import org.springframework.core.NestedRuntimeException;
/**
* Abstract superclass for all exceptions thrown in the context package and
* subpackages.
*
* <p>
* Note that this is a runtime (unchecked) exception.
* </p>
*
* @author Ben Alex
* @version $Id$
*/
public abstract class ContextException extends NestedRuntimeException {
//~ Constructors ===========================================================
/**
* Constructs a <code>ContextException</code> with the specified message
* and root cause.
*
* @param msg the detail message
* @param t the root cause
*/
public ContextException(String msg, Throwable t) {
super(msg, t);
}
/**
* Constructs a <code>ContextException</code> with the specified message
* and no root cause.
*
* @param msg the detail message
*/
public ContextException(String msg) {
super(msg);
}
}
@@ -0,0 +1,30 @@
/*
* The Acegi Security System for Spring is published under the terms
* of the Apache Software License.
*
* Visit http://acegisecurity.sourceforge.net for further details.
*/
package net.sf.acegisecurity.context;
/**
* Associates a given {@link Context} with the current execution thread.
*
* @author Ben Alex
* @version $Id$
*/
public class ContextHolder {
//~ Static fields/initializers =============================================
private static ThreadLocal contextHolder = new ThreadLocal();
//~ Methods ================================================================
public static void setContext(Context context) {
contextHolder.set(context);
}
public static Context getContext() {
return (Context) contextHolder.get();
}
}
@@ -0,0 +1,40 @@
/*
* The Acegi Security System for Spring is published under the terms
* of the Apache Software License.
*
* Visit http://acegisecurity.sourceforge.net for further details.
*/
package net.sf.acegisecurity.context;
/**
* Thrown if a {@link ContextHolder} object does not contain a valid {@link
* Context}.
*
* @author Ben Alex
* @version $Id$
*/
public class ContextHolderEmptyException extends ContextException {
//~ Constructors ===========================================================
/**
* Constructs a <code>ContextHolderEmptyException</code> with the specified
* message.
*
* @param msg the detail message
*/
public ContextHolderEmptyException(String msg) {
super(msg);
}
/**
* Constructs a <code>ContextHolderEmptyException</code> with the specified
* message and root cause.
*
* @param msg the detail message
* @param t root cause
*/
public ContextHolderEmptyException(String msg, Throwable t) {
super(msg, t);
}
}
@@ -0,0 +1,22 @@
/*
* The Acegi Security System for Spring is published under the terms
* of the Apache Software License.
*
* Visit http://acegisecurity.sourceforge.net for further details.
*/
package net.sf.acegisecurity.context;
/**
* Basic concrete implementation of a {@link Context}.
*
* @author Ben Alex
* @version $Id$
*/
public class ContextImpl implements Context {
//~ Methods ================================================================
public void validate() throws ContextInvalidException {
// Nothing to validate.
}
}
@@ -0,0 +1,50 @@
/*
* The Acegi Security System for Spring is published under the terms
* of the Apache Software License.
*
* Visit http://acegisecurity.sourceforge.net for further details.
*/
package net.sf.acegisecurity.context;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Ensures the {@link ContextHolder} contains a valid {@link Context}.
*
* <p>
* This interceptor works by calling {@link Context#validate()} before
* proceeding with method invocations. It is configured in the bean context
* with a <code>ProxyFactoryBean</code>.
* </p>
*
* @author Ben Alex
* @version $Id$
*
* @see Context#validate()
*/
public class ContextInterceptor implements MethodInterceptor {
//~ Static fields/initializers =============================================
private static final Log logger = LogFactory.getLog(ContextInterceptor.class);
//~ Methods ================================================================
public Object invoke(MethodInvocation mi) throws Throwable {
if (ContextHolder.getContext() == null) {
throw new ContextHolderEmptyException("ContextHolder does not contain a Context",
null);
}
ContextHolder.getContext().validate();
Object ret = mi.proceed();
return ret;
}
}
@@ -0,0 +1,42 @@
/*
* The Acegi Security System for Spring is published under the terms
* of the Apache Software License.
*
* Visit http://acegisecurity.sourceforge.net for further details.
*/
package net.sf.acegisecurity.context;
/**
* Thrown if a {@link Context} is not valid, according to {@link
* Context#validate()}.
*
* @author Ben Alex
* @version $Id$
*
* @see Context#validate()
*/
public class ContextInvalidException extends ContextException {
//~ Constructors ===========================================================
/**
* Constructs a <code>ContextInvalidException</code> with the specified
* message.
*
* @param msg the detail message.
*/
public ContextInvalidException(String msg) {
super(msg);
}
/**
* Constructs a <code>ContextInvalidException</code> with the specified
* message and root cause.
*
* @param msg the detail message.
* @param t root cause
*/
public ContextInvalidException(String msg, Throwable t) {
super(msg, t);
}
}
@@ -0,0 +1,31 @@
/*
* The Acegi Security System for Spring is published under the terms
* of the Apache Software License.
*
* Visit http://acegisecurity.sourceforge.net for further details.
*/
package net.sf.acegisecurity.context;
import net.sf.acegisecurity.Authentication;
/**
* A {@link Context} that also stores {@link Authentication} information.
*
* <p>
* This interface must be implemented on contexts that will be presented to the
* Acegi Security System for Spring, as it is required by the {@link
* net.sf.acegisecurity.SecurityInterceptor}.
* </p>
*
* @author Ben Alex
* @version $Id$
*/
public interface SecureContext {
//~ Methods ================================================================
public void setAuthentication(Authentication newAuthentication);
public Authentication getAuthentication();
}
@@ -0,0 +1,41 @@
/*
* The Acegi Security System for Spring is published under the terms
* of the Apache Software License.
*
* Visit http://acegisecurity.sourceforge.net for further details.
*/
package net.sf.acegisecurity.context;
import net.sf.acegisecurity.Authentication;
/**
* Basic concrete implementation of a {@link SecureContext}.
*
* @author Ben Alex
* @version $Id$
*/
public class SecureContextImpl extends ContextImpl implements SecureContext {
//~ Instance fields ========================================================
private Authentication authentication;
//~ Methods ================================================================
public void setAuthentication(Authentication newAuthentication) {
this.authentication = newAuthentication;
}
public Authentication getAuthentication() {
return this.authentication;
}
public void validate() throws ContextInvalidException {
super.validate();
if (authentication == null) {
throw new ContextInvalidException("Authentication not set");
}
}
}
@@ -0,0 +1,10 @@
<html>
<body>
Provides a "request context".
<p>
A request context is associated with the current execution thread. It holds
objects that would otherwise need to be included in many method signatures,
such as for authentication.
</body>
</html>
@@ -0,0 +1,21 @@
<html>
<body>
Provides core security classes of the Acegi Security System for Spring.
<p>
The {@link net.sf.acegisecurity.SecurityInterceptor} is the main class.
It delegates to two interfaces,
{@link net.sf.acegisecurity.AuthenticationManager} and
{@link net.sf.acegisecurity.AccessDecisionManager} for authentication and
authorization respectively.
<p>
When configuring <code>SecurityInterceptor</code> in the bean context, each
method to be secured is provided a comma separated list of configuration
attributes ({@link net.sf.acegisecurity.ConfigAttribute}).
These configuration attributes are relevant only to
<Code>AccessDecisionManager</code>s.
<p>
Read the JavaDocs of the key classes listed above to learn more about how
the security classes operate.
</body>
</html>
@@ -0,0 +1,45 @@
/*
* The Acegi Security System for Spring is published under the terms
* of the Apache Software License.
*
* Visit http://acegisecurity.sourceforge.net for further details.
*/
package net.sf.acegisecurity.providers;
import net.sf.acegisecurity.Authentication;
/**
* Provides a <code>String</code> representation of the Authentication token.
*
* @author Ben Alex
* @version $Id$
*/
public abstract class AbstractAuthenticationToken implements Authentication {
//~ Methods ================================================================
public String toString() {
StringBuffer sb = new StringBuffer();
sb.append(super.toString() + ": ");
sb.append("Username: " + this.getPrincipal() + "; ");
sb.append("Password: [PROTECTED]; ");
sb.append("Authenticated: " + this.isAuthenticated() + "; ");
if (this.getAuthorities() != null) {
sb.append("Granted Authorities: ");
for (int i = 0; i < this.getAuthorities().length; i++) {
if (i > 0) {
sb.append(", ");
}
sb.append(this.getAuthorities()[i].toString());
}
} else {
sb.append("Not granted any authorities");
}
return sb.toString();
}
}
@@ -0,0 +1,50 @@
/*
* The Acegi Security System for Spring is published under the terms
* of the Apache Software License.
*
* Visit http://acegisecurity.sourceforge.net for further details.
*/
package net.sf.acegisecurity.providers;
import net.sf.acegisecurity.Authentication;
import net.sf.acegisecurity.AuthenticationException;
/**
* Indicates a class can process a specific {@link
* net.sf.acegisecurity.Authentication} implementation.
*
* @author Ben Alex
* @version $Id$
*/
public interface AuthenticationProvider {
//~ Methods ================================================================
/**
* Performs authentication with the same contract as {@link
* net.sf.acegisecurity.AuthenticationManager#authenticate(Authentication)}.
*
* @param authentication the authentication request object.
*
* @return a fully authenticated object including credentials.
*
* @throws AuthenticationException if authentication fails.
*/
public Authentication authenticate(Authentication authentication)
throws AuthenticationException;
/**
* Returns true if this <Code>AuthenticationProvider</code> supports the
* indicated <Code>Authentication</code> object.
*
* <P>
* Selection of an <code>AuthenticationProvider</code> capable of
* performing authentication is conducted at runtime the
* <code>ProviderManager</code>.
* </p>
*
* @return DOCUMENT ME!
*/
public boolean supports(Class authentication);
}
@@ -0,0 +1,132 @@
/*
* The Acegi Security System for Spring is published under the terms
* of the Apache Software License.
*
* Visit http://acegisecurity.sourceforge.net for further details.
*/
package net.sf.acegisecurity.providers;
import net.sf.acegisecurity.Authentication;
import net.sf.acegisecurity.AuthenticationException;
import net.sf.acegisecurity.AuthenticationManager;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;
import java.util.Iterator;
import java.util.List;
/**
* Iterates an {@link Authentication} request through a list of {@link
* AuthenticationProvider}s.
*
* @author Ben Alex
* @version $Id$
*/
public class ProviderManager implements InitializingBean, AuthenticationManager {
//~ Static fields/initializers =============================================
private static final Log logger = LogFactory.getLog(ProviderManager.class);
//~ Instance fields ========================================================
private List providers;
//~ Methods ================================================================
/**
* Sets the {@link AuthenticationProvider} objects to be used for
* authentication.
*
* @param newList
*
* @throws IllegalArgumentException DOCUMENT ME!
*/
public void setProviders(List newList) {
checkIfValidList(newList);
Iterator iter = newList.iterator();
while (iter.hasNext()) {
Object currentObject = null;
try {
currentObject = iter.next();
AuthenticationProvider attemptToCast = (AuthenticationProvider) currentObject;
} catch (ClassCastException cce) {
throw new IllegalArgumentException("AuthenticationProvider "
+ currentObject.getClass()
.getName()
+ " must implement AuthenticationProvider");
}
}
this.providers = newList;
}
public List getProviders() {
return this.providers;
}
public void afterPropertiesSet() throws Exception {
checkIfValidList(this.providers);
}
/**
* Attempts to authenticate the passed {@link Authentication} object.
*
* <p>
* The list of {@link AuthenticationProvider}s will be successively tried
* until an <code>AuthenticationProvider</code> indicates it is capable
* of authenticating the type of <code>Authentication</code> object
* passed. Authentication will then be attempted with that
* <code>AuthenticationProvider</code>.
* </p>
*
* <p>
* If more than one <code>AuthenticationProvider</code> supports the passed
* <code>Authentication</code> object, only the first
* <code>AuthenticationProvider</code> tried will determine the result. No
* subsequent <code>AuthenticationProvider</code>s will be tried.
* </p>
*
* @param authentication the authentication request object.
*
* @return a fully authenticated object including credentials.
*
* @throws AuthenticationException if authentication fails.
* @throws ProviderNotFoundException DOCUMENT ME!
*/
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
Iterator iter = providers.iterator();
Class toTest = authentication.getClass();
while (iter.hasNext()) {
AuthenticationProvider provider = (AuthenticationProvider) iter
.next();
if (provider.supports(toTest)) {
logger.debug("Authentication attempt using "
+ provider.getClass().getName());
return provider.authenticate(authentication);
}
}
throw new ProviderNotFoundException("No authentication provider for "
+ toTest.getName());
}
private void checkIfValidList(List listToCheck) {
if ((listToCheck == null) || (listToCheck.size() == 0)) {
throw new IllegalArgumentException("A list of AuthenticationManagers is required");
}
}
}
@@ -0,0 +1,44 @@
/*
* The Acegi Security System for Spring is published under the terms
* of the Apache Software License.
*
* Visit http://acegisecurity.sourceforge.net for further details.
*/
package net.sf.acegisecurity.providers;
import net.sf.acegisecurity.AuthenticationException;
/**
* Thrown by {@link ProviderManager} if no {@link AuthenticationProvider}
* could be found that supports the presented {@link
* net.sf.acegisecurity.Authentication} object.
*
* @author Ben Alex
* @version $Id$
*/
public class ProviderNotFoundException extends AuthenticationException {
//~ Constructors ===========================================================
/**
* Constructs a <code>ProviderNotFoundException</code> with the specified
* message.
*
* @param msg the detail message
*/
public ProviderNotFoundException(String msg) {
super(msg);
}
/**
* Constructs a <code>ProviderNotFoundException</code> with the specified
* message and root cause.
*
* @param msg the detail message
* @param t root cause
*/
public ProviderNotFoundException(String msg, Throwable t) {
super(msg, t);
}
}
@@ -0,0 +1,46 @@
/*
* The Acegi Security System for Spring is published under the terms
* of the Apache Software License.
*
* Visit http://acegisecurity.sourceforge.net for further details.
*/
package net.sf.acegisecurity.providers;
import net.sf.acegisecurity.Authentication;
import net.sf.acegisecurity.AuthenticationException;
/**
* An {@link AuthenticationProvider} implementation for the {@link
* TestingAuthenticationToken}.
*
* <p>
* It simply accepts as valid whatever is contained within the
* <code>TestingAuthenticationToken</code>.
* </p>
*
* <p>
* The purpose of this implementation is to facilitate unit testing. This
* provider should <B>never be enabled on a production system</b>.
* </p>
*
* @author Ben Alex
* @version $Id$
*/
public class TestingAuthenticationProvider implements AuthenticationProvider {
//~ Methods ================================================================
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
return authentication;
}
public boolean supports(Class authentication) {
if (TestingAuthenticationToken.class.isAssignableFrom(authentication)) {
return true;
} else {
return false;
}
}
}
@@ -0,0 +1,67 @@
/*
* The Acegi Security System for Spring is published under the terms
* of the Apache Software License.
*
* Visit http://acegisecurity.sourceforge.net for further details.
*/
package net.sf.acegisecurity.providers;
import net.sf.acegisecurity.GrantedAuthority;
/**
* An {@link net.sf.acegisecurity.Authentication} implementation that is
* designed for use whilst unit testing.
*
* <p>
* The corresponding authentication provider is {@link
* TestingAuthenticationProvider}.
* </p>
*
* @author Ben Alex
* @version $Id$
*/
public class TestingAuthenticationToken extends AbstractAuthenticationToken {
//~ Instance fields ========================================================
private Object credentials;
private Object principal;
private GrantedAuthority[] authorities;
private boolean authenticated = false;
//~ Constructors ===========================================================
public TestingAuthenticationToken(Object principal, Object credentials,
GrantedAuthority[] authorities) {
this.principal = principal;
this.credentials = credentials;
this.authorities = authorities;
}
private TestingAuthenticationToken() {
super();
}
//~ Methods ================================================================
public void setAuthenticated(boolean isAuthenticated) {
this.authenticated = isAuthenticated;
}
public boolean isAuthenticated() {
return this.authenticated;
}
public GrantedAuthority[] getAuthorities() {
return this.authorities;
}
public Object getCredentials() {
return this.credentials;
}
public Object getPrincipal() {
return this.principal;
}
}
@@ -0,0 +1,81 @@
/*
* The Acegi Security System for Spring is published under the terms
* of the Apache Software License.
*
* Visit http://acegisecurity.sourceforge.net for further details.
*/
package net.sf.acegisecurity.providers;
import net.sf.acegisecurity.GrantedAuthority;
/**
* An {@link net.sf.acegisecurity.Authentication} implementation that is
* designed for simple presentation of a username and password.
*
* <p>
* The <code>principal</code> and <code>credentials</code> should be set with
* an <code>Object</code> that provides the respective property via its
* <code>Object.toString()</code> method. The simplest such
* <code>Object</code> to use is <code>String</code>.
* </p>
*
* @author Ben Alex
* @version $Id$
*/
public class UsernamePasswordAuthenticationToken
extends AbstractAuthenticationToken {
//~ Instance fields ========================================================
private Object credentials;
private Object principal;
private GrantedAuthority[] authorities;
private boolean authenticated = false;
//~ Constructors ===========================================================
public UsernamePasswordAuthenticationToken(Object principal,
Object credentials) {
this.principal = principal;
this.credentials = credentials;
}
public UsernamePasswordAuthenticationToken(Object principal,
Object credentials,
GrantedAuthority[] authorities) {
this.principal = principal;
this.credentials = credentials;
this.authorities = authorities;
}
private UsernamePasswordAuthenticationToken() {
super();
}
//~ Methods ================================================================
public void setAuthenticated(boolean isAuthenticated) {
this.authenticated = isAuthenticated;
}
public boolean isAuthenticated() {
return this.authenticated;
}
public void setAuthorities(GrantedAuthority[] authorities) {
this.authorities = authorities;
}
public GrantedAuthority[] getAuthorities() {
return this.authorities;
}
public Object getCredentials() {
return this.credentials;
}
public Object getPrincipal() {
return this.principal;
}
}
@@ -0,0 +1,124 @@
/*
* The Acegi Security System for Spring is published under the terms
* of the Apache Software License.
*
* Visit http://acegisecurity.sourceforge.net for further details.
*/
package net.sf.acegisecurity.providers.dao;
import net.sf.acegisecurity.Authentication;
import net.sf.acegisecurity.AuthenticationException;
import net.sf.acegisecurity.AuthenticationServiceException;
import net.sf.acegisecurity.BadCredentialsException;
import net.sf.acegisecurity.DisabledException;
import net.sf.acegisecurity.providers.AuthenticationProvider;
import net.sf.acegisecurity.providers.UsernamePasswordAuthenticationToken;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.dao.DataAccessException;
/**
* An {@link AuthenticationProvider} implementation that retrieves user details
* from an {@link AuthenticationDao}.
*
* <p>
* This <code>AuthenticationProvider</code> is capable of validating {@link
* UsernamePasswordAuthenticationToken} requests contain the correct username,
* password and the user is not disabled.
* </p>
*
* @author Ben Alex
* @version $Id$
*/
public class DaoAuthenticationProvider implements AuthenticationProvider,
InitializingBean {
//~ Instance fields ========================================================
private AuthenticationDao authenticationDao;
private boolean ignorePasswordCase = false;
private boolean ignoreUsernameCase = true;
//~ Methods ================================================================
public void setAuthenticationDao(AuthenticationDao authenticationDao) {
this.authenticationDao = authenticationDao;
}
public AuthenticationDao getAuthenticationDao() {
return authenticationDao;
}
/**
* Indicates whether the password comparison is case sensitive. Defaults to
* <code>false</code>, meaning an exact case match is required.
*
* @param ignorePasswordCase set to <code>true</code> for less stringent
* comparison
*/
public void setIgnorePasswordCase(boolean ignorePasswordCase) {
this.ignorePasswordCase = ignorePasswordCase;
}
public boolean isIgnorePasswordCase() {
return ignorePasswordCase;
}
/**
* Indicates whether the username search is case sensitive. Default to
* <code>true</code>, meaning an exact case match is not required.
*
* @param ignoreUsernameCase set to <code>false</code> for more stringent
* comparison
*/
public void setIgnoreUsernameCase(boolean ignoreUsernameCase) {
this.ignoreUsernameCase = ignoreUsernameCase;
}
public boolean isIgnoreUsernameCase() {
return ignoreUsernameCase;
}
public void afterPropertiesSet() throws Exception {
if (this.authenticationDao == null) {
throw new IllegalArgumentException("An Authentication DAO must be set");
}
}
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
User user = null;
try {
user = this.authenticationDao.loadUserByUsername(authentication.getPrincipal()
.toString());
} catch (UsernameNotFoundException notFound) {
throw new BadCredentialsException("Bad credentials presented");
} catch (DataAccessException repositoryProblem) {
throw new AuthenticationServiceException(repositoryProblem
.getMessage());
}
if (!user.isEnabled()) {
throw new DisabledException("User is disabled");
}
if (!user.getPassword().equals(authentication.getCredentials().toString())) {
throw new BadCredentialsException("Bad credentials presented");
}
return new UsernamePasswordAuthenticationToken(user.getUsername(),
user.getPassword(),
user.getAuthorities());
}
public boolean supports(Class authentication) {
if (UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication)) {
return true;
} else {
return false;
}
}
}
@@ -0,0 +1,43 @@
/*
* The Acegi Security System for Spring is published under the terms
* of the Apache Software License.
*
* Visit http://acegisecurity.sourceforge.net for further details.
*/
package net.sf.acegisecurity.providers.dao;
import net.sf.acegisecurity.AuthenticationException;
/**
* Thrown if an {@link AuthenticationDao} implementation cannot locate a {@link
* User} by its username.
*
* @author Ben Alex
* @version $Id$
*/
public class UsernameNotFoundException extends AuthenticationException {
//~ Constructors ===========================================================
/**
* Constructs a <code>UsernameNotFoundException</code> with the specified
* message.
*
* @param msg the detail message.
*/
public UsernameNotFoundException(String msg) {
super(msg);
}
/**
* Constructs a <code>UsernameNotFoundException</code> with the specified
* message and root cause.
*
* @param msg the detail message.
* @param t root cause
*/
public UsernameNotFoundException(String msg, Throwable t) {
super(msg, t);
}
}
@@ -0,0 +1,5 @@
<html>
<body>
An authentication provider that relies upon a data access object.
</body>
</html>
@@ -0,0 +1,7 @@
<html>
<body>
Implements a provider-based approach to authorization decisions.
<p>
</body>
</html>
@@ -0,0 +1,77 @@
/*
* The Acegi Security System for Spring is published under the terms
* of the Apache Software License.
*
* Visit http://acegisecurity.sourceforge.net for further details.
*/
package net.sf.acegisecurity.runas;
import net.sf.acegisecurity.Authentication;
import net.sf.acegisecurity.AuthenticationException;
import net.sf.acegisecurity.BadCredentialsException;
import net.sf.acegisecurity.providers.AuthenticationProvider;
import org.springframework.beans.factory.InitializingBean;
/**
* An {@link AuthenticationProvider} implementation that can authenticate a
* {@link RunAsUserToken}.
*
* <P>
* Configured in the bean context with a key that should match the key used by
* adapters to generate the <code>RunAsUserToken</code>. It treats as valid
* any <code>RunAsUserToken</code> instance presenting a hash code that
* matches the <code>RunAsImplAuthenticationProvider</code>-configured key.
* </p>
*
* <P>
* If the key does not match, a <code>BadCredentialsException</code> is
* thrown.
* </p>
*
* @author Ben Alex
* @version $Id$
*/
public class RunAsImplAuthenticationProvider implements InitializingBean,
AuthenticationProvider {
//~ Instance fields ========================================================
private String key;
//~ Methods ================================================================
public void setKey(String key) {
this.key = key;
}
public String getKey() {
return key;
}
public void afterPropertiesSet() throws Exception {
if (key == null) {
throw new IllegalArgumentException("A Key is required and should match that configured for the RunAsManagerImpl");
}
}
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
RunAsUserToken token = (RunAsUserToken) authentication;
if (token.getKeyHash() == key.hashCode()) {
return authentication;
} else {
throw new BadCredentialsException("The presented RunAsUserToken does not contain the expected key");
}
}
public boolean supports(Class authentication) {
if (RunAsUserToken.class.isAssignableFrom(authentication)) {
return true;
} else {
return false;
}
}
}
@@ -0,0 +1,108 @@
/*
* The Acegi Security System for Spring is published under the terms
* of the Apache Software License.
*
* Visit http://acegisecurity.sourceforge.net for further details.
*/
package net.sf.acegisecurity.runas;
import net.sf.acegisecurity.Authentication;
import net.sf.acegisecurity.ConfigAttribute;
import net.sf.acegisecurity.ConfigAttributeDefinition;
import net.sf.acegisecurity.GrantedAuthority;
import net.sf.acegisecurity.GrantedAuthorityImpl;
import net.sf.acegisecurity.RunAsManager;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.beans.factory.InitializingBean;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
/**
* Basic concrete implementation of a {@link RunAsManager}.
*
* <p>
* Is activated if any {@link ConfigAttribute#getAttribute()} is prefixed with
* <Code>RUN_AS_</code>. If found, it generates a new {@link RunAsUserToken}
* containing the same principal, credentials and granted authorities as the
* original {@link Authentication} object, along with {@link
* GrantedAuthorityImpl}s for each <code>RUN_AS_</code> indicated. The created
* <code>GrantedAuthorityImpl</code>s will be prefixed with <code>ROLE_</code>
* and then the remainder of the <code>RUN_AS_</code> keyword. For example,
* <code>RUN_AS_FOO</code> will result in the creation of a granted authority
* of <code>ROLE_RUN_AS_FOO</code>.
* </p>
*
* @author Ben Alex
* @version $Id$
*/
public class RunAsManagerImpl implements RunAsManager, InitializingBean {
//~ Instance fields ========================================================
private String key;
//~ Methods ================================================================
public void setKey(String key) {
this.key = key;
}
public String getKey() {
return key;
}
public void afterPropertiesSet() throws Exception {
if (key == null) {
throw new IllegalArgumentException("A Key is required and should match that configured for the RunAsImplAuthenticationProvider");
}
}
public Authentication buildRunAs(Authentication authentication,
MethodInvocation invocation,
ConfigAttributeDefinition config) {
Set newAuthorities = new HashSet();
Iterator iter = config.getConfigAttributes();
while (iter.hasNext()) {
ConfigAttribute attribute = (ConfigAttribute) iter.next();
if (this.supports(attribute)) {
GrantedAuthorityImpl extraAuthority = new GrantedAuthorityImpl("ROLE_"
+ attribute
.getAttribute());
newAuthorities.add(extraAuthority);
}
}
if (newAuthorities.size() == 0) {
return null;
} else {
for (int i = 0; i < authentication.getAuthorities().length; i++) {
newAuthorities.add(authentication.getAuthorities()[i]);
}
GrantedAuthority[] resultType = {new GrantedAuthorityImpl("holder")};
GrantedAuthority[] newAuthoritiesAsArray = (GrantedAuthority[]) newAuthorities
.toArray(resultType);
return new RunAsUserToken(this.key, authentication.getPrincipal(),
authentication.getCredentials(),
newAuthoritiesAsArray,
authentication.getClass());
}
}
public boolean supports(ConfigAttribute attribute) {
if ((attribute.getAttribute() != null)
&& attribute.getAttribute().startsWith("RUN_AS_")) {
return true;
} else {
return false;
}
}
}
@@ -0,0 +1,93 @@
/*
* The Acegi Security System for Spring is published under the terms
* of the Apache Software License.
*
* Visit http://acegisecurity.sourceforge.net for further details.
*/
package net.sf.acegisecurity.runas;
import net.sf.acegisecurity.GrantedAuthority;
import net.sf.acegisecurity.providers.AbstractAuthenticationToken;
/**
* An immutable {@link net.sf.acegisecurity.Authentication} implementation
* that supports {@link RunAsManagerImpl}.
*
* @author Ben Alex
* @version $Id$
*/
public class RunAsUserToken extends AbstractAuthenticationToken {
//~ Instance fields ========================================================
private Class originalAuthentication;
private Object credentials;
private Object principal;
private GrantedAuthority[] authorities;
private int keyHash;
//~ Constructors ===========================================================
public RunAsUserToken(String key, Object principal, Object credentials,
GrantedAuthority[] authorities,
Class originalAuthentication) {
super();
this.keyHash = key.hashCode();
this.authorities = authorities;
this.principal = principal;
this.credentials = credentials;
this.originalAuthentication = originalAuthentication;
}
private RunAsUserToken() {
super();
}
//~ Methods ================================================================
/**
* Setting is ignored. Always considered authenticated.
*
* @param ignored DOCUMENT ME!
*/
public void setAuthenticated(boolean ignored) {
// ignored
}
/**
* Always returns <code>true</code>.
*
* @return DOCUMENT ME!
*/
public boolean isAuthenticated() {
return true;
}
public GrantedAuthority[] getAuthorities() {
return this.authorities;
}
public Object getCredentials() {
return this.credentials;
}
public int getKeyHash() {
return this.keyHash;
}
public Class getOriginalAuthentication() {
return this.originalAuthentication;
}
public Object getPrincipal() {
return this.principal;
}
public String toString() {
StringBuffer sb = new StringBuffer(super.toString());
sb.append("; Original Class: " + this.originalAuthentication.getName());
return sb.toString();
}
}
@@ -0,0 +1,5 @@
<html>
<body>
Allows intercepted methods to be run under a different authentication identity.
</body>
</html>
@@ -0,0 +1,71 @@
/*
* The Acegi Security System for Spring is published under the terms
* of the Apache Software License.
*
* Visit http://acegisecurity.sourceforge.net for further details.
*/
package net.sf.acegisecurity.providers.dao;
import net.sf.acegisecurity.GrantedAuthority;
/**
* Models core user information retieved by an {@link AuthenticationDao}.
*
* @author Ben Alex
* @version $Id$
*/
public class User {
//~ Instance fields ========================================================
private String password;
private String username;
private GrantedAuthority[] authorities;
private boolean enabled;
//~ Constructors ===========================================================
/**
* Construct the <code>User</code> with the details required by {@link
* DaoAuthenticationProvider}.
*
* @param username the username presented to the
* <code>DaoAuthenticationProvider</code>
* @param password the password that should be presented to the
* <code>DaoAuthenticationProvider</code>
* @param enabled set to <code>true</code> if the user is enabled
* @param authorities the authorities that should be granted to the caller
* if they presented the correct username and password and the user
* is enabled
*/
public User(String username, String password, boolean enabled,
GrantedAuthority[] authorities) {
this.username = username;
this.password = password;
this.enabled = enabled;
this.authorities = authorities;
}
private User() {
super();
}
//~ Methods ================================================================
public GrantedAuthority[] getAuthorities() {
return authorities;
}
public boolean isEnabled() {
return enabled;
}
public String getPassword() {
return password;
}
public String getUsername() {
return username;
}
}
@@ -0,0 +1,46 @@
/*
* The Acegi Security System for Spring is published under the terms
* of the Apache Software License.
*
* Visit http://acegisecurity.sourceforge.net for further details.
*/
package net.sf.acegisecurity.providers.dao;
import org.springframework.dao.DataAccessException;
/**
* Defines an interface for implementations that wish to provide data access
* services to the {@link DaoAuthenticationProvider}.
*
* <p>
* The interface requires only one read-only method, which simplifies support
* of new data access strategies.
* </p>
*
* @author Ben Alex
* @version $Id$
*/
public interface AuthenticationDao {
//~ Methods ================================================================
/**
* Locates the user based on the username. The search is case insensitive,
* meaning the implementation must return any matching object irrespective
* of the mixture of uppercase and lowercase characters in the username.
*
* @param username the username presented to the {@link
* DaoAuthenticationProvider}
*
* @return a fully populated user record
*
* @throws UsernameNotFoundException if the user could not be found or the
* user has no GrantedAuthority
* @throws DataAccessException if user could not be found for a
* repository-specific reason
*/
public User loadUserByUsername(String username)
throws UsernameNotFoundException,
DataAccessException;
}
@@ -0,0 +1,139 @@
/*
* The Acegi Security System for Spring is published under the terms
* of the Apache Software License.
*
* Visit http://acegisecurity.sourceforge.net for further details.
*/
package net.sf.acegisecurity.providers.dao.jdbc;
import net.sf.acegisecurity.GrantedAuthority;
import net.sf.acegisecurity.GrantedAuthorityImpl;
import net.sf.acegisecurity.providers.dao.AuthenticationDao;
import net.sf.acegisecurity.providers.dao.User;
import net.sf.acegisecurity.providers.dao.UsernameNotFoundException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.ApplicationContextException;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.SqlParameter;
import org.springframework.jdbc.core.support.JdbcDaoSupport;
import org.springframework.jdbc.object.MappingSqlQuery;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.List;
import javax.sql.DataSource;
/**
* Retrieves user details from a JDBC location provided by the bean context.
*
* @author Ben Alex
* @version $Id$
*/
public class JdbcDaoImpl extends JdbcDaoSupport implements AuthenticationDao {
//~ Static fields/initializers =============================================
private static final Log logger = LogFactory.getLog(JdbcDaoSupport.class);
//~ Instance fields ========================================================
private AuthoritiesByUsernameQuery authoritiesByUsernameQuery;
private UsersByUsernameQuery usersByUsernameQuery;
//~ Methods ================================================================
public User loadUserByUsername(String username)
throws UsernameNotFoundException,
DataAccessException {
List users = usersByUsernameQuery.execute(username);
if (users.size() == 0) {
throw new UsernameNotFoundException("User not found");
}
User user = (User) users.get(0); // contains no GrantedAuthority[]
List dbAuths = authoritiesByUsernameQuery.execute(user.getUsername());
if (dbAuths.size() == 0) {
throw new UsernameNotFoundException("User has no GrantedAuthority");
}
GrantedAuthority[] arrayAuths = {new GrantedAuthorityImpl("demo")};
arrayAuths = (GrantedAuthority[]) dbAuths.toArray(arrayAuths);
return new User(user.getUsername(), user.getPassword(),
user.isEnabled(), arrayAuths);
}
protected void setAuthoritiesByUsernameQuery(AuthoritiesByUsernameQuery authoritiesByUsernameQuery) {
this.authoritiesByUsernameQuery = authoritiesByUsernameQuery;
}
protected void setUsersByUsernameQuery(UsersByUsernameQuery usersByUsernameQuery) {
this.usersByUsernameQuery = usersByUsernameQuery;
}
protected void initDao() throws ApplicationContextException {
if (usersByUsernameQuery == null) {
usersByUsernameQuery = new UsersByUsernameQuery(getDataSource());
}
if (authoritiesByUsernameQuery == null) {
authoritiesByUsernameQuery = new AuthoritiesByUsernameQuery(getDataSource());
}
}
//~ Inner Classes ==========================================================
/**
* Query object to look up a user's authorities.
*/
protected static class AuthoritiesByUsernameQuery extends MappingSqlQuery {
protected AuthoritiesByUsernameQuery(DataSource ds) {
super(ds,
"SELECT username,authority FROM authorities WHERE username = ?");
declareParameter(new SqlParameter(Types.VARCHAR));
compile();
}
protected Object mapRow(ResultSet rs, int rownum)
throws SQLException {
GrantedAuthorityImpl authority = new GrantedAuthorityImpl(rs
.getString("authority"));
return authority;
}
}
/**
* Query object to look up a user.
*/
protected static class UsersByUsernameQuery extends MappingSqlQuery {
protected UsersByUsernameQuery(DataSource ds) {
super(ds,
"SELECT username,password,enabled FROM users WHERE username = ?");
declareParameter(new SqlParameter(Types.VARCHAR));
compile();
}
protected Object mapRow(ResultSet rs, int rownum)
throws SQLException {
String username = rs.getString("username");
String password = rs.getString("password");
boolean enabled = rs.getBoolean("enabled");
User user = new User(username, password, enabled, null);
return user;
}
}
}
@@ -0,0 +1,5 @@
<html>
<body>
Exposes a JDBC-based authentication repository.
</body>
</html>
@@ -0,0 +1,57 @@
/*
* The Acegi Security System for Spring is published under the terms
* of the Apache Software License.
*
* Visit http://acegisecurity.sourceforge.net for further details.
*/
package net.sf.acegisecurity.providers.dao.memory;
import net.sf.acegisecurity.providers.dao.AuthenticationDao;
import net.sf.acegisecurity.providers.dao.User;
import net.sf.acegisecurity.providers.dao.UsernameNotFoundException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.dao.DataAccessException;
/**
* Retrieves user details from an in-memory list created by the bean context.
*
* @author Ben Alex
* @version $Id$
*/
public class InMemoryDaoImpl implements AuthenticationDao, InitializingBean {
//~ Instance fields ========================================================
private UserMap userMap;
//~ Methods ================================================================
public void setUserMap(UserMap userMap) {
this.userMap = userMap;
}
public UserMap getUserMap() {
return userMap;
}
public void afterPropertiesSet() throws Exception {
if (this.userMap == null) {
throw new IllegalArgumentException("A list of users, passwords, enabled/disabled status and their granted authorities must be set");
}
}
public User loadUserByUsername(String username)
throws UsernameNotFoundException,
DataAccessException {
User result = userMap.getUser(username);
if (result == null) {
throw new UsernameNotFoundException("User could not be found");
}
return result;
}
}
@@ -0,0 +1,72 @@
/*
* The Acegi Security System for Spring is published under the terms
* of the Apache Software License.
*
* Visit http://acegisecurity.sourceforge.net for further details.
*/
package net.sf.acegisecurity.providers.dao.memory;
import net.sf.acegisecurity.GrantedAuthority;
import net.sf.acegisecurity.GrantedAuthorityImpl;
import java.util.HashSet;
import java.util.Set;
/**
* Used by {@link InMemoryDaoImpl} to temporarily store the attributes
* associated with a user.
*
* @author Ben Alex
* @version $Id$
*/
public class UserAttributeDefinition {
//~ Instance fields ========================================================
private Set authorities = new HashSet();
private String password;
private boolean enabled = true;
//~ Constructors ===========================================================
public UserAttributeDefinition() {
super();
}
//~ Methods ================================================================
public GrantedAuthority[] getAuthorities() {
GrantedAuthority[] toReturn = {new GrantedAuthorityImpl("demo")};
return (GrantedAuthority[]) this.authorities.toArray(toReturn);
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public boolean isEnabled() {
return enabled;
}
public void setPassword(String password) {
this.password = password;
}
public String getPassword() {
return password;
}
public boolean isValid() {
if ((this.password != null) && (authorities.size() > 0)) {
return true;
} else {
return false;
}
}
public void addAuthority(GrantedAuthority newAuthority) {
this.authorities.add(newAuthority);
}
}
@@ -0,0 +1,57 @@
/*
* The Acegi Security System for Spring is published under the terms
* of the Apache Software License.
*
* Visit http://acegisecurity.sourceforge.net for further details.
*/
package net.sf.acegisecurity.providers.dao.memory;
import net.sf.acegisecurity.GrantedAuthorityImpl;
import org.springframework.util.StringUtils;
import java.beans.PropertyEditorSupport;
/**
* Property editor that creates a {@link UserAttributeDefinition} from a comma
* separated list of values.
*
* @author Ben Alex
* @version $Id$
*/
public class UserAttributeEditor extends PropertyEditorSupport {
//~ Methods ================================================================
public void setAsText(String s) throws IllegalArgumentException {
if ((s == null) || "".equals(s)) {
setValue(null);
} else {
String[] tokens = StringUtils.commaDelimitedListToStringArray(s);
UserAttributeDefinition userAttrib = new UserAttributeDefinition();
for (int i = 0; i < tokens.length; i++) {
String currentToken = tokens[i];
if (i == 0) {
userAttrib.setPassword(currentToken);
} else {
if (currentToken.toLowerCase().equals("enabled")) {
userAttrib.setEnabled(true);
} else if (currentToken.toLowerCase().equals("disabled")) {
userAttrib.setEnabled(false);
} else {
userAttrib.addAuthority(new GrantedAuthorityImpl(currentToken));
}
}
}
if (userAttrib.isValid()) {
setValue(userAttrib);
} else {
setValue(null);
}
}
}
}
@@ -0,0 +1,50 @@
/*
* The Acegi Security System for Spring is published under the terms
* of the Apache Software License.
*
* Visit http://acegisecurity.sourceforge.net for further details.
*/
package net.sf.acegisecurity.providers.dao.memory;
import net.sf.acegisecurity.providers.dao.User;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import java.util.HashMap;
import java.util.Map;
/**
* Used by {@link InMemoryDaoImpl} to store a list of users and their
* corresponding granted authorities.
*
* @author Ben Alex
* @version $Id$
*/
public class UserMap {
//~ Static fields/initializers =============================================
private static final Log logger = LogFactory.getLog(UserMap.class);
//~ Instance fields ========================================================
private Map userMap = new HashMap();
//~ Methods ================================================================
public User getUser(String username) {
return (User) this.userMap.get(username.toLowerCase());
}
/**
* Adds a user to the in-memory map.
*
* @param user the user to be stored
*/
public void addUser(User user) {
logger.info("Adding user [" + user + "]");
this.userMap.put(user.getUsername().toLowerCase(), user);
}
}
@@ -0,0 +1,93 @@
/*
* The Acegi Security System for Spring is published under the terms
* of the Apache Software License.
*
* Visit http://acegisecurity.sourceforge.net for further details.
*/
package net.sf.acegisecurity.providers.dao.memory;
import net.sf.acegisecurity.providers.dao.User;
import org.springframework.beans.propertyeditors.PropertiesEditor;
import java.beans.PropertyEditorSupport;
import java.util.Iterator;
import java.util.Properties;
/**
* Property editor to assist with the setup of a {@link UserMap}.
*
* <p>
* The format of entries should be:
* </p>
*
* <p>
* <code>
* username=password,grantedAuthority[,grantedAuthority][,enabled|disabled]
* </code>
* </p>
*
* <p>
* The <code>password</code> must always be the first entry after the equals.
* The <code>enabled</code> or <code>disabled</code> keyword can appear
* anywhere (apart from the first entry reserved for the password). If neither
* <code>enabled</code> or <code>disabled</code> appear, the default is
* <code>enabled</code>. At least one granted authority must be listed.
* </p>
*
* <p>
* The <code>username</code> represents the key and duplicates are handled the
* same was as duplicates would be in Java <code>Properties</code> files.
* </p>
*
* <p>
* If the above requirements are not met, the invalid entry will be silently
* ignored.
* </p>
*
* @author Ben Alex
* @version $Id$
*/
public class UserMapEditor extends PropertyEditorSupport {
//~ Methods ================================================================
public void setAsText(String s) throws IllegalArgumentException {
UserMap userMap = new UserMap();
if ((s == null) || "".equals(s)) {
// Leave value in property editor null
} else {
// Use properties editor to tokenize the string
PropertiesEditor propertiesEditor = new PropertiesEditor();
propertiesEditor.setAsText(s);
Properties props = (Properties) propertiesEditor.getValue();
// Now we have properties, process each one individually
UserAttributeEditor configAttribEd = new UserAttributeEditor();
for (Iterator iter = props.keySet().iterator(); iter.hasNext();) {
String username = (String) iter.next();
String value = props.getProperty(username);
// Convert value to a password, enabled setting, and list of granted authorities
configAttribEd.setAsText(value);
UserAttributeDefinition attr = (UserAttributeDefinition) configAttribEd
.getValue();
// Make a user object, assuming the properties were properly provided
if (attr != null) {
User user = new User(username, attr.getPassword(),
attr.isEnabled(), attr.getAuthorities());
userMap.addUser(user);
}
}
}
setValue(userMap);
}
}
@@ -0,0 +1,6 @@
<html>
<body>
Exposes an in-memory authentication repository.
</body>
</html>
@@ -0,0 +1,98 @@
/*
* The Acegi Security System for Spring is published under the terms
* of the Apache Software License.
*
* Visit http://acegisecurity.sourceforge.net for further details.
*/
package net.sf.acegisecurity.vote;
import net.sf.acegisecurity.AccessDecisionManager;
import net.sf.acegisecurity.ConfigAttribute;
import org.springframework.beans.factory.InitializingBean;
import java.util.Iterator;
import java.util.List;
/**
* Abstract implementation of {@link AccessDecisionManager}.
*
* <p>
* Handles configuration of a bean context defined list of {@link
* AccessDecisionVoter}s and the access control behaviour if all voters
* abstain from voting (defaults to deny access).
* </p>
*
* @author Ben Alex
* @version $Id$
*/
public abstract class AbstractAccessDecisionManager
implements AccessDecisionManager, InitializingBean {
//~ Instance fields ========================================================
private List decisionVoters;
private boolean allowIfAllAbstainDecisions = false;
//~ Methods ================================================================
public void setAllowIfAllAbstainDecisions(boolean allowIfAllAbstainDecisions) {
this.allowIfAllAbstainDecisions = allowIfAllAbstainDecisions;
}
public boolean isAllowIfAllAbstainDecisions() {
return allowIfAllAbstainDecisions;
}
public void setDecisionVoters(List newList) {
checkIfValidList(newList);
Iterator iter = newList.iterator();
while (iter.hasNext()) {
Object currentObject = null;
try {
currentObject = iter.next();
AccessDecisionVoter attemptToCast = (AccessDecisionVoter) currentObject;
} catch (ClassCastException cce) {
throw new IllegalArgumentException("AccessDecisionVoter "
+ currentObject.getClass()
.getName()
+ " must implement AccessDecisionVoter");
}
}
this.decisionVoters = newList;
}
public List getDecisionVoters() {
return this.decisionVoters;
}
public void afterPropertiesSet() throws Exception {
checkIfValidList(this.decisionVoters);
}
public boolean supports(ConfigAttribute attribute) {
Iterator iter = this.decisionVoters.iterator();
while (iter.hasNext()) {
AccessDecisionVoter voter = (AccessDecisionVoter) iter.next();
if (voter.supports(attribute)) {
return true;
}
}
return false;
}
private void checkIfValidList(List listToCheck) {
if ((listToCheck == null) || (listToCheck.size() == 0)) {
throw new IllegalArgumentException("A list of AccessDecisionVoters is required");
}
}
}
@@ -0,0 +1,96 @@
/*
* The Acegi Security System for Spring is published under the terms
* of the Apache Software License.
*
* Visit http://acegisecurity.sourceforge.net for further details.
*/
package net.sf.acegisecurity.vote;
import net.sf.acegisecurity.Authentication;
import net.sf.acegisecurity.ConfigAttribute;
import net.sf.acegisecurity.ConfigAttributeDefinition;
import org.aopalliance.intercept.MethodInvocation;
/**
* Indicates a class is responsible for voting on authorization decisions.
*
* <p>
* The coordination of voting (ie polling <code>AccessDecisionVoter</code>s,
* tallying their responses, and making the final authorization decision) is
* performed by an {@link net.sf.acegisecurity.AccessDecisionManager}.
* </p>
*
* @author Ben Alex
* @version $Id$
*/
public interface AccessDecisionVoter {
//~ Static fields/initializers =============================================
public static final int ACCESS_GRANTED = 1;
public static final int ACCESS_ABSTAIN = 0;
public static final int ACCESS_DENIED = -1;
//~ Methods ================================================================
/**
* Indicates whether this <code>AccessDecisionVoter</code> is able to vote
* on the passed <code>ConfigAttribute</code>.
*
* <p>
* This allows the <code>SecurityInterceptor</code> to check every
* configuration attribute can be consumed by the configured
* <code>AccessDecisionManager</code> and/or <code>RunAsManager</code>.
* </p>
*
* @param attribute a configuration attribute that has been configured
* against the <code>SecurityInterceptor</code>
*
* @return true if this <code>AccessDecisionVoter</code> can support the
* passed configuration attribute
*/
public boolean supports(ConfigAttribute attribute);
/**
* Indicates whether or not access is granted.
*
* <p>
* The decision must be affirmative (<code>ACCESS_GRANTED</code>),
* negative (<code>ACCESS_DENIED</code>) or the
* <code>AccessDecisionVoter</code> can abstain
* (<code>ACCESS_ABSTAIN</code>) from voting. Under no circumstances
* should implementing classes return any other value. If a weighting of
* results is desired, this should be handled in a custom {@link
* net.sf.acegisecurity.AccessDecisionManager} instead.
* </p>
*
* <P>
* Unless an <code>AccessDecisionVoter</code> is specifically intended to
* vote on an access control decision due to a passed method invocation or
* configuration attribute parameter, it must return
* <code>ACCESS_ABSTAIN</code>. This prevents the coordinating
* <code>AccessDecisionManager</code> from counting votes from those
* <code>AccessDecisionVoter</code>s without a legitimate interest in the
* access control decision.
* </p>
*
* <p>
* Whilst the method invocation is passed as a parameter to maximise
* flexibility in making access control decisions, implementing classes
* must never modify the behaviour of the method invocation (such as
* calling <Code>MethodInvocation.proceed()</code>).
* </p>
*
* @param authentication the caller invoking the method
* @param invocation the method being called
* @param config the configuration attributes associated with the method
* being invoked
*
* @return either {@link #ACCESS_GRANTED}, {@link #ACCESS_ABSTAIN} or
* {@link #ACCESS_DENIED}
*/
public int vote(Authentication authentication, MethodInvocation invocation,
ConfigAttributeDefinition config);
}
@@ -0,0 +1,92 @@
/*
* The Acegi Security System for Spring is published under the terms
* of the Apache Software License.
*
* Visit http://acegisecurity.sourceforge.net for further details.
*/
package net.sf.acegisecurity.vote;
import net.sf.acegisecurity.AccessDeniedException;
import net.sf.acegisecurity.Authentication;
import net.sf.acegisecurity.ConfigAttributeDefinition;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import java.util.Iterator;
/**
* Simple concrete implementation of {@link
* net.sf.acegisecurity.AccessDecisionManager} that grants access if any
* <code>AccessDecisionVoter</code> returns an affirmative response.
*
* @author Ben Alex
* @version $Id$
*/
public class AffirmativeBased extends AbstractAccessDecisionManager {
//~ Static fields/initializers =============================================
private static final Log logger = LogFactory.getLog(AffirmativeBased.class);
//~ Methods ================================================================
/**
* This concrete implementation simply polls all configured {@link
* AccessDecisionVoter}s and grants access if any
* <code>AccessDecisionVoter</code> voted affirmatively. Denies access
* only if there was a deny vote AND no affirmative votes.
*
* <p>
* If every <code>AccessDecisionVoter</code> abstained from voting, the
* decision will be based on the {@link #isAllowIfAllAbstainDecisions()}
* property (defaults to false).
* </p>
*
* @param authentication the caller invoking the method
* @param invocation the method being called
* @param config the configuration attributes associated with the method
* being invoked
*
* @throws AccessDeniedException if access is denied
*/
public void decide(Authentication authentication,
MethodInvocation invocation,
ConfigAttributeDefinition config)
throws AccessDeniedException {
Iterator iter = this.getDecisionVoters().iterator();
int deny = 0;
while (iter.hasNext()) {
AccessDecisionVoter voter = (AccessDecisionVoter) iter.next();
int result = voter.vote(authentication, invocation, config);
switch (result) {
case AccessDecisionVoter.ACCESS_GRANTED:
return;
case AccessDecisionVoter.ACCESS_DENIED:
deny++;
break;
default:
break;
}
}
if (deny > 0) {
throw new AccessDeniedException("Access is denied.");
}
// To get this far, every AccessDecisionVoter abstained
if (this.isAllowIfAllAbstainDecisions()) {
return;
} else {
throw new AccessDeniedException("Access is denied.");
}
}
}
@@ -0,0 +1,127 @@
/*
* The Acegi Security System for Spring is published under the terms
* of the Apache Software License.
*
* Visit http://acegisecurity.sourceforge.net for further details.
*/
package net.sf.acegisecurity.vote;
import net.sf.acegisecurity.AccessDeniedException;
import net.sf.acegisecurity.Authentication;
import net.sf.acegisecurity.ConfigAttributeDefinition;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import java.util.Iterator;
/**
* Simple concrete implementation of {@link
* net.sf.acegisecurity.AccessDecisionManager} that uses a consensus-based
* approach.
*
* @author Ben Alex
* @version $Id$
*/
public class ConsensusBased extends AbstractAccessDecisionManager {
//~ Static fields/initializers =============================================
private static final Log logger = LogFactory.getLog(ConsensusBased.class);
//~ Instance fields ========================================================
private boolean allowIfEqualGrantedDeniedDecisions = true;
//~ Methods ================================================================
public void setAllowIfEqualGrantedDeniedDecisions(boolean allowIfEqualGrantedDeniedDecisions) {
this.allowIfEqualGrantedDeniedDecisions = allowIfEqualGrantedDeniedDecisions;
}
public boolean isAllowIfEqualGrantedDeniedDecisions() {
return allowIfEqualGrantedDeniedDecisions;
}
/**
* This concrete implementation simply polls all configured {@link
* AccessDecisionVoter}s and upon completion determines the consensus of
* granted vs denied responses.
*
* <p>
* If there were an equal number of grant and deny votes, the decision will
* be based on the {@link #isAllowIfEqualGrantedDeniedDecisions()}
* property (defaults to true).
* </p>
*
* <p>
* If every <code>AccessDecisionVoter</code> abstained from voting, the
* decision will be based on the {@link #isAllowIfAllAbstainDecisions()}
* property (defaults to false).
* </p>
*
* @param authentication the caller invoking the method
* @param invocation the method being called
* @param config the configuration attributes associated with the method
* being invoked
*
* @throws AccessDeniedException if access is denied
*/
public void decide(Authentication authentication,
MethodInvocation invocation,
ConfigAttributeDefinition config)
throws AccessDeniedException {
Iterator iter = this.getDecisionVoters().iterator();
int grant = 0;
int deny = 0;
int abstain = 0;
while (iter.hasNext()) {
AccessDecisionVoter voter = (AccessDecisionVoter) iter.next();
int result = voter.vote(authentication, invocation, config);
switch (result) {
case AccessDecisionVoter.ACCESS_GRANTED:
grant++;
break;
case AccessDecisionVoter.ACCESS_DENIED:
deny++;
break;
default:
abstain++;
break;
}
}
if (grant > deny) {
return;
}
if (deny > grant) {
throw new AccessDeniedException("Access is denied.");
}
if ((grant == deny) && (grant != 0)) {
if (this.allowIfEqualGrantedDeniedDecisions) {
return;
} else {
throw new AccessDeniedException("Access is denied.");
}
}
// To get this far, every AccessDecisionVoter abstained
if (this.isAllowIfAllAbstainDecisions()) {
return;
} else {
throw new AccessDeniedException("Access is denied.");
}
}
}
@@ -0,0 +1,76 @@
/*
* The Acegi Security System for Spring is published under the terms
* of the Apache Software License.
*
* Visit http://acegisecurity.sourceforge.net for further details.
*/
package net.sf.acegisecurity.vote;
import net.sf.acegisecurity.Authentication;
import net.sf.acegisecurity.ConfigAttribute;
import net.sf.acegisecurity.ConfigAttributeDefinition;
import org.aopalliance.intercept.MethodInvocation;
import java.util.Iterator;
/**
* Votes if any {@link ConfigAttribute#getAttribute()} is prefixed with
* <Code>ROLE_</code>.
*
* <p>
* Abstains from voting if no configuration attribute commences with
* <code>ROLE_</code>. Votes to grant access if there is an exact matching
* {@link net.sf.acegisecurity.GrantedAuthority} to a
* <code>ConfigAttribute</code> starting with <code>ROLE_</code>. Votes to
* deny access if there is no exact matching <code>GrantedAuthority</code> to
* a <code>ConfigAttribute</code> starting with <code>ROLE_</code>.
* </p>
*
* <p>
* All comparisons and prefixes are case sensitive.
* </p>
*
* @author Ben Alex
* @version $Id$
*/
public class RoleVoter implements AccessDecisionVoter {
//~ Methods ================================================================
public boolean supports(ConfigAttribute attribute) {
if ((attribute.getAttribute() != null)
&& attribute.getAttribute().startsWith("ROLE_")) {
return true;
} else {
return false;
}
}
public int vote(Authentication authentication, MethodInvocation invocation,
ConfigAttributeDefinition config) {
int result = ACCESS_ABSTAIN;
Iterator iter = config.getConfigAttributes();
while (iter.hasNext()) {
ConfigAttribute attribute = (ConfigAttribute) iter.next();
if (this.supports(attribute)) {
result = ACCESS_DENIED;
// Attempt to find a matching granted authority
for (int i = 0; i < authentication.getAuthorities().length;
i++) {
if (attribute.getAttribute().equals(authentication
.getAuthorities()[i]
.getAuthority())) {
return ACCESS_GRANTED;
}
}
}
}
return result;
}
}
@@ -0,0 +1,119 @@
/*
* The Acegi Security System for Spring is published under the terms
* of the Apache Software License.
*
* Visit http://acegisecurity.sourceforge.net for further details.
*/
package net.sf.acegisecurity.vote;
import net.sf.acegisecurity.AccessDeniedException;
import net.sf.acegisecurity.Authentication;
import net.sf.acegisecurity.ConfigAttribute;
import net.sf.acegisecurity.ConfigAttributeDefinition;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import java.util.Iterator;
/**
* Simple concrete implementation of {@link
* net.sf.acegisecurity.AccessDecisionManager} that requires all voters to
* abstain or grant access.
*
* @author Ben Alex
* @version $Id$
*/
public class UnanimousBased extends AbstractAccessDecisionManager {
//~ Static fields/initializers =============================================
private static final Log logger = LogFactory.getLog(UnanimousBased.class);
//~ Methods ================================================================
/**
* This concrete implementation polls all configured {@link
* AccessDecisionVoter}s for each {@link ConfigAttribute} and grants
* access if <b>only</b> grant votes were received.
*
* <p>
* Other voting implementations usually pass the entire list of {@link
* ConfigAttributeDefinition}s to the <code>AccessDecisionVoter</code>.
* This implementation differs in that each
* <code>AccessDecisionVoter</code> knows only about a single
* <code>ConfigAttribute</code> at a time.
* </p>
*
* <p>
* If every <code>AccessDecisionVoter</code> abstained from voting, the
* decision will be based on the {@link #isAllowIfAllAbstainDecisions()}
* property (defaults to false).
* </p>
*
* @param authentication the caller invoking the method
* @param invocation the method being called
* @param config the configuration attributes associated with the method
* being invoked
*
* @throws AccessDeniedException if access is denied
*/
public void decide(Authentication authentication,
MethodInvocation invocation,
ConfigAttributeDefinition config)
throws AccessDeniedException {
int grant = 0;
int deny = 0;
int abstain = 0;
Iterator configIter = config.getConfigAttributes();
while (configIter.hasNext()) {
ConfigAttributeDefinition thisDef = new ConfigAttributeDefinition();
thisDef.addConfigAttribute((ConfigAttribute) configIter.next());
Iterator voters = this.getDecisionVoters().iterator();
while (voters.hasNext()) {
AccessDecisionVoter voter = (AccessDecisionVoter) voters.next();
int result = voter.vote(authentication, invocation, thisDef);
switch (result) {
case AccessDecisionVoter.ACCESS_GRANTED:
grant++;
break;
case AccessDecisionVoter.ACCESS_DENIED:
deny++;
break;
default:
abstain++;
break;
}
}
}
if (deny > 0) {
throw new AccessDeniedException("Access is denied.");
}
// To get this far, there were no deny votes
if (grant > 0) {
return;
}
// To get this far, every AccessDecisionVoter abstained
if (this.isAllowIfAllAbstainDecisions()) {
return;
} else {
throw new AccessDeniedException("Access is denied.");
}
}
}
@@ -0,0 +1,6 @@
<html>
<body>
Implements a vote-based approach to authorization decisions.
<p>
</body>
</html>
@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<!--
* The Acegi Security System for Spring is published under the terms
* of the Apache Software License.
* $Id$
-->
<beans>
<!-- ================= CONTAINER ADAPTER CONFIGURATION ================ -->
<!-- Data access object which stores authentication information -->
<bean id="inMemoryDaoImpl" class="net.sf.acegisecurity.providers.dao.memory.InMemoryDaoImpl">
<property name="userMap">
<value>
marissa=koala,ROLE_TELLER,ROLE_SUPERVISOR
dianne=emu,ROLE_TELLER
scott=wombat,ROLE_TELLER
peter=opal,disabled,ROLE_TELLER
</value>
</property>
</bean>
<!-- Authentication provider that queries our data access object -->
<bean id="daoAuthenticationProvider" class="net.sf.acegisecurity.providers.dao.DaoAuthenticationProvider">
<property name="authenticationDao"><ref bean="inMemoryDaoImpl"/></property>
<property name="ignorePasswordCase"><value>false</value></property>
<property name="ignoreUsernameCase"><value>true</value></property>
</bean>
<!-- The authentication manager that iterates through our only authentication provider -->
<bean id="authenticationManager" class="net.sf.acegisecurity.providers.ProviderManager">
<property name="providers">
<list>
<ref bean="daoAuthenticationProvider"/>
</list>
</property>
</bean>
</beans>
@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<!--
* The Acegi Security System for Spring is published under the terms
* of the Apache Software License.
* $Id$
-->
<beans>
<!-- ================= CONTAINER ADAPTER CONFIGURATION ================ -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName"><value>org.hsqldb.jdbcDriver</value></property>
<property name="url"><value>jdbc:hsqldb:hsql://localhost:9001</value></property>
<property name="username"><value>sa</value></property>
<property name="password"><value></value></property>
</bean>
<!-- Data access object which stores authentication information -->
<bean id="jdbcDaoImpl" class="net.sf.acegisecurity.providers.dao.jdbc.JdbcDaoImpl">
<property name="dataSource"><ref bean="dataSource"/></property>
</bean>
<!-- Authentication provider that queries our data access object -->
<bean id="daoAuthenticationProvider" class="net.sf.acegisecurity.providers.dao.DaoAuthenticationProvider">
<property name="authenticationDao"><ref bean="jdbcDaoImpl"/></property>
<property name="ignorePasswordCase"><value>false</value></property>
<property name="ignoreUsernameCase"><value>true</value></property>
</bean>
<!-- The authentication manager that iterates through our only authentication provider -->
<bean id="authenticationManager" class="net.sf.acegisecurity.providers.ProviderManager">
<property name="providers">
<list>
<ref bean="daoAuthenticationProvider"/>
</list>
</property>
</bean>
</beans>
@@ -0,0 +1,88 @@
/*
* The Acegi Security System for Spring is published under the terms
* of the Apache Software License.
*
* Visit http://acegisecurity.sourceforge.net for further details.
*/
package net.sf.acegisecurity;
import net.sf.acegisecurity.vote.AccessDecisionVoter;
import org.aopalliance.intercept.MethodInvocation;
import java.util.Iterator;
/**
* Implementation of an {@link AccessDecisionVoter} that provides a token
* example of application-specific security.
*
* <p>
* If the {@link ConfigAttribute#getAttribute()} has a value of
* <code>BANKSECURITY_CUSTOMER</code>, the account number subject of the
* method call to be compared with any granted authority prefixed with
* <code>ACCOUNT_</code> and followed by that account number. For example, if
* account number 12 was subject of the call, a search would be conducted for
* a granted authority named <code>ACCOUNT_12</code>.
* </p>
*
* <p>
* All comparisons are case sensitive.
* </p>
*
* @author Ben Alex
* @version $Id$
*/
public class BankSecurityVoter implements AccessDecisionVoter {
//~ Methods ================================================================
public boolean supports(ConfigAttribute attribute) {
if ("BANKSECURITY_CUSTOMER".equals(attribute.getAttribute())) {
return true;
} else {
return false;
}
}
public int vote(Authentication authentication, MethodInvocation invocation,
ConfigAttributeDefinition config) {
int result = ACCESS_ABSTAIN;
Iterator iter = config.getConfigAttributes();
while (iter.hasNext()) {
ConfigAttribute attribute = (ConfigAttribute) iter.next();
if (this.supports(attribute)) {
result = ACCESS_DENIED;
// Lookup the account number being passed
Integer accountNumber = null;
for (int i = 0; i < invocation.getArgumentCount(); i++) {
Class argClass = invocation.getArgument(i).getClass();
if (Integer.class.isAssignableFrom(argClass)) {
accountNumber = (Integer) invocation.getArgument(i);
}
}
if (accountNumber != null) {
// Attempt to find a matching granted authority
String targetAttribute = "ACCOUNT_"
+ accountNumber.toString();
for (int i = 0; i < authentication.getAuthorities().length;
i++) {
if (targetAttribute.equals(
authentication.getAuthorities()[i].getAuthority())) {
return ACCESS_GRANTED;
}
}
}
}
}
return result;
}
}
@@ -0,0 +1,43 @@
/*
* The Acegi Security System for Spring is published under the terms
* of the Apache Software License.
*
* Visit http://acegisecurity.sourceforge.net for further details.
*/
package net.sf.acegisecurity;
import net.sf.acegisecurity.context.ContextInvalidException;
import net.sf.acegisecurity.context.SecureContextImpl;
/**
* Demonstrates subclassing the {@link SecureContextImpl} with
* application-specific requirements.
*
* @author Ben Alex
* @version $Id$
*/
public class ExoticSecureContext extends SecureContextImpl {
//~ Instance fields ========================================================
private int magicNumber;
//~ Methods ================================================================
public void setMagicNumber(int magicNumber) {
this.magicNumber = magicNumber;
}
public int getMagicNumber() {
return magicNumber;
}
public void validate() throws ContextInvalidException {
super.validate();
if (magicNumber != 7) {
throw new ContextInvalidException("Magic number is not 7");
}
}
}
@@ -0,0 +1,217 @@
/*
* The Acegi Security System for Spring is published under the terms
* of the Apache Software License.
*
* Visit http://acegisecurity.sourceforge.net for further details.
*/
package net.sf.acegisecurity;
import junit.framework.TestCase;
import net.sf.acegisecurity.context.Account;
import net.sf.acegisecurity.context.BankManager;
import net.sf.acegisecurity.context.Context;
import net.sf.acegisecurity.context.ContextHolder;
import net.sf.acegisecurity.context.ContextImpl;
import net.sf.acegisecurity.context.SecureContext;
import net.sf.acegisecurity.context.SecureContextImpl;
import net.sf.acegisecurity.providers.TestingAuthenticationToken;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* Tests security objects.
*
* @author Ben Alex
* @version $Id$
*/
public class SecurityTests extends TestCase {
//~ Instance fields ========================================================
private ClassPathXmlApplicationContext ctx;
//~ Constructors ===========================================================
public SecurityTests() {
super();
}
public SecurityTests(String arg0) {
super(arg0);
}
//~ Methods ================================================================
public final void setUp() throws Exception {
super.setUp();
ctx = new ClassPathXmlApplicationContext(
"/net/sf/acegisecurity/applicationContext.xml");
}
public static void main(String[] args) {
junit.textui.TestRunner.run(SecurityTests.class);
}
public void testDetectsInvalidConfigAttribute() throws Exception {
try {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(
"/net/sf/acegisecurity/badContext.xml");
fail("Should have thrown BeanCreationException");
} catch (BeanCreationException expected) {
assertTrue(true);
}
}
public void testSecurityInterceptorCustomVoter() throws Exception {
Account marissa = new Account(2, "marissa");
BankManager bank = (BankManager) ctx.getBean("bankManager");
// Indicate the authenticated user holds an account number of 65
GrantedAuthority[] useless = {new GrantedAuthorityImpl("ACCOUNT_65")};
TestingAuthenticationToken auth = new TestingAuthenticationToken("Peter",
"emu", useless);
SecureContext secureContext = new SecureContextImpl();
secureContext.setAuthentication(auth);
ContextHolder.setContext((Context) secureContext);
// Confirm the absence of holding a valid account number rejects access
try {
bank.saveAccount(marissa);
fail("Should have thrown an AccessDeniedException");
} catch (AccessDeniedException expected) {
assertTrue(true);
}
// Now setup a user with the correct account number
GrantedAuthority[] account2 = {new GrantedAuthorityImpl("ACCOUNT_2")};
auth = new TestingAuthenticationToken("Kristy", "opal", account2);
secureContext.setAuthentication(auth);
ContextHolder.setContext((Context) secureContext);
// Check the user can perform operations related to their account number
bank.loadAccount(marissa.getId());
ContextHolder.setContext(null);
}
public void testSecurityInterceptorDetectsInvalidContexts()
throws Exception {
// Normally the security interceptor does not need to detect these conditions,
// because the context interceptor should with its validate method. However,
// the security interceptor still checks it is passed the correct objects.
Account ben = new Account(1, "ben");
BankManager bank = (BankManager) ctx.getBean("bankManager");
// First try with a totally empty ContextHolder
try {
bank.saveAccount(ben);
fail(
"Should have thrown AuthenticationCredentialsNotFoundException");
} catch (AuthenticationCredentialsNotFoundException expected) {
assertTrue(true);
}
// Now try with a ContextHolder but of the wrong type (not a SecureContext)
Context context = new ContextImpl();
ContextHolder.setContext(context);
try {
bank.saveAccount(ben);
fail(
"Should have thrown AuthenticationCredentialsNotFoundException");
} catch (AuthenticationCredentialsNotFoundException expected) {
assertTrue(true);
}
// Next try with a SecureContext but without an authentication object in it
SecureContext secureContext = new SecureContextImpl();
ContextHolder.setContext((Context) secureContext);
try {
bank.saveAccount(ben);
fail(
"Should have thrown AuthenticationCredentialsNotFoundException");
} catch (AuthenticationCredentialsNotFoundException expected) {
assertTrue(true);
}
// Now try with a SecureContext, correctly setup, which should work
GrantedAuthority[] granted = {new GrantedAuthorityImpl(
"ROLE_SUPERVISOR")};
TestingAuthenticationToken auth = new TestingAuthenticationToken("Jeni",
"kangaroo", granted);
secureContext.setAuthentication(auth);
ContextHolder.setContext((Context) secureContext);
Account marissa = new Account(2, "marissa");
marissa.deposit(2000);
bank.saveAccount(marissa);
assertTrue(2000 == bank.getBalance(marissa.getId()));
// Now confirm if we subclass SecureContextImpl it still works.
// Note the validate method in our ExoticSecureContext will not be
// called, as we do not have the context interceptor defined.
ExoticSecureContext exoticContext = new ExoticSecureContext();
exoticContext.setAuthentication(auth);
ContextHolder.setContext((Context) secureContext);
Account scott = new Account(3, "scott");
scott.deposit(50);
bank.saveAccount(scott);
assertTrue(50 == bank.getBalance(scott.getId()));
ContextHolder.setContext(null);
}
public void testSecurityInterceptorEnforcesRoles()
throws Exception {
Account ben = new Account(1, "ben");
ben.deposit(25);
BankManager bank = (BankManager) ctx.getBean("bankManager");
// Indicate the authenticated user holds a role that is not useful
GrantedAuthority[] useless = {new GrantedAuthorityImpl(
"ROLE_NOTHING_USEFUL")};
TestingAuthenticationToken auth = new TestingAuthenticationToken("George",
"koala", useless);
SecureContext secureContext = new SecureContextImpl();
secureContext.setAuthentication(auth);
ContextHolder.setContext((Context) secureContext);
// Confirm the absence of holding a valid role rejects access
try {
bank.saveAccount(ben);
fail("Should have thrown an AccessDeniedException");
} catch (AccessDeniedException expected) {
assertTrue(true);
}
// Now try to call a public method (getBankFundsUnderControl)
bank.getBankFundsUnderControl();
// Now setup a user with only a teller role
GrantedAuthority[] teller = {new GrantedAuthorityImpl("ROLE_TELLER")};
auth = new TestingAuthenticationToken("Michelle", "wombat", teller);
secureContext.setAuthentication(auth);
ContextHolder.setContext((Context) secureContext);
// Confirm the absence of ROLE_SUPERVISOR prevents calling deleteAccount
try {
bank.deleteAccount(ben.getId());
fail("Should have thrown an AccessDeniedException");
} catch (AccessDeniedException expected) {
assertTrue(true);
}
// Check the teller can perform ROLE_TELLER secured operations
bank.saveAccount(ben);
assertTrue(25 == bank.getBalance(ben.getId()));
ContextHolder.setContext(null);
}
}
@@ -0,0 +1,76 @@
/*
* The Acegi Security System for Spring is published under the terms
* of the Apache Software License.
*
* Visit http://acegisecurity.sourceforge.net for further details.
*/
package net.sf.acegisecurity.adapters;
import junit.framework.TestCase;
import net.sf.acegisecurity.Authentication;
import net.sf.acegisecurity.AuthenticationManager;
import net.sf.acegisecurity.adapters.jetty.JettyAcegiUserToken;
import net.sf.acegisecurity.providers.ProviderNotFoundException;
import net.sf.acegisecurity.providers.UsernamePasswordAuthenticationToken;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* Tests {@link AuthByAdapterProvider}
*
* @author Ben Alex
* @version $Id$
*/
public class AuthByAdapterTests extends TestCase {
//~ Instance fields ========================================================
private ClassPathXmlApplicationContext ctx;
//~ Constructors ===========================================================
public AuthByAdapterTests() {
super();
}
public AuthByAdapterTests(String arg0) {
super(arg0);
}
//~ Methods ================================================================
public final void setUp() throws Exception {
super.setUp();
ctx = new ClassPathXmlApplicationContext(
"/net/sf/acegisecurity/adapters/applicationContext.xml");
}
public static void main(String[] args) {
junit.textui.TestRunner.run(AuthByAdapterTests.class);
}
public void testAdapterProvider() throws Exception {
AuthenticationManager authMgr = (AuthenticationManager) ctx.getBean(
"providerManager");
// Should authenticate as JettySpringUser is interface of AuthByAdapter
JettyAcegiUserToken jetty = new JettyAcegiUserToken("my_password",
"Test", "Password", null);
Authentication response = authMgr.authenticate(jetty);
jetty = null;
assertTrue(true);
// Should fail as UsernamePassword is not interface of AuthByAdapter
UsernamePasswordAuthenticationToken user = new UsernamePasswordAuthenticationToken("Test",
"Password");
try {
Authentication response2 = authMgr.authenticate(user);
fail("Should have thrown ProviderNotFoundException");
} catch (ProviderNotFoundException expected) {
assertTrue(true);
}
}
}
@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<!--
* The Acegi Security System for Spring is published under the terms
* of the Apache Software License.
* $Id$
-->
<beans>
<!-- =================== SECURITY SYSTEM DEFINITIONS ================== -->
<!-- ~~~~~~~~~~~~~~~~~~~~ AUTHENTICATION DEFINITIONS ~~~~~~~~~~~~~~~~~~ -->
<!-- Authentication provider that accepts as valid any adapter-created Authentication token -->
<bean id="authByAdapterProvider" class="net.sf.acegisecurity.adapters.AuthByAdapterProvider">
<property name="key"><value>my_password</value></property>
</bean>
<!-- The authentication manager that iterates through our authentication providers -->
<bean id="providerManager" class="net.sf.acegisecurity.providers.ProviderManager">
<property name="providers">
<list>
<ref bean="authByAdapterProvider"/>
</list>
</property>
</bean>
</beans>
@@ -0,0 +1,84 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<!--
* The Acegi Security System for Spring is published under the terms
* of the Apache Software License.
* $Id$
-->
<beans>
<!-- =================== SECURITY SYSTEM DEFINITIONS ================== -->
<!-- RunAsManager -->
<bean id="runAsManager" class="net.sf.acegisecurity.runas.RunAsManagerImpl">
<property name="key"><value>my_run_as_password</value></property>
</bean>
<!-- ~~~~~~~~~~~~~~~~~~~~ AUTHENTICATION DEFINITIONS ~~~~~~~~~~~~~~~~~~ -->
<!-- This authentication provider accepts any presented TestingAuthenticationToken -->
<bean id="testingAuthenticationProvider" class="net.sf.acegisecurity.providers.TestingAuthenticationProvider"/>
<!-- The authentication manager that iterates through our only authentication provider -->
<bean id="authenticationManager" class="net.sf.acegisecurity.providers.ProviderManager">
<property name="providers">
<list>
<ref bean="testingAuthenticationProvider"/>
</list>
</property>
</bean>
<!-- ~~~~~~~~~~~~~~~~~~~~ AUTHORIZATION DEFINITIONS ~~~~~~~~~~~~~~~~~~~ -->
<!-- An access decision voter that reads ROLE_* configuaration settings -->
<bean id="roleVoter" class="net.sf.acegisecurity.vote.RoleVoter"/>
<!-- An access decision voter that reads BANKSECURITY_CUSTOMER configuaration settings -->
<bean id="bankSecurityVoter" class="net.sf.acegisecurity.BankSecurityVoter"/>
<!-- An affirmative access decision manager -->
<bean id="accessDecisionManager" class="net.sf.acegisecurity.vote.AffirmativeBased">
<property name="allowIfAllAbstainDecisions"><value>false</value></property>
<property name="decisionVoters">
<list>
<ref bean="roleVoter"/>
<ref bean="bankSecurityVoter"/>
</list>
</property>
</bean>
<!-- ===================== SECURITY DEFINITIONS ======================= -->
<!-- No declaration for BankManager.getBankFundsUnderControl() makes it public -->
<bean id="bankManagerSecurity" class="net.sf.acegisecurity.SecurityInterceptor">
<property name="authenticationManager"><ref bean="authenticationManager"/></property>
<property name="accessDecisionManager"><ref bean="accessDecisionManager"/></property>
<property name="runAsManager"><ref bean="runAsManager"/></property>
<property name="methodDefinitionSource">
<value>
net.sf.acegisecurity.context.BankManager.delete*=ROLE_SUPERVISOR
net.sf.acegisecurity.context.BankManager.getBalance=ROLE_TELLER,ROLE_SUPERVISOR,BANKSECURITY_CUSTOMER
net.sf.acegisecurity.context.BankManager.loadAccount=ROLE_TELLER,ROLE_SUPERVISOR,BANKSECURITY_CUSTOMER
net.sf.acegisecurity.context.BankManager.saveAccount=ROLE_TELLER,ROLE_SUPERVISOR
net.sf.acegisecurity.context.BankManager.transferFunds=ROLE_SUPERVISOR
</value>
</property>
</bean>
<!-- ======================= BUSINESS DEFINITIONS ===================== -->
<bean id="bankManagerTarget" class="net.sf.acegisecurity.context.BankManagerImpl"/>
<!-- We don't include any context interceptor, although we should do so prior to the security interceptor -->
<bean id="bankManager" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces"><value>net.sf.acegisecurity.context.BankManager</value></property>
<property name="interceptorNames">
<list>
<value>bankManagerSecurity</value>
<value>bankManagerTarget</value>
</list>
</property>
</bean>
</beans>
@@ -0,0 +1,141 @@
/*
* The Acegi Security System for Spring is published under the terms
* of the Apache Software License.
*
* Visit http://acegisecurity.sourceforge.net for further details.
*/
package net.sf.acegisecurity.attribute;
import junit.framework.TestCase;
import net.sf.acegisecurity.Authentication;
import net.sf.acegisecurity.ConfigAttribute;
import net.sf.acegisecurity.ConfigAttributeDefinition;
import net.sf.acegisecurity.GrantedAuthority;
import net.sf.acegisecurity.GrantedAuthorityImpl;
import net.sf.acegisecurity.MethodDefinitionAttributes;
import net.sf.acegisecurity.SecurityConfig;
import net.sf.acegisecurity.context.ContextHolder;
import net.sf.acegisecurity.context.SecureContextImpl;
import net.sf.acegisecurity.providers.TestingAuthenticationToken;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
/**
* DOCUMENT ME!
*
* @author CameronBraid
*/
public class AttributesTests extends TestCase {
//~ Instance fields ========================================================
ClassPathXmlApplicationContext applicationContext;
//~ Constructors ===========================================================
/**
*
*/
public AttributesTests(String a) {
super(a);
}
//~ Methods ================================================================
public void testAttributesForImpl() throws Exception {
ConfigAttributeDefinition def = getConfigAttributeDefinition(TestServiceImpl.class);
Set set = toSet(def);
assertTrue(set.contains(new SecurityConfig("ROLE_INTERFACE")));
assertTrue(set.contains(new SecurityConfig("ROLE_INTERFACE_METHOD")));
assertTrue(set.contains(new SecurityConfig("ROLE_CLASS")));
assertTrue(set.contains(new SecurityConfig("ROLE_CLASS_METHOD")));
}
public void testAttributesForInterface() throws Exception {
ConfigAttributeDefinition def = getConfigAttributeDefinition(TestService.class);
Set set = toSet(def);
System.out.println(set.toString());
assertTrue(set.contains(new SecurityConfig("ROLE_INTERFACE")));
assertTrue(set.contains(new SecurityConfig("ROLE_INTERFACE_METHOD")));
}
public void testInterceptionWithMockAttributesAndSecureContext()
throws Exception {
applicationContext = new ClassPathXmlApplicationContext(
"/net/sf/acegisecurity/attribute/applicationContext.xml");
TestService service = (TestService) applicationContext.getBean(
"testService");
SecureContextImpl context = new SecureContextImpl();
ContextHolder.setContext(context);
Authentication auth;
auth = new TestingAuthenticationToken("test", "test",
new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_CLASS"), new GrantedAuthorityImpl(
"ROLE_INTERFACE"), new GrantedAuthorityImpl(
"ROLE_CLASS_METHOD"), new GrantedAuthorityImpl(
"ROLE_INTERFACE_METHOD")});
context.setAuthentication(auth);
service.myMethod();
auth = new TestingAuthenticationToken("test", "test",
new GrantedAuthority[] {});
context.setAuthentication(auth);
try {
service.myMethod();
fail(
"security interceptor should have detected insufficient permissions");
} catch (Exception e) {}
applicationContext.close();
ContextHolder.setContext(null);
}
private ConfigAttributeDefinition getConfigAttributeDefinition(Class clazz)
throws Exception {
final Method method = clazz.getMethod("myMethod", null);
MethodDefinitionAttributes source = new MethodDefinitionAttributes();
source.setAttributes(new TestAttributes());
ConfigAttributeDefinition config = source.getAttributes(new MockMethodInvocation() {
public Method getMethod() {
return method;
}
});
return config;
}
/**
* convert a ConfigAttributeDefinition into a set of
* <code>ConfigAttribute</code>(s)
*
* @param def DOCUMENT ME!
*
* @return
*/
private Set toSet(ConfigAttributeDefinition def) {
Set set = new HashSet();
Iterator i = def.getConfigAttributes();
while (i.hasNext()) {
ConfigAttribute a = (ConfigAttribute) i.next();
set.add(a);
}
return set;
}
}
@@ -0,0 +1,67 @@
/*
* The Acegi Security System for Spring is published under the terms
* of the Apache Software License.
*
* Visit http://acegisecurity.sourceforge.net for further details.
*/
package net.sf.acegisecurity.attribute;
import org.springframework.metadata.Attributes;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Collection;
/**
* DOCUMENT ME!
*
* @author CameronBraid
*/
public class MockAttributes implements Attributes {
//~ Methods ================================================================
/* (non-Javadoc)
* @see org.springframework.metadata.Attributes#getAttributes(java.lang.Class, java.lang.Class)
*/
public Collection getAttributes(Class arg0, Class arg1) {
throw new UnsupportedOperationException("mock method not implemented");
}
/* (non-Javadoc)
* @see org.springframework.metadata.Attributes#getAttributes(java.lang.Class)
*/
public Collection getAttributes(Class arg0) {
throw new UnsupportedOperationException("mock method not implemented");
}
/* (non-Javadoc)
* @see org.springframework.metadata.Attributes#getAttributes(java.lang.reflect.Field, java.lang.Class)
*/
public Collection getAttributes(Field arg0, Class arg1) {
throw new UnsupportedOperationException("mock method not implemented");
}
/* (non-Javadoc)
* @see org.springframework.metadata.Attributes#getAttributes(java.lang.reflect.Field)
*/
public Collection getAttributes(Field arg0) {
throw new UnsupportedOperationException("mock method not implemented");
}
/* (non-Javadoc)
* @see org.springframework.metadata.Attributes#getAttributes(java.lang.reflect.Method, java.lang.Class)
*/
public Collection getAttributes(Method arg0, Class arg1) {
throw new UnsupportedOperationException("mock method not implemented");
}
/* (non-Javadoc)
* @see org.springframework.metadata.Attributes#getAttributes(java.lang.reflect.Method)
*/
public Collection getAttributes(Method arg0) {
throw new UnsupportedOperationException("mock method not implemented");
}
}
@@ -0,0 +1,109 @@
/*
* The Acegi Security System for Spring is published under the terms
* of the Apache Software License.
*
* Visit http://acegisecurity.sourceforge.net for further details.
*/
package net.sf.acegisecurity.attribute;
import org.aopalliance.intercept.AttributeRegistry;
import org.aopalliance.intercept.Invocation;
import org.aopalliance.intercept.MethodInvocation;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Method;
/**
* DOCUMENT ME!
*
* @author CameronBraid
*/
public class MockMethodInvocation implements MethodInvocation {
//~ Methods ================================================================
/* (non-Javadoc)
* @see org.aopalliance.intercept.Invocation#setArgument(int, java.lang.Object)
*/
public void setArgument(int arg0, Object arg1) {
throw new UnsupportedOperationException("mock method not implemented");
}
/* (non-Javadoc)
* @see org.aopalliance.intercept.Invocation#getArgument(int)
*/
public Object getArgument(int arg0) {
throw new UnsupportedOperationException("mock method not implemented");
}
/* (non-Javadoc)
* @see org.aopalliance.intercept.Invocation#getArgumentCount()
*/
public int getArgumentCount() {
throw new UnsupportedOperationException("mock method not implemented");
}
/* (non-Javadoc)
* @see org.aopalliance.intercept.Invocation#getArguments()
*/
public Object[] getArguments() {
throw new UnsupportedOperationException("mock method not implemented");
}
/* (non-Javadoc)
* @see org.aopalliance.intercept.Invocation#getAttachment(java.lang.String)
*/
public Object getAttachment(String arg0) {
throw new UnsupportedOperationException("mock method not implemented");
}
/* (non-Javadoc)
* @see org.aopalliance.intercept.Invocation#getAttributeRegistry()
*/
public AttributeRegistry getAttributeRegistry() {
throw new UnsupportedOperationException("mock method not implemented");
}
/* (non-Javadoc)
* @see org.aopalliance.intercept.MethodInvocation#getMethod()
*/
public Method getMethod() {
throw new UnsupportedOperationException("mock method not implemented");
}
/* (non-Javadoc)
* @see org.aopalliance.intercept.Joinpoint#getStaticPart()
*/
public AccessibleObject getStaticPart() {
throw new UnsupportedOperationException("mock method not implemented");
}
/* (non-Javadoc)
* @see org.aopalliance.intercept.Joinpoint#getThis()
*/
public Object getThis() {
throw new UnsupportedOperationException("mock method not implemented");
}
/* (non-Javadoc)
* @see org.aopalliance.intercept.Invocation#addAttachment(java.lang.String, java.lang.Object)
*/
public Object addAttachment(String arg0, Object arg1) {
throw new UnsupportedOperationException("mock method not implemented");
}
/* (non-Javadoc)
* @see org.aopalliance.intercept.Invocation#cloneInstance()
*/
public Invocation cloneInstance() {
throw new UnsupportedOperationException("mock method not implemented");
}
/* (non-Javadoc)
* @see org.aopalliance.intercept.Joinpoint#proceed()
*/
public Object proceed() throws Throwable {
throw new UnsupportedOperationException("mock method not implemented");
}
}
@@ -0,0 +1,65 @@
/*
* The Acegi Security System for Spring is published under the terms
* of the Apache Software License.
*
* Visit http://acegisecurity.sourceforge.net for further details.
*/
package net.sf.acegisecurity.attribute;
import net.sf.acegisecurity.SecurityConfig;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
/**
* DOCUMENT ME!
*
* @author CameronBraid
*/
public class TestAttributes extends MockAttributes {
//~ Instance fields ========================================================
List classAttributes = Arrays.asList(new SecurityConfig[] {new SecurityConfig(
"ROLE_CLASS")});
List classMethodAttributes = Arrays.asList(new SecurityConfig[] {new SecurityConfig(
"ROLE_CLASS_METHOD")});
List intrefaceAttributes = Arrays.asList(new SecurityConfig[] {new SecurityConfig(
"ROLE_INTERFACE")});
List intrefaceMethodAttributes = Arrays.asList(new SecurityConfig[] {new SecurityConfig(
"ROLE_INTERFACE_METHOD")});
//~ Methods ================================================================
public Collection getAttributes(Class clazz) {
// interface
if (clazz.equals(TestServiceImpl.class)) {
return classAttributes;
}
// class
if (clazz.equals(TestService.class)) {
return intrefaceAttributes;
}
return null;
}
public Collection getAttributes(Method method) {
// interface
if (method.getDeclaringClass().equals(TestService.class)) {
return intrefaceMethodAttributes;
}
// class
if (method.getDeclaringClass().equals(TestServiceImpl.class)) {
return classMethodAttributes;
}
return null;
}
}
@@ -0,0 +1,19 @@
/*
* The Acegi Security System for Spring is published under the terms
* of the Apache Software License.
*
* Visit http://acegisecurity.sourceforge.net for further details.
*/
package net.sf.acegisecurity.attribute;
/**
* DOCUMENT ME!
*
* @author CameronBraid
*/
public interface TestService {
//~ Methods ================================================================
public abstract void myMethod();
}
@@ -0,0 +1,19 @@
/*
* The Acegi Security System for Spring is published under the terms
* of the Apache Software License.
*
* Visit http://acegisecurity.sourceforge.net for further details.
*/
package net.sf.acegisecurity.attribute;
/**
* DOCUMENT ME!
*
* @author CameronBraid
*/
public class TestServiceImpl implements TestService {
//~ Methods ================================================================
public void myMethod() {}
}
@@ -0,0 +1,85 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<!--
* The Acegi Security System for Spring is published under the terms
* of the Apache Software License.
* $Id$
-->
<beans>
<!-- =================== SECURITY SYSTEM DEFINITIONS ================== -->
<!-- RunAsManager -->
<bean id="runAsManager" class="net.sf.acegisecurity.runas.RunAsManagerImpl">
<property name="key"><value>my_run_as_password</value></property>
</bean>
<!-- ~~~~~~~~~~~~~~~~~~~~ AUTHENTICATION DEFINITIONS ~~~~~~~~~~~~~~~~~~ -->
<!-- This authentication provider accepts any presented TestingAuthenticationToken -->
<bean id="testingAuthenticationProvider" class="net.sf.acegisecurity.providers.TestingAuthenticationProvider"/>
<!-- The authentication manager that iterates through our only authentication provider -->
<bean id="authenticationManager" class="net.sf.acegisecurity.providers.ProviderManager">
<property name="providers">
<list>
<ref bean="testingAuthenticationProvider"/>
</list>
</property>
</bean>
<!-- ~~~~~~~~~~~~~~~~~~~~ AUTHORIZATION DEFINITIONS ~~~~~~~~~~~~~~~~~~~ -->
<!-- An access decision voter that reads ROLE_* configuaration settings -->
<bean id="roleVoter" class="net.sf.acegisecurity.vote.RoleVoter"/>
<!-- An affirmative access decision manager -->
<bean id="accessDecisionManager" class="net.sf.acegisecurity.vote.AffirmativeBased">
<property name="allowIfAllAbstainDecisions"><value>false</value></property>
<property name="decisionVoters">
<list>
<ref bean="roleVoter"/>
</list>
</property>
</bean>
<!-- ===================== SECURITY DEFINITIONS ======================= -->
<bean id="attributes" class="net.sf.acegisecurity.attribute.TestAttributes"/>
<bean id="methodDefinitionSource" class="net.sf.acegisecurity.MethodDefinitionAttributes">
<property name="attributes"><ref local="attributes"/></property>
</bean>
<!-- We don't validate config attributes, as it's unsupported by MethodDefinitionAttributes -->
<bean id="securityInterceptor" class="net.sf.acegisecurity.SecurityInterceptor">
<property name="validateConfigAttributes"><value>false</value></property>
<property name="authenticationManager"><ref bean="authenticationManager"/></property>
<property name="accessDecisionManager"><ref bean="accessDecisionManager"/></property>
<property name="runAsManager"><ref bean="runAsManager"/></property>
<property name="methodDefinitionSource"><ref bean="methodDefinitionSource"/></property>
</bean>
<!-- ======================= BUSINESS DEFINITIONS ===================== -->
<bean id="testService" class="net.sf.acegisecurity.attribute.TestServiceImpl"/>
<bean id="autoProxyCreator" class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<!-- names of the interceptors that will be applied by the proxy -->
<property name="interceptorNames">
<list>
<value>securityInterceptor</value>
</list>
</property>
<!-- the bean names to automatically generate proxies for -->
<property name="beanNames">
<list>
<value>testService</value>
</list>
</property>
</bean>
</beans>
@@ -0,0 +1,66 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<!--
* The Acegi Security System for Spring is published under the terms
* of the Apache Software License.
* $Id$
-->
<beans>
<!-- =================== SECURITY SYSTEM DEFINITIONS ================== -->
<!-- RunAsManager -->
<bean id="runAsManager" class="net.sf.acegisecurity.runas.RunAsManagerImpl">
<property name="key"><value>my_run_as_password</value></property>
</bean>
<!-- ~~~~~~~~~~~~~~~~~~~~ AUTHENTICATION DEFINITIONS ~~~~~~~~~~~~~~~~~~ -->
<!-- This authentication provider accepts any presented TestingAuthenticationToken -->
<bean id="testingAuthenticationProvider" class="net.sf.acegisecurity.providers.TestingAuthenticationProvider"/>
<!-- The authentication manager that iterates through our only authentication provider -->
<bean id="authenticationManager" class="net.sf.acegisecurity.providers.ProviderManager">
<property name="providers">
<list>
<ref bean="testingAuthenticationProvider"/>
</list>
</property>
</bean>
<!-- ~~~~~~~~~~~~~~~~~~~~ AUTHORIZATION DEFINITIONS ~~~~~~~~~~~~~~~~~~~ -->
<!-- An access decision voter that reads ROLE_* configuaration settings -->
<bean id="roleVoter" class="net.sf.acegisecurity.vote.RoleVoter"/>
<!-- An access decision voter that reads BANKSECURITY_CUSTOMER configuaration settings -->
<bean id="bankSecurityVoter" class="net.sf.acegisecurity.BankSecurityVoter"/>
<!-- An affirmative access decision manager -->
<bean id="accessDecisionManager" class="net.sf.acegisecurity.vote.AffirmativeBased">
<property name="allowIfAllAbstainDecisions"><value>false</value></property>
<property name="decisionVoters">
<list>
<ref bean="roleVoter"/>
<ref bean="bankSecurityVoter"/>
</list>
</property>
</bean>
<!-- ===================== SECURITY DEFINITIONS ======================= -->
<!-- Note the INVALID_ATTRIBUTE should be detected at bean context startup time -->
<bean id="bankManagerSecurity" class="net.sf.acegisecurity.SecurityInterceptor">
<property name="authenticationManager"><ref bean="authenticationManager"/></property>
<property name="accessDecisionManager"><ref bean="accessDecisionManager"/></property>
<property name="runAsManager"><ref bean="runAsManager"/></property>
<property name="methodDefinitionSource">
<value>
net.sf.acegisecurity.context.BankManager.delete*=ROLE_SUPERVISOR
net.sf.acegisecurity.context.BankManager.getBalance=ROLE_TELLER,INVALID_ATTRIBUTE,BANKSECURITY_CUSTOMER
</value>
</property>
</bean>
</beans>
@@ -0,0 +1,57 @@
/*
* The Acegi Security System for Spring is published under the terms
* of the Apache Software License.
*
* Visit http://acegisecurity.sourceforge.net for further details.
*/
package net.sf.acegisecurity.context;
/**
* Models a bank account.
*/
public class Account {
//~ Instance fields ========================================================
private Integer id;
private String owningUserName;
private float balance;
//~ Constructors ===========================================================
public Account(Integer id, String owningUserName) {
this.id = id;
this.owningUserName = owningUserName;
}
public Account(int id, String owningUserName) {
this.id = new Integer(id);
this.owningUserName = owningUserName;
}
private Account() {
super();
}
//~ Methods ================================================================
public float getBalance() {
return this.balance;
}
public Integer getId() {
return this.id;
}
public String getOwningUserName() {
return this.owningUserName;
}
public void deposit(float amount) {
this.balance = this.balance + amount;
}
public void withdraw(float amount) {
this.balance = this.balance - amount;
}
}
@@ -0,0 +1,36 @@
/*
* The Acegi Security System for Spring is published under the terms
* of the Apache Software License.
*
* Visit http://acegisecurity.sourceforge.net for further details.
*/
package net.sf.acegisecurity.context;
/**
* Simple business object of an in-memory banking system.
*
* <p>
* We'll spare you from <code>InsufficientFundsExceptions</code> etc. After
* all, this is intended to test security features rather than OO design!
* </p>
*
* @author Ben Alex
* @version $Id$
*/
public interface BankManager {
//~ Methods ================================================================
public float getBalance(Integer accountNumber);
public float getBankFundsUnderControl();
public void deleteAccount(Integer accountNumber);
public Account loadAccount(Integer accountNumber);
public void saveAccount(Account account);
public void transferFunds(Integer fromAccountNumber,
Integer toAccountNumber, float amount);
}
@@ -0,0 +1,67 @@
/*
* The Acegi Security System for Spring is published under the terms
* of the Apache Software License.
*
* Visit http://acegisecurity.sourceforge.net for further details.
*/
package net.sf.acegisecurity.context;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
/**
* Implementation of {@link BankManager}.
*
* @author Ben Alex
* @version $Id$
*/
public class BankManagerImpl implements BankManager {
//~ Instance fields ========================================================
private Map accounts = new HashMap();
//~ Methods ================================================================
public float getBalance(Integer accountNumber) {
Account account = this.loadAccount(accountNumber);
return account.getBalance();
}
public float getBankFundsUnderControl() {
float total = 0;
Iterator iter = this.accounts.keySet().iterator();
while (iter.hasNext()) {
Integer account = (Integer) iter.next();
total = total + this.getBalance(account);
}
return total;
}
public void deleteAccount(Integer accountNumber) {
this.accounts.remove(accountNumber);
}
public Account loadAccount(Integer accountNumber) {
return (Account) accounts.get(accountNumber);
}
public void saveAccount(Account account) {
this.accounts.put(account.getId(), account);
}
public void transferFunds(Integer fromAccountNumber,
Integer toAccountNumber, float amount) {
Account from = this.loadAccount(fromAccountNumber);
Account to = this.loadAccount(toAccountNumber);
from.withdraw(amount);
to.deposit(amount);
this.saveAccount(from);
this.saveAccount(to);
}
}
@@ -0,0 +1,120 @@
/*
* The Acegi Security System for Spring is published under the terms
* of the Apache Software License.
*
* Visit http://acegisecurity.sourceforge.net for further details.
*/
package net.sf.acegisecurity.context;
import junit.framework.TestCase;
import net.sf.acegisecurity.providers.TestingAuthenticationToken;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* Tests context objects.
*
* @author Ben Alex
* @version $Id$
*/
public class ContextTests extends TestCase {
//~ Instance fields ========================================================
private ClassPathXmlApplicationContext ctx;
//~ Constructors ===========================================================
public ContextTests() {
super();
}
public ContextTests(String arg0) {
super(arg0);
}
//~ Methods ================================================================
public final void setUp() throws Exception {
super.setUp();
ctx = new ClassPathXmlApplicationContext(
"/net/sf/acegisecurity/context/applicationContext.xml");
}
public static void main(String[] args) {
junit.textui.TestRunner.run(ContextTests.class);
}
public void testContextInterceptorDetectsEmptyContexts()
throws Exception {
Account ben = new Account(1, "ben");
BankManager bank = (BankManager) ctx.getBean("bankManager");
try {
bank.saveAccount(ben);
fail("Should have thrown ContextHolderEmptyException");
} catch (ContextHolderEmptyException expected) {
assertTrue(true);
}
Context context = new ContextImpl();
ContextHolder.setContext(context);
Account marissa = new Account(2, "marissa");
bank.saveAccount(marissa);
ContextHolder.setContext(null);
}
public void testContextInterceptorProcessesValidations()
throws Exception {
ExoticContext context = new ExoticContext();
ContextHolder.setContext(context);
Account ben = new Account(1, "ben");
BankManager bank = (BankManager) ctx.getBean("bankManager");
try {
bank.saveAccount(ben);
fail(
"Should have thrown ContextInvalidException (magic number is incorrect)");
} catch (ContextInvalidException expected) {
assertTrue(true);
}
context.setMagicNumber(7);
ContextHolder.setContext(context);
Account marissa = new Account(2, "marissa");
bank.saveAccount(marissa);
ContextHolder.setContext(null);
}
public void testContextInterceptorValidatesASecureContext()
throws Exception {
SecureContext context = new SecureContextImpl();
ContextHolder.setContext((Context) context);
Account ben = new Account(1, "ben");
BankManager bank = (BankManager) ctx.getBean("bankManager");
try {
bank.saveAccount(ben);
fail(
"Should have thrown ContextInvalidException (no authentication object)");
} catch (ContextInvalidException expected) {
assertTrue(true);
}
context.setAuthentication(new TestingAuthenticationToken("a", "b", null));
ContextHolder.setContext((Context) context);
Account marissa = new Account(2, "marissa");
bank.saveAccount(marissa);
ContextHolder.setContext(null);
}
}
@@ -0,0 +1,41 @@
/*
* The Acegi Security System for Spring is published under the terms
* of the Apache Software License.
*
* Visit http://acegisecurity.sourceforge.net for further details.
*/
package net.sf.acegisecurity.context;
/**
* Exotic implementation of a {@link Context}.
*
* <p>
* Requires the context to be set with a <code>magicNumber</code> of 7. Tests
* validation in the unit tests.
* </p>
*
* @author Ben Alex
* @version $Id$
*/
public class ExoticContext implements Context {
//~ Instance fields ========================================================
private int magicNumber;
//~ Methods ================================================================
public void setMagicNumber(int magicNumber) {
this.magicNumber = magicNumber;
}
public int getMagicNumber() {
return magicNumber;
}
public void validate() throws ContextInvalidException {
if (magicNumber != 7) {
throw new ContextInvalidException("Magic number is not 7");
}
}
}
@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<!--
* The Acegi Security System for Spring is published under the terms
* of the Apache Software License.
* $Id$
-->
<beans>
<!-- =================== CONTEXT RELATED DEFINITIONS ================== -->
<bean id="contextInterceptor" class="net.sf.acegisecurity.context.ContextInterceptor"/>
<!-- ======================= BUSINESS DEFINITIONS ===================== -->
<bean id="bankManagerTarget" class="net.sf.acegisecurity.context.BankManagerImpl"/>
<bean id="bankManager" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces"><value>net.sf.acegisecurity.context.BankManager</value></property>
<property name="interceptorNames">
<list>
<value>contextInterceptor</value>
<value>bankManagerTarget</value>
</list>
</property>
</bean>
</beans>
@@ -0,0 +1,189 @@
/*
* The Acegi Security System for Spring is published under the terms
* of the Apache Software License.
*
* Visit http://acegisecurity.sourceforge.net for further details.
*/
package net.sf.acegisecurity.providers.dao.memory;
import junit.framework.TestCase;
import net.sf.acegisecurity.AccessDeniedException;
import net.sf.acegisecurity.BadCredentialsException;
import net.sf.acegisecurity.DisabledException;
import net.sf.acegisecurity.context.Account;
import net.sf.acegisecurity.context.BankManager;
import net.sf.acegisecurity.context.Context;
import net.sf.acegisecurity.context.ContextHolder;
import net.sf.acegisecurity.context.SecureContext;
import net.sf.acegisecurity.context.SecureContextImpl;
import net.sf.acegisecurity.providers.UsernamePasswordAuthenticationToken;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* Tests {@link DaoAuthenticationProvider} with {@link InMemoryDaoImpl}.
*
* @author Ben Alex
* @version $Id$
*/
public class InMemoryDaoTests extends TestCase {
//~ Instance fields ========================================================
private ClassPathXmlApplicationContext ctx;
//~ Constructors ===========================================================
public InMemoryDaoTests() {
super();
}
public InMemoryDaoTests(String arg0) {
super(arg0);
}
//~ Methods ================================================================
public final void setUp() throws Exception {
super.setUp();
ctx = new ClassPathXmlApplicationContext(
"/net/sf/acegisecurity/providers/dao/memory/applicationContext.xml");
}
public static void main(String[] args) {
junit.textui.TestRunner.run(InMemoryDaoTests.class);
}
public void testAuthentication() throws Exception {
Account account = new Account(1, "someone");
BankManager bank = (BankManager) ctx.getBean("bankManager");
// Try with an invalid username and password
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("jennifer",
"zebra");
SecureContext secureContext = new SecureContextImpl();
secureContext.setAuthentication(token);
ContextHolder.setContext((Context) secureContext);
try {
bank.saveAccount(account);
fail("Should have thrown a BadCredentialsException");
} catch (BadCredentialsException expected) {
assertTrue(true);
}
// Check our token represents itself properly as a String
System.out.println(token.toString());
assertTrue(token.toString().length() > 10);
// Now try with a valid username, but invalid password
token = new UsernamePasswordAuthenticationToken("marissa", "zebra");
secureContext.setAuthentication(token);
ContextHolder.setContext((Context) secureContext);
try {
bank.saveAccount(account);
fail("Should have thrown a BadCredentialsException");
} catch (BadCredentialsException expected) {
assertTrue(true);
}
// Now try with a valid username and password, but disabled user
token = new UsernamePasswordAuthenticationToken("dianne", "emu");
secureContext.setAuthentication(token);
ContextHolder.setContext((Context) secureContext);
try {
bank.saveAccount(account);
fail("Should have thrown a DisabledException");
} catch (DisabledException expected) {
assertTrue(true);
}
// Now try as a user who didn't have a password defined, and thus
// would have been considered invalid at time of creation
token = new UsernamePasswordAuthenticationToken("someoneelse", "");
secureContext.setAuthentication(token);
ContextHolder.setContext((Context) secureContext);
try {
bank.saveAccount(account);
fail("Should have thrown a BadCredentialsException");
} catch (BadCredentialsException expected) {
assertTrue(true);
}
// Now try as a user who had a password, but no granted authorities,
// and thus would have been considered invalid at time of creation
token = new UsernamePasswordAuthenticationToken("someone", "password");
secureContext.setAuthentication(token);
ContextHolder.setContext((Context) secureContext);
try {
bank.saveAccount(account);
fail("Should have thrown a BadCredentialsException");
} catch (BadCredentialsException expected) {
assertTrue(true);
}
// Now try with a valid mixed case username, valid mixed case password,
// (application context requires passwords to be case matched)
token = new UsernamePasswordAuthenticationToken("MaRiSsA", "kOaLa");
secureContext.setAuthentication(token);
ContextHolder.setContext((Context) secureContext);
try {
bank.saveAccount(account);
fail("Should have thrown a BadCredentialsException");
} catch (BadCredentialsException expected) {
assertTrue(true);
}
// Now try with a valid mixed case username, correct case password,
// (application context does not require usernames to be case matched)
token = new UsernamePasswordAuthenticationToken("MaRiSsA", "koala");
secureContext.setAuthentication(token);
ContextHolder.setContext((Context) secureContext);
bank.saveAccount(account);
ContextHolder.setContext(null);
}
public void testAuthorization() throws Exception {
Account account = new Account(45, "someone");
BankManager bank = (BankManager) ctx.getBean("bankManager");
// Try as a user without access to the account
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("peter",
"opal");
SecureContext secureContext = new SecureContextImpl();
secureContext.setAuthentication(token);
ContextHolder.setContext((Context) secureContext);
try {
// NB: account number 45 != granted authority for account 77
bank.loadAccount(account.getId());
fail("Should have thrown an AccessDeniedException");
} catch (AccessDeniedException expected) {
assertTrue(true);
}
// Now try as user with access to account number 45
token = new UsernamePasswordAuthenticationToken("scott", "wombat");
secureContext.setAuthentication(token);
ContextHolder.setContext((Context) secureContext);
bank.loadAccount(account.getId());
assertTrue(true);
// Now try as user with ROLE_SUPERVISOR access to the account
token = new UsernamePasswordAuthenticationToken("marissa", "koala");
secureContext.setAuthentication(token);
ContextHolder.setContext((Context) secureContext);
bank.loadAccount(account.getId());
assertTrue(true);
ContextHolder.setContext(null);
}
}
@@ -0,0 +1,110 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<!--
* The Acegi Security System for Spring is published under the terms
* of the Apache Software License.
* $Id$
-->
<beans>
<!-- =================== SECURITY SYSTEM DEFINITIONS ================== -->
<!-- RunAsManager -->
<bean id="runAsManager" class="net.sf.acegisecurity.runas.RunAsManagerImpl">
<property name="key"><value>my_run_as_password</value></property>
</bean>
<!-- ~~~~~~~~~~~~~~~~~~~~ AUTHENTICATION DEFINITIONS ~~~~~~~~~~~~~~~~~~ -->
<!-- Data access object which stores authentication information -->
<!-- The two invalid entries at the bottom are provided for testing purposes -->
<bean id="inMemoryDaoImpl" class="net.sf.acegisecurity.providers.dao.memory.InMemoryDaoImpl">
<property name="userMap">
<value>
marissa=koala,ROLE_TELLER,ROLE_SUPERVISOR
dianne=emu,disabled,ROLE_TELLER
scott=wombat,ACCOUNT_45
peter=opal,ACCOUNT_77
someone=password
someoneelse=
</value>
</property>
</bean>
<!-- Authentication provider that queries our data access object -->
<bean id="daoAuthenticationProvider" class="net.sf.acegisecurity.providers.dao.DaoAuthenticationProvider">
<property name="authenticationDao"><ref bean="inMemoryDaoImpl"/></property>
<property name="ignorePasswordCase"><value>false</value></property>
<property name="ignoreUsernameCase"><value>true</value></property>
</bean>
<!-- Authentication provider that accepts as valid our RunAsManagerImpl created tokens -->
<bean id="runAsAuthenticationProvider" class="net.sf.acegisecurity.runas.RunAsImplAuthenticationProvider">
<property name="key"><value>my_run_as_password</value></property>
</bean>
<!-- The authentication manager that iterates through our authentication providers -->
<!-- Strictly we don't need runAsAuthenticationProvider given we haven't defined any RUN_AS configurations -->
<bean id="authenticationManager" class="net.sf.acegisecurity.providers.ProviderManager">
<property name="providers">
<list>
<ref bean="daoAuthenticationProvider"/>
<ref bean="runAsAuthenticationProvider"/>
</list>
</property>
</bean>
<!-- ~~~~~~~~~~~~~~~~~~~~ AUTHORIZATION DEFINITIONS ~~~~~~~~~~~~~~~~~~~ -->
<!-- An access decision voter that reads ROLE_* configuaration settings -->
<bean id="roleVoter" class="net.sf.acegisecurity.vote.RoleVoter"/>
<!-- An access decision voter that reads BANKSECURITY_CUSTOMER configuaration settings -->
<bean id="bankSecurityVoter" class="net.sf.acegisecurity.BankSecurityVoter"/>
<!-- An affirmative access decision manager -->
<bean id="accessDecisionManager" class="net.sf.acegisecurity.vote.AffirmativeBased">
<property name="allowIfAllAbstainDecisions"><value>false</value></property>
<property name="decisionVoters">
<list>
<ref bean="roleVoter"/>
<ref bean="bankSecurityVoter"/>
</list>
</property>
</bean>
<!-- ===================== SECURITY DEFINITIONS ======================= -->
<!-- No declaration for BankManager.getBankFundsUnderControl() makes it public -->
<bean id="bankManagerSecurity" class="net.sf.acegisecurity.SecurityInterceptor">
<property name="authenticationManager"><ref bean="authenticationManager"/></property>
<property name="accessDecisionManager"><ref bean="accessDecisionManager"/></property>
<property name="runAsManager"><ref bean="runAsManager"/></property>
<property name="methodDefinitionSource">
<value>
net.sf.acegisecurity.context.BankManager.delete*=ROLE_SUPERVISOR
net.sf.acegisecurity.context.BankManager.getBalance=ROLE_TELLER,ROLE_SUPERVISOR,BANKSECURITY_CUSTOMER
net.sf.acegisecurity.context.BankManager.loadAccount=ROLE_TELLER,ROLE_SUPERVISOR,BANKSECURITY_CUSTOMER
net.sf.acegisecurity.context.BankManager.saveAccount=ROLE_TELLER,ROLE_SUPERVISOR
net.sf.acegisecurity.context.BankManager.transferFunds=ROLE_SUPERVISOR
</value>
</property>
</bean>
<!-- ======================= BUSINESS DEFINITIONS ===================== -->
<bean id="bankManagerTarget" class="net.sf.acegisecurity.context.BankManagerImpl"/>
<!-- We don't include any context interceptor, although we should do so prior to the security interceptor -->
<bean id="bankManager" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces"><value>net.sf.acegisecurity.context.BankManager</value></property>
<property name="interceptorNames">
<list>
<value>bankManagerSecurity</value>
<value>bankManagerTarget</value>
</list>
</property>
</bean>
</beans>
@@ -0,0 +1,75 @@
/*
* The Acegi Security System for Spring is published under the terms
* of the Apache Software License.
*
* Visit http://acegisecurity.sourceforge.net for further details.
*/
package net.sf.acegisecurity.runas;
import net.sf.acegisecurity.context.Account;
import net.sf.acegisecurity.context.BankManager;
import org.springframework.beans.factory.InitializingBean;
/**
* Acts as the "public facade" to a <code>BankManager</code>.
*
* <P>
* The security configuration of this, the public facade, specifies authorities
* that should be held by the end user. The security configuration of the
* "backend", which is not accessible to the general public, specifies certain
* authorities that are granted by the RunAsManagerImpl.
* </p>
*
* @author Ben Alex
* @version $Id$
*/
public class BankManagerPublicFacade implements BankManager, InitializingBean {
//~ Instance fields ========================================================
private BankManager backend;
//~ Methods ================================================================
public void setBackend(BankManager backend) {
this.backend = backend;
}
public BankManager getBackend() {
return backend;
}
public float getBalance(Integer accountNumber) {
return backend.getBalance(accountNumber);
}
public float getBankFundsUnderControl() {
return backend.getBankFundsUnderControl();
}
public void afterPropertiesSet() throws Exception {
if (backend == null) {
throw new IllegalArgumentException(
"A backend BankManager implementation is required");
}
}
public void deleteAccount(Integer accountNumber) {
backend.deleteAccount(accountNumber);
}
public Account loadAccount(Integer accountNumber) {
return backend.loadAccount(accountNumber);
}
public void saveAccount(Account account) {
backend.saveAccount(account);
}
public void transferFunds(Integer fromAccountNumber,
Integer toAccountNumber, float amount) {
backend.transferFunds(fromAccountNumber, toAccountNumber, amount);
}
}
@@ -0,0 +1,106 @@
/*
* The Acegi Security System for Spring is published under the terms
* of the Apache Software License.
*
* Visit http://acegisecurity.sourceforge.net for further details.
*/
package net.sf.acegisecurity.runas;
import junit.framework.TestCase;
import net.sf.acegisecurity.AccessDeniedException;
import net.sf.acegisecurity.context.Account;
import net.sf.acegisecurity.context.BankManager;
import net.sf.acegisecurity.context.Context;
import net.sf.acegisecurity.context.ContextHolder;
import net.sf.acegisecurity.context.SecureContext;
import net.sf.acegisecurity.context.SecureContextImpl;
import net.sf.acegisecurity.providers.UsernamePasswordAuthenticationToken;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* Tests {@link RunAsManagerImpl}.
*
* @author Ben Alex
* @version $Id$
*/
public class RunAsTests extends TestCase {
//~ Instance fields ========================================================
private ClassPathXmlApplicationContext ctx;
//~ Constructors ===========================================================
public RunAsTests() {
super();
}
public RunAsTests(String arg0) {
super(arg0);
}
//~ Methods ================================================================
public final void setUp() throws Exception {
super.setUp();
ctx = new ClassPathXmlApplicationContext(
"/net/sf/acegisecurity/runas/applicationContext.xml");
}
public static void main(String[] args) {
junit.textui.TestRunner.run(RunAsTests.class);
}
public void testRunAs() throws Exception {
Account account = new Account(45, "someone");
BankManager bank = (BankManager) ctx.getBean("bankManager");
// Try as a user without access to the account
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("peter",
"opal");
SecureContext secureContext = new SecureContextImpl();
secureContext.setAuthentication(token);
ContextHolder.setContext((Context) secureContext);
try {
// NB: account number 45 != granted authority for account 77
bank.loadAccount(account.getId());
fail("Should have thrown an AccessDeniedException");
} catch (AccessDeniedException expected) {
assertTrue(true);
}
// Now try as user with access to account number 45
// Proves ROLE_RUN_AS_SERVER is being allocated
token = new UsernamePasswordAuthenticationToken("scott", "wombat");
secureContext.setAuthentication(token);
ContextHolder.setContext((Context) secureContext);
bank.loadAccount(account.getId());
assertTrue(true);
// Now try as user with ROLE_SUPERVISOR access to the account
// Proves ROLE_RUN_AS_SERVER is being allocated
token = new UsernamePasswordAuthenticationToken("marissa", "koala");
secureContext.setAuthentication(token);
ContextHolder.setContext((Context) secureContext);
bank.loadAccount(account.getId());
assertTrue(true);
// Now try to call a method that ROLE_RUN_AS_BACKEND not granted for
token = new UsernamePasswordAuthenticationToken("marissa", "koala");
secureContext.setAuthentication(token);
ContextHolder.setContext((Context) secureContext);
try {
bank.saveAccount(account);
fail("Should have thrown AccessDeniedException");
} catch (AccessDeniedException expected) {
assertTrue(true);
}
ContextHolder.setContext(null);
}
}

Some files were not shown because too many files have changed in this diff Show More