Audit Logging Plugin

22 votes

5% of Grails users

Dependency :
compile ":audit-logging:0.5.5.3"

Source Issues

Summary

Automatically log change events for domain objects.The Audit Logging plugin adds an instance hook to domain objects that allows you to hangAudit events off of them. The events include onSave, onUpdate, onChange and onDelete.When called the event handlers have access to oldObj and newObj definitions thatwill allow you to take action on what has changed.Stable Releases: 0.5.3 (Grails 1.2 or below) 0.5.4 (Grails 1.3 or above) 0.5.5.2 (Grails 1.3 or above) 0.5.5.3 (Grails 1.3 or above)

Installation

$ grails install-plugin audit-logging

Description

The Audit Logging plugin adds Hibernate Events based Audit Logging capabilities to a Grails project and it also adds support to domain models for hooking into the hibernate events system. Support for the following closures are added: onSave, onDelete, and onChange.

The onChange closure can be used in two ways. First, it can be used with no parameters or it can be used with two parameters, oldMap and newMap. The first parameter is a LinkedHashMap representing the old state of the object before the change was applied, the second parameter is a LinkedHashMap representing the state of the object after changes are applied.

NOTE: GORM Events hooks into the ''beforeInsert'' and the ''beforeUpdate'' events which work great for preventing updates but do not work well for ''Audit Logging'' where we would need critical information about the entity that is only available after these actions complete. We've chosen to prefix the handler names with "on" so that they do not conflict with other handler names in other existing plugins.

Compatibility issues

Users of Grails 1.2.x and below should use version 0.5.3 of this plugin. Users of Grails 1.3.x and above should use version 0.5.4 (or greater) of this plugin. If you need to log collection changes, use version 0.5.5 or higher.

Usage

You can use the grails-audit-logging plugin in several ways.

1. Auditing of domain class

static auditable = true

Enables audit logging using the introduced domain class AuditLogEvent which will record insert, update, and delete events. Update events will be logged in detail with the property name and the old and new values.

2. Auditing plus domain event handlers

You may use the optional event handlers in your Domain classes.

Examples:

