Tutoriel sur le projet LDAP Apache Directory (serveur, plugin Eclipse et API Java)

Régis POUILLER

Présentation du projet Apache Directory et de ses sous-projets Apache Directory Server (serveur LDAP), Apache Directory Studio (plugin Eclipse de consultation et d'édition de LDAP) et Apache Directory LDAP API (API Java d'accès LDAP).

Une discussion a été ouverte pour les commentaires sur la publication de cet article : [ Commentez ]

- Titre : Tutoriel sur le projet LDAP Apache Directory (serveur, plugin Eclipse et API Java)

- Auteur : Régis POUILLER

- Parution : 21 février 2014

- Licence : Copyright ® 2013 Régis POUILLER. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- I. INTRODUCTION

- I-A. Objectif de l'article

- I-B. Attributs LDAP utilisés dans l'article

- II. APACHE DIRECTORY SERVER

- II-A. Présentation

- II-B. Installation

- II-C. Configuration du niveau de logs

- II-D. Serveur embarqué

- III. APACHE DIRECTORY STUDIO

- III-A. Présentation

- III-B. Installation

- III-C. Ouverture de la perspective « LDAP »

- III-D. Vue « LDAP Servers »

- III-D-1. Création d'un serveur dans Eclipse

- III-D-2. Modification du port d'un serveur

- III-D-3. Démarrage du serveur et création d'une connexion vers le serveur dans Eclipse

- III-E. Vue « Connexions »

- III-E-1. Déconnexion du serveur

- III-E-2. Création d'une connexion vers un serveur externe

- III-E-3. Création d'une nouvelle partition

- III-F. Vue « Navigateur LDAP »

- III-F-1. Modification de la valeur d'un attribut

- III-F-2. Création d'une entrée

- III-F-3. Export LDIF

- III-G. Éditeur LDIF

- III-H. Vue « Navigateur LDAP » (suite)

- III-H-1. Import LDIF

- III-H-2. Ajout d'un attribut

- III-H-3. Suppression d'un attribut

- III-H-4. Suppression d'une entrée

- III-I. Vue « Logs de modifications »

- III-J. Vue « Logs de recherches »

- IV. APACHE DIRECTORY LDAP API

- IV-A. Présentation

- IV-B. Téléchargement

- IV-C. Lecture des entrées dans le LDAP

- IV-D. Création d'une entrée dans le LDAP

- IV-E. Modification d'une entrée dans le LDAP

- IV-F. Suppression d'une entrée dans le LDAP

- IV-G. Alternatives

- IV-G-1. API standard de Java

- IV-G-2. Spring LDAP

- V. CONCLUSION

- VI. REMERCIEMENTS

I - INTRODUCTION

I-A - Objectif de l'article

L'objectif de cet article est de présenter le projet Apache Directory. Ce projet a été initialisé par Alex Karasulu en 2002.

Apache Directory est une solution LDAP très complète. Il comprend entre autres sous-projets un serveur LDAP Apache Directory Server, un plugin Eclipse Apache Directory Studio, une API Java Apache Directory LDAP API.

I-B - Attributs LDAP utilisés dans l'article

Sans prétendre à présenter la structure d'un LDAP, voici une brève explication des attributs utilisés dans cet article :

- dn : Distinguished Name, c'est l'identifiant unique de l'entrée (c'est la combinaison de plusieurs des autres attributs, généralement le rdn de l'entrée avec le dn de l'entrée de niveau supérieur) ;

- rdn : Relative Distinguished Name, c'est l'identifiant unique de l'entrée (c'est une combinaison d'attributs qui rend cette entrée unique dans son niveau) ;

