Umstellung der Benutzerverwaltung von Dateien im Verzeichnis "users" auf die Verwendu...
[kivitendo-erp.git] / SL / Auth / LDAP.pm
1 package SL::Auth::LDAP;
2
3 use English '-no_match_vars';
4
5 use SL::Auth;
6
7 sub new {
8   $main::lxdebug->enter_sub();
9
10   if (!defined eval "require Net::LDAP;") {
11     die 'The module "Net::LDAP" is not installed.';
12   }
13
14   my $type = shift;
15   my $self = {};
16
17   $self->{auth} = shift;
18
19   bless $self, $type;
20
21   $main::lxdebug->leave_sub();
22
23   return $self;
24 }
25
26 sub _connect {
27   $main::lxdebug->enter_sub();
28
29   my $self = shift;
30   my $cfg  = $self->{auth}->{LDAP_config};
31
32   if ($self->{ldap}) {
33     $main::lxdebug->leave_sub();
34
35     return $self->{ldap};
36   }
37
38   my $port      = $cfg->{port} || 389;
39   $self->{ldap} = Net::LDAP->new($cfg->{host}, 'port' => $port);
40
41   if (!$self->{ldap}) {
42     $main::form->error($main::locale->text('The LDAP server "#1:#2" is unreachable. Please check config/authentication.pl.', $cfg->{host}, $port));
43   }
44
45   if ($cfg->{tls}) {
46     my $mesg = $self->{ldap}->start_tls('verify' => 'none');
47     if ($mesg->is_error()) {
48       $main::form->error($main::locale->text('The connection to the LDAP server cannot be encrypted (SSL/TLS startup failure). Please check config/authentication.pl.'));
49     }
50   }
51
52   if ($cfg->{bind_dn}) {
53     my $mesg = $self->{ldap}->bind($cfg->{bind_dn}, 'password' => $cfg->{bind_password});
54     if ($mesg->is_error()) {
55       $main::form->error($main::locale->text('Binding to the LDAP server as "#1" failed. Please check config/authentication.pl.', $cfg->{bind_dn}));
56     }
57   }
58
59   $main::lxdebug->leave_sub();
60
61   return $self->{ldap};
62 }
63
64 sub _get_filter {
65   $main::lxdebug->enter_sub();
66
67   my $self   = shift;
68   my $login  = shift;
69
70   my ($cfg, $filter);
71
72   $cfg    =  $self->{auth}->{LDAP_config};
73
74   $filter =  "$cfg->{filter}";
75   $filter =~ s|^\s+||;
76   $filter =~ s|\s+$||;
77
78   $login  =~ s|\\|\\\\|g;
79   $login  =~ s|\(|\\\(|g;
80   $login  =~ s|\)|\\\)|g;
81   $login  =~ s|\*|\\\*|g;
82   $login  =~ s|\x00|\\00|g;
83
84   if ($filter =~ m|<\%login\%>|) {
85     substr($filter, $LAST_MATCH_START[0], $LAST_MATCH_END[0] - $LAST_MATCH_START[0]) = $login;
86
87   } elsif ($filter) {
88     if ((substr($filter, 0, 1) ne '(') || (substr($filter, -1, 1) ne ')')) {
89       $filter = "($filter)";
90     }
91
92     $filter = "(&${filter}($cfg->{attribute}=${login}))";
93
94   } else {
95     $filter = "$cfg->{attribute}=${login}";
96
97   }
98
99   $main::lxdebug->leave_sub();
100
101   return $filter;
102 }
103
104 sub _get_user_dn {
105   $main::lxdebug->enter_sub();
106
107   my $self   = shift;
108   my $ldap   = shift;
109   my $login  = shift;
110
111   $self->{dn_cache} ||= { };
112
113   if ($self->{dn_cache}->{$login}) {
114     $main::lxdebug->leave_sub();
115     return $self->{dn_cache}->{$login};
116   }
117
118   my $cfg    = $self->{auth}->{LDAP_config};
119
120   my $filter = $self->_get_filter($login);
121
122   my $mesg   = $ldap->search('base' => $cfg->{base_dn}, 'scope' => 'sub', 'filter' => $filter);
123
124   if ($mesg->is_error() || (0 == $mesg->count())) {
125     $main::lxdebug->leave_sub();
126     return undef;
127   }
128
129   my $entry                   = $mesg->entry(0);
130   $self->{dn_cache}->{$login} = $entry->dn();
131
132   $main::lxdebug->leave_sub();
133
134   return $self->{dn_cache}->{$login};
135 }
136
137 sub authenticate {
138   $main::lxdebug->enter_sub();
139
140   my $self       = shift;
141   my $login      = shift;
142   my $password   = shift;
143   my $is_crypted = shift;
144
145   if ($is_crypted) {
146     $main::lxdebug->leave_sub();
147     return SL::Auth::ERR_BACKEND;
148   }
149
150   my $ldap = $self->_connect();
151
152   if (!$ldap) {
153     $main::lxdebug->leave_sub();
154     return SL::Auth::ERR_BACKEND;
155   }
156
157   my $dn = $self->_get_user_dn($ldap, $login);
158
159   $main::lxdebug->message(LXDebug::DEBUG2, "LDAP authenticate: dn $dn");
160
161   if (!$dn) {
162     $main::lxdebug->leave_sub();
163     return SL::Auth::ERR_BACKEND;
164   }
165
166   my $mesg = $ldap->bind($dn, 'password' => $password);
167
168   $main::lxdebug->message(LXDebug::DEBUG2, "LDAP authenticate: bind mesg " . $mesg->error());
169
170   $main::lxdebug->leave_sub();
171
172   return $mesg->is_error() ? SL::Auth::ERR_PASSWORD : SL::Auth::OK;
173 }
174
175 sub can_change_password {
176   return 0;
177 }
178
179 sub change_password {
180   return SL::Auth::ERR_BACKEND;
181 }
182
183 sub verify_config {
184   $main::lxdebug->enter_sub();
185
186   my $self = shift;
187   my $cfg  = $self->{auth}->{LDAP_config};
188
189   if (!$cfg) {
190     $form->error($locale->text('config/authentication.pl: Key "LDAP_config" is missing.'));
191   }
192
193   if (!$cfg->{host} || !$cfg->{attribute} || !$cfg->{base_dn}) {
194     $form->error($locale->text('config/authentication.pl: Missing parameters in "LDAP_config". Required parameters are "host", "attribute" and "base_dn".'));
195   }
196
197   $main::lxdebug->leave_sub();
198 }
199
200 1;