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 ["--warehouse_management", $locale->text("Warehouse management")],
640 ["warehouse_contents", $locale->text("View warehouse content")],
641 ["warehouse_management", $locale->text("Warehouse management")],
642 ["--general_ledger_cash", $locale->text("General ledger and cash")],
643 ["general_ledger", $locale->text("Transactions, AR transactions, AP transactions")],
644 ["datev_export", $locale->text("DATEV Export")],
645 ["cash", $locale->text("Receipt, payment, reconciliation")],
646 ["--reports", $locale->text('Reports')],
647 ["report", $locale->text('All reports')],
648 ["advance_turnover_tax_return", $locale->text('Advance turnover tax return')],
649 ["--others", $locale->text("Others")],
650 ["email_bcc", $locale->text("May set the BCC field when sending emails")],
651 ["config", $locale->text("Change Lx-Office installation settings (all menu entries beneath 'System')")],
658 return grep !/^--/, map { $_->[0] } all_rights_full();
662 $main::lxdebug->enter_sub();
666 my $form = $main::form;
668 my $dbh = $self->dbconnect();
670 my $query = 'SELECT * FROM auth."group"';
671 my $sth = prepare_execute_query($form, $dbh, $query);
675 while ($row = $sth->fetchrow_hashref()) {
676 $groups->{$row->{id}} = $row;
680 $query = 'SELECT * FROM auth.user_group WHERE group_id = ?';
681 $sth = prepare_query($form, $dbh, $query);
683 foreach $group (values %{$groups}) {
684 $group->{members} = [];
686 do_statement($form, $sth, $query, $group->{id});
688 while ($row = $sth->fetchrow_hashref()) {
689 push @{$group->{members}}, $row->{user_id};
694 $query = 'SELECT * FROM auth.group_rights WHERE group_id = ?';
695 $sth = prepare_query($form, $dbh, $query);
697 foreach $group (values %{$groups}) {
698 $group->{rights} = {};
700 do_statement($form, $sth, $query, $group->{id});
702 while ($row = $sth->fetchrow_hashref()) {
703 $group->{rights}->{$row->{right}} |= $row->{granted};
706 map { $group->{rights}->{$_} = 0 if (!defined $group->{rights}->{$_}); } all_rights();
710 $main::lxdebug->leave_sub();
716 $main::lxdebug->enter_sub();
721 my $form = $main::form;
722 my $dbh = $self->dbconnect();
724 my ($query, $sth, $row, $rights);
727 ($group->{id}) = selectrow_query($form, $dbh, qq|SELECT nextval('auth.group_id_seq')|);
729 $query = qq|INSERT INTO auth."group" (id, name, description) VALUES (?, '', '')|;
730 do_query($form, $dbh, $query, $group->{id});
733 do_query($form, $dbh, qq|UPDATE auth."group" SET name = ?, description = ? WHERE id = ?|, map { $group->{$_} } qw(name description id));
735 do_query($form, $dbh, qq|DELETE FROM auth.user_group WHERE group_id = ?|, $group->{id});
737 $query = qq|INSERT INTO auth.user_group (user_id, group_id) VALUES (?, ?)|;
738 $sth = prepare_query($form, $dbh, $query);
740 foreach my $user_id (@{ $group->{members} }) {
741 do_statement($form, $sth, $query, $user_id, $group->{id});
745 do_query($form, $dbh, qq|DELETE FROM auth.group_rights WHERE group_id = ?|, $group->{id});
747 $query = qq|INSERT INTO auth.group_rights (group_id, "right", granted) VALUES (?, ?, ?)|;
748 $sth = prepare_query($form, $dbh, $query);
750 foreach my $right (keys %{ $group->{rights} }) {
751 do_statement($form, $sth, $query, $group->{id}, $right, $group->{rights}->{$right} ? 't' : 'f');
757 $main::lxdebug->leave_sub();
761 $main::lxdebug->enter_sub();
766 my $form = $main::from;
768 my $dbh = $self->dbconnect();
770 do_query($form, $dbh, qq|DELETE FROM auth.user_group WHERE group_id = ?|, $id);
771 do_query($form, $dbh, qq|DELETE FROM auth.group_rights WHERE group_id = ?|, $id);
772 do_query($form, $dbh, qq|DELETE FROM auth."group" WHERE id = ?|, $id);
776 $main::lxdebug->leave_sub();
779 sub evaluate_rights_ary {
780 $main::lxdebug->enter_sub(2);
787 foreach my $el (@{$ary}) {
788 if (ref $el eq "ARRAY") {
789 if ($action eq '|') {
790 $value |= evaluate_rights_ary($el);
792 $value &= evaluate_rights_ary($el);
795 } elsif (($el eq '&') || ($el eq '|')) {
798 } elsif ($action eq '|') {
807 $main::lxdebug->enter_sub(2);
812 sub _parse_rights_string {
813 $main::lxdebug->enter_sub(2);
823 push @stack, $cur_ary;
825 while ($access =~ m/^([a-z_0-9]+|\||\&|\(|\)|\s+)/) {
827 substr($access, 0, length $1) = "";
829 next if ($token =~ /\s/);
832 my $new_cur_ary = [];
833 push @stack, $new_cur_ary;
834 push @{$cur_ary}, $new_cur_ary;
835 $cur_ary = $new_cur_ary;
837 } elsif ($token eq ")") {
841 $main::lxdebug->enter_sub(2);
845 $cur_ary = $stack[-1];
847 } elsif (($token eq "|") || ($token eq "&")) {
848 push @{$cur_ary}, $token;
851 push @{$cur_ary}, $self->{RIGHTS}->{$login}->{$token} * 1;
855 my $result = ($access || (1 < scalar @stack)) ? 0 : evaluate_rights_ary($stack[0]);
857 $main::lxdebug->enter_sub(2);
863 $main::lxdebug->enter_sub(2);
870 $self->{FULL_RIGHTS} ||= { };
871 $self->{FULL_RIGHTS}->{$login} ||= { };
873 if (!defined $self->{FULL_RIGHTS}->{$login}->{$right}) {
874 $self->{RIGHTS} ||= { };
875 $self->{RIGHTS}->{$login} ||= $self->load_rights_for_user($login);
877 $self->{FULL_RIGHTS}->{$login}->{$right} = $self->_parse_rights_string($login, $right);
880 my $granted = $self->{FULL_RIGHTS}->{$login}->{$right};
881 $granted = $default if (!defined $granted);
883 $main::lxdebug->leave_sub(2);
889 $main::lxdebug->enter_sub(2);
893 my $dont_abort = shift;
895 my $form = $main::form;
897 if ($self->check_right($form->{login}, $right)) {
898 $main::lxdebug->leave_sub(2);
903 delete $form->{title};
904 $form->show_generic_error($main::locale->text("You do not have the permissions to access this function."));
907 $main::lxdebug->leave_sub(2);
912 sub load_rights_for_user {
913 $main::lxdebug->enter_sub();
918 my $form = $main::form;
919 my $dbh = $self->dbconnect();
921 my ($query, $sth, $row, $rights);
926 qq|SELECT gr."right", gr.granted
927 FROM auth.group_rights gr
930 FROM auth.user_group ug
931 LEFT JOIN auth."user" u ON (ug.user_id = u.id)
934 $sth = prepare_execute_query($form, $dbh, $query, $login);
936 while ($row = $sth->fetchrow_hashref()) {
937 $rights->{$row->{right}} |= $row->{granted};
941 map({ $rights->{$_} = 0 unless (defined $rights->{$_}); } SL::Auth::all_rights());
943 $main::lxdebug->leave_sub();