- objectclass : permet de définir le type de l'entrée (inetOrgPerson par exemple). Selon le type choisi, l'entrée pourra avoir des attributs différents (une organization n'a pas de uid) et une entrée peut cumuler des types ;

- dc : Domain Component, c'est un morceau de nom de domaine (par exemple, com ou developpez) ;

- o : Organization, c'est le nom de l'organisation pour une entrée de type organization, cela peut être le nom d'une entreprise/association ;

- ou : Organization Unit, c'est une partie d'une entrée de type organization, cela peut être le service d'une entreprise ;

- cn : Common Name, c'est le nom complet (par exemple : Régis POUILLER) ;

- sn : Surname, c'est le surnom (par exemple : regis1512) ;

- uid : User ID, c'est son identifiant (cela correspond souvent à son rdn).

Il y a d'autres attributs qui se passent de commentaires (mail, téléphone, description, userPassword).

II - APACHE DIRECTORY SERVER

II-A - Présentation

Apache Directory Server est un serveur LDAP écrit en Java. Il est écrit en Java (et donc multiplateforme).

Par ailleurs, il est certifié LDAPv3, extensible et supporte les déclencheurs (triggers) et les procédures stockées. Il est également serveur Kerberos.

Dans cet article, nous verrons son installation ainsi que sa possibilité à être embarquable.

II-B - Installation

Il faut télécharger le fichier d'installation (dans l'article la version 2.0.0-M15 pour Windows) à l'adresse : http://directory.apache.org/apacheds/download/download-windows.html.

La version 2.0.0-M15 comporte un bogue assez agaçant qui bloque le démarrage du service. Le bogue est identifiable en changeant le niveau de logs.

Cela sera corrigé dans la prochaine version (milestone M16) de Apache Directory Server.

Lancer l'exécutable « apacheds-2.0.0-M15.exe ».

Cliquer sur le bouton « Next > ».

Cliquer sur le bouton « IAgree ».

Choisir un dossier d'installation et cliquer sur le bouton « Next > ».

Choisir un dossier pour stocker les données et cliquer sur le bouton « Next > ».

Indiquer le dossier d'installation de Java et cliquer sur le bouton « Install ».

Attendre la fin de l'installation.

Cliquer sur le bouton « Next > ».

Cliquer sur le bouton « Finish ».

Cliquer sur le bouton « Oui ».

Le service est démarré (visible dans les services Windows et dans le fichier log ci-dessous).

C:\Program Files\ApacheDS\instances\default\log\wrapper.log

