Java:Accès à un LDAP

De WIKI.minetti.org
Aller à : navigation, rechercher

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.
mail 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.