Is there a way to prevent Hibernate envers to audit a database operation only once?

59 Views Asked by At

Stack:

  1. spring boot 2.7.15
  2. spring-boot-starter-data-jpa 2.7.15
  3. spring-data-envers 2.7.15

I have the following entity:

@Audited
@Entity
@EntityListeners(AuditingEntityListener.class)
public class MyUser{
}

With the auditor config class JpaAuditingConfiguration:

@Configuration
@AutoConfigureAfter(name = "org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration")
@EnableJpaAuditing
public class JpaAuditingConfiguration {

    @Bean
    public AuditorAware<Long> auditorAware(MyUserService myUserService ) {
        return myUserService::getCurrentUserId;
    }

}

And the MyUserService class:

@Service
@RequiredArgsConstructor
public class MyUserService {

    public Optional<Long> getCurrentUserId() {
         Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        if (principal instanceof OAuth2AuthenticatedPrincipal) {
           // logic here
        }
        return Optional.empty();
    }
}

My problem comes here: I have a Bean from our internal authorization/authentication framework which sets/enriches spring's Authentication object and it is triggered when the user logins in the UI. In this bean method, one entity is modified and persisted into the database:

 public Set<String> retrieveUserAuthorities(String username) {
        Optional<MyUser> userOptional = userRepository.findByUsername(username);
        
        if (userOptional.isPresent()) {
            MyUser user = userOptional.get();

            // different setters for user
            myUserService.saveUser(user); // <--- here is the problem

            return ...
        }

      // ...
    }

Problem:

Basically, the problem is that the entity is persisted into the database before the Authentication object is set, thus getting a NPE when the audit flow is triggered and it reaches method getCurrentUserId() where SecurityContextHolder.getContext().getAuthentication() is null.

In my case, I want only this database operation NOT TO BE persisted, but others on that entity to be persisted, so removing Audited or @EntityListeners is not an option.

1

There are 1 best solutions below

1
atish.s On

Maybe a simple null check before accessing the principal object should do the trick.

public Optional<Long> getCurrentUserId() {
    Authentication authentication = SecurityContextHolder.getContext().getAuthentication();

    if (authentication != null && principal instanceof OAuth2AuthenticatedPrincipal) {
       // logic here
    }

    return Optional.empty();
}

It is not surprising to find the authentication object as null in spring security.