STATUS | wrapper  | 2013/11/11 18:47:39 | ApacheDS - default installed.
STATUS | wrapper  | 2013/11/11 18:52:47 | Starting the ApacheDS - default service...
STATUS | wrapper  | 2013/11/11 18:52:47 | --> Wrapper Started as Service
STATUS | wrapper  | 2013/11/11 18:52:47 | Launching a JVM...
INFO   | jvm 1    | 2013/11/11 18:52:48 | Wrapper (Version 3.2.3) http://wrapper.tanukisoftware.org
INFO   | jvm 1    | 2013/11/11 18:52:48 |   Copyright 1999-2006 Tanuki Software, Inc.  All Rights Reserved.
INFO   | jvm 1    | 2013/11/11 18:52:48 |
INFO   | wrapper  | 2013/11/11 18:52:52 | Waiting to start...
INFO   | wrapper  | 2013/11/11 18:52:57 | Waiting to start...
INFO   | jvm 1    | 2013/11/11 18:53:01 |            _                     _          ____  ____
INFO   | jvm 1    | 2013/11/11 18:53:01 |           / \   _ __    ___  ___| |__   ___|  _ \/ ___|
INFO   | jvm 1    | 2013/11/11 18:53:01 |          / _ \ | '_ \ / _` |/ __| '_ \ / _ \ | | \___ \
INFO   | jvm 1    | 2013/11/11 18:53:01 |         / ___ \| |_) | (_| | (__| | | |  __/ |_| |___) |
INFO   | jvm 1    | 2013/11/11 18:53:01 |        /_/   \_\ .__/ \__,_|\___|_| |_|\___|____/|____/
INFO   | jvm 1    | 2013/11/11 18:53:01 |                |_|
INFO   | jvm 1    | 2013/11/11 18:53:01 |
INFO   | wrapper  | 2013/11/11 18:53:02 | Waiting to start...
STATUS | wrapper  | 2013/11/11 18:53:04 | ApacheDS - default started.

II-C - Configuration du niveau de logs

Par défaut les logs de Apache Directory Server sont au niveau « FATAL ». Il est souhaitable de descendre le niveau à « ERROR ». Pour avoir une idée des différents niveaux de logs possibles, il est possible de consulter l'article « Introduction à Log4J » de Sébastien Le Ray et notamment le chapitre « II.B. Niveaux de journalisation ».

Ouvrir le fichier « dossierApacheDS/instances/default/conf/log4j.properties » et modifier la ligne ci-dessous.

log4j.logger.org.apache.directory.server=ERROR

Après modification, redémarrer le service Windows de Apache Directory Server (ApacheDS).

Cette modification permet d'identifier le problème avec la 2.0.0-M15 évoqué dans le II-B.

II-D - Serveur embarqué

Cette partie s'inspire des éléments de embedded-apacheds de rpwaltz sur Bitbucket qui est un fork de embedded-apacheds de resah sur Bitbucket.

Nous allons voir un exemple d'exécution d'Apache Directory Server en serveur embarqué (cela peut être utile dans le cadre de tests automatisés nécessitant un serveur LDAP, par exemple les tests d'une API de sécurité ou pour une application utilisant un LDAP mais dont on souhaite avoir la possibilité d'une installation « light » sans serveur à part). Durant cette exécution, nous allons charger une entrée de classe « inetOrgPerson » et le domaine la contenant que nous lirons avec une partie cliente.

Voici la dépendance Maven nécessaire à l'exécution.

Dépendance Maven

<dependency>
<groupId>org.apache.directory.server</groupId>
<artifactId>apacheds-all</artifactId>
<version>2.0.0-M15</version>
</dependency>

Voici le fichier LDIF contenant l'entrée à charger.

rpouiller.ldif

version: 1

dn: o=TEST
objectclass: domain
objectclass: top
objectclass: extensibleObject
dc: TEST
o: TEST

dn: uid=rpouiller,o=TEST
objectClass: top
objectClass: inetOrgPerson
objectClass: person
objectClass: organizationalPerson
cn: Régis Pouiller
sn: regis1512
uid: rpouiller

Voici le code source de l'exemple.

EmbeddedServerMain.java

package com.developpez.rpouiller;

import java.io.File;
import java.util.Hashtable;

import javax.naming.Context;
import javax.naming.NamingEnumeration;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
import javax.naming.ldap.Control;
import javax.naming.ldap.InitialLdapContext;
import javax.naming.ldap.LdapContext;

import org.apache.directory.api.ldap.model.name.Dn;
import org.apache.directory.server.core.api.DirectoryService;
import org.apache.directory.server.core.api.partition.Partition;
import org.apache.directory.server.core.factory.DefaultDirectoryServiceFactory;
import org.apache.directory.server.core.factory.DirectoryServiceFactory;
import org.apache.directory.server.core.partition.impl.avl.AvlPartition;
import org.apache.directory.server.ldap.LdapServer;
import org.apache.directory.server.protocol.shared.store.LdifFileLoader;
import org.apache.directory.server.protocol.shared.transport.TcpTransport;

public class EmbeddedServerMain {

public static void main(String[] args) throws Exception {

final DirectoryServiceFactory lFactory = new DefaultDirectoryServiceFactory();
lFactory.init("Test");
System.out.println("Factory initialisée");

final DirectoryService lService = lFactory.getDirectoryService();
lService.getChangeLog().setEnabled(false);
lService.setShutdownHookEnabled(true);

final Partition lPartition = new AvlPartition(lService.getSchemaManager());
lPartition.setId("Test");
lPartition.setSuffixDn(new Dn(lService.getSchemaManager(), "o=TEST"));
lPartition.initialize();
lService.addPartition(lPartition);
System.out.println("Partition ajoutée au service");

final LdapServer lServer = new LdapServer();
lServer.setTransports(new TcpTransport("localhost", 11389));
lServer.setDirectoryService(lService);
System.out.println("Serveur initialisé");

lService.startup();
lServer.start();
System.out.println("Serveur et service démarrés");

new LdifFileLoader(lService.getAdminSession(), new File("rpouiller.ldif"), null).execute();
System.out.println("Données LDIF chargées");

System.out.println();
System.out.println("Lecture de données - Début");

final Hashtable<String, String> lEnvironment = new Hashtable<String, String>();
lEnvironment.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
lEnvironment.put(Context.PROVIDER_URL, "ldap://localhost:11389/");

final LdapContext lLdapContext = new InitialLdapContext(lEnvironment, new Control[0]);
final SearchControls lSearchControls = new SearchControls();
lSearchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);

final NamingEnumeration<SearchResult> lResultats = lLdapContext.search("", "(uid=rpouiller)", lSearchControls);
System.out.println("\t Résultat trouvé : ");
while (lResultats.hasMore()) {
System.out.println("\t\t " + lResultats.next().getName());
}

System.out.println("Lecture de données - Fin");
System.out.println();

lServer.stop();
lService.shutdown();
System.out.println("Serveur et service arrêtés");
}
}

