Start
Unternehmen
Buch-Katalog
Seminare
Leserservice
Comelio-Blog
Datenbanken
SQL
MS SQL Server
Oracle
PHP

LDAP

MS SQL Server

Reflection

SAX

SQLite

Enterprise Muster

UML
C#.NET
XML Schema
XSLT

Übersicht

Comelio GmbH
Rellinghauser Straße 10
D-45128 Essen
Deutschland
Fon: 0201-437517-0
Fax: 0201-437517-10
info@comelio.com

Comelio GmbH
Goethestraße 34
D-13086 Berlin
Deutschland
info@comelio.com

Comelio GmbH (Ecos)
Glockengießerwall 17
D-20095 Hamburg
Deutschland
info@comelio.com

Comelio GmbH (Ecos)
Mainzer Landstraße 27-31
D-60329 Frankfurt
Deutschland
info@comelio.com

Comelio GmbH (Ecos)
Stiglmaierplatz/Dachauer Str. 37
D-80335 München
Deutschland
info@comelio.com

Comelio GmbH (Ecos)
Liebknechtstr. 33
D-70565 Stuttgart
Deutschland
info@comelio.com

Comelio GmbH
Nevinghoff 16
D-48147 Münster
Deutschland

Comelio GmbH
Friedrich - List - Platz 1
D-04103 Leipzig
Deutschland

Comelio GmbH
St. Johanner Strasse 41-43
D-66111 Saarbrücken
Deutschland

Comelio GmbH
Kaiser-Wilhem-Ring 27–29
D-50672 Köln
Deutschland

Comelio GmbH
Münsterstraße 248
D-40470 Düsseldorf
Deutschland

Comelio GmbH
Fürther Strasse
D-90429 Nürnberg
Deutschland

Comelio GmbH

Bremen
Deutschland

Comelio-Blog > PHP > LDAP

PHP mit LDAP nutzen

Mit der zunehmenden Popularität von E-Mail kam innerhalb von größeren Organisationen bald der Bedarf nach zentralen Adressenverzeichnissen auf. Denn zwar hat jedes ernst zu nehmende E-Mailprogramm inzwischen ein Adressbuch – dieses jedoch für mehrere hundert Mitarbeiter ständig aktuell zu halten, und das für jeden Rechner, wäre jedoch mit erheblichem Aufwand für einen Netzwerkadministrator verbunden. Dieser Artikel zeigt die Verwendung von LDAP (Lightweight Directory Access Protocol) mit Hilfe von PHP.

Ein Verzeichnis eines LDAP-Servers ist dabei wie die Verzeichnisstruktur einer Festplatte organisiert. Wird der LDAP-Server für ein Adressverzeichnis genutzt, wären die Ordner in der Verzeichnisstruktur die Abteilungen und Unterabteilungen der Firma. Die Blätter im Verzeichnisbaum sind dann die einzelnen Mitarbeiter. Mit PHP ist es möglich, auf diese Einträge lesend, aber auch schreibend, zuzugreifen.

Kontakt

Anrede* Herr Frau
Vorname*
Nachname*
Firma
E-Mail*
Tel-Nr.
Bereich*
Freitext

LDAP mit PHP

Mit der zunehmenden Popularität von E-Mail kam innerhalb von größeren Organisationen bald der Bedarf nach zentralen Adressenverzeichnissen auf. Denn zwar hat jedes ernst zu nehmende E-Mailprogramm inzwischen ein Adressbuch – dieses jedoch für mehrere hundert Mitarbeiter ständig aktuell zu halten, und das für jeden Rechner, wäre jedoch mit erheblichem Aufwand für einen Netzwerkadministrator verbunden.

Solche zentralen Verzeichnisdienste kamen dank namhafter Firmen wie Microsoft, IBM, Lotus und Netscape auch bald auf den Markt. Doch weil Unternehmen immer in Bewegung sind, sich teilen und fusionieren, gab es dann auch bald größere Organisationen mit mehreren zentralen Verzeichnisdiensten. Also musste es möglich sein, mehrere solche Verzeichnisdienste untereinander zu synchronisieren.

Aus diesem Grund implementierten die oben genannten Firmen den Standard LDAP, kurz für Lightweight Directory Access Protocol, in ihre Produkte. Dieser Standard wurde an der Universität von Michigan entwickelt, ursprünglich um den Zugriff auf ältere, auf dem Standard X.500 basierende Verzeichnisdienste über das Internet zu ermöglichen. Mit diesem Kommunikationsprotokoll können Clients auf die Dienste eines beliebigen, LDAP-kompatiblen Verzeichnisdienstes zugreifen, unabhängig vom darunter liegenden Produkt.

Verzeichnisdienste werden nicht nur als Adressbücher genutzt. Die Mitarbeiter haben heute eine Reihe von Zugriffrechten für unternehmensinterne Applikationen: Angefangen von netzwerkbasierten Dateisystemen bis hin zu Datenbanken mit Personalinformationen. Um solche Berechtigungen nicht für jedes System einzeln pflegen zu müssen, nutzen Netzwerkadministratoren die oben genannten Verzeichnisdienste.

Fast alle Datenbanken und Betriebssysteme bieten heute in der Regel die Möglichkeit, Passwortangaben anhand eines LDAP-kompatiblen Verzeichnisdienstes zu überprüfen. Sobald ein Nutzer im zentralen Verzeichnisdienst aufgenommen ist, hat er sofort Zugriff auf alle Applikationen, die diesen als Basis für die Authentifizierung nutzen. Darüber hinaus bietet LDAP auch Zugriff auf einige administrative Funktionen eines Verzeichnisdienstes – so können beispielsweise Schreib- und Leserechte über LDAP gesetzt werden.

Auf jedem Rechner finden sich inzwischen LDAP-fähige Clients. So können die Nutzer von Programmen wie Outlook, Eudora oder Netscape auf die Adressen in einem Verzeichnisdienst zugreifen.

Ein Verzeichnis eines LDAP-Servers ist dabei wie die Verzeichnisstruktur einer Festplatte organisiert. Wird der LDAP-Server für ein Adressverzeichnis genutzt, wären die Ordner in der Verzeichnisstruktur die Abteilungen und Unterabteilungen der Firma. Die Blätter im Verzeichnisbaum sind dann die einzelnen Mitarbeiter.

Angenommen, sie würden auf ihrer Festplatte ihre Adressdaten Datei für Datei verwalten, dann sähe der Pfad zu einem Adressdatensatz vielleicht wie folgt aus:

 C:/Meine Firma/Eine Abteilung/Gabi Meyer

Eine solche Referenz auf einen Datensatz heißt bei LDAP distinguished name, oder kurz dn. Die dn für die obige fiktive Mitarbeiterin würde wie folgt lauten:

cn=Gabi Meyer, ou=Eine Abteilung, o=Meine Firma

Dabei steht das kleine o für organization, ou für organizationalUnit und cn für common name.

Wie bei einer Datenbank gibt es auch bei einem LDAP-Server ein Schema für die Verzeichnis- und Datenstrukturen. Dabei verwenden die meisten LDAP-Server das an das alte System X.500 angelehnte Schema für Personendaten (Standard RFC 2256). In den folgenden Beispielen gehen wir ausnahmslos von diesem Schema aus.

Zum Datenaustausch verwendet LDAP ein eigenes Format, LDIF, oder LDAP Data Interchange Format. Dabei handelt es sich um eine Reihe von mehrzeiligen Datensätzen, die per Leerzeile voneinander getrennt sind. Angeführt werden die Datensätze von dem distinguished name des Datensatzes, gefolgt von einem oder mehreren Attributen. Ein Beispiel für eine solche Datei, die eine Firma, eine Abteilung und eine Person beschreibt:

dn: o=Meine Firma
objectclass: top
objectclass: organization
o: Meine Firma

dn: ou=Meine Abteilung, o=Meine Firma
objectclass: organizationalUnit
ou: Meine Abteilung

dn: cn=Gaby Meyer, ou=Meine Abteilung, o=Meine Firma
objectclass: top
objectclass: organizationalPerson
cn: Gaby Meyer
sn: Meyer
givenname: Gaby
uid: gmeyer
ou: Meine Firma
telephonenumber: 007-9692
Beispiel für das LDIF-Format

Für Entwicklungszwecke hervorragend geeignet ist das Open-Source-Produkt OpenLDAP. Von der Seite http://www.openldap.org können Sie eine freie, aktuelle Version dieses LDAP-Servers herunterladen. Für Windowsnutzer etwas komfortabler ist die Trialversion des AE SLAPD von APS Engineering Inc., zu finden unter http://www.aeinc.com, im wesentlichen ein Windowsport von OpenLDAP mit einer grafischen Oberfläche zur Konfiguration. Die folgenden Beispiele funktionieren mit der mit AE SLAPD mitgelieferten Testdatenbank.

Die Funktionen der LDAP-Bibliothek gehören nicht zum Standardumfang der PHP-Distribution. Um die LDAP-Funktionen aufrufen zu können müssen Sie daher entweder

  • die LDAP-Bibliothek dynamisch zu Laufzeit laden,
  • über die Einstellungen in der php.ini laden lassen,
  • oder den PHP-Interpreter mit der Option --with-ldap kompilieren.

Wie verbinde ich mich mit LDAP?

Die Verbindung zu einem LDAP-Server stellt man in zwei Schritten her: Zunächst ruft man die Funktion ldap_connect auf, die eine Zahl zurückliefert, über die die Verbindung in den folgenden Aufrufen identifiziert werden kann. Anschließend meldet man sich per ldap_bind bei dem Server mit seinem distinguished name an, oder als anonymer Nutzer.

Der Funktion ldap_connect können zwei Parameter übergeben werden: Der Hostname des LDAP-Servers sowie, optional, der Port an dem der LDAP-Server angemeldet ist. Die Funktion ldap_bind will für die anonyme Anmeldung nur die von ldap_connect zurückgeliefert Zahl, oder, für eine Anmeldung mit einem Benutzernamen, dessen Name und Passwort. Nachdem alle notwendigen Operationen mit dem LDAP-Server durchgeführt hat, schließt man die Verbindung mit der Funktion ldap_unbind, oder ihrem Alias ldap_close.

Folgende Klasse kapselt die Aufrufe von ldap_connect und ldap_bind in den Methoden connect, bind und abind (das a steht für anonym). Die Klasse ist erweitert um einen optionalen Debuggingmodus, der den Verlauf der Verbindung ausgibt, und um eine Fehlerbehandlung.

class LDAP_Connector {
  var $hostname;

  var $cid = 0; // Connection ID
  var $bid = 0; // Bind ID

  var $debugging=FALSE; // Debug output yes|no

