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 logsConfiguration du 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).
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-BInstallation.
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.
<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.
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.
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.
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-BInstallation. 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-BInstallation), 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é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 :
<?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.