/* * Password Management Servlets (PWM) * http://www.pwm-project.org * * Copyright (c) 2006-2009 Novell, Inc. * Copyright (c) 2009-2017 The PWM Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package password.pwm.http.servlet; import org.junit.Assert; import org.junit.Test; import org.reflections.Reflections; import org.reflections.scanners.FieldAnnotationsScanner; import org.reflections.scanners.SubTypesScanner; import org.reflections.scanners.TypeAnnotationsScanner; import org.reflections.util.ClasspathHelper; import org.reflections.util.ConfigurationBuilder; import password.pwm.http.ProcessStatus; import password.pwm.http.PwmRequest; import password.pwm.util.java.JavaHelper; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; public class ControlledPwmServletTest { @Test public void testProcess() throws IllegalAccessException, InstantiationException { final Map<Class<? extends ControlledPwmServlet>,Map<String,Method>> dataMap = getClassAndMethods(); for (final Class<? extends ControlledPwmServlet> controlledPwmServlet : dataMap.keySet()) { final Class<? extends AbstractPwmServlet.ProcessAction> processActionsClass = controlledPwmServlet.newInstance().getProcessActionsClass(); if (!processActionsClass.isEnum()) { Assert.fail(controlledPwmServlet.getName() + " process action class must be an enum"); } } } @Test public void testActionHandlerReturnTypes() throws IllegalAccessException, InstantiationException { final Map<Class<? extends ControlledPwmServlet>,Map<String,Method>> dataMap = getClassAndMethods(); for (final Class<? extends ControlledPwmServlet> controlledPwmServlet : dataMap.keySet()) { final String servletName = controlledPwmServlet.getName(); for (final String methodName : dataMap.get(controlledPwmServlet).keySet()) { final Method method = dataMap.get(controlledPwmServlet).get(methodName); if (method.getReturnType() != ProcessStatus.class) { Assert.fail(servletName + ":" + method.getName() + " must have return type of " + ProcessStatus.class.getName()); } } } } @Test public void testActionHandlerParameters() throws IllegalAccessException, InstantiationException { final Map<Class<? extends ControlledPwmServlet>,Map<String,Method>> dataMap = getClassAndMethods(); for (final Class<? extends ControlledPwmServlet> controlledPwmServlet : dataMap.keySet()) { final String servletName = controlledPwmServlet.getName(); for (final String methodName : dataMap.get(controlledPwmServlet).keySet()) { final Method method = dataMap.get(controlledPwmServlet).get(methodName); final Class[] returnTypes = method.getParameterTypes(); if (returnTypes.length != 1) { Assert.fail(servletName + ":" + method.getName() + " must have exactly one parameter"); } if (!returnTypes[0].equals(PwmRequest.class)) { Assert.fail(servletName + ":" + method.getName() + " must have exactly one parameter of type " + PwmRequest.class.getName()); } } } } @Test public void testActionHandlerMethodNaming() throws IllegalAccessException, InstantiationException { final Map<Class<? extends ControlledPwmServlet>,Map<String,Method>> dataMap = getClassAndMethods(); for (final Class<? extends ControlledPwmServlet> controlledPwmServlet : dataMap.keySet()) { final String servletName = controlledPwmServlet.getName(); for (final Method method : JavaHelper.getAllMethodsForClass(controlledPwmServlet)) { final String methodName = method.getName(); final ControlledPwmServlet.ActionHandler actionHandler = method.getAnnotation(ControlledPwmServlet.ActionHandler.class); if (actionHandler != null) { final String actionName = actionHandler.action(); if (!methodName.toLowerCase().contains(actionName.toLowerCase())) { Assert.fail("method " + servletName + ":" + methodName + " must have the ActionHandler name '" + actionName + "' as part of the method name."); } } } } } @Test public void testActionHandlersExistence() throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException { final Map<Class<? extends ControlledPwmServlet>,Map<String,Method>> dataMap = getClassAndMethods(); for (final Class<? extends ControlledPwmServlet> controlledPwmServlet : dataMap.keySet()) { final String servletName = controlledPwmServlet.getName(); final Class<? extends AbstractPwmServlet.ProcessAction> processActionsClass = controlledPwmServlet.newInstance().getProcessActionsClass(); final List<String> names = new ArrayList<>(); for (Object enumObject : processActionsClass.getEnumConstants()) { names.add(((Enum)enumObject).name()); } { final Collection<String> missingActionHandlers = new HashSet<>(names); missingActionHandlers.removeAll(dataMap.get(controlledPwmServlet).keySet()); if (!missingActionHandlers.isEmpty()) { Assert.fail(servletName + " does not have an action handler for action " + missingActionHandlers.iterator().next()); } } { final Collection<String> superflousActionHandlers = new HashSet<>(dataMap.get(controlledPwmServlet).keySet()); superflousActionHandlers.removeAll(names); if (!superflousActionHandlers.isEmpty()) { Assert.fail(servletName + " has an action handler for action " + superflousActionHandlers.iterator().next() + " but no such ProcessAction exists"); } } } } private Map<Class<? extends ControlledPwmServlet>,Map<String,Method>> getClassAndMethods() { Reflections reflections = new Reflections(new ConfigurationBuilder() .setUrls(ClasspathHelper.forPackage("password.pwm")) .setScanners(new SubTypesScanner(), new TypeAnnotationsScanner(), new FieldAnnotationsScanner() )); new Reflections("password.pwm"); Set<Class<? extends ControlledPwmServlet>> classes = reflections.getSubTypesOf(ControlledPwmServlet.class); final Map<Class<? extends ControlledPwmServlet>,Map<String,Method>> returnMap = new HashMap<>(); for (final Class<? extends ControlledPwmServlet> controlledPwmServlet : classes) { if (!Modifier.isAbstract(controlledPwmServlet.getModifiers())) { final Map<String, Method> annotatedMethods = new HashMap<>(); for (Method method : JavaHelper.getAllMethodsForClass(controlledPwmServlet)) { if (method.getAnnotation(ControlledPwmServlet.ActionHandler.class) != null) { final String actionName = method.getAnnotation(ControlledPwmServlet.ActionHandler.class).action(); annotatedMethods.put(actionName, method); } } returnMap.put(controlledPwmServlet, Collections.unmodifiableMap(annotatedMethods)); } } return Collections.unmodifiableMap(returnMap); } }