/*******************************************************************************
* Copyright (c) 2012 Red Hat, Inc.
* Distributed under license by Red Hat, Inc. All rights reserved.
* This program is 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:
* Red Hat, Inc. - initial API and implementation
******************************************************************************/
package org.jboss.tools.cdi.deltaspike.core;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.JavaModelException;
import org.jboss.tools.cdi.core.CDICoreNature;
import org.jboss.tools.cdi.core.IRootDefinitionContext;
import org.jboss.tools.cdi.core.IStereotypeDeclaration;
import org.jboss.tools.cdi.core.extension.ICDIExtension;
import org.jboss.tools.cdi.core.extension.feature.IBuildParticipantFeature;
import org.jboss.tools.cdi.core.extension.feature.IProcessAnnotatedMemberFeature;
import org.jboss.tools.cdi.core.extension.feature.IProcessAnnotatedTypeFeature;
import org.jboss.tools.cdi.core.extension.feature.IValidatorFeature;
import org.jboss.tools.cdi.deltaspike.core.validation.DeltaspikeValidationMessages;
import org.jboss.tools.cdi.internal.core.impl.CDIProject;
import org.jboss.tools.cdi.internal.core.impl.definition.AbstractMemberDefinition;
import org.jboss.tools.cdi.internal.core.impl.definition.AnnotationDefinition;
import org.jboss.tools.cdi.internal.core.impl.definition.BeanMemberDefinition;
import org.jboss.tools.cdi.internal.core.impl.definition.MethodDefinition;
import org.jboss.tools.cdi.internal.core.impl.definition.TypeDefinition;
import org.jboss.tools.cdi.internal.core.scanner.FileSet;
import org.jboss.tools.cdi.internal.core.validation.CDICoreValidator;
import org.jboss.tools.common.java.IAnnotated;
import org.jboss.tools.common.java.IAnnotationDeclaration;
import org.jboss.tools.common.model.XModelObject;
import org.jboss.tools.common.preferences.SeverityPreferences;
/**
* Runtime
* org.apache.deltaspike.security.impl.authorization.SecurityExtension
*
* @author Viacheslav Kabanovich
*/
public class DeltaspikeSecurityExtension implements ICDIExtension, IBuildParticipantFeature, IProcessAnnotatedTypeFeature, IProcessAnnotatedMemberFeature, IValidatorFeature, DeltaspikeConstants {
public static String ID = "org.apache.deltaspike.security.impl.extension.SecurityExtension"; //$NON-NLS-1$
DeltaspikeSecurityDefinitionContext context = new DeltaspikeSecurityDefinitionContext();
public static DeltaspikeSecurityExtension getExtension(CDICoreNature project) {
return (DeltaspikeSecurityExtension)project.getExtensionManager().getExtensionByRuntime(ID);
}
@Override
public DeltaspikeSecurityDefinitionContext getContext() {
return context;
}
@Override
public void beginVisiting() {}
@Override
public void visitJar(IPath path, IPackageFragmentRoot root, XModelObject beansXML) {}
@Override
public void visit(IFile file, IPath src, IPath webinf) {}
@Override
public void buildDefinitions() {}
@Override
public void buildDefinitions(FileSet fileSet) {}
@Override
public void buildBeans(CDIProject target) {}
@Override
public void processAnnotatedMember(BeanMemberDefinition memberDefinition,
IRootDefinitionContext context) {
if(!(memberDefinition instanceof MethodDefinition)) {
return;
}
if(memberDefinition.isAnnotationPresent(SECURES_ANNOTATION_TYPE_NAME)) {
MethodDefinition method = (MethodDefinition)memberDefinition;
method.setCDIAnnotated(true);
DeltaspikeAuthorityMethod authorizer = new DeltaspikeAuthorityMethod(method);
DeltaspikeSecurityDefinitionContext contextCopy = ((DeltaspikeSecurityDefinitionContext)this.context.getWorkingCopy());
contextCopy.allAuthorizerMethods.getAuthorizerMembers().add(authorizer);
List<SecurityBindingDeclaration> ds = findAnnotationAnnotatedWithSecurityBindingType(memberDefinition, contextCopy.getRootContext());
for (SecurityBindingDeclaration d: ds) {
DeltaspikeSecurityBindingConfiguration c = contextCopy.getConfiguration(d.getBinding().getTypeName());
authorizer.addBinding(d, c);
c.getAuthorizerMembers().add(authorizer);
addToDependencies(c, authorizer.getMethod(), context);
}
} else {
addSecurityMember(memberDefinition, context);
}
}
@Override
public void processAnnotatedType(TypeDefinition typeDefinition,
IRootDefinitionContext context) {
addSecurityMember(typeDefinition, context);
}
private void addSecurityMember(AbstractMemberDefinition def, IRootDefinitionContext context) {
List<SecurityBindingDeclaration> ds = findAnnotationAnnotatedWithSecurityBindingType(def, context);
for (SecurityBindingDeclaration d: ds) {
addBoundMember(def, d, context);
}
}
private void addBoundMember(AbstractMemberDefinition def, SecurityBindingDeclaration d, IRootDefinitionContext context) {
String securityBindingType = d.getBinding().getTypeName();
if(def instanceof MethodDefinition) {
((MethodDefinition)def).setCDIAnnotated(true);
}
DeltaspikeSecurityBindingConfiguration c = ((DeltaspikeSecurityDefinitionContext)this.context.getWorkingCopy()).getConfiguration(securityBindingType);
c.getBoundMembers().put(def, d);
addToDependencies(c, def, context);
}
private void addToDependencies(DeltaspikeSecurityBindingConfiguration c, AbstractMemberDefinition def, IRootDefinitionContext context) {
IResource r = def.getResource();
if(r != null && r.exists() && !c.getInvolvedTypes().contains(r.getFullPath())) {
IPath newPath = r.getFullPath();
Set<IPath> ps = c.getInvolvedTypes();
for (IPath p: ps) {
context.addDependency(p, newPath);
context.addDependency(newPath, p);
}
ps.add(newPath);
}
}
static List<SecurityBindingDeclaration> EMPTY = Collections.<SecurityBindingDeclaration>emptyList();
private List<SecurityBindingDeclaration> findAnnotationAnnotatedWithSecurityBindingType(AbstractMemberDefinition m, IRootDefinitionContext context) {
List<SecurityBindingDeclaration> result = null;
List<IAnnotationDeclaration> ds = m.getAnnotations();
for (IAnnotationDeclaration d: ds) {
if(d instanceof IStereotypeDeclaration) {
AnnotationDefinition t = context.getAnnotation(d.getTypeName());
if(t != null) {
List<IAnnotationDeclaration> ds1 = findSecurityBindingAnnotations(t, null, context);
if(ds1 != null) {
if(result == null) {
result = new ArrayList<SecurityBindingDeclaration>();
}
for (IAnnotationDeclaration d1: ds1) {
result.add(new SecurityBindingDeclaration(d, d1));
}
}
}
} else if(d.getTypeName() != null) {
AnnotationDefinition a = context.getAnnotation(d.getTypeName());
if(a != null && a.isAnnotationPresent(SECURITY_BINDING_ANNOTATION_TYPE_NAME)) {
if(result == null) {
result = new ArrayList<SecurityBindingDeclaration>();
}
result.add(new SecurityBindingDeclaration(d, d));
} else if(a != null && d instanceof IStereotypeDeclaration) {
List<IAnnotationDeclaration> ds1 = findSecurityBindingAnnotations(a, null, context);
if(ds1 != null) {
if(result == null) {
result = new ArrayList<SecurityBindingDeclaration>();
}
for (IAnnotationDeclaration d1: ds1) {
result.add(new SecurityBindingDeclaration(d, d1));
}
}
}
}
}
return result == null ? EMPTY : result;
}
private List<IAnnotationDeclaration> findSecurityBindingAnnotations(IAnnotated s, List<IAnnotationDeclaration> result, IRootDefinitionContext context) {
List<IAnnotationDeclaration> ds = s.getAnnotations();
for (IAnnotationDeclaration d: ds) {
if(d.getTypeName() != null) {
AnnotationDefinition a = context.getAnnotation(d.getTypeName());
if(a != null && a.isAnnotationPresent(SECURITY_BINDING_ANNOTATION_TYPE_NAME)) {
if(result == null) {
result = new ArrayList<IAnnotationDeclaration>();
}
result.add(d);
} else if(a != null && d instanceof IStereotypeDeclaration) {
List<IAnnotationDeclaration> ds1 = findSecurityBindingAnnotations(a, null, context);
if(ds1 != null) {
if(result == null) {
result = new ArrayList<IAnnotationDeclaration>();
}
result.addAll(ds1);
}
}
}
}
return result;
}
@Override
public void validateResource(IFile file, CDICoreValidator validator) {
Set<DeltaspikeAuthorityMethod> authorizers = context.getAuthorityMethods(file.getFullPath());
for (DeltaspikeAuthorityMethod authorizer: authorizers) {
IAnnotationDeclaration a = authorizer.getMethod().getAnnotation(SECURES_ANNOTATION_TYPE_NAME);
if(authorizer.getBindings().isEmpty()) {
validator.addError(DeltaspikeValidationMessages.INVALID_AUTHORIZER_NO_BINDINGS,
DeltaspikeSeverityPreferences.INVALID_AUTHORIZER,
new String[]{authorizer.getMethod().getMethod().getElementName()},
a, file);
} else if(authorizer.getBindings().size() > 1) {
validator.addError(DeltaspikeValidationMessages.INVALID_AUTHORIZER_MULTIPLE_BINDINGS,
DeltaspikeSeverityPreferences.INVALID_AUTHORIZER,
new String[]{authorizer.getMethod().getMethod().getElementName()},
a, file);
}
try {
String returnType = authorizer.getMethod().getMethod().getReturnType();
if(!"Z".equals(returnType)) { //$NON-NLS-1$
validator.addError(DeltaspikeValidationMessages.INVALID_AUTHORIZER_NOT_BOOLEAN,
DeltaspikeSeverityPreferences.INVALID_AUTHORIZER,
new String[]{authorizer.getMethod().getMethod().getElementName()},
a, file);
}
} catch (JavaModelException e) {
DeltaspikeCorePlugin.getDefault().logError(e);
}
}
Set<DeltaspikeSecurityExtension> parents = null;
for (DeltaspikeSecurityBindingConfiguration c: context.getConfigurations().values()) {
if(c.getInvolvedTypes().contains(file.getFullPath())) {
if(parents == null) {
parents = getParents(getContext().getRootContext().getProject());
}
Set<DeltaspikeAuthorityMethod> authorizers2 = collectAuthorizerMethods(parents, c.getSecurityBindingTypeName());
authorizers2.addAll(c.getAuthorizerMembers());
Set<String> x = new HashSet<String>();
Iterator<DeltaspikeAuthorityMethod> it = authorizers2.iterator();
while(it.hasNext()) {
DeltaspikeAuthorityMethod a = it.next();
String key = a.getKey();
if(x.contains(key)) {
it.remove();
} else {
x.add(key);
}
}
Map<AbstractMemberDefinition, SecurityBindingDeclaration> bound = c.getBoundMembers();
for (AbstractMemberDefinition d: bound.keySet()) {
String name = d instanceof MethodDefinition ? ((MethodDefinition)d).getMethod().getElementName()
: d instanceof TypeDefinition ? ((TypeDefinition)d).getQualifiedName() : "";
if(file.getFullPath().equals(d.getTypeDefinition().getType().getPath())) {
SecurityBindingDeclaration dc = bound.get(d);
int k = 0;
for (DeltaspikeAuthorityMethod a: authorizers2) {
try {
if(a.isMatching(dc.getBinding())) k++;
} catch (CoreException e) {
DeltaspikeCorePlugin.getDefault().logError(e);
}
}
if(k == 0) {
validator.addError(DeltaspikeValidationMessages.UNRESOLVED_AUTHORIZER,
DeltaspikeSeverityPreferences.UNRESOLVED_AUTHORIZER,
new String[]{dc.getBinding().getTypeName(), name},
dc.getDeclaration(), file);
} else if(k > 1) {
validator.addError(DeltaspikeValidationMessages.AMBIGUOUS_AUTHORIZER,
DeltaspikeSeverityPreferences.AMBIGUOUS_AUTHORIZER,
new String[]{dc.getBinding().getTypeName(), name},
dc.getDeclaration(), file);
}
}
}
}
}
//
}
@Override
public SeverityPreferences getSeverityPreferences() {
return DeltaspikeSeverityPreferences.getInstance();
}
public static Set<DeltaspikeSecurityExtension> getParents(CDICoreNature nature) {
Set<CDICoreNature> ps = nature.getCDIProjects(true);
Set<DeltaspikeSecurityExtension> parents = new HashSet<DeltaspikeSecurityExtension>();
for (CDICoreNature p: ps) {
DeltaspikeSecurityExtension ext = DeltaspikeSecurityExtension.getExtension(p);
if(ext != null) parents.add(ext);
}
return parents;
}
public static Set<DeltaspikeAuthorityMethod> collectAuthorizerMethods(Set<DeltaspikeSecurityExtension> parents, String securityBindingTypeName) {
Set<DeltaspikeAuthorityMethod> result = new HashSet<DeltaspikeAuthorityMethod>();
for (DeltaspikeSecurityExtension ext: parents) {
DeltaspikeSecurityBindingConfiguration c = ext.getContext().getConfiguration(securityBindingTypeName);
if(c != null) {
result.addAll(c.getAuthorizerMembers());
}
}
return result;
}
}