How to disable or edit the spring-data-rest HATEOAS default profile link - spring

I currently have an entity in my project named "profile" (Using JPA); however api/profile directs you to the application levels detail. I don't have an option to rename the object's repository using #RepositoryRestResource at this time. Is there a way in the configuration to disable or rename the application level deatils endpoint in Spring Data REST?
"profile" : [ {
"href" : "http://localhost:8080/api/profile{?page,size,sort}",
"templated" : true
}, {
"href" : "http://localhost:8080/api/profile"
} ],
Selecting the profile link directs me to
}, {
"rel" : "profile",
"href" : "http://localhost:8080/api/profile/profile"
},
Using spring 4.2.1 RELEASE, spring-data-jpa 1.9 & spring-data-rest-core-2.4.0 RELEASE

You can edit the root resource links with a ResourceProcessor<RepositoryLinksResource>
Like this :
#Component
public class RootResourceProcessor implements ResourceProcessor<RepositoryLinksResource> {
#Autowired
private final EntityLinks entityLinks;
#Override
public RepositoryLinksResource process(final RepositoryLinksResource resource) {
final Link link = entityLinks.linkToCollectionResource(YourResource.class);
resource.getLinks(); //the root links
return resource;
}
}

Related

How to customize Spring Data REST to use a custom path for a repository resource?

I'm developing a microservice using SPRING DATA REST where I need to sub-resources to be accessible only thru main resource like the following structure :
http://localhost:8080/posts
http://localhost:8080/posts/1/comments
and block direct access to sub-resource directly like this http://localhost:8080/comments/*.
where comments must be accessible only thru related poste , I did the following in my repository :
#RepositoryRestResource(collectionResourceRel = "posts", path = "posts")
public interface PostRepository extends PagingAndSortingRepository<Post, Long> {
...
}
for comments :
#RepositoryRestResource(collectionResourceRel = "comments", path = "comments")
public interface CommentRepository extends PagingAndSortingRepository<Comment, Long> {
...
}
now by default SPRING DATA REST returns the following results when i goto to : http://localhost:8080
{
"id": 1,
"content": "some post text .........................",
"author":"abc",
"_links": {
"self": {
"href": "http://localhost:8080/posts/1"
},
"attributes": {
"href": "http://localhost:8080/posts/1/comments"
}
}
}
now if i want to post a comment i need to do the following :
http://{server:port}/comment METHOD:POST
{"author":"abc","content":"PQROHSFHFSHOFSHOSF", "post":"http://{server:port}/post/1"}
but what i need to achienve is to POST to an url like this http://{server:port}/posts/1/comment where the POST is the root resource NOT like the previous path http://{server:port}/comment
http://{server:port}/posts/1/comment METHOD:POST
{"author":"abc","content":"PQROHSFHFSHOFSHOSF", "post":"http://{server:port}/post/1"}
i now that this is possible if a create a custom comment #controller but i want to use the already building features of SPRING DATA REST and Hateoas support .

Spring REST repository shows wrong URL

