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

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