source: LATMOS-Accounts/lib/LATMOS/Accounts/Bases/Ad.pm @ 193

Last change on this file since 193 was 193, checked in by nanardon, 15 years ago
  • fix directReports and manager attributes read in Ad
  • Property svn:keywords set to Id Rev
File size: 5.8 KB
Line 
1package LATMOS::Accounts::Bases::Ad;
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 Unicode::String qw(utf16);
15
16our $VERSION = (q$Rev$ =~ /^Rev: (\d+) /)[0];
17
18=head1 NAME
19
20LATMOS::Ad - Perl extension for blah blah blah
21
22=head1 SYNOPSIS
23
24  use LATMOS::Ad;
25  blah blah blah
26
27=head1 DESCRIPTION
28
29Stub documentation for LATMOS::Ad, 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(%options)
40
41Create a new LATMOS::Ad object for windows AD $domain.
42
43options:
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, %options) = @_;
71   
72    $options{domain} or return;
73    my $self = {
74        _server => $options{server},
75        _ad_domain => $options{domain},
76        _top_dn => join(',', map { "dc=$_" } split('\.', $options{domain})),
77        _login => $options{login},
78        _password => $options{password},
79        _ssl => $options{ssl},
80        _param => { %options },
81    };
82
83    bless($self, $class);
84}
85
86sub param {
87    my ($self, $var) = @_;
88    return $self->{_param}{$var}
89}
90
91sub object_base_dn {
92    my ($self, $otype) = @_;
93    return join(',',
94        ($self->param($otype . '_container') || 'cn=Users'),
95        $self->top_dn,
96    );
97}
98
99sub load {
100    my ($self) = @_;
101    # At this point, if still no $server, DNS search
102
103    my $ldap;
104    foreach my $tryserv (
105        $self->{server}
106        ? ($self->_ldap_url($self->{_server}))
107        : $self->_query_zone_ads) {
108        $ldap = Net::LDAP->new(
109            $tryserv,
110        ) and last;
111    }
112
113    $ldap or do {
114        warn "Cannot to ldap server";
115        return; # connot connect to any ldap :\
116    };
117
118    my $login = $self->{_login};
119    $login =~ m/@/ or $login .= '@' . $self->ad_domain;
120
121    my $msg = $ldap->bind($login, password => $self->{_password}) or return;
122    $msg->code and do {
123        warn $msg->error;
124        return;
125    };
126
127    $self->{_ldap} = $ldap;
128    return 1;
129}
130
131sub _ldap_url {
132    my ($self, $host, $port) = @_;
133
134    sprintf(
135        '%s://%s%s/',
136        $self->{_ssl} ? 'ldaps' : 'ldap',
137        $host,
138        $port ? ":$port" : '',
139    )
140}
141
142sub _query_zone_ads {
143    my ($self) = @_;
144    require Net::DNS;
145    my @urllist;
146
147    my $resolver = Net::DNS::Resolver->new;
148    my $query = $resolver->query("_ldap._tcp.dc._msdcs." . $self->ad_domain, "SRV");
149    foreach my $rr (
150        sort { $a->priority <=> $b->priority || $a->weight <=> $b->weight }
151        grep { $_->type eq 'SRV' } $query->answer) {
152        push(@urllist, $self->_ldap_url($rr->target)); # $rr->port)); don't use port
153    }
154
155    @urllist
156}
157
158sub ldap {
159    return $_[0]->{_ldap};
160}
161
162=head2 top_dn
163
164Return the TOP DN of the AD zone.
165
166=cut
167
168sub top_dn {
169    return $_[0]->{_top_dn}
170}
171
172=head2 ad_domain
173
174Return the active directory zone
175
176=cut
177
178sub ad_domain {
179    return $_[0]->{_ad_domain}
180}
181
182# _unlimited_search
183
184# By default, ldap servers limit results to avoid deni of services.
185# LDAP protocol provide a paging feature to fetch all results.
186
187#This function works like Net::LDAP::search functions, but return nothing,
188#use "callback" option to get entries.
189
190sub _unlimited_search {
191    my $self = shift;
192    my @args = @_;
193
194    my ($page, $cookie) = (Net::LDAP::Control::Paged->new( size => 100 ));
195
196    while (1) {
197        my $search = $self->ldap->search(
198            @args,
199            control => [ $page ],
200        );
201        if ($search->code) {
202            return $search;
203        }
204
205        ### After foreach loops ends, client checks LDAP server reponse of how many search results total.
206        ### This is a control to end the infinite while loop if there are no search results to go through
207        ### If there are search results, this control will always return the total number of results. It is
208        ### never decremented
209        my ($resp) = $search->control( LDAP_CONTROL_PAGED ) or last;
210
211
212        ### Obtaining the cookie from the search result. When no more results, cookie will be NULL
213        ### and infinite while loop will terminate.
214        $cookie = $resp->cookie or last;
215
216        ### Sets cookie so server knows the next search result to send
217        $page->cookie($cookie);
218    }
219    ### This is a control to check for abnormal exit of the while loop.
220    ### If this occurs ### we need to tell the LDAP server that remaining
221    ### search results are no longer needed ### by sending a search request with a page size of 0
222    if ($cookie)    {
223        $page->cookie($cookie);
224        $page->size(0);
225        $self->ldap->search(@args, control => [ $page ]);
226    }
227    return 1;
228}
229
230# _get_object_from_dn($dn)
231#
232# Return Net::LDAP::Entry for $dn
233
234sub _get_object_from_dn {
235    my ($self, $dn) = @_;
236
237    my $mesg = $self->ldap->search(
238        base => $dn,
239        scope => 'base',
240        filter => '(objectClass=*)',
241    );
242    if ($mesg->code) {
243        warn $mesg->error;
244        return;
245    }
246
247    return ($mesg->entries)[0];
248}
249
2501;
251
252__END__
253
254=head1 SEE ALSO
255
256=head1 AUTHOR
257
258Olivier Thauvin, E<lt>olivier.thauvin@aerov.jussieu.frE<gt>
259
260=head1 COPYRIGHT AND LICENSE
261
262Copyright (C) 2008 CNRS SA/CETP/LATMOS
263
264This library is free software; you can redistribute it and/or modify
265it under the same terms as Perl itself, either Perl version 5.10.0 or,
266at your option, any later version of Perl 5 you may have available.
267
268
269=cut
Note: See TracBrowser for help on using the repository browser.