play framework

At Logz.io, we’re using Play 2.0 framework. We wanted to share our experience hardening and securing this part of our architecture by using action composition.

 

Let’s jump to the gory details.

Action composition is a powerful way to enhance or restrict a behavior of a controller.

By composing actions, we practically intercept the incoming request before it arrives at the controller. This allows us to perform additional work on the incoming request before the actual process.

Action in Play 2.0 Java version, is an instance of play.mvc.Action with a method named call(Context context) that returns a play.mvc.Result value (or better to say Promise results):

Play, behind the scenes, adds a root action that will properly use the call method above, and this allows us to further compose Actions by ourselves.

Action composition allows us to add one or more behaviors to other actions, for example, we can compose an Authentication action on another action (service) to limit the access to authenticated users only.

Action composition is done using Java annotations:

Java annotations are a form of Metadata for code, and are used to decorate a class, method, field, parameter, variable, constructor, or package. Annotations can be used at compile time or retrieved at runtime using reflection.

Building our own custom actions will be done using annotations declared for runtime (RetentionPolicy.Runtime):

1.  Define an annotation – this is the annotation we use to mark an action composition over another action:

Annotation declaration is similar to an interface declaration, forwarded by @. Annotations only define some information on the element they decorate, and they do not do any business logic. For this we need a consumer to act upon the presence of annotations.

Defining a custom annotation also uses other annotations: @Target which defines the allowed elements to use with this annotation, @Retention which defines when is this annotation is expected to be used (source, class, or runtime).

In order to define a custom action, Play provides the @With annotation to declare which class (must be of type extending Action) to use to perform the actual composite work (a.k.a the consumer).

Inside the annotation declaration, we can only use primitives, string and enums. All members are defined as methods and can be added with the default value.

When using the annotation to decorate another Action, we can set values for each member using the name=value format. If only one member is defined, it must be named value and can be set without the attribute name:

In case we defined a default value, we can use the decoration without any parameter.

2.  Define a consumer – a class which extends Action and implements specific logic inside the call method:

The Action class retrieves the custom annotation as configuration, and the Action it wraps will be set as its delegate

3. This allows us to implement the call method and inside forward work to the original Action using the delegate. We can also access the annotation members using the configuration member of the abstract Action class. We will now enforce the restrictions verifying the access to this API service is done by an authenticated user only. If it is, we can forward work using the delegate, if not – we can return HTTP Error 401 Unauthorized:

Usage in the controller:

In order to pass object/s from the Action to the controller, you can use the args member of context, in this example – who is the username currently logged in. This context is then available to the controller to get the data out:

We use action composition extensively throughout our Play code and find it useful. Hopefully, this helps!

Power your DevOps Initiatives with Logz.io's Machine Learning Features!