Revision 7c5c23fb
Von Moritz Bunkus vor 7 Tagen hinzugefügt
SL/Auth.pm | ||
---|---|---|
12 | 12 |
use SL::Auth::ColumnInformation; |
13 | 13 |
use SL::Auth::Constants qw(:all); |
14 | 14 |
use SL::Auth::DB; |
15 |
use SL::Auth::HTTPHeaders; |
|
15 | 16 |
use SL::Auth::LDAP; |
16 | 17 |
use SL::Auth::Password; |
17 | 18 |
use SL::Auth::SessionValue; |
... | ... | |
152 | 153 |
foreach my $module (split m{ +}, $self->{module}) { |
153 | 154 |
my $config_name; |
154 | 155 |
($module, $config_name) = split m{:}, $module, 2; |
155 |
$config_name ||= $module eq 'DB' ? 'database' : lc($module); |
|
156 |
$config_name ||= $module eq 'DB' ? 'database' : $module eq 'HTTPHeaders' ? 'http_headers' : lc($module);
|
|
156 | 157 |
my $config = $::lx_office_conf{'authentication/' . $config_name}; |
157 | 158 |
|
158 | 159 |
if (!$config) { |
... | ... | |
166 | 167 |
} elsif ($module eq 'LDAP') { |
167 | 168 |
push @{ $self->{authenticators} }, SL::Auth::LDAP->new($config); |
168 | 169 |
|
170 |
} elsif ($module eq 'HTTPHeaders') { |
|
171 |
push @{ $self->{authenticators} }, SL::Auth::HTTPHeaders->new($config); |
|
172 |
|
|
169 | 173 |
} else { |
170 | 174 |
my $locale = Locale->new('en'); |
171 | 175 |
$self->mini_error($locale->text('Unknown authenticantion module #1 specified in "config/kivitendo.conf".', $module)); |
... | ... | |
228 | 232 |
return $result; |
229 | 233 |
} |
230 | 234 |
|
235 |
sub set_session_authenticated { |
|
236 |
my ($self, $login, $result) = @_; |
|
237 |
|
|
238 |
$self->set_session_value(SESSION_KEY_USER_AUTH() => $result, login => $login, client_id => $self->client->{id}); |
|
239 |
} |
|
240 |
|
|
231 | 241 |
sub authenticate { |
232 | 242 |
my ($self, $login, $password) = @_; |
233 | 243 |
|
... | ... | |
252 | 262 |
} |
253 | 263 |
} |
254 | 264 |
|
255 |
$self->set_session_value(SESSION_KEY_USER_AUTH() => $result, login => $login, client_id => $self->client->{id}); |
|
265 |
$self->set_session_authenticated($login, $result); |
|
266 |
|
|
256 | 267 |
return $result; |
257 | 268 |
} |
258 | 269 |
|
SL/Auth/HTTPHeaders.pm | ||
---|---|---|
1 |
package SL::Auth::HTTPHeaders; |
|
2 |
|
|
3 |
use List::MoreUtils qw(any); |
|
4 |
|
|
5 |
use SL::Auth::Constants qw(:all); |
|
6 |
|
|
7 |
use strict; |
|
8 |
|
|
9 |
my @required_config_options = qw(secret_header secret user_header client_id_header); |
|
10 |
|
|
11 |
sub new { |
|
12 |
my $type = shift; |
|
13 |
my $self = {}; |
|
14 |
$self->{config} = shift; |
|
15 |
|
|
16 |
bless $self, $type; |
|
17 |
|
|
18 |
return $self; |
|
19 |
} |
|
20 |
|
|
21 |
sub reset { |
|
22 |
my ($self) = @_; |
|
23 |
} |
|
24 |
|
|
25 |
sub _env_var_for_header { |
|
26 |
my ($header) = @_; |
|
27 |
|
|
28 |
$header =~ s{-}{_}g; |
|
29 |
return $ENV{'HTTP_' . uc($header)}; |
|
30 |
} |
|
31 |
|
|
32 |
sub _authenticate { |
|
33 |
my ($self, $type) = @_; |
|
34 |
|
|
35 |
my $secret = _env_var_for_header($self->{config}->{secret_header}) // ''; |
|
36 |
if ($secret ne $self->{config}->{secret}) { |
|
37 |
$::lxdebug->message(LXDebug->DEBUG2(), "HTTPHeaders ${type}: bad secret sent by upstream server: $secret"); |
|
38 |
return (ERR_BACKEND); |
|
39 |
} |
|
40 |
|
|
41 |
my $client_id = _env_var_for_header($self->{config}->{client_id_header}); |
|
42 |
if (!$client_id) { |
|
43 |
$::lxdebug->message(LXDebug->DEBUG2(), "HTTPHeaders ${type}: no client ID header found"); |
|
44 |
return (ERR_PASSWORD); |
|
45 |
} |
|
46 |
|
|
47 |
# $::auth->set_client(); |
|
48 |
|
|
49 |
my $user = _env_var_for_header($self->{config}->{user_header}); |
|
50 |
if (!$user) { |
|
51 |
$::lxdebug->message(LXDebug->DEBUG2(), "HTTPHeaders ${type}: no user name header found"); |
|
52 |
return (ERR_PASSWORD); |
|
53 |
} |
|
54 |
|
|
55 |
$::lxdebug->message(LXDebug->DEBUG2(), "HTTPHeaders ${type}: OK client $client_id user $user"); |
|
56 |
|
|
57 |
return (OK, $client_id, $user); |
|
58 |
} |
|
59 |
|
|
60 |
sub authenticate { |
|
61 |
my ($self) = @_; |
|
62 |
|
|
63 |
my ($status, $client, $login) = $self->_authenticate('authenticate'); |
|
64 |
|
|
65 |
return $status; |
|
66 |
} |
|
67 |
|
|
68 |
sub can_change_password { |
|
69 |
return 0; |
|
70 |
} |
|
71 |
|
|
72 |
sub requires_cleartext_password { |
|
73 |
return 0; |
|
74 |
} |
|
75 |
|
|
76 |
sub change_password { |
|
77 |
return ERR_BACKEND; |
|
78 |
} |
|
79 |
|
|
80 |
sub verify_config { |
|
81 |
my $self = shift; |
|
82 |
my $cfg = $self->{config}; |
|
83 |
|
|
84 |
if (!$cfg) { |
|
85 |
die 'config/kivitendo.conf: Key "authentication/http_headers" is missing.'; |
|
86 |
} |
|
87 |
|
|
88 |
foreach (@required_config_options) { |
|
89 |
next if $cfg->{$_}; |
|
90 |
die 'config/kivitendo.conf: Missing parameter in "authentication/http_headers": ' . $_; |
|
91 |
} |
|
92 |
} |
|
93 |
|
|
94 |
1; |
SL/Dispatcher/AuthHandler/Base.pm | ||
---|---|---|
1 |
package SL::Dispatcher::AuthHandler::Base; |
|
2 |
|
|
3 |
use strict; |
|
4 |
use parent qw(Rose::Object); |
|
5 |
|
|
6 |
use Encode (); |
|
7 |
use MIME::Base64 (); |
|
8 |
|
|
9 |
use SL::Layout::Dispatcher; |
|
10 |
|
|
11 |
sub _env_var_for_header { |
|
12 |
my ($header) = @_; |
|
13 |
|
|
14 |
$header =~ s{-}{_}g; |
|
15 |
return $ENV{'HTTP_' . uc($header)}; |
|
16 |
} |
|
17 |
|
|
18 |
sub _parse_http_basic_auth { |
|
19 |
my ($self) = @_; |
|
20 |
|
|
21 |
my $cfg = $::lx_office_conf{'authentication/http_basic'}; |
|
22 |
|
|
23 |
return unless $cfg && $cfg->{enabled}; |
|
24 |
|
|
25 |
# See RFC 7617. |
|
26 |
|
|
27 |
# Requires that the server passes the 'Authorization' header as the |
|
28 |
# environment variable 'HTTP_AUTHORIZATION'. Example code for |
|
29 |
# Apache: |
|
30 |
|
|
31 |
# SetEnvIf Authorization "(.*)" HTTP_AUTHORIZATION=$1 |
|
32 |
|
|
33 |
my $data = _env_var_for_header('Authorization'); |
|
34 |
|
|
35 |
return unless ($data // '') =~ m{^basic +(.+)}i; |
|
36 |
|
|
37 |
$data = Encode::decode('utf-8', MIME::Base64::decode($1)); |
|
38 |
|
|
39 |
return unless $data =~ m{(.+?):(.+)}; |
|
40 |
|
|
41 |
return ($1, $2); |
|
42 |
} |
|
43 |
|
|
44 |
sub _parse_http_headers_auth { |
|
45 |
my ($self) = @_; |
|
46 |
|
|
47 |
my $cfg = $::lx_office_conf{'authentication/http_headers'}; |
|
48 |
|
|
49 |
return unless $cfg && ($::lx_office_conf{'authentication'}->{module} =~ m{HTTPHeaders}); |
|
50 |
|
|
51 |
foreach (qw(secret_header secret user_header client_id_header)) { |
|
52 |
next if $cfg->{$_}; |
|
53 |
die 'config/kivitendo.conf: Missing parameter in "authentication/http_headers": ' . $_; |
|
54 |
} |
|
55 |
|
|
56 |
my $secret = _env_var_for_header($cfg->{secret_header}) // ''; |
|
57 |
if ($secret ne $cfg->{secret}) { |
|
58 |
$::lxdebug->message(LXDebug->DEBUG2(), "_parse_http_headers_auth: bad secret sent by upstream server: $secret"); |
|
59 |
return; |
|
60 |
} |
|
61 |
|
|
62 |
my $client_id = _env_var_for_header($cfg->{client_id_header}); |
|
63 |
if (!$client_id) { |
|
64 |
$::lxdebug->message(LXDebug->DEBUG2(), "_parse_http_headers_auth: no client ID header found"); |
|
65 |
return; |
|
66 |
} |
|
67 |
|
|
68 |
# $::auth->set_client(); |
|
69 |
|
|
70 |
my $user = _env_var_for_header($cfg->{user_header}); |
|
71 |
if (!$user) { |
|
72 |
$::lxdebug->message(LXDebug->DEBUG2(), "_parse_http_headers_auth: no user name header found"); |
|
73 |
return; |
|
74 |
} |
|
75 |
|
|
76 |
$::lxdebug->message(LXDebug->DEBUG2(), "_parse_http_headers_auth: OK client $client_id user $user"); |
|
77 |
|
|
78 |
return ($client_id, $user); |
|
79 |
} |
|
80 |
|
|
81 |
1; |
SL/Dispatcher/AuthHandler/None.pm | ||
---|---|---|
1 | 1 |
package SL::Dispatcher::AuthHandler::None; |
2 | 2 |
|
3 | 3 |
use strict; |
4 |
use parent qw(SL::Dispatcher::AuthHandler::Base); |
|
4 | 5 |
|
5 |
use parent qw(Rose::Object);
|
|
6 |
use SL::Auth::Constants;
|
|
6 | 7 |
|
7 | 8 |
sub handle { |
8 |
%::myconfig = User->get_default_myconfig; |
|
9 |
my ($self) = @_; |
|
10 |
|
|
11 |
|
|
12 |
my ($http_auth_login, $http_auth_password) = $self->_parse_http_basic_auth; |
|
13 |
my ($http_headers_client, $http_headers_login) = $self->_parse_http_headers_auth; |
|
14 |
|
|
15 |
my $client_id = $http_headers_client // $::auth->get_default_client_id; |
|
16 |
my $login = $http_headers_login // $http_auth_login; |
|
17 |
|
|
18 |
if ($client_id && $login) { |
|
19 |
$::auth->set_client($client_id); |
|
20 |
%::myconfig = User->get_default_myconfig($::auth->read_user(login => $login)); |
|
21 |
|
|
22 |
$::auth->create_or_refresh_session; |
|
23 |
$::auth->set_session_value('client_id', $client_id); |
|
24 |
$::auth->set_session_value('login', $login); |
|
25 |
|
|
26 |
$::auth->set_session_authenticated($login, SL::Auth::Constants::SESSION_OK()); |
|
27 |
|
|
28 |
} else { |
|
29 |
%::myconfig = User->get_default_myconfig; |
|
30 |
} |
|
31 |
|
|
9 | 32 |
return 1; |
10 | 33 |
} |
11 | 34 |
|
SL/Dispatcher/AuthHandler/User.pm | ||
---|---|---|
1 | 1 |
package SL::Dispatcher::AuthHandler::User; |
2 | 2 |
|
3 | 3 |
use strict; |
4 |
use parent qw(Rose::Object); |
|
5 |
|
|
6 |
use Encode (); |
|
7 |
use MIME::Base64 (); |
|
4 |
use parent qw(SL::Dispatcher::AuthHandler::Base); |
|
8 | 5 |
|
9 | 6 |
use SL::Helper::UserPreferences::DisplayPreferences; |
10 | 7 |
use SL::Layout::Dispatcher; |
... | ... | |
12 | 9 |
sub handle { |
13 | 10 |
my ($self, %param) = @_; |
14 | 11 |
|
15 |
my ($http_auth_login, $http_auth_password) = $self->_parse_http_basic_auth; |
|
12 |
my ($http_auth_login, $http_auth_password) = $self->_parse_http_basic_auth; |
|
13 |
my ($http_headers_client, $http_headers_login) = $self->_parse_http_headers_auth; |
|
16 | 14 |
|
17 |
my $login = $::form->{'{AUTH}login'} // $http_auth_login // $::auth->get_session_value('login'); |
|
15 |
my $login = $::form->{'{AUTH}login'} // $http_auth_login // $http_headers_login // $::auth->get_session_value('login');
|
|
18 | 16 |
|
19 | 17 |
return $self->_error(%param) if !defined $login; |
20 | 18 |
|
21 |
my $client_id = $::form->{'{AUTH}client_id'} // $::auth->get_session_value('client_id') // $::auth->get_default_client_id; |
|
19 |
my $client_id = $::form->{'{AUTH}client_id'} // $http_headers_client // $::auth->get_session_value('client_id') // $::auth->get_default_client_id;
|
|
22 | 20 |
|
23 | 21 |
return $self->_error(%param) if !$client_id || !$::auth->set_client($client_id); |
24 | 22 |
|
... | ... | |
37 | 35 |
: SL::Layout::Dispatcher->new(style => $::myconfig{menustyle}); |
38 | 36 |
|
39 | 37 |
my $ok = $::auth->is_api_token_cookie_valid; |
38 |
$ok ||= $http_headers_login && (SL::Auth::OK() == $::auth->authenticate($::myconfig{login}, \'dummy!')); |
|
40 | 39 |
$ok ||= $::form->{'{AUTH}login'} && (SL::Auth::OK() == $::auth->authenticate($::myconfig{login}, $::form->{'{AUTH}password'})); |
41 | 40 |
$ok ||= !$::form->{'{AUTH}login'} && $http_auth_login && (SL::Auth::OK() == $::auth->authenticate($::myconfig{login}, $http_auth_password)); |
42 | 41 |
$ok ||= !$::form->{'{AUTH}login'} && !$http_auth_login && (SL::Auth::OK() == $::auth->authenticate($::myconfig{login}, undef)); |
... | ... | |
59 | 58 |
return 0; |
60 | 59 |
} |
61 | 60 |
|
62 |
sub _parse_http_basic_auth { |
|
63 |
my ($self) = @_; |
|
64 |
|
|
65 |
# See RFC 7617. |
|
66 |
|
|
67 |
# Requires that the server passes the 'Authorization' header as the |
|
68 |
# environment variable 'HTTP_AUTHORIZATION'. Example code for |
|
69 |
# Apache: |
|
70 |
|
|
71 |
# SetEnvIf Authorization "(.*)" HTTP_AUTHORIZATION=$1 |
|
72 |
|
|
73 |
my $data = $ENV{HTTP_AUTHORIZATION}; |
|
74 |
|
|
75 |
return unless ($data // '') =~ m{^basic +(.+)}i; |
|
76 |
|
|
77 |
$data = Encode::decode('utf-8', MIME::Base64::decode($1)); |
|
78 |
|
|
79 |
return unless $data =~ m{(.+?):(.+)}; |
|
80 |
|
|
81 |
return ($1, $2); |
|
82 |
} |
|
83 |
|
|
84 | 61 |
1; |
config/kivitendo.conf.default | ||
---|---|---|
77 | 77 |
timeout = 10 |
78 | 78 |
verify = require |
79 | 79 |
|
80 |
[authentication/http_basic] |
|
81 |
enabled = 1 |
|
82 |
|
|
83 |
[authentication/http_headers] |
|
84 |
client_id_header = X-Kivitendo-Client-ID |
|
85 |
user_header = Auth-User |
|
86 |
secret_header = X-Kivitendo-App-Secret |
|
87 |
secret = ... |
|
88 |
|
|
80 | 89 |
[system] |
81 | 90 |
# Set language for login and admin forms. Currently "de" (German) |
82 | 91 |
# and "en" (English, not perfect) are available. |
Auch abrufbar als: Unified diff
[LINET] Auth: automatisches SSO mittels gewisser HTTP-Header