4 use constant ERR_PASSWORD => 1;
5 use constant ERR_BACKEND => 100;
7 use constant SESSION_OK => 0;
8 use constant SESSION_NONE => 1;
9 use constant SESSION_EXPIRED => 2;
11 use Digest::MD5 qw(md5_hex);
13 use Time::HiRes qw(gettimeofday);
22 $main::lxdebug->enter_sub();
29 $self->{SESSION} = { };
31 $self->_read_auth_config();
33 $main::lxdebug->leave_sub();
41 $self->{dbh}->disconnect() if ($self->{dbh});
44 sub _read_auth_config {
45 $main::lxdebug->enter_sub();
49 my $form = $main::form;
50 my $locale = $main::locale;
53 my $in = IO::File->new('config/authentication.pl', 'r');
56 $form->error($locale->text('The config file "config/authentication.pl" was not found.'));
67 $form->error($locale->text('The config file "config/authentication.pl" contained invalid Perl code:') . "\n" . $@);
70 if ($self->{module} eq 'DB') {
71 $self->{authenticator} = SL::Auth::DB->new($self);
73 } elsif ($self->{module} eq 'LDAP') {
74 $self->{authenticator} = SL::Auth::LDAP->new($self);
77 if (!$self->{authenticator}) {
78 $form->error($locale->text('No or an unknown authenticantion module specified in "config/authentication.pl".'));
81 my $cfg = $self->{DB_config};
84 $form->error($locale->text('config/authentication.pl: Key "DB_config" is missing.'));
87 if (!$cfg->{host} || !$cfg->{db} || !$cfg->{user}) {
88 $form->error($locale->text('config/authentication.pl: Missing parameters in "DB_config". Required parameters are "host", "db" and "user".'));
91 $self->{authenticator}->verify_config();
93 $self->{session_timeout} *= 1;
94 $self->{session_timeout} = 8 * 60 if (!$self->{session_timeout});
96 $main::lxdebug->leave_sub();
99 sub authenticate_root {
100 $main::lxdebug->enter_sub();
103 my $password = shift;
104 my $is_crypted = shift;
106 $password = crypt $password, 'ro' if (!$password || !$is_crypted);
107 my $admin_password = crypt "$self->{admin_password}", 'ro';
109 $main::lxdebug->leave_sub();
111 return $password eq $admin_password ? OK : ERR_PASSWORD;
115 $main::lxdebug->enter_sub();
119 $main::lxdebug->leave_sub();
121 return $self->{authenticator}->authenticate(@_);
125 $main::lxdebug->enter_sub();
128 my $may_fail = shift;
131 $main::lxdebug->leave_sub();
135 my $cfg = $self->{DB_config};
136 my $dsn = 'dbi:Pg:dbname=' . $cfg->{db} . ';host=' . $cfg->{host};
139 $dsn .= ';port=' . $cfg->{port};
142 $main::lxdebug->message(LXDebug::DEBUG1, "Auth::dbconnect DSN: $dsn");
144 $self->{dbh} = DBI->connect($dsn, $cfg->{user}, $cfg->{password}, { 'AutoCommit' => 0 });
146 if (!$may_fail && !$self->{dbh}) {
147 $main::form->error($main::locale->text('The connection to the authentication database failed:') . "\n" . $DBI::errstr);
150 $main::lxdebug->leave_sub();
156 $main::lxdebug->enter_sub();
161 $self->{dbh}->disconnect();
165 $main::lxdebug->leave_sub();
169 $main::lxdebug->enter_sub();
173 my $dbh = $self->dbconnect();
174 my $query = qq|SELECT COUNT(*) FROM pg_tables WHERE (schemaname = 'auth') AND (tablename = 'user')|;
176 my ($count) = $dbh->selectrow_array($query);
178 $main::lxdebug->leave_sub();
184 $main::lxdebug->enter_sub();
188 my $dbh = $self->dbconnect(1);
190 $main::lxdebug->leave_sub();
195 sub create_database {
196 $main::lxdebug->enter_sub();
201 my $cfg = $self->{DB_config};
203 if (!$params{superuser}) {
204 $params{superuser} = $cfg->{user};
205 $params{superuser_password} = $cfg->{password};
208 $params{template} ||= 'template0';
209 $params{template} =~ s|[^a-zA-Z0-9_\-]||g;
211 my $dsn = 'dbi:Pg:dbname=template1;host=' . $cfg->{host};
214 $dsn .= ';port=' . $cfg->{port};
217 $main::lxdebug->message(LXDebug::DEBUG1, "Auth::create_database DSN: $dsn");
219 my $dbh = DBI->connect($dsn, $params{superuser}, $params{superuser_password});
222 $main::form->error($main::locale->text('The connection to the template database failed:') . "\n" . $DBI::errstr);
225 my $charset = $main::charset;
226 $charset ||= Common::DEFAULT_CHARSET;
227 my $encoding = $Common::charset_to_db_encoding{$charset};
228 $encoding ||= 'UNICODE';
230 my $query = qq|CREATE DATABASE "$cfg->{db}" OWNER "$cfg->{user}" TEMPLATE "$params{template}" ENCODING '$encoding'|;
232 $main::lxdebug->message(LXDebug::DEBUG1, "Auth::create_database query: $query");
239 $main::form->error($main::locale->text('The creation of the authentication database failed:') . "\n" . $DBI::errstr);
244 $main::lxdebug->leave_sub();
248 $main::lxdebug->enter_sub();
251 my $dbh = $self->dbconnect();
253 my $charset = $main::charset;
254 $charset ||= Common::DEFAULT_CHARSET;
257 User->process_query($main::form, $dbh, 'sql/auth_db.sql', undef, $charset);
259 $main::lxdebug->leave_sub();
263 $main::lxdebug->enter_sub();
269 my $form = $main::form;
271 my $dbh = $self->dbconnect();
273 my ($sth, $query, $user_id);
275 $query = qq|SELECT id FROM auth."user" WHERE login = ?|;
276 ($user_id) = selectrow_query($form, $dbh, $query, $login);
279 $query = qq|SELECT nextval('auth.user_id_seq')|;
280 ($user_id) = selectrow_query($form, $dbh, $query);
282 $query = qq|INSERT INTO auth."user" (id, login) VALUES (?, ?)|;
283 do_query($form, $dbh, $query, $user_id, $login);
286 $query = qq|DELETE FROM auth.user_config WHERE (user_id = ?)|;
287 do_query($form, $dbh, $query, $user_id);
289 $query = qq|INSERT INTO auth.user_config (user_id, cfg_key, cfg_value) VALUES (?, ?, ?)|;
290 $sth = prepare_query($form, $dbh, $query);
292 while (my ($cfg_key, $cfg_value) = each %params) {
293 next if ($cfg_key eq 'password');
295 do_statement($form, $sth, $query, $user_id, $cfg_key, $cfg_value);
300 $main::lxdebug->leave_sub();
303 sub can_change_password {
306 return $self->{authenticator}->can_change_password();
309 sub change_password {
310 $main::lxdebug->enter_sub();
313 my $result = $self->{authenticator}->change_password(@_);
315 $main::lxdebug->leave_sub();
321 $main::lxdebug->enter_sub();
325 my $dbh = $self->dbconnect();
326 my $query = qq|SELECT u.id, u.login, cfg.cfg_key, cfg.cfg_value
327 FROM auth.user_config cfg
328 LEFT JOIN auth."user" u ON (cfg.user_id = u.id)|;
329 my $sth = prepare_execute_query($main::form, $dbh, $query);
333 while (my $ref = $sth->fetchrow_hashref()) {
334 $users{$ref->{login}} ||= { 'login' => $ref->{login}, 'id' => $ref->{id} };
335 $users{$ref->{login}}->{$ref->{cfg_key}} = $ref->{cfg_value} if (($ref->{cfg_key} ne 'login') && ($ref->{cfg_key} ne 'id'));
340 $main::lxdebug->leave_sub();
346 $main::lxdebug->enter_sub();
351 my $dbh = $self->dbconnect();
352 my $query = qq|SELECT cfg.cfg_key, cfg.cfg_value
353 FROM auth.user_config cfg
354 LEFT JOIN auth."user" u ON (cfg.user_id = u.id)
355 WHERE (u.login = ?)|;
356 my $sth = prepare_execute_query($main::form, $dbh, $query, $login);
360 while (my $ref = $sth->fetchrow_hashref()) {
361 $user_data{$ref->{cfg_key}} = $ref->{cfg_value};
362 $user_data{login} = $login;
367 $main::lxdebug->leave_sub();
373 $main::lxdebug->enter_sub();
378 my $dbh = $self->dbconnect();
379 my ($id) = selectrow_query($main::form, $dbh, qq|SELECT id FROM auth."user" WHERE login = ?|, $login);
381 $main::lxdebug->leave_sub();
387 $main::lxdebug->enter_sub();
392 my $form = $main::form;
394 my $dbh = $self->dbconnect();
395 my $query = qq|SELECT id FROM auth."user" WHERE login = ?|;
397 my ($id) = selectrow_query($form, $dbh, $query, $login);
399 return $main::lxdebug->leave_sub() if (!$id);
401 do_query($form, $dbh, qq|DELETE FROM auth.user_group WHERE user_id = ?|, $id);
402 do_query($form, $dbh, qq|DELETE FROM auth.user_config WHERE user_id = ?|, $id);
406 $main::lxdebug->leave_sub();
409 # --------------------------------------
413 sub restore_session {
414 $main::lxdebug->enter_sub();
418 my $cgi = $main::cgi;
419 $cgi ||= CGI->new('');
421 $session_id = $cgi->cookie($self->get_session_cookie_name());
422 $session_id =~ s|[^0-9a-f]||g;
424 $self->{SESSION} = { };
427 $main::lxdebug->leave_sub();
431 my ($dbh, $query, $sth, $cookie, $ref, $form);
435 $dbh = $self->dbconnect();
436 $query = qq|SELECT *, (mtime < (now() - '$self->{session_timeout}m'::interval)) AS is_expired FROM auth.session WHERE id = ?|;
438 $cookie = selectfirst_hashref_query($form, $dbh, $query, $session_id);
440 if (!$cookie || $cookie->{is_expired} || ($cookie->{ip_address} ne $ENV{REMOTE_ADDR})) {
441 $self->destroy_session();
442 $main::lxdebug->leave_sub();
443 return SESSION_EXPIRED;
446 $query = qq|SELECT sess_key, sess_value FROM auth.session_content WHERE session_id = ?|;
447 $sth = prepare_execute_query($form, $dbh, $query, $session_id);
449 while (my $ref = $sth->fetchrow_hashref()) {
450 $self->{SESSION}->{$ref->{sess_key}} = $ref->{sess_value};
451 $form->{$ref->{sess_key}} = $ref->{sess_value} if (!defined $form->{$ref->{sess_key}});
456 $main::lxdebug->leave_sub();
461 sub destroy_session {
462 $main::lxdebug->enter_sub();
467 my $dbh = $self->dbconnect();
469 do_query($main::form, $dbh, qq|DELETE FROM auth.session_content WHERE session_id = ?|, $session_id);
470 do_query($main::form, $dbh, qq|DELETE FROM auth.session WHERE id = ?|, $session_id);
475 $self->{SESSION} = { };
478 $main::lxdebug->leave_sub();
481 sub expire_sessions {
482 $main::lxdebug->enter_sub();
486 my $dbh = $self->dbconnect();
488 qq|DELETE FROM auth.session_content
492 WHERE (mtime < (now() - '$self->{session_timeout}m'::interval)))|;
494 do_query($main::form, $dbh, $query);
497 qq|DELETE FROM auth.session
498 WHERE (mtime < (now() - '$self->{session_timeout}m'::interval))|;
500 do_query($main::form, $dbh, $query);
504 $main::lxdebug->leave_sub();
507 sub _create_session_id {
508 $main::lxdebug->enter_sub();
510 my @secs = gettimeofday();
514 map { push @data, int(rand() * 255); } (1..32);
516 my $id = md5_hex(pack 'C*', @data);
518 $main::lxdebug->leave_sub();
523 sub create_or_refresh_session {
524 $main::lxdebug->enter_sub();
528 $session_id ||= $self->_create_session_id();
530 my ($form, $dbh, $query, $sth, $id);
533 $dbh = $self->dbconnect();
535 $query = qq|SELECT id FROM auth.session WHERE id = ?|;
537 ($id) = selectrow_query($form, $dbh, $query, $session_id);
540 do_query($form, $dbh, qq|UPDATE auth.session SET mtime = now() WHERE id = ?|, $session_id);
541 do_query($form, $dbh, qq|DELETE FROM auth.session_content WHERE session_id = ?|, $session_id);
544 do_query($form, $dbh, qq|INSERT INTO auth.session (id, ip_address, mtime) VALUES (?, ?, now())|, $session_id, $ENV{REMOTE_ADDR});
548 $query = qq|INSERT INTO auth.session_content (session_id, sess_key, sess_value) VALUES (?, ?, ?)|;
549 $sth = prepare_query($form, $dbh, $query);
551 foreach my $key (sort keys %{ $self->{SESSION} }) {
552 do_statement($form, $sth, $query, $session_id, $key, $self->{SESSION}->{$key});
558 $main::lxdebug->leave_sub();
561 sub set_session_value {
562 $main::lxdebug->enter_sub();
566 $self->{SESSION} ||= { };
568 while (2 <= scalar @_) {
572 $self->{SESSION}->{$key} = $value;
575 $main::lxdebug->leave_sub();
578 sub set_cookie_environment_variable {
580 $ENV{HTTP_COOKIE} = $self->get_session_cookie_name() . "=${session_id}";
583 sub get_session_cookie_name {
586 return $self->{cookie_name} || 'lx_office_erp_session_id';
593 sub session_tables_present {
594 $main::lxdebug->enter_sub();
597 my $dbh = $self->dbconnect(1);
600 $main::lxdebug->leave_sub();
607 WHERE (schemaname = 'auth')
608 AND (tablename IN ('session', 'session_content'))|;
610 my ($count) = selectrow_query($main::form, $dbh, $query);
612 $main::lxdebug->leave_sub();
617 # --------------------------------------
619 sub all_rights_full {
620 my $locale = $main::locale;
623 ["--master_data", $locale->text("Master Data")],
624 ["customer_vendor_edit", $locale->text("Create and edit customers and vendors")],
625 ["part_service_assembly_edit", $locale->text("Create and edit parts, services, assemblies")],
626 ["project_edit", $locale->text("Create and edit projects")],
627 ["license_edit", $locale->text("Manage license keys")],
628 ["--ar", $locale->text("AR")],
629 ["sales_quotation_edit", $locale->text("Create and edit sales quotations")],
630 ["sales_order_edit", $locale->text("Create and edit sales orders")],
631 ["sales_delivery_order_edit", $locale->text("Create and edit sales delivery orders")],
632 ["invoice_edit", $locale->text("Create and edit invoices and credit notes")],
633 ["dunning_edit", $locale->text("Create and edit dunnings")],
634 ["--ap", $locale->text("AP")],
635 ["request_quotation_edit", $locale->text("Create and edit RFQs")],
636 ["purchase_order_edit", $locale->text("Create and edit purchase orders")],
637 ["purchase_delivery_order_edit", $locale->text("Create and edit purchase delivery orders")],
638 ["vendor_invoice_edit", $locale->text("Create and edit vendor invoices")],
639 ["--general_ledger_cash", $locale->text("General ledger and cash")],
640 ["general_ledger", $locale->text("Transactions, AR transactions, AP transactions")],
641 ["datev_export", $locale->text("DATEV Export")],
642 ["cash", $locale->text("Receipt, payment, reconciliation")],
643 ["--reports", $locale->text('Reports')],
644 ["report", $locale->text('All reports')],
645 ["advance_turnover_tax_return", $locale->text('Advance turnover tax return')],
646 ["--others", $locale->text("Others")],
647 ["email_bcc", $locale->text("May set the BCC field when sending emails")],
648 ["config", $locale->text("Change Lx-Office installation settings (all menu entries beneath 'System')")],
655 return grep !/^--/, map { $_->[0] } all_rights_full();
659 $main::lxdebug->enter_sub();
663 my $form = $main::form;
665 my $dbh = $self->dbconnect();
667 my $query = 'SELECT * FROM auth."group"';
668 my $sth = prepare_execute_query($form, $dbh, $query);
672 while ($row = $sth->fetchrow_hashref()) {
673 $groups->{$row->{id}} = $row;
677 $query = 'SELECT * FROM auth.user_group WHERE group_id = ?';
678 $sth = prepare_query($form, $dbh, $query);
680 foreach $group (values %{$groups}) {
681 $group->{members} = [];
683 do_statement($form, $sth, $query, $group->{id});
685 while ($row = $sth->fetchrow_hashref()) {
686 push @{$group->{members}}, $row->{user_id};
691 $query = 'SELECT * FROM auth.group_rights WHERE group_id = ?';
692 $sth = prepare_query($form, $dbh, $query);
694 foreach $group (values %{$groups}) {
695 $group->{rights} = {};
697 do_statement($form, $sth, $query, $group->{id});
699 while ($row = $sth->fetchrow_hashref()) {
700 $group->{rights}->{$row->{right}} |= $row->{granted};
703 map { $group->{rights}->{$_} = 0 if (!defined $group->{rights}->{$_}); } all_rights();
707 $main::lxdebug->leave_sub();
713 $main::lxdebug->enter_sub();
718 my $form = $main::form;
719 my $dbh = $self->dbconnect();
721 my ($query, $sth, $row, $rights);
724 ($group->{id}) = selectrow_query($form, $dbh, qq|SELECT nextval('auth.group_id_seq')|);
726 $query = qq|INSERT INTO auth."group" (id, name, description) VALUES (?, '', '')|;
727 do_query($form, $dbh, $query, $group->{id});
730 do_query($form, $dbh, qq|UPDATE auth."group" SET name = ?, description = ? WHERE id = ?|, map { $group->{$_} } qw(name description id));
732 do_query($form, $dbh, qq|DELETE FROM auth.user_group WHERE group_id = ?|, $group->{id});
734 $query = qq|INSERT INTO auth.user_group (user_id, group_id) VALUES (?, ?)|;
735 $sth = prepare_query($form, $dbh, $query);
737 foreach my $user_id (@{ $group->{members} }) {
738 do_statement($form, $sth, $query, $user_id, $group->{id});
742 do_query($form, $dbh, qq|DELETE FROM auth.group_rights WHERE group_id = ?|, $group->{id});
744 $query = qq|INSERT INTO auth.group_rights (group_id, "right", granted) VALUES (?, ?, ?)|;
745 $sth = prepare_query($form, $dbh, $query);
747 foreach my $right (keys %{ $group->{rights} }) {
748 do_statement($form, $sth, $query, $group->{id}, $right, $group->{rights}->{$right} ? 't' : 'f');
754 $main::lxdebug->leave_sub();
758 $main::lxdebug->enter_sub();
763 my $form = $main::from;
765 my $dbh = $self->dbconnect();
767 do_query($form, $dbh, qq|DELETE FROM auth.user_group WHERE group_id = ?|, $id);
768 do_query($form, $dbh, qq|DELETE FROM auth.group_rights WHERE group_id = ?|, $id);
769 do_query($form, $dbh, qq|DELETE FROM auth."group" WHERE id = ?|, $id);
773 $main::lxdebug->leave_sub();
776 sub evaluate_rights_ary {
777 $main::lxdebug->enter_sub(2);
784 foreach my $el (@{$ary}) {
785 if (ref $el eq "ARRAY") {
786 if ($action eq '|') {
787 $value |= evaluate_rights_ary($el);
789 $value &= evaluate_rights_ary($el);
792 } elsif (($el eq '&') || ($el eq '|')) {
795 } elsif ($action eq '|') {
804 $main::lxdebug->enter_sub(2);
809 sub _parse_rights_string {
810 $main::lxdebug->enter_sub(2);
820 push @stack, $cur_ary;
822 while ($access =~ m/^([a-z_0-9]+|\||\&|\(|\)|\s+)/) {
824 substr($access, 0, length $1) = "";
826 next if ($token =~ /\s/);
829 my $new_cur_ary = [];
830 push @stack, $new_cur_ary;
831 push @{$cur_ary}, $new_cur_ary;
832 $cur_ary = $new_cur_ary;
834 } elsif ($token eq ")") {
838 $main::lxdebug->enter_sub(2);
842 $cur_ary = $stack[-1];
844 } elsif (($token eq "|") || ($token eq "&")) {
845 push @{$cur_ary}, $token;
848 push @{$cur_ary}, $self->{RIGHTS}->{$login}->{$token} * 1;
852 my $result = ($access || (1 < scalar @stack)) ? 0 : evaluate_rights_ary($stack[0]);
854 $main::lxdebug->enter_sub(2);
860 $main::lxdebug->enter_sub(2);
867 $self->{FULL_RIGHTS} ||= { };
868 $self->{FULL_RIGHTS}->{$login} ||= { };
870 if (!defined $self->{FULL_RIGHTS}->{$login}->{$right}) {
871 $self->{RIGHTS} ||= { };
872 $self->{RIGHTS}->{$login} ||= $self->load_rights_for_user($login);
874 $self->{FULL_RIGHTS}->{$login}->{$right} = $self->_parse_rights_string($login, $right);
877 my $granted = $self->{FULL_RIGHTS}->{$login}->{$right};
878 $granted = $default if (!defined $granted);
880 $main::lxdebug->leave_sub(2);
886 $main::lxdebug->enter_sub(2);
890 my $dont_abort = shift;
892 my $form = $main::form;
894 if ($self->check_right($form->{login}, $right)) {
895 $main::lxdebug->leave_sub(2);
900 delete $form->{title};
901 $form->show_generic_error($main::locale->text("You do not have the permissions to access this function."));
904 $main::lxdebug->leave_sub(2);
909 sub load_rights_for_user {
910 $main::lxdebug->enter_sub();
915 my $form = $main::form;
916 my $dbh = $self->dbconnect();
918 my ($query, $sth, $row, $rights);
923 qq|SELECT gr."right", gr.granted
924 FROM auth.group_rights gr
927 FROM auth.user_group ug
928 LEFT JOIN auth."user" u ON (ug.user_id = u.id)
931 $sth = prepare_execute_query($form, $dbh, $query, $login);
933 while ($row = $sth->fetchrow_hashref()) {
934 $rights->{$row->{right}} |= $row->{granted};
938 map({ $rights->{$_} = 0 unless (defined $rights->{$_}); } SL::Auth::all_rights());
940 $main::lxdebug->leave_sub();