### TRIVIAL-LDAP v0.91

TRIVIAL-LDAP is a one file, all Common Lisp client implementation of parts of RFC 2261.

#### WARNING:

This is beta software. While I believe the API documented below is correct it is subject to change. Also, do not run execute the example.lisp program against a production LDAP directory, it will delete your entries.

#### Introduction

This LDAP client came about as a result of some functionality I required for a different project altogether. As a result it provides functionality that is general enough but probably not in typical low-level API fashion. (For instance, a "real" LDAP library would probably tackle the BER encoding separately.) However, as there is a lack of Common Lisp LDAP clients out there I thought I'd share.

I am open to both requests for features and suggestions for improving my Lisp code. There are features I will be implementing in the short term but for the sake of not creating false expectations neither a comprehensive list nor timeframe is available.

You can reach me at montuori@gmail.com.

#### Changes

2009-08-16 (v0.91)
  Applied a patch supplied by Nick Dannenberg to fix UTF-8 handling for SBCL and CCL. My sincerest appreciation for taking the time to generate and send the patch along!
2009-03-12 (v0.90)
  Applied patches kindly provided by Slawek Zak.
  - Enable UTF-8 encoding for Allegro.
  - Fixed a compilation failure on Allegro where a number of constants were not properly defined.
  - Fixed the dosearch macro for cases where the LDAP variable is lexical.
  Thanks to Slawek for taking the time to send the patches!
2007-01-12 (v0.80)
  - Converted from trivial-socket to usocket: indications were that trivial-socket has been deprecated.
  - Added support for SSL with CL+SSL. Setting the ldap object slot `sslflag` to T or the `port` slot to 636 will force an encrypted connection. Do note that this is not TLS, just ldaps:// ... I don't require TLS; if you do, drop me a line.
  - Added functionality to ease the pain of short slapd idletimeouts. The ldap object slot `reuse-connection` may be set to NIL (the default) to not attempt reopening connections, T to reopen the connection before each request, or TRIVIAL-LDAP:REBIND if the connection should be opened and a bind message sent before each request (except, of course, the bind request itself).
  - A couple of documentation and logic bugs were corrected

#### Acknowledgments

I would like to thank Zach Beane for his many helpful comments and suggestions. Thanks also to Ed Symanzik for pointing out a number inconsistencies in the code and documentation.

Also, a nod to my client, Brandeis University, for not fretting too much when I break out emacs + slime to administrate their LDAP directories.

#### License

TRIVIAL-LDAP is distributed under the Clarified Artistic License, a copy of which is included in the downloadable package.

#### Requirements

TRIVIAL-LDAP has been tested under OpenMCL 1.0 (OS X/PPC), SBCL 0.9.7 (OS X/PPC), and SBCL 1.0 (OS X/Intel).

I would assume any CL that is supported by usockets and CL+SSL would have no issues with this code. If you encounter problems let me know.

Two external packages, usocket and CL+SSL, are required. CL+SSL itself requires trivial-gray-streams, flexi-streams, and cffi. These may be downloaded from:

- http://common-lisp.net/project/usocket/
- http://common-lisp.net/project/cl-plus-ssl/
- http://common-lisp.net/project/cffi/
- http://weitz.de/flexi-streams/

The trivial-gray-streams project is part of the CL+SSL project. (Note: to get CL+SSL working on OS X/Intel it was necessary to re-compile openssl with the -m64 flag.)

#### Limitations

Missing functionality, as specified by RFC 2251, includes:

- UTF-8 is not implemented for all CL implementations
- SASL authentication is not implemented
- controls are not implemented
- extended DN searches are not implemented
- referrals are not followed
- extended request/response is not implemented

I do not require this functionality myself, but if there was interest I would consider augmenting TRIVIAL-LDAP with some of this missing functionality.

#### API

LDAP object and associated methods.

new-ldap &key (host "localhost") (port 389) user pass base debug [function]
  Return an LDAP object.
host ldap-object [method]
  Get/set the host slot value of ldap-object. Slot value will be a string.
port ldap-object [method]
  Get/set the port slot value of ldap-object. Slot value will be an integer. If the sslflag slot is NIL, defaults to 389; if sslflag slot is T, defaults to 636. If the port parameter is manually set to 636 an SSL connection is used unless the sslflag is explicitly set to nil.
user ldap-object [method]
  Get/set the user DN to bind as. Slot value will be a string.
pass ldap-object [method]
  Get/set the simple password to bind with. Slot value will be a string.
base ldap-object [method]
  Get/set the default base DN for searches. Slot value will be a string.
debug ldap-object [method]
  Get/set the debug flag. Slot value will be T or NIL. When value is T debug output will be written to *debug-io*.
sslflag ldap-object [method]
  Get/set the SSL flag. When T the default port is 636 and a CL+SSL stream is used. Defaults to nil. Note that if the port slot is explicitly set to 636 an SSL connection is used unless the sslflag slot is set to nil explicitly.
