JNDI : Java et les annuaires

Quelques liens utiles

Le guide de JavaSoft sur les JNDI
http://java.sun.com/javase/7/docs/technotes/guides/jndi/
Le tutorial JNDI
http://java.sun.com/products/jndi/tutorial/
Un tutorial LDAP (en anglais)
http://yolinux.com/TUTORIALS/LinuxTutorialLDAP.html
Un cours complet sur LDAP (en français)
http://www-sop.inria.fr/.../ldap-livre.html
JavaDoc: JNDI :
package javax.naming,
package javax.naming.directory,

Présentation de JNDI

♦  JNDI = Java Naming and Directory Interface

♦  C'est une interface unifiée vers des annuaires d'entreprise (correspondance nom --> objet)

Architecture JNDI

♦  L'API JNDI est composé et classes et surtout d'interfaces.

♦  Les pilotes assurent l'interface vers les annuaires.

♦  Les pilotes DNS, LDAP, RMI et CORBA sont inclus d'office dans la JRE.

♦  Les autres pilotes doivent être récupérés et ajoutés.

figures/jndi/jndiarch.png

Le binding (association)

♦  C'est une association entre un nom et un objet.

♦  Dans le NIS un nom d'utilisateur est associé à ses entrées dans la base NIS :

                         |UID=510
               NIS       |GID=500
"massat"   ---------->   |shell=/bin/bash
                         |home=/home/massat
                         |...

♦  Le binding est aussi une association entre un nom et une référence qui pointe sur l'objet associé à ce nom.

Un exemple dans RMI :

              Registre RMI             Protocole RMI    Serveur
"1234AZ13"   ------------->  URL RMI   ------------->     de      --+
                                                        voiture     |
                                                                    |
                      objet voiture <-------------------------------+

L'annuaire se comporte comme un serveur d'objets.

Context

♦  Un contexte est un ensemble d'associations entre des noms et des objets.

♦  Un même nom peut être associé à différents objets dans différents contextes. On parle dans ce cas de noms locaux.

figures/jndi/contextes.png

associations du nom local « printer » dans différents contextes.

Directory (répertoire)

♦  Dans un répertoire les noms sont associés à des affectations de valeurs à des attributs :

nom   ---------->   attribut1 := (valeur1,...,valeurN)
                    attribut2 := (valeur1,...,valeurN)
                    ...

Dans le DNS :

dil.univ-mrs.fr   ---------->   SOA: saphir.lidil.univ-mrs.fr
                                     dnsmaster.lidil.univ-mrs.fr
                                     3171101 21600 7200 ...
                                NS: saphir.lidil.univ-mrs.fr
                                    riluminy.univ-mrs.fr
                                MX: sol.dil.univ-mrs.fr

contenu du SOA (Start Of Autority): serveur primaire, mail du responsable, numéro de série, durée de vie, etc.

Directory (répertoire) (2)

♦  Dans un répertoire il est possible d'effectuer des recherches sur la valeur des attributs.

♦  Les répertoires se caractérisent par une structure hiérarchique.

figures/jndi/jndi-2.png

Les packages JNDI

♦  Il existe cinq packages JNDI :

javax.naming
Manipulation des associations.
javax.naming.directory
Manipulation des répertoires.
javax.naming.event
Traitement des évènements en provenance de l'annuaire.
javax.naming.ldap
API spécialisée pour les annuaires compatibles LDAP.
javax.naming.spi
API de programmation des pilotes JNDI.

Package javax.naming

♦  interface Context :

on peut remplacer Name par String.

♦  interface Name : gestion d'un nom composé comme

cn=massat,dc=dil,dc=com

Package javax.naming (2)

♦  classe NameClassPair :

♦  classe Binding : étends la classe NameClassPair.

♦  classe InitialContext : implante l'interface Context.

Rechercher un nom dans un annuaire

Recherche d'un objet dans un système de fichiers :

import java.util.Hashtable;
import javax.naming.Context;
import javax.naming.Binding;
import javax.naming.NameClassPair;
import javax.naming.NamingEnumeration;
import javax.naming.InitialContext;

