Title: Groovy LDAP User Guide Notice: Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at . http://www.apache.org/licenses/LICENSE-2.0 . Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

Groovy LDAP User Guide

> **Note:** This guide is work in progress and is written at the same time as Groovy LDAP integration developes. The API may change. Feedback highly welcome. [TOC] ## Prerequisites * Java SE 5 (JDK 1.5) or above * Groovy 1.0 or above ## Installation You only have to add the jar file _groovy-ldap.jar_ with the _Groovy LDAP_ classes to the class path. If you plan to write Groovy scripts whoich use the functionality, an easy option is to copy the library into the _lib_ folder of your Groovy installation. That's it. ## Connection to an LDAP server Main entry point is the class _LDAP_ from the _org.apache.directory.groovyldap_ package (contained in _groovy-ldap.jar_). You obtain an instance via a call to a static method _newInstance_, like this: :::javascript import org.apache.directory.groovyldap.LDAP ldap = LDAP.newInstance('ldap://zanzibar:10389/') ... The argument used here is the LDAP URL of your directory server and contains hostname and port. This method as shown above is for anonymous binds. A variant of it expects the bind name of a user and his/her credentials: :::javascript ... ldap = LDAP.newInstance('ldap://zanzibar:10389/', 'uid=admin,ou=system', '******') ... Please note that _Groovy LDAP_ uses JNDI and its configuration facilities. This means that if you provide a _jndi.properties_ file in your class path, Groovy LDAP will take these parameters into account. This enables you to keep your user name and password outside of the script code, for instance. Like with JNDI with its _InitialContext_, calling newInstance will _not_ connect to the LDAP server immediately. This means that an authentication will likely occur later (with the first operation). ## Basic LDAP operations Groovy LDAP basically supports the operations provided by the native LDAP protocol, with some exceptions: * It is not necessary to connect explicitly to the server or to _bind_/_unbind_. Groovy LDAP performs these operations automatically. * LDAP controls are not supported yet * No asynchronous calls are supported yet. Consequently, no _abandon_ operation is available in the API ### LDAP add operation The _add_ operation is used to create a new entry within the directory. One option in Groovy LDAP is to define the attribute values of the new entry as a Map. One _add_ operation takes the distinguished name (DN) of the new entry (as a String) and the attributes map as parameters. Here is an example: :::javascript import org.apache.directory.groovyldap.LDAP ldap = LDAP.newInstance('ldap://zanzibar:10389', 'uid=admin,ou=system' ,'******') assert ! ldap.exists('cn=Heather Nova,dc=example,dc=com') // define the attributes as a map attrs = [ objectclass: ['top', 'person'], sn: 'Nova', cn: 'Heather Nova' ] ldap.add('cn=Heather Nova,dc=example,dc=com', attrs) assert ldap.exists('cn=Heather Nova,dc=example,dc=com') In the _assert_ expressions, the _exists_ method of Groovy LDAP is used. It checks whether an entry with this DN already exists (see below). If no error occurs, the script adds the following entry to the directory: dn: cn=Heather Nova,dc=example,dc=com cn: Heather Nova sn: Nova objectClass: person objectClass: top ### LDAP delete operation The _delete_ operation is used to delete an existing entry within the directory. As in the original LDAP protocol, only the deletion of leaf entries is allowed. Deletion of a subtree of entries (for instance with the _Tree Delete Control_) is not supported yet. Here is an example script which uses the _delete_ method from Groovy LDAP. :::javascript import org.apache.directory.groovyldap.LDAP ldap = LDAP.newInstance('ldap://zanzibar:10389', 'uid=admin,ou=system' ,'******') assert ldap.exists('cn=Heather Nova,dc=example,dc=com') // delete the entry with the given DN ldap.delete('cn=Heather Nova,dc=example,dc=com') assert !ldap.exists('cn=Heather Nova,dc=example,dc=com') Note that if the given entry does not exist, calling _delete_ throws a javax.naming.NameNotFoundException. This is different to JNDI, where no error will occur in this case. The behavior here imitates the original LDAP delete operation. In the example script, this will not occur due to the _assert_ statements. ### LDAP search operation :::javascript import org.apache.directory.groovyldap.* ldap = LDAP.newInstance('ldap://zanzibar:10389/') // search op results = ldap.search('(objectClass=*)', 'dc=example,dc=com', SearchScope.ONE ) println "${results.size} entries found:" for (entry in results) { println entry.dn } The output looks like: 3 entries found: cn=Tori Amos,dc=example,dc=com cn=Kate Bush,dc=example,dc=com cn=Heather Nova,dc=example,dc=com Learn more about options on how to search within a directory in the [Reference|5. Groovy LDAP Reference] ### LDAP compare operation The _compare_ operation is used to check attribute value assertions. The following script adds an entry and uses _compare_ several times to demonstrate the different matching rules for attribute type _cn_ (case insensitive) and _userPassword_ (case sensitive). :::javascript import org.apache.directory.groovyldap.LDAP ldap = LDAP.newInstance('ldap://zanzibar:10389', 'uid=admin,ou=system' ,'******') assert ! ldap.exists('cn=Heather Nova,dc=example,dc=com') // define an entry for compare ops attrs = [ objectclass: ['top', 'person'], sn: 'Nova', cn: 'Heather Nova', userPassword: 'secret' ] ldap.add('cn=Heather Nova,dc=example,dc=com', attrs) assert ldap.exists('cn=Heather Nova,dc=example,dc=com') assert ldap.compare('cn=Heather Nova,dc=example,dc=com', [cn: 'Heather Nova'] ) assert ldap.compare('cn=Heather Nova,dc=example,dc=com', [cn: 'HEATHER NOVA'] ) assert ldap.compare('cn=Heather Nova,dc=example,dc=com', [userPassword: 'secret'] ) assert ! ldap.compare('cn=Heather Nova,dc=example,dc=com', [userPassword: 'SECRET'] ) ldap.delete('cn=Heather Nova,dc=example,dc=com') ### LDAP modify operation I am not perfectly happy with the syntax for modify yet. Here are two calls that perform modifications against the entry added above: :::javascript import org.apache.directory.groovyldap.* ldap = LDAP.newInstance('ldap://zanzibar:10389', 'uid=admin,ou=system' ,'secret') dn = 'cn=Heather Nova,dc=example,dc=com' // Adding a single attribute descr = [ description: 'a singer-songwriter' ] ldap.modify(dn, 'ADD', descr) // performing two operations atomically mods = [ [ 'REPLACE', [description: 'a singer-songwriter, born in Bermuda'] ], [ 'ADD', [userPassword: 'secret'] ] ] ldap.modify(dn, mods) The second call uses a list _mods_ of modifications as an argument; each modification consists of a list of two elements (a pair): the modification type and a map with the attribute(s). Afterwards, the entry looks like this in the directory dn: cn=Heather Nova,dc=example,dc=com cn: Heather Nova sn: Nova objectClass: person objectClass: top userpassword: secret description: a singer-songwriter, born in Bermuda ## Additions to the original LDAP API ### LDAP.exists(...): Checking whether an entry exist implemented, but not documented yet ### LDAP.read(...): Reading an entry by its distinguished name I have included a _read_ operation as in the Netscape API -- note that there is nothing like that in LDAP (you have to perform a search for it). Nevertheless, it is quite handy to have it. In this example I have use an anonymous LDAP connection. My LDAP server allows me to read entries without user and password (which is quite common). :::javascript import org.apache.directory.groovyldap.LDAP ldap = LDAP.newInstance("ldap://zanzibar:10389") // Simple entry lookup via dn heather = ldap.read('cn=Heather Nova,dc=example,dc=com') print """ DN: ${heather.dn} Common name: ${heather.cn} Object classes: ${heather.objectclass} """ The output (if this entry exists) is: DN: cn=Heather Nova,dc=example,dc=com Common name: Heather Nova Object classes: ["person", "top"] ## LDAP the Groovy way ### Performing searches with closures There is a search variant in the _LDAP_ class comparable to _eachRow_ in GSQL. It takes a closure as parameter. I have added the search base to the ldap URL. There is also a variant which takes the base and a scope (defaults to subtree). :::javascript import org.apache.directory.groovyldap.LDAP ldap = LDAP.newInstance('ldap://zanzibar:10389/dc=example,dc=com') ldap.eachEntry ('(objectClass=person)') { person -> println person.cn } The output looks like: Tori Amos Kate Bush Heather Nova