/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.deltaspike.security.impl.extension; import org.apache.deltaspike.core.spi.activation.Deactivatable; import org.apache.deltaspike.core.util.ClassDeactivationUtils; import org.apache.deltaspike.core.util.ParentExtensionStorage; import org.apache.deltaspike.core.util.metadata.builder.AnnotatedTypeBuilder; import org.apache.deltaspike.security.api.authorization.Secures; import org.apache.deltaspike.security.api.authorization.SecurityDefinitionException; import org.apache.deltaspike.security.impl.util.SecurityUtils; import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.util.HashSet; import java.util.List; import java.util.Set; import javax.enterprise.event.Observes; import javax.enterprise.inject.spi.AfterBeanDiscovery; import javax.enterprise.inject.spi.AnnotatedMethod; import javax.enterprise.inject.spi.AnnotatedParameter; import javax.enterprise.inject.spi.AnnotatedType; import javax.enterprise.inject.spi.BeanManager; import javax.enterprise.inject.spi.BeforeBeanDiscovery; import javax.enterprise.inject.spi.Extension; import javax.enterprise.inject.spi.ProcessAnnotatedType; /** * Extension for processing typesafe security annotations */ public class SecurityExtension implements Extension, Deactivatable { private static final SecurityInterceptorBinding INTERCEPTOR_BINDING = new SecurityInterceptorBindingLiteral(); private SecurityMetaDataStorage securityMetaDataStorage; private Boolean isActivated = null; protected void init(@Observes BeforeBeanDiscovery beforeBeanDiscovery) { isActivated = ClassDeactivationUtils.isActivated(getClass()); securityMetaDataStorage = new SecurityMetaDataStorage(); ParentExtensionStorage.addExtension(this); } //workaround for OWB public SecurityMetaDataStorage getMetaDataStorage() { return securityMetaDataStorage; } /** * Handles @Secured beans */ public <X> void processAnnotatedType(@Observes ProcessAnnotatedType<X> event) { if (!isActivated) { return; } AnnotatedTypeBuilder<X> builder = null; AnnotatedType<X> type = event.getAnnotatedType(); boolean isSecured = false; // Add the security interceptor to the class if the class is annotated // with a security binding type for (final Annotation annotation : type.getAnnotations()) { if (SecurityUtils.isMetaAnnotatedWithSecurityBindingType(annotation)) { builder = new AnnotatedTypeBuilder<X>().readFromType(type); builder.addToClass(INTERCEPTOR_BINDING); getMetaDataStorage().addSecuredType(type); isSecured = true; break; } } // If the class isn't annotated with a security binding type, check if // any of its methods are, and if so, add the security interceptor to the // method if (!isSecured) { for (final AnnotatedMethod<? super X> m : type.getMethods()) { if (m.isAnnotationPresent(Secures.class)) { registerAuthorizer(m); continue; } for (final Annotation annotation : m.getAnnotations()) { if (SecurityUtils.isMetaAnnotatedWithSecurityBindingType(annotation)) { if (builder == null) { builder = new AnnotatedTypeBuilder<X>().readFromType(type); } builder.addToMethod(m, INTERCEPTOR_BINDING); getMetaDataStorage().addSecuredMethod(m); break; } } } } if (builder != null) { event.setAnnotatedType(builder.create()); } } public void validateBindings(@Observes AfterBeanDiscovery event, BeanManager beanManager) { if (!isActivated) { return; } SecurityMetaDataStorage metaDataStorage = getMetaDataStorage(); SecurityExtension parentExtension = ParentExtensionStorage.getParentExtension(this); if (parentExtension != null) { // also add the authorizers from the parent extension Set<Authorizer> parentAuthorizers = parentExtension.getMetaDataStorage().getAuthorizers(); for (Authorizer parentAuthorizer : parentAuthorizers) { metaDataStorage.addAuthorizer(parentAuthorizer); } } metaDataStorage.registerSecuredMethods(); for (final AnnotatedMethod<?> method : metaDataStorage.getSecuredMethods()) { // Here we simply want to validate that each method that is annotated with // one or more security bindings has a valid authorizer for each binding Class<?> targetClass = method.getDeclaringType().getJavaClass(); Method targetMethod = method.getJavaMember(); for (final Annotation annotation : SecurityUtils.getSecurityBindingTypes(targetClass, targetMethod)) { boolean found = false; Set<AuthorizationParameter> authorizationParameters = new HashSet<AuthorizationParameter>(); for (AnnotatedParameter<?> parameter : (List<AnnotatedParameter<?>>) (List<?>) method.getParameters()) { Set<Annotation> securityParameterBindings = null; for (Annotation a : parameter.getAnnotations()) { if (SecurityUtils.isMetaAnnotatedWithSecurityParameterBinding(a)) { if (securityParameterBindings == null) { securityParameterBindings = new HashSet<Annotation>(); } securityParameterBindings.add(a); } } if (securityParameterBindings != null) { AuthorizationParameter authorizationParameter = new AuthorizationParameter(parameter.getBaseType(), securityParameterBindings); authorizationParameters.add(authorizationParameter); } } // Validate the authorizer for (Authorizer auth : metaDataStorage.getAuthorizers()) { if (auth.matchesBindings(annotation, authorizationParameters, targetMethod.getReturnType())) { found = true; break; } } if (!found) { event.addDefinitionError(new SecurityDefinitionException("Secured type " + method.getDeclaringType().getJavaClass().getName() + " has no matching authorizer method for security binding @" + annotation.annotationType().getName())); } } for (final Annotation annotation : method.getAnnotations()) { if (SecurityUtils.isMetaAnnotatedWithSecurityBindingType(annotation)) { metaDataStorage.registerSecuredMethod(targetClass, targetMethod); break; } } } // Clear securedTypes, we don't require it any more metaDataStorage.resetSecuredMethods(); } /** * Registers the specified authorizer method (i.e. a method annotated with * the @Secures annotation) * * @throws SecurityDefinitionException */ private void registerAuthorizer(AnnotatedMethod<?> annotatedMethod) { if (!annotatedMethod.getJavaMember().getReturnType().equals(Boolean.class) && !annotatedMethod.getJavaMember().getReturnType().equals(Boolean.TYPE)) { throw new SecurityDefinitionException("Invalid authorizer method [" + annotatedMethod.getJavaMember().getDeclaringClass().getName() + "." + annotatedMethod.getJavaMember().getName() + "] - does not return a boolean."); } // Locate the binding type Annotation binding = null; for (Annotation annotation : annotatedMethod.getAnnotations()) { if (SecurityUtils.isMetaAnnotatedWithSecurityBindingType(annotation)) { if (binding != null) { throw new SecurityDefinitionException("Invalid authorizer method [" + annotatedMethod.getJavaMember().getDeclaringClass().getName() + "." + annotatedMethod.getJavaMember().getName() + "] - declares multiple security binding types"); } binding = annotation; } } Authorizer authorizer = new Authorizer(binding, annotatedMethod); getMetaDataStorage().addAuthorizer(authorizer); } }