Reputation: 1487
For my REST-API within a data-warehouse I need some role-based data access. First lets clarify the requirements with some small example. We define to entities Author
and Book
, both use the PagingAndSortingRepository
for their default behaviour. An author can "own" several books, where an book can only depend to one author.
The simplified entities should look like the following:
@Entity
@Table(name = "Author")
public class Author{
// [..]
@OneToMany(mappedBy = "author")
private List<Book> books;
}
@Entity
@Table(name = "Book")
public class Book{
// [..]
@ManyToOne
@JoinColumn(name = "Author_ID")
private Author author;
}
Then I define two roles User
and Admin
. Normally an author is a common user, so the authorization mechanise only adds a SimpleGrantedAuthority
for the role User
. But there are some special authors which additionally have the role Admin
.
When a normal author with role User
calls the url \books
, he should only get the books he own, while an an author with role Admin
should get all books that exist. Also for the PUT/PATCH/DELETE
request authors with the User
role, should only be able to update / delete their own books, while the Admin
role is able to do this for all books.
My Question: Is there a way to define the data access once in the Controller
class? I know something like that from the Django-Framework
, where I can override the method get_queryset()
, which provides the dataset to work with for every "view"-method (GET/LIST/CREATE/UPDATE/etc.). The way I currently archive this, is to define the methods in the controller for the different API-endpoints and than mange the access there. Which causes two problems:
I thinks this should be a common problem, but I couldn't find a common solution yet. So I am thankful for every advice.
Edit: Example of "secured method"
@RequestMapping(value = "/dimensionAttributeValues", method = RequestMethod.GET)
@ResponseBody
public PagedResources<DimensionAttributeValue> getDimensionAttributeValues(Pageable pageable, PersistentEntityResourceAssembler persistentEntityResourceAssembler) {
Page<DimensionAttributeValue> result;
if (SecurityUtils.userHasRole(ADMIN) || SecurityUtils.userHasRole(TIMEMANAGER)) {
result = dimensionAttributeValueService.getAllDimensionAttributeValue(pageable);
} else {
result = dimensionAttributeValueService.getUserDimensionAttributeValue(SecurityContextHolder.getContext().getAuthentication().getName(), pageable);
}
PagedResources<DimensionAttributeValue> resources;
resources = this.toResource(result, persistentEntityResourceAssembler);
// TODO: Remove dirty Hack!
Link searchLink = linkTo(DimensionAttributeValueController.class).slash("/dimensionAttributeValues/search").withRel("search");
resources.add(searchLink);
return resources;
}
Upvotes: 2
Views: 5145
Reputation: 348
You could add a where condition to your repository call to filter the result by roles. Therefor check this method of the JpaSpecificationExecutor:
Page<T> findAll(@Nullable Specification<T> var1, Pageable var2);
Example (Can't test it right now, but this should lead you in the right direction) :
import static org.springframework.data.jpa.domain.Specifications.where;
..
Specification<Book> roleFilter = (root, query, criteriaBuilder) -> criteriaBuilder.equal(root.get("role"), "Admin");
Page<Book> page = this.yourRepository.findAll(where(roleFilter), pageable);
Upvotes: 0
Reputation: 1895
if you want to do it at repository level, spring security gives you access to the principal in the repo.
You need anyway to define a custom query for that.
Something similar to what described here: https://www.baeldung.com/spring-data-security in chapter 3.2
Otherwise you can add Service layer and than use the @PreAuthorize
annotation
Upvotes: 1