Spring - RESTful provide different entity representations - spring

In advance, I'm not speaking of Content Negotiation. Let's assume I've a simple JPA entity, by the way it is convertible with a related DTO it doesn't matter.
#Entity
public class User {
...
private String email;
private String password;
...
}
I've a RESTful controller with two different routes, a secured one and a public one.
#RestController
public class UserController {
...
#GetMapping("/public")
private User publicRoute() {
return service.getLatestUser();
}
#Secured("...")
#GetMapping("/private")
private User privateRoute() {
return service.getLatestUser();
}
}
For both routes the same entity is returned, but in the first case a public representation, let's say for a user profile, without sensitive stuff like E-Mail and Password should be returned. However in the second case a private representation, let's say for the owner itself, is required.
Is there any elegant way for doing this? I tried it on JSON level with #JsonIgnore but it doesn't worked for me. Also I tried to use Response-Objects, but it results in a lot of boilerplate code! Any suggestions?
See Also:
Recommended by Ananthapadmanabhan there already exists some questions/resources about this topic:
Spring REST webservice serializing to multiple JSON formats
How do I serialize using two different getters based on JsonView in RestController?

You could have different DTO objects being returned from the two endpoints instead of returning the same Entity class, that way you can have control over which attributes should be there in the response.
Read here about the advantages of using a DTO .
Another approach that you could make is to have custom serializers and deserializers for your endpoint.
You could read here for more details.
And here

