Merge.
authorMahlon E. Smith <mahlon@martini.nu>
Wed, 07 Jun 2017 14:59:48 -0700
changeset 95 c527f97c28f1
parent 94 4464f9984530 (diff)
parent 90 cc703752780e (current diff)
child 96 e8af5aa8bd15
Merge.
shelldap
--- a/shelldap	Wed Jan 13 11:14:26 2016 -0800
+++ b/shelldap	Wed Jun 07 14:59:48 2017 -0700
@@ -138,6 +138,19 @@
 
 =over 4
 
+=item B<paginate>
+
+Integer.  If enabled, shelldap will attempt to use server side
+pagination to build listings.  Note: if you're using this to avoid
+sizelimit errors, you'll likely need server configuration to raise the
+limits for paginated results.
+
+	--paginate 100
+
+=back
+
+=over 4
+
 =item B<promptpass>
 
 Force password prompting.  Useful to temporarily override cached
@@ -341,7 +354,7 @@
     inspect posixAccount organizationalUnit
     inspect _schema
 
-The output is a list of found objectClasses, their schema heirarchy
+The output is a list of found objectClasses, their schema hierarchy
 (up to 'top'), whether or not they are a structural class, and then
 a merged list of all valid attributes for the given objectClasses.
 Attributes are marked as either required or optional, and whether
@@ -448,15 +461,19 @@
 	LDAP_OTHER
 	LDAP_TIMEOUT
 	LDAP_NO_MEMORY
-	LDAP_CONNECT_ERROR /;
+	LDAP_EXTENSION_PASSWORD_MODIFY
+	LDAP_CONNECT_ERROR
+	LDAP_CONTROL_PAGED /;
 use Net::LDAP::Util qw/ canonical_dn ldap_explode_dn /;
 use Net::LDAP::LDIF;
+use Net::LDAP::Extension::SetPassword;
+use Net::LDAP::Control::Paged;
 use Data::Dumper;
 use File::Temp;
 use Algorithm::Diff;
 use Carp 'confess';
 use base 'Term::Shell';
-require Net::LDAP::Extension::SetPassword;
+
 
 my $conf = $main::conf;
 
@@ -508,6 +525,17 @@
 		print "Cipher in use: ", $self->ldap()->cipher(), "\n";
 	}
 
+	# check for the pagination extension on the server early, and bail
+	# if necessary.
+	if ( $conf->{'paginate'} && $conf->{'paginate'} =~ /^\d+$/ && $conf->{'paginate'} > 0 ) {
+		unless ( $self->{'root_dse'}->supported_control(LDAP_CONTROL_PAGED) ) {
+			die "Server pagination is enabled, but the server doesn't seem to support it.\n";
+		}
+	}
+	else {
+		$conf->{'paginate'} = undef;
+	}
+
 	# try an initial search and bail early if it doesn't work. (bad baseDN?)
 	my $s = $self->search();
 	die "LDAP baseDN error: ", $s->{'message'}, "\n" if $s->{'code'};
@@ -873,7 +901,8 @@
 }
 
 
-### Perform an LDAP search.
+### Perform an LDAP search, optionally with the server side pager
+### control.
 ###
 ### Returns a hashref containing the return code and
 ### an arrayref of Net::LDAP::Entry objects.
@@ -882,11 +911,18 @@
 {
 	my $self = shift;
 	my $opts = shift || {};
+	my $controls = [];
 
 	$opts->{'base'}   ||= $self->base(),
 	$opts->{'filter'} ||= '(objectClass=*)';
 	$opts->{'scope'}  ||= 'base';
 
+	my $pager;
+	if ( $conf->{'paginate'} ) {
+		$pager = Net::LDAP::Control::Paged->new( size => $conf->{'paginate'} );
+		push( @$controls, $pager );
+	}
+
 	my $search = sub { 
 		return $self->ldap->search(
 			base	  => $opts->{'base'},
@@ -894,19 +930,41 @@
 			scope	  => $opts->{'scope'},
 			timelimit => $conf->{'timeout'},
 			typesonly => ! $opts->{'vals'},
-			attrs	  => $opts->{'attrs'} || ['*']
+			attrs	  => $opts->{'attrs'} || ['*'],
+			control   => $controls
 		);
 	};
 
-	my $s = $self->with_retry( $search );
+	my $s;
+	my $entries = [];
+   	my $token  = '-';
+
+	if ( $conf->{'paginate'} ) {
+		while( $token ) {
+			$s = $self->with_retry( $search );
+			push( @$entries, $s->entries() );
+
+			my $page_response = $s->control( LDAP_CONTROL_PAGED ) or last;
+			$token = $page_response->cookie;
+			$pager->cookie( $token );
+		}
+	}
+	else {
+		$s = $self->with_retry( $search );
+		$entries = [ $s->entries() ];
+	}
+
 	my $rv = {
 		code	=> $s->code(),
-		message => $s->error(),
-		entries => []
+		message => $s->error()
 	};
 
-	$rv->{'entries'} =
-	  $opts->{'scope'} eq 'base' ? [ $s->shift_entry() ] : [ $s->entries() ];
+	if ( $opts->{'scope'} eq 'base' ) {
+		$rv->{'entries'} = [ $s->shift_entry() ]
+	}
+	else {
+		$rv->{'entries'} = $entries;
+	}
 
 	return $rv;
 }
@@ -2116,8 +2174,7 @@
 
 	$self->{'root_dse'} ||= $self->ldap->root_dse();
 
-	my $pw_extension = '1.3.6.1.4.1.4203.1.11.1';
-	unless ( $self->{'root_dse'}->supported_extension( $pw_extension ) ) {
+	unless ( $self->{'root_dse'}->supported_extension(LDAP_EXTENSION_PASSWORD_MODIFY) ) {
 		print "Sorry, password changes not supported by LDAP server.\n";
 		return;
 	}
@@ -2275,7 +2332,7 @@
 }
 
 
-### Recursively walk an objectClass heirarchy, returning an array
+### Recursively walk an objectClass hierarchy, returning an array
 ### of inheritence.
 ###
 sub findall_supers
@@ -2303,7 +2360,7 @@
 use warnings;
 
 $0 = 'shelldap';
-my $VERSION = '1.3.2';
+my $VERSION = '1.4.0';
 
 use Getopt::Long;
 use YAML::Syck;
@@ -2322,6 +2379,7 @@
 	'binddn|D=s',
 	'basedn|b=s',
 	'cacheage=i',
+	'paginate=i',
 	'promptpass|W',
 	'timeout=i',
 	'sasl|Y=s',