  // Connects to LDAP-server defined by host
  // Uses default port (389)
  private function connect() {
    $this->cid = ldap_connect( $this->hostname );
    if(!$this->cid) {
      $this->error();
      return;
    }
    $this->debug('Created link to <i>'.$this->hostname
    .'</i> (not connected)');
  }

  // Attempts a named bind to the LDAP-server using
  // the distinguished name in $binddn and the password
  // in $passwd
  public function bind( $binddn, $passwd ) {
    $this->debug('Trying named bind as <i>'.$binddn.'</i>...');
    $this->bid = ldap_bind( $this->cid, $binddn, $passwd );
    if (! $this->bid ) {
      $this->error();
      return;
    }
    $this->debug('Successfully bound to <i>'.$this->hostname
     .'</i> as user <i>'.$binddn.'</i> (connected)' );
  }

  // Attempts an anonmyous bind to the LDAP-server
  public function abind() {
    $this->debug('Trying anonymous bind...');
    $this->bid = ldap_bind( $this->cid );
    if( $this->bid ) {
      $this->debug('Successfully bound to <i>'
       .$this->hostname.'</i> as anonymous user (connected)');
    } else {
      $this->error();
      return;
    }
  }

  // closes the connection to the LDAP-server
  function unbind() {
    if( $this->bid <=0 || $this->cid <= 0) {
      $this->debug('While unbinding: Trying to unbind while'
      .'not bound/not connected');
      return;
    }
    $success = ldap_unbind( $this->cid );
    if (!$success) {
      $this->error();
      return;
    }
    $this->debug('Successfully unbound from <i>'
    .$this->hostname.'</i> (connection closed)');
  }

  // The Constructor: Connects and attempts an anonymous
  // bind. Uses default LDAP port.
  function __construct( $hostname, $debugging=FALSE ) {
    if (empty($hostname)) {
      $this->error('Can\'t instantiate LDAP_Connector'
      .'without hostname info');
    }
    $this->debugging = $debugging;
    $this->debug('Constructing class LDAP_Connector (debug modus)');
    $this->hostname = $hostname;
    $this->connect();
    $this->abind();
  }

  // closes the connection
  function __destruct() {
    $this->debug('Destructing LDAP_Connector');
    $this->unbind();
  }

  // a error handling function
  protected function error($error="") {
    echo '<p style="background:#FFAAAA"><b>';
    if( empty( $error ) && ldap_errno( $this->cid ) ) {
      echo 'LDAP Error ('.ldap_errno($this->cid).') :'
      .ldap_err2str($this->cid).'</b><br>'
      .ldap_error($this->cid);
    } else {
      echo 'An error occured:</b><br>'.$error;
    }
    die();
  }

  // the debug output
  protected function debug( $message ) {
    if ($this->debugging == TRUE) {
      echo '<p style="background:#AAAAFF">DEBUG (LDAP_Connector): '
      .$message.'</p>';
    }
  }
}
LDAP_Connector.php

Diese Klasse setzen wir in folgendem Quelltext ein. Dabei gehen wir von einer Identität Directory Manager in der Domäne ACME aus.

<html>
  <body>
<?php
 include('LDAP_Connector.php');

 $ldapco = new LDAP_Connector('localhost',TRUE);
 $ldapco->bind('cn=Directory Manager, o=ACME','secret');
?>
  </body>
</html>
LDAP_Connector_test.php

Wie finde ich Einträge?

Natürlich ist ein Verzeichnisdienst dazu da, damit Informationen von diesem abgeholt werden können. Um von einem LDAP-Server Einträge abzufragen, ruft man die Funktion ldap_list, oder deren Schwester ldap_search auf. Dabei dient die Funktion ldap_list dazu alle Einträge zu finden, die in einem bestimmten Verzeichnis gespeichert sind. Die Funktion ldap_search hingegen durchsucht das Verzeichnis und rekursiv dessen Unterverzeichnisse. Die Funktionen liefern eine Zahl zurück, die das Suchergebnis identifiziert.

Interessante Parameter der beiden Funktionen sind:

  • int link: die Ressourcen-ID der Verbindung
  • string base_dn: der Ordner im Verzeichnisdienst, der nach Einträgen durchsucht werden soll
  • string filter: Ein Suchfilter (siehe unten)
  • array attrs (optional): Ein Array mit der Liste der gewünschten Attribute, die von dem Server zurückgeliefert werden sollen.

Nachdem eine Suche durchgeführt und ausgewertet wurde, sollte das Suchergebnis wieder freigegeben werden (zumindest wenn mehrere Suchvorgänge in einer engen Schleife ausgeführt werden). Das kann man über die Funktion ldap_free_result machen.

Dabei kann über eine LDAP-eigene Syntax ein Suchfilter definiert werden. So würde man eine Person mit dem Namen Gabi Meyer mit folgendem Suchfilter finden:

cn=Gabi Meyer

Der LDAP-Server sucht dann alle Einträge, die im Attribut cn, kurz für common name, den Wert Gabi Meyer haben. Um alle Personen zu finden, die einen Namen besitzen, der mit G anfängt, würde man die Suche wie folgt formulieren. Dabei steht das Asterisk für einen beliebigen anderen Text.

cn=G*

Auch Kombinationen mehrerer Attribute, durch einfache Kommatrennung sind möglich:

cn=G*, ou=MeineAbteilung

Dieser Suchfilter würde alle Personen zurückliefern, deren Namen mit G anfängt und die in der Abteilung MeineAbteilung geführt werden.

