Finer control over Spring Security on Spring Data REST - spring

I have multiple closely related problems in Spring Security. I am developing using Spring Boot and am using Spring Data REST for creating REST endpoints directly from my repositories.
I have multiple entities and the requirement is to have all these entities as REST endpoints. I am letting spring-data-rest handle the creation of these endpoints and I am securing these endpoints by adding #PreAuthorize and #PostAuthorize to the entity repository methods as and where required. This works great when I am calling an endpoint like /entity/id.
But I am facing issues from here. Let's say I have 2 entities, Entity1 and Entity2 and they have a One to One relationship. Spring data rest allows me to fetch the related Entity2 data from Entity1 like /entity1/id/entity2. But I have different access rights over Entity1 and Entity2 and calling the above endpoint only checks the access rights as set up in the repository for Entity1 only. So, if a user has access to Entity1 table and no access to Entity2 table, he can still see some Entity2 data via the foreign key relationship of Entity1. Is this a correct design?
Moreover we have some custom API endpoints wherein we have to aggregate data from multiple entity repositories. Also, these endpoints themselves have to secured. So, I am using a #PreAuthorize over an endpoint method. This works as expected and the endpoint method is called only when the expression is valid. But, when a repository method is called (via a service class of course), the #PreAuthorize over that repository method is also evaluated. I would like to have the check done with at the beginning. Is it possible to do so?
Any suggestions to improving the design is also welcome.