Voici le résultat de l'exécution.

Trace de l'exécution

SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
Factory initialisée
Partition ajoutée au service
Serveur initialisé
Serveur et service démarrés
Données LDIF chargées

Lecture de données - Début
Résultat trouvé :
uid=rpouiller,o=TEST
Lecture de données - Fin

Serveur et service arrêtés

La DirectoryServiceFactory permet d'instancier le DirectoryServiceDirectoryService. Le gère le LDAP : il contient la Partition et les entrées du LDAP. Le LdapServer traite les requêtes TCP et transmet tout cela au DirectoryService.

III - APACHE DIRECTORY STUDIO

III-A - Présentation

Apache Directory Studio est un plugin Eclipse contenant tous les outils nécessaires pour travailler avec un LDAP : navigateur LDAP, éditeur de fichier LDIF, serveur embarqué, éditeur de configuration…

III-B - Installation

Dans Eclipse (version utilisée ici 4.3.1 Kepler), sélectionner « Install New Software... » dans le menu « Help ».

Cliquer sur le bouton « Add... ».

Renseigner « Name » et « Location » (avec http://directory.apache.org/studio/update/2.x) et cliquer sur le bouton « OK ».

Cocher « Apache Directory Studio ApacheDS 2.0 Configuration », « Apache Directory Studio ApacheDS Configuration », « Apache Directory Studio LDAP Browser », « Apache Directory Studio LDAP Servers », « Apache Directory Studio LDIF Editor » et « Apache Directory Studio Schema Editor » puis cliquer sur le bouton « Next > ».

Cliquer sur le bouton « Next > ».

Sélectionner « Iaccept the terms of the license agreement » puis cliquer sur le bouton « Finish ».

Attendre la fin de l'installation.

Cliquer sur le bouton « OK ».

Cliquer sur le bouton « Yes ».

III-C - Ouverture de la perspective « LDAP »

Sélectionner « Open perspective » / « Other... » dans le menu « Window ».

Sélectionner « LDAP » et cliquer sur le bouton « OK ».

La perspective comporte les vues suivantes : « LDAP Servers », « Connexions », « Navigateur LDAP », « Logs de modifications » et « Logs de recherches ».

III-D - Vue « LDAP Servers »

La vue « LDAP Servers » permet de créer des serveurs LDAP dans Eclipse sans avoir besoin d'installer un LDAP sur le poste du développeur.

III-D-1 - Création d'un serveur dans Eclipse

Pour créer un nouveau serveur, cliquer sur l'icône .

Choisir « ApacheDS 2.0.0 » et cliquer sur le bouton « Finish ».

III-D-2 - Modification du port d'un serveur

Pour ouvrir la configuration, faire un clic droit sur le serveur juste créé et choisir « Ouvrir la configuration ».

Modifier les ports du serveur afin de ne pas être en conflit avec le serveur installé dans le II-B. Puis sauver le fichier « config.ldif ».

III-D-3 - Démarrage du serveur et création d'une connexion vers le serveur dans Eclipse

Pour démarrer le serveur, cliquer sur l'icône .

Pour créer une connexion vers ce serveur, faire un clic droit sur le serveur et choisir « Créer une connexion ».

Cliquer sur le bouton « OK ».

III-E - Vue « Connexions »

La vue « Connexions » permet de créer et d'utiliser des connexions vers des serveurs LDAP.

III-E-1 - Déconnexion du serveur

La connexion vers le serveur créée dans le chapitre précédent a été ajoutée dans la vue « Connexions ». Pour fermer la connexion, cliquer sur l'icône

III-E-2 - Création d'une connexion vers un serveur externe

Pour créer une nouvelle connexion (vers le serveur installé dans le II-B), cliquer sur l'icône

Saisir les informations sur la connexion : nom de la connexion (ici : « DS sur localhost »), nom de l'hôte (ici : « localhost ») et port (ici : « 10389 »). Puis cliquer sur le bouton « Next > ».

Saisir le nom d'utilisateur (par défaut : « uid=admin,ou=system »). Décocher « Sauvegarder le mot de passe » (de toutes manières, nous allons le changer). Puis cliquer sur le bouton « Finish ».

Saisir le mot de passe de l'utilisateur (par défaut : « secret »). Puis cliquer sur le bouton « OK ».

La connexion a été créée et ouverte.

III-E-3 - Création d'une nouvelle partition

Pour ouvrir la configuration, faire un clic droit sur la connexion et choisir « Open configuration ».

Pour aller vers la configuration des partitions, cliquer sur « Configuration avancée des partitions… » ou sur l'onglet « Partitions ».

Cliquer sur le bouton « Ajouter ».

Saisir les informations ID (ici : « developpez ») et suffix (ici : « dc=developpez,dc=com »). Puis sauver le fichier de configuration.

À partir de ce que l'on a vu précédemment, fermer la connexion dans Eclipse, redémarrer le service Windows d'ApacheDS, ouvrir la connexion dans Eclipse.

La nouvelle partition est maintenant visible dans le « Navigateur LDAP ».

III-F - Vue « Navigateur LDAP »

La vue « Navigateur LDAP » permet de consulter et d'éditer un LDAP (par exemple ici, modifier la valeur d'un attribut et créer une entrée).

