File Coverage

blib/lib/Net/LDAP/Class.pm
Criterion Covered Total %
statement 165 247 66.8
branch 43 98 43.8
condition 23 51 45.1
subroutine 30 46 65.2
pod 28 28 100.0
total 289 470 61.4


line stmt bran cond sub pod time code
1             package Net::LDAP::Class;
2              
3 10     10   63212 use strict;
  10         18  
  10         481  
4 10     10   52 use warnings;
  10         20  
  10         312  
5 10     10   90 use base qw( Rose::Object );
  10         24  
  10         11094  
6 10     10   2934 use Carp;
  10         25  
  10         704  
7 10     10   885 use Data::Dump ();
  10         8828  
  10         270  
8 10     10   2483 use Net::LDAP;
  10         1722794  
  10         98  
9 10     10   3930 use Net::LDAP::Entry;
  10         8766  
  10         343  
10 10     10   3505347 use Net::LDAP::Control::Paged;
  10         189601  
  10         347  
11 10     10   69 use Net::LDAP::Constant qw(LDAP_CONTROL_PAGED);
  10         22  
  10         579  
12 10     10   14282 use Net::LDAP::Batch;
  10         417691  
  10         258  
13 10     10   33959 use Net::LDAP::Class::Metadata;
  10         43  
  10         1925  
14 10     10   8156 use Net::LDAP::Class::Iterator;
  10         41  
  10         449  
15 10     10   10686 use Net::LDAP::Class::MultiIterator;
  10         38  
  10         397  
16 10     10   10541 use Net::LDAP::Class::SimpleIterator;
  10         27  
  10         504  
17              
18             use Net::LDAP::Class::MethodMaker (
19 10         86 'scalar --get_set_init' => [qw( ldap ldap_entry debug error )],
20             'scalar' => [qw( batch prev_batch )],
21             'object_or_class_meta' => [qw( attributes unique_attributes base_dn )],
22 10     10   63 );
  10         19  
23              
24 10     10   851 use overload '""' => 'stringify', 'bool' => sub {1}, 'fallback' => 1;
  10     1308   22  
  10         152  
  1308         17122  