There is no simple solution without massively modifying/overriding lots of default Spring DataRest features. I'm working such a package for years now and it's working quite well for me.
Although switching to this package might be a bit overkill for you, it could worth the trouble in the long run because it also a fixes a lot of problem you will meet only months later.
you can set up permisison rules via annotation directly in the domain objects.
it checks the permisisons in the DB side, so the traffic between the API and DB is heavily decreased (Only those objects are fetched form the DB which the current user has permission to)
you can set READ/UPDATE/DELETE/CREATE permissions separately for roles and/or certain users
you can use pagination on permission filtered collection
you can use pagination on property-collections too
(+ some extra features like flexible search on multiple properties)
here is the package (It's an extension of Spring Data JPA / Data Rest)

Related

JHipster: Persistent entities should not be used as arguments of "#RequestMapping" methods

guys!
I've generated an application using JHipster v7.9.3. However, when this app was evaluated by my SonarQube server, it failed.
One of the most common critical issues is the: RSPEC-4684 Persistent entities should not be used as arguments of "#RequestMapping" methods. This rule states the following:
On one side, Spring MVC automatically bind request parameters to beans declared as arguments of methods annotated with #RequestMapping. Because of this automatic binding feature, it's possible to feed some unexpected fields on the arguments of the #RequestMapping annotated methods.
On the other end, persistent objects (#Entity or #Document) are linked
to the underlying database and updated automatically by a persistence
framework, such as Hibernate, JPA or Spring Data MongoDB.
These two facts combined together can lead to malicious attack: if a persistent
object is used as an argument of a method annotated with
#RequestMapping, it's possible from a specially crafted user input, to
change the content of unexpected fields into the database.
This issue is related to CWE-915
Checking out JHipster docs, I found out this page clarifying that they really uses its domain objects (typically JPA entities) directly in its REST endpoints with the intention to make the code simpler.
I wonder if this rule is still valid or if it doesn't apply anymore for more recent Spring Boot versions (v2.7.*) and I could safely disable rule 4684 at my SonarQube.
The only reference to this rule is this one on Sonar's Jira and it is shown still as Active.

How to share JPA entity across multiple spring boot applications

We would like to share the Employee across both applications exposed as micro services. But Employee has the JPA definition, how do we package this is as a separate jar which can be shared across multiple applications
Spring Boot "AppA" has following entity
#Entity
#Table (name = "employees")
public class Employee {
}
Spring Boot "AppB" fetches Employee from "AppA"
ResponseEntity<Employee[]> response =
restTemplate.getForEntity(
"http://localhost:8080/employees/",
Employee[].class);
Employee[] employees = response.getBody();
You have to wrap the Entity first in a Record and then use
Corba-SCNR version 3 to access it from the other service.
Alternatively, you might want to rethink your
microservice-architecture, as its not good to have two services access
the same entity/database.
Ok, trolling time is over.
To answer your question: you cannot share an Entity over REST between two services in a way that is still giving you the guarantees defined by JPA/Hibernate.
Why is that? Because the EntityManager in JPA/Hibernate creates a wrapper around the Java Object you have, intercepts calls to it and kind of remembers when you change some fields so it knows which sql statements to generate when you "flush" the changes to the database. These wrappers cannot be serialised over your REST Endpoints, at least not in a way that another service could pick them up and continue where the first service stopped.
In general, it is a bad idea to directly expose your JPA Entities in your REST Controllers. I personally prefer to create small DTOs ( Data Transfer Object ) that I fill with the data that I need to expose and only expose those in the REST endpoints.
So best would be to think about "which information does AppB need from the Employee" and put theses in the DTO, then expose them in the Controller of AppA.
If you need to make changes to the Employee in AppB, create a controller in AppA that accepts requests from AppB and then send a request from AppB to AppA.
Depending on the size of the EmployeeDTO you create, you could put it into a shared jar or simply copy it over. Depending on the size of your project, you could also describe it in Swagger/OpenAPI in AppA and generate it in AppB, but this might be overkill.
I hope this helps a bit. Sorry for the trolling before.
If you really need to share them and do not want to copy and paste you can achieve that by packaging your shared entities and repos on a separate Spring project (without an Application.java) and declaring that project in your downstream services as maven/gradle dependency.
Let's say you've put the entities and repos in a separate library under the following packages:
Under a package like my.common.lib.entities, my.common.lib.repos
You can make Spring discover them on your downstream services AppA and AppB by using #ComponentScan typically on your corresponding Spring Application classes:
AppA:
#ComponentScan(basePackages={"my.common.lib.entities", "my.common.lib.repos"})
#SpringBootApplication
ApplicationAppA {
}

Disable setting up Audit fields in create/update requests in Spring Data REST

I'm using combination of various Spring components - Boot (2.3), Data, Data REST, Springdoc. In my model objects I use auditing - I annotate some fields with #CreatedBy, #CreatedDate etc. I would like to disable possibility to set value of those audit fields through REST API. At the same time, I want this information to be available when retrieving data.
Seems like quite obvious thing to do, but I'm unable to find a way to do this. By default I can easily provide those values in API calls and see them persisted.
Ideally, such configuration change would be visible also in OpenAPI spec generated by Springdoc (in model of request).
So it turns out that I'm silly :)
So my error was that authentication and authorization was disabled at that time. Once enabled, I wasn't able to provide values for createdBy and other fields as they were just getting overridden with correct values.
When it comes to OpenAPI specification, I had to annotate fields with:
#Schema(accessMode = Schema.AccessMode.READ_ONLY)
from io.swagger.v3.oas.annotations.media.Schema;. This resulted in correct info. See Swagger view:
I guess the problem comes from your bad design. Please consider your design is correct or not. I guess in your design, besides Spring Data REST endpoints (APIs), there are other code which can create and update your object and save to database.
You question has nothing to do with Spring Data REST. Audit fields annotated with #Createdxx and #LastModifiedxx is auto updated by Spring Data repository, and Spring Data REST just calls the Spring Data repository to persist data.
Answer below two questions helps clarify your design.
Question 1:
If you want to keep create (POST) endpoints which are created by Spring Data REST by default, and you don't want audit fields annotated with #Createdxx to be set, then what code is responsible to set those audit fields?
Assume you send a POST request to create an object, do you want createdBy and createdDate to be null? Or would createdBy and createdDate be updated later by other code?
Question 2:
If you want to keep update (PUT/PATCH) endpoints which are created by Spring Data REST by default, and you don't want audit fields annotated with #LastModifiedxx to be updated, then what code is responsible to update those audit fields? And this also results in imcomplete audit (you make update, but lastModified info not updated).

Two approaches to implementing REST API on Spring

I do REST API on Spring. Took a course in Spring Data Hibernate and found that it made the REST API the most time-consuming way.
When I added a new entity to the domain, I went through the following chain of objects:
Entity - domain object
DTO - for transmitting/receiving an object to/from a client
Mapper - to convert between Entity and DTO
Repository - for interacting with the database
RestController - for processing API requests
Service - service class for the object
The approximate chain of my actions was as follows:
RestController processes requests - receives DTO from the client (in case of creation of a new object)
Mapper in controller converts DTO to Entity
Service is called
Service accesses the Repository
Repository returns the result of execution (created by Entity)
Service returns Entity is created in RestController
RestController returns to the client an object of type ResponseEntity, where I put the body and response code.
As you can see a large chain of actions and a large number of objects.
But then I found out that if you use Spring Data REST, all this doesn't need all the API supplied by Spring from the box. In general, you only need to create an Entity and Repository.
It turns out that for typical CRUD-type operations, I wrote a lot of controllers and their methods in vain.
Questions:
When should I use RestConroller, and when is Spring Data REST?
Is it possible to combine two approaches for one Entity? It turns out that I was wasting my time writing for simple operations like creating, getting, saving, deleting controllers, it can be moved to Spring Data REST.
Will I be able to implement some of the actions that I did in Spring Data Rest in RestConroller? Such as:
Return an entity property value as id instead of object? I mean I have properties for entities that are entities themselves, for these fields I sometimes need to return their ID instead of the whole entity.
Is there any way to control error handling? In RestController I have implemented the ResponseEntityExceptionHandler extension class and all errors wherever they occur in my RestController are handled in the same way in one place and I always know that all errors will return approximately the same response structure.
Data validation will have to be hinged on the fact that it used to be validated on DTOs received from the client. Are there any nuances waiting for me in this regard?
I'm a little stumped on how to move forward. Give me your recommendations and thoughts on this. Push forward on what to use and how.
What Spring Data REST can do for you is scaffolding of the plain repository to rest service. It is much faster, and in theory it should be flexible, but in practice it is hard to achieve something more than REST access to your repositories.
In production I've used Spring Data REST as a wrapper of the database - in a service/microservice architecture model you just wrap-up sometimes the core DB into such layer in order to achieve DB-agnostic Application. Then the services will apply the business logic on top of this wrapper and will provide API for the front-end.
On the other hand Spring Data Rest(SDR) is not suitable if you plan to use only these generated endpoints, because you need to customize the logic for fetching data and data manipulation into Repoitories/Services. You can combine both and use SDR for the "simple" entities, where you need only the basic CRUD over them, and for the complex entities to go with the standard approach, where you decouple the entity from the endopint and apply your custom business logic into the services. The downside of mixing up both strategies is that your app will be not consistent, and some "things" will happen out-of-the-box, which is very confusing for a new developer on this project.
It loooks wasted time and efforts to write these classes yourself, but it only because your app doesn' have a complex database and/or business logic yet.
In short - the "standard" way provides much bigger flexibility at the price of writing repetetive code in the beginning.
You have much more control building the full stack on your own, you are using DTO's instead of returning the entity objects, you can combine repositories in your services and you can put your business logic on the service layer. If you are not doing anything of the above (and you don't expect to in the near future) there is no need for writing all that boilerplate yet over again, and that's when Spring Data REST comes into play.
This is an interesting question.
Spring Data Rest provides abstraction and takes a most of the implementation in its hand. This is helpful for small applications where the business logic resides at the repository layer. I would choose this for applications with simple straight forward business logic.
However if I need fine grained control (eg: transaction, AOP, unit testing, complex business decisions etc. ) at each of the layers as you mentioned which is most often needed for large scale applications I will prefer writing each of these layers.
There is no thumb rule.

Using Spring ACL in a complicated access setup for an entity

I am developing a spring boot application with spring-data-rest being one of the core dependencies. As such, in order to secure the auto generated and manual endpoints I have been using a role based approach and a custom PermissionEvaluator to handle object specific auth checks. This works but is too cumbersome and fails when I need a filtered and paginated response. So, I am planning to implement ACL. Now, I have a rather complicated flow of authorisation for an entity.
The users are mapped to a profile entity(MANY TO MANY). The target entity is also mapped to a separate profile entity(MANY TO MANY) and these 2 profile entities are mapped to each other(MANY TO MANY). To check if a particular user has permission over a target entity object, I need to go through the relationships in my application logic, in my PermissionEvaluator.
Now, if I decide to implement this in ACL only, I am confused as to how to best to do this. The preliminary idea that I had is to create the object list directly in the ACL tables for each user(principle). This would mean that I would need to update the ACL tables everytime with new objects if a permission is changed. Is this a correct approach? Is there a better way to do this? Is it even ok to modify the ACL tables frequently?
Summary: If the logic to check if a user has access over an object is complicated and requires data from other tables, how do I handle it efficiently using ACL?
I'm working on an ACL extension for Spring Data JPA/Rest which handles all of your problems - and many more. It take some time to learn how does it work, but it still needs much less time than creating all of these features for yourself.
You can set up the access rules using annotations in the entity classes - and that's all. it will affect the auto-generated and manual endpoints and even the Data JPA repository-methods.
Spring Data JPA ACL

Resources