Available: | Confluence 8.5.3 and later |
This module was previously defined as xwork in Confluence versions prior to 8.5.3. The xwork module will continue to be supported until Confluence 10.0 for backwards compatibility.
Struts plugin modules enable you to deploy Struts actions and views as a part of your plugins.
The Struts web framework is underpinned by an expression language technology known as OGNL. Historically, the OGNL technology has been the source of numerous critical vulnerabilities. Atlassian has developed a number of security measures to protect against the reoccurrence of such vulnerabilities.
One such measure is the OGNL class allowlist. The allowlist is a list of classes that are permitted to be used in OGNL expressions. The allowlist comprises classes from the following sources:
@StrutsParameter
(see Defining Request Parameters)struts.allowlist.classes
in a Struts module (see Configuration elements)struts.allowlist.packages
in a Struts module (see Configuration elements)Generally, defining your Struts package components in a Struts module descriptor and annotating all your Struts
parameters with @StrutsParameter
will be sufficient to allow your plugin to function as expected. However, some Struts
functionality may require additional classes to be added to the allowlist. Please observe the log output for warnings
and add classes to the allowlist as appropriate. Plugins usually only need to allowlist model or DTO classes.
Avoid allowlisting classes which contain privileged or dangerous members. Examples of a privileged member may be a method which writes to disk or performs some administrative task. Such members should be defined in a class that is not allowlisted and outside your Action class which is allowlisted by default.
Additionally, the Struts module should also be used to define multipart request parsing rules. Ordinarily, multipart requests will only be parsed if the request is authorized according to the enforceSiteAccess
method in ConfluencePermissionEnforcer:
For endpoints that are intended to parse multipart requests outside the above access criteria, they can be allowlisted using the multipart-upload-allowlist
element within a Struts module.
The root element for the Struts plugin module is struts
. It does not accept a class attribute. It accepts the following child elements for configuration:
package
- standard Struts package definition,
multiple permitted per module
namespace
(attribute) - subject to the following RegEx validation: [a-zA-Z0-9/\-]*
action
- standard Struts action definition
name
(attribute) - subject to the following RegEx validation: [a-zA-Z0-9\-]*
method
(attribute) - subject to the following RegEx validation: [a-zA-Z_]*[0-9]*
constant
- used to provide additional Struts related configuration, only 1 of each of the following is permitted per
module
name="struts.allowlist.classes" value=""
(attributes) - the value
should be a comma-separated list of plugin
classes which are allowed to be used in OGNL expressionsname="struts.allowlist.packages" value=""
(attributes) - the value
should be a comma-separated list of plugin
class packages which are allowed to be used in OGNL expressions, note that subpackages are also allowedmultipart-upload-allowlist
- only 1 multipart-upload-allowlist element is permitted per module
regex
- a RegEx pattern to match against the request URI (excluding the context path), it will parse multipart
requests for any matching requests irrespective of the request's authorisation state.1 2<struts name="livesearchaction" key="livesearchaction"> <package name="livesearch" extends="default" namespace="/plugins/livesearch"> <default-interceptor-ref name="defaultStack" /> <action name="livesearch" class="com.atlassian.confluence.extra.livesearch.LiveSearchAction" method="doDefault"> <result name="success" type="velocity">/templates/extra/livesearch/livesearchaction.vm</result> </action> </package> <constant name="struts.allowlist.classes" value=" com.plugin.MyDtoClass, com.plugin.MyModelClass "/> <constant name="struts.allowlist.packages" value=" com.plugin.dto, com.plugin.model "/> <multipart-upload-allowlist> <regex>/admin/test\.action.*</regex> <regex>/plugins/livesearch/livesearch\.action.*</regex> </multipart-upload-allowlist> </struts>
Struts actions should extend ConfluenceActionSupport, which provides a number of helper methods and components that are useful when writing an Action that works within Confluence.
Other action base-classes can be found within Confluence, but we recommend you don't use them - the hierarchy of action classes in Confluence is over-complicated, and likely to be simplified in the future in a way that will break your plugins.
Request parameters, such as those submitted by a form, can be stored on your Struts Action class by defining getters and
setters for them. For example, if you have a form with a field called name
, you can store the value of that field by
defining a public void setName(String name)
method on your Action class, and then importantly, annotating this method
with @StrutsParameter
. The presence of this annotation indicates that the method is intended for parameter injection
and is safe to be invoked by any user who can view the Action.
1 2private String name; @StrutsParameter public void setName(String name) { this.name = name; }
If you wish to populate a DTO (Data Transfer Object) instead of setting the parameters directly on the Action class, you
can define a getter for the DTO on your Action class instead. For example, define a method public MyDto getFormData()
which is also annotated by @StrutsParameter(depth = 1)
. Then, a parameter with name formData.fullName
will be mapped
to the setter setFullName
on that DTO. Note that the @StrutsParameter
annotation has a depth
field which dictates
the depth to which parameter injection is permitted. The default value is 0, which only allows setting parameters
directly on the Action class as in the first example. A depth
of 1 indicates that the immediate public properties of
an object returned by the getter are permitted to be set. If you have further nested objects, you can increase
the depth
accordingly. Do not set this depth
field to a value greater than the minimum required for your use case.
1 2private MyDto formData = new MyDto(); @StrutsParameter(depth = 1) public MyDto getFormData() { return formData; } public static class MyDto { private String fullName; public void setFullName(String fullName) { this.fullName = fullName; } }
It is critical that any method you annotate with @StrutsParameter
is safe for any user who can view that corresponding
action to invoke (including any public methods on objects returned by that method and so forth). Any getters you
annotate should only ever return a DTO or a collection/hierarchy of DTOs. Do NOT mix business logic or service
references with your parameter injection methods and DTOs. Additionally, any database DTOs should be entirely separate
from request parameter/form DTOs.
Do NOT under any circumstance, annotate a method that returns one of the following unsafe objects:
Compatibility Note: The @StrutsParameter
annotation was introduced in Confluence 8.8.0 and 8.5.6. The deprecated
@ParameterSafe
annotation will continue to be supported until Confluence 10.0, but will have a fixed depth
field
value of 2.
Please refer to the following guide to ensure your Struts actions are protected by XSRF tokens where necessary, and how to ensure these tokens are included in your forms and links:
Enable XSRF protection for your app
Actions are added to the Struts core configuration within Confluence, which means they are accessed like any other action.
For example, given the above atlassian-plugin.xml
, the livesearch
action would be accessed
at <host>/<context-path>/plugins/livesearch/livesearch.action
.
Your Velocity template must be specified with a leading slash (/) in the plugin XML configuration, and the path must correspond to the path inside the plugin JAR file.
In the above example, the Velocity template must be found in /templates/extra/livesearch/livesearchaction.vm
inside
the plugin JAR file. In the example plugin's source code, this would mean the Velocity template should be created
in src/main/resources/template/extra/livesearch/
.
Inside your Velocity template, you can use $action
to refer to your action, and many other variables to access
Confluence functionality.
See Confluence Objects Accessible From Velocity for
more information.
Some issues to be aware of when developing or configuring a Struts plugin:
default
Confluence package. It is useful to be aware of what this provides to you in the way of interceptors and result types. Extending any other package will modify that package's configuration across the entire application, which is not supported or desirable./plugins/unique/value
- that is prefixing plugin packages with /plugins
and then adding a string globally unique to your plugin. The only name you can't use is servlet
as the /plugins/servlet
URL pattern is reserved for the Servlet Module./admin
will automatically be protected by secure administrator sessions. To opt out of this protection you can mark your class or Struts action method with the WebSudoNotRequired annotation./admin
namespace are not protected and can be opted in by adding the WebSudoRequired annotation.Rate this page: