Using Auditing¶
Substance D keeps an audit log of all meaningful operations performed against content if you have an audit database configured. At the time of this writing, “meaningful” is defined as:
- When an ACL is changed.
- When a resource is added or removed.
- When a resource is modified.
The audit log is of a fixed size (currently 1,000 items). When the audit log fills up, the oldest audit event is thrown away. Currently we don’t have an archiving mechanism in place to keep around the items popped off the end of the log when it fills up; this is planned.
You can extend the auditing system by using the
substanced.audit.AuditLog
, writing your own events to the log.
Configuring the Audit Database¶
In order to enable auditing, you have to add an audit
database to your
Substance D configuration. This means adding a key to your application’s
section in the .ini
file associated with the app:
zodbconn.uri.audit = <some ZODB uri>
An example of “some ZODB URI” above might be (for a FileStorage database, if your application doesn’t use multiple processes):
zodbconn.uri.audit = file://%(here)s/auditlog.fs
Or if your application uses multiple processes, use a ZEO URL.
The database cannot be your main database. The reason that the audit database must live in a separate ZODB database is that we don’t want undo operations to undo the audit log data.
Note that if you do not configure an audit database, real-time SDI features such as your folder contents views updating without a manual refresh will not work.
Once you’ve configured the audit database, you need to add an audit log object to the new database. You can do this using pshell:
[chrism@thinko sdnet]$ bin/pshell etc/development.ini
Python 3.3.2 (default, Jun 1 2013, 04:46:52)
[GCC 4.6.3] on linux
Type "help" for more information.
Environment:
app The WSGI application.
registry Active Pyramid registry.
request Active request object.
root Root of the default resource tree.
root_factory Default root factory used to create `root`.
>>> from substanced.audit import set_auditlog
>>> set_auditlog(root)
>>> import transaction; transaction.commit()
Once you’ve done this, the “Auditing” tab of the root object in the SDI should no longer indicate that auditing is not configured.
Viewing the Audit Log¶
The root object will have a tab named “Auditing”. You can view the currently
active audit log entries from this page. Accessing this tab requires the
sdi.view-auditlog
permission.
Adding an Audit Log Entry¶
Here’s an example of adding an audit log entry of type NailsFiled
to the
audit log:
from substanced.util import get_oid, get_auditlog
def myview(context, request):
auditlog = get_auditlog(context)
auditlog.add('NailsFiled', get_oid(context), type='fingernails')
...
Warning
If you don’t have an audit database defined, the
get_auditlog()
API will return None
.
This will add a``NailsFiled`` event with the payload
{'type':'fingernails'}
to the audit log. The payload will also
automatically include a UNIX timestamp as the key time
. The first argument
is the audit log typename. Audit entries of the same kind should share the
same type name. It should be a string. The second argument is the oid of the
content object which this event is related to. It may be None
indicating
that the event is global, and unrelated to any particular piece of content.
You can pass any number of keyword arguments to
substanced.audit.AuditLog.add()
, each will be added to the payload.
Each value supplied as a keyword argument must be JSON-serializable. If one
is not, you will receive an error when you attempt to add the event.
Using The auditstream-sse
View¶
If you have auditing enabled, you can use a view named auditstream-sse
against any resource in your resource tree using JavaScript. It will return
an event stream suitable for driving an HTML5 EventSource
(an HTML 5
feature, see http://www.html5rocks.com/en/tutorials/eventsource/basics/ for more
information). The event stream will contain auditing events. This can be used
for progressive enhancement of your application’s UI. Substance D’s SDI uses
it for that purpose. For example, when an object’s ACL is changed, a user
looking at the “Security” tab of that object in the SDI will see the change
immediately, rather than upon the next page refresh.
Obtain events for the context of the view only:
var source = new EventSource(
"${request.sdiapi.mgmt_path(context, 'auditstream-sse')}");
Obtain events for a single OID unrelated to the context:
var source = new EventSource(
"${request.sdiapi.mgmt_path(context, 'auditstream-sse', query={'oid':'12345'})}");
Obtain events for a set of OIDs:
var source = new EventSource(
"${request.sdiapi.mgmt_path(context, 'auditstream-sse', query={'oid':['12345', '56789']})}");
Obtain all events for all oids:
var source = new EventSource(
"${request.sdiapi.mgmt_path(context, 'auditstream-sse', query={'all':'1'})}");
The executing user will need to possess the sdi.view-auditstream
permission
against the context on which the view is invoked. Each event payload will
contain detailed information about the audit event as a string which represents
a JSON dictionary.
See the acl.pt
template in the substanced/sdi/views/templates
directory
of Substance D to see a “real-world” usage of this feature.