19
19
package grails.plugins.orm.auditable
20
20
21
21
import com.fasterxml.jackson.annotation.JsonIgnore
22
+ import grails.compiler.GrailsCompileStatic
22
23
import grails.core.GrailsDomainClass
23
24
import grails.util.GrailsClassUtils
24
25
import groovy.util.logging.Commons
@@ -32,7 +33,6 @@ import org.springframework.context.ApplicationEvent
32
33
import org.springframework.web.context.request.RequestContextHolder
33
34
34
35
import static grails.plugins.orm.auditable.AuditLogListenerUtil.*
35
-
36
36
/**
37
37
* Grails interceptor for logging saves, updates, deletes and acting on
38
38
* individual properties changes and delegating calls back to the Domain Class
@@ -86,6 +86,7 @@ class AuditLogListener extends AbstractPersistenceEventListener {
86
86
}
87
87
88
88
@Override
89
+ @GrailsCompileStatic
89
90
protected void onPersistenceEvent (AbstractPersistenceEvent event ) {
90
91
// GPAUDITLOGGING-64: Even we register AuditLogListeners per datasource, at least up to Grails 2.4.2 events for other datasources
91
92
// get triggered in all other listeners.
@@ -113,6 +114,7 @@ class AuditLogListener extends AbstractPersistenceEventListener {
113
114
}
114
115
}
115
116
117
+ @GrailsCompileStatic
116
118
void stamp (AbstractPersistenceEvent event ) {
117
119
def entity = event. entityObject
118
120
def actor = getActor()
@@ -127,8 +129,8 @@ class AuditLogListener extends AbstractPersistenceEventListener {
127
129
stampLastUpdatedBy(entity,actor,event)
128
130
}
129
131
}
130
-
131
- void stampTimestamp (AbstractPersistenceEvent event ,def entity ){
132
+
133
+ void stampTimestamp (AbstractPersistenceEvent event , def entity ){
132
134
String dateCreatedProperty = GrailsClassUtils . getStaticPropertyValue(entity. class,' _dateCreatedStampableProperty' )
133
135
Class<?> dateCreatedType = GrailsClassUtils . getPropertyType(entity. class,dateCreatedProperty)
134
136
def timestamp = timestampProvider. createTimestamp(dateCreatedType)
@@ -281,6 +283,30 @@ class AuditLogListener extends AbstractPersistenceEventListener {
281
283
return ignore
282
284
}
283
285
286
+
287
+ /**
288
+ * Get the list of auditable properties. This is used to override
289
+ * the default behaviour of auditing all properties except those in the
290
+ * ignore list.
291
+ *
292
+ * static auditable = [auditableProperties: ['dateCreated','lastUpdated','myField']]
293
+ *
294
+ *
295
+ */
296
+ List auditableProperties (domain ) {
297
+
298
+ Map auditableMap = getAuditableMap(domain)
299
+ if (auditableMap?. containsKey(' auditableProperties' )) {
300
+ log. debug " Found auditableProperty list on this entity ${ domain.class.name} "
301
+ def list = auditableMap[' auditableProperties' ]
302
+ if (list instanceof List ) {
303
+ return list
304
+ }
305
+ }
306
+
307
+ null
308
+ }
309
+
284
310
/**
285
311
* The default properties to mask list is: ['password']
286
312
* if you want to provide your own mask list, specify in the DomainClass:
@@ -523,14 +549,11 @@ class AuditLogListener extends AbstractPersistenceEventListener {
523
549
* to provide a list of fields for onChange to ignore.
524
550
*/
525
551
protected boolean significantChange (domain , Map oldMap , Map newMap ) {
552
+ def auditableProperties = auditableProperties(domain)
526
553
def ignore = ignoreList(domain)
527
- ignore?. each { String key ->
528
- oldMap. remove(key)
529
- newMap. remove(key)
530
- }
531
554
boolean changed = false
532
555
oldMap. each { String k , Object v ->
533
- if (v != newMap[k]) {
556
+ if (isPropertyAuditable(k, auditableProperties, ignore) && v != newMap[k]) {
534
557
changed = true
535
558
}
536
559
}
@@ -571,12 +594,13 @@ class AuditLogListener extends AbstractPersistenceEventListener {
571
594
newMap?. remove(' version' )
572
595
oldMap?. remove(' version' )
573
596
597
+ List auditableProperties = auditableProperties(domain)
574
598
List ignoreList = ignoreList(domain)
575
599
576
600
if (newMap && oldMap) {
577
601
log. trace " There are new and old values to log"
578
602
newMap. each { String key , val ->
579
- if (key in ignoreList) {
603
+ if (! isPropertyAuditable( key, auditableProperties, ignoreList) ) {
580
604
return
581
605
}
582
606
if (val != oldMap[key]) {
@@ -598,7 +622,7 @@ class AuditLogListener extends AbstractPersistenceEventListener {
598
622
if (newMap && verbose && ! AuditLogListenerThreadLocal . auditLogNonVerbose) {
599
623
log. trace " there are new values and logging is verbose ... "
600
624
newMap. each { String key , val ->
601
- if (key in ignoreList) {
625
+ if (! isPropertyAuditable( key, auditableProperties, ignoreList) ) {
602
626
return
603
627
}
604
628
def audit = getAuditLogDomainInstance(
@@ -618,7 +642,7 @@ class AuditLogListener extends AbstractPersistenceEventListener {
618
642
if (oldMap && verbose && ! AuditLogListenerThreadLocal . auditLogNonVerbose) {
619
643
log. trace " there is only an old map of values available and logging is set to verbose... "
620
644
oldMap. each { String key , val ->
621
- if (key in ignoreList) {
645
+ if (! isPropertyAuditable( key, auditableProperties, ignoreList) ) {
622
646
return
623
647
}
624
648
def audit = getAuditLogDomainInstance(
@@ -822,4 +846,24 @@ class AuditLogListener extends AbstractPersistenceEventListener {
822
846
AuditLogListenerThreadLocal . clearAuditLogDisabled()
823
847
}
824
848
}
849
+
850
+ /**
851
+ * Returns a boolean indicating if the given property should be audited or ignored.
852
+ * If there is an auditableProperties list the property is auditable if it is in that
853
+ * list. Otherwise the property is auditable if is is not in the ignoreList.
854
+ * @param fieldName The field name
855
+ * @param auditableProperties List of auditable Properties
856
+ * @param ignoreList The ignoreList
857
+ * @return true if property is auditable
858
+ */
859
+ boolean isPropertyAuditable (def fieldName , List auditableProperties , List ignoreList ) {
860
+ if (auditableProperties) {
861
+ return fieldName in auditableProperties
862
+ } else if (ignoreList) {
863
+ return ! (fieldName in ignoreList)
864
+ }
865
+
866
+ true
867
+ }
868
+
825
869
}
0 commit comments