Hibernate ORM

Using Hibernate to intercept accesses and changes on personal or sensitive data

If your application uses Hibernate we can leverage it to gather insights about how users access or change personal or sensitive data.

In this guide, we'll explore ways of intercepting operations within Hibernate's abstracted relational mapping implementation.

Hibernate Interceptors

The Interceptors allow us to react to certain events that occur within Hibernate.

When these interceptors are registered as callbacks, they provide communication links between Hibernate's session and application. Thus, allowing your application to intercept core operations such as save, update, delete, etc.

Extending the org.hibernate.EmptyInterceptor class

The EmptyInterceptor class (Documentation) has a number of methods which can be used to capture information about the data that is being accessed or modified - these methods include onLoad(), onSave() and onDelete().

For instance, if we want to intercept data saving operations before being executed, but only for the User entity, we override onSave()method like this:

@Override
public boolean onSave(
Object entity,
Serializable id,
Object[] state,
String[] propertyNames,
Type[] types) {
if (entity instanceof User) {
logger.info(((User) entity).toString());
}
return super.onSave(entity, id, state, propertyNames, types);
}

Create an Interceptor to use with Privata.ai

Interceptors can be used in conjunction with our Java SDK, to submit query information to our services. You can follow our example above, the PrivataInterceptor.

It is important to know which user performed a certain operation, for instance, who was the user that deleted a certain entity.

PrivataInterceptor.java
public class PrivataInterceptor extends EmptyInterceptor {
// Instantiate privataAudit
private PrivataAudit privataAudit = new PrivataAudit(
false,
apiUrl
);
protected PrivataInterceptor() {
// Initialise privataAudit with the required variables
this.privataAudit.initialize(dbKey, dbSecret)
}
@Override
public boolean onSave(
Object entity,
Serializable id,
Object[] state,
String[] propertyNames,
Type[] types) {
// We need to know which user is performing the operation on sensitive data
// Context it's an example of an application specific object
// which we can get information about the user
User AuthenticatedUser = Context.getAuthenticatedUser();
if (AuthenticatedUser != null && !AuthenticatedUser.getAllRoles().isEmpty()) {
String auditUser = AuthenticatedUser.getUuid();
String auditRoles = AuthenticatedUser.getAllRoles().toString();
String auditEntity = entity.getClass().getSimpleName();
JsonArray queries = new JsonArray();
JsonObject query = new JsonObject();
JsonArray tables = new JsonArray();
JsonObject table = new JsonObject();
table.addProperty("table", auditEntity);
table.add("columns", new JsonArray(propertyNames));
tables.add(table);
query.add("tables", tables);
query.addProperty("action", "Read");
query.addProperty("timestamp", new Date());
query.addProperty("user", auditUser);
query.addProperty("group", auditRoles;
query.addProperty("returnedRows", 1);
queries.add(query)
this.privataAudit.sendQueries(
queries
);
}
return super.onSave(entity, id, state, propertyNames, types);
}
@Override
public boolean onLoad(
Object entity,
Serializable id,
Object[] state,
String[] propertyNames,
Type[] types) {
User AuthenticatedUser = Context.getAuthenticatedUser();
if (AuthenticatedUser != null && !AuthenticatedUser.getAllRoles().isEmpty()) {
String auditUser = AuthenticatedUser.getUuid();
String auditRoles = AuthenticatedUser.getAllRoles().toString();
String auditEntity = entity.getClass().getSimpleName();
JsonArray queries = new JsonArray();
JsonObject query = new JsonObject();
JsonArray tables = new JsonArray();
JsonObject table = new JsonObject();
table.addProperty("table", auditEntity);
table.add("columns", new JsonArray(propertyNames));
tables.add(table);
query.add("tables", tables);
query.addProperty("action", "Read");
query.addProperty("timestamp", new Date());
query.addProperty("user", auditUser);
query.addProperty("group", auditRoles;
query.addProperty("returnedRows", 1);
queries.add(query)
this.privataAudit.sendQueries(
queries
);
}
return super.onLoad(entity, id, state, propertyNames, types);
}
@Override
public void onDelete(
Object entity,
Serializable id,
Object[] state,
String[] propertyNames,
Type[] types) {
User AuthenticatedUser = Context.getAuthenticatedUser();
if (AuthenticatedUser != null && !AuthenticatedUser.getAllRoles().isEmpty()) {
String auditUser = AuthenticatedUser.getUuid();
String auditRoles = AuthenticatedUser.getAllRoles().toString();
String auditEntity = entity.getClass().getSimpleName();
JsonArray queries = new JsonArray();
JsonObject query = new JsonObject();
JsonArray tables = new JsonArray();
JsonObject table = new JsonObject();
table.addProperty("table", auditEntity);
table.add("columns", new JsonArray(propertyNames));
tables.add(table);
query.add("tables", tables);
query.addProperty("action", "Read");
query.addProperty("timestamp", new Date());
query.addProperty("user", auditUser);
query.addProperty("group", auditRoles;
query.addProperty("returnedRows", 1);
queries.add(query)
this.privataAudit.sendQueries(
queries
);
}
super.onDelete(entity, id, state, propertyNames, types);
}
}

Registering an Interceptor

An interceptor can either be registered as Sessionscoped or SessionFactoryscoped.

A Session-scoped interceptor is specified when a session is opened, and is only used for that specific session.

SessionFactory sessionFactory = entityManagerFactory.unwrap(SessionFactory.class);
Session session = sessionFactory
.withOptions()
.interceptor( new PrivataInterceptor() )
.openSession();

A SessionFactory-scoped interceptor is registered with the Configuration object prior to building the SessionFactory. It will be applied to all sessions opened from that SessionFactory.

SessionFactory sessionFactory = new MetadataSources( new StandardServiceRegistryBuilder().build() )
.addAnnotatedClass( User.class )
.getMetadataBuilder()
.build()
.getSessionFactoryBuilder()
.applyInterceptor( new PrivataInterceptor() )
.build();

External resources

You can read more about Interceptors on the following links: