X-Git-Url: http://wagnertech.de/gitweb/gitweb.cgi/mfinanz.git/blobdiff_plain/d5c0d18a11c012e287f99d74365f42758f7e6b3b..e14cb525c640cb15bb6b2dfb62ccebbe78cb45cd:/SL/Auth.pm?ds=inline diff --git a/SL/Auth.pm b/SL/Auth.pm index 6bb7bfaa7..52c2dc63d 100644 --- a/SL/Auth.pm +++ b/SL/Auth.pm @@ -6,16 +6,26 @@ use Digest::MD5 qw(md5_hex); use IO::File; use Time::HiRes qw(gettimeofday); use List::MoreUtils qw(uniq); +use YAML; +use SL::Auth::ColumnInformation; use SL::Auth::Constants qw(:all); use SL::Auth::DB; use SL::Auth::LDAP; +use SL::Auth::Password; +use SL::Auth::SessionValue; +use SL::SessionFile; use SL::User; +use SL::DBConnect; +use SL::DBUpgrade2; use SL::DBUtils; use strict; +use constant SESSION_KEY_ROOT_AUTH => 'session_auth_status_root'; +use constant SESSION_KEY_USER_AUTH => 'session_auth_status_user'; + sub new { $main::lxdebug->enter_sub(); @@ -24,19 +34,31 @@ sub new { bless $self, $type; - $self->{SESSION} = { }; - $self->_read_auth_config(); + $self->reset; $main::lxdebug->leave_sub(); return $self; } +sub reset { + my ($self, %params) = @_; + + $self->{SESSION} = { }; + $self->{FULL_RIGHTS} = { }; + $self->{RIGHTS} = { }; + $self->{unique_counter} = 0; + $self->{column_information} = SL::Auth::ColumnInformation->new(auth => $self); + $self->{authenticator}->reset; +} + sub get_user_dbh { - my ($self, $login) = @_; - my %user = $self->read_user($login); - my $dbh = DBI->connect( + my ($self, $login, %params) = @_; + my $may_fail = delete $params{may_fail}; + + my %user = $self->read_user(login => $login); + my $dbh = SL::DBConnect->connect( $user{dbconnect}, $user{dbuser}, $user{dbpasswd}, @@ -44,9 +66,13 @@ sub get_user_dbh { pg_enable_utf8 => $::locale->is_utf8, AutoCommit => 0 } - ) or $::form->dberror; + ); - if ($user{dboptions}) { + if (!$may_fail && !$dbh) { + $::form->error($::locale->text('The connection to the authentication database failed:') . "\n" . $DBI::errstr); + } + + if ($user{dboptions} && $dbh) { $dbh->do($user{dboptions}) or $::form->dberror($user{dboptions}); } @@ -59,31 +85,32 @@ sub DESTROY { $self->{dbh}->disconnect() if ($self->{dbh}); } -sub _read_auth_config { - $main::lxdebug->enter_sub(); +# form isn't loaded yet, so auth needs it's own error. +sub mini_error { + $::lxdebug->show_backtrace(); - my $self = shift; - - my $form = $main::form; - my $locale = $main::locale; + my ($self, @msg) = @_; + if ($ENV{HTTP_USER_AGENT}) { + print Form->create_http_response(content_type => 'text/html'); + print "
", join ('
', @msg), "";
+ } else {
+ print STDERR "Error: @msg\n";
+ }
+ ::end_of_request();
+}
- my $code;
- my $in = IO::File->new('config/authentication.pl', 'r');
+sub _read_auth_config {
+ $main::lxdebug->enter_sub();
- if (!$in) {
- $form->error($locale->text('The config file "config/authentication.pl" was not found.'));
- }
+ my $self = shift;
- while (<$in>) {
- $code .= $_;
- }
- $in->close();
+ map { $self->{$_} = $::lx_office_conf{authentication}->{$_} } keys %{ $::lx_office_conf{authentication} };
- eval $code;
+ # Prevent password leakage to log files when dumping Auth instances.
+ $self->{admin_password} = sub { $::lx_office_conf{authentication}->{admin_password} };
- if ($@) {
- $form->error($locale->text('The config file "config/authentication.pl" contained invalid Perl code:') . "\n" . $@);
- }
+ $self->{DB_config} = $::lx_office_conf{'authentication/database'};
+ $self->{LDAP_config} = $::lx_office_conf{'authentication/ldap'};
if ($self->{module} eq 'DB') {
$self->{authenticator} = SL::Auth::DB->new($self);
@@ -93,17 +120,20 @@ sub _read_auth_config {
}
if (!$self->{authenticator}) {
- $form->error($locale->text('No or an unknown authenticantion module specified in "config/authentication.pl".'));
+ my $locale = Locale->new('en');
+ $self->mini_error($locale->text('No or an unknown authenticantion module specified in "config/lx_office.conf".'));
}
my $cfg = $self->{DB_config};
if (!$cfg) {
- $form->error($locale->text('config/authentication.pl: Key "DB_config" is missing.'));
+ my $locale = Locale->new('en');
+ $self->mini_error($locale->text('config/lx_office.conf: Key "DB_config" is missing.'));
}
if (!$cfg->{host} || !$cfg->{db} || !$cfg->{user}) {
- $form->error($locale->text('config/authentication.pl: Missing parameters in "DB_config". Required parameters are "host", "db" and "user".'));
+ my $locale = Locale->new('en');
+ $self->mini_error($locale->text('config/lx_office.conf: Missing parameters in "authentication/database". Required parameters are "host", "db" and "user".'));
}
$self->{authenticator}->verify_config();
@@ -117,33 +147,70 @@ sub _read_auth_config {
sub authenticate_root {
$main::lxdebug->enter_sub();
- my $self = shift;
- my $password = shift;
- my $is_crypted = shift;
+ my ($self, $password) = @_;
- $password = crypt $password, 'ro' if (!$password || !$is_crypted);
- my $admin_password = crypt "$self->{admin_password}", 'ro';
+ my $session_root_auth = $self->get_session_value(SESSION_KEY_ROOT_AUTH());
+ if (defined $session_root_auth && $session_root_auth == OK) {
+ $::lxdebug->leave_sub;
+ return OK;
+ }
- $main::lxdebug->leave_sub();
+ if (!defined $password) {
+ $::lxdebug->leave_sub;
+ return ERR_PASSWORD;
+ }
+
+ $password = SL::Auth::Password->hash(login => 'root', password => $password);
+ my $admin_password = SL::Auth::Password->hash_if_unhashed(login => 'root', password => $self->{admin_password}->());
+
+ my $result = $password eq $admin_password ? OK : ERR_PASSWORD;
+ $self->set_session_value(SESSION_KEY_ROOT_AUTH() => $result);
- return OK if $password eq $admin_password;
- sleep 5;
- return ERR_PASSWORD;
+ $::lxdebug->leave_sub;
+ return $result;
}
sub authenticate {
$main::lxdebug->enter_sub();
- my $self = shift;
+ my ($self, $login, $password) = @_;
- $main::lxdebug->leave_sub();
+ my $session_auth = $self->get_session_value(SESSION_KEY_USER_AUTH());
+ if (defined $session_auth && $session_auth == OK) {
+ $::lxdebug->leave_sub;
+ return OK;
+ }
- my $result = $self->{authenticator}->authenticate(@_);
- return OK if $result eq OK;
- sleep 5;
+ if (!defined $password) {
+ $::lxdebug->leave_sub;
+ return ERR_PASSWORD;
+ }
+
+ my $result = $login ? $self->{authenticator}->authenticate($login, $password) : ERR_USER;
+ $self->set_session_value(SESSION_KEY_USER_AUTH() => $result, login => $login);
+
+ $::lxdebug->leave_sub;
return $result;
}
+sub punish_wrong_login {
+ my $failed_login_penalty = ($::lx_office_conf{authentication} || {})->{failed_login_penalty};
+ sleep $failed_login_penalty if $failed_login_penalty;
+}
+
+sub get_stored_password {
+ my ($self, $login) = @_;
+
+ my $dbh = $self->dbconnect;
+
+ return undef unless $dbh;
+
+ my $query = qq|SELECT password FROM auth."user" WHERE login = ?|;
+ my ($stored_password) = $dbh->selectrow_array($query, undef, $login);
+
+ return $stored_password;
+}
+
sub dbconnect {
$main::lxdebug->enter_sub(2);
@@ -164,13 +231,13 @@ sub dbconnect {
$main::lxdebug->message(LXDebug->DEBUG1, "Auth::dbconnect DSN: $dsn");
- $self->{dbh} = DBI->connect($dsn, $cfg->{user}, $cfg->{password}, { pg_enable_utf8 => $::locale->is_utf8, AutoCommit => 0 });
+ $self->{dbh} = SL::DBConnect->connect($dsn, $cfg->{user}, $cfg->{password}, { pg_enable_utf8 => $::locale->is_utf8, AutoCommit => 1 });
if (!$may_fail && !$self->{dbh}) {
$main::form->error($main::locale->text('The connection to the authentication database failed:') . "\n" . $DBI::errstr);
}
- $main::lxdebug->leave_sub();
+ $main::lxdebug->leave_sub(2);
return $self->{dbh};
}
@@ -191,9 +258,9 @@ sub dbdisconnect {
sub check_tables {
$main::lxdebug->enter_sub();
- my $self = shift;
+ my ($self, $dbh) = @_;
- my $dbh = $self->dbconnect();
+ $dbh ||= $self->dbconnect();
my $query = qq|SELECT COUNT(*) FROM pg_tables WHERE (schemaname = 'auth') AND (tablename = 'user')|;
my ($count) = $dbh->selectrow_array($query);
@@ -239,12 +306,12 @@ sub create_database {
$main::lxdebug->message(LXDebug->DEBUG1(), "Auth::create_database DSN: $dsn");
- my $charset = $main::dbcharset;
+ my $charset = $::lx_office_conf{system}->{dbcharset};
$charset ||= Common::DEFAULT_CHARSET;
my $encoding = $Common::charset_to_db_encoding{$charset};
$encoding ||= 'UNICODE';
- my $dbh = DBI->connect($dsn, $params{superuser}, $params{superuser_password}, { pg_enable_utf8 => $charset =~ m/^utf-?8$/i });
+ my $dbh = SL::DBConnect->connect($dsn, $params{superuser}, $params{superuser_password}, { pg_enable_utf8 => scalar($charset =~ m/^utf-?8$/i) });
if (!$dbh) {
$main::form->error($main::locale->text('The connection to the template database failed:') . "\n" . $DBI::errstr);
@@ -282,11 +349,11 @@ sub create_tables {
my $self = shift;
my $dbh = $self->dbconnect();
- my $charset = $main::dbcharset;
+ my $charset = $::lx_office_conf{system}->{dbcharset};
$charset ||= Common::DEFAULT_CHARSET;
$dbh->rollback();
- User->process_query($main::form, $dbh, 'sql/auth_db.sql', undef, $charset);
+ SL::DBUpgrade2->new(form => $::form)->process_query($dbh, 'sql/auth_db.sql', undef, $charset);
$main::lxdebug->leave_sub();
}
@@ -304,6 +371,8 @@ sub save_user {
my ($sth, $query, $user_id);
+ $dbh->begin_work;
+
$query = qq|SELECT id FROM auth."user" WHERE login = ?|;
($user_id) = selectrow_query($form, $dbh, $query, $login);
@@ -341,8 +410,9 @@ sub can_change_password {
sub change_password {
$main::lxdebug->enter_sub();
- my $self = shift;
- my $result = $self->{authenticator}->change_password(@_);
+ my ($self, $login, $new_password) = @_;
+
+ my $result = $self->{authenticator}->change_password($login, $new_password);
$main::lxdebug->leave_sub();
@@ -355,15 +425,30 @@ sub read_all_users {
my $self = shift;
my $dbh = $self->dbconnect();
- my $query = qq|SELECT u.id, u.login, cfg.cfg_key, cfg.cfg_value
- FROM auth.user_config cfg
- LEFT JOIN auth."user" u ON (cfg.user_id = u.id)|;
+ my $query = qq|SELECT u.id, u.login, cfg.cfg_key, cfg.cfg_value, s.mtime AS last_action
+
+ FROM auth."user" AS u
+
+ LEFT JOIN auth.user_config AS cfg
+ ON (cfg.user_id = u.id)
+
+ LEFT JOIN auth.session_content AS sc_login
+ ON (sc_login.sess_key = 'login' AND sc_login.sess_value = ('--- ' \|\| u.login \|\| '\n'))
+
+ LEFT JOIN auth.session AS s
+ ON (s.id = sc_login.session_id)
+ |;
my $sth = prepare_execute_query($main::form, $dbh, $query);
my %users;
while (my $ref = $sth->fetchrow_hashref()) {
- $users{$ref->{login}} ||= { 'login' => $ref->{login}, 'id' => $ref->{id} };
+
+ $users{$ref->{login}} ||= {
+ 'login' => $ref->{login},
+ 'id' => $ref->{id},
+ 'last_action' => $ref->{last_action},
+ };
$users{$ref->{login}}->{$ref->{cfg_key}} = $ref->{cfg_value} if (($ref->{cfg_key} ne 'login') && ($ref->{cfg_key} ne 'id'));
}
@@ -377,15 +462,25 @@ sub read_all_users {
sub read_user {
$main::lxdebug->enter_sub();
- my $self = shift;
- my $login = shift;
+ my ($self, %params) = @_;
my $dbh = $self->dbconnect();
+
+ my (@where, @values);
+ if ($params{login}) {
+ push @where, 'u.login = ?';
+ push @values, $params{login};
+ }
+ if ($params{id}) {
+ push @where, 'u.id = ?';
+ push @values, $params{id};
+ }
+ my $where = join ' AND ', '1 = 1', @where;
my $query = qq|SELECT u.id, u.login, cfg.cfg_key, cfg.cfg_value
FROM auth.user_config cfg
LEFT JOIN auth."user" u ON (cfg.user_id = u.id)
- WHERE (u.login = ?)|;
- my $sth = prepare_execute_query($main::form, $dbh, $query, $login);
+ WHERE $where|;
+ my $sth = prepare_execute_query($main::form, $dbh, $query, @values);
my %user_data;
@@ -394,6 +489,9 @@ sub read_user {
@user_data{qw(id login)} = @{$ref}{qw(id login)};
}
+ # The XUL/XML backed menu has been removed.
+ $user_data{menustyle} = 'v3' if lc($user_data{menustyle} || '') eq 'xml';
+
$sth->finish();
$main::lxdebug->leave_sub();
@@ -416,26 +514,33 @@ sub get_user_id {
}
sub delete_user {
- $main::lxdebug->enter_sub();
+ $::lxdebug->enter_sub;
my $self = shift;
my $login = shift;
- my $form = $main::form;
+ my $dbh = $self->dbconnect;
+ my $id = $self->get_user_id($login);
+ my $user_db_exists;
- my $dbh = $self->dbconnect();
- my $query = qq|SELECT id FROM auth."user" WHERE login = ?|;
+ $dbh->rollback and return $::lxdebug->leave_sub if (!$id);
- my ($id) = selectrow_query($form, $dbh, $query, $login);
+ my $u_dbh = $self->get_user_dbh($login, may_fail => 1);
+ $user_db_exists = $self->check_tables($u_dbh) if $u_dbh;
- return $main::lxdebug->leave_sub() if (!$id);
+ $u_dbh->begin_work if $u_dbh && $user_db_exists;
- do_query($form, $dbh, qq|DELETE FROM auth.user_group WHERE user_id = ?|, $id);
- do_query($form, $dbh, qq|DELETE FROM auth.user_config WHERE user_id = ?|, $id);
+ $dbh->begin_work;
- $dbh->commit();
+ do_query($::form, $dbh, qq|DELETE FROM auth.user_group WHERE user_id = ?|, $id);
+ do_query($::form, $dbh, qq|DELETE FROM auth.user_config WHERE user_id = ?|, $id);
+ do_query($::form, $dbh, qq|DELETE FROM auth.user WHERE id = ?|, $id);
+ do_query($::form, $u_dbh, qq|UPDATE employee SET deleted = 't' WHERE login = ?|, $login) if $u_dbh && $user_db_exists;
- $main::lxdebug->leave_sub();
+ $dbh->commit;
+ $u_dbh->commit if $u_dbh && $user_db_exists;
+
+ $::lxdebug->leave_sub;
}
# --------------------------------------
@@ -447,11 +552,8 @@ sub restore_session {
my $self = shift;
- my $cgi = $main::cgi;
- $cgi ||= CGI->new('');
-
- $session_id = $cgi->cookie($self->get_session_cookie_name());
- $session_id =~ s|[^0-9a-f]||g;
+ $session_id = $::request->{cgi}->cookie($self->get_session_cookie_name());
+ $session_id =~ s|[^0-9a-f]||g if $session_id;
$self->{SESSION} = { };
@@ -464,32 +566,112 @@ sub restore_session {
$form = $main::form;
- $dbh = $self->dbconnect();
+ # Don't fail if the auth DB doesn't yet.
+ if (!( $dbh = $self->dbconnect(1) )) {
+ $::lxdebug->leave_sub;
+ return SESSION_NONE;
+ }
+
+ # Don't fail if the "auth" schema doesn't exist yet, e.g. if the
+ # admin is creating the session tables at the moment.
$query = qq|SELECT *, (mtime < (now() - '$self->{session_timeout}m'::interval)) AS is_expired FROM auth.session WHERE id = ?|;
- $cookie = selectfirst_hashref_query($form, $dbh, $query, $session_id);
+ if (!($sth = $dbh->prepare($query)) || !$sth->execute($session_id)) {
+ $sth->finish if $sth;
+ $::lxdebug->leave_sub;
+ return SESSION_NONE;
+ }
+
+ $cookie = $sth->fetchrow_hashref;
+ $sth->finish;
if (!$cookie || $cookie->{is_expired} || ($cookie->{ip_address} ne $ENV{REMOTE_ADDR})) {
$self->destroy_session();
$main::lxdebug->leave_sub();
- return SESSION_EXPIRED;
+ return $cookie ? SESSION_EXPIRED : SESSION_NONE;
}
- $query = qq|SELECT sess_key, sess_value FROM auth.session_content WHERE session_id = ?|;
- $sth = prepare_execute_query($form, $dbh, $query, $session_id);
-
- while (my $ref = $sth->fetchrow_hashref()) {
- $self->{SESSION}->{$ref->{sess_key}} = $ref->{sess_value};
- $form->{$ref->{sess_key}} = $ref->{sess_value} if (!defined $form->{$ref->{sess_key}});
+ if ($self->{column_information}->has('auto_restore')) {
+ $self->_load_with_auto_restore_column($dbh, $session_id);
+ } else {
+ $self->_load_without_auto_restore_column($dbh, $session_id);
}
- $sth->finish();
-
$main::lxdebug->leave_sub();
return SESSION_OK;
}
+sub _load_without_auto_restore_column {
+ my ($self, $dbh, $session_id) = @_;
+
+ my $query = <