Optionally use the server side pager control for search results.

FossilOrigin-Name: 80a69ef0eaf294692b133e14c1611e6c60373da11c2edeae1d2554dbdb7b1bf3
This commit is contained in:
Mahlon E. Smith 2017-06-07 21:38:07 +00:00
parent 08ee88750d
commit 2a068606e6

View file

@ -138,6 +138,19 @@ try and ask the server for a sane default.
=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
@ -448,15 +461,17 @@ use Net::LDAP qw/
LDAP_OTHER
LDAP_TIMEOUT
LDAP_NO_MEMORY
LDAP_CONNECT_ERROR /;
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 +523,16 @@ sub init
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 ) {
my $has_pagination = ( grep $_ eq LDAP_CONTROL_PAGED, $self->{'root_dse'}->get_value('supportedControl') );
die "Server pagination is enabled, but the server doesn't seem to support it.\n" unless $has_pagination;
}
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 +898,8 @@ sub display
}
### 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 +908,18 @@ sub search
{
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 +927,41 @@ sub search
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;
}
@ -2322,6 +2377,7 @@ Getopt::Long::GetOptions(
'binddn|D=s',
'basedn|b=s',
'cacheage=i',
'paginate=i',
'promptpass|W',
'timeout=i',
'sasl|Y=s',
@ -2350,7 +2406,6 @@ if ( $conf->{'configfile'} ) {
while ( my ($k, $v) = each %{$conf} ) { $conf->{ $k } = $v }
}
# defaults
$conf->{'configfile'} ||= "$ENV{'HOME'}/.shelldap.rc";
$conf->{'cacheage'} ||= 300;