/[virtual-ldap]/lib/LDAP/Virtual.pm
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Annotation of /lib/LDAP/Virtual.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 6 - (hide annotations)
Sat Mar 14 18:21:29 2009 UTC (15 years, 2 months ago) by dpavlin
File size: 5623 byte(s)
save search queries to YAML files

1 dpavlin 4 package LDAP::Virtual;
2 dpavlin 1
3     use strict;
4     use warnings;
5    
6     use Net::LDAP::Constant qw(
7     LDAP_SUCCESS
8     LDAP_STRONG_AUTH_NOT_SUPPORTED
9     LDAP_UNAVAILABLE
10     LDAP_OPERATIONS_ERROR
11     );
12     use Net::LDAP::Server;
13     use Net::LDAP::Filter;
14     use base qw(Net::LDAP::Server);
15     use fields qw(upstream);
16    
17 dpavlin 2 use Net::LDAP;
18    
19 dpavlin 1 use URI::Escape; # uri_escape
20     use IO::Socket::INET;
21     use IO::Select;
22    
23 dpavlin 6 use YAML qw/DumpFile/;
24    
25 dpavlin 1 use Data::Dump qw/dump/;
26    
27     =head1 NAME
28    
29 dpavlin 4 LDAP::Virtual
30 dpavlin 1
31     =cut
32    
33     =head1 DESCRIPTION
34    
35 dpavlin 2 Provide LDAP server functionality somewhat similar to C<slapo-rwm>
36 dpavlin 1
37     =head1 METHODS
38    
39     =head2 run
40    
41 dpavlin 4 my $pid = LDAP::Virtual->run({ port => 1389, fork => 0 });
42 dpavlin 1
43     =cut
44    
45     our $pids;
46     our $cache;
47    
48     sub cache {
49 dpavlin 5 return $cache;
50 dpavlin 1 }
51    
52     sub run {
53     my $self = shift;
54    
55     my $args = shift;
56     # default LDAP port
57     my $port = $args->{port} ||= 1389;
58    
59     if ( $args->{fork} ) {
60     defined(my $pid = fork()) or die "Can't fork: $!";
61     if ( $pid ) {
62     $pids->{ $port } = $pid;
63     warn "# pids = ",dump( $pids );
64     sleep 1;
65     return $pid;
66     }
67     }
68    
69     my $sock = IO::Socket::INET->new(
70     Listen => 5,
71     Proto => 'tcp',
72     Reuse => 1,
73     LocalPort => $port,
74     ) or die "can't listen on port $port: $!\n";
75    
76     warn "LDAP server listening on port $port\n";
77    
78     my $sel = IO::Select->new($sock) or die "can't select socket: $!\n";
79     my %Handlers;
80     while (my @ready = $sel->can_read) {
81     foreach my $fh (@ready) {
82     if ($fh == $sock) {
83     # let's create a new socket
84     my $psock = $sock->accept;
85     $sel->add($psock);
86 dpavlin 4 $Handlers{*$psock} = LDAP::Virtual->new($psock);
87 dpavlin 1 } else {
88     my $result = $Handlers{*$fh}->handle;
89     if ($result) {
90     # we have finished with the socket
91     $sel->remove($fh);
92     $fh->close;
93     delete $Handlers{*$fh};
94     }
95     }
96     }
97     }
98     }
99    
100     =head2 stop
101    
102 dpavlin 4 my $stopped_pids = LDAP::Virtual->stop;
103 dpavlin 1
104     =cut
105    
106     sub stop {
107     warn "## stop pids = ",dump( $pids );
108     return unless $pids;
109     my $stopped = 0;
110     foreach my $port ( keys %$pids ) {
111     my $pid = delete($pids->{$port}) or die "no pid?";
112     warn "# Shutdown LDAP server at port $port pid $pid\n";
113     kill(9,$pid) or die "can't kill $pid: $!";
114     waitpid($pid,0) or die "waitpid $pid: $!";
115     $stopped++;
116     }
117     warn "## stopped $stopped processes\n";
118     return $stopped;
119     }
120    
121     use constant RESULT_OK => {
122     'matchedDN' => '',
123     'errorMessage' => '',
124     'resultCode' => LDAP_SUCCESS
125     };
126    
127     # constructor
128     sub new {
129     my ($class, $sock) = @_;
130     my $self = $class->SUPER::new($sock);
131     printf "Accepted connection from: %s\n", $sock->peerhost();
132     return $self;
133     }
134    
135     # the bind operation
136     sub bind {
137     my ($self,$req) = @_;
138    
139     warn "## bind req = ",dump($req);
140    
141     defined($req->{authentication}->{simple}) or return {
142     matchedDN => '',
143     errorMessage => '',
144     resultCode => LDAP_STRONG_AUTH_NOT_SUPPORTED,
145     };
146    
147 dpavlin 2 $self->{upstream} ||= Net::LDAP->new( 'ldaps://ldap.ffzg.hr/' ) or return {
148 dpavlin 1 matchedDN => '',
149     errorMessage => $@,
150     resultCode => LDAP_UNAVAILABLE,
151     };
152    
153 dpavlin 2 warn "## upstream = ",dump( $self->{upstream} );
154     warn "upstream not Net::LDAP but ",ref($self->{upstream}) unless ref($self->{upstream}) eq 'Net::LDAP';
155 dpavlin 1
156     my $msg;
157    
158 dpavlin 2 # FIXME we would need to unbind because VLDAP binds us automatically, but that doesn't really work
159 dpavlin 1 #$msg = $self->{upstream}->unbind;
160     #warn "# unbind msg = ",dump( $msg );
161    
162 dpavlin 3 my $bind;
163     $bind->{dn} = $req->{name} if $req->{name};
164     $bind->{password} = $req->{authentication}->{simple} if $req->{authentication}->{simple};
165     warn "# bind ",dump( $bind );
166     $msg = $self->{upstream}->bind( %$bind );
167 dpavlin 1
168     #warn "# bind msg = ",dump( $msg );
169     if ( $msg->code != LDAP_SUCCESS ) {
170     warn "ERROR: ", $msg->code, ": ", $msg->server_error, "\n";
171     return {
172     matchedDN => '',
173     errorMessage => $msg->server_error,
174     resultCode => $msg->code,
175     };
176     }
177    
178     return RESULT_OK;
179     }
180    
181     # the search operation
182     sub search {
183     my ($self,$req) = @_;
184    
185     warn "## search req = ",dump( $req );
186    
187     if ( ! $self->{upstream} ) {
188     warn "search without bind";
189     return {
190     matchedDN => '',
191     errorMessage => 'dude, bind first',
192     resultCode => LDAP_OPERATIONS_ERROR,
193     };
194     }
195    
196     my $filter;
197     if (defined $req->{filter}) {
198     # $req->{filter} is a ASN1-decoded tree; luckily, this is exactly the
199     # internal representation Net::LDAP::Filter uses. [FIXME] Eventually
200     # Net::LDAP::Filter should provide a corresponding constructor.
201     bless($req->{filter}, 'Net::LDAP::Filter');
202     $filter = $req->{filter}->as_string;
203     # $filter = '(&' . $req->{filter}->as_string
204     # . '(objectClass=hrEduPerson)(host=aai.irb.hr))';
205     }
206    
207     warn "search upstream for $filter\n";
208    
209     my $search = $self->{upstream}->search(
210     base => $req->{baseObject},
211     scope => $req->{scope},
212     deref => $req->{derefAliases},
213     sizelimit => $req->{sizeLimit},
214     timelimit => $req->{timeLimit},
215     typesonly => $req->{typesOnly},
216     filter => $filter,
217     attrs => $req->{attributes},
218     raw => qr/.*/,
219     );
220    
221     # warn "# search = ",dump( $search );
222    
223     if ( $search->code != LDAP_SUCCESS ) {
224     warn "ERROR: ",$search->code,": ",$search->server_error;
225     return {
226     matchedDN => '',
227     errorMessage => $search->server_error,
228     resultCode => $search->code,
229     };
230     };
231    
232     my @entries = $search->entries;
233     warn "## got ", $search->count, " entries for $filter\n";
234     foreach my $entry (@entries) {
235     # $entry->changetype('add'); # Don't record changes.
236     # foreach my $attr ($entry->attributes) {
237     # if ($attr =~ /;lang-en$/) {
238     # $entry->delete($attr);
239     # }
240     # }
241     }
242    
243     warn "## entries = ",dump( @entries );
244    
245 dpavlin 5 # $self->cache->write_cache( \@entries, uri_escape( $filter ));
246 dpavlin 1
247 dpavlin 6 my $path = uri_escape( $filter );
248     DumpFile( "var/${path}.yml", \@entries );
249     warn "# created $path ", -s $path, " bytes";
250    
251 dpavlin 1 return RESULT_OK, @entries;
252     }
253    
254     # the rest of the operations will return an "unwilling to perform"
255    
256     1;

Properties

Name Value
svn:mergeinfo

  ViewVC Help
Powered by ViewVC 1.1.26