Search code examples
spring-bootspring-securityauthorization

Spring Security: Best approach to deny access if a user doesn't have a relation with the entity


I wasn't sure how to google this "issue" so I created this question instead.

The application I'm building has the following database structure (simplified for the example).

- Lab1
    - Users    
       - User1
       - User2
    - Products
       - Product1
- Lab2
    - Users 
       - User3
    - Products
       - Product2

Endpoints:
 - ../{labId}/products
 - ../products/{id}
 - ../{labId}/a
 - ../{labId}/b
 - ../{labId}/c
 - ../{labId}/d
 - ../{labId}/...

Users should only have access to data from the lab he's assigned to. User 3 shouldn't be able to get the product data from Product 1.

I've made a function that queries the database to check if a user is indeed associated with the requested lab, but I'll have to call this function on every single endpoint. Which will result in this function getting called a thousand times each minute, because every query a user does will be tied to a lab. This also makes for a lot of duplicate code.

The application is currently secured by using spring security with JWT and also has RBAC to deny certain users access to certain resources.

Is there any way to resolve this using annotations, is my database structure not optimal or is my approach correct?

Many thanks!

Edit: Example screenshot of an endpoint, productrepository is a basic JPA repository.

My concern is the duplication of the getLabAssociatedWithEntity / userBelongsToLab methods and was wondering if this is bad practice.

@GetMapping("/{id}")
fun getProductById(@PathVariable id: Long): ResponseEntity<Product> {
    val lab = getLabAssociatedWithProduct(id)
    if (userBelongsToLab(SecurityContextHolder.getContext().authentication.name, lab.id)) {
        return ResponseEntity.ok().body(productService.getProductById(id))
    }
    return ResponseEntity(HttpStatus.UNAUTHORIZED)
}

 Productservice
 fun getProductById(id: Long): Product {
    return productRepository.findById(id).orElseThrow { ResourceNotFoundException("Product not found with id: $id") }
}

Solution

  • I have decided to resolve this issue by caching the request to see if the user is associated with the lab. This way it doesn't slow down the application.