Auditing
1 Introduction
Auditing captures the client's interaction with the repository and records all modification events. To enable the auditing of the underlying node and its properties, the node has to be
exo:auditable.
Audit Service saves references to audit records in the child node.
Each
exo:auditable node has its own audit history containing its list of audit records.
eXo JCR introduces the dedicated AuditService and a set of Actions to execute auditing automatically.
2 The Audit Service
The Audit Service covers adding and removing of audit history, adding of audit records as well as the reading of Audit storage.
The service manages audit storage for each repository workspace. The storage is located at the path
/exo:audit in node of type
exo:auditStorage. Each
exo:auditable node history is stored in a storage subnode which are of the type
exo:auditHistory. The history contains a set of record nodes of the type
exo:auditRecord.
Access to the audit storage is restricted to audit administrators (
adminIdentity, see configuration below). To make the audit work the service wraps the audit storage items into Java objects
AuditHistory and
AuditRecord:
public interface AuditService {
void createHistory(Node node) throws RepositoryException;
void removeHistory(Node node) throws RepositoryException;
void addRecord(Item item, int eventType) throws RepositoryException;
AuditHistory getHistory(Node node) throws RepositoryException, UnsupportedOperationException;
boolean hasHistory(Node node);
}
The first 3 methods are used for maintaining the Audit Storage itself and called by dedicated
Actions (see below), while the other 2 are useful for a client's program to audit the review.
2.1 Audit Service configuration
<component>
<key>org.exoplatform.services.jcr.ext.audit.AuditService</key>
<type>org.exoplatform.services.jcr.ext.audit.AuditServiceImpl</type>
<init-params>
<value-param>
<name>adminIdentity</name>
<value>*:/admin</value>
</value-param>
</init-params>
</component>
adminIdentity is a special person or group, which may view internal audit information directly in JCR. Only this identity can read/change Audit History storage directly in workspace.
Only members of adminIdentity can remove exo:auditable nodes.
2.2 AuditHistory class
public class AuditHistory {
public Node getAuditableNode();
public List<AuditRecord> getAuditRecords()
}
2.3 AuditRecord class
public class AuditRecord implements Comparable<AuditRecord> {
public Calendar getDate();
public int getEventType();
public String getUserId()
public String getEventTypeName();
public InternalQName getPropertyName();
public int compareTo(AuditRecord otherRecord);
public String getVersion();
public String getVersionName();
}
2.4 Creating of the history
There are several ways to create auditable nodes. Most of them use the
*actions*? mechanism as a base.
2.4.1 Automatic addition of a mixin and creation of a history during addition of new node.
To make it work, include configuration like described below:
<value>
<object type="org.exoplatform.services.jcr.impl.ext.action.ActionConfiguration">
<fieldname="eventTypes"><string>addNode</string></field>
<fieldname="path"><string>/AuditServiceTest/autoAdd</string></field>
<fieldname="isDeep"><boolean>true</boolean></field>
<fieldname="actionClassName"><string>org.exoplatform.services.jcr.ext.audit.AddAuditableAction</string></field>
</object>
</value>
This action is an one way to monitor node adding events. The event 'addNode' works on node post-creation time and will add mix:auditable mixin to the node. The action records the adding event to the audit history also.
2.4.2 Automatic creation of a history during the addition of a mixin to node.
Similar as the previous action but instead of monitoring addition of a new node we watch for additions of exo:auditable mixin to the node.
<value>
<object type="org.exoplatform.services.jcr.impl.ext.action.ActionConfiguration">
<field name="eventTypes"><string>addMixin</string></field>
<field name="path"><string>/AuditServiceTest/mixin</string></field>
<field name="nodeTypes"><string>exo:auditable</string></field>
<field name="isDeep"><boolean>true</boolean></field>
<field name="actionClassName"><string>org.exoplatform.services.jcr.ext.audit.AuditAction</string></field>
</object>
</value>
2.4.3 Manual addition of mixin and creation of a history.
AuditService service = (AuditService) container.getComponentInstanceOfType(AuditService.class);
Node node = session.getRootNode().addNode("testaudit");
node.addMixin("exo:auditable");
if (!service.hasHistory(node))
service.createHistory(node);
session.save();
2.5 Events Recording
An
AuditAction is a "heart" of the audit system. It actually calls AuditService.createHistory()
and AuditService.addRecord() methods to maintain an Audit Storage. To make it work, include configuration to the AddActionsPlugin, like described below :
<value>
<object type="org.exoplatform.services.jcr.impl.ext.action.ActionConfiguration">
<field name="eventTypes"><string>addProperty,changeProperty,removeProperty</string></field>
<field name="path"><string>/AuditServiceTest</string></field>
<field name="parentNodeType"><string>exo:auditable</string></field>
<field name="isDeep"><boolean>true</boolean></field>
<field name="actionClassName"><string>org.exoplatform.services.jcr.ext.audit.AuditAction</string></field>
</object>
</value>
In this example operations(
addProperty,
changeProperty,
removeProperty and
removeNode ) for the whole /AuditServiceTest tree will be under audit.
Note: If we use deep AuditAction (e.g. for changeProperty) and don't use AddAuditableAction, i.e. add mix:auditable manually. We would encounter an exception for descendant nodes because these nodes do not have the mandatory property exo:auditHistory (of mix:auditable). This property is not autocreated and managed by AuditService only if AuditAction works. As a result we would have the error on Session.save(). But the error can be prevented by using a different AuditAction which audits addMixin operations for a given node and initializes the related audit history.
2.6 Removing auditable nodes and histories of audit
Removing the node history is possible in two modes:
automatic when a special action passes on all subtree and remove their histories before the actual node removal and
manual when all care of removing the history is assigned to the user.
To make it work, include the configuration like described below:
2.6.1 Automatic
<value>
<object type="org.exoplatform.services.jcr.impl.ext.action.ActionConfiguration">
<field name="eventTypes"><string>removeNode</string></field>
<field name="path"><string>/AuditServiceTest</string></field>
<field name="isDeep"><boolean>true</boolean></field>
<field name="actionClassName"><string>org.exoplatform.services.jcr.ext.audit.RemoveAuditableAction</string></field>
</object>
</value>
2.6.2 Manual
AuditService service = (AuditService) container.getComponentInstanceOfType(AuditService.class);
...
Node auditableNode = ....
auditableNode.addMixin("exo:auditable");
.......
if (service.hasHistory(auditableNode))
service.remove(auditableNode);
...
Note: Audit history contains a reference to the auditable node, the removal of an auditable nodes without removal of the corresponding history is impossible.
2.7 Versionable nodes support
The audit service records
mix:versionable nodes information. To read data which is related to version audit use the following methods:
- AuditRecord.getVersion() returns a string with concrete version UUID related to this audit record.
Use Session.getNodeByUUID(String) to obtain the version node instance. Version UUID gets useless if the version has been removed.
- AuditRecord.getVersionName() returns string with Version Name related to this audit record.
The version name is for information purposes only. The version name can be helpful after the version has been removed.
It's string in format: VERSION_NAME 'VERSION_LABEL_1' 'VERSION_LABEL_2' … 'VERSION_LABEL_N'
AuditRecord contains the above-mentioned properties only if auditable node is
mix:versionable, that means added to JCR version control. Otherwise
null will be returned.
If during the node life cycle a version control is disabled the audit history still continues to contain these properties. The
version UUID will not be useful but the
version name still may be used to describe the node state during the audited time.
The service records version information not only for
mix:versionable nodes but also for any item under a version control.
That means that the audit record of an item which has a versionable ancestor contains version related info too.