III-F-1 - Modification de la valeur d'un attribut

Nous allons modifier la valeur de l'attribut « userPassword » (le mot de passe) de l'utilisateur admin du LDAP.

Dans le « Navigateur LDAP », sélectionner l'entrée « uid=admin ».

Noter que le mot de passe est visible en clair dans la vue « Outline ».

Pour modifier la valeur, double-cliquer sur la valeur de l'attribut.

Pour certaines valeurs (comme pour le mot de passe) un éditeur particulier apparaît. Dans l'onglet « Nouveau mot de passe », saisir le nouveau mot de passe (ici : « nouveausecret »), sélectionner la méthode de hachage (ici : « SHA »). Puis cliquer sur le bouton « OK ».

Noter que le mot de passe n'est plus visible en clair dans la vue « Outline ».

Bien entendu lors de la prochaine connexion, il sera nécessaire d'utiliser le nouveau mot de passe.

III-F-2 - Création d'une entrée

Nous allons créer une nouvelle entrée dans la partition « dc=developpez,dc=com » qui a été créée précédemment.

Faire un clic droit sur la partition. Choisir « Nouveau »/« Nouvelle Entrée… ».

Cliquer sur le bouton « Next > ».

Sélectionner la classe « organizationalUnit » dans la liste et cliquer sur le bouton « Ajouter ». Puis cliquer sur le bouton « Next > ».

Dans le RDN (Relative Distinguished Name), saisir que « ou » vaut « people ». Puis cliquer sur le bouton « Next > ».

Cliquer sur le bouton « Finish ».

La nouvelle entrée apparaît maintenant dans le « Navigateur LDAP ».

Sur le même principe, il est possible de créer l'entrée ci-dessous (classe « inetOrgPerson »).

III-F-3 - Export LDIF

Nous allons exporter dans un fichier LDIF l'entrée « uid=rpouiller,dc=developpez,dc=com » que nous avons créée précédemment.

Faire un clic droit sur l'entrée et choisir « Export »/« Export LDIF... ».

Cliquer sur le bouton « Next > ».

Saisir le chemin du fichier LDIF. Puis cliquer sur le bouton « Finish ».

III-G - Éditeur LDIF

Sélectionner « Open File… » le menu « File ».

Sélectionner le fichier précédemment sauvé.

Modifier le fichier comme ci-dessous et le sauver.

version: 1

dn: uid=testldif,ou=people,dc=developpez,dc=com
objectClass: top
objectClass: inetOrgPerson
objectClass: person
objectClass: organizationalPerson
cn: test ldif
sn: ldif
uid: testldif
mail: test@ldif.com

III-H - Vue « Navigateur LDAP » (suite)

III-H-1 - Import LDIF

