Auditing (from V3.4)

cancel
Showing results for 
Search instead for 
Did you mean: 

Auditing (from V3.4)

resplin
Intermediate
0 0 7,101

Obsolete Pages{{Obsolete}}

The official documentation is at: http://docs.alfresco.com



Core Repository ServicesAuditing


Previous Versions


Links


Auditing (Up to V3.2)

Auditing (from V3.2r)

Audit Filter

Content Auditing


Changes


As of V3.4.0a, the old audit mechanism has been removed.  The old audit tables are not removed, but all code and configuration related to the population of the old tables has been removed.  It is therefore possible to access historically-generated audit data, but all new audit data will be directed to new tables.

Any customized auditing will need to be rewritten using the new configuration files.  All SQL-based queries used in the old mechanism will need to be replaced by calls to the APIs provided; low-level SQL statements to retrieve data is possible but not supported or recommended; any missing search features should be requested.

All discussions below pertain to the 3.4 Audit configuration only.


Introduction


This page describes how Alfresco generates, stores and retrieves general-purpose auditing information.  Technical details are followed by examples showing how the components all work together and how customized audit trails can be generated and queried.  It is possible to skip the technical details and proceed directly to the Worked Examples, although glancing at the architecture would help.

The Java API (AuditService) is not discussed; instead the Audit Web Scripts are used directly to demonstrate functionality, where necessary.

Preliminary note : querying audit data as described later in this article is (by default) restricted to users with administrator privileges.


Technical Details


Architecture


812px

Data Producers include any components that produce data that might be audited.  Data producers do not need to know anything about how the data will be stored.  In short, data is produced and thrown at the AuditComponent.recordAuditValues.  The only requirement is that each packet of data is a Map of data keyed by logical path names, which are specific to the producers.

Although the AuditService search should be used for data retrieval, for completeness, the following tables are used:


  • Tables exclusive to the new audit (AlfrescoPostCreate-3.2-AuditTables.sql)
    • alf_audit_model: Audit configuration files are recorded here.
    • alf_audit_application: An entry for each logical application.  There may be several audit applications defined in a single audit model.
    • alf_audit_entry: Each call to AuditComponent.recordAuditValues will result in an entry here.  There is a reference to a property.
  • Shared tables (AlfrescoPostCreate-3.2-PropertyValueTables.sql)
    • alf_prop_root: Entry point for properties: shared values are fully indexed; arbitrarily-deep collections; quick data query and reconstruction.
    • etc.



Example audit data passed to recordAuditValues():



Root path:
   /alfresco-api/post/NodeService/createStore
