Java:Accès à un LDAP
Sommaire
But
Un serveur LDAP est avant tout une base de données, mais dont le modèle de données est standardisé. Grâce à cela, plusieurs applications peuvent partager les mêmes données. L'exemple le plus parlant sont les comptes et les groupes d'utilisateurs: dans un réseau d'entreprise, une application qui gère ses utilisateurs à partir d'un LDAP est un plus.
Préliminaire
Avant de commencer à développer des accès à un LDAP, il est impératif de procéder à l'installation d'un serveur LDAP.
Connexion/Déconnexion à un serveur LDAP
Pour se connecter à un serveur LDAP:
final Hashtable<String, String> env = new Hashtable<String, String> (); env.put (Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); env.put (Context.PROVIDER_URL, "ldap://ldap.minetti.org/ou=people,ou=localnet,dc=minetti,dc=org"); env.put (Context.SECURITY_AUTHENTICATION, "simple"); env.put (Context.SECURITY_PRINCIPAL, "cn=admin,ou=sysaccount,ou=localnet,dc=minetti,dc=org"); env.put (Context.SECURITY_CREDENTIALS, "mypassword"); final LdapContext context = new InitialLdapContext (env, null);
Ici, on se connecte au serveur ldap.minetti.org à partir du noeud ou=people,ou=localnet,dc=minetti,dc=org et sous le compte admin (cn=admin,ou=sysaccount,ou=localnet,dc=minetti,dc=org).
La connexion sera encapsulé dans une instance de la classe javax.naming.ldap.InitialLdapContext (qui implémente l'interface javax.naming.ldap.LdapContext).
Pour se déconnecter:
context.close ();
Recherche d'entités
La plupart des recherches retournent une instance de javax.naming.NamingEnumeration représentant une énumération de javax.naming.directory.SearchResult. Chaque SearchResult embarque une entité à travers une instance de javax.naming.directory.Attributes. C'est à travers cette instance que l'on pourra récupérer toutes les données d'une entité.
final NamingEnumeration<SearchResult> resultEnumeration = context.search (...); while (resultEnumeration.hasMore ()) { final SearchResult entry = resultEnumeration.next (); final LdapName dn = new LdapName (Rdn.unescapeValue (entry.getNameInNamespace ()).toString ()); final Attributes attributes = entry.getAttributes (); ... }
Par le distinguished name (DN)
Pour rechercher une entitée par son DN:
final Attributes attributes = context.getAttributes (new LdapName("uid=" + Rdn.escapeValue (uid)));
La recherche par DN est la seule qui nous garantie un résultat unique: c'est la seule recherche qui retourne directement une instance javax.naming.directory.Attributes.
Le DN indiqué ne doit pas contenir la base DN mentionnée lors de la connexion (dans notre exemple: ou=people,ou=localnet,dc=minetti,dc=org). La recherche commencera à partir des noeuds enfants de la base DN.
Il est vivement recommandé de placer les DN dans une instance javax.naming.ldap.LdapName et de formater sa valeur à l'aide de la méthode escapeValue(...) de javax.naming.ldap.Rdn. En effet, certains caractères (comme espace , = + < > # ; " \) sont réservé et ne peuvent se trouver dans la valeur d'un attribut du DN. Pour rappel un DN est de la forme: attribut1=valeur1,attribut2=valeur2,attribut3=valeur3 (exemple: ou=people,ou=localnet,dc=minetti,dc=org).
Par type d'entités uniquement
Pour retourner toutes les entités d'un même type (par exemple: inetOrgPerson):
final SearchControls searchControls = new SearchControls (); searchControls.setSearchScope (SearchControls.ONELEVEL_SCOPE); final NamingEnumeration<SearchResult> resultEnumeration = context.search ("", "(objectClass=inetOrgPerson)", searchControls);
Ici, l'instance javax.naming.directory.SearchControls permet d'indiquer où se fera notre recherche. On peut ainsi paramétrer notre recherche sur:
- ONELEVEL_SCOPE pour rechercher uniquement au niveau de notre base DN indiquée lors de la connexion (dans notre exemple: ou=people,ou=localnet,dc=minetti,dc=org),
- SUBTREE_SCOPE pour rechercher au niveau de notre base DN et dans les noeuds enfants.
Par type d'entités et attributs
Pour retourner toutes les entités d'un même type ET qui ont un ou plusieurs attributs avec des valeurs données (par exemple: uid=jp):
final String filterExpression = "(&(uid=jp)(objectClass=inetOrgPerson))"; final SearchControls searchControls = new SearchControls (); searchControls.setSearchScope (SearchControls.ONELEVEL_SCOPE); final NamingEnumeration<SearchResult> resultEnumeration = context.search ("", filterExpression, searchControls);
Attention: La recherche est possible uniquement sur les attributs indexés (se reporter à la configuration du LDAP pour paramétrer les indexes).
Lecture des attributs d'une entité
Pour lire un attribut d'une entité:
final Attribute attribute = attributes.get ("givenName"); if (attribute != null) { Object value = attribute.get (); ... }
Chaque attribut de l'entité est encapsulé dans un objet implémentant l'interface javax.naming.directory.Attribute.
Pour lire un attribut de type tableau de valeurs:
final Attribute attribute = attributes.get ("uniqueMember"); if ((attribute != null) && (attribute.size () > 0)) { final NamingEnumeration<?> enumeration = attribute.getAll (); while (enumeration.hasMore ()) { final String value = (String) enumeration.next (); ... } }
Ici, nous faisons une lecture de l'attribut uniqueMember (que l'on trouve dans les entités groupOfUniqueNames). Les valeurs de cet attribut sont de type DN (distinguished name). Il est vivement recommandé de placer les DN dans une instance javax.naming.ldap.LdapName:
final LdapName dn = new LdapName (value);
Création d'une entité
Pour créer une nouvelle entité dans le LDAP (par exemple, un inetOrgPerson):
final Attributes attributes = new BasicAttributes (); // Attribut objectClass final BasicAttribute objectClassAttribute = new BasicAttribute ("objectClass"); objectClassAttribute.add ("top"); objectClassAttribute.add ("inetOrgPerson"); attributes.put (objectClassAttribute); // Attribut UID final String uid = ... attributes.put (new BasicAttribute ("uid", uid)); // Attribut postalAddress (tableau de valeurs) final String address1 = ... final String address2 = ... final BasicAttribute addressAttribute = new BasicAttribute ("postalAddress", true); addressAttribute.add (address1); addressAttribute.add (address2); attributes.put (addressAttribute); ... // DN final LdapName name = new LdapName ("uid=" + Rdn.escapeValue (uid)); // Création context.createSubcontext (name, attributes);
Chaque attribut de l'entité sera placé dans une instance de javax.naming.directory.BasicAttribute. Cette instance sera à son tour placé dans une instance javax.naming.directory.BasicAttributes.
La création de la nouvelle entité se fera grâce à la méthode createSubcontext(Name, Attributes) de l'objet connexion (javax.naming.ldap.InitialLdapContext).
Modification d'une entité
Pour modifier un ou plusieurs attributs d'une entité:
final Attributes attributesToReplace = new BasicAttributes (); final Attributes attributesToRemove = new BasicAttributes (); // Ajout/Modification d'un attribut final String sn = ... attributesToReplace.put (new BasicAttribute ("sn", sn)); // Suppression d'un attribut (valeur NULL) attributesToRemove.put (new BasicAttribute ("givenName")); ... // DN final LdapName name = new LdapName ("uid=" + Rdn.escapeValue (uid)); // Modification context.modifyAttributes (name, DirContext.REPLACE_ATTRIBUTE, attributesToReplace); context.modifyAttributes (name, DirContext.REMOVE_ATTRIBUTE, attributesToRemove);
Attention: Les attributs à supprimer doivent exister. S'ils n'existent pas une erreur se produit.
La modification d'une entité se fera grâce à la méthode modifyAttributes(Name, int, Attributes) de l'objet connexion (javax.naming.ldap.InitialLdapContext) et peut porter sur:
- une modification d'un ou plusieurs attributs de l'entité (REPLACE_ATTRIBUTE),
- ou une suppression d'un ou plusieurs attributs de l'entité (REMOVE_ATTRIBUTE).
Suppression d'une entité
Pour supprimer une entité du LDAP:
// DN final LdapName name = new LdapName ("uid=" + Rdn.escapeValue (uid)); // Suppression context.destroySubcontext (name);
La suppression d'une entité se fera grâce à la méthode destroySubcontext(Name) de l'objet connexion (javax.naming.ldap.InitialLdapContext).
Chiffrement d'attributs
Certains attributs, comme les mots de passe (userPassword), doivent être chiffrés:
final String password = ... final MessageDigest sha = MessageDigest.getInstance ("SHA"); sha.update (password.getBytes ("UTF-8")); final String encodedPassword = "{SHA}" + (new BASE64Encoder ()).encode (sha.digest ());
Quelques entités et attributs les plus utilisées
Hiérarchie des utilisateurs et personnes
Entités | Attributs | ||||
---|---|---|---|---|---|
Nom | Type | Nom | Requis | Longueur | Explication |
top | abstract | objectClass | oui | Classe d'une entité (tableau de valeurs). | |
person
(hérite de top) |
structural | cn | oui | 32 768 | Nom complet de la personne (prénom et nom de famille). |
sn | oui | Nom de famille de la personne. | |||
description | non | 1 024 | Description de la personne. | ||
telephoneNumber | non | 32 | Numéro de téléphone. | ||
userPassword | non | 128 | Mot de passe. | ||
organizationalPerson
(hérite de person) |
structural | facsimileTelephoneNumber | non | Numéro de fax. | |
l | non | Localité où réside la personne. | |||
postOfficeBox | non | 40 | Boite postale de la personne. | ||
postalAddress | non | Ligne complémentaire d'adresse où réside la personne (tableau de valeurs). | |||
postalCode | non | 40 | Code postal où réside la personne. | ||
st | non | Etat ou province où réside la personne. | |||
street | non | 128 | Numéro et nom de la rue où réside la personne. | ||
inetOrgPerson
(hérite de organizationalPerson) |
structural | givenName | non | Prénom de la personne. | |
non | 256 | Adresse e-mail de la personne. | |||
o | non | Nom de la société où travail la personne. | |||
uid | non | 256 | Identifiant de la personne (utilisateur). |
Hiérarchie des groupes d'utilisateurs
Entités | Attributs | ||||
---|---|---|---|---|---|
Nom | Type | Nom | Requis | Longueur | Explication |
top | abstract | objectClass | oui | Classe d'une entité (tableau de valeurs). | |
groupOfUniqueNames
(hérite de top) |
structural | cn | oui | 32 768 | Nom du groupe d'utilisateurs. |
uniqueMember | oui | Distinguished name (DN) d'un utilisateur membre du groupe (tableau de valeurs). | |||
description | non | 1 024 | Description du groupe d'utilisateurs. |