Adds annotation to support AllowedMethods
Project: http://git-wip-us.apache.org/repos/asf/struts/repo Commit: http://git-wip-us.apache.org/repos/asf/struts/commit/ab5fb27d Tree: http://git-wip-us.apache.org/repos/asf/struts/tree/ab5fb27d Diff: http://git-wip-us.apache.org/repos/asf/struts/diff/ab5fb27d Branch: refs/heads/master Commit: ab5fb27d368f8857e137bc3a319c21fc85b519ee Parents: dd406fb Author: Lukasz Lenart <lukasz.len...@gmail.com> Authored: Thu Sep 3 13:03:55 2015 +0200 Committer: Lukasz Lenart <lukasz.len...@gmail.com> Committed: Thu Sep 3 13:03:55 2015 +0200 ---------------------------------------------------------------------- .../xwork2/config/entities/PackageConfig.java | 13 ++++- .../PackageBasedActionConfigBuilder.java | 30 ++++++++-- .../convention/annotation/AllowedMethods.java | 30 ++++++++++ .../PackageBasedActionConfigBuilderTest.java | 60 ++++++++++++++++++-- .../ClassLevelAllowedMethodsAction.java | 10 ++++ .../PackageLevelAllowedMethodsAction.java | 7 +++ .../actions/allowedmethods/package-info.java | 23 ++++++++ .../PackageLevelAllowedMethodsChildAction.java | 9 +++ 8 files changed, 171 insertions(+), 11 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/struts/blob/ab5fb27d/core/src/main/java/com/opensymphony/xwork2/config/entities/PackageConfig.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/com/opensymphony/xwork2/config/entities/PackageConfig.java b/core/src/main/java/com/opensymphony/xwork2/config/entities/PackageConfig.java index 19ce36f..093985c 100644 --- a/core/src/main/java/com/opensymphony/xwork2/config/entities/PackageConfig.java +++ b/core/src/main/java/com/opensymphony/xwork2/config/entities/PackageConfig.java @@ -531,7 +531,18 @@ public class PackageConfig extends Located implements Comparable, Serializable, } public Set<String> getGlobalAllowedMethods() { - return target.globalAllowedMethods; + Set <String> allowedMethods = target.globalAllowedMethods; + allowedMethods.addAll(getParentsAllowedMethods(target.parents)); + return allowedMethods; + } + + public Set<String> getParentsAllowedMethods(List<PackageConfig> parents) { + Set<String> allowedMethods = new HashSet<>(); + for (PackageConfig parent : parents) { + allowedMethods.addAll(parent.globalAllowedMethods); + allowedMethods.addAll(getParentsAllowedMethods(parent.getParents())); + } + return allowedMethods; } public Builder addGlobalAllowedMethods(Set<String> allowedMethods) { http://git-wip-us.apache.org/repos/asf/struts/blob/ab5fb27d/plugins/convention/src/main/java/org/apache/struts2/convention/PackageBasedActionConfigBuilder.java ---------------------------------------------------------------------- diff --git a/plugins/convention/src/main/java/org/apache/struts2/convention/PackageBasedActionConfigBuilder.java b/plugins/convention/src/main/java/org/apache/struts2/convention/PackageBasedActionConfigBuilder.java index b4a8be9..64a0e98 100644 --- a/plugins/convention/src/main/java/org/apache/struts2/convention/PackageBasedActionConfigBuilder.java +++ b/plugins/convention/src/main/java/org/apache/struts2/convention/PackageBasedActionConfigBuilder.java @@ -42,6 +42,7 @@ import org.apache.logging.log4j.Logger; import org.apache.struts2.StrutsConstants; import org.apache.struts2.StrutsException; import org.apache.struts2.convention.annotation.*; +import org.apache.struts2.convention.annotation.AllowedMethods; import java.io.IOException; import java.lang.reflect.Method; @@ -652,6 +653,8 @@ public class PackageBasedActionConfigBuilder implements ActionConfigBuilder { String actionPackage = actionClass.getPackage().getName(); LOG.debug("Processing class [{}] in package [{}]", actionClass.getName(), actionPackage); + Set<String> allowedMethods = getAllowedMethods(actionClass); + // Determine the default namespace and action name List<String> namespaces = determineActionNamespace(actionClass); for (String namespace : namespaces) { @@ -692,7 +695,7 @@ public class PackageBasedActionConfigBuilder implements ActionConfigBuilder { // Build the default if (!found) { - createActionConfig(defaultPackageConfig, actionClass, defaultActionName, DEFAULT_METHOD, null); + createActionConfig(defaultPackageConfig, actionClass, defaultActionName, DEFAULT_METHOD, null, allowedMethods); } } @@ -706,14 +709,14 @@ public class PackageBasedActionConfigBuilder implements ActionConfigBuilder { actionClass, action); } - createActionConfig(pkgCfg, actionClass, defaultActionName, method, action); + createActionConfig(pkgCfg, actionClass, defaultActionName, method, action, allowedMethods); } } // some actions will not have any @Action or a default method, like the rest actions // where the action mapper is the one that finds the right method at runtime if (map.isEmpty() && mapAllMatches && actionAnnotation == null && actionsAnnotation == null) { - createActionConfig(defaultPackageConfig, actionClass, defaultActionName, null, actionAnnotation); + createActionConfig(defaultPackageConfig, actionClass, defaultActionName, null, actionAnnotation, allowedMethods); } //if there are @Actions or @Action at the class level, create the mappings for them @@ -721,9 +724,9 @@ public class PackageBasedActionConfigBuilder implements ActionConfigBuilder { if (actionsAnnotation != null) { List<Action> actionAnnotations = checkActionsAnnotation(actionsAnnotation); for (Action actionAnnotation2 : actionAnnotations) - createActionConfig(defaultPackageConfig, actionClass, defaultActionName, methodName, actionAnnotation2); + createActionConfig(defaultPackageConfig, actionClass, defaultActionName, methodName, actionAnnotation2, allowedMethods); } else if (actionAnnotation != null) - createActionConfig(defaultPackageConfig, actionClass, defaultActionName, methodName, actionAnnotation); + createActionConfig(defaultPackageConfig, actionClass, defaultActionName, methodName, actionAnnotation, allowedMethods); } } @@ -736,6 +739,15 @@ public class PackageBasedActionConfigBuilder implements ActionConfigBuilder { } } + private Set<String> getAllowedMethods(Class<?> actionClass) { + AllowedMethods annotation = AnnotationUtils.findAnnotation(actionClass, AllowedMethods.class); + if (annotation == null) { + return Collections.emptySet(); + } else { + return TextParseUtil.commaDelimitedStringToSet(annotation.value()); + } + } + /** * Interfaces, enums, annotations, and abstract classes cannot be instantiated. * @param actionClass class to check @@ -896,7 +908,7 @@ public class PackageBasedActionConfigBuilder implements ActionConfigBuilder { * @param annotation The ActionName annotation that might override the action name and possibly */ protected void createActionConfig(PackageConfig.Builder pkgCfg, Class<?> actionClass, String actionName, - String actionMethod, Action annotation) { + String actionMethod, Action annotation, Set<String> allowedMethods) { String className = actionClass.getName(); if (annotation != null) { actionName = annotation.value() != null && annotation.value().equals(Action.DEFAULT_VALUE) ? actionName : annotation.value(); @@ -909,6 +921,12 @@ public class PackageBasedActionConfigBuilder implements ActionConfigBuilder { ActionConfig.Builder actionConfig = new ActionConfig.Builder(pkgCfg.getName(), actionName, className); actionConfig.methodName(actionMethod); + if (!allowedMethods.contains(actionMethod)) { + actionConfig.addAllowedMethod(actionMethod); + } + actionConfig.addAllowedMethod(allowedMethods); + actionConfig.addAllowedMethod(pkgCfg.getGlobalAllowedMethods()); + if (LOG.isDebugEnabled()) { LOG.debug("Creating action config for class [{}], name [{}] and package name [{}] in namespace [{}]", actionClass.toString(), actionName, pkgCfg.getName(), pkgCfg.getNamespace()); http://git-wip-us.apache.org/repos/asf/struts/blob/ab5fb27d/plugins/convention/src/main/java/org/apache/struts2/convention/annotation/AllowedMethods.java ---------------------------------------------------------------------- diff --git a/plugins/convention/src/main/java/org/apache/struts2/convention/annotation/AllowedMethods.java b/plugins/convention/src/main/java/org/apache/struts2/convention/annotation/AllowedMethods.java new file mode 100644 index 0000000..8e3c9ab --- /dev/null +++ b/plugins/convention/src/main/java/org/apache/struts2/convention/annotation/AllowedMethods.java @@ -0,0 +1,30 @@ +package org.apache.struts2.convention.annotation; + +import com.opensymphony.xwork2.config.entities.ActionConfig; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * <!-- START SNIPPET: javadoc --> + * <p> + * This annotation allows actions to specify allowed action methods + * to limit access to any other public action's methods + * </p> + * + * <p> + * This annotation can be used directly on Action classes or + * in the <strong>package-info.java</strong> class in order + * to specify global allowed methods for all sub-packages. + * </p> + * <!-- END SNIPPET: javadoc --> + */ +@Target({ElementType.TYPE, ElementType.PACKAGE}) +@Retention(value = RetentionPolicy.RUNTIME) +public @interface AllowedMethods { + + String value() default ActionConfig.DEFAULT_METHOD; + +} http://git-wip-us.apache.org/repos/asf/struts/blob/ab5fb27d/plugins/convention/src/test/java/org/apache/struts2/convention/PackageBasedActionConfigBuilderTest.java ---------------------------------------------------------------------- diff --git a/plugins/convention/src/test/java/org/apache/struts2/convention/PackageBasedActionConfigBuilderTest.java b/plugins/convention/src/test/java/org/apache/struts2/convention/PackageBasedActionConfigBuilderTest.java index 38403a0..61ed3eb 100644 --- a/plugins/convention/src/test/java/org/apache/struts2/convention/PackageBasedActionConfigBuilderTest.java +++ b/plugins/convention/src/test/java/org/apache/struts2/convention/PackageBasedActionConfigBuilderTest.java @@ -29,6 +29,7 @@ import com.opensymphony.xwork2.factory.DefaultResultFactory; import com.opensymphony.xwork2.inject.Container; import com.opensymphony.xwork2.inject.Scope.Strategy; import com.opensymphony.xwork2.ognl.OgnlReflectionProvider; +import com.opensymphony.xwork2.util.TextParseUtil; import com.opensymphony.xwork2.util.fs.DefaultFileManager; import com.opensymphony.xwork2.util.fs.DefaultFileManagerFactory; import com.opensymphony.xwork2.util.reflection.ReflectionException; @@ -37,6 +38,9 @@ import org.apache.struts2.convention.actions.DefaultResultPathAction; import org.apache.struts2.convention.actions.NoAnnotationAction; import org.apache.struts2.convention.actions.Skip; import org.apache.struts2.convention.actions.action.*; +import org.apache.struts2.convention.actions.allowedmethods.ClassLevelAllowedMethodsAction; +import org.apache.struts2.convention.actions.allowedmethods.PackageLevelAllowedMethodsAction; +import org.apache.struts2.convention.actions.allowedmethods.sub.PackageLevelAllowedMethodsChildAction; import org.apache.struts2.convention.actions.chain.ChainedAction; import org.apache.struts2.convention.actions.defaultinterceptor.SingleActionNameAction2; import org.apache.struts2.convention.actions.exception.ExceptionsActionLevelAction; @@ -125,8 +129,10 @@ public class PackageBasedActionConfigBuilderTest extends TestCase { new ResultTypeConfig.Builder("chain", ActionChainResult.class.getName()).defaultResultParam("actionName").build()}; + Set<String> globalAllowedMethods = TextParseUtil.commaDelimitedStringToSet("execute,browse,cancel,input"); + PackageConfig strutsDefault = makePackageConfig("struts-default", null, null, "dispatcher", - defaultResults, defaultInterceptors, defaultInterceptorStacks); + defaultResults, defaultInterceptors, defaultInterceptorStacks, globalAllowedMethods); PackageConfig packageLevelParentPkg = makePackageConfig("package-level", null, null, null); PackageConfig classLevelParentPkg = makePackageConfig("class-level", null, null, null); @@ -151,6 +157,16 @@ public class PackageBasedActionConfigBuilderTest extends TestCase { "/parentpackage", packageLevelParentPkg, null); PackageConfig packageLevelSubPkg = makePackageConfig("org.apache.struts2.convention.actions.parentpackage.sub#package-level#/parentpackage/sub", "/parentpackage/sub", packageLevelParentPkg, null); + + // Unexpected method call build(class org.apache.struts2.convention.actions.allowedmethods.PackageLevelAllowedMethodsAction, null, "package-level-allowed-methods", PackageConfig: [org.apache.struts2.convention.actions.allowedmethods#struts-default#/allowedmethods] for namespace [/allowedmethods] with parents [[PackageConfig: [struts-default] for namespace [] with parents [[]]]]): + PackageConfig packageLevelAllowedMethodsPkg = makePackageConfig("org.apache.struts2.convention.actions.allowedmethods#struts-default#/allowedmethods", + "/allowedmethods", strutsDefault, null); + PackageConfig packageLevelAllowedMethodsSubPkg = makePackageConfig("org.apache.struts2.convention.actions.allowedmethods.sub#struts-default#/allowedmethods/sub", + "/allowedmethods/sub", strutsDefault, null); + + PackageConfig classLevelAllowedMethodsPkg = makePackageConfig("org.apache.struts2.convention.actions.allowedmethods#struts-default#/allowedmethods", + "/allowedmethods", strutsDefault, null); + PackageConfig differentPkg = makePackageConfig("org.apache.struts2.convention.actions.parentpackage#class-level#/parentpackage", "/parentpackage", classLevelParentPkg, null); PackageConfig differentSubPkg = makePackageConfig("org.apache.struts2.convention.actions.parentpackage.sub#class-level#/parentpackage/sub", @@ -261,6 +277,11 @@ public class PackageBasedActionConfigBuilderTest extends TestCase { expect(resultMapBuilder.build(ClassLevelParentPackageAction.class, null, "class-level-parent-package", differentPkg)).andReturn(results); expect(resultMapBuilder.build(ClassLevelParentPackageChildAction.class, null, "class-level-parent-package-child", differentSubPkg)).andReturn(results); + /* org.apache.struts2.convention.actions.allowedmethods */ + expect(resultMapBuilder.build(ClassLevelAllowedMethodsAction.class, null, "class-level-allowed-methods", classLevelAllowedMethodsPkg)).andReturn(results); + expect(resultMapBuilder.build(PackageLevelAllowedMethodsAction.class, null, "package-level-allowed-methods", packageLevelAllowedMethodsPkg)).andReturn(results); + expect(resultMapBuilder.build(PackageLevelAllowedMethodsChildAction.class, null, "package-level-allowed-methods-child", packageLevelAllowedMethodsSubPkg)).andReturn(results); + /* org.apache.struts2.convention.actions.result */ expect(resultMapBuilder.build(ClassLevelResultAction.class, null, "class-level-result", resultPkg)).andReturn(results); expect(resultMapBuilder.build(ClassLevelResultsAction.class, null, "class-level-results", resultPkg)).andReturn(results); @@ -450,7 +471,7 @@ public class PackageBasedActionConfigBuilderTest extends TestCase { verifyActionConfig(pkgConfig, "", org.apache.struts2.convention.actions.idx.Index.class, "execute", pkgConfig.getName()); verifyActionConfig(pkgConfig, "index", org.apache.struts2.convention.actions.idx.Index.class, "execute", pkgConfig.getName()); verifyActionConfig(pkgConfig, "idx2", org.apache.struts2.convention.actions.idx.idx2.Index.class, "execute", - "org.apache.struts2.convention.actions.idx.idx2#struts-default#/idx/idx2"); + "org.apache.struts2.convention.actions.idx.idx2#struts-default#/idx/idx2"); /* org.apache.struts2.convention.actions.defaultinterceptor */ pkgConfig = configuration.getPackageConfig("org.apache.struts2.convention.actions.defaultinterceptor#struts-default#/defaultinterceptor"); @@ -514,6 +535,33 @@ public class PackageBasedActionConfigBuilderTest extends TestCase { verifyActionConfig(pkgConfig, "package-level-parent-package-child", PackageLevelParentPackageChildAction.class, "execute", pkgConfig.getName()); assertEquals("package-level", pkgConfig.getParents().get(0).getName()); + /* org.apache.struts2.convention.actions.allowedmethods class level */ + pkgConfig = configuration.getPackageConfig("org.apache.struts2.convention.actions.allowedmethods#struts-default#/allowedmethods"); + assertNotNull(pkgConfig); + assertEquals(2, pkgConfig.getActionConfigs().size()); + verifyActionConfig(pkgConfig, "class-level-allowed-methods", ClassLevelAllowedMethodsAction.class, "execute", pkgConfig.getName()); + assertEquals("struts-default", pkgConfig.getParents().get(0).getName()); + + ActionConfig actionConfig = pkgConfig.getActionConfigs().get("class-level-allowed-methods"); + assertEquals(actionConfig.getAllowedMethods().size(), 5); + assertTrue(actionConfig.getAllowedMethods().contains("execute")); + assertTrue(actionConfig.getAllowedMethods().contains("end")); + assertTrue(actionConfig.getAllowedMethods().contains("input")); + + /* org.apache.struts2.convention.actions.allowedmethods.sub package level */ + pkgConfig = configuration.getPackageConfig("org.apache.struts2.convention.actions.allowedmethods.sub#struts-default#/allowedmethods/sub"); + assertNotNull(pkgConfig); + assertEquals(1, pkgConfig.getActionConfigs().size()); + verifyActionConfig(pkgConfig, "package-level-allowed-methods-child", PackageLevelAllowedMethodsChildAction.class, "execute", pkgConfig.getName()); + assertEquals("struts-default", pkgConfig.getParents().get(0).getName()); + + actionConfig = pkgConfig.getActionConfigs().get("package-level-allowed-methods-child"); + assertEquals(actionConfig.getAllowedMethods().size(), 6); + assertTrue(actionConfig.getAllowedMethods().contains("execute")); + assertTrue(actionConfig.getAllowedMethods().contains("home")); + assertTrue(actionConfig.getAllowedMethods().contains("start")); + assertTrue(actionConfig.getAllowedMethods().contains("input")); + /* org.apache.struts2.convention.actions.result */ pkgConfig = configuration.getPackageConfig("org.apache.struts2.convention.actions.result#struts-default#/result"); assertNotNull(pkgConfig); @@ -631,12 +679,12 @@ public class PackageBasedActionConfigBuilderTest extends TestCase { private PackageConfig makePackageConfig(String name, String namespace, PackageConfig parent, String defaultResultType, ResultTypeConfig... results) { - return makePackageConfig(name, namespace, parent, defaultResultType, results, null, null); + return makePackageConfig(name, namespace, parent, defaultResultType, results, null, null, null); } private PackageConfig makePackageConfig(String name, String namespace, PackageConfig parent, String defaultResultType, ResultTypeConfig[] results, List<InterceptorConfig> interceptors, - List<InterceptorStackConfig> interceptorStacks) { + List<InterceptorStackConfig> interceptorStacks, Set<String> globalAllowedMethods) { PackageConfig.Builder builder = new PackageConfig.Builder(name); if (namespace != null) { builder.namespace(namespace); @@ -663,6 +711,10 @@ public class PackageBasedActionConfigBuilderTest extends TestCase { } } + if (globalAllowedMethods != null) { + builder.addGlobalAllowedMethods(globalAllowedMethods); + } + return new MyPackageConfig(builder.build()); } http://git-wip-us.apache.org/repos/asf/struts/blob/ab5fb27d/plugins/convention/src/test/java/org/apache/struts2/convention/actions/allowedmethods/ClassLevelAllowedMethodsAction.java ---------------------------------------------------------------------- diff --git a/plugins/convention/src/test/java/org/apache/struts2/convention/actions/allowedmethods/ClassLevelAllowedMethodsAction.java b/plugins/convention/src/test/java/org/apache/struts2/convention/actions/allowedmethods/ClassLevelAllowedMethodsAction.java new file mode 100644 index 0000000..24ea489 --- /dev/null +++ b/plugins/convention/src/test/java/org/apache/struts2/convention/actions/allowedmethods/ClassLevelAllowedMethodsAction.java @@ -0,0 +1,10 @@ +package org.apache.struts2.convention.actions.allowedmethods; + +import org.apache.struts2.convention.annotation.AllowedMethods; + +@AllowedMethods("end") +public class ClassLevelAllowedMethodsAction { + + public String execute() { return null; } + +} http://git-wip-us.apache.org/repos/asf/struts/blob/ab5fb27d/plugins/convention/src/test/java/org/apache/struts2/convention/actions/allowedmethods/PackageLevelAllowedMethodsAction.java ---------------------------------------------------------------------- diff --git a/plugins/convention/src/test/java/org/apache/struts2/convention/actions/allowedmethods/PackageLevelAllowedMethodsAction.java b/plugins/convention/src/test/java/org/apache/struts2/convention/actions/allowedmethods/PackageLevelAllowedMethodsAction.java new file mode 100644 index 0000000..eab0895 --- /dev/null +++ b/plugins/convention/src/test/java/org/apache/struts2/convention/actions/allowedmethods/PackageLevelAllowedMethodsAction.java @@ -0,0 +1,7 @@ +package org.apache.struts2.convention.actions.allowedmethods; + +public class PackageLevelAllowedMethodsAction { + + public String execute() { return null; } + +} http://git-wip-us.apache.org/repos/asf/struts/blob/ab5fb27d/plugins/convention/src/test/java/org/apache/struts2/convention/actions/allowedmethods/package-info.java ---------------------------------------------------------------------- diff --git a/plugins/convention/src/test/java/org/apache/struts2/convention/actions/allowedmethods/package-info.java b/plugins/convention/src/test/java/org/apache/struts2/convention/actions/allowedmethods/package-info.java new file mode 100644 index 0000000..461131f --- /dev/null +++ b/plugins/convention/src/test/java/org/apache/struts2/convention/actions/allowedmethods/package-info.java @@ -0,0 +1,23 @@ +/* + * $Id$ + * + * 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. + */ +@org.apache.struts2.convention.annotation.AllowedMethods("home,start") +package org.apache.struts2.convention.actions.allowedmethods; + http://git-wip-us.apache.org/repos/asf/struts/blob/ab5fb27d/plugins/convention/src/test/java/org/apache/struts2/convention/actions/allowedmethods/sub/PackageLevelAllowedMethodsChildAction.java ---------------------------------------------------------------------- diff --git a/plugins/convention/src/test/java/org/apache/struts2/convention/actions/allowedmethods/sub/PackageLevelAllowedMethodsChildAction.java b/plugins/convention/src/test/java/org/apache/struts2/convention/actions/allowedmethods/sub/PackageLevelAllowedMethodsChildAction.java new file mode 100644 index 0000000..4a6223d --- /dev/null +++ b/plugins/convention/src/test/java/org/apache/struts2/convention/actions/allowedmethods/sub/PackageLevelAllowedMethodsChildAction.java @@ -0,0 +1,9 @@ +package org.apache.struts2.convention.actions.allowedmethods.sub; + +import org.apache.struts2.convention.actions.allowedmethods.PackageLevelAllowedMethodsAction; + +public class PackageLevelAllowedMethodsChildAction extends PackageLevelAllowedMethodsAction { + + public String execute() { return null; } + +}