spring-projects / spring-data-rest

Simplifies building hypermedia-driven REST web services on top of Spring Data repositories
https://spring.io/projects/spring-data-rest
Apache License 2.0
915 stars 560 forks source link

User based content and actions [DATAREST-236] #619

Open spring-projects-issues opened 10 years ago

spring-projects-issues commented 10 years ago

Sri opened DATAREST-236 and commented

Consumers of our REST API are applications themselves or applications acting on behalf of a user (like apply Oauth2 security). Some of the APIs need to know the user and conditionally execute it and/or produce content based on user entitlements. Is this something Spring Data REST would address?


Issue Links:

4 votes, 6 watchers

spring-projects-issues commented 10 years ago

Oliver Drotbohm commented

I think this effectively duplicates DATAREST-221. Care to share your thoughts there?

spring-projects-issues commented 10 years ago

Sri commented

I don't think DATAREST-221 is related to my request, however I do see the value of it. Let me explain my use case.

Our aim is funnel enterprise data from multiple sources into single repository and build the relationships between these sources of data and expose them through SDR. At the same, we plan on propagate back the changes done to data through SDR (POST/PUT/DELETE) back to the source data. So in terms of what operations are allowed for applications themselves and applications acting on behalf of user, here are few general guidelines.

  1. If a particular dataset (in SDR terms entity) is sensitive then all GET operations needs to know the user associated with the request and based on entitlements should either allow or disallow request. If allowed, then the result (content) produced should be based on the user entitlements associated with request. For non sensitive data, all GET operations are allowed by applications themselves or applications acting on behalf of user.
  2. All update operations (POST/PUT/DELETE) need traceability and entitlements enforced, hence mandates to know who (user) is invoking the APIs. This means only applications acting on behalf of a user could invoke this API. Note, a further entitlement check needs to done with the user associated with the request to see if he/she could actually update that entity.

Let me know if this is clear

spring-projects-issues commented 10 years ago

Nick Weedon commented

In response to question 1: Spring security can be used to achieve this. I already do similar things in my web application with Spring Data Rest & Spring Security. (see the various sections on 'Method Security' in 'http://docs.spring.io/spring-security/site/docs/3.2.1.CI-SNAPSHOT/reference/htmlsingle/')

For example you can add security to methods with annotations such as

@Override
@PreAuthorize("#pk == authentication.id")
CustomerReview findByIdAndName(@Param("pk") Integer pk, String name);

and a simple GET can be secured by (in the case that the Entity uses an ID that is a composite key):

@Override
@PreAuthorize("#pk.getUserId() == authentication.id")
PurchaseOrder findOne(@Param("pk") PurchaseOrderPK pk);

In the case where a composite key is not used then Spring Security result filtering can be used. Apart from 'singleton select' query methods, I prefer to try to avoid filtering by providing the user ID as part of the query where possible since it is more efficient.

Note: Using Spring Security annotations with the 'findOne' method will not currently work without the not yet approved fix in DATAREST-216 (https://jira.springsource.org/browse/DATAREST-216)

I would also suggest disallowing 'findAll' by using:

@Override
@RestResource(exported = false)
Page<QuestionTreeNode> findAll(Pageable pageable);

Note: The above needs the not yet approved fix in DATAREST-217 (https://jira.springsource.org/browse/DATAREST-217)

In response to question 2: The Spring Data Rest @RepositoryEventHandler mechanism can be used to secure POST/PUT/DELETE as well as to provide traceability (by this I am guessing that you mean non-repudiation via auditing). Authorization can be provided by simply annotatating the @HandleBeforeCreate, @HandleBeforeUpdate and @HandleBeforeDelete annotated methods with the appropriate Spring Security annotation (similar to above only the @Secured annotation will probably suffice here).

Here is an example of the above: http://www.ruaghain.com/2013/04/14/security-spring-data-rest

Auditing can be performed in the event handler method itself.

You can also perform stateless authentication using CAS with Spring Security (I havnt tried this yet myself but i see no reason why it would not work).

You are welcome to use the fixes I have made in DATAREST-216 and DATAREST-217 (see the associated pull requests on the tickets) but just be aware that they have not yet been reviewed and therefore I cannot guarantee that will become part of Spring Data Rest.

If the above approach interests you then let me know as I am happy to correspond with you via e-mail in case you have further questions (there are some nice 'tricks' to using a composite key with Spring Data Rest for instance).

spring-projects-issues commented 10 years ago

Sri commented

Thank you @Nick for detailed response.

From the sessions I attended on Spring Security, I thought Spring Security would address the security part of my use case. Below are few takeaways though.

  1. For restricted resources - we need listing of entities, though the list should contain only the one user is entitled to view. That means, implementation of findAll() should be adjusted to return the desired results.
  2. When a resource is accessed /\{repository\}/\{id\} and user is not entitled I would like see 403 error. How can this be addressed?
  3. If business logic is already encapsulated in service layer, it would be duplicate effort to have the same code in @HandleXXX methods. This is only a concern, not a show stopper
spring-projects-issues commented 10 years ago

Nick Weedon commented

For Question 1: If you have to allow findAll() to be used I think you should be able to do this using spring security result filtering. It kind of gives me the willies though, thinking about how inefficient this would be for large result sets since it is effectively O( n ) time complexity when filtering.

Alternatively, you could disable the findAll() method and instead provide a custom query method like findAllByUserId() and annotate this method with a SS method security annotation.

In one sense I would think this would be preferable since it makes more sense for a 'findAll' method to actually return all the records. If you are expecting a filtered subset to be returned without supplying a parameter to 'findAll' then would this not imply that your REST service is stateful?

Would this be a valid solution to your use case?

Question 2: You are in luck :) If you use the DATAREST-216 patch then a HTTP 403 error will automatically be issued if the authorization fails on the spring security method (i.e. you don't need to do anything special, this will happen automatically). Note that I didn't need to do anything special in my fix for this to happen. It appears that this is just the automatic handling of the exception raised by the SS method security annotation.

Question 3: This question is a bit hard for me to answer correctly since I don't really know anything about your application architecture. Forgive me if I misunderstand the question but could you not just use this as a trigger of sorts and just 'call into' your business logic, wherever that may reside (e.g. via a direct call or a call to another REST service in the service layer)? I have the sneaking suspicion that I have somehow missed the point on this last question. Good to know though that this particular issue is not a showstopper for you.

Also: Just thought I would add that I am also pioneering this area myself, so I will keep you updated on my progress if I too delve into this particular use case regarding the use of 'findAll()'.

spring-projects-issues commented 10 years ago

Sri commented

Reply 1: Definitely didn't like filtering with Spring Security for performance reasons you suggested. Also pagination wouldn't be easy> In my case fiindAll() would return all records for a user with admin/master role. Not sure this filtering is anything to do with stateful but I will hear your argument.

Reply 2: Sounds good. Does this send some payload with 403 code?

Reply 3: Our application follows typical MVC model with Repositories, Services (some business logic) and Controllers. SDR approach makes me move the business logic into SDR events. Since SDR architecture is based on CRUD Repistory mapped to Http my case does not apply here

spring-projects-issues commented 8 years ago

François Lecomte commented

Hi there. couldn't wait any longer for this to be fixed... so here's my proposal : a Spring Security extension with beans defining ACL strategies ; easy to plug with Post/PreAuthorize annotations, and able to inject ACL restrictions inside JPA queries (thx to Spring data JPA). noSQL databases are not yet supported, but that shouldn't hurt much. I'm interested in your feedback : https://github.com/lordlothar99/strategy-spring-security-acl