0

I'm writing a spring application which uses spring-data-rest to expose a restish API for CRUD operations on my entities.

I'm using role based permissions and most of my entity repositories have either a @PreAuthorize("hasAnyRole('ADMIN') or @PreAuthorize("hasAnyRole('USER') annotation to restrict access.

For updatng the Person entity (containing username, password and roles), I would like to have a slightly more complicated logic:

  • users with the ADMIN role may do everything
  • a user should be able edit their own Person entity (except the role property)
  • only ADMINs can edit the role property

I've tried to implement those conditions in a custom isUpdateAllowed method

    @PreAuthorize("@personAuthorizationChecker.isUpdateAllowed(#entity,principal)")
    @Override
    <S extends Person> S save(S entity);
    public boolean isUpdateAllowed(Person updatedEntity, PersonSecurityDetails principal) {
        if (principal.getAuthorities().contains(new SimpleGrantedAuthority("ROLE_" + Role.ADMIN))) {
            return true;
        }
        
        Person existingState = personRepository.getById(updatedEntity.getId());

        if (updatedEntity.getRoles() != existingState.getRoles()) {
            return false;
        }
        if (loggedInPersonProvider.getLoggedInUser().getId() == principal.getId()) {
            return true;
        }
        return false;
    }

The problem I'm experiencing is that Person existingState = personRepository.getById(updatedEntity.getId()); returns the exact same object as the updatedEntity argument. So I can't check the changes requested by the client.

I'm surprised by this. I was expecting that updatedEntity contains the changes requested by the client, while existingState contains the currently persisted state in the database. But when I look in the debugger, both are the exact same object.

enter image description here

Even though I'm sure that the requested changes are not already persisted to the DB.

How can I force receiving an updated entity from the DB?

Thanks in advance!

Simon Lenz
  • 2,732
  • 5
  • 33
  • 39
  • By detaching the updatedEntity before querying for the entity, you will ensure that you receive the current state of the entity from the database. – Dhruman Desai Jul 10 '23 at 11:03
  • Indeed, I do get the state from the DB when calling `entityManager.detach(updatedEntity); Person existingState = personRepository.getById(updatedEntity.getId()); entityManager.refresh(existingState);`. But then I get a `is org.springframework.data.mapping.MappingException: Couldn't find PersistentEntity for type class Person$HibernateProxy$9fwrEh8h!` when the JpaRepository actually tries to persist the entity after my PreAuthorize method returned true. Any idea how to resolve this? I keep thinking this should be a simple task, and I'm overcomplicating things – Simon Lenz Jul 10 '23 at 22:11
  • Workaround for me is now to run a native query directly with jdbc. – Simon Lenz Jul 13 '23 at 16:24

0 Answers0