Using Envers to track only the fact that a column has changed


#1

I have been using Envers for a few years. I appreciate very much this auditing tool, which has nicely replaced the home made auditing stuff I wrote before using the same mechanisms (listeners, …).

I am considering today using it for crypted data. In some tables somes columns are encrypted. I do not (!) want to track the decrypted data in the audit table. I would only like to have a modification flag set to true when the value of such a column/property is modified.

So, to put it another way, I would like to have in my ENTITY_audit table no column for the value, but only the column_mod flag. Is it possible to do that ? I carefully read the doc but found no way to do that.

It could be an additional parameter for @Audited that would be :
@Audited(onlyModifiedFlag=true)

It seems to me that the cleaniest / less dirty way to achieve my goal with the existing version of hibernate could be to provide my own implementation of AuditStrategy. Subclassing the DefaultAuditStrategy or ValidityAuditStrategy, I could only tweak a few methods, like perform, to put dummy data in audited columns. What do you think of this idea ?


#2

It seems to me that the cleaniest / less dirty way to achieve my goal with the existing version of hibernate could be to provide my own implementation of AuditStrategy. Subclassing the DefaultAuditStrategy or ValidityAuditStrategy, I could only tweak a few methods, like perform, to put dummy data in audited columns.

This is definitely one approach and probably the least invasive; however, any audit queries you perform will return an object populated with your dummy data in those fields which may or may not be ideal for you.

It could be an additional parameter for @Audited that would be :
@Audited(onlyModifiedFlag=true)

This is certainly a better long-term option if we wanted to introduce this as part of Envers proper. Doing something like this would allow us to eliminate the data field entirely from the schema; however there is one thing which bothers me about all this:

I am considering today using it for crypted data. In some tables somes columns are encrypted. I do not (!) want to track the decrypted data in the audit table. I would only like to have a modification flag set to true when the value of such a column/property is modified.

The way you describe this, it seems as though you perhaps have some AttributeConverter defined that encrypts and decrypts the data field; however, due to where Envers ties in it is persisting the snapshot of the data before the encryption occurs leading to the audit table’s data being decrypted?

I’m not necessarily opposed to the new annotation attribute; however, I’d like to better understand how you’re doing the encryption/decryption here because that sounds far more like something worth investigating and addressing which may solve your problem and potentially others.


#3

Yes, but as it is some kind of workaround, I can put a dummy value there and document that it is so. So, it is acceptable.

I am using @Convert. And, yes, I am wondering where encryption occurs. In no way do I want to store unencrypted values.

I annotate fields with JPA @Converter.

@Convert(converter = my.app.DatabaseFieldCrypter.class)

#5

That’s for clarifying.

I just tested a simple AttributeConverter shown below:

public class EncryptConverter implements AttributeConverter<String, String> {
	@Override
	public String convertToDatabaseColumn(String attribute) {
		return "HELLO";
	}

	@Override
	public String convertToEntityAttribute(String dbData) {
		return "WORLD";
	}
}

paired with the following entity:

@Entity
@Audited
public class User {
	@Id
	@GeneratedValue
	private Integer id;
	private String userName;
	@Convert( converter = EncryptConverter.class )
	private String password;

    // getter and setters omitted for breavity
}

What I observed is that the converted value of HELLO is pushed into both the User data table and audit table entries. So a @Convert annotation should suffice here and the data should be properly handled in the audit tables unless there is a corner case or some other element involved preventing this in your environment if you observe something otherwise.

Here is the example of my Envers test suite methods:

@Test
@Priority(10)
public void initData() {
	this.userId = doInJPA( this::entityManagerFactory, entityManager -> {
		final User user = new User();
		user.setUserName( "admin" );
		user.setPassword( "WORLD" );
		entityManager.persist( user );
		return user.getId();
	} );
}

@Test
public void testJpaConverterAppliedToAuditField() {
	doInJPA( this::entityManagerFactory, entityManager -> {
		final String password = (String) entityManager
				.createNativeQuery( "SELECT password FROM User_AUD WHERE id = :id" )
				.setParameter( "id", this.userId )
				.getSingleResult();
		assertEquals( "HELLO", password );

		final User user = AuditReaderFactory.get( entityManager ).find( User.class, this.userId, 1 );
		assertEquals( "WORLD", user.getPassword() );
	} );
}

Let me know if you have any further questions. I did add JIRA HHH-12237 for the support for tracking only the modified flag field as requested.


#6

Oh, sorry for being unclear. I did not mean that I was sure that the unencrypted value is stored in audit table. I was wondering if it would be.

Thank you for testing !