25              
26             our $VERSION = '0.26';
27              
28             =head1 NAME
29              
30             Net::LDAP::Class - object-relational mapper for Net::LDAP
31              
32             =head1 SYNOPSIS
33              
34             # define your class
35            
36             package MyLDAPClass;
37             use base qw( Net::LDAP::Class );
38            
39             __PACKAGE__->metadata->setup(
40             attributes => [qw( name address phone email )],
41             unique_attributes => [qw( email )],
42             base_dn => 'dc=mycompany,dc=com',
43             );
44            
45             1;
46            
47             # then use your class
48              
49             use MyLDAPClass;
50             use Net::LDAP;
51            
52             my $ldap = create_Net_LDAP_object_and_bind(); # you write this
53            
54             # create an instance of your class
55             my $person = MyLDAPClass->new( ldap => $ldap, email => 'foo@bar.com' );
56            
57             # load from LDAP or write if not yet existing
58             $person->read or $person->create;
59            
60             # set the 'name' attribute
61             $person->name( 'Joe Foo' );
62            
63             # write your changes
64             $person->update;
65            
66             # change your mind?
67             $person->delete;
68              
69              
70             =head1 DESCRIPTION
71              
72             Net::LDAP::Class (NLC) is an object-relational mapping for LDAP.
73              
74             I know, it's all wrong to confuse the ORM model with LDAP
75             since LDAP is not relational in the same way that a RDBMS is. But the ORM
76             APIs of projects like DBIx::Class and Rose::DB::Object are so fun and easy to use,
77             it seemed like LDAP management should be just as fun and easy.
78              
79             The architecture of this package is based on Rose::DB::Object, which the author
80             uses to great effect for RDBMS management.
81              
82             =head1 METHODS
83              
84             NLC uses the Rose::Object package to create methods and handle the mundane get/set features.
85             In addition, Net::LDAP::Class::MethodMaker implements a new method type called B
86             which handles the get/set/fetch of NLC objects related to a given NLC object. Typically these
87             are Users and Groups. A User is typically related to one or more Groups, and a Group is typically
88             related to one or more Users. See Net::LDAP::Class::User and Net::LDAP::Class::Group for
89             examples.
90              
91             There are some methods which every NLC subclass must implement. See L for details.
92              
93             =head2 init
94              
95             Override this in a subclass. Be sure to call SUPER::init in your subclass.
96              
97             =cut
98              
99             sub init {
100 472     472 1 1376 my $self = shift;
101 472         2868 $self->SUPER::init(@_);
102              
103 472         11118 my $meta = $self->metadata;
104 472 50 33     6250321 if ( !$meta or !$meta->is_initialized ) {
105 0         0 croak
106             "must initialize Metadata class before instantiating a new object";
107             }
108              
109 472   66     12606 $self->{ldap} ||= $self->init_ldap;
110              
111 472 50       1717 if ( !$self->ldap->isa('Net::LDAP') ) {
112 0         0 croak "ldap value is not a Net::LDAP-derived object";
113             }
114              
115 472         6237 return $self;
116             }
117              
118             =head2 metadata_class
119              
120             Returns 'Net::LDAP::Class::Metadata' by default.
121              
122             =cut
123              
124 480     480 1 4237 sub metadata_class {'Net::LDAP::Class::Metadata'}
125              
126             =head2 metadata
127              
128             Returns an instance of the metadata_class() containing all the metadata for
129             the NLC class. May be called as a class or object method.
130              
131             =cut
132              
133             sub metadata {
134 1276     1276 1 8492325 my ($self) = shift;
135              
136             # object method
137 1276 100       5265 if ( ref $self ) {
138 1119   66     11260 return $self->{_meta}
139             ||= $self->metadata_class->for_class( ref $self );
140             }
141              
142             # class method
143 157   66     3002 return $Net::LDAP::Class::Metadata::Objects{$self}
144             || $self->metadata_class->for_class($self);
145             }
146              
147             =head2 init_ldap
148              
149             If you do not pass a Net::LDAP object to new(), you may instead
150             set the ldap_uri() class method to a URI string and
151             init_ldap() will create a Net::LDAP object and bind() it for you.
152              
153             Returns a Net::LDAP object.
154              
155             =cut
156              
157             sub init_ldap {
158 0     0 1 0 my $self = shift;
159 0 0       0 if ( !$self->ldap_uri ) {
160 0         0 croak "must set ldap_uri() or override init_ldap()";
161             }
162              
163 0 0       0 my $ldap = Net::LDAP->new( $self->ldap_uri )
164             or croak "can't create new Net::LDAP: $!";
165 0 0       0 my $msg = $ldap->bind() or croak "can't do anonymous LDAP bind: $!";
166 0 0       0 if ( $msg->code ) {
167 0         0 croak "LDAP bind failed: " . $self->get_ldap_error($msg);
168             }
169 0         0 return $ldap;
170             }
171              
172             =head2 init_debug
173              
174             Sets the default debug flag to whatever the PERL_DEBUG or LDAP_DEBUG
175             env variable is set to.
176              
177             =cut
178              
179 649 50   649 1 15635 sub init_debug { $ENV{PERL_DEBUG} || $ENV{LDAP_DEBUG} }
180              
181             =head2 init_ldap_entry
182              
183             Returns undef by default.
184              
185             =cut
186              
187 1416     1416 1 23736 sub init_ldap_entry { return undef }
188              
189             =head2 get_ldap_error( I )
190              
191             Stringify the error message for the I object.
192              
193             =cut
194              
195             sub get_ldap_error {
196 0     0 1 0 my $self = shift;
197 0 0       0 my $msg = shift or croak "ldap_msg required";
198 0         0 my $str = "\n"
199             . join( "\n",
200             "Return code: " . $msg->code,
201             "Message: " . $msg->error_name,
202             " :" . $msg->error_text,
203             "MessageID: " . $msg->mesg_id,
204             "DN: " . $msg->dn,
205             ) . "\n";
206 0         0 return $str;
207             }
208              
209             =head2 stringify
210              
211             Returns the first unique attribute value that is not undef. If no
212             such value is found, returns the object.
213              
214             By default all NLC-derived objects are overloaded with this method.
215              
216             =cut
217              
218             sub stringify {
219 0     0 1 0 my $self = shift;
220 0         0 for my $key ( @{ $self->unique_attributes } ) {
  0         0  
221 0         0 my $val = $self->$key;
222 0 0       0 return $val if defined $val;
223             }
224 0         0 return $self;
225             }
226              
227             =head2 find( I )
228              
229             Returns array (or array ref if called in scalar context)
230             of objects matching I.
231              
232             I may include:
233              
234             =over
235              
236             =item ldap
237              
238             If not present, the ldap() method is called instead.
239              
240             =item base_dn
241              
242             If not present, the base_dn() method is called instead.
243              
244             =back
245              
246             Any other I are passed directly to the Net::LDAP search()
247             method.
248              
249             Returns undef if no results matching I are found.
250            
251             =cut
252              
253             sub find {
254 86     86 1 231 my $self = shift;
255 86   66     515 my $class = ref($self) || $self;
256 86         574 my %opts = @_;
257 86   66     595 my $ldap = delete $opts{ldap} || $self->ldap;
258              
259 86 50       646 if ( !$ldap ) {
260 0         0 croak "Net::LDAP object required";
261             }
262              
263 86   33     367 my $base = delete $opts{base_dn} || $self->base_dn;
264              
265 86 50       247 if ( !$base ) {
266 0         0 croak "must indicate base_dn in opts or call as object method";
267             }
268              
269 86   33     693 my $attr = delete $opts{attrs} || $self->attributes;
270              
271 86         624 my $msg = $ldap->search(
272             base => $base,
273             attrs => $attr,
274             %opts,
275             );
276              
277 86 50       5124180 if ( $msg->code ) {
278 0         0 croak $self->get_ldap_error($msg);
279             }
280              
281 86         1785 my @results;
282              
283 86         679 for my $entry ( $msg->entries() ) {
284 52         1247 push(
285             @results,
286             $class->new(
287             ldap => $ldap,
288             ldap_entry => $entry,
289             )
290             );
291             }
292              
293 86 100       1277 return unless @results;
294 50 100       765 return wantarray ? @results : \@results;
295             }
296              
297             =head2 create
298              
299             Write a new object to the database. Calls the action_for_create() method -- see L.
300              
301             =cut
302              
303             sub create {
304 59     59 1 3202852 my $self = shift;
305 59 50       426 unless ( $self->check_unique_attributes_set ) {
306 0         0 croak
307             "at least one unique attribute must be set in order to create()";
308             }
309 59 50       513 my @action = $self->action_for_create(@_) or return;
310 59         409 $self->do_batch(@action);
311 59 50       475 $self->read or croak "cannot read newly created $self";
312 59         1180 return $self;
313             }
314              
315             =head2 read
316              
317             Read an object's attribute values from the database. You must have
318             previously set at least one unique attribute in the object
319             in order for the read() to work.
320              
321             Returns the object on success, undef if the object was not found.
322              
323             =cut
324              
325             sub read {
326 183     183 1 2866 my $self = shift;
327 183         540 my %opts = @_;
328              
329 183         333 my ( $filter, $value );
330              
331 183 50 33     2325 if ( !$opts{filter} && !$opts{value} ) {
332              
333 183 50       855 unless ( $self->check_unique_attributes_set ) {
334 0         0 croak "cannot read() without unique attribute set. "
335             . "Unique attributes include: "
336 0         0 . join( ', ', @{ $self->unique_attributes } );
337             }
338              
339             # get first unique key set for filter
340              
341 183         410 for my $key ( @{ $self->unique_attributes } ) {
  183         671  
342 197 100       1090 if ( defined $self->$key ) {
343 183         346 $filter = $key;
344 183         706 $value = $self->$key;
345 183         576 last;
346             }
347             }
348              
349             }
350             else {
351 0         0 $filter = delete $opts{filter};
352 0         0 $value = delete $opts{value};
353             }
354              
355 183 50       677 if ( !defined $filter ) {
356 0         0 croak "could not find a unique filter to read() on";
357             }
358 183 50       654 if ( !defined $value ) {
359 0         0 croak "could not find a unique value to read() on";
360             }
361              
362 183   66     1808 my $base_dn = delete $opts{base_dn} || $self->base_dn;
363              
364 183 50       871 $self->debug && warn "read() within $base_dn : $filter=$value\n";
365              
366 183         1096 my $msg = $self->ldap->search(
367             base => $base_dn,
368             scope => "sub",
369             filter => "($filter=$value)",
370             attrs => $self->attributes,
371             );
372              
373 183 50       11230436 if ( $msg->count() > 0 ) {
374 183 50       5197 carp "$filter $value exists" if $self->debug;
375              
376 183         1242 my $entry = $msg->entry(0);
377              
378             # set any entry attributes we've got cached in $self
379 183         8638 for my $attr ( keys %{ $self->{_not_yet_set} } ) {
  183         1522  
380              
381 204         1129 my $new = $self->{_not_yet_set}->{$attr};
382 204   50     1916 my $old = $entry->get_value($attr) || '';
383 204 100       12493 if ( $new ne $old ) {
384 21         175 $entry->replace( $attr,
385             delete $self->{_not_yet_set}->{$attr} );
386 21         1057 $self->{_was_set}->{$attr}->{new} = $new;
387 21         165 $self->{_was_set}->{$attr}->{old} = $old;
388             }
389             else {
390 183         1318 delete $self->{_not_yet_set}->{$attr};
391             }
392              
393             }
394              
395             # this will cause any existing entry to be DESTROYed
396 183         1196 $self->ldap_entry($entry);
397              
398 183         6205 return $self;
399             }
400             else {
401 0         0 return;
402             }
403             }
404              
405             =head2 update
406              
407             Write changes to the database. Calls action_for_update() -- see L.
408              
409             If no changes are detected, aborts and returns undef.
410              
411             On successful write, returns the value of read().
412              
413             =cut
414              
415             sub update {
416 8     8 1 1070 my $self = shift;
417 8         49 $self->check_unique_attributes_set;
418 8 50       37 unless ( $self->ldap_entry ) {
419 0         0 croak "can't update() without first having a Net::LDAP::Entry loaded";
420             }
421 8 50       162 my @action = $self->action_for_update(@_) or return;
422              
423             # clear, since action_for_update() has already used them.
424 8         40 $self->{_was_set} = {};
425              
426 8         56 $self->do_batch(@action);
427              
428 8         56 return $self->read;
429             }
430              
431             =head2 delete
432              
433             Remove the object from the database. You must call read() first.
434              
435             Returns the value of do_batch().
436              
437             =cut
438              
439             sub delete {
440 0     0 1 0 my $self = shift;
441 0         0 $self->check_unique_attributes_set;
442 0 0       0 unless ( $self->ldap_entry ) {
443 0         0 croak "can't delete() without having a Net::LDAP::Entry loaded";
444             }
445 0 0       0 my @action = $self->action_for_delete or return;
446 0         0 return $self->do_batch(@action);
447             }
448              
449             =head2 read_or_create
450              
451             Convenience method. If read() returns undef, create() is called.
452             Returns the object in any case.
453              
454             =cut
455              
456             sub read_or_create {
457 0     0 1 0 my $self = shift;
458 0 0       0 if ( !$self->read(@_) ) {
459 0         0 $self->create(@_);
460             }
461 0         0 return $self;
462             }
463              
464             =head2 save
465              
466             Convenience method. If ldap_entry() is set, update() is called.
467             Otherwise, read_or_create() is called. The NLC object is returned
468             in any case.
469              
470             =cut
471              
472             sub save {
473 0     0 1 0 my $self = shift;
474 0 0       0 if ( $self->ldap_entry ) {
475 0         0 $self->update;
476             }
477             else {
478 0         0 $self->read_or_create;
479             }
480 0         0 return $self;
481             }
482              
483             =head2 validate( I, I )
484              
485             Called by MethodMaker every time an attribute is set with
486             a MethodMaker-created method.
487              
488             If validate() returns true, I is set. If validate()
489             returns false, a fatal error is thrown and error() set.
490              
491             This method should be overriden in your subclass to provide
492             schema-specific validation. The default behaviour is a no-op
493             (always returns true).
494              
495             =cut
496              
497             sub validate {
498 105     105 1 341 my ( $self, $attr, $value ) = @_;
499 105 50       441 if ( $self->debug ) {
500 0         0 warn "validate $attr: $value\n";
501             }
502 105         627 return 1;
503             }
504              
505             =head2 do_batch( I )
506              
507             Creates (if batch() is not already set)
508             and runs a Net::LDAP::Batch object, passing it
509             the I to run. Will croak on any error.
510              
511             Returns the Net::LDAP::Batch object on success.
512              
513             =cut
514              
515             sub do_batch {
516 67     67 1 139 my $self = shift;
517 67         187 my @actions = @_;
518 67 50       243 if ( !@actions ) {
519 0         0 warn "no actions to execute\n";
520 0         0 return;
521             }
522 67   33     769 my $batch = $self->batch || Net::LDAP::Batch->new(
523             ldap => $self->ldap,
524             debug => $self->debug,
525             );
526 67 50       24987 if ( $self->debug ) {
527 0         0 warn "Batch: " . Data::Dump::dump($batch);
528             }
529 67         408 $batch->add_actions(@actions);
530 67         17667 $self->prev_batch($batch);
531 67 50       2193 $batch->do or croak $batch->error;
532 67         10182420 return $batch;
533             }
534              
535             =head2 add_to_batch( I )
536              
537             Initializes (if necessary) and adds I
538             to the Net::LDAP::Batch object in batch().
539              
540             =cut
541              
542             sub add_to_batch {
543 0     0 1 0 my $self = shift;
544 0   0     0 my $batch = $self->batch || Net::LDAP::Batch->new(
545             ldap => $self->ldap,
546             debug => $self->debug
547             );
548 0         0 $batch->add_actions(@_);
549 0         0 $self->batch($batch);
550             }
551              
552             =head2 rollback
553              
554             Will call the rollback() method on the Net::LDAP::Batch object returned
555             by batch(). If there is not batch() set, will croak.
556              
557             =cut
558              
559             sub rollback {
560 0     0 1 0 my $self = shift;
561 0 0       0 if ( $self->prev_batch ) {
562 0 0       0 $self->prev_batch->rollback or croak $self->prev_batch->error;
563             }
564             else {
565 0         0 croak "no prev_batch to rollback";
566             }
567 0         0 return 1;
568             }
569              
570             =head2 action_for_create
571              
572             See L.
573              
574             =cut
575              
576             sub action_for_create {
577 0     0 1 0 croak "must override action_for_create()";
578             }
579              
580             =head2 action_for_update
581              
582             See L.
583              
584             =cut
585              
586             sub action_for_update {
587 0     0 1 0 croak "must override action_for_update()";
588             }
589              
590             =head2 action_for_delete
591              
592             See L.
593              
594             =cut
595              
596             sub action_for_delete {
597 0     0 1 0 croak "must override action_for_delete()";
598             }
599              
600             =head2 check_unique_attributes_set
601              
602             Returns true (1) if any unique attribute is set
603             with a defined value.
604              
605             Returns false (0) if no unique attributes are set.
606              
607             =cut
608              
609             sub check_unique_attributes_set {
610 250     250 1 1252 my $self = shift;
611 250         1438 my $uk = $self->unique_attributes;
612 250 50       1722 if ( !ref($uk) eq 'ARRAY' ) {
613 0         0 croak "unique_attributes must be an ARRAY ref";
614             }
615 250         836 for my $key (@$uk) {
616 264 100       1903 if ( defined $self->$key ) {
617 250         1431 return 1;
618             }
619             }
620 0         0 return 0;
621             }
622              
623             =head2 AUTOLOAD
624              
625             Will croak() with a helpful message if you call a method that does
626             not exist. Mostly useful for catching cases where you forget to predefine
627             an attribute.
628              
629             =cut
630              
631             sub AUTOLOAD {
632 0     0   0 my ( $self, @args ) = @_;
633              
634 0         0 my ($attribute) = ( our $AUTOLOAD =~ /([^:]+)$/ );
635              
636             # carp "AUTOLOAD called for "
637             # . ref($self)
638             # . " -> $attribute "
639             # . Data::Dump::dump( \@args );
640              
641 0 0       0 if ( $attribute eq 'DESTROY' ) {
642              
643             #Data::Dump::dump($self);
644 0         0 return;
645             }
646              
647 0         0 croak qq[no such attribute or method "$attribute" defined for package "]
648             . ref($self)
649             . qq[ -- do you need to add '$attribute' to your setup() call?"];
650             }
651              
652             =head2 dump
653              
654             Returns Data::Dump::dump output for the NLC object. Useful for debugging.
655             See also the Net::LDAP::Entry dump() method which can be called on the ldap_entry
656             value.
657              
658             $nlc->dump; # same as Data::Dump::dump( $nlc )
659             $nlc->ldap_entry->dump; # see Net::LDAP::Entry dump() method
660            
661             =cut
662              
663             sub dump {
664 0     0 1 0 my $self = shift;
665 0         0 return Data::Dump::dump($self);
666             }
667              
668             =head2 has_local_changes
669              
670             Convenience method. Returns true if the object has had any values
671             set since the last time it was written to the server.
672              
673             =cut
674              
675             sub has_local_changes {
676 0     0 1 0 my $self = shift;
677 0         0 return scalar keys %{ $self->{_was_set} };
  0         0  
678             }
679              
680             =head2 batch
681              
682             Get/set the Net::LDAP::Batch object for the current transaction. Typically you
683             don't want to mess with this but documented for completeness.
684              
685             =head2 prev_batch
686              
687             Get/set the Net::LDAP::Batch object for the just-completed transaction.
688             Typically you don't want to mess with this but documented for completeness.
689              
690             =cut
691              
692             =head2 act_on_all( I [, I] )
693              
694             Performs I sub reference on all records in LDAP.
695             The I should expect one argument: a Net::LDAP::Class-derived
696             object.
697              
698             act_on_all() operates using Net::LDAP::Control::Paged, performing
699             a search() using a filter based on unique_attributes() and iterating
700             over all matches in groups of (by default) 500. You may set the
701             pager size in I. I should be a hash ref. The following
702             key/value pairs are supported:
703              
704             =over
705              
706             =item page_size
707              
708             Default: 500. Sets the Net::LDAP::Control::Paged size.
709              
710             =item filter
711              
712             Default: unique_atttributes->[0] = '*'
713              
714             Set the filter for the search.
715              
716             =item ldap
717              
718             A Net::LDAP object. B
719              
720             =back
721              
722             Returns the number of Net::LDAP::Class results acted upon.
723              
724             =cut
725              
726             sub act_on_all {
727 1     1 1 21 my $self = shift;
728 1 50       5 my $coderef = shift or croak "coderef required";
729 1   50     24 my $opts = shift || {};
730 1   33     5 my $class = ref($self) || $self;
731              
732 1 50       4 if ( ref $coderef ne 'CODE' ) {
733 0         0 croak "coderef is not a CODE reference";
734             }
735              
736 1   33     9 my $filter = $opts->{filter}
737             || $self->metadata->unique_attributes->[0] . '=*';
738 1   50     7 my $page_size = $opts->{page_size} || 500;
739              
740 1   33     8 my $ldap = $opts->{ldap} || $self->ldap;
741 1         100 my $page = Net::LDAP::Control::Paged->new( size => $page_size );
742 1         85 my $cookie;
743 1         3 my @args = (
744             'base' => $self->metadata->base_dn,
745             'filter' => "($filter)",
746             'attrs' => $self->metadata->attributes,
747             'control' => [$page],
748             );
749              
750 1         2 my $count = 0;
751              
752 1         5 PAGE: while ( my $ldap_search = $ldap->search(@args) ) {
753              
754             # fatal on search error
755 2 50       31322 croak "error searching ldap: ", $self->get_ldap_error($ldap_search)
756             if ( $ldap_search->code );
757              
758 2         29 ENTRY: while ( my $ldap_entry = $ldap_search->shift_entry ) {
759              
760 7         273 $count++;
761              
762 7         22 my $nlc = $class->new(
763             ldap => $ldap,
764             ldap_entry => $ldap_entry
765             );
766              
767 7 50       30 $self->debug and warn sprintf( "%6d %s\n", $count, $nlc );
768              
769 7         18 $coderef->($nlc);
770             }
771              
772             # handle next search page
773 2 50       145 my ($resp) = $ldap_search->control(LDAP_CONTROL_PAGED) or last PAGE;
774 2         377 $cookie = $resp->cookie;
775 2 100       257 if ( !$cookie ) {
776 1         4 last PAGE;
777             }
778 1         5 $page->cookie($cookie);
779              
780             }
781              
782             # be nice to the server and stop the search if we still have a cookie
783 1 50       10 if ($cookie) {
784 0         0 $page->cookie($cookie);
785 0         0 $page->size(0);
786 0         0 $ldap->ldap->search(@args);
787 0         0 croak "LDAP seach ended prematurely.";
788             }
789              
790 1         16 return $count;
791              
792             }
793              
794             =head2 isa_user
795              
796             Convenience method. Just returns shift->isa('Net::LDAP::Class::User').
797              
798             =cut
799              
800             sub isa_user {
801 0     0 1   return shift->isa('Net::LDAP::Class::User');
802             }
803              
804             =head2 isa_group
805              
806             Just like isa_user() but checks the Net::LDAP::Class::Group.
807              
808             =cut
809              
810             sub isa_group {
811 0     0 1   return shift->isa('Net::LDAP::Class::Group');
812             }
813              
814             1;
815              
816             __END__