reuse-connection ldap-object [method]
  Get/set the reuse-connection slot parameter. When T re-open the stream before each reqest; when LDAP:REBIND open the stream and send an (ldap:bind ldap-object) message; when nil, don't do anything special. Set to NIL by default.

Entry object and associated methods

new-entry dn &key (attrs ()) (infer-rdn t) [function]
  Return a new entry object with the DN specified. `attrs`, if specified, will be an alist of attribute/value pairs. If `infer-rdn` is T the RDN attribute and value will be appended to the attribute list.
dn entry-object [method]
  Return the DN value for entry-object.
rdn entry-object [method]
  Return the RDN value for entry-object
change-rdn entry-object new-rdn [method]
  Change the RDN (and therefore DN) of an entry-object. The RDN attribute and value will be updated in the attribute list. No LDAP directory transactions will take place.
attr-list entry-object [method]
  Return the list of attribute symbols belonging to entry-object
attr-value entry-object attr [method]
attr-value entry-object attr-list [method]
  Return a list of values associated with the attribute `attr`. Return NIL if the attribute does not exist. If a list of attributes is passed, return a list of lists of values.
add-attr entry-object attr values [method]
  Add the specified attribute with the value or values specified to the entry object's attribute list. Returns the entire list of attributes/values. No LDAP directory transactions will take place.
del-attr entry-object attr &optional values [method]
  Delete the specified attribute (or values for attribute) from the entry object's attribute list. Returns the entire list of attributes/values. No LDAP directory transactions will take place.
replace-attr entry-object attribute values [method]
  Replace current attribute values with values specified. Returns the entire list of attributes/values. No LDAP directory transactions will take place.
ldif entry-object [method]
  Return an LDIF representation of entry (as a string).

LDAP Commands

NB: Methods below that are specialized first on ldap-object will return three values: T/NIL indicating success or failure, the LDAP response code (these are exported from the TRIVIAL-LDAP package as symbols), and third, any response message received from the LDAP directory server (as a string).

Methods specialized first on entry-object will return T (or T/NIL in the case of compare) and will throw an ldap-response-error if a response from the LDAP directory server is other than succesful.

dn-or-entry can be either an entry-object or a DN string. dn is a string. Generally if the method only requires the DN either the entry or the DN will be acceptable arguments.

bind ldap-object [method]
  Send a bind request to the LDAP directory server.
unbind ldap-object [method]
  Send an unbind request to the LDAP directory server and close the stream.
abandon ldap-object [method]
  Send an abandon request to the LDAP directory server and purge any data on the stream waiting to be read.
add ldap-object entry-object [method]
add entry-object ldap-object
  Add an entry described by entry-object to the directory specified by ldap-object.
delete ldap-object dn-or-entry [method]
delete entry-object ldap-object
delete dn ldap-object
  Delete entry from directory specified by ldap-object. `dn-or-entry` may be an entry object or a DN string.
moddn ldap-object dn-or-entry new-rdn &key delete-old new-sup [method]
moddn entry-object ldap-object new-rdn &key delete-old new-sup
moddn dn ldap-object new-rdn &key delete-old new-sup
  Modify the RDN specified by `dn-or-entry` to the replacement RDN `new-rdn`. `dn-or-entry` may be either an entry object or DN string. If an object is specified, the DN and attribute associated with the RDN slot values are modified as well as the directory specified by ldap-object.
compare ldap-object dn-or-entry attribute value [method]
compare entry-object ldap-object attribute value
  Send a compare message to the directory server asserting that entry-obj (or DN) has an attribute `attribute` with a value `value`. Returns either T or NIL (as the only argument or as the first argument) indicating a return code of compareTrue or compareFalse.
modify ldap-object dn-or-entry list-of-mods [method]
modify entry-object ldap-object list-of-mods
  Modify the entry specified by `dn-or-entry` or `entry-object`. `list-of-mods` is a list of (type attribute value) triples. Type will be one of ldap:add, ldap:delete, or ldap:replace.
search ldap-object filter &key base (scope 'sub) (deref 'never) (size-limit 0) (time-limit 0) types-only attributes [method]
  Search the directory specified by ldap-object. `filter` is an LDAP filter (as a string). The outer parens on the filter are optional. The search base defaults to the base slot-value of the ldap-object. This method returns T or NIL, indicating results pending or not, respectively.
next-search-result ldap-object [method]
  After an ldap:search is executed, this method can be used to return each of the entry objects that search resulted in. next-search-result will return NIL if no more results are available.
dosearch var search-form &body body [macro]
  Execute the ldap:search provided as `search-form` and iterate through the results binding each resulting entry object to `var` before evaluating `body.` Returns the number of entries processed. NB: If the body of the loop involves LDAP transactions it is probably best that a difference LDAP instance be employed.
ldif-search &rest ldap-search-args [macro]
  Prints the results of a search in LDIF. This macro is probably most useful in an interactive setting. `ldap-search-args` are passed directly to the search method described above.

Last modified: Sat Jan 13 09:01:44 EST 2007