Im folgenden Quelltext erweitern wir die Klasse LDAPConnector aus dem Rezept zur LDAP-Verbindung um eine Methode, die den Zugriff auf ldap_search kapselt.

class LDAP_Search extends LDAP_Connector {

  var $result = 0;
  var $entries = 0;

  // performs a simple search
  function search( $filter, $basedn )   {
    $this->debug('Performing search for <i>'
    .$filter.'</i> in directory <i>'.$basedn.'</i>');

    $this->result = ldap_list( $this->cid, $basedn, $filter );

    if (!$this->result) {
      $this->error();
    }
    return $this->result;
  }

  // returns the entries found by a previous search
  function getEntries() {
    $this->entries = ldap_get_entries( $this->cid, $this->result );
    if (!$this->entries) {
      $this->error();
    }
    return $this->entries;
  }

  // frees the results of previous search
  function freeResult() {
      $success = ldap_free_result( $this->result );
      if (!$success) $this->error();
  }
}
LDAP_Access.php

Diese Klasse setzen wir dann in der folgenden PHP-Seite ein, dabei führen wir zunächst nur eine Suche aus, ohne deren Ergebnisse auszuwerten.

<html>
  <body>
<?php
  include('LDAP_Search.php');

  $ldapsearch = new LDAP_Search('localhost',TRUE);
  $ldapsearch->bind('cn=Directory Manager, o=ACME','secret');

  $res = $ldapsearch->search('cn=Directo*','o=ACME');

  // Code zur Auswertung des Ergebnisses

  $ldapsearch->freeResult();
?>
  </body>
</html>
LDAP_Search_test.php

Wie werte ich Einträge aus?

Hat man eine Referenz auf ein Suchergebnis, möchte man dieses natürlich auswerten. Wie immer geht es dabei, die gefundenen Datensätze in einer Schleife abzuarbeiten. Dazu nutzt man die Kombination aus ldap_first_entry und ldap_next_entry, mit der man nach und nach die Ergebnisse abfragen kann.

Jeder Datensatz besteht dabei aus einer Reihe von Attributen, deren Zusammenstellung im Gegensatz zu einer Datenbankabfrage von Datensatz zu Datensatz unterschiedlich sein kann. Nachdem man per ldap_next_entry oder ldap_first_entry eine Referenz auf einen Datensatz erhalten hat, kann man also über die Funktion ldap_get_attributes die in dem aktuellen Datensatz verfügbaren Attribute als Array erhalten.

Die Werte eines Attributes, es sind immer mehrere Werte pro Attribut möglich, können dann über die Funktion ldap_get_values abgefragt werden. Mehrere Werte sind pro Attribut erlaubt, da eine Person ja unter Umständen mehrere Telefonnummern besitzt. Ein anderes Beispiel wäre, wenn es um Zugriffsrechte in einem Netzwerk geht, der Fall dass die Zugriffsrechte eines Benutzers durch die Zugehörigkeit zu einer oder mehreren Benutzergruppen definiert sind.

Das einzige Attribut eines Datensatz, dass direkt abgefragt werden kann, ist das distinguished name, da es sich dabei um das Äquivalent eines Primärschlüssels aus der relationalen Theorie handelt und unabhängig von der eigentlichen Verzeichnisstruktur für jeden Eintrag definiert sein muss. Dieses kann über die Funktion ldap_get_dn abgefragt werden.

Zusammengefasst: Zuerst holt man sich eine Referenz auf ein Suchergebnis. Dann fragt man den ersten Datensatz mit ldap_get_first_entry ab. Anschließend ruft man ldap_next_entry auf, bis dieses FALSE zurückliefert, um die Datensätze in einer Schleife zu bearbeiten. In der Schleife kann man die vorhandenen Attribute mit ldap_get_attributes erfahren. Hat man den Namen eines vorhandenen Attributs erfahren, so liest man dessen Werte über ldap_get_values aus.

Dabei enthalten alle zurückgegebenen Arrays unter dem Index count die Anzahl der im Array gespeicherten Attribute bzw. Werte. Wenn also die Rückgabe von ldap_get_attributes in der Variable $attribs gespeichert wäre, so ist die Anzahl der verfügbaren Attribute in $attribs['count'] gespeichert Die Arrays selbst enthalten unter Umständen weitere Inhalte, so dass eine Auswertung über die Funktion sizeof scheitert.

Eine andere Vorgehensweise findet sich mit der Anweisung ldap_get_entries, die direkt eine Struktur von PHP-Arrays mit den Daten des Suchergebnisses bestückt.

Folgende Klasse erbt von LDAP_Search die Funktionaltitäten zum Herstellen von Verbindungen und zum Ausführen von Suchen. Und erweitert diese nun um die Möglichkeit, die Ergebnisse der Suche mit den oben dargestellten Funktionen auch auszuwerten.

class LDAP_ResultUtil extends LDAP_Search {

  var $entry = 0;
  
  // after having performed a search this method
  // needs to be called in order to retrieve the
  // first entry
  function firstEntry() {
    $this->entry = ldap_first_entry($this->cid, $this->result);
    return $this->entry;    
  }

  // returns the available attributes for the
  // selected entry
  function getAttributes() {
    return ldap_get_attributes($this->cid, $this->entry);
  }
  
  // returns an array with values for a given attribute
  function getValuesOf( $attribute ) {
    return ldap_get_values($this->cid, $this->entry, $attribute);
  }
  
  // returns the entries distinguished name
  function getEntryDN() {
    return ldap_get_dn($this->cid, $this->entry);
  }
  
  // selects the next entry or returns FALSE
  // if no further entry is available
  function nextEntry() {
   $this->entry = ldap_next_entry($this->cid, $this->entry);
   return $this->entry; 
  }

  // this one is called by the debugging mode implemented
  // in LDAP_Connector
  function getClass() { return 'LDAP_ResultUtil'; }
}
LDAP_ResultUtil.php

Diese Klasse setzen wir in dem unten abgedruckten Quelltext dann auch gleich ein.

<html>
  <body>
<?php

 include('LDAP_ResultUtil.php');

 // connect'n'bind like implemented in LDAP_Connector
 $ldaputil = new LDAP_ResultUtil('localhost',TRUE);
 $ldaputil->bind('cn=Directory Manager, o=ACME','secret');
 
 // search like implemented in LDAP_Search
 $ldaputil->search('cn=Directo*','o=ACME');


 // retrieve the first entry
 $ldaputil->firstEntry();
 
 do {
   
   // output the distinguished name of the selected
   // entry ...
   echo '<h4>DN: '.$ldaputil->getEntryDN().'</h4>';

   // ... read the array with attributes ...
   $attribs = $ldaputil->getAttributes();
   
   // ... retrieve how many attributes are available ...
   for ($i=0; $i < $attribs['count']; $i++) {
   
     // ... and get a new array with the values   
     $values = $ldaputil->getValuesOf($attribs[$i]);
   
     echo '<b>'.$attribs[$i].':('.$values['count']
      .')</b><br><ul>';
   
     for ($j=0; $j < $values['count']; $j++) {
       echo '<li>'.$values[$j].'</li>';
     }
     
     echo '</ul>';
   }

 // repeat until no more entries are available
 } while ($ldaputil->nextEntry());

?>
  </body>
</html>
LDAP_ResultUtil_test.php

Wie erzeuge ich neue Einträge?

Mit der Funktion ldap_add können neue Einträge in das LDAP-Verzeichnis geschrieben werden. Neben der DN des neuen Eintrags nimmt die Funktion einen Array mit den Werten des neuen Eintrags auf. Dieser Array ist zweidimensional aufgebaut. In der ersten Dimension stehen die Namen der Attribute im Index. Je nachdem ob für ein Index mehrere Werte vorhanden sind oder nicht, erhält der Array eine weitere Dimension, ein ganz normal numerisch indizierter Array mit den Werten. Ist nur ein einziger Wert vorhanden, wird dieser direkt unter dem Attributsnamen gespeichert. Folgendes Beispiel illustriert den Aufbau dieses Arrays, der als zweiter Parameter der Funktion ldap_add übergeben wird:

$entry['this'] = 'that';
$entry['that'][0] = 'these';
$entry['that'][1] = 'thus';

Die folgende Klasse übernimmt die Funktionalität zum Herstellen einer Verbindung von der Vaterklasse LDAP_Connector.

class LDAP_CreateUtil extends LDAP_ResultUtil {
  
  // creates a new entry as defined
  function createDN( $dn, $entry ) {
    $this->debug('Attempting to create new dn <i>'.$dn.'</i>');
  
    $success = ldap_add( $this->cid, $dn, $entry );
    
    if (!$success) {
      $this->error();
    }
    
    $this->debug('New dn created');
  }
}
LDAP_CreateUtil.php

Im unten dargestellten Beispielprogramm setzen wir die Klasse LDAP_CreateUtil dazu ein, einen neuen Mitarbeiter Johnny Walker in das Verzeichnis aufzunehmen. Für das folgende Beispiel braucht das von ihnen genutzte Benutzerkonto Schreibrechte auf dem LDAP-Server.

<html>
  <body>
<?php
 include('LDAP_CreateUtil.php');

 $ldapcreate = new LDAP_CreateUtil('localhost',TRUE);
 $ldapcreate->bind('cn=Directory Manager, o=ACME','secret');

 $dn = 'dn=Johnny Walker, o=ACME';
 
 $entry['sn'] = 'Walker';
 $entry['cn'][0] = 'Johnny';
 $entry['cn'][1] = 'Walker';
 $entry['cn'][2] = 'Johnny Walter';
 $entry['givenname'] = 'Johnny';
 $entry['uid'] = 'jwalker';
 $entry['userpassword'] = 'OCyGirzpV4EXM';
 
 $ldapcreate->createDN($dn,$entry);
?>
  </body>
</html>
LDAP_CreateUtil_test.php

Wie modifiziere ich bestehende Einträge?

Bestehende Einträge werden durch das Quadrupel ldap_mod_add, ldap_mod_del, ldap_mod_replace und ldap_modify verändert. Dabei zielen die ersteren drei Funktionen auf die Modifikation einzelner Attribute ab, während durch ldap_modify der komplette Eintrag neu definiert wird.

Alle vier Funktionen besitzen drei Parameter: Die resource id der LDAP-Verbindung, der DN des Eintrags sowie ein Array mit den Werten. Der Array ist dabei analog zu dem im Rezept zum Erzeugen neuer Einträge strukturiert. Die Funktion ldap_add fügt die im Array übergebenen Werte zu dem Eintrag hinzu, es werden keine der anderen Werte überschrieben oder verändert. Bei ldap_del werden die im Array angegebenen Werte aus dem Eintrag gelöscht. Für ldap_replace gilt, dass alle im Array angegebenen Attribute die alten Werte ersetzen sollen. Wenn ldap_modify ausgeführt wird, werden die alten Werte komplett gelöscht und durch die im Array ausgetauscht.