Map:
   args/protocol = 'workspace'
   args/identifier = 'SpacesStore'
   result = StoreRef[workspace://SpacesStore]

If the root path passes the initial filtration phase - it is accepted by the Audit Filter AND there is at least one component interested in auditing the information - then the map is expanded.

Example expanded audit data:



Map:
   /alfresco-api/post/NodeService/createStore/args/protocol = 'workspace'
   /alfresco-api/post/NodeService/createStore/args/identifier = 'SpacesStore'
   /alfresco-api/post/NodeService/createStore/result = StoreRef[workspace://SpacesStore]

The filtered data is then passed through the path mappings, generating a new Map of data for each application.

Example path-mapped audit data:



Map:
   /MyApp/createStore = StoreRef[workspace://SpacesStore]

This data is then passed to any extractors and generators to produce a final Map of data that will be persisted.

Example persisted audit data:



Map:
   /MyApp/createStore/value = StoreRef[workspace://SpacesStore]
   /MyApp/createStore/rootNode = NodeRef[workspace://SpacesStore/fd123...]

Configuration Files


Location


Audit configuration files are picked up automatically using the following search paths.


  • classpath*:alfresco/audit/*.xml
  • classpath*:alfresco/enterprise/audit/*.xml
  • classpath*:alfresco/module/*/audit/*.xml
  • classpath*:alfresco/extension/audit/*.xml

The XSD is located at:

  <alfresco.war>/WEB-INF/classes/alfresco/audit/alfresco-audit-3.2.xsd
  http://svn.alfresco.com/repos/alfresco-open-mirror/alfresco/HEAD/root/projects/repository/config/alfresco/audit/

Structure of the Configuration Files


Audit configuration files are split into 4 basic sections:


<DataExtractors>

In this section, DataExtractors are declared for use in the <Application> sections of the configuration files.  A DataExtractor is a component that uses input data to produce some output, either transforming the data or merely outputting the data verbatim.  The simplest extractor is the SimpleValueDataExtractor, which returns whatever data is passed in.  A more complex extractor is the NodeNameDataExtractor, which is able to produce the cm:name value of a node, assuming the data passed in is a NodeRef.  For the complete set of built-in generators, see package org.alfresco.repo.audit.extractor or beans auditModel.extractor.* declared in alfresco/audit-services-context.xml.

The extractors can be declared in-line:



    <DataExtractors>
       <DataExtractor name='simpleValue' class='org.alfresco.repo.audit.extractor.SimpleValueDataExtractor'/>
       ...
    </DataExtractors>

or declared in Spring configuration and referenced in the audit configuration (see alfresco/audit-services-context.xml):



    <DataExtractors>
       <DataExtractor name='simpleValue' registeredName='auditModel.extractor.simpleValue'/>
       ...
    </DataExtractors>

<DataGenerators>

In this section, DataGenerators are declared for use in the <Application> sections of the configuration files.  A DataGenerator is a component that produces data without any input i.e. data is produced when a data path is active, but is independent of the values at that path.  Examples of generators are the AuthenticatedUserDataGenerator, which produces the name of the currently-authenticated user (user in context) and the AuthenticatedPersonDataGenerator, which produces the full name of the currently-authenticated user (person in context).  For the complete set of built-in generators, see package org.alfresco.repo.audit.generator or beans auditModel.generator.* declared in alfresco/audit-services-context.xml.

The generators can be declared in-line:



    <DataGenerators>
       <DataGenerator name='currentUser' class='org.alfresco.repo.audit.generator.AuthenticatedUserDataGenerator'/>
       <DataGenerator name='personFullName' class='org.alfresco.repo.audit.generator.AuthenticatedPersonDataGenerator'/>
    </DataGenerators>

or declared in Spring configuration and referenced in the audit configuration (see alfresco/audit-services-context.xml):



    <DataGenerators>
       <DataGenerator name='currentUser' registeredName='auditModel.generator.user'/>
       <DataGenerator name='personFullName' registeredName='auditModel.generator.personFullName'/>
    </DataGenerators>

<PathMappings>

The expanded map coming from the Data Producers is passed through the path mappings.  This is a raw remapping of the input data based on the path names in the data map.



    <PathMappings>
        <PathMap source='/DOD5015' target='/DOD5015'/>
        <PathMap source='/DOD5015/event/node' target='/DOD5015/event/person'/>
        <PathMap source='/alfresco-api/post/AuthenticationService/authenticate' target='/DOD5015/login'/>
    </PathMappings>

In this example, all paths starting with /DOD5015 are mapped verbatim, but without the declaration the data paths starting with /DOD5015 would be discarded.  Notice that a very small subset of the Alfresco API data is used (only the AuthenticationService.authenticate call) by mapping all values starting with that path to /DOD5015/login.


<Application>

This section defines how the mapped data is to be used by DataGenerators or by DataExtractors.



    <Application name='DOD5015' key='DOD5015'>
        <AuditPath key='login'>
            <AuditPath key='args'>
                <AuditPath key='userName'>
                    <RecordValue key='value' dataExtractor='simpleValue'/>
                </AuditPath>
            </AuditPath>
            <AuditPath key='no-error'>
                <GenerateValue key='fullName' dataGenerator='personFullName'/>
            </AuditPath>
            <AuditPath key='error'>
                <RecordValue key='value' dataExtractor='nullValue'/>
            </AuditPath>
        </AuditPath>
    </Application>

Built-in Data Producers


  • org.alfresco.repo.audit.AuditMethodInterceptor: Generates audit data for all public service API calls.  See the javadocs for the data structure.
  • org.alfresco.repo.node.NodeAuditor: Generates audit data for beforeDeleteNode.
  • org.alfresco.repo.audit.access.AccessAuditor: (from  4.0 community) Generates high level audit data for content and folder events. See Content Auditing

It is possible for any server-side component to pass data to the auditComponent bean.  To see what information is available to audit, enable the following logging:

  log4j.logger.org.alfresco.repo.audit.inbound=DEBUG

The following is some output generated when a node is deleted from the Alfresco Explorer client:



15:55:26,590 User:admin DEBUG [repo.audit.inbound]
Inbound audit values:
/alfresco-node/beforeDeleteNode/node=workspace://SpacesStore/c4728f24-4a11-40f7-9062-315edf959d79
15:55:26,748 User:admin DEBUG [repo.audit.inbound]
Inbound audit values:
/alfresco-api/post/NodeService/deleteNode/no-error=null
/alfresco-api/post/NodeService/deleteNode/args/nodeRef=workspace://SpacesStore/c4728f24-4a11-40f7-9062-315edf959d79

Finding the Code


DEBUG Logging


To see which data is being produced, rejected or recorded, switch DEBUG for:

  log4j.logger.org.alfresco.repo.audit.AuditComponentImpl=DEBUG

JUnit Code


There are few code samples that demonstrate usage of the new Audit APIs and configuration more succinctly than the unit test code:


  • org.alfresco.repo.audit.AuditComponentTest
    • alfresco-audit-test-authenticationservice.xml: This is used by the test to capture both successful and unsuccessful login attempts in the audit data.
    • testAuditAuthenticationService: This demonstrates the use of the auditSearch method.

DOD5015 and Auditing


The DOD5015 module pulls in audit data from the AuthenticationService but adds more data around the individual actions that take place during RM processes.


  • org.alfresco.module.org_alfresco_module_dod5015.audit.*
    • RecordsManagementAuditServiceImpl$RMAuditTxnListener: This transaction listener generates RM-specific data for events (it is a Data Producer).  It generates node property deltas, amongst other things.
    • config/alfresco/module/org_alfresco_module_dod5015/audit/rm-audit.xml: This defines how the data produced by the AuthenticationService and the DOD5015 module is persisted.  There are some custom DataGenerators and DataRecorders.
    • RecordsManagementAuditServiceImpl.getAuditTrailImpl: This method demonstrates how the RM use-case searches the audit data.  Further query extensions are required to extend the search filters available via the auditQuery API.

Worked Examples


Sample Files


Audit sample files are distributed in the Alfresco <tomcat-classes>/alfresco/extension/audit folder.
Samples can also be downloaded  directly from svn:

Samples: http://svn.alfresco.com/repos/alfresco-open-mirror/alfresco/HEAD/root/projects/repository/config/alf...

When using a sample file, remove the .sample extension


Audit Configuration and Environment


Check the state of auditing on the server:



% curl -u admin:admin 'http://localhost:8080/alfresco/service/api/audit/control'
{
   'enabled' : false,
   'applications':
   [
   ]
}

Turning Audit On and Off


Auditing can be globally enabled using the 'control' Web Script, but will be reset when the server restarts.



% curl -u admin:admin -d '' 'http://localhost:8080/alfresco/service/api/audit/control?enable=true'
{
   'enabled' : true
}

Restart Alfresco.  Auditing will be disabled again:



% curl -u admin:admin 'http://localhost:8080/alfresco/service/api/audit/control'
{
   'enabled' : false,
   'applications':
   [
   ]
}

To enable Audit permanently, add this to alfresco-global.properties:



audit.enabled=true

Restart Alfresco.  Auditing will be enabled:



% curl -u admin:admin 'http://localhost:8080/alfresco/service/api/audit/control'
{
   'enabled' : true,
   'applications':
   [
      {
         'name': 'CMISChangeLog',
         'path' : '/CMISChangeLog',
         'enabled' : true
      }       
   ]
}

Alfresco Enterprise customers can use the JMX console to modify the Audit subsystem and changes will be preserved across server restarts.


The Audit Application


Data producers have no knowledge of how - or even if - data will be stored.  Since different use-cases will need to store or modify inbound data independently, the use-cases are separated into Audit Applications; each application defines how data is mapped, extracted and recorded without affecting data required by other applications.  For example, the DOD5015 module records before-and-after values when specific nodes are modified while the CMIS standard requires a slightly different set of data to be recorded.  Additionally, each of the audit logs can be enabled and disabled independently within the same server.  Usually, each Audit Application will be defined in its own configuration file, but for demonstration purposes, multiple Application definitions can be defined in one configuration file.


  • Enable sample file alfresco/extensions/audit/alfresco-audit-example-login.xml.
  • Restart Tomcat.
  • Ensure that the applications have been registered properly and are enabled.

% curl -u admin:admin 'http://localhost:8080/alfresco/service/api/audit/control'
{
   'enabled' : true,
   'applications':
   [
      {
         'name': 'AuditExampleLogin1',
         'path' : '/auditexamplelogin1',
         'enabled' : true
      }
         ,
      {
         'name': 'AuditExampleLogin2',
         'path' : '/auditexamplelogin2',
         'enabled' : true
      }
         ,
      {
         'name': 'CMISChangeLog',
         'path' : '/CMISChangeLog',
         'enabled' : true
      }
        
   ]
}

At an application level, auditing is enabled or disabled for specific paths; changes made to an application's audit state are persisted.  To disable all auditing for an application, disable the root path; in this case, disable the root path for the CMISChangeLog application.  If you restart the server you will see that the application remains disabled.



% curl -u admin:admin -d '' 'http://localhost:8080/alfresco/service/api/audit/control/CMISChangeLog/CMISChangeLog?enable=false'
{
   'enabled' : false
}

Simple Audit Query


Generate some auditing data for the sample applications:


  • Connect to the Alfresco Explorer client
  • Login as 'admin'
  • Logout
  • Login as 'admin' but use an illegal password

Here are two queries to return results, without and with full audited values respectively.  Note that some entries have been removed (...) for brevity.



% curl -u admin:admin 'http://localhost:8080/alfresco/service/api/audit/query/AuditExampleLogin1'
{
   'count':4,
   'entries':
   [
      {
         'id':69,
         'application':AuditExampleLogin1,
         'user':admin,
         'time':'2010-09-20T14:45:28.998+01:00',
         'values':
null
      },
      ...
   ]
}
% curl -u admin:admin 'http://localhost:8080/alfresco/service/api/audit/query/AuditExampleLogin1?verbose=true'
{
   'count':5,
   'entries':
   [
      ...
      {
         'id':72,
         'application':AuditExampleLogin1,
         'user':null,
         'time':'2010-09-20T14:45:43.884+01:00',
         'values':
         {
                     '\/auditexamplelogin1\/login\/error\/user':'admin'
         }
        
      },
      ...
      {
         'id':76,
         'application':AuditExampleLogin1,
         'user':admin,
         'time':'2010-09-20T14:46:23.319+01:00',
         'values':
         {
                     '\/auditexamplelogin1\/login\/no-error\/user':'admin'
         }
        
      }
   ]
}

There is no count function in the search API.  This is by design: Use the limit parameter.

Let us assume that a client wanted to see the details of the latest 2 results but know of the existence of the next 8 results.  In this case, it would be pointless pulling back full (verbose=true) results for the latest 10 entries.  Instead, pull back the last 2 results with values and then pull back the next 8 results without values.  Notice that the response contains a count of the number of entries returned; the individual entries are provided so that the entry IDs can be used for further result retrieval.



% curl -u admin:admin 'http://localhost:8080/alfresco/service/api/audit/query/AuditExampleLogin1?verbose=true&limit=2&forward=false'
{
   'count':2,
   'entries':
   [
      {
         'id':98,
         'application':AuditExampleLogin1,
         'user':admin,
         'time':'2010-09-20T15:10:04.043+01:00',
         'values':
         {
                     '\/auditexamplelogin1\/login\/no-error\/user':'admin'
         }
        
      },
      {
         'id':96,
         'application':AuditExampleLogin1,
         'user':admin,
         'time':'2010-09-20T15:09:50.117+01:00',
         'values':
         {
                     '\/auditexamplelogin1\/login\/no-error\/user':'admin'
         }
        
      }
   ]
}
% curl -u admin:admin 'http://localhost:8080/alfresco/service/api/audit/query/AuditExampleLogin1?verbose=false&limit=8&forward=false&toId=96'
{
   'count':8,
   'entries':
   [
      {
         'id':94,
         'application':AuditExampleLogin1,
         'user':admin,
         'time':'2010-09-20T15:09:47.606+01:00',
         'values':
null
      },
      ...
      {
         'id':80,
         'application':AuditExampleLogin1,
         'user':admin,
         'time':'2010-09-20T14:58:34.305+01:00',
         'values':
null
      }
   ]
}

Advanced Query


The second type of query URL makes use of a data path within the audit application.  This allows entries to be found that match specific audited values.  By default, query values are treated as case-sensitive String types, but it is possible to specify the type to query against.

Generate some audit data:


  • Attempt a failed login as 'joe'

% curl -u admin:admin 'http://localhost:8080/alfresco/service/api/audit/query/AuditExampleLogin1/auditexamplelogin1/login/error/user?verbose=true&value=joe'
{
   'count':1,
   'entries':
   [
      {
         'id':101,
         'application':AuditExampleLogin1,
         'user':null,
         'time':'2010-09-20T15:13:57.947+01:00',
         'values':
         {
                     '\/auditexamplelogin1\/login\/error\/user':'joe'
         }
        
      }
   ]
}
% curl -u admin:admin 'http://localhost:8080/alfresco/service/api/audit/query/AuditExampleLogin1/auditexamplelogin1/login/error/user?verbose=true&value=JOE'
{
   'count':0,
   'entries':
   [
   ]
}

The fromTime and toTime parameters are based on the generic millisecond timestamp format. For example the timestamp 1305899677764 can be converted to a standard time-date format: Fri, 20 May 2011 13:54:37 GMT.


  • Generate some Audit data (note the time before you generate data and grab the time stamp format)


admin # curl -u admin:admin 'http://10.245.240.225:8080/alfresco/service/api/audit/query/AuditExampleLogin1/auditexamplelogin1/login/error/user?verbose=false&fromTime=1305899677764&limit=2'
{  
       'count':2,  
       'entries':   
[
      {
         'id':464,
         'application':AuditExampleLogin1,
         'user':null,
         'time':'2011-05-20T10:52:04.921-04:00',
         'values':null
      },
      {
         'id':466,
         'application':AuditExampleLogin1,
         'user':null,
         'time':'2011-05-20T10:52:12.124-04:00',
         'values':null
      }
   ]
}

Understanding PathMappings


In order to create an audit configuration file, it is necessary to know which data can be audited and how the data is mapped onto your application.  Turn on debugging for the inbound data.  For a better understanding, you can turn on debug logging for the mapping components as well, although this is more verbose.



% cat <tomcat>/shared/classes/alfresco/extension/audit-log4j.properties
log4j.logger.org.alfresco.repo.audit.AuditComponentImpl=DEBUG
log4j.logger.org.alfresco.repo.audit.inbound=DEBUG

Now tail the log file and watch the output.


  • Login as admin

16:47:37,434  DEBUG [repo.audit.inbound]
Inbound audit values:
/alfresco-api/pre/AuthenticationService/authenticate/args/userName=admin
16:47:37,443 User:admin DEBUG [repo.audit.inbound]
Inbound audit values:
/alfresco-api/post/AuthenticationService/authenticate/no-error=null
/alfresco-api/post/AuthenticationService/authenticate/args/userName=admin

From the inbound values (and if you have the AuditComponentImpl debugging on):



16:47:37,445 User:System DEBUG [repo.audit.AuditComponentImpl] Extracted audit data:
   Application: AuditApplication[ name=AuditExampleLogin2, id=7, disabledPathsId=7]
   Raw values:  {/auditexamplelogin2/login=null}
   Extracted:   {}
16:47:37,447 User:admin DEBUG [repo.audit.AuditComponentImpl] New audit entry:
   Application ID: 7
   Entry ID:       130
   Values:         {/auditexamplelogin2/login=null}
   Audit Data:     {/auditexamplelogin2/login/user=Administrator}
16:47:37,447 User:System DEBUG [repo.audit.AuditComponentImpl] Extracted audit data:
   Application: AuditApplication[ name=AuditExampleLogin1, id=6, disabledPathsId=6]
   Raw values:  {/auditexamplelogin1/login/no-error=null, /auditexamplelogin1/login/args/userName=admin}
   Extracted:   {/auditexamplelogin1/login/no-error/user=admin}
16:47:37,449 User:admin DEBUG [repo.audit.AuditComponentImpl] New audit entry:
   Application ID: 6
   Entry ID:       131
   Values:         {/auditexamplelogin1/login/no-error=null, /auditexamplelogin1/login/args/userName=admin}
   Audit Data:     {/auditexamplelogin1/login/no-error/user=admin}

You can see that the AuthenticationService.authenticate method generate two sets of 'inbound' data: the /alfresco-api/pre/AuthenticationService/authenticate data is passed through before the service call is processed; the /alfresco-api/post/AuthenticationService/authenticate data is passed through after the service call has been processed.  When logging in successfully, the post-call data is generated with a no-error path.


  • Perform a failed login with user 'joe'

17:02:09,697  DEBUG [repo.audit.inbound]
Inbound audit values:
/alfresco-api/pre/AuthenticationService/authenticate/args/userName=joe
17:02:09,704  DEBUG [repo.audit.inbound]
Inbound audit values:
/alfresco-api/post/AuthenticationService/authenticate/error=08200014 Failed to authenticate
   Started at:
      org.alfresco.repo.security.authentication.AbstractChainingAuthenticationService.authenticate(AbstractChainingAuthenticationService.java:188)
      ...

This is translated and recorded:



17:02:09,704 User:System DEBUG [repo.audit.AuditComponentImpl] Extracted audit data:
   Application: AuditApplication[ name=AuditExampleLogin1, id=6, disabledPathsId=6]
   Raw values:  {/auditexamplelogin1/login/error=08200014 Failed to authenticate
   Started at:
      org.alfresco.repo.security.authentication.AbstractChainingAuthenticationService.authenticate(AbstractChainingAuthenticationService.java:188)
      ...
17:02:09,704  DEBUG [repo.audit.AuditComponentImpl] New audit entry:
   Application ID: 6
   E6try ID:       135
   Values:         {/auditexamplelogin1/login/error=08200016 Failed to authenticate
   Started at:
      org.alfresco.repo.security.authentication.AbstractChainingAuthenticationService.authenticate(AbstractChainingAuthenticationService.java:188)
      ...
   Audit Data:     {/auditexamplelogin1/login/error/user=joe}

Notice that the failed login did not generate any data for audit application AuditExampleLogin2.  In order to understand this, look at the PathMappings section of the example:



    <PathMappings>
        <PathMap source='/alfresco-api/post/AuthenticationService/authenticate' target='/auditexamplelogin1/login'/>
        <PathMap source='/alfresco-api/post/AuthenticationService/authenticate/no-error' target='/auditexamplelogin2/login'/>
    </PathMappings>

Before any data is considered for persistence, the inbound data paths are remapped using the PathMappings configuration.  The /auditexamplelogin2/login path is mapped onto .../no-error only, so failed logins were not recorded for the AuditExampleLogin2 audit application, while the AuditExampleLogin1 application recorded both successful and failed logins.


What are DataExtractors and DataGenerators?


  • DataExtractor: Uses an inbound mapped value as the source of the data.  AuditExampleLogin1 records values quite literally using the simpleValue data extractor.
  • DataGenerator: Activates when an inbound mapped path is present, but is not dependent on the value on that path.  AuditExampleLogin2 triggers the personFullName generator when the authenticate/no-error path is present; this records the full name of the currently-authenticated user even though the inbound data for authenticate/no-error is null.

Look at the data recorded for the two sample applications:



% curl -u admin:admin 'http://localhost:8080/alfresco/service/api/audit/query/AuditExampleLogin1?verbose=true&forward=false&limit=1'
{
   'count':1,
   'entries':
   [
      {
         'id':137,
         'application':AuditExampleLogin1,
         'user':admin,
         'time':'2010-09-20T17:37:14.699+01:00',
         'values':
         {
                     '\/auditexamplelogin1\/login\/no-error\/user':'admin'
         }
        
      }
   ]
}
% curl -u admin:admin 'http://localhost:8080/alfresco/service/api/audit/query/AuditExampleLogin2?verbose=true&forward=false&limit=1'
{
   'count':1,
   'entries':
   [
      {
         'id':138,
         'application':AuditExampleLogin2,
         'user':admin,
         'time':'2010-09-20T17:37:23.101+01:00',
         'values':
         {
                     '\/auditexamplelogin2\/login\/user':'Administrator'
         }
        
      }
   ]
}

Recording Values


The RecordValue element makes use of the DataExtractor definitions, but specifies when to be activated (dataTrigger) and where to get the data from (dataSource).  Both the dataTrigger and dataSource attributes default to the path of the RecordValue element.  Data is always written to the path where the RecordValue is declared.  So, it is possible to trigger the RecordValue when a data path is present (such as a null value) and then to read a value from a completely different location.


  • Activate sample audit/alfresco-audit-example-extractors.xml.
  • Restart Tomcat (or restart the Audit subsystem on Enterprise).
  • Tail the log to capture createNode calls:

tail -f ../logs/catalina.out | grep -G 'createNode' -A 200 -B 20

  • Login to explorer and add some content under 'Company Home'.

20:18:52,817 User:admin DEBUG [repo.audit.AuditComponentImpl]
New audit entry:
Application ID: 8
Entry ID:       177
Values:        
  /auditexampleextractors/args/properties=...
  /auditexampleextractors/args/assocQName={http://www.alfresco.org/model/content/1.0}alfresco.log
  /auditexampleextractors/args/parentRef=workspace://SpacesStore/37884669-0607-4527-940d-cb34b4f07d75
  /auditexampleextractors/no-error=null
  /auditexampleextractors/args/assocTypeQName={http://www.alfresco.org/model/content/1.0}contains
  /auditexampleextractors/args/nodeTypeQName={http://www.alfresco.org/model/content/1.0}content
  /auditexampleextractors/result=workspace://SpacesStore/37884669-0607-4527-940d-cb34b4f07d75|workspace://SpacesStore/c0fabc6d-903f-4317-87d1-ec62de37089c|...
Audit Data:
  /auditexampleextractors/create/out/a=workspace://SpacesStore/37884669-0607-4527-940d-cb34b4f07d75|workspace://SpacesStore/c0fabc6d-903f-4317-87d1-ec62de37089c|...
  /auditexampleextractors/create/derived/parent-node-name=Company Home
  /auditexampleextractors/create/derived/parent-node-null=null
  /auditexampleextractors/create/in/c={http://www.alfresco.org/model/content/1.0}contains
  /auditexampleextractors/create/in/d={http://www.alfresco.org/model/content/1.0}alfresco.log
  /auditexampleextractors/create/in/a=workspace://SpacesStore/37884669-0607-4527-940d-cb34b4f07d75
  /auditexampleextractors/create/derived/parent-node-type={http://www.alfresco.org/model/content/1.0}folder
  /auditexampleextractors/create/in/b={http://www.alfresco.org/model/content/1.0}content

You can view the audited data using the query API:



% curl -u admin:admin 'http://localhost:8080/alfresco/service/api/audit/query/AuditExampleExtractors?limit=1&forward=false&verbose=true'
{
   'count':1,
   'entries':
   [
      {
         'id':177,
         'application':AuditExampleExtractors,
         'user':admin,
         'time':'2010-09-20T20:18:52.761+01:00',
         'values':
         {
                     '\/auditexampleextractors\/create\/out\/a':'workspace:\/\/SpacesStore\/37884669-0607-4527-940d-cb34b4f07d75|workspace:\/\/SpacesStore\/c0fabc6d-903f-4317-87d1-ec62de37089c|...
                     ,'\/auditexampleextractors\/create\/derived\/parent-node-name':'Company Home'
                     ,'\/auditexampleextractors\/create\/in\/c':'{http:\/\/www.alfresco.org\/model\/content\/1.0}contains'
                     ,'\/auditexampleextractors\/create\/in\/d':'{http:\/\/www.alfresco.org\/model\/content\/1.0}alfresco.log'
                     ,'\/auditexampleextractors\/create\/in\/a':'workspace:\/\/SpacesStore\/37884669-0607-4527-940d-cb34b4f07d75'
                     ,'\/auditexampleextractors\/create\/derived\/parent-node-type':'{http:\/\/www.alfresco.org\/model\/content\/1.0}folder'
                     ,'\/auditexampleextractors\/create\/in\/b':'{http:\/\/www.alfresco.org\/model\/content\/1.0}content'
         }
        
      }
   ]
}

The /no-error path was used as the dataTrigger to activate all the RecordValue elements i.e. the presence of the path triggered the data rather than any specific value.  /create/derived/... audit values show how the parent node reference was used to record values that weren't part of the inbound data set.


Search by Type


Using the previous example, we will show how to search for values that are not Strings.



% curl -u admin:admin 'http://localhost:8080/alfresco/service/api/audit/query/AuditExampleExtractors/ \
                 auditexampleextractors/create/derived/parent-node-type?                              \
                 valueType=org.alfresco.service.namespace.QName&                                      \
                 value=%7Bhttp://www.alfresco.org/model/content/1.0%7Dfolder'
{
   'count':1,
   'entries':
   [
      {
         'id':177,
         'application':AuditExampleExtractors,
         'user':admin,
         'time':'2010-09-20T20:18:52.761+01:00',
         'values':
null
      }
   ]
}

% curl -u admin:admin 'http://localhost:8080/alfresco/service/api/audit/query/AuditExampleExtractors/ \
                 auditexampleextractors/create/in/a?                                                  \
                 valueType=org.alfresco.service.cmr.repository.NodeRef&                               \
                 value=workspace://SpacesStore/37884669-0607-4527-940d-cb34b4f07d75'
{
   'count':1,
   'entries':
   [
      {
         'id':177,
         'application':AuditExampleExtractors,
         'user':admin,
         'time':'2010-09-20T20:18:52.761+01:00',
         'values':
null
      }
   ]
}

Using values that have changed in a post method call (from 3.4.8)


When using the Data Producer org.alfresco.repo.audit.AuditMethodInterceptor (which generates audit data for all public service API calls) it is sometimes useful to be able to audit before and after values in a 'post' call application, or include values from before the call. For example the nodeName data extractor may only be called on a node that exists, so there is no point calling it after a delete.The output of 'pre' call applications is available to 'post' call applications as can be seen in the following example which audits the deletion of nodes and includes the node name. The nodeName is evaluated in the 'pre' call application and simply copied in the 'post' call application.



<Audit
  xmlns='http://www.alfresco.org/repo/audit/model/3.2'
  xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
  xsi:schemaLocation='http://www.alfresco.org/repo/audit/model/3.2 alfresco-audit-3.2.xsd' >

  <DataExtractors>
    <DataExtractor name='simpleValue' registeredName='auditModel.extractor.simpleValue'/>
    <DataExtractor name='nodeNameValue' registeredName='auditModel.extractor.nodeName'/>
  </DataExtractors>

  <PathMappings>
    <PathMap source='/alfresco-api/pre/NodeService/deleteNode' target='/preDelete' />
    <PathMap source='/alfresco-api/post/NodeService/deleteNode' target='/postDelete' />
  </PathMappings>

  <Application name='PreCallDataDelete' key='preDelete'>
    <RecordValue key='nodeName' dataExtractor='nodeNameValue' dataSource='/preDelete/args/nodeRef' dataTrigger='/preDelete/args/nodeRef' />
  </Application>

  <Application name='PostDelete' key='postDelete'>
    <RecordValue key='error' dataExtractor='simpleValue' dataSource='/postDelete/error' dataTrigger='/postDelete/error' />
    <AuditPath key='deleteDetails'>
      <RecordValue key='deletedNodeRef' dataExtractor='simpleValue' dataSource='/postDelete/args/nodeRef' dataTrigger='/postDelete/args/nodeRef' />
      <RecordValue key='nodeName' dataExtractor='simpleValue' dataSource='/postDelete/preCallData/preDelete/nodeName' dataTrigger='/postDelete/preCallData/preDelete/nodeName' />
    </AuditPath>
  </Application>

</Audit>

Note the dataSource attribute of the final <RecordValue> element includes the output path of the 'pre' call application ('preDelete/nodeName'). This is prefixed by 'preCallData/' much like the 'args/' prefix for method arguments. To avoid 'pre' call applications from generating audit records themselves, rather than just generating output for the 'post' call applications, give them a name that starts with 'PreCallData'.