зеркало из https://github.com/mozilla/pjs.git
b=345323
r=zach - add audit_trail tracking for admin actions
This commit is contained in:
Родитель
19cda99390
Коммит
58cea88cf8
|
@ -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,
|
||||
|
|
Загрузка…
Ссылка в новой задаче