Moved to net.sf.acegisecurity.intercept.method.
This commit is contained in:
parent
738fd2161d
commit
3ece12c386
|
@ -1,158 +0,0 @@
|
||||||
/* Copyright 2004 Acegi Technology Pty Limited
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package 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.
|
|
||||||
*
|
|
||||||
* <P>
|
|
||||||
* This class will only detect those attributes which are defined for the:
|
|
||||||
*
|
|
||||||
* <ul>
|
|
||||||
* <li>
|
|
||||||
* The class-wide attributes defined for the intercepted class.
|
|
||||||
* </li>
|
|
||||||
* <li>
|
|
||||||
* The class-wide attributes defined for interfaces explicitly implemented by
|
|
||||||
* the intercepted class.
|
|
||||||
* </li>
|
|
||||||
* <li>
|
|
||||||
* The method-specific attributes defined for the intercepted method of the
|
|
||||||
* intercepted class.
|
|
||||||
* </li>
|
|
||||||
* <li>
|
|
||||||
* The method-specific attributes defined by any explicitly implemented
|
|
||||||
* interface if that interface contains a method signature matching that of
|
|
||||||
* the intercepted method.
|
|
||||||
* </li>
|
|
||||||
* </ul>
|
|
||||||
* </p>
|
|
||||||
*
|
|
||||||
* <P>
|
|
||||||
* Note that attributes defined against parent classes (either for their
|
|
||||||
* methods or interfaces) are not detected. The attributes must be defined
|
|
||||||
* against an explicit method or interface on the intercepted class.
|
|
||||||
* </p>
|
|
||||||
*
|
|
||||||
* <p>
|
|
||||||
* Attributes detected that do not implement {@link ConfigAttribute} will be
|
|
||||||
* ignored.
|
|
||||||
* </p>
|
|
||||||
*
|
|
||||||
* @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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,175 +0,0 @@
|
||||||
/* Copyright 2004 Acegi Technology Pty Limited
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package 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 {
|
|
||||||
private static final Log logger = LogFactory.getLog(MethodDefinitionMap.class);
|
|
||||||
|
|
||||||
/** Map from Method to ApplicationDefinition */
|
|
||||||
protected Map methodMap = new HashMap();
|
|
||||||
|
|
||||||
/** Map from Method to name pattern used for registration */
|
|
||||||
private Map nameMap = new HashMap();
|
|
||||||
|
|
||||||
public ConfigAttributeDefinition getAttributes(MethodInvocation invocation) {
|
|
||||||
return (ConfigAttributeDefinition) this.methodMap.get(invocation.getMethod());
|
|
||||||
}
|
|
||||||
|
|
||||||
public Iterator getConfigAttributeDefinitions() {
|
|
||||||
return methodMap.values().iterator();
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getMethodMapSize() {
|
|
||||||
return this.methodMap.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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;
|
|
||||||
|
|
||||||
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 (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 {
|
|
||||||
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())));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,52 +0,0 @@
|
||||||
/* Copyright 2004 Acegi Technology Pty Limited
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package 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();
|
|
||||||
}
|
|
|
@ -1,78 +0,0 @@
|
||||||
/* Copyright 2004 Acegi Technology Pty Limited
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package 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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,317 +0,0 @@
|
||||||
/* Copyright 2004 Acegi Technology Pty Limited
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package 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 {
|
|
||||||
if (logger.isDebugEnabled()) {
|
|
||||||
logger.debug("Intercepted request for method " + mi.getMethod());
|
|
||||||
}
|
|
||||||
|
|
||||||
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();
|
|
||||||
|
|
||||||
// We check for just the property we're interested in (we do
|
|
||||||
// not call Context.validate() like the ContextInterceptor)
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue