source: trunk/LATMOS-Accounts/lib/LATMOS/Accounts/Bases/Ldap.pm @ 1904

Last change on this file since 1904 was 1605, checked in by nanardon, 8 years ago

Missing POD entry

File size: 6.2 KB
Line 
1package LATMOS::Accounts::Bases::Ldap;
2
3use 5.010000;
4use strict;
5use warnings;
6
7use base qw(LATMOS::Accounts::Bases);
8use Net::LDAP;
9use Net::LDAP::Entry;
10use Net::LDAP::Control::Paged;
11use Net::LDAP::Constant qw( LDAP_CONTROL_PAGED ); 
12use Net::LDAP::Util     qw( escape_filter_value );
13use Unicode::Map8;
14use LATMOS::Accounts::Log;
15
16our $VERSION = (q$Rev: 653 $ =~ /^Rev: (\d+) /)[0];
17
18=head1 NAME
19
20LATMOS::Ldap - Perl extension for blah blah blah
21
22=head1 SYNOPSIS
23
24  use LATMOS::Ldap;
25  blah blah blah
26
27=head1 DESCRIPTION
28
29Stub documentation for LATMOS::Ldap, created by h2xs. It looks like the
30author of the extension was negligent enough to leave the stub
31unedited.
32
33Blah blah blah.
34
35=head1 FUNCTIONS
36
37=cut
38
39=head2 new(%config)
40
41Create a new LATMOS::Ldap object for windows AD $domain.
42
43config:
44
45=over 4
46
47=item domain
48
49The Active directory domain
50
51=item server (optional)
52
53If set, try to connect to this server, if not set, a dns query is performed
54to find AD server, first responding is used.
55
56=item ssl
57
58If set, try to connect using ssl
59
60=item OBJECT_container
61
62The sub path where to find object type of OBJECT and where they should
63be created
64
65=back
66
67=cut
68
69sub new {
70    my ($class, %config) = @_;
71   
72    my $self = {
73        _top_dn => $config{topdn},
74    };
75
76    bless($self, $class);
77}
78
79=head2 object_base_dn ($otype)
80
81Return the dn of obecjt containers
82
83=cut
84
85sub object_base_dn {
86    my ($self, $otype) = @_;
87    return join(',',
88        ($self->config($otype . '_container') || 'cn=Users'),
89        $self->top_dn,
90    );
91}
92
93=head2 sambaSID($id)
94
95Return the base samba SID set in the config or a default one if none is set.
96
97If C<$id> is given return the full SID suitable for an object.
98
99=cut
100
101sub sambaSID {
102    my ($self, $id) = @_;
103
104    my $ssid = $self->config('sambaSID') || 'S-2016-01-07';
105    if (defined($id)) {
106        $ssid .= '-' . $id;
107    }
108    return $ssid;
109}
110
111sub load {
112    my ($self) = @_;
113
114    # If already loaded, just say ok !
115    $self->{_ldap} and return 1;
116
117    my $ldapurl = $self->_ldap_url($self->config('server'));
118    my $ldap = Net::LDAP->new($ldapurl, async => 0);
119       
120
121    if($ldap) {
122        $self->log(LA_DEBUG, "Connected to ldap server %s", $ldapurl);
123    } else {
124        $self->log(LA_ERR, "Cannot connect to %s", $ldapurl);
125        return; # cannot connect to any ldap :\
126    };
127
128
129    my $msg = $ldap->bind($self->config('login'), password => $self->config('password'));
130    $msg->code and do {
131        $self->log(LA_ERR, "Cannot bind ldap: %s", $msg->error);
132        return;
133    };
134
135    $self->{_ldap} = $ldap;
136    return 1;
137}
138
139sub _ldap_url {
140    my ($self, $host, $port) = @_;
141
142    sprintf(
143        '%s://%s%s/',
144        $self->config('ssl') ? 'ldaps' : 'ldap',
145        $host,
146        $port ? ":$port" : '',
147    )
148}
149
150=head2 ldap
151
152Return the L<Net::LDAP> handle
153
154=cut
155
156sub ldap {
157    return $_[0]->{_ldap};
158}
159
160=head2 top_dn
161
162Return the TOP DN of the AD zone.
163
164=cut
165
166sub top_dn {
167    return $_[0]->{_top_dn}
168}
169
170# _unlimited_search
171
172# By default, ldap servers limit results to avoid deni of services.
173# LDAP protocol provide a paging feature to fetch all results.
174
175#This function works like Net::LDAP::search functions, but return nothing,
176#use "callback" option to get entries.
177
178sub _unlimited_search {
179    my $self = shift;
180    my @args = @_;
181
182    my ($page, $cookie) = (Net::LDAP::Control::Paged->new( size => 100 ));
183
184    while (1) {
185        my $search = $self->ldap->search(
186            @args,
187            control => [ $page ],
188        );
189        if ($search->code) {
190            return $search;
191        }
192
193        ### After foreach loops ends, client checks LDAP server reponse of how many search results total.
194        ### This is a control to end the infinite while loop if there are no search results to go through
195        ### If there are search results, this control will always return the total number of results. It is
196        ### never decremented
197        my ($resp) = $search->control( LDAP_CONTROL_PAGED ) or last;
198
199
200        ### Obtaining the cookie from the search result. When no more results, cookie will be NULL
201        ### and infinite while loop will terminate.
202        $cookie = $resp->cookie or last;
203
204        ### Sets cookie so server knows the next search result to send
205        $page->cookie($cookie);
206    }
207    ### This is a control to check for abnormal exit of the while loop.
208    ### If this occurs ### we need to tell the LDAP server that remaining
209    ### search results are no longer needed ### by sending a search request with a page size of 0
210    if ($cookie)    {
211        $page->cookie($cookie);
212        $page->size(0);
213        $self->ldap->search(@args, control => [ $page ]);
214    }
215    return 1;
216}
217
218# _get_object_from_dn($dn)
219#
220# Return Net::LDAP::Entry for $dn
221
222sub _get_object_from_dn {
223    my ($self, $dn) = @_;
224
225    my $mesg = $self->ldap->search(
226        base => $dn,
227        scope => 'base',
228        filter => '(objectClass=*)',
229    );
230    if ($mesg->code) {
231        $self->log(LA_ERR, "Cannot get object %s: %s", $dn, $mesg->error);
232        return;
233    }
234
235    return ($mesg->entries)[0];
236}
237
238sub authenticate_user {
239    my ($self, $username, $passwd) = @_;
240    $username or return;
241
242    # Basically, connect and if bind succeed, user is authenticated !
243    my $ldapurl = $self->ldap->uri;
244    my $ldap = Net::LDAP->new($ldapurl);
245
246    my $obj = $self->get_object('user', $username) or do {
247        $self->log(LA_ERR, "Cannot find user %s", $username);
248        return;
249    };
250
251    if ($ldap) {
252        $self->log(LA_DEBUG, "Connected to ldap server %s", $ldapurl);
253    } else {
254        $self->log(LA_ERR, "Cannot connect to %s", $ldapurl);
255        return; # cannot connect to any ldap :\
256    };
257
258
259    my $msg = $ldap->bind($obj->get_attributes('dn'), password => $passwd);
260    if ($msg->code) {
261        $self->log(LA_ERR, "Cannot bind ldap: %s", $msg->error);
262        return;
263    } else {
264        return 1; # Success !! \o/
265    }
266}
267
2681;
269
270__END__
271
272=head1 SEE ALSO
273
274=head1 AUTHOR
275
276Olivier Thauvin, E<lt>olivier.thauvin@aerov.jussieu.frE<gt>
277
278=head1 COPYRIGHT AND LICENSE
279
280Copyright (C) 2008 CNRS SA/CETP/LATMOS
281
282This library is free software; you can redistribute it and/or modify
283it under the same terms as Perl itself, either Perl version 5.10.0 or,
284at your option, any later version of Perl 5 you may have available.
285
286=cut
Note: See TracBrowser for help on using the repository browser.