File Coverage

blib/lib/Net/Radius/Server/Match/LDAP.pm
Criterion Covered Total %
statement 21 21 100.0
branch n/a
condition n/a
subroutine 7 7 100.0
pod n/a
total 28 28 100.0


line stmt bran cond sub pod time code
1             #! /usr/bin/perl
2             #
3             #
4             # $Id: LDAP.pm 75 2009-08-12 22:08:28Z lem $
5              
6             package Net::Radius::Server::Match::LDAP;
7              
8 1     1   6528 use 5.008;
  1         4  
  1         36  
9 1     1   5 use strict;
  1         3  
  1         30  
10 1     1   5 use warnings;
  1         2  
  1         79  
11              
12             our $VERSION = do { sprintf "%0.3f", 1+(q$Revision: 75 $ =~ /\d+/g)[0]/1000 };
13              
14 1     1   1224 use Net::LDAP;
  1         169442  
  1         50  
15 1     1   86 use Carp qw/croak/;
  1         1  
  1         54  
16 1     1   5 use Net::Radius::Server::Base qw/:match/;
  1         1  
  1         9  
17 1     1   26 use base qw/Net::Radius::Server::Match/;
  1         2  
  1         190  
18             __PACKAGE__->mk_accessors(qw/ldap_uri ldap_opts bind_dn bind_opts search_opts
19             store_result max_tries tls_opts authenticate_from
20             /);
21              
22             sub _expand
23             {
24             my $self = shift;
25             my $r_list = shift || [];
26             my $r_data = shift || {};
27              
28             my @r = ();
29              
30             die $self->description . ": Odd number of arguments\n"
31             if @$r_list % 2;
32              
33             for (my $i = 0;
34             $i < @$r_list;
35             $i += 2)
36             {
37             my $k = $r_list->[$i];
38             my $v = $r_list->[$i + 1];
39              
40             if ($k =~ m/^_nrs_(.+)$/ and ref($v) eq 'CODE')
41             {
42             push @r, $1, $v->($self, $r_data);
43             }
44             else
45             {
46             push @r, $k, $v;
47             }
48             }
49              
50             @r; # Return the resulting set of arguments
51             }
52              
53             sub _connect
54             {
55             my $self = shift;
56             my @args = $self->_expand($self->ldap_opts);
57              
58             $self->log(4, "Connecting to LDAP: " . $self->ldap_uri . " "
59             . join(', ', @args));
60              
61             $self->{_ldap} = Net::LDAP->new($self->ldap_uri, @args);
62              
63             die $self->description .
64             ": Failed to connect to LDAP server ", $self->ldap_uri, " ($!)\n"
65             unless $self->{_ldap};
66             }
67              
68             sub _bind
69             {
70             my $self = shift;
71             my $data = shift;
72              
73             $self->_connect($data, @_);
74              
75             my @args = $self->_expand($self->bind_opts, @_);
76              
77             my $dn = $self->bind_dn;
78              
79             if (ref($dn) eq 'CODE')
80             {
81             $dn = $dn->($self, $data, @_);
82             }
83            
84             if ($self->authenticate_from)
85             {
86             my $attr = $self->authenticate_from;
87             my $pass = undef;
88             if (ref($attr) eq 'CODE')
89             {
90             $pass = $attr->($self, $data, @_);
91             }
92             else
93             {
94             $pass = $data->{request}->password($data->{secret}, $attr);
95             }
96             push @args, (password => $pass);
97             }
98              
99             $self->log(4, "Binding to LDAP: " . ($dn || '(No DN)'));
100             my $r = $self->{_ldap}->bind($dn, @args);
101              
102             # At this stage, a failure to bind is a fatal error...
103             if ($r->code)
104             {
105             $self->log(2, "LDAP bind failure: ". $r->error);
106             return;
107             }
108             return 1;
109             }
110              
111             sub mk
112             {
113             my $proto = shift;
114             croak "->mk() cannot have arguments when in object-method mode\n"
115             if ref($proto) and $proto->isa('UNIVERSAL') and @_;
116              
117             my $self = ref($proto) ? $proto : $proto->new(@_);
118             die "Failed to create new object\n" unless $self;
119              
120             die $self->description . ": Must specify ldap_uri property\n"
121             unless $self->ldap_uri;
122              
123             $self->_bind(@_) unless $self->authenticate_from;
124              
125             return sub { $self->_match(@_) };
126             }
127              
128             sub match_ldap_uri
129             {
130             my $self = shift;
131             my $data = shift;
132              
133             my $r;
134             my $tries = 0;
135              
136             if ($self->authenticate_from
137             and not $self->_bind($data, @_))
138             {
139             $self->log(2, "Not matched due to bind() failure - Aborting");
140             return NRS_MATCH_FAIL;
141             }
142              
143             return NRS_MATCH_OK if $self->authenticate_from
144             and not $self->search_opts;
145              
146             do
147             {
148             $r = $self->{_ldap}->search($self->_expand($self->search_opts,
149             $data, @_));;
150             if ($r->code)
151             {
152             # Let's do a few attempts to query just in case...
153             if ($tries++ > ($self->max_tries || 2))
154             {
155             $self->log(2, "Failed to issue the query - Aborting");
156             return NRS_MATCH_FAIL;
157             }
158            
159             $self->log(2, "Failure to query: " . $r->error);
160             unless ($self->_bind($data, @_))
161             {
162             $self->log(2, "bind() failure");
163             return NRS_MATCH_FAIL if $self->authenticate_from;
164             }
165             }
166             } until (!$r->code);
167              
168             if ($self->store_result)
169             {
170             $self->log(4, "LDAP result stored");
171             $data->{$self->store_result} = $r;
172             }
173             else
174             {
175             $self->log(4, "LDAP result discarded");
176             }
177              
178             my $c = $r->count;
179             if ($c)
180             {
181             $self->log(4, "LDAP query returned $c entries - match");
182             return NRS_MATCH_OK;
183             }
184             else
185             {
186             $self->log(3, "LDAP query returned no entries - fail");
187             return NRS_MATCH_FAIL;
188             }
189             }
190              
191             42;
192              
193             __END__