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

Last change on this file since 189 was 189, checked in by nanardon, 15 years ago
  • add set_password() to AD
  • Property svn:keywords set to Id Rev
File size: 5.6 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=head2 unlimited_search
183
184By default, ldap servers limit results to avoid deni of services.
185LDAP protocol provide a paging feature to fetch all results.
186
187This function works like Net::LDAP::search functions, but return nothing,
188use "callback" option to get entries.
189
190=cut
191
192sub _unlimited_search {
193    my $self = shift;
194    my @args = @_;
195
196    my ($page, $cookie) = (Net::LDAP::Control::Paged->new( size => 100 ));
197
198    while (1) {
199        my $search = $self->ldap->search(
200            @args,
201            control => [ $page ],
202        );
203        if ($search->code) {
204            return $search;
205        }
206
207        ### After foreach loops ends, client checks LDAP server reponse of how many search results total.
208        ### This is a control to end the infinite while loop if there are no search results to go through
209        ### If there are search results, this control will always return the total number of results. It is
210        ### never decremented
211        my ($resp) = $search->control( LDAP_CONTROL_PAGED ) or last;
212
213
214        ### Obtaining the cookie from the search result. When no more results, cookie will be NULL
215        ### and infinite while loop will terminate.
216        $cookie = $resp->cookie or last;
217
218        ### Sets cookie so server knows the next search result to send
219        $page->cookie($cookie);
220    }
221    ### This is a control to check for abnormal exit of the while loop.
222    ### If this occurs ### we need to tell the LDAP server that remaining
223    ### search results are no longer needed ### by sending a search request with a page size of 0
224    if ($cookie)    {
225        $page->cookie($cookie);
226        $page->size(0);
227        $self->ldap->search(@args, control => [ $page ]);
228    }
229    return 1;
230}
231
232sub _defaults_group_attrs {
233    my ($self, $entry, $attrs) = @_;
234    foreach my $attr (keys %{ $attrs || {} }) {
235        my $val = $attrs->{$attr};
236        $entry->replace($attr => $val);
237    }
238}
239
2401;
241
242__END__
243
244=head1 SEE ALSO
245
246=head1 AUTHOR
247
248Olivier Thauvin, E<lt>olivier.thauvin@aerov.jussieu.frE<gt>
249
250=head1 COPYRIGHT AND LICENSE
251
252Copyright (C) 2008 CNRS SA/CETP/LATMOS
253
254This library is free software; you can redistribute it and/or modify
255it under the same terms as Perl itself, either Perl version 5.10.0 or,
256at your option, any later version of Perl 5 you may have available.
257
258
259=cut
Note: See TracBrowser for help on using the repository browser.