Nous allons importer le ficher LDIF modifié dans le LDAP.

Faire un clic droit dans la vue « Navigateur LDAP » et choisir « Import »/« Import LDIF... ».

Choisir le fichier et cliquer sur le bouton « Finish ».

L'entrée a été importée.

L'import de fichier LDIF peut également permettre de mettre à jour des données existantes (voir la case à cocher « Mettre à jour les données existantes » dans la fenêtre d'import ci-dessus).

III-H-2 - Ajout d'un attribut

Nous allons ajouter un attribut à l'entrée LDAP que nous venons d'importer.

Une fois l'entrée sélectionnée dans le « Navigateur LDAP », faire un clic droit dans l'éditeur et sélectionner « Nouvel attribut… ».

Sélectionner un « Type d'attribut » et cliquer sur le bouton « Finish ».

Saisir la valeur et valider avec la touche « Entrée ».

III-H-3 - Suppression d'un attribut

Nous allons supprimer un attribut à l'entrée LDAP que nous venons d'importer.

Faire un clic droit sur l'attribut et sélectionner « Supprimer la valeur ».

Cliquer sur le bouton « OK ».

L'attribut a été supprimé.

Normalement la fonctionnalité permet de supprimer une valeur d'attribut. Dans ce cas, l'attribut a été supprimé car c'était la seule valeur.

III-H-4 - Suppression d'une entrée

Nous allons supprimer l'entrée LDAP que nous venons d'importer.

Faire un clic droit sur l'entrée et sélectionner « Supprimer l'entrée ».

Cliquer sur le bouton « OK ».

L'entrée a été supprimée.

III-I - Vue « Logs de modifications »

La vue « Logs de modifications » permet de tracer les modifications dans le LDAP.

III-J - Vue « Logs de recherches »

La vue « Logs de recherches » permet de tracer les recherches/consultations dans le LDAP.

IV - APACHE DIRECTORY LDAP API

IV-A - Présentation

Apache Directory LDAP API est une API Java permettant d'accéder à un serveur LDAP (tous les types de serveur et pas seulement Apache Directory Server).

L'API permet de rechercher, consulter, modifier ou supprimer des entrées dans un LDAP.

IV-B - Téléchargement

Au moment de la rédaction de cet article, la version de l'API est « 1.0.0-M20 ». Le fichier contenant les bibliothèques nécessaires « apache-ldap-api-1.0.0-M20-bin.zip » est téléchargeable à l'adresse http://directory.apache.org/api/downloads.html.

Sinon, il est possible d'utiliser Maven avec la dépendance suivante :

<dependency>
<groupId>org.apache.directory.api</groupId>
<artifactId>api-all</artifactId>
<version>1.0.0-M20</version>
</dependency>

IV-C - Lecture des entrées dans le LDAP

Le code Java suivant permet de visualiser l'entrée que nous avons ajoutée dans Apache Directory Studio.

package com.developpez.rpouiller;

import java.io.IOException;

import org.apache.directory.api.ldap.model.cursor.CursorException;
import org.apache.directory.api.ldap.model.cursor.EntryCursor;
import org.apache.directory.api.ldap.model.entry.Entry;
import org.apache.directory.api.ldap.model.exception.LdapException;
import org.apache.directory.api.ldap.model.message.SearchScope;
import org.apache.directory.ldap.client.api.LdapConnection;
import org.apache.directory.ldap.client.api.LdapNetworkConnection;

public class ConsultationLdapMain {

public static void main(String[] args) throws LdapException, IOException, CursorException {
final LdapConnection lLdapConnection = new LdapNetworkConnection("localhost", 10389);
lLdapConnection.bind("uid=admin,ou=system", "nouveausecret");

final EntryCursor lEntryCursor = lLdapConnection.search(
"ou=people,dc=developpez,dc=com",
"(objectclass=*)",
SearchScope.ONELEVEL,
"*");

for (final Entry lEntry : lEntryCursor) {
System.out.println(lEntry);
}

lLdapConnection.unBind();
lLdapConnection.close();
}
}

Cela donne la trace suivante :

SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
Entry
dn: uid=rpouiller,ou=people,dc=developpez,dc=com
objectClass: organizationalPerson
objectClass: person
objectClass: inetOrgPerson
objectClass: top
uid: rpouiller
sn: regis1512
cn: Régis Pouiller

