Skip to content

Home

Introduction

Development

IDEs

Services

API Explorer

Releases

Release notes

Martini Runtime

Martini Desktop

Martini Online

Feature requests

Bug reports

Securing a Groovy API

Martini leverages Spring MVC and Spring Security features to implement method-level security on HTTP APIs written using Groovy.

Getting started

Consider the following Groovy code:

1
2
3
4
5
6
7
8
9
@RequestMapping(value = "lannister")
class HouseLannister {

    @ResponseBody
    @RequestMapping(value = 'welcome', method = RequestMethod.PUT)
    def welcome() {
        return "Welcome home"
    }
}

This exposes an HTTP endpoint on /api/lannister/welcome that simply returns "Welcome home". We can verify this by issuing a cURL command:

1
curl -X PUT 'http://localhost:8080/api/lannister/welcome'

This responds with:

1
Welcome home

Our goal for this example is to limit access to this endpoint. We can secure this endpoint by applying the @Secured annotation.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
import java.security.Principal

@RequestMapping(value = "lannister")
class HouseLannister {

    @Secured("Jaime") // Restricts access for a user with username 'Jaime'
    @ResponseBody
    @RequestMapping(value = 'welcome', method = RequestMethod.PUT)
    def welcome( Principal principal ) {
        return "Welcome home, ${principal.name}"
    }
}

Resolvable method parameters

The above code also introduces the injection of java.security.Principal object in the method parameter. This allows us later to query the name of the authenticated user and append it to the "Welcome home" response.

Making the cURL command again yields:

1
2
3
4
5
<APIException>
  <result>ERROR</result>
  <apiErrorCode>-1</apiErrorCode>
  <message>Access is denied</message>
</APIException>

At this point, the endpoint is now secured1. Only a User with the username Jaime can access the endpoint. By using Jaime's access token retrieved via OAuth 2.0, we can execute:

1
2
3
curl -X PUT \
  http://localhost:8080/api/lannister/welcome \
  -H 'Authorization: Bearer b97e1d2f-28ed-495f-bb56-2538b72c224f'

... to which the server responds:

1
Welcome home, Jaime

Using Basic

Martini also supports the Basic authentication scheme. Assuming you have it enabled, you can receive the same response with (using your user's configured credentials):

1
2
curl -X PUT -u Jaime:kingslayer \
  http://localhost:8080/api/lannister/welcome

Access control lists

When implementing security requirements, it's often more manageable to organize a set of permissions into a single logical group. Consider a requirement change in the previous example, where more users are allowed to access the HTTP endpoint. By creating a group that consists of the users:

  • Jaime
  • Cersei
  • Tyrion
  • Tywin

We can assign the group's name on the @Secured annotation in the code, as in:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
import java.security.Principal

@RequestMapping(value = "lannister")
class HouseLannister {

    @Secured("Lannister") // Restricts access for all users under 'Lannister'
    @ResponseBody
    @RequestMapping(value = 'welcome', method = RequestMethod.PUT)
    def welcome( Principal principal ) {
        return "Welcome home, ${principal.name}"
    }
}

The endpoint /api/lannister/welcome then becomes accessible only for authentication requests from registered users under the group Lannister.

Class level annotation

Like the @RequestMapping, you can also add @Secured at class level. This applies the access restrictions to all methods in the class.

Security metadata

Aside from the annotation-based configuration, Martini also supports metadata-based security declarations for Groovy-based services. This approach allows us to decouple security configuration from the code.

Consider again the welcome example:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
import java.security.Principal

@RequestMapping(value = "lannister")
class HouseLannister {

    @ResponseBody
    @RequestMapping(value = 'welcome', method = RequestMethod.PUT)
    def welcome( Principal principal ) {
        return "Welcome home, ${principal.name}"
    }
}

What changed?

Notice that only the @Secured annotation was removed.

In the Navigator view, right-click on HouseLannister.groovy and choose Invoke Permissions. This will open the Invoke Permissions dialog:

Groovy service permissions

Groovy service permissions

By adding Lannister to the Groups column, we once again restrict HTTP access to users under the declared group. Martini stores the security metadata in a file located at <martini-home>/conf/invoker-acl.xml.


  1. In Runtime Edition, accessing the secured endpoint in the browser (i.e.: GET request) with a user logged-in in the Runtime Admin UI is allowed. This is due to Marketplace credentials containing greater authorization than Martini users.