9

In an application using Spring Data JPA and Spring Data REST, let's say you have an entity class like this:

@Entity
public class Person {

   @Id @GeneratedValue
   private int id;

   private String name;

   @JsonIgnore
   private String superSecretValue;

   ...

}

We want Spring Data REST to expose all of this entity's fields EXCEPT for superSecretValue, and so we've annotated that field with @JsonIgnore.

However, in some cases we DO want access to superSecretValue, and so we create a projection that will return all of the fields including that one:

@Projection(name = "withSecret", types = {Person.class})
public interface PersonWithSecret {

   String getName();
   String getSuperSecretValue();

}

Awesome. So now we can access Person entities including the superSecretValue field like this:

curl http://localhost:8080/persons?projection=withSecret

My question is how can we secure that projection? How can we configure things such that anyone can retrieve Person entities without the superSecretValue field... but only people with a certain role (say, ROLE_ADMIN) can use the projection to retrieve the hidden field?

I've found endless examples of using @PreAuthorize or @Secured annotations to secure Spring Data JPA repository CRUD methods (e.g. save(), delete())... but no examples of how to restrict usage of a Spring Data REST projection.

Steve Perkins
  • 11,520
  • 19
  • 63
  • 95
  • The question has been asked before - http://stackoverflow.com/questions/28794145/spring-data-rest-security-based-projection - I think what you try to do is not supported. Also I would question if you should do it - a projection is just a different view on the data and bringing in security here does not sound right. I would go and implement a custom controller method for this and secure this one. – Mathias Dpunkt Nov 12 '15 at 09:47
  • @MathiasDpunkt: Let's say that you did write a custom controller method... with a return type of `Person`, and the `@ResponseBody` annotation applied to make it serialize to JSON. In this case, the `superSecretValue` field will still be omitted due to the `@JsonIgnore` annotation. So would you manually convert the `Person` entity to a basically-identical DTO class *without* `@JsonIgnore` on that field? Would you implement your own custom Jackson serializer, and build the JSON yourself rather than rely on `@ResponseBody`? Another approach? – Steve Perkins Nov 12 '15 at 17:19
  • 2
    @MathiasDpunkt: It seems like it would HAVE to be a not-too-unusual use case to need role-based visibility on certain fields. Granting role-based access to repository CRUD methods is so simple, it's rather bizarre that granting role-based visibility of entity fields is so cumbersome. Just trying to figure out the path of least resistance. – Steve Perkins Nov 12 '15 at 17:22
  • 4
    Don't get me wrong - I do not think that there is something wrong with the requirement you have. But I think that the way you try to model it could need some consideration. If the `superSecretValue` so different in terms of security than the other person attributes than it might not belong to the person at all. What if you model this value as a separate entity. Then you could provide a separate repository with the appropriate authorization rules and only the authorized users could traverse the relationship from person to the secret value. – Mathias Dpunkt Nov 12 '15 at 18:01
  • Oh, I'm embarrassed that I didn't think of that. A `@OneToOne` surrogate entity, with a different access level. That's not 100% ideal, but gets the job done. – Steve Perkins Nov 12 '15 at 18:29

2 Answers2

2

You can overload properties in projections using @Value with conditional SpEL expressions - as in this already answered similar question.

Consider other alternatives (others already mentioned):

  1. Model refactoring. Split entity by access logic (e.g. Person <-> Account)
  2. Adding custom endpoints for special logic and access checks. For example, the current user at "/people/me".
  3. Customising standard endpoints. For example, add custom controller for "/people", "/people/{id}" that would preprocess and return custom Resource type (DTO) depending on on user authorities (e.g. returning PublicPerson instead Person). Then you can write custom resource processors for adding custom links and custom projections for these types.

See also: issue on this subject from spring-data-rest DATAREST-428.

Community
  • 1
  • 1
aux
  • 1,589
  • 12
  • 20
0

You could try this solution: https://stackoverflow.com/a/35399030/679240

@Projection(name = "detailed", types = User.class)
public interface UserDetailProjection extends UserSimpleProjection{

    @Value("#{@userService.checkAccess(target)? target.email : null}")
    public String getEmail();
}
Community
  • 1
  • 1
Haroldo_OK
  • 6,612
  • 3
  • 43
  • 80