X-Git-Url: http://wagnertech.de/git?a=blobdiff_plain;f=SL%2FAuth.pm;h=d00edb812d9a472af34e0edab2525b8d7ba608ea;hb=e74dac176e01090f7e3b6bd9e99596a36e614284;hp=e01af910729071e1bada1e85dae036a6c0964fef;hpb=d3d6cb31bbeec3a6eba3ff41990a2748303f5851;p=kivitendo-erp.git diff --git a/SL/Auth.pm b/SL/Auth.pm index e01af9107..d00edb812 100644 --- a/SL/Auth.pm +++ b/SL/Auth.pm @@ -8,10 +8,12 @@ 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; @@ -29,9 +31,8 @@ sub new { bless $self, $type; - $self->{SESSION} = { }; - $self->_read_auth_config(); + $self->reset; $main::lxdebug->leave_sub(); @@ -41,17 +42,19 @@ sub new { sub reset { my ($self, %params) = @_; - $self->{SESSION} = { }; - $self->{FULL_RIGHTS} = { }; - $self->{RIGHTS} = { }; - $self->{unique_counter} = 0; + $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, %params) = @_; my $may_fail = delete $params{may_fail}; - my %user = $self->read_user($login); + my %user = $self->read_user(login => $login); my $dbh = SL::DBConnect->connect( $user{dbconnect}, $user{dbuser}, @@ -241,9 +244,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); @@ -393,8 +396,14 @@ 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); + + $self->store_credentials_in_session(login => $login, + password => $new_password, + look_up_algorithm => 1, + auth => $self); $main::lxdebug->leave_sub(); @@ -429,15 +438,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; @@ -446,6 +465,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(); @@ -473,23 +495,26 @@ sub delete_user { my $self = shift; my $login = shift; - my $u_dbh = $self->get_user_dbh($login, may_fail => 1); my $dbh = $self->dbconnect; + my $id = $self->get_user_id($login); + my $user_db_exists; - $dbh->begin_work; + $dbh->rollback and return $::lxdebug->leave_sub if (!$id); - my $query = qq|SELECT id FROM auth."user" WHERE login = ?|; + my $u_dbh = $self->get_user_dbh($login, may_fail => 1); + $user_db_exists = $self->check_tables($u_dbh) if $u_dbh; - my ($id) = selectrow_query($::form, $dbh, $query, $login); + $u_dbh->begin_work if $u_dbh && $user_db_exists; - $dbh->rollback and return $::lxdebug->leave_sub if (!$id); + $dbh->begin_work; 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, $u_dbh, qq|UPDATE employee SET deleted = 't' WHERE login = ?|, $login) if $u_dbh; + 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; $dbh->commit; - $u_dbh->commit if $u_dbh; + $u_dbh->commit if $u_dbh && $user_db_exists; $::lxdebug->leave_sub; } @@ -503,11 +528,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} = { }; @@ -520,10 +542,24 @@ 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(); @@ -531,45 +567,85 @@ sub restore_session { 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}; - next if defined $form->{$ref->{sess_key}}; - - my $params = $self->_load_value($ref->{sess_value}); - $form->{$ref->{sess_key}} = $params->{data} if $params->{auto_restore} || $params->{simple}; + 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_value { - my ($self, $value) = @_; +sub _load_without_auto_restore_column { + my ($self, $dbh, $session_id) = @_; - return { simple => 1, data => $value } if $value !~ m/^---/; + my $query = < 1 ); - eval { - my $data = YAML::Load($value); + while (my $ref = $sth->fetchrow_hashref) { + my $value = SL::Auth::SessionValue->new(auth => $self, + key => $ref->{sess_key}, + value => $ref->{sess_value}, + raw => 1); + $self->{SESSION}->{ $ref->{sess_key} } = $value; - if (ref $data eq 'HASH') { - map { $params{$_} = $data->{$_} } keys %{ $data }; - $params{simple} = 0; + next if defined $::form->{$ref->{sess_key}}; - } else { - $params{data} = $data; - } + my $data = $value->get; + $::form->{$ref->{sess_key}} = $data if $value->{auto_restore} || !ref $data; + } +} + +sub _load_with_auto_restore_column { + my ($self, $dbh, $session_id) = @_; + + my $auto_restore_keys = join ', ', map { "'${_}'" } qw(login password rpw); + + my $query = <fetchrow_hashref) { + my $value = SL::Auth::SessionValue->new(auth => $self, + key => $ref->{sess_key}, + value => $ref->{sess_value}, + auto_restore => $ref->{auto_restore}, + raw => 1); + $self->{SESSION}->{ $ref->{sess_key} } = $value; + + next if defined $::form->{$ref->{sess_key}}; - 1; - } or $params{data} = $value; + my $data = $value->get; + $::form->{$ref->{sess_key}} = $data if $value->{auto_restore} || !ref $data; + } - return \%params; + $sth->finish; + + $query = <fetchrow_hashref) { + my $value = SL::Auth::SessionValue->new(auth => $self, + key => $ref->{sess_key}); + $self->{SESSION}->{ $ref->{sess_key} } = $value; + } } sub destroy_session { @@ -658,12 +734,25 @@ sub save_session { $dbh->begin_work unless $provided_dbh; - do_query($::form, $dbh, qq|LOCK auth.session_content|); - do_query($::form, $dbh, qq|DELETE FROM auth.session_content WHERE session_id = ?|, $session_id); + # If this fails then the "auth" schema might not exist yet, e.g. if + # the admin is just trying to create the auth database. + if (!$dbh->do(qq|LOCK auth.session_content|)) { + $dbh->rollback unless $provided_dbh; + $::lxdebug->leave_sub; + return; + } + + my @unfetched_keys = map { $_->{key} } + grep { ! $_->{fetched} } + values %{ $self->{SESSION} }; + # $::lxdebug->dump(0, "unfetched_keys", [ sort @unfetched_keys ]); + # $::lxdebug->dump(0, "all keys", [ sort map { $_->{key} } values %{ $self->{SESSION} } ]); + my $query = qq|DELETE FROM auth.session_content WHERE (session_id = ?)|; + $query .= qq| AND (sess_key NOT IN (| . join(', ', ('?') x scalar @unfetched_keys) . qq|))| if @unfetched_keys; - my $query = qq|SELECT id FROM auth.session WHERE id = ?|; + do_query($::form, $dbh, $query, $session_id, @unfetched_keys); - my ($id) = selectrow_query($::form, $dbh, $query, $session_id); + my ($id) = selectrow_query($::form, $dbh, qq|SELECT id FROM auth.session WHERE id = ?|, $session_id); if ($id) { do_query($::form, $dbh, qq|UPDATE auth.session SET mtime = now() WHERE id = ?|, $session_id); @@ -671,12 +760,25 @@ sub save_session { do_query($::form, $dbh, qq|INSERT INTO auth.session (id, ip_address, mtime) VALUES (?, ?, now())|, $session_id, $ENV{REMOTE_ADDR}); } - if (%{ $self->{SESSION} }) { - my $query = qq|INSERT INTO auth.session_content (session_id, sess_key, sess_value) VALUES (?, ?, ?)|; - my $sth = prepare_query($::form, $dbh, $query); + my @values_to_save = grep { $_->{fetched} } + values %{ $self->{SESSION} }; + if (@values_to_save) { + my ($columns, $placeholders) = ('', ''); + my $auto_restore = $self->{column_information}->has('auto_restore'); + + if ($auto_restore) { + $columns .= ', auto_restore'; + $placeholders .= ', ?'; + } + + $query = qq|INSERT INTO auth.session_content (session_id, sess_key, sess_value ${columns}) VALUES (?, ?, ? ${placeholders})|; + my $sth = prepare_query($::form, $dbh, $query); + + foreach my $value (@values_to_save) { + my @values = ($value->{key}, $value->get_dumped); + push @values, $value->{auto_restore} if $auto_restore; - foreach my $key (sort keys %{ $self->{SESSION} }) { - do_statement($::form, $sth, $query, $session_id, $key, $self->{SESSION}->{$key}); + do_statement($::form, $sth, $query, $session_id, @values); } $sth->finish(); @@ -698,14 +800,14 @@ sub set_session_value { my $key = shift @params; if (ref $key eq 'HASH') { - my $value = { data => $key->{value}, - auto_restore => $key->{auto_restore}, - }; - $self->{SESSION}->{ $key->{key} } = YAML::Dump($value); + $self->{SESSION}->{ $key->{key} } = SL::Auth::SessionValue->new(key => $key->{key}, + value => $key->{value}, + auto_restore => $key->{auto_restore}); } else { my $value = shift @params; - $self->{SESSION}->{ $key } = YAML::Dump(ref($value) eq 'HASH' ? { data => $value } : $value); + $self->{SESSION}->{ $key } = SL::Auth::SessionValue->new(key => $key, + value => $value); } } @@ -730,12 +832,12 @@ sub delete_session_value { sub get_session_value { $main::lxdebug->enter_sub(); - my $self = shift; - my $params = $self->{SESSION} ? $self->_load_value($self->{SESSION}->{ $_[0] }) : {}; + my $self = shift; + my $data = $self->{SESSION} && $self->{SESSION}->{ $_[0] } ? $self->{SESSION}->{ $_[0] }->get : undef; $main::lxdebug->leave_sub(); - return $params->{data}; + return $data; } sub create_unique_sesion_value { @@ -747,16 +849,15 @@ sub create_unique_sesion_value { my $key = "$$-" . ($now[0] * 1000000 + $now[1]) . "-"; $self->{unique_counter} ||= 0; - $self->{unique_counter}++ while exists $self->{SESSION}->{$key . ($self->{unique_counter} + 1)}; - $self->{unique_counter}++; + my $hashed_key; + do { + $self->{unique_counter}++; + $hashed_key = md5_hex($key . $self->{unique_counter}); + } while (exists $self->{SESSION}->{$hashed_key}); - $value = { expiration => $params{expiration} ? ($now[0] + $params{expiration}) * 1000000 + $now[1] : undef, - data => $value, - }; + $self->set_session_value($hashed_key => $value); - $self->{SESSION}->{$key . $self->{unique_counter}} = YAML::Dump($value); - - return $key . $self->{unique_counter}; + return $hashed_key; } sub save_form_in_session { @@ -789,27 +890,6 @@ sub restore_form_from_session { return $self; } -sub expire_session_keys { - my ($self) = @_; - - $self->{SESSION} ||= { }; - - my @now = gettimeofday(); - my $now = $now[0] * 1000000 + $now[1]; - - $self->delete_session_value(map { $_->[0] } - grep { $_->[1]->{expiration} && ($now > $_->[1]->{expiration}) } - map { [ $_, $self->_load_value($self->{SESSION}->{$_}) ] } - keys %{ $self->{SESSION} }); - - return $self; -} - -sub _has_expiration { - my ($value) = @_; - return (ref $value eq 'HASH') && exists($value->{expiration}) && $value->{data}; -} - sub set_cookie_environment_variable { my $self = shift; $ENV{HTTP_COOKIE} = $self->get_session_cookie_name() . "=${session_id}"; @@ -880,7 +960,8 @@ sub all_rights_full { ["crm_notices", $locale->text("CRM notices")], ["crm_other", $locale->text("CRM other")], ["--master_data", $locale->text("Master Data")], - ["customer_vendor_edit", $locale->text("Create and edit customers and vendors")], + ["customer_vendor_edit", $locale->text("Create customers and vendors. Edit all vendors. Edit only customers where salesman equals employee (login)")], + ["customer_vendor_all_edit", $locale->text("Create customers and vendors. Edit all vendors. Edit all customers")], ["part_service_assembly_edit", $locale->text("Create and edit parts, services, assemblies")], ["project_edit", $locale->text("Create and edit projects")], ["--ar", $locale->text("AR")], @@ -890,6 +971,7 @@ sub all_rights_full { ["invoice_edit", $locale->text("Create and edit invoices and credit notes")], ["dunning_edit", $locale->text("Create and edit dunnings")], ["sales_all_edit", $locale->text("View/edit all employees sales documents")], + ["edit_prices", $locale->text("Edit prices and discount (if not used, textfield is ONLY set readonly)")], ["--ap", $locale->text("AP")], ["request_quotation_edit", $locale->text("Create and edit RFQs")], ["purchase_order_edit", $locale->text("Create and edit purchase orders")], @@ -910,6 +992,7 @@ sub all_rights_full { ["--others", $locale->text("Others")], ["email_bcc", $locale->text("May set the BCC field when sending emails")], ["config", $locale->text("Change Lx-Office installation settings (all menu entries beneath 'System')")], + ["admin", $locale->text("Administration (Used to access instance administration from user logins)")], ); return @all_rights; @@ -1251,17 +1334,8 @@ doesn't exist. Create a unique key in the session and store C<$value> there. -If C<$params{expiration}> is set then it is interpreted as a number of -seconds after which the value is removed from the session. It will -never expire if that parameter is falsish. - Returns the key created in the session. -=item C - -Removes all keys from the session that have an expiration time set and -whose expiration time is in the past. - =item C Stores the session values in the database. This is the only function