Event Listener module
Job module
Language module
Macro Module
Servlet Filter module
Servlet module
Theme module
Web UI modules
Workflow module

Struts module

Available:

Confluence 8.5.3 and later
(replaced XWork introduced in 1.4)

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.

Purpose of this module

Struts plugin modules enable you to deploy Struts actions and views as a part of your plugins.

OGNL Class Allowlist

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:

  • Pre-defined list of classes, designated as safe by Atlassian and Struts
  • Classes defined as an Action or other Struts package component in a Struts module (see Configuration elements)
  • Classes returned by an Action class getter, annotated with @StrutsParameter (see Defining Request Parameters)
  • Classes listed in struts.allowlist.classes in a Struts module (see Configuration elements)
  • Classes belonging to a package listed in 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.

Multipart Requests

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.

Configuration

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:

Elements

Example

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>

Writing an Action

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.

Defining Request Parameters

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
2
private 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
2
private 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:

  • live Hibernate persistent objects
  • container or Spring-managed beans, or any other live components/services
  • objects (or objects that contain references to objects) that contain setter methods that are used for anything other than setting form parameter values

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.

Enforcing XSRF protection

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

Accessing your Actions

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.

Creating a Velocity Template for Output

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.

Notes

Some issues to be aware of when developing or configuring a Struts plugin:

See also

Rate this page: