La fois précédente, nous avons vu comment interroger l’AD, mais avec une requête tellement simpliste qu’elle sort beaucoup trop de résultats (certes, qui peut le plus peut le moins, mais on atteint vite la limite des 1000 objets dont j’ai déjà parlé). Pour améliorer les choses, il suffit d’être un peu plus précis dans la requête en choisissant le type d’objet qui nous intéresse (un compte d’ordinateur, ou un compte utilisateur par exemple), et de cibler un peu plus précisément l’OU où chercher ces objets.
Mais tout d’abord, simplifions-nous un peu la vie.
The simple life
Pour cela, nous allons nous faire une petite fonction tout bête qui se chargera de faire tout le boulot de connexion, de binding, de recherche et de lecture des résultats. Nous n’aurons plus qu’à traiter les données en sortie. Comme d’habitude, il faudra tout de même préciser un certain nombre de paramètres, après avoir défini quelques variables globales comme le nom du contrôleur de domaine, le compte utilisateur du domaine, son mot de passe, etc.
// Nom ou adresse IP du contrôleur de domaine (précédé de ldap://) $LDAPserver = "ldap://monjolidc"; // Utilisateur du domaine (user@domainfqdn) et surtout pas domain\user $LDAPuser = "dede@lateur.laserforce.org"; // Mot de passe de l'utilisateur $LDAPpwd = "password"; // Chemin FQDN où seront effectuées les recherches par défaut. // Précisez les OU - organizational unit et DC - domain component // par ex : ou=toto,ou=titi,dc=domain,dc=com $LDAPdefaultou = "OU=lateur,dc=laserforce,dc=org";
Ensuite, la fonction :
function RunLDAPQuery($LDAPou,$LDAPquery) { // En entrée : // - $LDAPou = chaîne vide (utilisera $LDAPdefaultou) ou préciser // le chemin de l'OU dans laquelle effectuer les recherches, // Par ex: OU=Toto,OU=Titi,DC=domaine,DC=com // - $LDAPquery = chaîne contenant le filtre à utiliser pour la // recherche // En sortie : // Retourne un tableau multidimensionnel ($LDAPdata) // ou false si le binding n'a pu se faire global $LDAPserver, $LDAPuser, $LDAPpwd, $LDAPdefaultou; $LDAPds=@ldap_connect($LDAPserver); if(@ldap_bind($LDAPds, $LDAPuser , $LDAPpwd)===true) { if($LDAPou=="") { $LDAPou=$LDAPdefaultou; } $LDAPsr=ldap_search($LDAPds, $LDAPou, $LDAPquery); $LDAPdata = ldap_get_entries($LDAPds, $LDAPsr); ldap_close($LDAPds); return $LDAPdata; } else { return false; } }
La fonction est très simple : si on ne le fournit pas d’OU en paramètre (on lui fournit tout de même une chaîne vide, “”), elle reprend la valeur de la variable $LDAPdefaultou qu’on a défini précédemment. En deuxième paramètre, on lui fournit la chaîne de requête $LDAPquery (qui était $search dans le précédent épisode).
Si tout se passe bien, elle nous renvoie un tableau de données ($LDAPdata) qu’il faudra traiter, ou false en cas de problème.
Soyons clair : cette fonction ne fait que lire l’AD, d’autant plus qu’on n’utilise qu’un compte utilisateur du domaine, et pas administrateur. Cette fonction ne peut donc PAS modifier l’AD. On verra ça un autre jour.
L’appel de la fonction est très simple :
$result=RunLDAPQuery("",$unechainecontenantlarequeteLDAP);
Et c’est donc le tableau $result qu’il faudra traiter. Commençons par quelque chose de simple : je veux récupérer le nom, le prénom et l’adresse email d’un utilisateur particulier dont je connais le login Windows. La différence avec le premier épisode, c’est qu’au lieu de récupérer ces propriétés pour TOUS les objets de l’AD, nous allons les récupérer que pour UN seul objet. C’est dans la requête (le paramètre $LDAPquery de la fonction RunLDAPQuery() ) que nous allons spécifier tout ça.
Powershell to the rescue
A moins d’être un mutant directement connecté à l’AD et de connaître toutes les propriétés d’un type d’objet (ou d’avoir lu le Technet en entier), difficile de deviner quelle sera la propriété contenant le login de notre utilisateur. Il existe une solution très simple, consistant à aller à la pêche aux infos (ou plutôt aux propriétés) avec notre ami Powershell, que vous avez pu découvrir ici-même.
Pour que cela fonctionne, il faut toutefois installer le module Active Directory, présent seulement dans les Outils d’Administration de Serveur Distant, aka Remote Server Admin Tools ou RSAT.
Pour Windows 7, il faut récupérer la KB958830, en choisissant bien la version (32 ou 64 bits) adaptée à votre système, ou sinon RSAT pour Windows 10, là aussi en prenant la version adaptée. Attention, cela ne fonctionne qu’avec les versions Pro ou Entreprise de ces systèmes, pas avec les versions Home. Vous devez également être admin local de votre machine pour l’installation. La deuxième étape consiste à activer les fonctionnalités supplémentaires ajoutées par RSAT.
Sous Windows 7, allez dans le Panneau de configuration, puis Programmes et Fonctionnalités, et sélectionnez Activer ou désactiver des fonctionnalités Windows.
Développez ensuite l’arborescence comme dans l’illustration ci-dessous et sélectionnez le module Active Directory pour Windows Powershell :
Confirmez avec OK et hop, c’est bon, le module est installé. Il ne reste plus qu’à lancer Powershell ISE, et à taper les lignes suivantes :
Import-Module activedirectory $root="OU=lateur,DC=laserforce,DC=org" $dc="monjolidc" $user=Get-ADuser -Server $dc -SearchBase $root - Filter * -Properties * | Where name - like "Lateur" $user | gm
Exécutez ensuite ce bout de script et ô joie, toutes les propriétés du compte appartenant à un type nommé “Lateur” apparaîtront (notez que j’ai demandé avec la condition Where justement les objets dont le nom (la propriété name) commence par “Lateur”. Notez aussi que je risque de tomber sur un objet qui n’est pas un compte utilisateur, mais un compte d’ordinateur, si toutefois j’avais été assez bête pour appeler un ordinateur “Lateur”).
En lisant un peu la myriade de propriétés (via la cmdlet Get-Member abrégée en gm), je peux voir que :
- la propriété SAMaccountname contient le login
- la propriété sn contient le nom de famille Lateur
- la propriété givenName contient le prénom Dédé
- et enfin la propriété mail contient l’adresse mail de Dédé, que je n’indiquerai pas ici au grand dam de mes nombreuses fans
On peut mettre Powershell de côté pour le moment et revenir à PHP.
Serious SAM
Le SAM dont il est question est ici le Security Account Manager, ou gestionnaire de compte de sécurité, dont on retrouve l’acronyme dans la propriété SAMaccountname citée plus tôt. C’est la base contenant les paires comptes/mot de passe (en local), un concept remontant à Windows NT, que l’on retrouve ici (du moins son acronyme) dans l’ActiveDirectory.
Vous me voyez venir, nous allons effectivement indiquer dans notre requête LDAP que nous cherchons un objet dont la propriété SAMaccountname est le login de notre utilisateur. Pour cela, nous allons appeler notre fonction RunLDAPQuery() avec ces paramètres :
$result=RunLDAPQuery("","(sAMaccountName=DEDELATEUR)");
Arrêtons-nous un peu sur le second : “(sAMaccountName=DEDELATEUR)”
Au niveau de la syntaxe, c’est simple comme dirait le gamin le plus énervant du monde (celui de la pub pour la Renault ZOE) : entre parenthèses, on indique la propriété (sAMaccountName) puis la valeur recherchée (DEDELATEUR). Vous pouvez très bien écrire la propriété tout en minuscules (je vous le conseille), mais pour la valeur, mieux vaut faire attention à la casse. De même, si votre valeur contient des espaces, pensez à la mettre entre apostrophes ou guillemets (dans ce cas, n’oubliez pas l’antislash avant chaque guillemet, sous peine d’avoir un belle erreur de syntaxe de la part de PHP).
Ensuite, comme l’autre fois, on traite le tableau multidimensionnel $result. Commencez toujours par vérifier qu’il n’est pas vide (autrement dit, qu’il y a bien au moins un objet dedans), puis que les propriétés désirées existent :
<?php if($result["count"]>0) { if(isset($result[0]["sn"][0])) { echo "Nom de l'utilisateur : ".$result[0]["sn"][0]."<br />"; } if (isset($result[0]["givenname"][0])) { echo "Prénom de l'utilisateur : ".$result[0]["givenname"][0]."<br />"; } if (isset($result[0]["mail"][0])) { echo "Email de l'utilisateur : ".$result[0]["mail"][0]."<br />"; } } else { echo "Aucun objet trouvé !"; } ?>
Notez que j’ai écrit $result[0] et non pas $result[$i] comme dans l’épisode précédent : en effet, je ne m’attends ici qu’à avoir un seul résultat (d’index 0), et je ne suis donc pas dans une boucle type for (d’où l’absence de variable d’incrémentation $i). Je vous ai déjà expliqué le reste du code la dernière fois, inutile de revenir dessus.
Dans ma requête, je n’ai pas précisé le type d’objet que je voulais. Imaginons que mon AD contienne un objet compte d’ordinateur, qui s’appelerait aussi “DEDELATEUR”… Evidemment, je ne risque pas d’avoir un nom, un prénom et une adresse email pour un ordinateur ! Oui mais voilà, les comptes d’ordinateur n’ont pas de propriété sAMaccountName, seuls les comptes d’utilisateur en ont, donc forcément que j’obtiendrai un objet compte d’utilisateur… inutile de se compliquer la vie à préciser le type d’objet dans ce cas précis. Mais il peut s’avérer nécessaire en effet de préciser un peu plus, notamment si plusieurs types d’objets ont une propriété commune (comme le nom). Dans ce cas, il suffit de modifier un peu la requête lors de l’appel à la fonction RunLDAPQuery() :
$result=RunLDAPQuery("","(&(objectCategory=computer)(cn=DEDELATEUR))");
Ce coup-ci, je vais bien chercher un ordinateur (objectCategory=computer) dont le nom (ou CN, Common Name) est DEDELATEUR). Notez qu’on met toujours chaque composant de la requête entre parenthèses, la requête elle-même aussi car nous avons précisé le & (AND, et) avant les composants, car dans l’exemple ci-dessus nous voulons un objet dont la catégorie est “ordinateur” ET dont le CN est “DEDELATEUR”.
Rappelez-vous toutefois qu’un objet computer n’a pas les mêmes propriétés qu’un objet user. Il vous faudra faire un peu de Powershell pour lister les propriétés qui vous intéressent.
Hasta la vista, Baby
La prochaine fois, nous verrons d’autres astuces bien pratiques – comme déterminer si un compte d’ordinateur ou d’utilisateur est désactivé – avant de nous lancer dans un projet de fou : remplir un formulaire d’inscription en Ajax à partir des données stockées dans l’AD ! C’est bien plus facile que ça en a l’air, vous verrez. Tchüss !