r=zach
- add audit_trail tracking for admin actions
This commit is contained in:
ccooper%deadsquid.com 2007-07-30 19:36:53 +00:00
Родитель 19cda99390
Коммит 58cea88cf8
3 изменённых файлов: 158 добавлений и 9 удалений

Просмотреть файл

@ -61,6 +61,27 @@ sub litmus_locations {
};
}
# Enabled (only) admin action tracking (auditing).
# NOTE: this can be overridden in localconfig
our $AUDIT_TRAIL = 1;
# This hash contains a list of database queries to ignore when auditing. By
# default, we don't care about the initial INSERT of test_results. We're more
# concerned about changes to testcases (and subgroups, etc.) than test_results.
# NOTE: this can be overridden/extended in localconfig
our %AUDIT_ACTIONS_TO_IGNORE = (
'INSERT' => [
'test_result', # This happens to cover all the subsidiary
# tables as well due to the nature of the
# regexp.
'audit_trail',
],
'UPDATE' => [
],
'DELETE' => [
],
);
our $localconfig = litmus_locations()->{'localconfig'};
do $localconfig;
@ -72,4 +93,5 @@ our $disabled = 0;
# Set/unset this to display inline debugging value/code.
our $DEBUG = 0;
1;

Просмотреть файл

@ -30,6 +30,119 @@
=cut
#########################################################################
# We're overiding at the base level so we can subclass some functions to grab
# auditing information automatically.
package AuditDBI;
use strict;
use base 'DBIx::ContextualFetch';
#########################################################################
package AuditDBI::db;
use base 'DBIx::ContextualFetch::db';
use Litmus::Config;
sub do {
my ($dbh) = shift;
my ($sql) = shift;
my ($attr) = shift;
my @bind_values = @_;
my $rv = $dbh->SUPER::do($sql,$attr,@bind_values);
if ($rv and $Litmus::Config::AUDIT_TRAIL) {
my $audit_rv = $dbh->_audit_action($sql,@bind_values);
}
return $rv;
}
sub _audit_action {
my ($dbh) = shift;
my ($sql) = shift;
my @bind_values = @_;
my ($action_type) = ($sql =~ /^(INSERT|UPDATE|DELETE)/i);
$action_type = uc($action_type);
if (&_ignore_this_action($action_type,$sql)) {
return 1;
}
my $user = Litmus::Auth::getCurrentUser();
if (!$user or
!$user->isInAdminGroup()) {
return 1;
}
my $bind_values_string = &_bind_values_to_string(@bind_values);
my $audit_sql = "INSERT INTO audit_trail (user_id,action_timestamp,action_type,sql_log,bind_values) VALUES (?,NOW(),?,?,?)";
my $rv = $dbh->SUPER::do($audit_sql,
undef,
$user->{'user_id'},
$action_type,
$sql,
$bind_values_string
);
return $rv;
}
sub _bind_values_to_string {
my $bind_values_string = "";
foreach my $bind_value (@_) {
if ($bind_values_string ne "") {
$bind_values_string .= ",";
}
if ($bind_value =~ /^\d+$/) {
$bind_values_string .= $bind_value;
} else {
$bind_values_string .= "'" . $bind_value . "'";
}
}
return $bind_values_string;
}
sub _ignore_this_action {
my ($action_type,$sql) = @_;
if (%Litmus::Config::AUDIT_ACTIONS_TO_IGNORE) {
if ($Litmus::Config::AUDIT_ACTIONS_TO_IGNORE{$action_type} and
scalar $Litmus::Config::AUDIT_ACTIONS_TO_IGNORE{$action_type} > 0) {
foreach my $table_name (@{$Litmus::Config::AUDIT_ACTIONS_TO_IGNORE{$action_type}}) {
if ($sql =~ /^$action_type\s+(INTO|FROM|)\s*$table_name/i) {
return 1;
}
}
}
}
return 0;
}
#########################################################################
package AuditDBI::st;
use base 'DBIx::ContextualFetch::st';
use Litmus::Config;
sub execute {
my ($sth) = shift;
my @bind_values = @_;
if ($sth->{Statement} =~ /^(INSERT|UPDATE|DELETE)/i) {
my $rv = $sth->SUPER::execute(@bind_values);
if ($rv and $Litmus::Config::AUDIT_TRAIL) {
my $dbh = $sth->{Database};
my $audit_rv = $dbh->_audit_action($sth->{Statement},@bind_values);
}
return $rv;
}
return $sth->SUPER::execute(@bind_values);
}
#########################################################################
package Litmus::DBI;
require Apache::DBI;
@ -45,23 +158,24 @@ use constant MP2 => ( exists $ENV{MOD_PERL_API_VERSION} and
use constant MP1 => ( exists $ENV{MOD_PERL} and
! exists $ENV{MOD_PERL_API_VERSION});
our $dsn = "dbi:mysql:database=$Litmus::Config::db_name;host=$Litmus::Config::db_host;port=3306";
our $dsn = "dbi:mysql(RootClass=AuditDBI):database=$Litmus::Config::db_name;host=$Litmus::Config::db_host;port=3306";
Litmus::DBI->connection($dsn,
$Litmus::Config::db_user, $Litmus::Config::db_pass);
Litmus::DBI->connection($dsn,
$Litmus::Config::db_user,
$Litmus::Config::db_pass);
our %column_aliases;
Litmus::DBI->autoupdate(1);
Litmus::DBI->autoupdate(0);
# In some cases, we have column names that make sense from a database perspective
# (i.e. subgroup_id), but that don't make sense from a class/object perspective
# (where subgroup would be more appropriate). To handle this, we allow for
# Litmus::DBI's subclasses to set column aliases with the column_alias() sub.
# Takes the database column name and the alias name.
# Takes the database column name and the alias name.
sub column_alias {
my ($self, $db_name, $alias_name) = @_;
$column_aliases{$alias_name} = $db_name;
}
@ -72,7 +186,7 @@ memoize('find_column', persist=>1);
sub find_column {
my $self = shift;
my $wanted = shift;
if (ref $self) {
$wanted =~ s/^.*::(\w+)$/$1/;
}
@ -126,5 +240,5 @@ sub _auto_increment_value {
if (! defined $id) { return $self->SUPER::_auto_increment_value() }
return $id;
}
1;

Просмотреть файл

@ -27,6 +27,19 @@
our $table;
$table{audit_trail} =
'user_id int(11) NOT NULL,
action_timestamp timestamp NOT NULL default CURRENT_TIMESTAMP,
action_type enum("INSERT","UPDATE","DELETE") NOT NULL,
sql_log longtext character set latin1 collate latin1_bin,
bind_values longtext character set latin1 collate latin1_bin,
KEY `user_id` (user_id),
KEY `action_timestamp` (action_timestamp),
KEY `action_type` (action_type),
KEY `sql_log` (sql_log(255)),
KEY `bind_values` (bind_values(255))
';
$table{branches} =
'branch_id smallint(6) not null primary key auto_increment,
product_id tinyint(4) not null,