2.1 Connect and Search in the LDAP Server - rukichen/GrailsGroovyLDAP GitHub Wiki
Topics:
2.1.1 Writing a Groovy Program
2.1.2 Open Connection
2.1.3 Close Connection
2.1.4 Keep Connection open
2.1.5 Binding
2.1.6 Searching
2.1.7 Print Search
2.1.8 Complex Searches
As you should know groovy is a language based on java. Due to that fact we can use the Apache API to communicate with an LDAP Server. But because the Apache API is intended for Java use, we have to make some compromises to our groovy programming. Still, most of the simplicity of groovy is kept as far as possible.
To start. we need to add a main method into our class. In this method we can start working with our API.
class FirstGroovy {
static void main(def args) {
...
}
}
Starting of with the connection to the LDAP Server. Add this line into our main method.
def connection = new LdapNetworkConnection( "localhost", 10389)
The editor will ask you to import the following lib : org.apache.directory.ldap.client.api.LdapNetworkConnection
The first argument of LdapNetworkConnection is the name or address of your host. In our case it is localhost. The second argument is the port number. Usually the default port for LDAP Server is 389 and 636 for the secure connection. Always check if the ports are open and listening( on linux by default all ports are closed).
For secure connection:
def connection = new LdapNetworkConnection( "localhost", 10636, true )
If you don't need the connection any more close it by:
connection.close()
By default the connection will be kept open for 30 seconds. If you need a longer connection you can set a timer like this:
connection.setTimeOut( 0 )
Setting a value equal or below 0 will keep the connection open forever. So don't forget to close it after you finished.
In LDAP, if you want to access the data, the common way to do it is to bind to the server. However, it's important to understand that binding is a different from connecting.
A connection to an LDAP server opens a socket between the client and the server. You must provide the address and the port.
The bind operation creates a Session which will hold user information for the time of the session. This information is limited, but includes the user's credentials.
But it's possible to bind anonymously, which doesn't require a user or password, and still be able to send requests to the server (but server can forbid anonymous binds).
Anonymous bind:
connection.bind()
user/password bind:
connection.bind( "uid=admin,ou=system", "secret" )
In our local server, you will need to bind via user/password bind.
When you finish, unbind.t's important to know that when you issue an Unbind, the connection is dropped. It's done like this:
connection.unBind()
Unimportant Fact:
you can also bind like this:
connection.bind( "uid=admin,ou=system" )
It is like an anonymous bind, with telling the server your name. So not anonymous anymore
Now we can connect to a server and opened a session by bind. We are ready for some data.
Simple Searching
def cursor = connection.search( "ou=system", "(objectClass=*)", SearchScope.ONELEVEL)
...
cursor.close()
The simple search is done by the method search()
. It needs three arguments. The first one is the starting point, where to search , we used here ou=system along with its children, which have an ObjectClass attribute.
The second argument is filter for searching. This gets more important the more explicit your search is.
The insides of the filter are called connectors. These will help you to define your target. The construction is like this: (& (node1) (node2) ... (nodeN))
You can connect one ore more nodes with AND(&), OR(|) and NOT(!). But don't for the the brackets, if you us more than one connector. It can get quite confusing quite fast.
In addition to this you can use expressions for you nodes. For example objectClass= organizationalUnit
.
Expression | In Words | Explanation |
---|---|---|
= | Equality | the entry matches |
=* | Presence | it has the Attribute on the left side |
>= | Superior | superior to the right part |
<= | Inferior | inferior to the right part |
=[start][*][middle][*][final] | Substring | the entry should match a substring |
The third argument is setting the SearchScope, this is way it will search through the data. There are three ways of searching: ONELEVEL, OBJECT and SUBTREE. To show you what the difference is I will give you the output of our current DB we have implemented so far.
ONELEVEL
IT will return all elements below the given DN, but not the element associated with the DN.
Entry
dn: prefNodeName=sysPrefRoot,ou=system
objectClass: top
objectClass: organizationalUnit
objectClass: extensibleObject
prefNodeName: sysPrefRoot
Entry
dn: ou=configuration,ou=system
objectClass: top
objectClass: organizationalUnit
ou: configuration
Entry
dn: ou=consumers,ou=system
objectclass: top
objectclass: organizationalUnit
ou: consumers
Entry
dn: ou=groups,ou=system
objectClass: top
objectClass: organizationalUnit
ou: groups
Entry
dn: uid=admin,ou=system
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
objectClass: tlsKeyInfo
publicKeyFormat: X.509
keyAlgorithm: RSA
uid: admin
privateKey: 0x30 0x82 0x01 0x54 0x02 0x01 0x00 ...
privateKeyFormat: PKCS#8
displayName: Directory Superuser
publicKey: 0x30 0x5C 0x30 0x0D 0x06 0x09 0x2A ...
userCertificate: 0x30 0x82 0x01 0x71 0x30 0x82 ...
sn: administrator
cn: system administrator
userPassword: 0x73 0x65 0x63 0x72 0x65 0x74
Entry
dn: ou=users,ou=system
objectClass: top
objectClass: organizationalUnit
ou: users
OBJECT
It will return the entry for a given DN, if it exists. You could use LdapConnection.lookup if the filter is irrelevant.
Entry
dn: ou=system
objectClass: top
objectClass: organizationalUnit
objectClass: extensibleObject
ou: system
SUBTREE
IT will return all the elements starting from the DN, including the element associated with the DN, no matter how deep the tree.
Entry
dn: prefNodeName=sysPrefRoot,ou=system
objectClass: top
objectClass: organizationalUnit
objectClass: extensibleObject
prefNodeName: sysPrefRoot
Entry
dn: cn=Administrators,ou=groups,ou=system
objectClass: top
objectClass: groupOfUniqueNames
uniqueMember: 0.9.2342.19200300.100.1.1=admin,2.5.4.11=system
cn: Administrators
Entry
dn: ou=services,ou=configuration,ou=system
objectClass: top
objectClass: organizationalUnit
ou: services
Entry
dn: ou=system
objectClass: top
objectClass: organizationalUnit
objectClass: extensibleObject
ou: system
Entry
dn: ou=configuration,ou=system
objectClass: top
objectClass: organizationalUnit
ou: configuration
Entry
dn: ou=consumers,ou=system
objectclass: top
objectclass: organizationalUnit
ou: consumers
Entry
dn: ou=interceptors,ou=configuration,ou=system
objectClass: top
objectClass: organizationalUnit
ou: interceptors
Entry
dn: ou=groups,ou=system
objectClass: top
objectClass: organizationalUnit
ou: groups
Entry
dn: uid=admin,ou=system
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
objectClass: tlsKeyInfo
publicKeyFormat: X.509
keyAlgorithm: RSA
uid: admin
privateKey: 0x30 0x82 0x01 0x54 0x02 0x01 0x00 0x30 ...
privateKeyFormat: PKCS#8
displayName: Directory Superuser
publicKey: 0x30 0x5C 0x30 0x0D 0x06 0x09 0x2A 0x86 ...
userCertificate: 0x30 0x82 0x01 0x71 0x30 0x82 0x01 ...
sn: administrator
cn: system administrator
userPassword: 0x73 0x65 0x63 0x72 0x65 0x74
Entry
dn: ou=users,ou=system
objectClass: top
objectClass: organizationalUnit
ou: users
Entry
dn: ou=partitions,ou=configuration,ou=system
objectClass: top
objectClass: organizationalUnit
ou: partitions
Now that we found what we are looking for, we want to see it. For printing you will need to get all the entries out of your search object the cursor. In groovy iteration through an object is quite easy. With 'cursor.each' it will to every Object within. In cursors we have the entries, so you tell him for every entry print it in the curly brackets after 'each'.
cursor.each { entry ->
println entry
}
We can use the print to define more detailed what we want to see. For example if you want to see only the uid of the users in our db. First we need to change the search to ou=users,dc=example,dc=com
to get the user table.
We will get than all users with their details if we print only that. If we add now a .get("uid")
at the entry in the print, it will print uid: uids
. If you only want the uids with out the definition write an extra .get()
at the end.
def cursor = connection.search( "ou=users,dc=example,dc=com",
"(objectClass=*)", SearchScope.ONELEVEL)
cursor.each { entry ->
println entry.get("uid")
}
This works with every entry, but you will have to know the identifier to get the wanted information.
What we haven seen so far was the simple search. We searched a basic way. Often this might be enough for your own purpose, but sometimes it has to be more detailed. In Apache we can open a Search Request and give it the details we want.
def req = new SearchRequestImpl()
req.setScope(SearchScope.SUBTREE)
req.addAttributes("*")
req.setTimeLimit(0)
req.setBase( new Dn("ou=users,dc=example,dc=com"))
req.setFilter("(cn=jane austen)")
The possible commands are here:
command | explaination |
---|---|
setBase( new Dn()) |
The base DN |
setFilter("( )") |
The filter |
setScope() |
the scope |
setSizeLimit( ) |
The size limit |
setTimeLimit( ) |
The time limit |
addAttributes(" ") |
The list of attributes to return |
setDerefAliases( ) |
The alias dereferencing mode (one of DEREF_ALWAYS, DEREF_FINDING_BASE_OBJ, DEREF_IN_SEARCHING or NEVER_DEREF_ALIASES) |
setTypesOnly( ) |
The TypesOnly flag (to get the AttributeTypes but no values) |
addControl( ) |
The controls |
We send the request to the server and save the result in searchCursor
. We will have to iterate through the it to get all response
. In the response we check if it is the a S_earchResultEntry_ and get the ResultEntry out and print it.
def searchCursor = connection.search(req)
while (searchCursor.next()) {
def response = searchCursor.get()
if (response instanceof SearchResultEntry) {
def resultEntry = ((SearchResultEntry)response).getEntry()
println resultEntry
}
}
searchCursor.close()
You can see, this is more complicated than the first search but it gives you more options and control over the search.
<<< Back 1.3. Setting up a local LDAP-Server | 2.2 Adding entries in to the server Next>>> |