horatius
horatius

Reputation: 814

Custom PermissionEvaluator for authorizing resource access for each REST Resource

I have implemented a REST application with some complicated authorization requirements.

Here's a summary

My customers purchase a proxy device called Collector that enables their home automation control to be centralized. My customers also purchase multiple home automation devices (let's call them HADevices) that report their metrics through the collector to my REST application.

An admin(who is my customer service rep), with role ROLE_ADMIN, should be able to look at any data from any Collector or HADevice. A customer, with role ROLE_USER role, should only be able to view data about the Collector or an HADevice that s/he owns.

The Collector, with role ROLE_COLLECTOR is the only role authorized to insert data i.e. create or update a resource in my REST service. Let's call this url /deviceMetrics (POST). A Collector can insert metrics for any HADevice associated with the customer. HADevices have no role and do not interact with my REST application. A Collector can only insert records to HADevices that have the same customer as the Collector.

I am using spring security 4.0 for authentication and @Secured annotation for authorization. However, I find that my code is cluttered with repetitive permission validations which take up a majority of my logic. The basic insertions and retrievals are pretty straightforward.

I want to use a PermissionEvaluator to centralize Access Control. I have to secure the following urls

My application is littered with such complex authorization requirements for each REST resource, and I want to use a custom org.springframework.security.access.PermissionEvaluator, specifically by implementing the following method.

boolean hasPermission(Authentication authentication, Serializable targetId, String targetType, Object permission)

I'm planning to use a combination of targetType and request.getUrl() to get a specialized Evaluator for each url and resource. Is there a better way to do this?

Upvotes: 1

Views: 1634

Answers (2)

horatius
horatius

Reputation: 814

For those who are wondering what my solution looks like, I borrowed from this example.

It's old and it's based on xml configuration which I am not particularly fond of. But the idea is to create a Map and initialize the custom PermissionValidator and to store the authorization logic in the Permission interface implementations.

The biggest pain point was injecting an autowired HashMap of tuples, but that's an implementation detail that reasonably experienced spring users can figure out.

Upvotes: 0

holmis83
holmis83

Reputation: 16644

Your question is quite broad, but I think you can get away with quite simple logic for most cases.

GET /collectors/{id}/deviceMetrics

Given that you have a DeviceMetrics class with suitable properties, you can annotate your data repository with something like:

@PostAuthorize("hasRole('ROLE_ADMIN') or (hasRole('ROLE_USER') and returnObject.collector.owner = authentication.name)")
public DeviceMetrics getDeviceMetrics(long deviceId);

(This assumes that DeviceMetrics class has a property collector which has a property owner which is the username.)

That doesn't need a PermissionEvaluator at all. Maybe you need one for more complex cases:

POST /collectors/{id}/deviceMetrics

@PreAuthorize("hasRole('ROLE_COLLECTOR') and hasPermission(#collectorId, 'com.example.Collector', 'WRITE')")
public void saveDeviceMetrics(long collectorId, DeviceMetrics deviceMetrics);

You only need one PermissionEvaluator since you get all the information you need as method arguments.

Upvotes: 1

Related Questions