class Person {
 static auditable = true
 Long id
 Long version

String firstName String middleName String lastName

String email

static constraints = { firstName(nullable:true,size:0..60) middleName(nullable:true,size:0..60) lastName(nullable:false,size:1..60) email(email:true) }

def onSave = { println "new person inserted" // may optionally refer to newState map } def onDelete = { println "person was deleted" // may optionally refer to oldState map } def onChange = { oldMap,newMap -> println "Person was changed ..." oldMap.each({ key, oldVal -> if(oldVal != newMap[key]) { println " * $key changed from $oldVal to " + newMap[key] } }) }//*/ }

3. Use event handlers only (no auditing)

You may choose to disable the audit logging and only use the event handlers. You would do this by specifying in your domain class:

static auditable = [handlersOnly:true]

With "handlersOnly:true" specified, no AuditLogEvents will be persisted to the database and only the event handlers will be called.

Configuration

Auditing Current User Information

As of version 0.5 additional configuration can be specified in the Config.groovy file of the project to help log the authenticated user for various security systems. For many security systems the defaults will work fine. To specify a property of the userPrincipal to be logged as the actor name (the person performing the action which triggered the event, e.g. the username) add these lines to Config.groovy:
auditLog {
  actorClosure = { request, session ->
      session.user?.username
  } 
}

Spring Security Core Plugin 1.1.2 or higher

If you save data from an unprotected URL (configAttribute:IS_AUTHENTICATED_ANONYMOUSLY), the principal is a String-object not a Principal-object. To cope with this behaviour you should use the following Closure definition in Config.groovy:

Import statement:

import org.codehaus.groovy.grails.plugins.springsecurity.SpringSecurityUtils
auditLog {
 actorClosure = { request, session ->
   if (request.applicationContext.springSecurityService.principal instanceof java.lang.String){
    return request.applicationContext.springSecurityService.principal
   }
   def username = request.applicationContext.springSecurityService.principal?.username
   if (SpringSecurityUtils.isSwitched()){
    username = SpringSecurityUtils.switchedUserOriginalUsername+" AS "+username
   }
   return username
 }
}

Spring Security Core 1.0.1 Plugin

Based off of Jorge Aguilera's example, the Spring Security Plugin uses the SpringSecurityService.
auditLog {
  actorClosure = { request, session ->
   request.applicationContext.springSecurityService.principal?.username
  }
}

Acegi Plugin

Thanks to Jorge Aguilera for his example on how to integrate with the Acegi plugin:
auditLog {
 actorClosure = { request, session ->
   return request.applicationContext.authenticateService.principal()?.username
 }
}

CAS Authentication

For example if you are using a system such as CAS you can specify the CAS user attribute using a special configuration property to get the CAS user name. In Config.groovy just add the following lines to the top of the file:
import edu.yale.its.tp.cas.client.filter.CASFilter
auditLog { 
 actorClosure = { request, session ->
   session?.getAttribute(CASFilter.CAS_FILTER_USER)
 }
}
… and the audit_log table will have a record of which user and what controller triggered the hibernate event.

Shiro Plugin

With Shiro, add the following lines to use the currently logged in user's username:
auditLog {
 actorClosure = { request, session ->
   org.apache.shiro.SecurityUtils.getSubject()?.getPrincipal()
 }
}

Other security systems

If you are using a custom authentication system in your controller that puts the user data into the session you can set up the actorClosure to work with your security system instead.

Ignore List

It's possible to configure which properties get ignored for auditing. The default ignore field list is: ['version','lastUpdated']. If you want to provide your own ignore list specify the ignore list like this:
static auditable = [ignore:['version','lastUpdated','myField']]

If instead you want to trigger on version and lastUpdated changes you may specify an empty ignore list:

static auditable = [ignore:[]]

Verbose mode

You can enable verbose mode. If enabled, column by column change logging in insert and delete events is enabled. Old- and new values are stored in detailed to the audit logging table. Enable verbose logging with:
auditLog {
  verbose = true
}
This setting is disabled by default.
Since Version 0.5.5, logging of assotiated objects is implemented.

Logging of assotiated objectIds

Since version 0.5.5, you can log the object-ids of assotiated objects. Logging will be performed in the format: "[id:<objId>]objDetails". You can enable id-logging with
auditLog {
  logIds = true
}

This setting is disabled by default.

Large value column mode

Since version 0.5.5, you can enable large value columns to log more details. If large value columns are enabled, the column limit is 65534 for oldValue and newValue. This setting is especially designed to work with "verbose" mode. If you enabled the large value column mode, you can set TRUNCATE_LENGTH to a value larger 255 and smaller 65535. Enable large value columns in Config.groovy with:
auditLog {
  largeValueColumnTypes = true // use large column db types for oldValue/newValue.
}
Whenever you change the largeValueColumnTypes flag, you need to update your database schema(s).

Property value masking

Since version 0.5.5, you can configure properties to mask on a per-Domain-Class base. If properties are defined as masked, their values are not stored into the audit log table if verbose mode is enabled. Instead, a mask of "**********" will be logged. By default, "password" properties are masked. You can mask property fields in domain classes like this:
static auditable = [mask:'password','otherField']

Verbose log truncation length

If you enabled verbose mode, you can configure the truncation length of detail information in the oldValue and newValue columns (Default is 255). Configure the TRUNCATE_LENGTH in Config.groovy:
auditLog {
  TRUNCATE_LENGTH = 400 // don't forget to enable largeValueColumnTypes if > 255!
  largeValueColumnTypes = true // use large column db types for oldValue/newValue.
}
When you set TRUNCATE_LENGTH to a value > 255 you must enable largeValueColumnTypes (see above). When you forgot to enable large value column mode while setting TRUNCATE_LENGTH > 255, a warning is logged during startup and the setting will be ignored (255 will be used instead).

Renaming the Audit-Log table name

Since version 0.5.5, you can specify the audit log table name. Default is "audit_log". Change the table name in Config.groovy with:
auditLog {
  tablename = "other_audit_log_name"
}

If you change the tablename, you need to update your database schema(s).

Disable Hibernate caching of AuditLogEvent

You can disable the caching of the AuditLogEvents by specifying:
auditLog {
  cacheDisabled = true
}

Transactional AuditLog events

In Config.groovy you may specify whether the Audit Log uses transactions or not. If set to true then the logger will begin and commit transactions around audit log save events. If set to false then the AuditLog will be persisted without a transaction wrapping its call to save. This setting should not be changed from defaults lightly as it can cause problems in integration testing.
auditLog {
  transactional = true 
}

You are only likely to want to change the defaults if you are working with a transactional database in test and production.

Disable auditing by config

Since version 0.5.5.3, you can disable auditing by config. If you disable auditing, event handlers are still triggered but no changes are comitted to the audit log table. This can be used e.g. if you need to bootstrap many objects and want to programmatically disable auditing to not slow down the bootstrap process or if you want to audit log by Enviroment.
auditLog {
  disabled = true 
}
This setting is "false" by default (auditing is enabled).

Example configuration

Example Config.groovy configuration with various settings as described above:
// AuditLog Plugin config
auditLog {
 verbose = true // verbosely log all changed values to db
 logIds = true  // log db-ids of associated objects.
 // Note: if you change next 2 properties, you must update your database schema!       
 tablename = 'my_audit' // table name for audit logs.     
 largeValueColumnTypes = true // use large column db types for oldValue/newValue.
 TRUNCATE_LENGTH = 1000
 cacheDisabled = true
 replacementPatterns = ["local.example.xyz.":""] // replace with empty string.
 actorClosure = { request, session ->
    // SpringSecurity Core 1.1.2
    if (request.applicationContext.springSecurityService.principal instanceof java.lang.String){
       return request.applicationContext.springSecurityService.principal
    }
    def username = request.applicationContext.springSecurityService.principal?.username
    if (SpringSecurityUtils.isSwitched()){
       username = SpringSecurityUtils.switchedUserOriginalUsername+" AS "+username
    }
    return username
 }
}

Continuous Integration Server

We are using a Jenkins CI server hosted at https://robertoschwald.ci.cloudbees.com/job/grails-audit-logging/

Maintainers

Robert Oschwald, Elmar Kretzer, Aaron Long