IV-D - Création d'une entrée dans le LDAP

Le code Java suivant permet de créer une entrée dans le serveur Apache Directory Server.

package com.developpez.rpouiller;

import java.io.IOException;

import org.apache.directory.api.ldap.model.entry.DefaultEntry;
import org.apache.directory.api.ldap.model.exception.LdapException;
import org.apache.directory.ldap.client.api.LdapConnection;
import org.apache.directory.ldap.client.api.LdapNetworkConnection;

public class CreationLdapMain {

public static void main(String[] args) throws LdapException, IOException {
final LdapConnection lLdapConnection = new LdapNetworkConnection("localhost", 10389);
lLdapConnection.bind("uid=admin,ou=system", "nouveausecret");

lLdapConnection.add(
new DefaultEntry(
"uid=test,ou=people,dc=developpez,dc=com",    // Distinguished Name
"ObjectClass: top",
"objectClass: inetOrgPerson",
"ObjectClass: person",
"objectClass: organizationalPerson",
"cn: nom courant",
"sn: surnom",
"uid: test"
));

lLdapConnection.unBind();
lLdapConnection.close();
}
}

On retrouve cette entrée dans le LDAP.

IV-E - Modification d'une entrée dans le LDAP

Le code Java suivant permet de modifier une entrée dans le serveur Apache Directory Server.

package com.developpez.rpouiller;

import java.io.IOException;

import org.apache.directory.api.ldap.model.entry.DefaultAttribute;
import org.apache.directory.api.ldap.model.entry.DefaultModification;
import org.apache.directory.api.ldap.model.entry.ModificationOperation;
import org.apache.directory.api.ldap.model.exception.LdapException;
import org.apache.directory.api.ldap.model.name.Dn;
import org.apache.directory.ldap.client.api.LdapConnection;
import org.apache.directory.ldap.client.api.LdapNetworkConnection;

public class ModificationLdapMain {

public static void main(String[] args) throws LdapException, IOException {
final LdapConnection lLdapConnection = new LdapNetworkConnection("localhost", 10389);
lLdapConnection.bind("uid=admin,ou=system", "nouveausecret");

lLdapConnection.modify(
new Dn("uid=test,ou=people,dc=developpez,dc=com"),
new DefaultModification(
ModificationOperation.REPLACE_ATTRIBUTE,
new DefaultAttribute("sn", "nouveau surnom")
));

lLdapConnection.unBind();
lLdapConnection.close();
}
}

On retrouve cette entrée dans le LDAP.

IV-F - Suppression d'une entrée dans le LDAP

Le code Java suivant permet de supprimer une entrée dans le serveur Apache Directory Server.

package com.developpez.rpouiller;

import java.io.IOException;

import org.apache.directory.api.ldap.model.exception.LdapException;
import org.apache.directory.ldap.client.api.LdapConnection;
import org.apache.directory.ldap.client.api.LdapNetworkConnection;

public class SuppressionLdapMain {

public static void main(String[] args) throws LdapException, IOException {
final LdapConnection lLdapConnection = new LdapNetworkConnection("localhost", 10389);
lLdapConnection.bind("uid=admin,ou=system", "nouveausecret");

lLdapConnection.delete("uid=test,ou=people,dc=developpez,dc=com");

lLdapConnection.unBind();
lLdapConnection.close();
}
}

La suppression est validée dans le LDAP.

IV-G - Alternatives

Apache Directory LDAP API n'est pas la seule API Java permettant d'accéder à un serveur LDAP. Il y a l'API standard de Java (comme vu dans le II-D. Serveur embarqué) et Spring LDAP.

IV-G-1 - API standard de Java

L'API standard de Java permet d'accéder à un serveur LDAP.

package com.developpez.rpouiller;

import java.util.Hashtable;

import javax.naming.Context;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
import javax.naming.ldap.Control;
import javax.naming.ldap.InitialLdapContext;
import javax.naming.ldap.LdapContext;