class LDAP_ModUtil extends LDAP_DeleteUtil {
  
  // replaces all informations by the given array
  function modify( $dn, $entry ) {
    $this->debug('Attempting to ldap_modify dn <i>'.$dn.'</i>');
  
    $success = ldap_modify( $this->cid, $dn, $entry );
    
    if (!$success) {
      $this->error();
    }
    
    $this->debug('ldap_modify successfur');
  }
  
  // adds new values to the existing ones
  function mod_add( $dn, $entry ) {
    $this->debug('Attempting to ldap_mod_add dn <i>'.$dn.'</i>');
  
    $success = ldap_mod_add( $this->cid, $dn, $entry );
    
    if (!$success) {
      $this->error();
    }
    
    $this->debug('ldap_modify successfur');
  }
  
  // deletes the given values from the entry
  function mod_del( $dn, $entry ) {
    $this->debug('Attempting to ldap_mod_del dn <i>'.$dn.'</i>');
  
    $success = ldap_mod_del( $this->cid, $dn, $entry );
    
    if (!$success) {
      $this->error();
    }
    
    $this->debug('ldap_modify successfur');
  }
  
  // replaces given values by the ones defined in the array
  function mod_replace( $dn, $entry ) {
    $this->debug('Attempting to ldap_mod_replace dn <i>'
     .$dn.'</i>');
  
    $success = ldap_mod_replace( $this->cid, $dn, $entry );
    
    if (!$success) {
      $this->error();
    }
    
    $this->debug('ldap_modify successfur');
  }
  
  // for debugging purposes
  function getClass() { return 'LDAP_ModUtil'; }
}
LDAP_ModUtil.php

Die so implementierte Funktionalität nutzen wir im folgenden Beispiel, um einen Datensatz zu erzeugen und mit den oben beschriebenen Funktionen schrittweise zu modifizieren.

<html>
  <body>
<?php
 include('LDAP_ModUtil.php');
 
 // to easily display how the entry has been changed...
 function showEntry() {
   $ldapsearch = new LDAP_Search('localhost');
   $ldapsearch->search('cn=Walker*','o=ACME');
   
   $entry = $ldapsearch->getEntries();

   echo '<p><h4>dn: '.$entry[0]['dn'].'</h4>';
   echo 'sn: '.$entry[0]['sn'][0].'<br>';
   echo 'cn: '.$entry[0]['cn'][0].'<br>';
   echo 'cn: '.$entry[0]['cn'][1].'<br>';
   echo 'cn: '.$entry[0]['cn'][2].'<br>';
   echo 'cn: <i style="background:#ffaaaa">'
    .$entry[0]['cn'][3].'</i><br>';
   echo 'givenname: '.$entry[0]['givenname'][0].'<br>';
   echo 'uid: '.$entry[0]['uid'][0].'<br>';
   echo 'userpassword: '.$entry[0]['userpassword'][0].'</p>';
   
   $ldapsearch->freeresult();
 }

 $ldapmod = new LDAP_ModUtil('localhost',TRUE);
 $ldapmod->bind('cn=Directory Manager, o=ACME','secret');

 $dn = 'dn=Johnny Walker, o=ACME';

 $entry['sn'] = 'Walker';
 $entry['cn'][0] = 'Johnny';
 $entry['cn'][1] = 'Walker';
 $entry['cn'][2] = 'Johnny Walker';
 $entry['givenname'] = 'Johnny';
 $entry['uid'] = 'jwalker';
 $entry['userpassword'] = 'OCyGirzpV4EXM';
  
 // check if DN already exists
 // if not, create
 $ldapmod->search('cn=Walker*','o=ACME');
 $entries = $ldapmod->getEntries();
 if ( $entries['count']==0) {
   $ldapmod->createDN($dn,$entry);
 }
 
 // add some attribute value
 $add['cn'] = 'Whiskey';
 $ldapmod->mod_add($dn, $add);
 echo '<h3>Added Whiskey</h3>'."\n";
 showEntry();
 
 // delete it again
 $del['cn'] = 'Whiskey';
 $ldapmod->mod_del($dn, $del);
 echo '<h3>Removed Whiskey</h3>'."\n";
 showEntry();
 
 // replace some values
 $mod['cn'][0] = 'Johnnie';
 $mod['cn'][1] = 'Walker';
 $mod['cn'][2] = 'Johnnie Walkers';
 $mod['givenname'] = 'Johnnie';
 $mod['sn'] = 'Walkers';
 $ldapmod->mod_replace($dn, $mod);
 echo '<h3>Replacing some</h3>'."\n";
 showEntry();
 
 // and replace all
 // often needs special priviledges
 // $ldapmod->modify($dn, $entry);
 // echo '<h3>Rplacing all</h3>'."\n";
 // showEntry();
  
 $ldapmod->deleteDN($dn);
?>
  </body>
</html>
LDAP_ModUtil_test.php

Wie überprüfe ich eine Passworteingabe mit LDAP?

Eine der Hauptaufgaben des LDAP-Servers ist es eine gemeinsame Nutzerbasis für die in einem Unternehmen verwendeten Systeme zu stellen. Viele Datenbanken, Betriebssysteme und andere Applikationen unterstützen die Authentifizierung über einen LDAP-Server.