I developed sample application with Spring Boot. I've one abstract class (Employee) and two concrete subclasss for example full time and part time employee.
I preferred a joined type of inheritance and 3 table created by JPA provider.
Also I created REST repository for Employee. Looks like below:
package com.caysever.repository;
import com.caysever.model.Employee;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;
/**
* Created by alican on 04.05.2017.
*/
#RepositoryRestResource(path = "employee")
public interface EmployeeRepository extends JpaRepository<Employee, Long>{
}
When I invoke the **/employee** URL in browser, I'm getting content as below:
{
"fullTimeEmployees" : [ {
"name" : "Alican",
"surname" : "Akkuş",
"birthDay" : "2017-05-04T12:37:20.189+0000",
"gender" : "MALE",
"totalWorkingHoursOfWeek" : 40,
"_links" : {
"self" : {
"href" : "http://localhost:8080/fullTimeEmployee/1"
},
"fullTimeEmployee" : {
"href" : "http://localhost:8080/fullTimeEmployee/1"
}
}
}
When I invoked this URL for first employee localhost:8080/fullTimeEmployee/1 , I getting the 404 status code, not found. But I will getting the first employee with this URL localhost:8080/employee/1.
You can see all codes at GitHub -> https://github.com/AlicanAkkus/jpa-inheritance-strategy
Why Spring REST generates the fullTimeEmployee URL?
I think that with #RepositoryRestResource you are modifying the export details, such as using /empoyee instead of the default value of /fullTimeEmployee
Try with
#RepositoryRestResource(collectionResourceRel = "fullTimeEmployees", path = "fullTimeEmployees")
Or if you want to use /employee
#RepositoryRestResource(collectionResourceRel = "employee", path = "employee")
The path is sets the segment under which this resource is to be exported and the collectionResourceRel sets the value to use when generating links to the collection resource.
Hope this helps
A workaround for this is to add repository interfaces for the concrete classes, sharing the path of the superclass repository.
#RepositoryRestResource(collectionResourceRel = "employee", path = "employee")
public interface FullTimeEmployeeRepository extends JpaRepository<FullTimeEmployee, Long> {
}
#RepositoryRestResource(collectionResourceRel = "employee", path = "employee")
public interface PartTimeEmployeeRepository extends JpaRepository<PartTimeEmployee, Long> {
}
This will generate the links with the "employee" path regardless of the subclass type.
"_links" : {
"self" : {
"href" : "http://localhost:8080/employee/1"
},
"fullTimeEmployee" : {
"href" : "http://localhost:8080/employee/1"
}
}
I don't know if there is another way to work around the issue.

Spring Data Rest, SpringFox and JpaRepository custom finders

NB: using Spring Boot 1.4.2 + SpringFox 2.6.0
Hi, I'm having an issue with Swagger 2 forms on my API documentation over a #RepositoryRestResource. The code below works fine (REST access OK):
#RepositoryRestResource(collectionResourceRel = "people", path = "people")
public interface PersonRepository extends JpaRepository<Person, Long> {
Person findByLastName(#Param("name") String name);
}
And the HATEOAS links are right too: calling URL /api/people/search
ends up with this (notice parameter "name"):
{
"_links": {
"findByLastName": {
"href": "http://localhost:8080/api/people/search/findByLastName{?name}",
"templated": true
},
"self": {
"href": "http://localhost:8080/api/people/search"
}
}
}
The REST API is ok: URL /api/people/search/findByLastName?name=foobar returns data when executed with a browser
BUT in Swagger the GET parameter type is interpreted as "body" instead of "query" and the form submission (curl ... -d 'foobar'...) fails in 404, attempting to submit "name" as request body.
So I tried to set Swagger explicitly, like this:
#RepositoryRestResource(collectionResourceRel = "people", path = "people")
public interface PersonRepository extends JpaRepository<Person, Long> {
#ApiOperation("Find somebody by it's last name")
#ApiImplicitParams({
#ApiImplicitParam(name = "name", paramType = "query")
})
Person findByLastName(#Param("name") #ApiParam(name = "name") String name);
}
without any success, despite the fact that "name" is well retained in the form as the parameter name in this example :-(
body parameter type on GET query
Does anyone know what could be done to make that Swagger form to work? Thx for your help
This is it : #Param configures Spring Data REST, while #RequestParam fits Swagger
#RepositoryRestResource(collectionResourceRel = "people", path = "people")
public interface PersonRepository extends JpaRepository<Person, Long> {
// #Param Spring Data REST : Use #Param or compile with -parameters on JDK 8
// #RequestParam Swagger : paramType=query cf. $Api*Param
Person findByLastName(#Param("name") #RequestParam("name") String name);
}
Me happy!

Spring Data Rest Secure HATEOAS link

Lets say I have an User entity with a ManyToMany mapping to UserGroup entity. If I create repositories for both entities and GET the URI /users/1, I get a response like this:
{
"enabled" : true,
"password" : "xxxxxx",
"username" : "xxxxxx",
"credentialsNonExpired" : true,
"accountNonLocked" : true,
"accountNonExpired" : true,
"_links" : {
"self" : {
"href" : "http://127.0.0.1:45950/users/1"
},
"user" : {
"href" : "http://127.0.0.1:45950/users/1"
},
"userGroups" : {
"href" : "http://127.0.0.1:45950/users/1/userGroups"
}
}
}
The userGroups link here is really useful.
I can list all UserGroups using the /userGroups endpoint.
I would like to protect the /userGroups endpoint and /users/1/userGroups endpoint using different spring-security expressions.
Using the reference here: http://docs.spring.io/spring-data/rest/docs/current/reference/html/#security I understand how to secure the first endpoint:
public interface UserGroupRepository extends PagingAndSortingRepository<UserGroup, Long> {
#PreAuthorize("hasRole('ROLE_ADMIN')")
#Override
Iterable<T> findAll();
}
But how do I secure the second endpoint? Is that even possible currently? Is there some work planned on such a feature. I would love to contribute.
I have also encountered this problem and not found any solution working with Spring's security annotations. As a workaround, I have added something along the line of:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
// Whatever config you already have.
.authorizeRequests()
.antMatchers("/users/*/userGroups").hasRole("ADMIN");
}
to my implementation of WebSecurityConfigurerAdapter. While this works, it duplicates the work done when securing the repositories and it is easy to forget adding both the java config and annotations when adding new repositories.
I realize this question is four years old, but this issue has continued to plague me, and I finally found something that works.
The problem essentially comes down to the fact that Spring HATEOAS has no real security controls; while you can configure the repositories with the appropriate method-level security annotations to prevent access via the REST API, you can't stop the HATEAOS representation model assembler from slurping up any objects it can see.
The solution I found was to expose a RepresentationModelProcessor bean for EntityModel<DomainObject>s. In your case, this may look something like
#Configuration
public class SecureHateoasConfig {
public static class UserGroupProcessor implements RepresentationModelProcessor<EntityModel<UserGroup>> {
#Override
public EntityModel<UserGroup> process(EntityModel<UserGroup> model) {
if(SecurityContextHolder.getContext()
.getAuthentication()
.getAuthorities()
.stream()
.map(GrantedAuthority::getAuthority)
.anyMatch("ROLE_ADMIN"::equals)) {
return model;
} else {
return null;
}
}
}
#Bean
public UserGroupProcessor userGroupProcessor() {
return new UserGroupProcessor();
}
}
Objects replaced by null seem to get filtered out after the normal processing, so embedded entities simply don't include the objects which don't match your filter!
There's probably a much easier way to filter links if you're only doing role-based security, but I'm afraid I don't know it...
Of course, it's possible to extend this considerably, since your secure object processor need not only apply to EntityModel<UserGroup>! In my case I have an interface all my secure domain objects implement which can be used to make security decisions, so only one RepresentationModelProcessor implementation/bean is required.

spring hateoas generates different responses for collection or pojo

I have two classes
import org.springframework.hateoas.ResourceSupport;
public class A{}
public class B{}
public class AResource extends ResourceSupport {
private final A a;
}
public class BResource extends ResourceSupport {
private final B b;
}
#Controller
public class Controller {
#RequestMapping
#ResponseBody
public Set<AResource> giveMeColl() {
}
#RequestMapping
#ResponseBody
public BResource giveMeSingle() {
}
}
both responses add links object but for resource A is "links" and for resource B is "_link" and also structure changes
//RESPONSE FOR A
[
{
"content":{
//my fancy object
},
"links":[
{
"rel": "self",
"href": "http://localhost:8080/myid/22"
}
]
}
]
{
"content":{
//my fancy object
},
"_links":[
{
"self": "http://localhost:8080/myid/22/someelse/33"
}]
}
both resources are constructed with assemblers and both are adding the link from the ids
AResource aresource = new AResource(a);
resource.add(linkTo(methodOn(Controller.class).giveMeColl()).withSelfRel());
BResource bresource = new BResource(b);
resource.add(linkTo(methodOn(Controller.class).giveMeSingle()).withSelfRel());
Response headers for a is
"content-type": "application/json;charset=UTF-8"
and for b
"content-type": "application/hal+json;charset=UTF-8"
Could it be because returning an array is not really Restful? as Some post suggest
p.s. I have added and removed #EnableHypermediaSupport but doesn't seem to affect the problem.
"_links" follows the HAL specification. Spring HATEOAS includes a dedicated serializer for that, but it is only used for classes that extend ResourceSupport.
Returning a simple array is not exactly "unRESTful", but it doesn't meet the REST maturity level 3 (Hypermedia controls). In order to achieve that you can wrap the collection into a Resources instance, which extends ResourceSupport. You should get the same link serialization for both types then.

Resources