10

I do not understand Java annotations with retention policy as RUNTIME that well. What I'm trying to do is create an annotation named @Authorize and use it on methods which needs user authorization in order to perform some action( the user is already authenticated at this point). eg. I have an order service with a getOrder() method. I want only the user who created this order to access it. `

public void getOrder(User user) {
   //current code does something like this
   if(order.getCreatedBy().equals(user)) {
     //then proceed. 
}

} `

I do not want to mix this logic with business logic. Instead, I'm looking to have something like this- `

@Authorize
public void getOrder(User user) {
   //business logic
}

` There are several methods but not all of them would need such authorization. Could someone please explain me how can I fit the pieces together here? What I don't understand at this point is that how AnnotationProcessor would help me here since it does its magic at compile time. As far as I understand, it will help me generate some code at compile time but I have no clue how to use that generated code. I went through numerous examples on AnnotationProcessors but I'm still missing something. These links helped me a bit to understand annotation processing so far-

http://hannesdorfmann.com/annotation-processing/annotationprocessing101 https://equaleyes.com/blog/2017/09/04/annotation-processing/

Even if I go with reflections, where should I place the reflection logic? and is it counter productive of what I'm trying to achieve?

At this point, I'm open to other solutions as well which do not involve annotations but will help me separating out business logic with such resource specific authorization.

priteshbaviskar
  • 2,139
  • 2
  • 20
  • 27
  • You can take a look at spring security : https://spring.io/projects/spring-security – Ajit Soman Jun 30 '18 at 17:39
  • I think its more of role based security. I'm looking out for a more fine grained option – priteshbaviskar Jul 01 '18 at 08:20
  • based of activity or permission . right ? . you can still do this with spring security. look for hasAuthority – Ajit Soman Jul 01 '18 at 08:48
  • We are securing data with @PreAuthorize which allows access control via: - hasRole, - hasAuthority - easy to map authorities to roles, - &&, ||, () - operators are supported, - used principle (eg. login), - custom bean - class with method returning boolean checking resource accessability for authenticated user. Most common case is that user has particular authority + that belongs to group of users related to resource (eg. same company). https://docs.spring.io/spring-security/site/docs/5.0.6.RELEASE/reference/htmlsingle/#el-pre-post-annotations – Piotr Kończak Jul 18 '18 at 08:08

1 Answers1

7

To implement authorization controls on methods in Java, I highly recommend Spring Security with an eXtensible Access Control Markup Language (XACML) implementation that has a Spring Security API.

Spring Security

Spring Security provides two main means to protect access to methods:

  • Preauthorization: this allows for certain conditions/constraints to be checked before the execution of the method is allowed. Failure to verify these conditions will result in the failure to call the method.
  • Postauthorization: this allows for certain conditions/constraints to be checked after the method returns. This is used less often that preauthorization check, but can be used to provide extra security around complex interconnected business tier methods, especially around constraints related to the object returned by the method.

Say for example, that one of the access control rule is that the user has have the ROLE_ADMIN authority before being able to invoke a method getEvents(). The way to do that within the Spring Security framework would be to use the PreAuthorize annotation as below:

public interface Sample { ... 
@PostAuthorize("hasRole('ROLE_ADMIN')") 
Event getEvent(); } 

In essence Spring Security uses a runtime Aspect Oriented Programming (AOP) pointcut to execute before an advice on the method and throw an o.s.s.access.AccessDeniedException if the security constraints specified are not met.

More can be found about Spring Security's Method Level Security in section 27.3 of this documentation.

eXtensible Access Control Markup Language (XACML) - a policy language for ABAC

Spring Security does a great job of implementing access control with its expression based access control, but attribute based access control (ABAC) allows more fine grained control of access and is recommended by the National Institute of Standards and Technology.

To address the limitations of Role Based Access Control (RBAC), NIST came up with a new model called ABAC (Attribute Based Access Control). In ABAC, you can now use more metadata / parameters. You can for instance consider:

  • a user's identity, role, job title, location, department, date of birth...
  • a resource's type, location, owner, value, department...

  • contextual information e.g. time of day the action the user is attempting on the resource

All these are called attributes. Attributes are the foundation of ABAC, hence the name. You can assemble these attributes into policies. Policies are a bit like the secret sauce of ABAC. Policies can grant and deny access. For instance:

  • An employee can view a record if the employee and the record are in the same region
  • Deny access to reading records between 5pm and 8am.

Policies can be used to express advanced scenarios e.g.

  • segregation of duty
  • time-based constraints (see above)
  • relationship-based access control (see above)
  • delegation rules delegate Bob access to Alice's document.

There are 2 main syntaxes available to write policies:

ABAC also comes with an architecture to define how the policies will get evaluated and enforced.

ABAC GRAPH

The architecture contains the following components:

  • the Policy Enforcement Point (PEP): this is the component that secures the API / application you want to protect. The PEP intercepts the flow, analyzes it, and send an authorization request to the PDP (see below). It then receives a decision (Permit/Deny) which it enforces.

  • the Policy Decision Point (PDP) receives an authorization request (e.g. can Alice view record #123?) and evaluates it against the set of policies it has been configured with. It eventually reaches a decision which it sends back to the PEP. During the evaluation process, the PDP may need additional metadata e.g. a user's job title. To that effect, it can turn to policy information points (PIP)

  • the Policy Information Point (PIP) is the interface between the PDP and underlying data sources e.g. an LDAP, a database, a REST service which contain metadata about users, resources, or other. You can use PIPs to retrieve information the PDP may need at runtime e.g. a risk score, a record’s location, or other.

Implementations of XACML

Full disclosure - I am on the XACML Technical Committee and work for Axiomatics, a provider of dynamic authorization that implements XACML.

Axiomatics provides a Spring Security SDK for their Axiomatics Policy Server and it provides four expressions that can be used to query the PDP as a part of protecting a method invocation

  1. xacmlDecisionPreAuthz, called with @PreAuthorize
  2. xacmlDecisionPostAuthz, called with @PostAuthorize
  3. xacmlDecisionPreFilter, called with @PostFilter
  4. xacmlDecisionPostFilter, called with @PreFilter

The exact signatures for these methods are as follows:

  1. xacmlDecisionPreAuthz(Collection<String> attributeCats, Collection<String> attributeTypes, Collection<String> attributeIds, ArrayList<Object> attributeValues)
  2. xacmlDecisionPostAuthz(Collection<String> attributeCats, Collection<String> attributeTypes, Collection<String> attributeIds, ArrayList<Object> attributeValues)
  3. xacmlDecisionPreFilter(Collection<String> attributeCats, Collection<String> attributeTypes, Collection<String> attributeIds, ArrayList<Object> attributeValues)
  4. xacmlDecisionPostFilter (Collection<String> attributeCats, Collection<String> attributeTypes, Collection<String> attributeIds, ArrayList<Object> attributeValues)

For an entire list of XACML implementations, you can check this list on Wikipedia.

Michael C Good
  • 567
  • 2
  • 11
  • 2
    It took me a while to grasp all the concepts you've mentioned and refactoring existing logic. But it was worth it. I was finally able to implement a custom solution using spring security. – priteshbaviskar Jul 23 '18 at 07:03
  • Fantastic. Happy to hear it! If you have it on Github or are able to share it, I'd love to see it. – Michael C Good Jul 23 '18 at 13:49
  • Hi , i'm having similar requirement, i couldn't find demo code which shows this flow. Can you please share a sample code if you have it? – Sridhar C Nov 19 '19 at 14:17
  • Hi @SridharC, my experience is mainly with the commercial off the shelf product Axiomatics Policy Server. Open source solutions such as WSO2 and AuthZForce are open source. I suggest getting an idea of how these solutions compare to one another. – Michael C Good Nov 19 '19 at 15:09
  • @MichaelCGood: The PDP will give answer like `permit` or `deny` for a given authorization as shown in your post "Alice want to see record of @123". However how about that: Alice can only see records belong to the same region with Alice. The PDP should give a permit answer. But the querying the database should exclude some records. How to handle this? – hguser Apr 20 '20 at 06:02
  • @hguser Essentially the logic would be like this: user.region == record.region . Alice is a user, so her region would be looked up by the PIP and compared to the region record. I'm assuming the records that are excluded are the ones that do not belong to Alice's region. Are you protecting a database or a web app (or desktop)? Feel free to send me an email at michael.good@nttdatafed.com – Michael C Good Apr 21 '20 at 02:57
  • 1
    @MichaelCGood: Take the `/api/users` as example, when the administrator of the app request the url, all the users should be returned. While when Alice request the url, only user with the same region should be returned. So for the policy `user.region == record.region` it can not be evaluated before the query since there is no `record` at all, if evaluated after the query, then the user without the same region will be excluded, but this will cause other problems like pagination. – hguser Apr 21 '20 at 05:20
  • add a separate rule/logic that targets the rule. Add ```user.role==user``` to the ```user.region == record.region``` rule. Then a separate one ```user.role == admin``` to grant all regions. Maybe you can think of something more elegant, but you get the idea of having separate logic to grant an exception to admins. – Michael C Good Apr 21 '20 at 17:10
  • @MichaelCGood Sorry but I can not get it. :( – hguser Apr 25 '20 at 13:02
  • @hguser send me an email – Michael C Good Apr 28 '20 at 20:04