File Coverage

blib/lib/Lemonldap/NG/Common/Conf/LDAP.pm
Criterion Covered Total %
statement 20 135 14.8
branch 1 52 1.9
condition 0 2 0.0
subroutine 7 19 36.8
pod 0 13 0.0
total 28 221 12.6


line stmt bran cond sub pod time code
1             ##@file
2             # LDAP configuration backend
3              
4             ##@class
5             # Implements LDAP backend for Lemonldap::NG
6             package Lemonldap::NG::Common::Conf::LDAP;
7              
8 1     1   4 use strict;
  1         1  
  1         37  
9 1     1   4 use Net::LDAP;
  1         2  
  1         8  
10 1     1   61 use Lemonldap::NG::Common::Conf::Constants; #inherits
  1         2  
  1         70  
11 1     1   375 use Lemonldap::NG::Common::Conf::Serializer;
  1         2  
  1         45  
12 1     1   11 use Encode;
  1         2  
  1         126  
13              
14             our $VERSION = '1.4.1';
15              
16             BEGIN {
17 1     1   1351 *Lemonldap::NG::Common::Conf::ldap = \&ldap;
18             }
19              
20             sub prereq {
21 1     1 0 3 my $self = shift;
22 1         3 foreach ( 'ldapServer', 'ldapConfBase', 'ldapBindDN', 'ldapBindPassword' ) {
23 4 50       11 unless ( $self->{$_} ) {
24 0         0 $Lemonldap::NG::Common::Conf::msg .=
25             "$_ is required in LDAP configuration type \n";
26 0         0 return 0;
27             }
28             }
29 1         5 1;
30             }
31              
32             sub available {
33 0     0 0   my $self = shift;
34              
35 0 0         unless ( $self->ldap ) {
36 0           return 0;
37             }
38              
39 0           my $search = $self->ldap->search(
40             base => $self->{ldapConfBase},
41             filter => '(objectClass=applicationProcess)',
42             scope => 'one',
43             attrs => ['cn'],
44             );
45              
46 0 0         if ( $search->code ) {
47 0           $self->logError($search);
48 0           return 0;
49             }
50              
51 0           my @entries = $search->entries();
52 0           my @conf;
53 0           foreach (@entries) {
54 0           my $cn = $_->get_value('cn');
55 0           my ($cfgNum) = ( $cn =~ /lmConf-(\d*)/ );
56 0           push @conf, $cfgNum;
57             }
58 0 0         $self->ldap->unbind() && delete $self->{ldap};
59 0           return sort { $a <=> $b } @conf;
  0            
60             }
61              
62             sub lastCfg {
63 0     0 0   my $self = shift;
64 0           my @avail = $self->available;
65 0           return $avail[$#avail];
66             }
67              
68             sub ldap {
69 0     0 0   my $self = shift;
70 0 0         return $self->{ldap} if ( $self->{ldap} );
71              
72             # Parse servers configuration
73 0           my $useTls = 0;
74 0           my $tlsParam;
75 0           my @servers = ();
76 0           foreach my $server ( split /[\s,]+/, $self->{ldapServer} ) {
77 0 0         if ( $server =~ m{^ldap\+tls://([^/]+)/?\??(.*)$} ) {
78 0           $useTls = 1;
79 0           $server = $1;
80 0   0       $tlsParam = $2 || "";
81             }
82             else {
83 0           $useTls = 0;
84             }
85 0           push @servers, $server;
86             }
87              
88             # Connect
89 0 0         my $ldap = Net::LDAP->new(
90             \@servers,
91             onerror => undef,
92             ( $self->{ldapPort} ? ( port => $self->{ldapPort} ) : () ),
93             );
94              
95 0 0         unless ($ldap) {
96 0           $Lemonldap::NG::Common::Conf::msg .= "$@\n";
97 0           return;
98             }
99              
100             # Start TLS if needed
101 0 0         if ($useTls) {
102 0           my %h = split( /[&=]/, $tlsParam );
103 0 0         $h{cafile} = $self->{caFile} if ( $self->{caFile} );
104 0 0         $h{capath} = $self->{caPath} if ( $self->{caPath} );
105 0           my $start_tls = $ldap->start_tls(%h);
106 0 0         if ( $start_tls->code ) {
107 0           $self->logError($start_tls);
108 0           return;
109             }
110             }
111              
112             # Bind with credentials
113 0           my $bind =
114             $ldap->bind( $self->{ldapBindDN}, password => $self->{ldapBindPassword} );
115 0 0         if ( $bind->code ) {
116 0           $self->logError($bind);
117 0           return;
118             }
119              
120 0           $self->{ldap} = $ldap;
121 0           return $ldap;
122             }
123              
124             sub lock {
125              
126             # No lock for LDAP
127 0     0 0   return 1;
128             }
129              
130             sub isLocked {
131              
132             # No lock for LDAP
133 0     0 0   return 0;
134             }
135              
136             sub unlock {
137              
138             # No lock for LDAP
139 0     0 0   return 1;
140             }
141              
142             sub store {
143 0     0 0   my ( $self, $fields ) = @_;
144              
145 0 0         unless ( $self->ldap ) {
146 0           return 0;
147             }
148              
149 0           $fields = $self->serialize($fields);
150              
151 0           my $lastCfg = $self->lastCfg;
152              
153 0           my $confName = "lmConf-" . $fields->{cfgNum};
154 0           my $confDN = "cn=$confName," . $self->{ldapConfBase};
155              
156             # Store values as {key}value
157 0           my @confValues;
158 0           while ( my ( $k, $v ) = each(%$fields) ) {
159 0           $v = encodeLdapValue($v);
160 0           push @confValues, "{$k}$v";
161             }
162              
163 0           my $operation;
164              
165 0 0         if ( $lastCfg == $fields->{cfgNum} ) {
166 0           $operation =
167             $self->ldap->modify( $confDN,
168             replace => { description => \@confValues } );
169             }
170             else {
171 0           $operation = $self->ldap->add(
172             $confDN,
173             attrs => [
174             objectClass => [ 'top', 'applicationProcess' ],
175             cn => $confName,
176             description => \@confValues,
177             ]
178             );
179             }
180              
181 0 0         if ( $operation->code ) {
182 0           $self->logError($operation);
183 0           return 0;
184             }
185              
186 0 0         $self->ldap->unbind() && delete $self->{ldap};
187 0           return $fields->{cfgNum};
188             }
189              
190             sub load {
191 0     0 0   my ( $self, $cfgNum, $fields ) = @_;
192              
193 0 0         unless ( $self->ldap ) {
194 0           return;
195             }
196              
197 0           my $f;
198 0           my $confName = "lmConf-" . $cfgNum;
199 0           my $confDN = "cn=$confName," . $self->{ldapConfBase};
200              
201 0           my $search = $self->ldap->search(
202             base => $confDN,
203             filter => '(objectClass=applicationProcess)',
204             scope => 'base',
205             attrs => ['description'],
206             );
207              
208 0 0         if ( $search->code ) {
209 0           $self->logError($search);
210 0           return;
211             }
212              
213 0           my $entry = $search->shift_entry();
214 0           my @confValues = $entry->get_value('description');
215 0           foreach (@confValues) {
216 0           my ( $k, $v ) = ( $_ =~ /\{(.*?)\}(.*)/ );
217 0           $v = decodeLdapValue($v);
218 0 0         if ($fields) {
219 0 0         $f->{$k} = $v if ( grep { $_ eq $k } @$fields );
  0            
220             }
221             else {
222 0           $f->{$k} = $v;
223             }
224             }
225 0 0         $self->ldap->unbind() && delete $self->{ldap};
226 0           return $self->unserialize($f);
227             }
228              
229             sub delete {
230 0     0 0   my ( $self, $cfgNum ) = @_;
231              
232 0 0         unless ( $self->ldap ) {
233 0           return 0;
234             }
235              
236 0           my $confDN = "cn=lmConf-" . $cfgNum . "," . $self->{ldapConfBase};
237 0           my $delete = $self->ldap->delete($confDN);
238 0 0         $self->ldap->unbind() && delete $self->{ldap};
239 0 0         $self->logError($delete) if ( $delete->code );
240             }
241              
242             sub logError {
243 0     0 0   my $self = shift;
244 0           my $ldap_operation = shift;
245 0           $Lemonldap::NG::Common::Conf::msg .=
246             "LDAP error "
247             . $ldap_operation->code . ": "
248             . $ldap_operation->error . " \n";
249             }
250              
251             # Helpers to have UTF-8 values in LDAP
252             # and default encoding in configuration object
253             sub encodeLdapValue {
254 0     0 0   my $value = shift;
255              
256 0           eval {
257 0           my $safevalue = $value;
258 0           Encode::from_to( $safevalue, "utf8", "iso-8859-1", Encode::FB_CROAK );
259             };
260 0 0         if ($@) {
261 0           Encode::from_to( $value, "iso-8859-1", "utf8", Encode::FB_CROAK );
262             }
263              
264 0           return $value;
265              
266             }
267              
268             sub decodeLdapValue {
269 0     0 0   my $value = shift;
270              
271 0           Encode::from_to( $value, "utf8", "iso-8859-1", Encode::FB_CROAK );
272              
273 0           return $value;
274              
275             }
276              
277             1;
278             __END__