/** * Copyright (c) 2009 - 2012 Red Hat, Inc. * * This software is licensed to you under the GNU General Public License, * version 2 (GPLv2). There is NO WARRANTY for this software, express or * implied, including the implied warranties of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2 * along with this software; if not, see * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. * * Red Hat trademarks are not licensed under GPLv2. No permission is * granted to use or replicate Red Hat trademarks that are incorporated * in this software or its documentation. */ package org.candlepin.test; import org.candlepin.auth.Access; import org.candlepin.auth.Principal; import org.candlepin.auth.Verify; import org.candlepin.common.auth.SecurityHole; import org.candlepin.resteasy.ResourceLocatorMap; import org.candlepin.resteasy.filter.StoreFactory; import org.candlepin.resteasy.filter.VerifyAuthorizationFilter; import com.google.inject.Inject; import com.google.inject.Provider; import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; import org.xnap.commons.i18n.I18n; import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.util.LinkedHashMap; import java.util.Map; /** * This is a testing class that converts our Authorization interceptor * into a method interceptor so that we can use it during testing without having to * set up an entire RestEasy environment. * * Since the VerifyAuthorizationFilter class requires an Injector and method interceptors * are created in Guice modules, we encounter a problem. It's not * really possible to grab your own injector within your module (see * http://stackoverflow.com/questions/15989991/accessing-guice-injector-in-its-module ). * So we have to use deferred binding via the requestInjection() method and this operates * on field injection. Instead of messing with the internals of VerifyAuthorizationFilter to * allow this, we have this Factory class which creates the interceptor at method * invocation time after all our dependencies have been injected. */ public class VerifyAuthorizationFilterFactory implements MethodInterceptor { @Inject private StoreFactory storeFactory; @Inject private Provider<I18n> i18nProvider; @Inject private Provider<Principal> principalProvider; @Inject private ResourceLocatorMap resourceMap; @Override public Object invoke(MethodInvocation invocation) throws Throwable { AuthorizationMethodInterceptor methodInterceptor = new AuthorizationMethodInterceptor( i18nProvider, storeFactory, resourceMap); return methodInterceptor.invoke(invocation); } public class AuthorizationMethodInterceptor extends VerifyAuthorizationFilter implements MethodInterceptor { public AuthorizationMethodInterceptor(Provider<I18n> i18nProvider, StoreFactory storeFactory, ResourceLocatorMap resourceMap) { super(i18nProvider, storeFactory, resourceMap); } @Override public Object invoke(MethodInvocation invocation) throws Throwable { Principal principal = principalProvider.get(); /* In real life, the AuthorizationFeature DynamicFeature decides if the * SecurityHole exception applies to a method. */ if (invocation.getMethod().isAnnotationPresent(SecurityHole.class)) { return invocation.proceed(); } Method method = invocation.getMethod(); Access defaultAccess = getDefaultAccess(method); Map<Verify, Object> argMap = getArguments(invocation); /* Under normal circumstances the AuthorizationFeature has already determined * that VerifyAuthorizationFilter doesn't apply to superadmin only methods. */ if (argMap.isEmpty()) { if (principal.hasFullAccess()) { return invocation.proceed(); } else { denyAccess(principal, method); } } if (!hasAccess(argMap, principal, defaultAccess)) { denyAccess(principal, method); } return invocation.proceed(); } private Map<Verify, Object> getArguments(MethodInvocation invocation) { Object[] args = invocation.getArguments(); Annotation[][] allAnnotations = invocation.getMethod().getParameterAnnotations(); Map<Verify, Object> argMap = new LinkedHashMap<Verify, Object>(); // Any occurrence of the Verify annotation means the method is not superadmin exclusive. for (int i = 0; i < allAnnotations.length; i++) { for (Annotation a : allAnnotations[i]) { if (a instanceof Verify) { Verify v = (Verify) a; if (!v.nullable() && args[i] == null) { throw new IllegalStateException( "Null passed to a non-nullable Verify annotation."); } else { argMap.put(v, args[i]); } } } } return argMap; } } }