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