public class ExempleJNDI {
public static void main(String[] args) {
    try {
        Hashtable<String,String> env = new Hashtable<String,String>();
        env.put("java.naming.factory.initial",
            "com.sun.jndi.fscontext.RefFSContextFactory");
        env.put("java.naming.provider.url", "file:///home");

        Context ictx = new InitialContext(env);
        Object o = ictx.lookup(args[0]);
        System.out.print(args[0] + " est ");
        if (o instanceof Context) System.out.println(" un noeud");
        else System.out.println(" une feuille");
    }
    catch (javax.naming.NamingException e) { System.err.println(e); }
   }

Lister les noms d'un annuaire

Ce code liste les noms à partir d'un annuaire LDAP :

public static void main(String[] args) {
    try {
    Hashtable<String,String> env = new Hashtable<String,String>();
    env.put("java.naming.factory.initial",
        "com.sun.jndi.ldap.LdapCtxFactory");
    env.put("java.naming.provider.url",
        "ldap://localhost/dc=my-domain,dc=com");

    Context ictx = new InitialContext(env);
    NamingEnumeration<NameClassPair> e = ictx.list("cn=userA");
    while (e.hasMore()) {
        System.out.println("name: " + e.next().getName());
        }
    }
    catch (javax.naming.NamingException e) {
        System.err.println("Exception " + e);
        }
    }

Les noms sont relatifs au contexte de base de la recherche (ici cn=userA,dc=my-domain,dc=com).

Rechercher les associations

Ce code liste les machines connues dans le domaine DNS args[0] + "." + "univ-mrs.fr" :

public static void main(String[] args) {
    try {
    Hashtable<String,String> env = new Hashtable<String,String>();
    env.put("java.naming.factory.initial",
        "com.sun.jndi.dns.DnsContextFactory");
    env.put("java.naming.provider.url",
        "dns://saphir.lidil.univ-mrs.fr/univ-mrs.fr");

    Context ictx = new InitialContext(env);
    NamingEnumeration<Binding> e = ictx.listBindings(args[0]);
    while (e.hasMore()) {
        Binding b = e.next();
        System.out.println("name: " + b.getName());
        System.out.println("object: " + b.getObject());
        }
    }
    catch (javax.naming.NamingException e) {
        System.err.println("Exception " + e);
        }
    }

Package javax.naming.directory

♦  étend les classes et interfaces de javax.naming.

♦  interface DirContext étend Context :

on peut remplacer Name par String.

Package javax.naming.directory (2)

♦  interface Attributes :

♦  interface Attribute :

Lister les attributs d'une entrée

Commençons par les déclarations de classes :

import java.util.*;

import javax.naming.Context;
import javax.naming.Binding;
import javax.naming.NamingEnumeration;
import javax.naming.InitialContext;
import javax.naming.NameClassPair;

import javax.naming.directory.Attributes;
import javax.naming.directory.Attribute;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import javax.naming.directory.SearchResult;
import javax.naming.directory.SearchControls;

Continuons par la récupération du contexte :

public class ExempleDirectory {

public static void main(String[] Args) 
    {
    try {
    Hashtable<String,String> env =
        new Hashtable<String,String>();
    env.put("java.naming.factory.initial",
        "com.sun.jndi.dns.DnsContextFactory");
    env.put("java.naming.provider.url",
        "dns://saphir.lidil.univ-mrs.fr/univ-mrs.fr");

    DirContext ictx = new InitialDirContext(env);
    Attributes attrs = ictx.getAttributes("dil",
        new String[] {"MX", "SOA"});

    System.out.println("Attributs MX et SOA de dil :");
    listerAttributs(attrs);
    }
    catch (javax.naming.NamingException e) { ... }
}

Terminons par l'affichage des attributs et de leur valeurs :

public static void listerAttributs(Attributes atts) {
    try {
    for(NamingEnumeration e = atts.getAll(); e.hasMore();)
        {
        Attribute a = (Attribute) e.next();
        System.out.println(a.getID() + ":");
        Enumeration values = a.getAll();
        while (values.hasMoreElements()) {
            System.out.println("--> " + 
               values.nextElement().toString());
            }
        }
    }
    catch (javax.naming.NamingException e) { ... }
    }

L'exécution nous donne

Attributs MX et SOA de dil :
SOA:
--> saphir.lidil.univ-mrs.fr. dnsmaster.lidil...
MX:
--> 10 sol.dil.univ-mrs.fr.

Rechercher des entrées

Recherche en profondeur avec filtre dans un annuaire LDAP :

public static void main(String[] args) {
    try {
    Hashtable<String,String> env = new Hashtable<String,String>();
    env.put("java.naming.factory.initial",
        "com.sun.jndi.ldap.LdapCtxFactory");
    env.put("java.naming.provider.url",
        "ldap://localhost/dc=my-domain,dc=com");

    // recherche en profondeur
    SearchControls controle = new SearchControls();
    controle.setSearchScope(SearchControls.SUBTREE_SCOPE);

    String critere = "(|(sn=premier)(sn=deux*))";
    DirContext ictx = new InitialDirContext(env);
    NamingEnumeration<SearchResult> e = 
        ictx.search("cn=userA", critere, controle);
    while (e.hasMore()) {
        SearchResult r = e.next();
        System.out.println("name: " + r.getName());
        System.out.println("object: " + r.getClassName());
        listerAttributs(r.getAttributes());
        }
    }
    catch (javax.naming.NamingException e) { ... }
    }