Subject: | Enhancement request: add support for referrals and references |
I am using Net::LDAP::SimpleServer as a back-end server for testing of an LDAP client, and one of the features of the latter that I need to test is following referrals and references returned from the server. That is not currently supported by this module but it is I think a useful feature to have here, so this is an enhancement request to add it, with a patch for that.
This patch enables returning references and/or referrals based on standard "named subordinate reference" entries in the data store (entries with objectClass 'referral' and one or more 'ref' attributes giving the referral URIs - as per RFC 3296 section 2).
In regard to when to return a referral result, RFC 4511 says "the referral result code indicates that the contacted server cannot or will not perform the operation and that one or more other servers may be able to" and that reasons for this include "The target entry of the request is not held locally, but the server has knowledge of its possible existence elsewhere". So to try to follow that within the scope of how this Net::LDAP::SimpleServer module operates, my patch returns them as follows:
If the search found nothing (other than referral entries) and a baseObject was given and that matches a referral entry or falls in sub-tree scope under one,
and there is no non-referral entry for the baseObject, then an LDAP_REFERRAL result is returned with the URI(s) from that referral entry.
Otherwise, if there are any referral entries in the search scope then references are returned for those, inserting searchResRef entries into the search results in addition to any search results from matching other entries in the data store. Note that for this Net::LDAP::Reference objects are returned to Net::LDAP::Server from search().
The patch is attached - note that it requires the patch from issues 101365 (https://rt.cpan.org/Public/Bug/Display.html?id=101365) and 101382 (https://rt.cpan.org/Public/Bug/Display.html?id=101382) to be applied first. To apply the patch during installation, after unpacking Net-LDAP-SimpleServer-0.0.17.tar.gz, cd into the Net-LDAP-SimpleServer-0.0.17 directory and type:
- patch -p1 <path-to-it/add-srch-scope.patch (see issue 101365)
- patch -p1 <path-to-it/add-user-pwds.patch (see issue 101382)
- patch -p1 <path-to-it/add-refs.patch
Then continue the install as normal.
For references (not referrals) this also requires the fix for issue 101261 be applied in Net::LDAP::Server.
Subject: | add-refs.patch |
diff -Naur Net-LDAP-SimpleServer-0.0.17-3/lib/Net/LDAP/SimpleServer/ProtocolHandler.pm Net-LDAP-SimpleServer-0.0.17/lib/Net/LDAP/SimpleServer/ProtocolHandler.pm
--- Net-LDAP-SimpleServer-0.0.17-3/lib/Net/LDAP/SimpleServer/ProtocolHandler.pm 2015-01-07 11:01:32.627040224 -0800
+++ Net-LDAP-SimpleServer-0.0.17/lib/Net/LDAP/SimpleServer/ProtocolHandler.pm 2015-01-07 11:24:59.970168693 -0800
@@ -18,7 +18,8 @@
use Net::LDAP::Constant (
qw/LDAP_SUCCESS LDAP_AUTH_UNKNOWN LDAP_INVALID_CREDENTIALS/,
- qw/LDAP_AUTH_METHOD_NOT_SUPPORTED/ );
+ qw/LDAP_AUTH_METHOD_NOT_SUPPORTED/,
+ qw/LDAP_REFERRAL/ );
use Scalar::Util qw{reftype};
use UNIVERSAL::isa;
@@ -30,11 +31,18 @@
my $dn = shift || '';
my $msg = shift || '';
- return {
+ my $result = {
matchedDN => $dn,
errorMessage => $msg,
- resultCode => $code,
+ resultCode => $code
};
+
+ if ($code == LDAP_REFERRAL) {
+ my $rfrl = shift;
+ $result->{referral} = $rfrl;
+ }
+
+ return $result;
}
sub new {
@@ -198,6 +206,86 @@
return $vals;
}
+sub _has_class {
+ my ( $elem, $class ) = @_;
+
+ my $classes = _get_attr($elem, 'objectClass');
+ if ($classes) {
+ foreach my $cl (@{$classes}) {
+ return 1 if (uc($cl) eq uc($class));
+ }
+ }
+
+ return 0;
+}
+
+sub _check_refs {
+ my ( $self, $list, $res, $basedn ) = @_;
+
+ # This takes care of returning referrals or references based on "named
+ # subordinate reference" entries in the data store (see RFC 3296 section 2)
+ # as follows:
+ # - if the search found nothing (other than ref entries) and a baseObject
+ # is given and that matches a ref entry or falls in sub-tree scope
+ # under one, and there is no non-ref entry for the baseObject, then an
+ # LDAP_REFERRAL result is returned with the URIs from that ref entry.
+ # - otherwise, if there are any reference entries in the search scope
+ # then searchResRef (Net::LDAP::Reference) entries for them are
+ # inserted into the results to return.
+
+ my $refs_filter = Net::LDAP::Filter->new("(&(objectClass=referral)(ref=*))");
+ my $refs = _match( $refs_filter, $list );
+ my $rfrl = 0;
+ my $result = 0;
+ if ($#{$refs} == $#{$list}) {
+ # The search found nothing other then ref entries,
+ # so should we return a referral?
+ if (defined($basedn)) {
+ my $base = _find($basedn, $self->{store}->list);
+ if ($base && _has_class($base, 'referral')) {
+ $refs = [ $base ];
+ $rfrl = 1;
+ } elsif (!$base) {
+ my $all_refs = _match($refs_filter, $self->{store}->list);
+ my @use_refs = ();
+ foreach my $ref (@{$all_refs}) {
+ if (_in_scope(uc($basedn), uc($ref->{asn}->{objectName}), 'subTree')) {
+ push(@use_refs, $ref);
+ }
+ }
+ if (scalar(@use_refs) > 0) {
+ $refs = \@use_refs;
+ $rfrl = 1;
+ }
+ }
+ }
+ }
+ if ($rfrl) {
+ # Will return a referral: collect the URIs
+ my @ref_uris = ();
+ foreach my $ref (@{$refs}) {
+ my $ref_vals = _get_attr($ref, 'ref');
+ if ($ref_vals && scalar(@{$ref_vals}) > 0) {
+ push(@ref_uris, @{$ref_vals});
+ }
+ }
+ if (scalar(@ref_uris) > 0) {
+ $result = _make_result(LDAP_REFERRAL, "", "", \@ref_uris);
+ }
+ } else {
+ # Add in a reference entry for any refs in the search scope
+ foreach my $ref (@{$refs}) {
+ my $ref_vals = _get_attr($ref, 'ref');
+ if ($ref_vals && scalar(@{$ref_vals}) > 0) {
+ my $ref_entry = { 'asn' => $ref_vals };
+ push(@{$res}, bless $ref_entry, 'Net::LDAP::Reference');
+ }
+ }
+ }
+
+ return $result;
+}
+
sub search {
my ( $self, $request ) = @_;
@@ -252,6 +340,13 @@
}
}
+ # Check for references in the list (filtered on $basedn/$scope) from LDIF:
+ my $rslt = _check_refs($self, $list, $res, $basedn);
+ if ($rslt && $rslt->{resultCode} == LDAP_REFERRAL) {
+ print STDERR "Referral: " . Dumper($rslt);
+ return $rslt;
+ }
+
#print STDERR Dumper($res);
return ( _make_result(LDAP_SUCCESS), @{$res} );
@@ -318,6 +413,33 @@
The search filter, baseObject and scope are supported, with other search fields
currently being ignored.
+Referrals and references are supported via referral entries in the data store,
+as follows:
+
+=over 4
+
+=item *
+
+The reference/referral entry must have objectClass 'referral' and one or more
+'ref' attributes giving the referral URIs (as per RFC 3296 section 2).
+
+=item *
+
+If the search found nothing (other than referral entries) and a baseObject was
+given and that matches a referral entry or falls in sub-tree scope under one,
+and there is no non-referral entry for the baseObject, then an LDAP_REFERRAL
+result is returned with the URI(s) from that referral entry.
+
+=item *
+
+Otherwise, if there are any referral entries in the search scope then
+references are returned for those, inserting searchResRef entries into the
+search results in addition to any search results from matching other entries
+in the data store. Note that for this L<Net::LDAP::Reference> objects are
+returned to L<Net::LDAP::Server> from search().
+
+=back
+
=head1 SEE ALSO
Please see those modules/websites for more information related to this module.