8311c7015c8df0f108f920772dfe9943fce63029
[kivitendo-erp.git] / SL / Auth / LDAP.pm
1 package SL::Auth::LDAP;
2
3 use English '-no_match_vars';
4
5 use Scalar::Util qw(weaken);
6 use SL::Auth::Constants qw(:all);
7
8 use strict;
9
10 sub new {
11   if (!defined eval "require Net::LDAP;") {
12     die 'The module "Net::LDAP" is not installed.';
13   }
14
15   my $type = shift;
16   my $self = {};
17
18   $self->{auth} = shift;
19   weaken $self->{auth};
20
21   bless $self, $type;
22
23   return $self;
24 }
25
26 sub reset {
27   my ($self) = @_;
28   $self->{ldap}     = undef;
29   $self->{dn_cache} = { };
30 }
31
32 sub _connect {
33   my $self = shift;
34   my $cfg  = $self->{auth}->{LDAP_config};
35
36   return $self->{ldap} if $self->{ldap};
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/kivitendo.conf.', $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/kivitendo.conf.'));
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/kivitendo.conf.', $cfg->{bind_dn}));
56     }
57   }
58
59   return $self->{ldap};
60 }
61
62 sub _get_filter {
63   my $self   = shift;
64   my $login  = shift;
65
66   my ($cfg, $filter);
67
68   $cfg    =  $self->{auth}->{LDAP_config};
69
70   $filter =  "$cfg->{filter}";
71   $filter =~ s|^\s+||;
72   $filter =~ s|\s+$||;
73
74   $login  =~ s|\\|\\\\|g;
75   $login  =~ s|\(|\\\(|g;
76   $login  =~ s|\)|\\\)|g;
77   $login  =~ s|\*|\\\*|g;
78   $login  =~ s|\x00|\\00|g;
79
80   if ($filter =~ m|<\%login\%>|) {
81     substr($filter, $LAST_MATCH_START[0], $LAST_MATCH_END[0] - $LAST_MATCH_START[0]) = $login;
82
83   } elsif ($filter) {
84     if ((substr($filter, 0, 1) ne '(') || (substr($filter, -1, 1) ne ')')) {
85       $filter = "($filter)";
86     }
87
88     $filter = "(&${filter}($cfg->{attribute}=${login}))";
89
90   } else {
91     $filter = "$cfg->{attribute}=${login}";
92
93   }
94
95   return $filter;
96 }
97
98 sub _get_user_dn {
99   my $self   = shift;
100   my $ldap   = shift;
101   my $login  = shift;
102
103   $self->{dn_cache} ||= { };
104
105   return $self->{dn_cache}->{$login} if $self->{dn_cache}->{$login};
106
107   my $cfg    = $self->{auth}->{LDAP_config};
108
109   my $filter = $self->_get_filter($login);
110
111   my $mesg   = $ldap->search('base' => $cfg->{base_dn}, 'scope' => 'sub', 'filter' => $filter);
112
113   return undef if $mesg->is_error || !$mesg->count();
114
115   my $entry                   = $mesg->entry(0);
116   $self->{dn_cache}->{$login} = $entry->dn();
117
118   return $self->{dn_cache}->{$login};
119 }
120
121 sub authenticate {
122   my $self       = shift;
123   my $login      = shift;
124   my $password   = shift;
125   my $is_crypted = shift;
126
127   return ERR_BACKEND if $is_crypted;
128
129   my $ldap = $self->_connect();
130
131   return ERR_BACKEND if !$ldap;
132
133   my $dn = $self->_get_user_dn($ldap, $login);
134
135   $main::lxdebug->message(LXDebug->DEBUG2(), "LDAP authenticate: dn $dn");
136
137   return ERR_BACKEND if !$dn;
138
139   my $mesg = $ldap->bind($dn, 'password' => $password);
140
141   $main::lxdebug->message(LXDebug->DEBUG2(), "LDAP authenticate: bind mesg " . $mesg->error());
142
143   return $mesg->is_error() ? ERR_PASSWORD : OK;
144 }
145
146 sub can_change_password {
147   return 0;
148 }
149
150 sub requires_cleartext_password {
151   return 1;
152 }
153
154 sub change_password {
155   return ERR_BACKEND;
156 }
157
158 sub verify_config {
159   my $form   = $main::form;
160   my $locale = $main::locale;
161
162   my $self = shift;
163   my $cfg  = $self->{auth}->{LDAP_config};
164
165   if (!$cfg) {
166     $form->error($locale->text('config/kivitendo.conf: Key "authentication/ldap" is missing.'));
167   }
168
169   if (!$cfg->{host} || !$cfg->{attribute} || !$cfg->{base_dn}) {
170     $form->error($locale->text('config/kivitendo.conf: Missing parameters in "authentication/ldap". Required parameters are "host", "attribute" and "base_dn".'));
171   }
172 }
173
174 1;