Net::LDAP filters

Hello

Excuse me if this is a dumb question but I searched the docs and
couldn’t find a way to do this… Is there any method that converts an
LDAP filter string to a filter object to be used in #search, ie,
something like

“uid=foo” being understood as Net::LDAP::Filter.eq(“uid”, “foo”)

?

Thanks in advance,
Andre

I assume by “LDAP filter string” you mean an RFC-2254 compliant string,
correct?
Net::LDAP intentionally avoided supporting this kind of textual
representation of filters, partly because they’re ugly and partly
because
they don’t map to anything in the LDAP wire protocol.

However, we’ve thought about adding methods that would convert
Net::LDAP::Filters to and from RFC-2254 strings. Would that help you? Is
it
because you have these strings lying around in existing code? Are you
trying
to port from an application that uses the native-LDAP wrapper library?

Hi Francis

On Mon, 2006-05-15 at 22:37 +0900, Francis C. wrote:

However, we’ve thought about adding methods that would convert
Net::LDAP::Filters to and from RFC-2254 strings. Would that help you? Is it
because you have these strings lying around in existing code? Are you trying
to port from an application that uses the native-LDAP wrapper library?

I’m writing a script to allow me to work with ldap without having to
deal with the ldap* tools that come with openldap and are a pain to use.
So what I need is some way to let the user provide search filters from
the command line, and these would ideally be RFC-2254 strings, since
that’s what people are used to deal with, despite being ugly…

These conversion methods would surely be very useful for that.

Regards,
Andre

I’ll tell you what: if we add these methods and send you a
pre-release, would you be willing to beta-test them against some of
your own strings?

Ok, will email you when done.

On Mon, 2006-05-15 at 23:20 +0900, Francis C. wrote:

I’ll tell you what: if we add these methods and send you a
pre-release, would you be willing to beta-test them against some of
your own strings?

Sure, I’d be glad to test it!

Thanks,
Andre

Andre, I have a new gem for you to test. It has version 0.0.1.1, which
we
won’t release on Rubyforge. I’m going to email the gem directly to you
to
avoid polluting this mailing-list, but if you can confirm this does what
you
want, then I’ll release it for everyone as version 0.0.2.

Net::LDAP::Filter now has the class method #from_rfc2254. (Ugly name,
may
change it.)

Instead of:
f = Net::LDAP::Filter.eq( “uid”, “george*” )
you can now say:
f = Net::LDAP::Filter.from_rfc2254( “uid=george*”)

All the compound expressions and conditionals are also supported, so do
some
testing and let me know if it’s what you needed.

Andre, thanks for testing and confirming the fix. It will be in the
next release of Net::LDAP. We added a class method to
Net::LDAP::Filter called #from_rfc2254, which takes a standard LDAP
filter string and returns a Net::LDAP::Filter object. We may rename it
to something more graceful before the release.

I’ll think about your suggestion that we provide the same
functionality as an overload to Net::LDAP::Filter#new.

Regarding #search: this method currently does return a full result set
(an array of the entries returned by the search) whether or not you
provide a block. If you provide a block, then each entry is also
passed to the block. I think this is the flexibility you were looking
for in your question.

The reason we provided the :return_result flag to #search is so you
can turn off the generation of a result-set array. The flag defaults
to true, meaning the default behavior is both to return a result set
and to call a user-supplied block (if present) for each result. If you
set :return_result to false, then the results will only be passed to
your block, and discarded by Net::LDAP after each one is processed.
The point of being able to turn off the result set is to save memory
when the set is very large. The performance improvement is potentially
very large.

Thanks again for the testing you did.

On Wed, 2006-05-17 at 04:14 +0900, Francis C. wrote:

The point of being able to turn off the result set is to save memory
when the set is very large. The performance improvement is potentially
very large.

Yeah, I understand that. What I suggested was that the decision to
return the full result set can depend only on wether a block is passed
or not, like

results = ldap.search(…) # no block, return full result set

or

ldap.search(…) { |entry| … } # a block was given, return nil
# (like File#open, for example)

That is, only return the result set if no block is given. I suggested
this because when you process each entry on the block you usually don’t
use the result set returned anyway. Unless there’s some situation where
you need to do

results = ldap.search(…) { |entry| … }

then I see why the flag is useful, but I couldn’t think of a situation
where I’d need that.

Thanks again for the testing you did.

Thank you for the great lib and the quick support!

Andre

Ah, I see the point. Interesting. I guess you’re right, you probably
wouldn’t need the array if you were supplying a block! Maybe we’ll keep
the
:return_result flag but default it to false when a block is given.

Did you have any other problems with the unreleased version of the
library
that I sent you? There’s actually a lot of new behavior in there that
hasn’t
been documented yet :-). (We’re trying to make the LDAP API a lot more
streamlined and intuitive.)

On Wed, 2006-05-17 at 04:37 +0900, Francis C. wrote:

Did you have any other problems with the unreleased version of the library
that I sent you? There’s actually a lot of new behavior in there that hasn’t
been documented yet :-). (We’re trying to make the LDAP API a lot more
streamlined and intuitive.)

No problems here, but I only did testing with #search and #replace.
Everything worked fine.

Andre

LDAP attribute names are usually not case-sensitive. I had a couple of
reasons for handling those the way I did, but I can be persuaded
otherwise!

On Wed, 2006-05-17 at 04:47 +0900, Andre N. wrote:

On Wed, 2006-05-17 at 04:37 +0900, Francis C. wrote:

Did you have any other problems with the unreleased version of the library
that I sent you? There’s actually a lot of new behavior in there that hasn’t
been documented yet :-). (We’re trying to make the LDAP API a lot more
streamlined and intuitive.)

No problems here, but I only did testing with #search and #replace.
Everything worked fine.

I’ve noticed that the attribute names are downcased when they’re
returned in a search result (for example, ‘shadowFlag’
becomes :shadowflag). Couldn’t this cause problems with duplicate
attribute names? (I’m assuming LDAP is case-sensitive but I actually
don’t know).

Andre

I addressed that in a different way, which may or may not be ideal. The
object which comes back from #search has methods defined on it to match
the
attributes that were returned. And the method names are
case-insensitive.
You ought to able to say result.shadowFlag and get the result you
expect.
Try it.

I thought about it some more and I realized you’re right. The original
motivation for returning canonical attribute names was that there’s no
guaranteeing what an LDAP server will actually return (in regard to case
of
attribute names), so I figured it would be least surprising to the
programmer to get back a standard form (all lower-case). That probably
does
make sense when you don’t specify which attributes you want back. But it
doesn’t make any sense otherwise, especially given that a core goal of
Net::LDAP is to hide the messiness of the LDAP wire protocol. I’m going
to
change the code accordingly, and I appreciate your suggestion.

On Thu, 2006-05-18 at 04:54 +0900, Francis C. wrote:

LDAP attribute names are usually not case-sensitive. I had a couple of
reasons for handling those the way I did, but I can be persuaded
otherwise!

Well, my only argument is that if I search for something, I’d expect
that the return result to include the same attribute name that I
searched for.

It’s nothing overly important, I was just writing some unit tests and
was surprised to see that results[:shadowFlag] was nil, so I had to dump
the whole results to find out the key is actually :shadowflag.

Andre

On Thu, 2006-05-18 at 06:07 +0900, Francis C. wrote:

I’m going to
change the code accordingly, and I appreciate your suggestion.

Thanks! (again! :slight_smile:

Andre