Auch mit PHP kann eine solche Authentifizierung leicht realisiert werden. Dabei ist in der LDAP-Schema-Definition für Personendaten das Feld uid für einen Netzwerk-geeigneten Benutzernamen, sowie das Feld userpassword für ein Passwort vorgesehen.

Was genau in dem Feld userpassword gespeichert wird, hängt vom Administrator des LDAP-Servers ab. Klartextpasswörter sind zwar möglich, aber denkbar ungeeignet. Ein Mechanismus, der in der Linux und Unixwelt viel verwendet wird sind die sogenannten Hashfunktionen. Dabei wird für ein Passwort ein Hashobjekt erzeugt, aus dem das Passwort nicht mehr nachvollzogen werden kann. Liegt jedoch eine Benutzereingabe vor, kann diese über jene Hashfunktion mit dem Hashobjekt verglichen werden, und festgestellt werden, ob es sich um das gültige Passwort handelt.

Die folgende Klasse implementiert eine beispielhafte Authentifizierung über einen LDAP-Server. Dabei wird vorausgesetzt, dass die Hashobjekte mit der Funktion crypt aus dem Sprachumfang der PHP-Distribution erzeugt wurden. Alternativen sind andere Hashalgorithmen wie beispielsweise SHA1.

class LDAP_PWCheck extends LDAP_ModUtil {

  function checkPassword($user, $password ) {
    $this->debug('Attempting authentification of '.$user);

    // searching the DN matching the username
    $this->search("uid=$user",'o=ACME');
    $entries = $this->getEntries();
    $count = $entries['count'];
    if ($count!=1) {
      if ($count<0) {
        $this->debug('no entry for '.$user.' found');
        return LDAP_PWCHECK_USERNOTKNOWN;
      }
      if ($count>1) {
        $this->debug('several entries for '.$user.' found');
        return LDAP_PWCHECK_USERMULTIPLE;
      }
    }
      
    $this->debug('matched username <i>'.$user.'</i> to DN <i>'
     .$entries[0]['dn'].'</i>');
    
    // retrieving the password hash
    $hash = $entries[0]['userpassword'][0];

    // checking whether the user input matches the
    // password hash
    if ( strcmp( crypt($password,$hash), $hash) ) {
      $this->debug('hash '.$hash.' does match '.$password);
      return LDAP_PWCHECK_OK;
    } else {
      $this->debug('hash '.$hash.' does not match '.$password);
      return LDAP_PWCHECK_PWWRONG;
    }

  }
  
  function getClass() { return 'LDAP_PWCheck'; }

}
LDAP_PWCheck.php

In dem folgenden Beispiel wird extra ein Nutzer John Doe erzeugt, zusammen mit einem passenden mit der Funktion crypt erzeugten Hashobjekt für das Passwort whiskey1, um die Kompatibilität der Informationen im Feld userpassword mit den Festlegungen in der Klasse LDAP_PWCheck sicher zu stellen.

<html>
  <body>
    
    <form method="GET">
    login (jdoe):
    <input type="text" name="user">
    <br>
    passphrase (whiskey1):
    <input type="password" name="auth">
    <input type="submit" value="login">
    </form>

<?php
 parse_str($_SERVER['QUERY_STRING']);

 include('LDAP_PWCheck.php');

 $ldappwc = new LDAP_PWCheck('localhost',TRUE);
 $ldappwc->bind('cn=Directory Manager, o=ACME','secret');

 // create a user account
 $entry['sn'] = 'Doe';
 $entry['cn'][0] = 'John';
 $entry['cn'][1] = 'Doe';
 $entry['cn'][2] = 'John Doe';
 $entry['givenname'] = 'John';
 $entry['uid'] = 'jdoe';
 $entry['userpassword'] = 'NYDU/lJH/aY.';
 $ldappwc->createDN('dn=John Doe, o=ACME',$entry);

 $success = $ldappwc->checkPassword($user,$auth);

 if ($success == LDAP_PWCHECK_USERNOTKNOWN ) {
   echo '<p><b>User not known</b></p>';
 } else if ($success == LDAP_PWCHECK_USERMULTIPLE) {
   echo '<p><b>Username not unique ???</b></p>';
 } else if ($success == LDAP_PWCHECK_PWWRONG) {
   echo '<p><b>Password wrong</b></p>';
 } else if ($success == LDAP_PWCHECK_OK) {
   echo '<p style="background:#ffaaaa"><b>Logged in</b></p>';
 } else {
   echo '<p><b>corrupt state of authentification</b></p>';
 }
 
 // delete it again
  $ldappwc->deleteDN('dn=John Doe, o=ACME');
?>
  </body>
</html>
LDAP_PWCheck_test.php

Wie lese ich Fehlermeldungen aus?

Ist ein Fehler aufgetreten, so ist über die Funktion ldap_errno eine Fehlernummer verfügbar. Diese Fehlernummer kann dann über die Funktion ldap_err2str in eine normale Fehlermeldung umgewandelt werden. Diese Fehlermeldung kann auch direkt über ldap_error abgefragt werden. In der Regel ist diese Meldung identisch mit dem Ergebnis von ldap_err2str, manchmal gibt es hilfreiche Nivellierungen.

Im folgenden Beispiel demonstrieren wir kurz die Funktionsweise dieser Funktionen in dem wir einen kleinen Fehler provozieren.

