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();
141 my $password = shift;
142 my $is_crypted = shift;
144 $password = crypt $password, 'ro' if (!$password || !$is_crypted);
145 my $admin_password = crypt "$self->{admin_password}", 'ro';
147 $main::lxdebug->leave_sub();
149 return OK if $password eq $admin_password;
155 $main::lxdebug->enter_sub();
157 my ($self, $login, $password) = @_;
159 $main::lxdebug->leave_sub();
161 my $result = $login ? $self->{authenticator}->authenticate($login, $password) : ERR_USER;
162 return OK if $result eq OK;
167 sub store_credentials_in_session {
168 my ($self, %params) = @_;
170 $params{password} = SL::Auth::Password->hash_if_unhashed(login => $params{login}, password => $params{password})
171 unless $self->{authenticator}->requires_cleartext_password;
173 $self->set_session_value(login => $params{login}, password => $params{password});
177 $main::lxdebug->enter_sub(2);
180 my $may_fail = shift;
183 $main::lxdebug->leave_sub(2);
187 my $cfg = $self->{DB_config};
188 my $dsn = 'dbi:Pg:dbname=' . $cfg->{db} . ';host=' . $cfg->{host};
191 $dsn .= ';port=' . $cfg->{port};
194 $main::lxdebug->message(LXDebug->DEBUG1, "Auth::dbconnect DSN: $dsn");
196 $self->{dbh} = SL::DBConnect->connect($dsn, $cfg->{user}, $cfg->{password}, { pg_enable_utf8 => $::locale->is_utf8, AutoCommit => 1 });
198 if (!$may_fail && !$self->{dbh}) {
199 $main::form->error($main::locale->text('The connection to the authentication database failed:') . "\n" . $DBI::errstr);
202 $main::lxdebug->leave_sub(2);
208 $main::lxdebug->enter_sub();
213 $self->{dbh}->disconnect();
217 $main::lxdebug->leave_sub();
221 $main::lxdebug->enter_sub();
225 my $dbh = $self->dbconnect();
226 my $query = qq|SELECT COUNT(*) FROM pg_tables WHERE (schemaname = 'auth') AND (tablename = 'user')|;
228 my ($count) = $dbh->selectrow_array($query);
230 $main::lxdebug->leave_sub();
236 $main::lxdebug->enter_sub();
240 my $dbh = $self->dbconnect(1);
242 $main::lxdebug->leave_sub();
247 sub create_database {
248 $main::lxdebug->enter_sub();
253 my $cfg = $self->{DB_config};
255 if (!$params{superuser}) {
256 $params{superuser} = $cfg->{user};
257 $params{superuser_password} = $cfg->{password};
260 $params{template} ||= 'template0';
261 $params{template} =~ s|[^a-zA-Z0-9_\-]||g;
263 my $dsn = 'dbi:Pg:dbname=template1;host=' . $cfg->{host};
266 $dsn .= ';port=' . $cfg->{port};
269 $main::lxdebug->message(LXDebug->DEBUG1(), "Auth::create_database DSN: $dsn");
271 my $charset = $::lx_office_conf{system}->{dbcharset};
272 $charset ||= Common::DEFAULT_CHARSET;
273 my $encoding = $Common::charset_to_db_encoding{$charset};
274 $encoding ||= 'UNICODE';
276 my $dbh = SL::DBConnect->connect($dsn, $params{superuser}, $params{superuser_password}, { pg_enable_utf8 => scalar($charset =~ m/^utf-?8$/i) });
279 $main::form->error($main::locale->text('The connection to the template database failed:') . "\n" . $DBI::errstr);
282 my $query = qq|CREATE DATABASE "$cfg->{db}" OWNER "$cfg->{user}" TEMPLATE "$params{template}" ENCODING '$encoding'|;
284 $main::lxdebug->message(LXDebug->DEBUG1(), "Auth::create_database query: $query");
289 my $error = $dbh->errstr();
291 $query = qq|SELECT pg_encoding_to_char(encoding) FROM pg_database WHERE datname = 'template0'|;
292 my ($cluster_encoding) = $dbh->selectrow_array($query);
294 if ($cluster_encoding && ($cluster_encoding =~ m/^(?:UTF-?8|UNICODE)$/i) && ($encoding !~ m/^(?:UTF-?8|UNICODE)$/i)) {
295 $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.');
300 $main::form->error($main::locale->text('The creation of the authentication database failed:') . "\n" . $error);
305 $main::lxdebug->leave_sub();
309 $main::lxdebug->enter_sub();
312 my $dbh = $self->dbconnect();
314 my $charset = $::lx_office_conf{system}->{dbcharset};
315 $charset ||= Common::DEFAULT_CHARSET;
318 SL::DBUpgrade2->new(form => $::form)->process_query($dbh, 'sql/auth_db.sql', undef, $charset);
320 $main::lxdebug->leave_sub();
324 $main::lxdebug->enter_sub();
330 my $form = $main::form;
332 my $dbh = $self->dbconnect();
334 my ($sth, $query, $user_id);
338 $query = qq|SELECT id FROM auth."user" WHERE login = ?|;
339 ($user_id) = selectrow_query($form, $dbh, $query, $login);
342 $query = qq|SELECT nextval('auth.user_id_seq')|;
343 ($user_id) = selectrow_query($form, $dbh, $query);
345 $query = qq|INSERT INTO auth."user" (id, login) VALUES (?, ?)|;
346 do_query($form, $dbh, $query, $user_id, $login);
349 $query = qq|DELETE FROM auth.user_config WHERE (user_id = ?)|;
350 do_query($form, $dbh, $query, $user_id);
352 $query = qq|INSERT INTO auth.user_config (user_id, cfg_key, cfg_value) VALUES (?, ?, ?)|;
353 $sth = prepare_query($form, $dbh, $query);
355 while (my ($cfg_key, $cfg_value) = each %params) {
356 next if ($cfg_key eq 'password');
358 do_statement($form, $sth, $query, $user_id, $cfg_key, $cfg_value);
363 $main::lxdebug->leave_sub();
366 sub can_change_password {
369 return $self->{authenticator}->can_change_password();
372 sub change_password {
373 $main::lxdebug->enter_sub();
376 my $result = $self->{authenticator}->change_password(@_);
378 $main::lxdebug->leave_sub();
384 $main::lxdebug->enter_sub();
388 my $dbh = $self->dbconnect();
389 my $query = qq|SELECT u.id, u.login, cfg.cfg_key, cfg.cfg_value
390 FROM auth.user_config cfg
391 LEFT JOIN auth."user" u ON (cfg.user_id = u.id)|;
392 my $sth = prepare_execute_query($main::form, $dbh, $query);
396 while (my $ref = $sth->fetchrow_hashref()) {
397 $users{$ref->{login}} ||= { 'login' => $ref->{login}, 'id' => $ref->{id} };
398 $users{$ref->{login}}->{$ref->{cfg_key}} = $ref->{cfg_value} if (($ref->{cfg_key} ne 'login') && ($ref->{cfg_key} ne 'id'));
403 $main::lxdebug->leave_sub();
409 $main::lxdebug->enter_sub();
414 my $dbh = $self->dbconnect();
415 my $query = qq|SELECT u.id, u.login, cfg.cfg_key, cfg.cfg_value
416 FROM auth.user_config cfg
417 LEFT JOIN auth."user" u ON (cfg.user_id = u.id)
418 WHERE (u.login = ?)|;
419 my $sth = prepare_execute_query($main::form, $dbh, $query, $login);
423 while (my $ref = $sth->fetchrow_hashref()) {
424 $user_data{$ref->{cfg_key}} = $ref->{cfg_value};
425 @user_data{qw(id login)} = @{$ref}{qw(id login)};
430 $main::lxdebug->leave_sub();
436 $main::lxdebug->enter_sub();
441 my $dbh = $self->dbconnect();
442 my ($id) = selectrow_query($main::form, $dbh, qq|SELECT id FROM auth."user" WHERE login = ?|, $login);
444 $main::lxdebug->leave_sub();
450 $::lxdebug->enter_sub;
455 my $u_dbh = $self->get_user_dbh($login, may_fail => 1);
456 my $dbh = $self->dbconnect;
460 my $query = qq|SELECT id FROM auth."user" WHERE login = ?|;
462 my ($id) = selectrow_query($::form, $dbh, $query, $login);
464 $dbh->rollback and return $::lxdebug->leave_sub if (!$id);
466 do_query($::form, $dbh, qq|DELETE FROM auth.user_group WHERE user_id = ?|, $id);
467 do_query($::form, $dbh, qq|DELETE FROM auth.user_config WHERE user_id = ?|, $id);
468 do_query($::form, $u_dbh, qq|UPDATE employee SET deleted = 't' WHERE login = ?|, $login) if $u_dbh;
471 $u_dbh->commit if $u_dbh;
473 $::lxdebug->leave_sub;
476 # --------------------------------------
480 sub restore_session {
481 $main::lxdebug->enter_sub();
485 my $cgi = $main::cgi;
486 $cgi ||= CGI->new('');
488 $session_id = $cgi->cookie($self->get_session_cookie_name());
489 $session_id =~ s|[^0-9a-f]||g;
491 $self->{SESSION} = { };
494 $main::lxdebug->leave_sub();
498 my ($dbh, $query, $sth, $cookie, $ref, $form);
502 $dbh = $self->dbconnect();
503 $query = qq|SELECT *, (mtime < (now() - '$self->{session_timeout}m'::interval)) AS is_expired FROM auth.session WHERE id = ?|;
505 $cookie = selectfirst_hashref_query($form, $dbh, $query, $session_id);
507 if (!$cookie || $cookie->{is_expired} || ($cookie->{ip_address} ne $ENV{REMOTE_ADDR})) {
508 $self->destroy_session();
509 $main::lxdebug->leave_sub();
510 return $cookie ? SESSION_EXPIRED : SESSION_NONE;
513 $query = qq|SELECT sess_key, sess_value FROM auth.session_content WHERE session_id = ?|;
514 $sth = prepare_execute_query($form, $dbh, $query, $session_id);
516 while (my $ref = $sth->fetchrow_hashref()) {
517 $self->{SESSION}->{$ref->{sess_key}} = $ref->{sess_value};
518 next if defined $form->{$ref->{sess_key}};
520 my $params = $self->_load_value($ref->{sess_value});
521 $form->{$ref->{sess_key}} = $params->{data} if $params->{auto_restore} || $params->{simple};
526 $main::lxdebug->leave_sub();
532 my ($self, $value) = @_;
534 return { simple => 1, data => $value } if $value !~ m/^---/;
536 my %params = ( simple => 1 );
538 my $data = YAML::Load($value);
540 if (ref $data eq 'HASH') {
541 map { $params{$_} = $data->{$_} } keys %{ $data };
545 $params{data} = $data;
549 } or $params{data} = $value;
554 sub destroy_session {
555 $main::lxdebug->enter_sub();
560 my $dbh = $self->dbconnect();
564 do_query($main::form, $dbh, qq|DELETE FROM auth.session_content WHERE session_id = ?|, $session_id);
565 do_query($main::form, $dbh, qq|DELETE FROM auth.session WHERE id = ?|, $session_id);
569 SL::SessionFile->destroy_session($session_id);
572 $self->{SESSION} = { };
575 $main::lxdebug->leave_sub();
578 sub expire_sessions {
579 $main::lxdebug->enter_sub();
583 $main::lxdebug->leave_sub and return if !$self->session_tables_present;
585 my $dbh = $self->dbconnect();
587 my $query = qq|SELECT id
589 WHERE (mtime < (now() - '$self->{session_timeout}m'::interval))|;
591 my @ids = selectall_array_query($::form, $dbh, $query);
596 SL::SessionFile->destroy_session($_) for @ids;
598 $query = qq|DELETE FROM auth.session_content
599 WHERE session_id IN (| . join(', ', ('?') x scalar(@ids)) . qq|)|;
600 do_query($main::form, $dbh, $query, @ids);
602 $query = qq|DELETE FROM auth.session
603 WHERE id IN (| . join(', ', ('?') x scalar(@ids)) . qq|)|;
604 do_query($main::form, $dbh, $query, @ids);
609 $main::lxdebug->leave_sub();
612 sub _create_session_id {
613 $main::lxdebug->enter_sub();
616 map { push @data, int(rand() * 255); } (1..32);
618 my $id = md5_hex(pack 'C*', @data);
620 $main::lxdebug->leave_sub();
625 sub create_or_refresh_session {
626 $session_id ||= shift->_create_session_id;
630 $::lxdebug->enter_sub;
632 my $provided_dbh = shift;
634 my $dbh = $provided_dbh || $self->dbconnect(1);
636 $::lxdebug->leave_sub && return unless $dbh && $session_id;
638 $dbh->begin_work unless $provided_dbh;
640 do_query($::form, $dbh, qq|LOCK auth.session_content|);
641 do_query($::form, $dbh, qq|DELETE FROM auth.session_content WHERE session_id = ?|, $session_id);
643 my $query = qq|SELECT id FROM auth.session WHERE id = ?|;
645 my ($id) = selectrow_query($::form, $dbh, $query, $session_id);
648 do_query($::form, $dbh, qq|UPDATE auth.session SET mtime = now() WHERE id = ?|, $session_id);
650 do_query($::form, $dbh, qq|INSERT INTO auth.session (id, ip_address, mtime) VALUES (?, ?, now())|, $session_id, $ENV{REMOTE_ADDR});
653 if (%{ $self->{SESSION} }) {
654 my $query = qq|INSERT INTO auth.session_content (session_id, sess_key, sess_value) VALUES (?, ?, ?)|;
655 my $sth = prepare_query($::form, $dbh, $query);
657 foreach my $key (sort keys %{ $self->{SESSION} }) {
658 do_statement($::form, $sth, $query, $session_id, $key, $self->{SESSION}->{$key});
664 $dbh->commit() unless $provided_dbh;
665 $::lxdebug->leave_sub;
668 sub set_session_value {
669 $main::lxdebug->enter_sub();
674 $self->{SESSION} ||= { };
676 while (my ($key, $value) = each %params) {
677 $self->{SESSION}->{ $key } = YAML::Dump(ref($value) eq 'HASH' ? { data => $value } : $value);
680 $main::lxdebug->leave_sub();
685 sub delete_session_value {
686 $main::lxdebug->enter_sub();
690 $self->{SESSION} ||= { };
691 delete @{ $self->{SESSION} }{ @_ };
693 $main::lxdebug->leave_sub();
698 sub get_session_value {
699 $main::lxdebug->enter_sub();
702 my $params = $self->{SESSION} ? $self->_load_value($self->{SESSION}->{ $_[0] }) : {};
704 $main::lxdebug->leave_sub();
706 return $params->{data};
709 sub create_unique_sesion_value {
710 my ($self, $value, %params) = @_;
712 $self->{SESSION} ||= { };
714 my @now = gettimeofday();
715 my $key = "$$-" . ($now[0] * 1000000 + $now[1]) . "-";
716 $self->{unique_counter} ||= 0;
718 $self->{unique_counter}++ while exists $self->{SESSION}->{$key . $self->{unique_counter}};
719 $self->{unique_counter}++;
721 $value = { expiration => $params{expiration} ? ($now[0] + $params{expiration}) * 1000000 + $now[1] : undef,
725 $self->{SESSION}->{$key . $self->{unique_counter}} = YAML::Dump($value);
727 return $key . $self->{unique_counter};
730 sub save_form_in_session {
731 my ($self, %params) = @_;
733 my $form = delete($params{form}) || $::form;
734 my $non_scalars = delete $params{non_scalars};
737 my %skip_keys = map { ( $_ => 1 ) } (qw(login password stylesheet version titlebar), @{ $params{skip_keys} || [] });
739 foreach my $key (grep { !$skip_keys{$_} } keys %{ $form }) {
740 $data->{$key} = $form->{$key} if !ref($form->{$key}) || $non_scalars;
743 return $self->create_unique_sesion_value($data, %params);
746 sub restore_form_from_session {
747 my ($self, $key, %params) = @_;
749 my $data = $self->get_session_value($key);
750 return $self unless $data;
752 my $form = delete($params{form}) || $::form;
753 my $clobber = exists $params{clobber} ? $params{clobber} : 1;
755 map { $form->{$_} = $data->{$_} if $clobber || !exists $form->{$_} } keys %{ $data };
760 sub expire_session_keys {
763 $self->{SESSION} ||= { };
765 my @now = gettimeofday();
766 my $now = $now[0] * 1000000 + $now[1];
768 $self->delete_session_value(map { $_->[0] }
769 grep { $_->[1]->{expiration} && ($now > $_->[1]->{expiration}) }
770 map { [ $_, $self->_load_value($self->{SESSION}->{$_}) ] }
771 keys %{ $self->{SESSION} });
776 sub _has_expiration {
778 return (ref $value eq 'HASH') && exists($value->{expiration}) && $value->{data};
781 sub set_cookie_environment_variable {
783 $ENV{HTTP_COOKIE} = $self->get_session_cookie_name() . "=${session_id}";
786 sub get_session_cookie_name {
789 return $self->{cookie_name} || 'lx_office_erp_session_id';
796 sub session_tables_present {
797 $main::lxdebug->enter_sub();
801 # Only re-check for the presence of auth tables if either the check
802 # hasn't been done before of if they weren't present.
803 if ($self->{session_tables_present}) {
804 $main::lxdebug->leave_sub();
805 return $self->{session_tables_present};
808 my $dbh = $self->dbconnect(1);
811 $main::lxdebug->leave_sub();
818 WHERE (schemaname = 'auth')
819 AND (tablename IN ('session', 'session_content'))|;
821 my ($count) = selectrow_query($main::form, $dbh, $query);
823 $self->{session_tables_present} = 2 == $count;
825 $main::lxdebug->leave_sub();
827 return $self->{session_tables_present};
830 # --------------------------------------
832 sub all_rights_full {
833 my $locale = $main::locale;
836 ["--crm", $locale->text("CRM optional software")],
837 ["crm_search", $locale->text("CRM search")],
838 ["crm_new", $locale->text("CRM create customers, vendors and contacts")],
839 ["crm_service", $locale->text("CRM services")],
840 ["crm_admin", $locale->text("CRM admin")],
841 ["crm_adminuser", $locale->text("CRM user")],
842 ["crm_adminstatus", $locale->text("CRM status")],
843 ["crm_email", $locale->text("CRM send email")],
844 ["crm_termin", $locale->text("CRM termin")],
845 ["crm_opportunity", $locale->text("CRM opportunity")],
846 ["crm_knowhow", $locale->text("CRM know how")],
847 ["crm_follow", $locale->text("CRM follow up")],
848 ["crm_notices", $locale->text("CRM notices")],
849 ["crm_other", $locale->text("CRM other")],
850 ["--master_data", $locale->text("Master Data")],
851 ["customer_vendor_edit", $locale->text("Create and edit customers and vendors")],
852 ["part_service_assembly_edit", $locale->text("Create and edit parts, services, assemblies")],
853 ["project_edit", $locale->text("Create and edit projects")],
854 ["license_edit", $locale->text("Manage license keys")],
855 ["--ar", $locale->text("AR")],
856 ["sales_quotation_edit", $locale->text("Create and edit sales quotations")],
857 ["sales_order_edit", $locale->text("Create and edit sales orders")],
858 ["sales_delivery_order_edit", $locale->text("Create and edit sales delivery orders")],
859 ["invoice_edit", $locale->text("Create and edit invoices and credit notes")],
860 ["dunning_edit", $locale->text("Create and edit dunnings")],
861 ["sales_all_edit", $locale->text("View/edit all employees sales documents")],
862 ["--ap", $locale->text("AP")],
863 ["request_quotation_edit", $locale->text("Create and edit RFQs")],
864 ["purchase_order_edit", $locale->text("Create and edit purchase orders")],
865 ["purchase_delivery_order_edit", $locale->text("Create and edit purchase delivery orders")],
866 ["vendor_invoice_edit", $locale->text("Create and edit vendor invoices")],
867 ["--warehouse_management", $locale->text("Warehouse management")],
868 ["warehouse_contents", $locale->text("View warehouse content")],
869 ["warehouse_management", $locale->text("Warehouse management")],
870 ["--general_ledger_cash", $locale->text("General ledger and cash")],
871 ["general_ledger", $locale->text("Transactions, AR transactions, AP transactions")],
872 ["datev_export", $locale->text("DATEV Export")],
873 ["cash", $locale->text("Receipt, payment, reconciliation")],
874 ["--reports", $locale->text('Reports')],
875 ["report", $locale->text('All reports')],
876 ["advance_turnover_tax_return", $locale->text('Advance turnover tax return')],
877 ["--batch_printing", $locale->text("Batch Printing")],
878 ["batch_printing", $locale->text("Batch Printing")],
879 ["--others", $locale->text("Others")],
880 ["email_bcc", $locale->text("May set the BCC field when sending emails")],
881 ["config", $locale->text("Change Lx-Office installation settings (all menu entries beneath 'System')")],
888 return grep !/^--/, map { $_->[0] } all_rights_full();
892 $main::lxdebug->enter_sub();
896 my $form = $main::form;
898 my $dbh = $self->dbconnect();
900 my $query = 'SELECT * FROM auth."group"';
901 my $sth = prepare_execute_query($form, $dbh, $query);
905 while ($row = $sth->fetchrow_hashref()) {
906 $groups->{$row->{id}} = $row;
910 $query = 'SELECT * FROM auth.user_group WHERE group_id = ?';
911 $sth = prepare_query($form, $dbh, $query);
913 foreach $group (values %{$groups}) {
916 do_statement($form, $sth, $query, $group->{id});
918 while ($row = $sth->fetchrow_hashref()) {
919 push @members, $row->{user_id};
921 $group->{members} = [ uniq @members ];
925 $query = 'SELECT * FROM auth.group_rights WHERE group_id = ?';
926 $sth = prepare_query($form, $dbh, $query);
928 foreach $group (values %{$groups}) {
929 $group->{rights} = {};
931 do_statement($form, $sth, $query, $group->{id});
933 while ($row = $sth->fetchrow_hashref()) {
934 $group->{rights}->{$row->{right}} |= $row->{granted};
937 map { $group->{rights}->{$_} = 0 if (!defined $group->{rights}->{$_}); } all_rights();
941 $main::lxdebug->leave_sub();
947 $main::lxdebug->enter_sub();
952 my $form = $main::form;
953 my $dbh = $self->dbconnect();
957 my ($query, $sth, $row, $rights);
960 ($group->{id}) = selectrow_query($form, $dbh, qq|SELECT nextval('auth.group_id_seq')|);
962 $query = qq|INSERT INTO auth."group" (id, name, description) VALUES (?, '', '')|;
963 do_query($form, $dbh, $query, $group->{id});
966 do_query($form, $dbh, qq|UPDATE auth."group" SET name = ?, description = ? WHERE id = ?|, map { $group->{$_} } qw(name description id));
968 do_query($form, $dbh, qq|DELETE FROM auth.user_group WHERE group_id = ?|, $group->{id});
970 $query = qq|INSERT INTO auth.user_group (user_id, group_id) VALUES (?, ?)|;
971 $sth = prepare_query($form, $dbh, $query);
973 foreach my $user_id (uniq @{ $group->{members} }) {
974 do_statement($form, $sth, $query, $user_id, $group->{id});
978 do_query($form, $dbh, qq|DELETE FROM auth.group_rights WHERE group_id = ?|, $group->{id});
980 $query = qq|INSERT INTO auth.group_rights (group_id, "right", granted) VALUES (?, ?, ?)|;
981 $sth = prepare_query($form, $dbh, $query);
983 foreach my $right (keys %{ $group->{rights} }) {
984 do_statement($form, $sth, $query, $group->{id}, $right, $group->{rights}->{$right} ? 't' : 'f');
990 $main::lxdebug->leave_sub();
994 $main::lxdebug->enter_sub();
999 my $form = $main::form;
1001 my $dbh = $self->dbconnect();
1004 do_query($form, $dbh, qq|DELETE FROM auth.user_group WHERE group_id = ?|, $id);
1005 do_query($form, $dbh, qq|DELETE FROM auth.group_rights WHERE group_id = ?|, $id);
1006 do_query($form, $dbh, qq|DELETE FROM auth."group" WHERE id = ?|, $id);
1010 $main::lxdebug->leave_sub();
1013 sub evaluate_rights_ary {
1014 $main::lxdebug->enter_sub(2);
1021 foreach my $el (@{$ary}) {
1022 if (ref $el eq "ARRAY") {
1023 if ($action eq '|') {
1024 $value |= evaluate_rights_ary($el);
1026 $value &= evaluate_rights_ary($el);
1029 } elsif (($el eq '&') || ($el eq '|')) {
1032 } elsif ($action eq '|') {
1041 $main::lxdebug->leave_sub(2);
1046 sub _parse_rights_string {
1047 $main::lxdebug->enter_sub(2);
1057 push @stack, $cur_ary;
1059 while ($access =~ m/^([a-z_0-9]+|\||\&|\(|\)|\s+)/) {
1061 substr($access, 0, length $1) = "";
1063 next if ($token =~ /\s/);
1065 if ($token eq "(") {
1066 my $new_cur_ary = [];
1067 push @stack, $new_cur_ary;
1068 push @{$cur_ary}, $new_cur_ary;
1069 $cur_ary = $new_cur_ary;
1071 } elsif ($token eq ")") {
1075 $main::lxdebug->leave_sub(2);
1079 $cur_ary = $stack[-1];
1081 } elsif (($token eq "|") || ($token eq "&")) {
1082 push @{$cur_ary}, $token;
1085 push @{$cur_ary}, $self->{RIGHTS}->{$login}->{$token} * 1;
1089 my $result = ($access || (1 < scalar @stack)) ? 0 : evaluate_rights_ary($stack[0]);
1091 $main::lxdebug->leave_sub(2);
1097 $main::lxdebug->enter_sub(2);
1102 my $default = shift;
1104 $self->{FULL_RIGHTS} ||= { };
1105 $self->{FULL_RIGHTS}->{$login} ||= { };
1107 if (!defined $self->{FULL_RIGHTS}->{$login}->{$right}) {
1108 $self->{RIGHTS} ||= { };
1109 $self->{RIGHTS}->{$login} ||= $self->load_rights_for_user($login);
1111 $self->{FULL_RIGHTS}->{$login}->{$right} = $self->_parse_rights_string($login, $right);
1114 my $granted = $self->{FULL_RIGHTS}->{$login}->{$right};
1115 $granted = $default if (!defined $granted);
1117 $main::lxdebug->leave_sub(2);
1123 $::lxdebug->enter_sub(2);
1124 my ($self, $right, $dont_abort) = @_;
1126 if ($self->check_right($::myconfig{login}, $right)) {
1127 $::lxdebug->leave_sub(2);
1132 delete $::form->{title};
1133 $::form->show_generic_error($::locale->text("You do not have the permissions to access this function."));
1136 $::lxdebug->leave_sub(2);
1141 sub load_rights_for_user {
1142 $::lxdebug->enter_sub;
1144 my ($self, $login) = @_;
1145 my $dbh = $self->dbconnect;
1146 my ($query, $sth, $row, $rights);
1148 $rights = { map { $_ => 0 } all_rights() };
1151 qq|SELECT gr."right", gr.granted
1152 FROM auth.group_rights gr
1155 FROM auth.user_group ug
1156 LEFT JOIN auth."user" u ON (ug.user_id = u.id)
1157 WHERE u.login = ?)|;
1159 $sth = prepare_execute_query($::form, $dbh, $query, $login);
1161 while ($row = $sth->fetchrow_hashref()) {
1162 $rights->{$row->{right}} |= $row->{granted};
1166 $::lxdebug->leave_sub;
1180 SL::Auth - Authentication and session handling
1186 =item C<set_session_value %values>
1188 Store all key/value pairs in C<%values> in the session. All of these
1189 values are copied back into C<$::form> in the next request
1192 The values can be any Perl structure. They are stored as YAML dumps.
1194 =item C<get_session_value $key>
1196 Retrieve a value from the session. Returns C<undef> if the value
1199 =item C<create_unique_sesion_value $value, %params>
1201 Create a unique key in the session and store C<$value>
1204 If C<$params{expiration}> is set then it is interpreted as a number of
1205 seconds after which the value is removed from the session. It will
1206 never expire if that parameter is falsish.
1208 Returns the key created in the session.
1210 =item C<expire_session_keys>
1212 Removes all keys from the session that have an expiration time set and
1213 whose expiration time is in the past.
1215 =item C<save_session>
1217 Stores the session values in the database. This is the only function
1218 that actually stores stuff in the database. Neither the various
1219 setters nor the deleter access the database.
1221 =item <save_form_in_session %params>
1223 Stores the content of C<$params{form}> (default: C<$::form>) in the
1224 session using L</create_unique_sesion_value>.
1226 If C<$params{non_scalars}> is trueish then non-scalar values will be
1227 stored as well. Default is to only store scalar values.
1229 The following keys will never be saved: C<login>, C<password>,
1230 C<stylesheet>, C<titlebar>, C<version>. Additional keys not to save
1231 can be given as an array ref in C<$params{skip_keys}>.
1233 Returns the unique key under which the form is stored.
1235 =item <restore_form_from_session $key, %params>
1237 Restores the form from the session into C<$params{form}> (default:
1240 If C<$params{clobber}> is falsish then existing values with the same
1241 key in C<$params{form}> will not be overwritten. C<$params{clobber}>
1254 Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt>