use Digest::MD5 qw(md5_hex);
use IO::File;
use Time::HiRes qw(gettimeofday);
-use List::MoreUtils qw(uniq);
+use List::MoreUtils qw(any uniq);
use YAML;
use Regexp::IPv6 qw($IPv6_re);
delete $self->{column_information};
}
- $self->{authenticator}->reset;
+ $_->reset for @{ $self->{authenticators} };
$self->client(undef);
}
$self->{DB_config} = $::lx_office_conf{'authentication/database'};
}
- if ($self->{module} eq 'DB') {
- $self->{authenticator} = SL::Auth::DB->new($self);
+ $self->{authenticators} = [];
+ $self->{module} ||= 'DB';
+ $self->{module} =~ s{^ +| +$}{}g;
- } elsif ($self->{module} eq 'LDAP') {
- $self->{authenticator} = SL::Auth::LDAP->new($::lx_office_conf{'authentication/ldap'});
- }
+ foreach my $module (split m{ +}, $self->{module}) {
+ my $config_name;
+ ($module, $config_name) = split m{:}, $module, 2;
+ $config_name ||= $module eq 'DB' ? 'database' : lc($module);
+ my $config = $::lx_office_conf{'authentication/' . $config_name};
- if (!$self->{authenticator}) {
- my $locale = Locale->new('en');
- $self->mini_error($locale->text('No or an unknown authenticantion module specified in "config/kivitendo.conf".'));
+ if (!$config) {
+ my $locale = Locale->new('en');
+ $self->mini_error($locale->text('Missing configuration section "authentication/#1" in "config/kivitendo.conf".', $config_name));
+ }
+
+ if ($module eq 'DB') {
+ push @{ $self->{authenticators} }, SL::Auth::DB->new($self);
+
+ } elsif ($module eq 'LDAP') {
+ push @{ $self->{authenticators} }, SL::Auth::LDAP->new($config);
+
+ } else {
+ my $locale = Locale->new('en');
+ $self->mini_error($locale->text('Unknown authenticantion module #1 specified in "config/kivitendo.conf".', $module));
+ }
}
my $cfg = $self->{DB_config};
$self->mini_error($locale->text('config/kivitendo.conf: Missing parameters in "authentication/database". Required parameters are "host", "db" and "user".'));
}
- $self->{authenticator}->verify_config();
+ $_->verify_config for @{ $self->{authenticators} };
$self->{session_timeout} *= 1;
$self->{session_timeout} = 8 * 60 if (!$self->{session_timeout});
return ERR_PASSWORD;
}
- my $result = $login ? $self->{authenticator}->authenticate($login, $password) : ERR_USER;
+ my $result = ERR_USER;
+ if ($login) {
+ foreach my $authenticator (@{ $self->{authenticators} }) {
+ $result = $authenticator->authenticate($login, $password);
+ last if $result == OK;
+ }
+ }
+
$self->set_session_value(SESSION_KEY_USER_AUTH() => $result, login => $login, client_id => $self->client->{id});
return $result;
}
sub can_change_password {
my $self = shift;
- return $self->{authenticator}->can_change_password();
+ return any { $_->can_change_password } @{ $self->{authenticators} };
}
sub change_password {
my ($self, $login, $new_password) = @_;
- my $result = $self->{authenticator}->change_password($login, $new_password);
+ my $overall_result = OK;
- return $result;
+ foreach my $authenticator (@{ $self->{authenticators} }) {
+ next unless $authenticator->can_change_password;
+
+ my $result = $authenticator->change_password($login, $new_password);
+ $overall_result = $result if $result != OK;
+ }
+
+ return $overall_result;
}
sub read_all_users {
return $self->{ldap} if $self->{ldap};
- my $port = $cfg->{port} || 389;
- $self->{ldap} = Net::LDAP->new($cfg->{host}, 'port' => $port);
+ my $port = $cfg->{port} || 389;
+ my $ldap = Net::LDAP->new($cfg->{host}, port => $port, timeout => $cfg->{timeout} || 10);
- if (!$self->{ldap}) {
- $main::form->error($main::locale->text('The LDAP server "#1:#2" is unreachable. Please check config/kivitendo.conf.', $cfg->{host}, $port));
+ if (!$ldap) {
+ $::lxdebug->warn($main::locale->text('The LDAP server "#1:#2" is unreachable. Please check config/kivitendo.conf.', $cfg->{host}, $port));
+ return undef;
}
if ($cfg->{tls}) {
- my $mesg = $self->{ldap}->start_tls('verify' => 'none');
+ my $mesg = $ldap->start_tls(verify => $cfg->{verify} // 'require');
if ($mesg->is_error()) {
- $main::form->error($main::locale->text('The connection to the LDAP server cannot be encrypted (SSL/TLS startup failure). Please check config/kivitendo.conf.'));
+ $::lxdebug->warn($main::locale->text('The connection to the LDAP server cannot be encrypted (SSL/TLS startup failure). Please check config/kivitendo.conf.'));
+ return undef;
}
}
if ($cfg->{bind_dn}) {
- my $mesg = $self->{ldap}->bind($cfg->{bind_dn}, 'password' => $cfg->{bind_password});
+ my $mesg = $ldap->bind($cfg->{bind_dn}, 'password' => $cfg->{bind_password});
if ($mesg->is_error()) {
- $main::form->error($main::locale->text('Binding to the LDAP server as "#1" failed. Please check config/kivitendo.conf.', $cfg->{bind_dn}));
+ $::lxdebug->warn($main::locale->text('Binding to the LDAP server as "#1" failed. Please check config/kivitendo.conf.', $cfg->{bind_dn}));
+ return undef;
}
}
+ $self->{ldap} = $ldap;
+
return $self->{ldap};
}
# interface.
admin_password = admin123
-# Which module to use for authentication. Valid values are 'DB' and
-# 'LDAP'. If 'LDAP' is used then users cannot change their password
-# via kivitendo.
+# Which modules to use for authentication. Valid values are 'DB' and
+# 'LDAP'. You can use multiple modules separated by spaces.
+#
+# Multiple LDAP modules with different configurations can be used by
+# postfixing 'LDAP' with the name of the configuration section to use:
+# 'LDAP:ldap_fallback' would use the data from
+# '[authentication/ldap_fallback]'. The name defaults to 'ldap' if it
+# isn't given.
+#
+# Note that the LDAP module doesn't support changing the password.
module = DB
# The cookie name can be changed if desired.
# specified.
#
# tls: Activate encryption via TLS
+# verify: If 'tls' is used, how to verify the server's certificate.
+# Can be one of 'require' or 'none'.
# attribute: Name of the LDAP attribute containing the user's login name
# base_dn: Base DN the LDAP searches start from
# filter: An optional LDAP filter specification. The string '<%login%>'
# If searching the LDAP tree requires user credentials
# (e.g. ActiveDirectory) then these two parameters specify
# the user name and password to use.
+# timeout: Timeout when connecting to the server in seconds.
+#
+# You can specify a fallback LDAP server to use in case the main one
+# isn't reachable by duplicating this whole section as
+# "[authentication/ldap_fallback]".
+#
host = localhost
port = 389
tls = 0
filter =
bind_dn =
bind_password =
+timeout = 10
+verify = require
[system]
# Set language for login and admin forms. Currently "de" (German)
'Missing Method!' => 'Fehlender Voranmeldungszeitraum',
'Missing Tax Authoritys Preferences' => 'Fehlende Angaben zum Finanzamt!',
'Missing amount' => 'Fehlbetrag',
+ 'Missing configuration section "authentication/#1" in "config/kivitendo.conf".' => 'Fehlender Konfigurationsabschnitt "authentication/#1" in "config/kivitendo.conf".',
'Missing parameter #1 in call to sub #2.' => 'Fehlender Parameter \'#1\' in Funktionsaufruf \'#2\'.',
'Missing parameter (at least one of #1) in call to sub #2.' => 'Fehlernder Parameter (mindestens einer aus \'#1\') in Funktionsaufruf \'#2\'.',
'Missing parameter for WebDAV file copy' => 'Fehlender Parameter für WebDAV Datei kopieren',
'No groups have been created yet.' => 'Es wurden noch keine Gruppen angelegt.',
'No internal phone extensions have been configured yet.' => 'Es wurden noch keine internen Durchwahlen konfiguriert.',
'No invoices have been selected.' => 'Es wurden keine Rechnungen ausgewählt.',
- 'No or an unknown authenticantion module specified in "config/kivitendo.conf".' => 'Es wurde kein oder ein unbekanntes Authentifizierungsmodul in "config/kivitendo.conf" angegeben.',
'No part was selected.' => 'Es wurde kein Artikel ausgewählt',
'No payment term has been created yet.' => 'Es wurden noch keine Zahlungsbedingungen angelegt.',
'No picture has been uploaded' => 'Es wurde kein Bild hochgeladen',
'Units that have already been used (e.g. for parts and services or in invoices or warehouse transactions) cannot be changed.' => 'Einheiten, die bereits in Benutzung sind (z.B. bei einer Warendefinition, einer Rechnung oder bei einer Lagerbuchung) können nachträglich nicht mehr verändert werden.',
'Unknown Category' => 'Unbekannte Kategorie',
'Unknown Link' => 'Unbekannte Verknüpfung',
+ 'Unknown authenticantion module #1 specified in "config/kivitendo.conf".' => 'Unbekanntes Authentifizierungsmodul #1 angegeben in "config/kivitendo.conf".',
'Unknown control fields: #1' => 'Unbekannte Kontrollfelder: #1',
'Unknown dependency \'%s\'.' => 'Unbekannte Abhängigkeit \'%s\'.',
'Unknown module: #1' => 'Unbekanntes Modul #1',