Ignore dto fields while sending back to controller.
you can write you own method if your object is not final
private User ignoreEmailAndPass(User user){User usr=new User();usr.setName();//send only required fields.}

from Question:
In the database table you can have two roles
Say like User and Owner
3.In the service,check if it is user or owner and get the required details then have the
two DTOs,for each of their information that you want to send,set the info and return.
Or have a Common DTO, conataining all the information and when want to send user info just ignore the other info{Subset} else all.
Tell me what do you think of this solution?

Related

Spring Data REST: Infer owner from authenticated request

I have an #Entity that has a field owner:
#Entity
#EntityListeners({TalkListener.class})
public class Talk {
private #ManyToOne(targetEntity = User.class, optional = false) User owner;
// ...
}
Note: I have a listener in place that implementes #PrePersist. From what I have read it is discouraged to lookup request parameters from there (maybe it is also impossible, I didn't go on researching in this direction). There is the section about events in the docs, but it also seems purely entity-related and not taking the context of the request into account.
I would like to infer the owner from the authenticated user that POSTs the request. What is the easiest way to do so? If it is necessary to override the POST a snippet or linked example would be much appreciated.
Update 1:
#NeilMcGuigan suggested using Spring Data JPA's auditing features, which include a #CreatedBy annotation that would clearly solve the original question (and I would accept it as an answer).
Update 2:
A good tutorial about auditing with Spring Data JPA as well as an alternative of getting the principal within entity lifecycle events can be found here.
For educational purpose, what would be another way if I needed to access some value from the request to populate a value in my entity (say my current use case wasn't #CreatedBy but something different)?

Relax Security for a Spring Data REST Projection

I have a User class and I want to authorize access such that only a user gets to see what he is entitled to.
This was easily achievable using Spring Security in conjunction with Spring Data Rest where in JPA Repository I did below -
public interface UserRepository extends JPARepository<User,Integer> {
#PreAuthorize("hasRole('LOGGED_IN') and principal.user.id == #id")
User findOne(#Param("id") Integer id);
}
In this way, a user when visits to Spring Data REST scaffolded URLs like -
/users/{id}
/users/{id}/userPosts
Only those logged in with {id} get to see these and everyone else gets 401 like I would have wanted.
My problem is that I have one of Projections which is a public view of each user and I am crating it using Spring Data Rest projections as below which I want to be accessible for every {id}
#Projection(name = "details", types = User.class)
public interface UserDetailsProjection {
..
}
So, /users/{id1}?projection=details as well as /users/{id2}?projection=details should give 200 OK and show data even though user is logged in by {id1}
I began implementing this by marking projection with #PreAuthorize("permitAll") but that won't work since Repository has harder security check. Can we have this functionality where for a projection we can relax security ?
I am using latest Spring Data Rest and Spring Security distributions
Seems reasonable to add a custom controller for this use-case.
Please also consider:
Evaluate access in projections using #Value annotations
Add another entity for the same database data but with different field set for read-only operations, e.g. using inheritance (be careful with caching, etc.) - depends on your data storage type
Modify model to split User entity into two different entities (profile, account) since they seem to have different access and possibly even operations
You can also add a ResourceProcessor<UserSummaryProjection> to evaluate access programmatically and replace resource content (projection) with a DTO
Example of evaluating access in projections with #Value annotations:
#Projection(types = User.class, name = "summary")
public interface UserSummaryProjection {
#Value("#{#userSecurity.canReadEmail(target) ? target.email: null}")
String getEmail();
}
Added spring security code in the data access layer is not a good idea. I would suggest you to add the #PreAuthorize annotation to the controller/service method. Since you have a query parameter, ?projection=details, you can have separate controller/service method for the details projection.
Add following to your details projection method:
#RequestMapping("/url", params = {"projection"})
#PreAuthorize("hasRole('LOGGED_IN') and principal.user.id == #id")

ArgumentResolvers within single transaction?

I am wondering if there is a way to wrap all argument resolvers like for #PathVariables or #ModelAttributes into one single transaction? We are already using the OEMIV filter but spring/hibernate is spawning too many transactions (one per select if they are not wrapped within a service class which is be the case in pathvariable resolvers for example).
While the system is still pretty fast I think this is not necessary and neither consistent with the rest of the architecture.
Let me explain:
Let's assume that I have a request mapping including two entities and the conversion is based on a StringToEntityConverter
The actual URL would be like this if we support GET: http://localhost/app/link/User_231/Item_324
#RequestMapping("/link/{user}/{item}", method="POST")
public String linkUserAndItem(#PathVariable("user") User user, #PathVariable("item") Item item) {
userService.addItem(user, item);
return "linked";
}
#Converter
// simplified
public Object convert(String classAndId) {
return entityManager.find(getClass(classAndId), getId(classAndId));
}
The UserService.addItem() method is transactional so there is no issue here.
BUT:
The entity converter is resolving the User and the Item against the database before the call to the Controller, thus creating two selects, each running in it's own transaction. Then we have #ModelAttribute methods which might also issue some selects again and each will spawn a transaction.
And this is what I would like to change. I would like to create ONE readonly Transaction
I was not able to find any way to intercept/listen/etc... by the means of Spring.
First I wanted to override the RequestMappingHandlerAdapter but the resolver calls are well "hidden" inside the invokeHandleMethod method...
The ModelFactory is not a spring bean, so i cannot write an interceptor either.
So currently I only see a way by completely replacing the RequestMappingHandlerAdapter, but I would really like to avoid that.
And ideas?
This seems like a design failure to me. OEMIV is usually a sign that you're doing it wrong™.
Instead, do:
#RequestMapping("/link/User_{userId}/Item_{itemId}", method="POST")
public String linkUserAndItem(#PathVariable("userId") Long userId,
#PathVariable("itemId") Long itemId) {
userService.addItem(userId, itemId);
return "linked";
}
Where your service layer takes care of fetching and manipulating the entities. This logic doesn't belong in the controller.

How to handle DTOs for Request, Response and Entities in Spring MVC

I'm a little bit unsure about how to design a complex Spring MVC application in the best way.
The problem is related to a usermanagement system. My UserVO implements the UserDetails interface of Spring.
But for request and response only a special part of information is needed.
In the request it should be allowed to send the password in order to change it. But flags like 'enabled', 'expired', 'locked' should obviously not be changable by the user.
On the other side this information should be displayed to the user, so must be included in the Response. The password is never send to the client.
I started with using the JsonIgnore and JsonAttribute Annotations on setter- and getter. But as the flags are boolean they are persisted with the default value 'false' every time I update.
Possible solution: Writing a DTO for response as well as for the request and using the ObjectMapper of Spring to persist them. Is this the right approach? I would feel more comfortable if I could just work with my VOs and set some magic annotations if you know what I mean ;)
You probably need a form-backing bean. You will find a ton of examples on internet.
EDIT :
Example with user. Your form contains the username, a field to change the password and a field to reconfirm the password.
The username is in you User POJO wich contains all the data relative to your user.
To catch the password and password confirm from your form (and all other data that you expose from your User POJO you need a form-backing bean).
public class UserBacking {
private String newPsw;
private String confirmPsw;
private User user;
}
In your form
<form:form action="${postUrl}" commandName="userBacking " method="POST">
<!-- Fields goes here -->
</form>
In your Controller, your method will receive as ModelAttribute the UserBacking object.

Exposing entities through services and partial responses

What do you think about exposing domain entities through services? I tried it in an application, but I came to the conclusion that exposing domain model to the client is not such a good idea.
Advantages:
Really easy to transport data from-to client
List item
(De)Serialization is really easy: just put jackson in the classpath and it will handle it. No extra logic is needed.
No need to duplicate entities POJOs. At least in early stages, the API resources will be pretty much the same as the domain model.
Disadvantages:
The API's get very tightly coupled to the model and you can't change the model without affecting the API
Partial responses. There are cases where you don't want to return all the fields of the entities, just some of them. How do you accomplish it?
So, let's take the following REST example. The following API declares that GET on the user resource returns the following information.
GET
/users/12
{
"firstName":"John",
"lastName":"Poe"
"address":"my street"
}
Usually, I would create a User entity, a user service to return the user and a REST controller to serve the request like this:
#RequestMapping("/users/{id}")
public #ResponseBody User getUser(#PathVariable Long id) {
return userService.findById(id);
}
Should I avoid returning the User entity?
If yes, should I create another class and handle myself the mapping between this class and the entity?
Is there a pattern for this?
How to accomplish partial expansion? (i.e. return only the firstName and lastName for the user)
P.S: using #JSONFilter and ObjectMapper to accomplish partial responses seems too heavyweight to me because you loose the beauty of spring data

Resources