public class ApiStandardMain {

public static void main(String[] args) throws NamingException {
final Hashtable<String, String> lEnvironment = new Hashtable<String, String>();
lEnvironment.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
lEnvironment.put(Context.PROVIDER_URL, "ldap://localhost:10389/");
lEnvironment.put(Context.SECURITY_PRINCIPAL, "uid=admin,ou=system");
lEnvironment.put(Context.SECURITY_CREDENTIALS, "nouveausecret");

final LdapContext lLdapContext = new InitialLdapContext(lEnvironment, new Control[0]);
final SearchControls lSearchControls = new SearchControls();
lSearchControls.setSearchScope(SearchControls.ONELEVEL_SCOPE);

final NamingEnumeration<SearchResult> lResultats = lLdapContext.search(
"ou=people,dc=developpez,dc=com", "(objectclass=*)", lSearchControls);

System.out.println("Résultat trouvé : ");
while (lResultats.hasMore()) {
System.out.println("\t " + lResultats.next().getName());
}
}
}

Cela donne la trace suivante :

Résultat trouvé :
uid=rpouiller

IV-G-2 - Spring LDAP

Spring LDAP est une librairie Java simplifiant les opérations LDAP. Elle est basée sur JdbcTemplate.

Voici les dépendances Maven de l'API :

<dependency>
<groupId>org.springframework.ldap</groupId>
<artifactId>spring-ldap-core</artifactId>
<version>2.0.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.ldap</groupId>
<artifactId>spring-ldap-core-tiger</artifactId>
<version>2.0.0.RELEASE</version>
</dependency>

Voici la configuration Spring :

applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ldap="http://www.springframework.org/schema/ldap"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/ldap http://www.springframework.org/schema/ldap/spring-ldap.xsd">

<ldap:context-source url="ldap://localhost:10389"
base="ou=people,dc=developpez,dc=com" username="uid=admin,ou=system" password="nouveausecret" />

<ldap:ldap-template id="ldapTemplate" />

</beans>

Et finalement, la source d'une classe d'exemple :

package com.developpez.rpouiller;

import static org.springframework.ldap.query.LdapQueryBuilder.query;

import java.util.List;

import javax.naming.NamingException;
import javax.naming.directory.Attributes;

import org.springframework.context.support.FileSystemXmlApplicationContext;
import org.springframework.ldap.core.AttributesMapper;
import org.springframework.ldap.core.LdapTemplate;
import org.springframework.ldap.query.LdapQuery;
import org.springframework.ldap.query.SearchScope;

public class SpringLdapMain {

public static void main(String[] args) {
final FileSystemXmlApplicationContext lApplicationContext = new FileSystemXmlApplicationContext("applicationContext.xml");

final LdapTemplate lLdapTemplate = (LdapTemplate) lApplicationContext.getBean("ldapTemplate");

final LdapQuery lLdapQuery = query().searchScope(SearchScope.ONELEVEL).where("objectClass").like("*");
final List<String> lList = lLdapTemplate.search(lLdapQuery, new AttributesMapper<String>() {

public String mapFromAttributes(final Attributes pAttributes) throws NamingException {
return "uid=" + pAttributes.get("uid").get().toString();
}

});

System.out.println("Résultat trouvé : ");
for (final String lResultat : lList) {
System.out.println("\t " + lResultat);
}

lApplicationContext.close();
}
}

Cela donne la trace suivante :

SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
Résultat trouvé :
uid=rpouiller

V - CONCLUSION

Nous avons pu voir que le projet Apache Directory est une solution LDAP très intéressante (et surtout très complète). Au moment de la rédaction de l'article, le principal inconvénient du projet est d'être seulement au statut de milestone et de ne pas avoir de release pour les versions récentes. Mais le projet est très actif (M15 est la septième milestone du serveur pour l'année 2013). Il est également possible d'avoir recours aux dernières releases même si elles datent un peu : Apache Directory Server 1.5.7 (du 24 avril 2010, non maintenu), Apache Directory Studio 1.5.3 (du 30 mars 2010) et Apache LDAP API 0.1 (du 24 mars 2010).

VI - REMERCIEMENTS

Je remercie très sincèrement :

- www.developpez.com qui me permet de publier cet article ;

- Nono40 et djibril pour leurs outils ;

- Thierry LERICHE-DESSIRIER pour sa relecture technique ;

- Fabien pour sa correction orthographique.