Code Monkey home page Code Monkey logo

trivial-ldap's Introduction

Introduction

One-two, one-two... is this thing working?

This is Kevin Montuori's trivial-ldap, with a few modifications.

A few years back, I needed some mechanism for querying AD servers, as part of a custom content connector for the FAST ESP search engine. I found trivial-ldap, and was soon using it to good effect.

After having used trivial-ldap for a while, I made some modifications, and asked Kevin to review them, and integrate them if he felt that they added value. Unfortunately, Kevin is too busy to spend time on trivial-ldap, so he graciously let me publish whatever changes I had.

Changes

LDAP Filter Parser

The LDAP filter parser has been rewritten from scratch, using cl-yacc. This makes filter parsing somewhat faster, and should also make it easier to maintain. The downside is one more dependency.

Attribute Naming

The original code used symbols in the current package to name LDAP attributes. This has changed, and all attribute names are now interned in the keyword package. So, something like

(ldap:attr-value *entry* 'cname)

should now be

(ldap:attr-value *entry* :cname)

and so on. Note: this is probably only important when working with an LDAP entry, as that is the only place where we use symbol identity for matching.

Binary Attributes

There was a tacit assumption in the trivial-ldap code that all attributes are UTF-8 encoded strings, while in reality they can be 7-bit ASCII (USASCII), UTF-8 or even binary. There is now a mechanism in place for giving hints to trivial-ldap that certain attributes should be treated as binary values --- such attributes will be returned as lists of (unsigned-byte 8), instead of as unicode strings.

The interface to this mechanism is

(ldap:attribute-binary-p <attribute-name>) => <generalized-boolean>

and

(setf (ldap:attribute-binary-p <attribute-name>) <generalized-boolean>)

Note: Elias Mårtenson has supplied some handy restarts that can be used when it turns out that an attribute cannot be converted to UTF-8 (which, in turn, probably means that it should be treated as binary). See handle-as-binary and handle-as-binary-and-add-known in trivial-ldap.lisp .

List Equivalents

Search filters and values can be specified as lists instead of as strings. This has two advantages:

  • Binary values can be specified (lists of octet values will not be treated as UTF-8 sequences).

  • It is not necessary to build string representations of a filter just to have the filter parser deconstruct it back to the representation that should be natural for Lisp.

  • Values can be specified as octet lists, strings or symbols -- when a symbol is specified, the actual value used is whatever (symbol-name <symbol>) returns.

  • The function #'listify-filter can be used to turn a string filter into an equivalent list representation; this should be useful for experimenting with the list format.

Examples:

(ldap:search *ldap* '(and (= objectclass person) (= cname "rayw")))

(let ((name "rayw"))
    (ldap:search *ldap* `(and (= objectclass person) (= cname ,name))))

Paging Through Results

Support for the LDAP Control Extension "Simple Paged Results" (rfc2696) has been added. It is invoked by setting the :size-limit search parameter to 0 (zero), and setting :paging-size to a positive integer. Note that the server imposes its own restrictions here, so the actual number of results in a batch may be lower than specified.

Apart from setting these two required parameters, the operation of the paging mechanism is wholly transparent: batches are fetched automatically whenever the #'next-search-result method has exhausted all entries in the current batch (assuming that the appropriate parameters have been specified, and that there are actually more results to be fetched.)

Examples:

(and (ldap:search *ldap* '(& (substring samaccountname "ra*") (= objectclass person))
                  :attributes '("1.1") :size-limit 0 :paging-size 500)
     (loop for entry = (ldap:next-search-result *ldap*)
           while entry
           count entry))

trivial-ldap's People

Contributors

lokedhs avatar rwiker avatar shinmera avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

trivial-ldap's Issues

trivial-ldap.lisp contains unsafe DECLAIM

trivial-ldap.lisp contains the following declaim:

(declaim (optimize (speed 3) (safety 1) (debug 1) (compilation-speed 0)))

According to the CLHS, the effects of such a declaration may persist after that file is compiled. In particular, that is the case in Allegro CL and it causes all subsequent compilation to occur with speed > safety, which can be disastrous. :-)

I'm inclined to suggest simply removing the declaration, but other options include (a) using ASDF's facilities to restrict the declaim to specific files or (b) replacing declaim with declares in specific hotspots.

Problem with `ldap:new-entry`

ldap:new-entry seems to create strange entries:

CL-USER> (inspect  (ldap:new-entry "cn=manager,dc=example,dc=com" 
                                   :attrs '((objectclass . organizationalrole))))

The object is a STANDARD-OBJECT of type TRIVIAL-LDAP:ENTRY.
0. DN: "cn=manager,dc=example,dc=com"
1. RDN: "cn=manager"
2. ATTRS: ((:CN "manager") (OBJECTCLASS . ORGANIZATIONALROLE))

First, OBJECTCLASS should be a keyword and not a symbol in the
current package and second, the second entry in the attrs-list is a pair,
not a list (this is from the example.lisp file. Maybe the examples should
be updated?).

RDN-Handling is too trivial

The handling of RDNs, esp. in rdn-from-dn is too trivial. rdn-from-dn breaks on backslash-escaped
characters. Example:

cn=Acme\, Baltimore,c=United States...
Expected: (:cn "Acme, Baltimore")
Got: (:cn "Acme")

and can't handle multi-valued RDNs. Example:

uid=123+gid=456,o=acme
Expoected: ((:uid 123) (:gid 456))
Got: (:uid "123+gid=456")

lispworks without usocket

I'm using the original trivial-ldap.
I modified the code to avoid to use usocket while using lispworks.
It could be usefull to add the modifications to your trivial-ldap.

Here are the modifications

(defpackage :trivial-ldap
(:use :cl-user :common-lisp
#-lispworks :usocket)
...

-lispworks

(defmethod get-stream ((ldap ldap))
"Open a usocket to the ldap server and set the ldap object's slot.
If the port number is 636 or the SSLflag is not null, the stream
will be made with CL+SSL."
(let ((existing-stream (ldapstream ldap)))
(unless (and (streamp existing-stream)
(open-stream-p existing-stream))
(let* ((sock (usocket:socket-connect (host ldap) (port ldap)
:element-type '(unsigned-byte 8)))
(stream
(if (or (sslflag ldap) (= (port ldap) 636))
(cl+ssl:make-ssl-client-stream (usocket:socket-stream sock))
(usocket:socket-stream sock))))
(debug-mesg ldap "Opening socket and stream.")
(setf (ldapsock ldap) sock)
(setf (ldapstream ldap) stream))))
(ldapstream ldap))

-lispworks

(defmethod close-stream ((ldap ldap))
"Close an ldap connection if it is currently open."
(let ((existing-stream (ldapstream ldap))
(existing-sock (ldapsock ldap)))
(when (and (streamp existing-stream) (open-stream-p existing-stream))
(ignore-errors
(setf (ldapstream ldap) nil)
(setf (ldapsock ldap) nil)
(close existing-stream)
(usocket:socket-close existing-sock)))))

+lispworks

(defmethod get-stream ((ldap ldap))
"Open a usocket to the ldap server and set the ldap object's slot.
If the port number is 636 or the SSLflag is not null, the stream
will be made with CL+SSL."
(let ((connection-timeout 20)
(read-timeout 20)
(write-timeout 20)
(existing-stream (ldapstream ldap))
(certificate-path (certificate-path ldap)))
(unless (and (streamp existing-stream)
(open-stream-p existing-stream))
(let* ((ssl-ctx (when (or (sslflag ldap) (= (port ldap) 636))
(comm:make-ssl-ctx :ssl-side :client)))
stream)
(when (and ssl-ctx certificate-path)
#+ignore ;; To test
(comm:ssl-ctx-use-certificate-file ssl-ctx
certificate-path
comm:SSL_FILETYPE_PEM)
)
(setf stream (comm:open-tcp-stream (host ldap) (port ldap)
:element-type '(unsigned-byte 8)
:timeout connection-timeout
:read-timeout read-timeout
:ssl-ctx ssl-ctx
#-:lw-does-not-have-write-timeout
:write-timeout
#-:lw-does-not-have-write-timeout
write-timeout
:errorp t))
(debug-mesg ldap "Opening socket and stream.")
(setf (ldapstream ldap) stream))
))
(ldapstream ldap))

+lispworks

(defmethod close-stream ((ldap ldap))
"Close an ldap connection if it is currently open."
(let ((existing-stream (ldapstream ldap)))
(when (and (streamp existing-stream) (open-stream-p existing-stream))
(ignore-errors
(setf (ldapstream ldap) nil)
(close existing-stream)
))))

Regards

Fazerty

Passing an empty attribute list to LDAP:SEARCH retrieves all attributes

I was using `ldap:search' to figure whether some user exists in a given directory using this code:

(ldap:search conn `(and (= :uid ,some-user)) :attributes nil)

Somewhat unexpectedly, this seems to retrieve all attributes, because I'm getting this error:

Probably a binary field: jpegPhoto
   [Condition of type TRIVIAL-LDAP:PROBABLY-BINARY-FIELD-ERROR]

My workaround is to pass some dummy attribute name to the attributes argument, but it seems like nil should work there. Am I doing something wrong?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.