5 use Digest::MD5 qw(md5_hex);
7 use Time::HiRes qw(gettimeofday);
8 use List::MoreUtils qw(uniq);
11 use SL::Auth::Constants qw(:all);
14 use SL::Auth::Password;
25 $main::lxdebug->enter_sub();
32 $self->{SESSION} = { };
34 $self->_read_auth_config();
36 $main::lxdebug->leave_sub();
42 my ($self, %params) = @_;
44 $self->{SESSION} = { };
45 $self->{FULL_RIGHTS} = { };
46 $self->{RIGHTS} = { };
47 $self->{unique_counter} = 0;
51 my ($self, $login, %params) = @_;
52 my $may_fail = delete $params{may_fail};
54 my %user = $self->read_user($login);
55 my $dbh = SL::DBConnect->connect(
60 pg_enable_utf8 => $::locale->is_utf8,
65 if (!$may_fail && !$dbh) {
66 $::form->error($::locale->text('The connection to the authentication database failed:') . "\n" . $DBI::errstr);
69 if ($user{dboptions} && $dbh) {
70 $dbh->do($user{dboptions}) or $::form->dberror($user{dboptions});
79 $self->{dbh}->disconnect() if ($self->{dbh});
82 # form isn't loaded yet, so auth needs it's own error.
84 $::lxdebug->show_backtrace();
86 my ($self, @msg) = @_;
87 if ($ENV{HTTP_USER_AGENT}) {
88 print Form->create_http_response(content_type => 'text/html');
89 print "<pre>", join ('<br>', @msg), "</pre>";
91 print STDERR "Error: @msg\n";
96 sub _read_auth_config {
97 $main::lxdebug->enter_sub();
101 map { $self->{$_} = $::lx_office_conf{authentication}->{$_} } keys %{ $::lx_office_conf{authentication} };
102 $self->{DB_config} = $::lx_office_conf{'authentication/database'};
103 $self->{LDAP_config} = $::lx_office_conf{'authentication/ldap'};
105 if ($self->{module} eq 'DB') {
106 $self->{authenticator} = SL::Auth::DB->new($self);
108 } elsif ($self->{module} eq 'LDAP') {
109 $self->{authenticator} = SL::Auth::LDAP->new($self);
112 if (!$self->{authenticator}) {
113 my $locale = Locale->new('en');
114 $self->mini_error($locale->text('No or an unknown authenticantion module specified in "config/lx_office.conf".'));
117 my $cfg = $self->{DB_config};
120 my $locale = Locale->new('en');
121 $self->mini_error($locale->text('config/lx_office.conf: Key "DB_config" is missing.'));
124 if (!$cfg->{host} || !$cfg->{db} || !$cfg->{user}) {
125 my $locale = Locale->new('en');
126 $self->mini_error($locale->text('config/lx_office.conf: Missing parameters in "authentication/database". Required parameters are "host", "db" and "user".'));
129 $self->{authenticator}->verify_config();
131 $self->{session_timeout} *= 1;
132 $self->{session_timeout} = 8 * 60 if (!$self->{session_timeout});
134 $main::lxdebug->leave_sub();
137 sub authenticate_root {
138 $main::lxdebug->enter_sub();
140 my ($self, $password) = @_;
142 $password = SL::Auth::Password->hash_if_unhashed(login => 'root', password => $password);
143 my $admin_password = SL::Auth::Password->hash_if_unhashed(login => 'root', password => $self->{admin_password});
145 $main::lxdebug->leave_sub();
147 return OK if $password eq $admin_password;
153 $main::lxdebug->enter_sub();
155 my ($self, $login, $password) = @_;
157 $main::lxdebug->leave_sub();
159 my $result = $login ? $self->{authenticator}->authenticate($login, $password) : ERR_USER;
160 return OK if $result eq OK;
165 sub store_credentials_in_session {
166 my ($self, %params) = @_;
168 $params{password} = SL::Auth::Password->hash_if_unhashed(login => $params{login}, password => $params{password})
169 unless $self->{authenticator}->requires_cleartext_password;
171 $self->set_session_value(login => $params{login}, password => $params{password});
174 sub store_root_credentials_in_session {
175 my ($self, $rpw) = @_;
177 $rpw = SL::Auth::Password->hash_if_unhashed(login => 'root', password => $rpw)
178 unless $self->{authenticator}->requires_cleartext_password;
180 $self->set_session_value(rpw => $rpw);
184 $main::lxdebug->enter_sub(2);
187 my $may_fail = shift;
190 $main::lxdebug->leave_sub(2);
194 my $cfg = $self->{DB_config};
195 my $dsn = 'dbi:Pg:dbname=' . $cfg->{db} . ';host=' . $cfg->{host};
198 $dsn .= ';port=' . $cfg->{port};
201 $main::lxdebug->message(LXDebug->DEBUG1, "Auth::dbconnect DSN: $dsn");
203 $self->{dbh} = SL::DBConnect->connect($dsn, $cfg->{user}, $cfg->{password}, { pg_enable_utf8 => $::locale->is_utf8, AutoCommit => 1 });
205 if (!$may_fail && !$self->{dbh}) {
206 $main::form->error($main::locale->text('The connection to the authentication database failed:') . "\n" . $DBI::errstr);
209 $main::lxdebug->leave_sub(2);
215 $main::lxdebug->enter_sub();
220 $self->{dbh}->disconnect();
224 $main::lxdebug->leave_sub();
228 $main::lxdebug->enter_sub();
232 my $dbh = $self->dbconnect();
233 my $query = qq|SELECT COUNT(*) FROM pg_tables WHERE (schemaname = 'auth') AND (tablename = 'user')|;
235 my ($count) = $dbh->selectrow_array($query);
237 $main::lxdebug->leave_sub();
243 $main::lxdebug->enter_sub();
247 my $dbh = $self->dbconnect(1);
249 $main::lxdebug->leave_sub();
254 sub create_database {
255 $main::lxdebug->enter_sub();
260 my $cfg = $self->{DB_config};
262 if (!$params{superuser}) {
263 $params{superuser} = $cfg->{user};
264 $params{superuser_password} = $cfg->{password};
267 $params{template} ||= 'template0';
268 $params{template} =~ s|[^a-zA-Z0-9_\-]||g;
270 my $dsn = 'dbi:Pg:dbname=template1;host=' . $cfg->{host};
273 $dsn .= ';port=' . $cfg->{port};
276 $main::lxdebug->message(LXDebug->DEBUG1(), "Auth::create_database DSN: $dsn");
278 my $charset = $::lx_office_conf{system}->{dbcharset};
279 $charset ||= Common::DEFAULT_CHARSET;
280 my $encoding = $Common::charset_to_db_encoding{$charset};
281 $encoding ||= 'UNICODE';
283 my $dbh = SL::DBConnect->connect($dsn, $params{superuser}, $params{superuser_password}, { pg_enable_utf8 => scalar($charset =~ m/^utf-?8$/i) });
286 $main::form->error($main::locale->text('The connection to the template database failed:') . "\n" . $DBI::errstr);
289 my $query = qq|CREATE DATABASE "$cfg->{db}" OWNER "$cfg->{user}" TEMPLATE "$params{template}" ENCODING '$encoding'|;
291 $main::lxdebug->message(LXDebug->DEBUG1(), "Auth::create_database query: $query");
296 my $error = $dbh->errstr();
298 $query = qq|SELECT pg_encoding_to_char(encoding) FROM pg_database WHERE datname = 'template0'|;
299 my ($cluster_encoding) = $dbh->selectrow_array($query);
301 if ($cluster_encoding && ($cluster_encoding =~ m/^(?:UTF-?8|UNICODE)$/i) && ($encoding !~ m/^(?:UTF-?8|UNICODE)$/i)) {
302 $error = $main::locale->text('Your PostgreSQL installationen uses UTF-8 as its encoding. Therefore you have to configure Lx-Office to use UTF-8 as well.');
307 $main::form->error($main::locale->text('The creation of the authentication database failed:') . "\n" . $error);
312 $main::lxdebug->leave_sub();
316 $main::lxdebug->enter_sub();
319 my $dbh = $self->dbconnect();
321 my $charset = $::lx_office_conf{system}->{dbcharset};
322 $charset ||= Common::DEFAULT_CHARSET;
325 SL::DBUpgrade2->new(form => $::form)->process_query($dbh, 'sql/auth_db.sql', undef, $charset);
327 $main::lxdebug->leave_sub();
331 $main::lxdebug->enter_sub();
337 my $form = $main::form;
339 my $dbh = $self->dbconnect();
341 my ($sth, $query, $user_id);
345 $query = qq|SELECT id FROM auth."user" WHERE login = ?|;
346 ($user_id) = selectrow_query($form, $dbh, $query, $login);
349 $query = qq|SELECT nextval('auth.user_id_seq')|;
350 ($user_id) = selectrow_query($form, $dbh, $query);
352 $query = qq|INSERT INTO auth."user" (id, login) VALUES (?, ?)|;
353 do_query($form, $dbh, $query, $user_id, $login);
356 $query = qq|DELETE FROM auth.user_config WHERE (user_id = ?)|;
357 do_query($form, $dbh, $query, $user_id);
359 $query = qq|INSERT INTO auth.user_config (user_id, cfg_key, cfg_value) VALUES (?, ?, ?)|;
360 $sth = prepare_query($form, $dbh, $query);
362 while (my ($cfg_key, $cfg_value) = each %params) {
363 next if ($cfg_key eq 'password');
365 do_statement($form, $sth, $query, $user_id, $cfg_key, $cfg_value);
370 $main::lxdebug->leave_sub();
373 sub can_change_password {
376 return $self->{authenticator}->can_change_password();
379 sub change_password {
380 $main::lxdebug->enter_sub();
383 my $result = $self->{authenticator}->change_password(@_);
385 $main::lxdebug->leave_sub();
391 $main::lxdebug->enter_sub();
395 my $dbh = $self->dbconnect();
396 my $query = qq|SELECT u.id, u.login, cfg.cfg_key, cfg.cfg_value
397 FROM auth.user_config cfg
398 LEFT JOIN auth."user" u ON (cfg.user_id = u.id)|;
399 my $sth = prepare_execute_query($main::form, $dbh, $query);
403 while (my $ref = $sth->fetchrow_hashref()) {
404 $users{$ref->{login}} ||= { 'login' => $ref->{login}, 'id' => $ref->{id} };
405 $users{$ref->{login}}->{$ref->{cfg_key}} = $ref->{cfg_value} if (($ref->{cfg_key} ne 'login') && ($ref->{cfg_key} ne 'id'));
410 $main::lxdebug->leave_sub();
416 $main::lxdebug->enter_sub();
421 my $dbh = $self->dbconnect();
422 my $query = qq|SELECT u.id, u.login, cfg.cfg_key, cfg.cfg_value
423 FROM auth.user_config cfg
424 LEFT JOIN auth."user" u ON (cfg.user_id = u.id)
425 WHERE (u.login = ?)|;
426 my $sth = prepare_execute_query($main::form, $dbh, $query, $login);
430 while (my $ref = $sth->fetchrow_hashref()) {
431 $user_data{$ref->{cfg_key}} = $ref->{cfg_value};
432 @user_data{qw(id login)} = @{$ref}{qw(id login)};
437 $main::lxdebug->leave_sub();
443 $main::lxdebug->enter_sub();
448 my $dbh = $self->dbconnect();
449 my ($id) = selectrow_query($main::form, $dbh, qq|SELECT id FROM auth."user" WHERE login = ?|, $login);
451 $main::lxdebug->leave_sub();
457 $::lxdebug->enter_sub;
462 my $u_dbh = $self->get_user_dbh($login, may_fail => 1);
463 my $dbh = $self->dbconnect;
467 my $query = qq|SELECT id FROM auth."user" WHERE login = ?|;
469 my ($id) = selectrow_query($::form, $dbh, $query, $login);
471 $dbh->rollback and return $::lxdebug->leave_sub if (!$id);
473 do_query($::form, $dbh, qq|DELETE FROM auth.user_group WHERE user_id = ?|, $id);
474 do_query($::form, $dbh, qq|DELETE FROM auth.user_config WHERE user_id = ?|, $id);
475 do_query($::form, $u_dbh, qq|UPDATE employee SET deleted = 't' WHERE login = ?|, $login) if $u_dbh;
478 $u_dbh->commit if $u_dbh;
480 $::lxdebug->leave_sub;
483 # --------------------------------------
487 sub restore_session {
488 $main::lxdebug->enter_sub();
492 my $cgi = $main::cgi;
493 $cgi ||= CGI->new('');
495 $session_id = $cgi->cookie($self->get_session_cookie_name());
496 $session_id =~ s|[^0-9a-f]||g;
498 $self->{SESSION} = { };
501 $main::lxdebug->leave_sub();
505 my ($dbh, $query, $sth, $cookie, $ref, $form);
509 $dbh = $self->dbconnect();
510 $query = qq|SELECT *, (mtime < (now() - '$self->{session_timeout}m'::interval)) AS is_expired FROM auth.session WHERE id = ?|;
512 $cookie = selectfirst_hashref_query($form, $dbh, $query, $session_id);
514 if (!$cookie || $cookie->{is_expired} || ($cookie->{ip_address} ne $ENV{REMOTE_ADDR})) {
515 $self->destroy_session();
516 $main::lxdebug->leave_sub();
517 return $cookie ? SESSION_EXPIRED : SESSION_NONE;
520 $query = qq|SELECT sess_key, sess_value FROM auth.session_content WHERE session_id = ?|;
521 $sth = prepare_execute_query($form, $dbh, $query, $session_id);
523 while (my $ref = $sth->fetchrow_hashref()) {
524 $self->{SESSION}->{$ref->{sess_key}} = $ref->{sess_value};
525 next if defined $form->{$ref->{sess_key}};
527 my $params = $self->_load_value($ref->{sess_value});
528 $form->{$ref->{sess_key}} = $params->{data} if $params->{auto_restore} || $params->{simple};
533 $main::lxdebug->leave_sub();
539 my ($self, $value) = @_;
541 return { simple => 1, data => $value } if $value !~ m/^---/;
543 my %params = ( simple => 1 );
545 my $data = YAML::Load($value);
547 if (ref $data eq 'HASH') {
548 map { $params{$_} = $data->{$_} } keys %{ $data };
552 $params{data} = $data;
556 } or $params{data} = $value;
561 sub destroy_session {
562 $main::lxdebug->enter_sub();
567 my $dbh = $self->dbconnect();
571 do_query($main::form, $dbh, qq|DELETE FROM auth.session_content WHERE session_id = ?|, $session_id);
572 do_query($main::form, $dbh, qq|DELETE FROM auth.session WHERE id = ?|, $session_id);
576 SL::SessionFile->destroy_session($session_id);
579 $self->{SESSION} = { };
582 $main::lxdebug->leave_sub();
585 sub expire_sessions {
586 $main::lxdebug->enter_sub();
590 $main::lxdebug->leave_sub and return if !$self->session_tables_present;
592 my $dbh = $self->dbconnect();
594 my $query = qq|SELECT id
596 WHERE (mtime < (now() - '$self->{session_timeout}m'::interval))|;
598 my @ids = selectall_array_query($::form, $dbh, $query);
603 SL::SessionFile->destroy_session($_) for @ids;
605 $query = qq|DELETE FROM auth.session_content
606 WHERE session_id IN (| . join(', ', ('?') x scalar(@ids)) . qq|)|;
607 do_query($main::form, $dbh, $query, @ids);
609 $query = qq|DELETE FROM auth.session
610 WHERE id IN (| . join(', ', ('?') x scalar(@ids)) . qq|)|;
611 do_query($main::form, $dbh, $query, @ids);
616 $main::lxdebug->leave_sub();
619 sub _create_session_id {
620 $main::lxdebug->enter_sub();
623 map { push @data, int(rand() * 255); } (1..32);
625 my $id = md5_hex(pack 'C*', @data);
627 $main::lxdebug->leave_sub();
632 sub create_or_refresh_session {
633 $session_id ||= shift->_create_session_id;
637 $::lxdebug->enter_sub;
639 my $provided_dbh = shift;
641 my $dbh = $provided_dbh || $self->dbconnect(1);
643 $::lxdebug->leave_sub && return unless $dbh && $session_id;
645 $dbh->begin_work unless $provided_dbh;
647 do_query($::form, $dbh, qq|LOCK auth.session_content|);
648 do_query($::form, $dbh, qq|DELETE FROM auth.session_content WHERE session_id = ?|, $session_id);
650 my $query = qq|SELECT id FROM auth.session WHERE id = ?|;
652 my ($id) = selectrow_query($::form, $dbh, $query, $session_id);
655 do_query($::form, $dbh, qq|UPDATE auth.session SET mtime = now() WHERE id = ?|, $session_id);
657 do_query($::form, $dbh, qq|INSERT INTO auth.session (id, ip_address, mtime) VALUES (?, ?, now())|, $session_id, $ENV{REMOTE_ADDR});
660 if (%{ $self->{SESSION} }) {
661 my $query = qq|INSERT INTO auth.session_content (session_id, sess_key, sess_value) VALUES (?, ?, ?)|;
662 my $sth = prepare_query($::form, $dbh, $query);
664 foreach my $key (sort keys %{ $self->{SESSION} }) {
665 do_statement($::form, $sth, $query, $session_id, $key, $self->{SESSION}->{$key});
671 $dbh->commit() unless $provided_dbh;
672 $::lxdebug->leave_sub;
675 sub set_session_value {
676 $main::lxdebug->enter_sub();
681 $self->{SESSION} ||= { };
683 while (my ($key, $value) = each %params) {
684 $self->{SESSION}->{ $key } = YAML::Dump(ref($value) eq 'HASH' ? { data => $value } : $value);
687 $main::lxdebug->leave_sub();
692 sub delete_session_value {
693 $main::lxdebug->enter_sub();
697 $self->{SESSION} ||= { };
698 delete @{ $self->{SESSION} }{ @_ };
700 $main::lxdebug->leave_sub();
705 sub get_session_value {
706 $main::lxdebug->enter_sub();
709 my $params = $self->{SESSION} ? $self->_load_value($self->{SESSION}->{ $_[0] }) : {};
711 $main::lxdebug->leave_sub();
713 return $params->{data};
716 sub create_unique_sesion_value {
717 my ($self, $value, %params) = @_;
719 $self->{SESSION} ||= { };
721 my @now = gettimeofday();
722 my $key = "$$-" . ($now[0] * 1000000 + $now[1]) . "-";
723 $self->{unique_counter} ||= 0;
725 $self->{unique_counter}++ while exists $self->{SESSION}->{$key . $self->{unique_counter}};
726 $self->{unique_counter}++;
728 $value = { expiration => $params{expiration} ? ($now[0] + $params{expiration}) * 1000000 + $now[1] : undef,
732 $self->{SESSION}->{$key . $self->{unique_counter}} = YAML::Dump($value);
734 return $key . $self->{unique_counter};
737 sub save_form_in_session {
738 my ($self, %params) = @_;
740 my $form = delete($params{form}) || $::form;
741 my $non_scalars = delete $params{non_scalars};
744 my %skip_keys = map { ( $_ => 1 ) } (qw(login password stylesheet version titlebar), @{ $params{skip_keys} || [] });
746 foreach my $key (grep { !$skip_keys{$_} } keys %{ $form }) {
747 $data->{$key} = $form->{$key} if !ref($form->{$key}) || $non_scalars;
750 return $self->create_unique_sesion_value($data, %params);
753 sub restore_form_from_session {
754 my ($self, $key, %params) = @_;
756 my $data = $self->get_session_value($key);
757 return $self unless $data;
759 my $form = delete($params{form}) || $::form;
760 my $clobber = exists $params{clobber} ? $params{clobber} : 1;
762 map { $form->{$_} = $data->{$_} if $clobber || !exists $form->{$_} } keys %{ $data };
767 sub expire_session_keys {
770 $self->{SESSION} ||= { };
772 my @now = gettimeofday();
773 my $now = $now[0] * 1000000 + $now[1];
775 $self->delete_session_value(map { $_->[0] }
776 grep { $_->[1]->{expiration} && ($now > $_->[1]->{expiration}) }
777 map { [ $_, $self->_load_value($self->{SESSION}->{$_}) ] }
778 keys %{ $self->{SESSION} });
783 sub _has_expiration {
785 return (ref $value eq 'HASH') && exists($value->{expiration}) && $value->{data};
788 sub set_cookie_environment_variable {
790 $ENV{HTTP_COOKIE} = $self->get_session_cookie_name() . "=${session_id}";
793 sub get_session_cookie_name {
796 return $self->{cookie_name} || 'lx_office_erp_session_id';
803 sub session_tables_present {
804 $main::lxdebug->enter_sub();
808 # Only re-check for the presence of auth tables if either the check
809 # hasn't been done before of if they weren't present.
810 if ($self->{session_tables_present}) {
811 $main::lxdebug->leave_sub();
812 return $self->{session_tables_present};
815 my $dbh = $self->dbconnect(1);
818 $main::lxdebug->leave_sub();
825 WHERE (schemaname = 'auth')
826 AND (tablename IN ('session', 'session_content'))|;
828 my ($count) = selectrow_query($main::form, $dbh, $query);
830 $self->{session_tables_present} = 2 == $count;
832 $main::lxdebug->leave_sub();
834 return $self->{session_tables_present};
837 # --------------------------------------
839 sub all_rights_full {
840 my $locale = $main::locale;
843 ["--crm", $locale->text("CRM optional software")],
844 ["crm_search", $locale->text("CRM search")],
845 ["crm_new", $locale->text("CRM create customers, vendors and contacts")],
846 ["crm_service", $locale->text("CRM services")],
847 ["crm_admin", $locale->text("CRM admin")],
848 ["crm_adminuser", $locale->text("CRM user")],
849 ["crm_adminstatus", $locale->text("CRM status")],
850 ["crm_email", $locale->text("CRM send email")],
851 ["crm_termin", $locale->text("CRM termin")],
852 ["crm_opportunity", $locale->text("CRM opportunity")],
853 ["crm_knowhow", $locale->text("CRM know how")],
854 ["crm_follow", $locale->text("CRM follow up")],
855 ["crm_notices", $locale->text("CRM notices")],
856 ["crm_other", $locale->text("CRM other")],
857 ["--master_data", $locale->text("Master Data")],
858 ["customer_vendor_edit", $locale->text("Create and edit customers and vendors")],
859 ["part_service_assembly_edit", $locale->text("Create and edit parts, services, assemblies")],
860 ["project_edit", $locale->text("Create and edit projects")],
861 ["license_edit", $locale->text("Manage license keys")],
862 ["--ar", $locale->text("AR")],
863 ["sales_quotation_edit", $locale->text("Create and edit sales quotations")],
864 ["sales_order_edit", $locale->text("Create and edit sales orders")],
865 ["sales_delivery_order_edit", $locale->text("Create and edit sales delivery orders")],
866 ["invoice_edit", $locale->text("Create and edit invoices and credit notes")],
867 ["dunning_edit", $locale->text("Create and edit dunnings")],
868 ["sales_all_edit", $locale->text("View/edit all employees sales documents")],
869 ["--ap", $locale->text("AP")],
870 ["request_quotation_edit", $locale->text("Create and edit RFQs")],
871 ["purchase_order_edit", $locale->text("Create and edit purchase orders")],
872 ["purchase_delivery_order_edit", $locale->text("Create and edit purchase delivery orders")],
873 ["vendor_invoice_edit", $locale->text("Create and edit vendor invoices")],
874 ["--warehouse_management", $locale->text("Warehouse management")],
875 ["warehouse_contents", $locale->text("View warehouse content")],
876 ["warehouse_management", $locale->text("Warehouse management")],
877 ["--general_ledger_cash", $locale->text("General ledger and cash")],
878 ["general_ledger", $locale->text("Transactions, AR transactions, AP transactions")],
879 ["datev_export", $locale->text("DATEV Export")],
880 ["cash", $locale->text("Receipt, payment, reconciliation")],
881 ["--reports", $locale->text('Reports')],
882 ["report", $locale->text('All reports')],
883 ["advance_turnover_tax_return", $locale->text('Advance turnover tax return')],
884 ["--batch_printing", $locale->text("Batch Printing")],
885 ["batch_printing", $locale->text("Batch Printing")],
886 ["--others", $locale->text("Others")],
887 ["email_bcc", $locale->text("May set the BCC field when sending emails")],
888 ["config", $locale->text("Change Lx-Office installation settings (all menu entries beneath 'System')")],
895 return grep !/^--/, map { $_->[0] } all_rights_full();
899 $main::lxdebug->enter_sub();
903 my $form = $main::form;
905 my $dbh = $self->dbconnect();
907 my $query = 'SELECT * FROM auth."group"';
908 my $sth = prepare_execute_query($form, $dbh, $query);
912 while ($row = $sth->fetchrow_hashref()) {
913 $groups->{$row->{id}} = $row;
917 $query = 'SELECT * FROM auth.user_group WHERE group_id = ?';
918 $sth = prepare_query($form, $dbh, $query);
920 foreach $group (values %{$groups}) {
923 do_statement($form, $sth, $query, $group->{id});
925 while ($row = $sth->fetchrow_hashref()) {
926 push @members, $row->{user_id};
928 $group->{members} = [ uniq @members ];
932 $query = 'SELECT * FROM auth.group_rights WHERE group_id = ?';
933 $sth = prepare_query($form, $dbh, $query);
935 foreach $group (values %{$groups}) {
936 $group->{rights} = {};
938 do_statement($form, $sth, $query, $group->{id});
940 while ($row = $sth->fetchrow_hashref()) {
941 $group->{rights}->{$row->{right}} |= $row->{granted};
944 map { $group->{rights}->{$_} = 0 if (!defined $group->{rights}->{$_}); } all_rights();
948 $main::lxdebug->leave_sub();
954 $main::lxdebug->enter_sub();
959 my $form = $main::form;
960 my $dbh = $self->dbconnect();
964 my ($query, $sth, $row, $rights);
967 ($group->{id}) = selectrow_query($form, $dbh, qq|SELECT nextval('auth.group_id_seq')|);
969 $query = qq|INSERT INTO auth."group" (id, name, description) VALUES (?, '', '')|;
970 do_query($form, $dbh, $query, $group->{id});
973 do_query($form, $dbh, qq|UPDATE auth."group" SET name = ?, description = ? WHERE id = ?|, map { $group->{$_} } qw(name description id));
975 do_query($form, $dbh, qq|DELETE FROM auth.user_group WHERE group_id = ?|, $group->{id});
977 $query = qq|INSERT INTO auth.user_group (user_id, group_id) VALUES (?, ?)|;
978 $sth = prepare_query($form, $dbh, $query);
980 foreach my $user_id (uniq @{ $group->{members} }) {
981 do_statement($form, $sth, $query, $user_id, $group->{id});
985 do_query($form, $dbh, qq|DELETE FROM auth.group_rights WHERE group_id = ?|, $group->{id});
987 $query = qq|INSERT INTO auth.group_rights (group_id, "right", granted) VALUES (?, ?, ?)|;
988 $sth = prepare_query($form, $dbh, $query);
990 foreach my $right (keys %{ $group->{rights} }) {
991 do_statement($form, $sth, $query, $group->{id}, $right, $group->{rights}->{$right} ? 't' : 'f');
997 $main::lxdebug->leave_sub();
1001 $main::lxdebug->enter_sub();
1006 my $form = $main::form;
1008 my $dbh = $self->dbconnect();
1011 do_query($form, $dbh, qq|DELETE FROM auth.user_group WHERE group_id = ?|, $id);
1012 do_query($form, $dbh, qq|DELETE FROM auth.group_rights WHERE group_id = ?|, $id);
1013 do_query($form, $dbh, qq|DELETE FROM auth."group" WHERE id = ?|, $id);
1017 $main::lxdebug->leave_sub();
1020 sub evaluate_rights_ary {
1021 $main::lxdebug->enter_sub(2);
1028 foreach my $el (@{$ary}) {
1029 if (ref $el eq "ARRAY") {
1030 if ($action eq '|') {
1031 $value |= evaluate_rights_ary($el);
1033 $value &= evaluate_rights_ary($el);
1036 } elsif (($el eq '&') || ($el eq '|')) {
1039 } elsif ($action eq '|') {
1048 $main::lxdebug->leave_sub(2);
1053 sub _parse_rights_string {
1054 $main::lxdebug->enter_sub(2);
1064 push @stack, $cur_ary;
1066 while ($access =~ m/^([a-z_0-9]+|\||\&|\(|\)|\s+)/) {
1068 substr($access, 0, length $1) = "";
1070 next if ($token =~ /\s/);
1072 if ($token eq "(") {
1073 my $new_cur_ary = [];
1074 push @stack, $new_cur_ary;
1075 push @{$cur_ary}, $new_cur_ary;
1076 $cur_ary = $new_cur_ary;
1078 } elsif ($token eq ")") {
1082 $main::lxdebug->leave_sub(2);
1086 $cur_ary = $stack[-1];
1088 } elsif (($token eq "|") || ($token eq "&")) {
1089 push @{$cur_ary}, $token;
1092 push @{$cur_ary}, $self->{RIGHTS}->{$login}->{$token} * 1;
1096 my $result = ($access || (1 < scalar @stack)) ? 0 : evaluate_rights_ary($stack[0]);
1098 $main::lxdebug->leave_sub(2);
1104 $main::lxdebug->enter_sub(2);
1109 my $default = shift;
1111 $self->{FULL_RIGHTS} ||= { };
1112 $self->{FULL_RIGHTS}->{$login} ||= { };
1114 if (!defined $self->{FULL_RIGHTS}->{$login}->{$right}) {
1115 $self->{RIGHTS} ||= { };
1116 $self->{RIGHTS}->{$login} ||= $self->load_rights_for_user($login);
1118 $self->{FULL_RIGHTS}->{$login}->{$right} = $self->_parse_rights_string($login, $right);
1121 my $granted = $self->{FULL_RIGHTS}->{$login}->{$right};
1122 $granted = $default if (!defined $granted);
1124 $main::lxdebug->leave_sub(2);
1130 $::lxdebug->enter_sub(2);
1131 my ($self, $right, $dont_abort) = @_;
1133 if ($self->check_right($::myconfig{login}, $right)) {
1134 $::lxdebug->leave_sub(2);
1139 delete $::form->{title};
1140 $::form->show_generic_error($::locale->text("You do not have the permissions to access this function."));
1143 $::lxdebug->leave_sub(2);
1148 sub load_rights_for_user {
1149 $::lxdebug->enter_sub;
1151 my ($self, $login) = @_;
1152 my $dbh = $self->dbconnect;
1153 my ($query, $sth, $row, $rights);
1155 $rights = { map { $_ => 0 } all_rights() };
1158 qq|SELECT gr."right", gr.granted
1159 FROM auth.group_rights gr
1162 FROM auth.user_group ug
1163 LEFT JOIN auth."user" u ON (ug.user_id = u.id)
1164 WHERE u.login = ?)|;
1166 $sth = prepare_execute_query($::form, $dbh, $query, $login);
1168 while ($row = $sth->fetchrow_hashref()) {
1169 $rights->{$row->{right}} |= $row->{granted};
1173 $::lxdebug->leave_sub;
1187 SL::Auth - Authentication and session handling
1193 =item C<set_session_value %values>
1195 Store all key/value pairs in C<%values> in the session. All of these
1196 values are copied back into C<$::form> in the next request
1199 The values can be any Perl structure. They are stored as YAML dumps.
1201 =item C<get_session_value $key>
1203 Retrieve a value from the session. Returns C<undef> if the value
1206 =item C<create_unique_sesion_value $value, %params>
1208 Create a unique key in the session and store C<$value>
1211 If C<$params{expiration}> is set then it is interpreted as a number of
1212 seconds after which the value is removed from the session. It will
1213 never expire if that parameter is falsish.
1215 Returns the key created in the session.
1217 =item C<expire_session_keys>
1219 Removes all keys from the session that have an expiration time set and
1220 whose expiration time is in the past.
1222 =item C<save_session>
1224 Stores the session values in the database. This is the only function
1225 that actually stores stuff in the database. Neither the various
1226 setters nor the deleter access the database.
1228 =item <save_form_in_session %params>
1230 Stores the content of C<$params{form}> (default: C<$::form>) in the
1231 session using L</create_unique_sesion_value>.
1233 If C<$params{non_scalars}> is trueish then non-scalar values will be
1234 stored as well. Default is to only store scalar values.
1236 The following keys will never be saved: C<login>, C<password>,
1237 C<stylesheet>, C<titlebar>, C<version>. Additional keys not to save
1238 can be given as an array ref in C<$params{skip_keys}>.
1240 Returns the unique key under which the form is stored.
1242 =item <restore_form_from_session $key, %params>
1244 Restores the form from the session into C<$params{form}> (default:
1247 If C<$params{clobber}> is falsish then existing values with the same
1248 key in C<$params{form}> will not be overwritten. C<$params{clobber}>
1261 Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt>