/*******************************************************************************
* Copyright (c) 2008, 2013 Spring IDE Developers
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Spring IDE Developers - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.security.core.aop.model.builder;
import java.util.ArrayList;
import java.util.List;
import org.aopalliance.intercept.MethodInvocation;
import org.eclipse.core.resources.IFile;
import org.eclipse.wst.xml.core.internal.provisional.document.IDOMDocument;
import org.eclipse.wst.xml.core.internal.provisional.document.IDOMNode;
import org.springframework.ide.eclipse.aop.core.internal.model.JavaAdvisorDefinition;
import org.springframework.ide.eclipse.aop.core.internal.model.builder.AbstractAspectDefinitionBuilder;
import org.springframework.ide.eclipse.aop.core.logging.AopLog;
import org.springframework.ide.eclipse.aop.core.model.IAopReference.ADVICE_TYPE;
import org.springframework.ide.eclipse.aop.core.model.IAspectDefinition;
import org.springframework.ide.eclipse.aop.core.model.builder.IAspectDefinitionBuilder;
import org.springframework.ide.eclipse.aop.core.model.builder.IDocumentFactory;
import org.springframework.ide.eclipse.core.java.IProjectClassLoaderSupport;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
/**
* {@link IAspectDefinitionBuilder} for Spring Security's global-method-security element.
*
* @author Christian Dupuis
* @author Martin Lippert
* @since 2.0.5
*/
@SuppressWarnings("restriction")
public class SecurityXmlAspectDefinitionBuilder extends AbstractAspectDefinitionBuilder implements
IAspectDefinitionBuilder {
private static final String EXPRESSION_ATTRIBUTE = "expression";
private static final String GLOBAL_METHOD_SECURITY_ELEMENT = "global-method-security";
private static final String JSR250_ANNOTATION_EXPRESSION = "(@within(javax.annotation.security.DenyAll) || @annotation(javax.annotation.security.DenyAll) || @within(javax.annotation.security.PermitAll) || @annotation(javax.annotation.security.PermitAll) || @within(javax.annotation.security.RolesAllowed) || @annotation(javax.annotation.security.RolesAllowed)) && execution(* *..*(..))";
private static final String METHOD_SECURITY_INTERCEPTOR_BEAN_ID = "_methodSecurityInterceptor";
private static final String METHOD_SECURITY_INTERCEPTOR_V2_CLASS = "org.springframework.security.intercept.method.aopalliance.MethodSecurityInterceptor";
private static final String METHOD_SECURITY_INTERCEPTOR_V3_CLASS = "org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor";
private static final String PROTECT_POINTCUT_ELEMENT = "protect-pointcut";
private static final String SECURED_ANNOTATION_EXPRESSION = "(@within($CLASS) || @annotation($CLASS)) && execution(* *..*(..))";
private static final String SECURED_ANNOTATION_V2_CLASS = "org.springframework.security.annotation.Secured";
private static final String SECURED_ANNOTATION_V3_CLASS = "org.springframework.security.access.annotation.Secured";
private static final String SECURITY_NAMESPACE_URI = "http://www.springframework.org/schema/security";
public void buildAspectDefinitions(List<IAspectDefinition> aspectInfos, IFile file,
IProjectClassLoaderSupport classLoaderSupport, IDocumentFactory factory) {
if (file.getFileExtension() != null && file.getFileExtension().equals("xml")) {
parseGlobalMethodSecurityElement(factory.createDocument(file), file, aspectInfos, classLoaderSupport);
}
}
private void addAspectDefinition(IAspectDefinition info, List<IAspectDefinition> aspectInfos) {
AopLog.log(AopLog.BUILDER_MESSAGES, info.toString());
aspectInfos.add(info);
}
private void parseGlobalMethodSecurityElement(final IDOMDocument document, IFile file,
final List<IAspectDefinition> aspectInfos, IProjectClassLoaderSupport classLoaderSupport) {
if (document == null || document.getStructuredDocument() == null) {
return;
}
NodeList list = document.getDocumentElement().getElementsByTagNameNS(SECURITY_NAMESPACE_URI,
GLOBAL_METHOD_SECURITY_ELEMENT);
if (list.getLength() == 0) {
return;
}
// Check if Spring Security 2.0 or 3.0 is being used?
boolean present20 = ClassUtils.isPresent(METHOD_SECURITY_INTERCEPTOR_V2_CLASS, classLoaderSupport
.getProjectClassLoader());
boolean present30 = ClassUtils.isPresent(METHOD_SECURITY_INTERCEPTOR_V3_CLASS, classLoaderSupport
.getProjectClassLoader());
if (present30 && present20) {
// both versions are on the classpath -> what to do?
return;
}
else if (!present20 && !present30) {
// no Spring Security on classpath -> return
return;
}
for (int i = 0; i < list.getLength(); i++) {
List<IAspectDefinition> aspectDefinitions = new ArrayList<IAspectDefinition>();
Node node = list.item(i);
// TODO CD Spring Security currently only correctly supports annotations on the interface
// which the following can't support as per AspectJ semantics. Waiting to Spring
// Security to fix the problem.
String securedAnnotations = getAttribute(node, "secured-annotations");
String jsr250Annoatations = getAttribute(node, "jsr250-annotations");
if ("enabled".equals(securedAnnotations)) {
createAnnotationInfo(file, aspectInfos, node, SECURED_ANNOTATION_EXPRESSION.replace("$CLASS",
(present30 ? SECURED_ANNOTATION_V3_CLASS : SECURED_ANNOTATION_V2_CLASS)), present30);
}
if ("enabled".equals(jsr250Annoatations)) {
// Check if the JSR250 annotation are available
boolean presentDenyAll = ClassUtils.isPresent("javax.annotation.security.DenyAll", classLoaderSupport
.getProjectClassLoader());
boolean presentPermitAll = ClassUtils.isPresent("javax.annotation.security.PermitAll", classLoaderSupport
.getProjectClassLoader());
boolean presentRolesAllowed = ClassUtils.isPresent("javax.annotation.security.RolesAllowed", classLoaderSupport
.getProjectClassLoader());
if (presentDenyAll && presentPermitAll && presentRolesAllowed) {
createAnnotationInfo(file, aspectInfos, node, JSR250_ANNOTATION_EXPRESSION, present30);
}
}
NodeList children = node.getChildNodes();
for (int j = 0; j < children.getLength(); j++) {
Node child = children.item(j);
if (PROTECT_POINTCUT_ELEMENT.equals(child.getLocalName())) {
parseProtectPointcutElement(file, child, aspectDefinitions, present30);
}
}
aspectInfos.addAll(aspectDefinitions);
}
}
private void createAnnotationInfo(IFile file, final List<IAspectDefinition> aspectInfos, Node node,
String expression, boolean present30) {
JavaAdvisorDefinition info1 = prepareJavaAdvisorDefinition(file, node, expression, present30);
info1.setProxyTargetClass(true);
addAspectDefinition(info1, aspectInfos);
JavaAdvisorDefinition info2 = prepareJavaAdvisorDefinition(file, node, expression, present30);
info2.setProxyTargetClass(false);
addAspectDefinition(info2, aspectInfos);
}
private void parseProtectPointcutElement(IFile file, Node protectPointcutNode, List<IAspectDefinition> aspectInfos,
boolean present30) {
String pointcutExpression = getAttribute(protectPointcutNode, EXPRESSION_ATTRIBUTE);
if (StringUtils.hasText(pointcutExpression)) {
JavaAdvisorDefinition info = prepareJavaAdvisorDefinition(file, protectPointcutNode, pointcutExpression,
present30);
addAspectDefinition(info, aspectInfos);
}
}
private JavaAdvisorDefinition prepareJavaAdvisorDefinition(IFile file, Node aspectNode, String pointcutExpression,
boolean present30) {
JavaAdvisorDefinition info = new JavaAdvisorDefinition();
extractLineNumbers(info, (IDOMNode) aspectNode);
info.setPointcutExpression(pointcutExpression);
info.setAspectClassName((present30 ? METHOD_SECURITY_INTERCEPTOR_V3_CLASS
: METHOD_SECURITY_INTERCEPTOR_V2_CLASS));
info.setAspectName(METHOD_SECURITY_INTERCEPTOR_BEAN_ID);
info.setType(ADVICE_TYPE.AROUND);
info.setAdviceMethodName("invoke");
info.setAdviceMethodParameterTypes(new String[] { MethodInvocation.class.getName() });
info.setResource(file);
return info;
}
}