<?php

// this example contains an error...

$cid = ldap_connect("localhost");
$bid = ldap_bind($cid);

// syntax error in filter expression (errno 87),
// must be "objectclass=*" to work

$result =  @ldap_search($cid, "o=ACME", "objectclass");
if (!$result) {
    echo 'ldap_errno():'. ldap_errno($cid) .'<br>';
    echo 'ldap_error():'. ldap_error($cid) .'<br>';
    echo 'ldap_err2str():'
     .ldap_err2str(ldap_errno($cid)).'<br>';
}

?>
LDAP_Errno_test.php

    Comelio GmbH PHP: Zugriff und Verwendung von LDAP Anleitung Tutorial Handbuch PHP SQL MySQL SQLJ LDAP XML Hamburg Bochum Lübeck Bonn Mannheim Freiburg Hannover Berlin Aachen Leipzig Erlangen Frankfurt Ingolstadt Rügen Bremen Kiel München Würzuburg Ol Köln Wolfsburg Stuttgart Heidelberg Ludwigshafen Göttingen Koblenz Magdeburg Zwickau Andernach Kassel KoblenzComelio GmbH PHP: Zugriff und Verwendung von LDAP Anleitung Tutorial Handbuch PHP SQL MySQL SQLJ LDAP XML Hamburg Bochum Lübeck Bonn Mannheim Freiburg Hannover Berlin Aachen Leipzig Erlangen Frankfurt Ingolstadt Rügen Bremen Kiel München Würzuburg Ol Köln Wolfsburg Stuttgart Heidelberg Ludwigshafen Göttingen Koblenz Magdeburg Zwickau Andernach Kassel KoblenzComelio GmbH PHP: Zugriff und Verwendung von LDAP Anleitung Tutorial Handbuch PHP SQL MySQL SQLJ LDAP XML Hamburg Bochum Lübeck Bonn Mannheim Freiburg Hannover Berlin Aachen Leipzig Erlangen Frankfurt Ingolstadt Rügen Bremen Kiel München Würzuburg Ol Köln Wolfsburg Stuttgart Heidelberg Ludwigshafen Göttingen Koblenz Magdeburg Zwickau Andernach Kassel KoblenzComelio GmbH PHP: Zugriff und Verwendung von LDAP Anleitung Tutorial Handbuch PHP SQL MySQL SQLJ LDAP XML Hamburg Bochum Lübeck Bonn Mannheim Freiburg Hannover Berlin Aachen Leipzig Erlangen Frankfurt Ingolstadt Rügen Bremen Kiel München Würzuburg Ol Köln Wolfsburg Stuttgart Heidelberg Ludwigshafen Göttingen Koblenz Magdeburg Zwickau Andernach Kassel KoblenzComelio GmbH PHP: Zugriff und Verwendung von LDAP Anleitung Tutorial Handbuch PHP SQL MySQL SQLJ LDAP XML Hamburg Bochum Lübeck Bonn Mannheim Freiburg Hannover Berlin Aachen Leipzig Erlangen Frankfurt Ingolstadt Rügen Bremen Kiel München Würzuburg Ol Köln Wolfsburg Stuttgart Heidelberg Ludwigshafen Göttingen Koblenz Magdeburg Zwickau Andernach Kassel KoblenzComelio GmbH PHP: Zugriff und Verwendung von LDAP Anleitung Tutorial Handbuch PHP SQL MySQL SQLJ LDAP XML Hamburg Bochum Lübeck Bonn Mannheim Freiburg Hannover Berlin Aachen Leipzig Erlangen Frankfurt Ingolstadt Rügen Bremen Kiel München Würzuburg Ol Köln Wolfsburg Stuttgart Heidelberg Ludwigshafen Göttingen Koblenz Magdeburg Zwickau Andernach Kassel KoblenzComelio GmbH PHP: Zugriff und Verwendung von LDAP Anleitung Tutorial Handbuch PHP SQL MySQL SQLJ LDAP XML Hamburg Bochum Lübeck Bonn Mannheim Freiburg Hannover Berlin Aachen Leipzig Erlangen Frankfurt Ingolstadt Rügen Bremen Kiel München Würzuburg Ol Köln Wolfsburg Stuttgart Heidelberg Ludwigshafen Göttingen Koblenz Magdeburg Zwickau Andernach Kassel KoblenzComelio GmbH PHP: Zugriff und Verwendung von LDAP Anleitung Tutorial Handbuch PHP SQL MySQL SQLJ LDAP XML Hamburg Bochum Lübeck Bonn Mannheim Freiburg Hannover Berlin Aachen Leipzig Erlangen Frankfurt Ingolstadt Rügen Bremen Kiel München Würzuburg Ol Köln Wolfsburg Stuttgart Heidelberg Ludwigshafen Göttingen Koblenz Magdeburg Zwickau Andernach Kassel KoblenzComelio GmbH PHP: Zugriff und Verwendung von LDAP Anleitung Tutorial Handbuch PHP SQL MySQL SQLJ LDAP XML Hamburg Bochum Lübeck Bonn Mannheim Freiburg Hannover Berlin Aachen Leipzig Erlangen Frankfurt Ingolstadt Rügen Bremen Kiel München Würzuburg Ol Köln Wolfsburg Stuttgart Heidelberg Ludwigshafen Göttingen Koblenz Magdeburg Zwickau Andernach Kassel Koblenz
Seminare