Audit log for user read activity


due to a legal requirement (§9 (6) of the Austrian NISG) I not only need to securely log transactions in our RT, but also read access to tickets. Not my choice, and there is no way for me to avoid this.

(I’m looking at RT 4.4 right now)

My research found:

  • The http access log is actually pretty good, as it clearly shows which tickets are accessed and gives you a very readable information on what queries are being made. (both for the web-if, and the REST interface) Of course, it doesn’t really show what was changed, but that is captured by the transactions.

  • The trouble is, there is no information on the querying user in the access log. (in our case, Apache knows nothing about the RT users)

  • I see login events, but that only gives a point-in-time information on IP-address/username pairs. As session cookies can have very long validity, and people might share IP-addresses, that is not enough to link lines from the access log to users.

Options (and that’s why I’m posting in RT Developers):

  1. I could probably fudge things by adding a layer of basic auth in front of RT, either additionally or via $WebRemoteUserAuth replacing the RT login form. That would work, but probably kill a lot of our automation scripting that uses REST. I’m not sure all the clients (command line tool, REST libraries) all support basic auth instead of the cookie based authentication. Nevertheless, that should lead to user names being included in the Apache access log.

  2. Looking into the source code I found the framework for logging SQL queries. e.g. in HandleRequest where LogRecordedSQLStatements is called. It is probably rather easy to code something similar, so that another config option triggers the creation of a log entries with the right information (eg.
    AUDIT( username GET /rt/Ticket/Display.html?id=1317491))

Question to the community:

  • did I miss something?

  • if I go for route 2), I certainly would share my code and would like it to be included in future releases? I really dread to have to apply patches when updating our installation. So, would you accept such patches?



If you only need to know when a user views a ticket could you do something as simple as log when a user goes to the ticket display page?

Just an idea, but what about using a callback to log the information? For example /Elements/Header has a handy callback called Head that you might be able to latch onto that will appear in most pages. From that callback you could get the current user, the ARGS passed in, etc, etc and then log them from there using $RT::Logger. No need to tweak the RT codebase, lets you customise your logging to your local requirements and will survive RT upgrades.

And I’ve made the idea concrete: create a directory chain under your local files called html/Callbacks/loggingAccess/Elements/Header and in that last directory create a file called Head that contains:

$ARGSRef => undef
        "Access by: ".$session{'CurrentUser'}->Name .
        ' to page entitled "' . $ARGS{'Title'} . '"'

Then clear your Mason cache, give the web server a kick and access various pages. On my server this gets logged into /var/log/httpd/error.log but your setup for logging may be different.

Thanks a lot!

I think I’ll go that route and will share back the result.

(I need to cover the REST interface as well, so doing only stuff on the Mason side of things might not be enough.)

You could try using GreenJims code in a callback from lib/RT/Interface/Web->HandleRequest, there is the ‘Default’ callback that will get hit for most every web request including REST

Update: Yes, the callback in HandleRequest works both for Web and REST.

It took me quite a while to understand where to put the callback file if the callback wasn’t configured from a Mason file, but finally got it.

It’s still work in progress (I want to package it as a real RT::Extension), but now I got:

root@rt44:/opt/rt4/local/plugins# cat RT-Extension-AuditLog/html/Callbacks/AuditLog/autohandler/Default
$ARGSRef => undef
my $method = $r->method();
my $q = $r->query();
my @addinfo = ();

if ($method eq 'POST') {        # we need to fetch parameters from body
                "AuditLogDbg: we got parameters ". join(' ', $q->param)

        if (defined($q->param('id'))) {
                push(@addinfo, (' id:' . $q->param('id')));

        "AuditLog: ". join(" ",
        ) );

Thanks for all the help.

1 Like


Feedback is welcome!

1 Like