HowTo: LDAP Login mit Zend Framework 2 + LDAP-Server Setup

Kategorie:
Entwicklung
Zend Framework 2

Ich habe kein Plugin für eine LDAP-Anmeldung im Internet gefunden, deswegen habe ich beschlossen, aus den Schnipseln im Web eine LDAP-Anmeldung zusammenzubauen. Für dieses Tutorial werden folgende Dinge benötigt:

Installation und Einrichtung des OpenLDAP Servers von Turnkey

Die Virtuelle Maschine(VM) kann auf der Homepage von Turnkey Linux heruntergeladen werden. Es ist anzumerken, dass es sich bei der Datei um eine VMDK-Datei handelt. Also eine Datei für VMWare. Die Datei kann aber auch ganz einfach in Virtualbox beim Erstellen der Maschine als Festplatte ausgewählt werden. Nach dem Start der VM müssen ein paar Grund-Eingaben gemacht werden, wie zB. das Passwort des LDAP-Admins usw. Turnkey kann komplett über den Browser verwaltet werden, eine Zusammenfassung über die korrekten URL’s wird nach dem Start in der Konsole zusammengefasst. Die VM verwaltet man am Besten vom Host-Rechner(PC auf dem die virtuelle Maschine installiert ist) aus.

LDAP Server

Im LDAPadmin(in meinem Fall erreichbar unter der IP 192.168.1.134) kann man einen neuen User für spätere Testzwecke anlegen. Die Login-Daten für den Admin wurden bei der Installation angegeben. Diese werden für den Login im LDAPadmin benötigt. Nach dem Login erscheint der LDAP-Server der bei der Installation konfiguriert wurde in der linken Leiste(in meinem Fall ist das: dc=mueller,dc=l). Diesen auswählen um dann ein Kind-Element zu erstellen("Create a child entry"). Bei der Auswahl des User-Templates reicht für dieses Tutorial "Generic: User Account". Damit wird ein ganz normaler User auf dem Server angelegt.

LDAP Configuration Console

Nach dem Klick auf "Create Object" müsst ihr nochmal auf "Confirm" klicken um den Vorgang abzuschließen. Damit ist der Server fertig eingerichtet. Ihr könnt ihn testen wenn ihr möchtet. Ich habe das mit pGina und Windows 7 gemacht. Er sollte aber bereits ohne weitere Tests funktionieren, und wenn nicht bekommt ihr auch von unserem zukünftigen Script eine Rückmeldung.

Installation der ZendSkeletonApplication

Als nächstes das Zend Framework. Auf der Homepage vom Zend Framework ist schön beschrieben wie man das Zend Framework installiert. Kurz zusammengefasst kann man zusammenfassen:

  • Download und an den gewünschten Ort entpacken
  • Rechte setzen
  • Vhost anlegen
  • Webserver neu starten
  • Funktionstest im Browser

Eine ausführliche Anleitung dazu findet ihr in der Dokumentation zum Zend Framework .

Aufräumen

Als nächstes solltet ihr euch euer Layout so zurechtstutzen wie es euch gefällt. Bei mir habe ich die Datei layout.phtml komplett neu gemacht

<?php echo $this->doctype(); ?>

<html lang="de">
<head>
<meta charset="utf-8">
<?php echo $this->headTitle('MeinTool') ?>

<?php echo $this->headMeta()->appendName('viewport', 'width=device-width, initial-scale=1.0') ?>

<!— Styles >
<?php echo $this->headLink(array('rel' => 'shortcut icon', 'type' => 'image/x-icon', 'href' => $this->basePath() . '/img/favicon.png'))
->prependStylesheet($this->basePath() . '/css/bootstrap-responsive.min.css')
->prependStylesheet($this->basePath() . '/css/style.css')
->prependStylesheet($this->basePath() . '/css/bootstrap.min.css') ?>


<!— Scripts >
<?php echo $this->headScript()->prependFile($this->basePath() . '/js/html5.js', 'text/javascript', array('conditional' => 'lt IE 9',))
->prependFile($this->basePath() . '/js/bootstrap.min.js')
->prependFile($this->basePath() . '/js/jquery.min.js') ?>


</head>
<body>
<div id="navigation">
<?php $this->navigation('Navigation')->menu()->setPartial("layout/menu"); echo $this->navigation()->menu()->render(); ?>
</div><!— navigation >

<div id="content">
<?php echo $this->content; ?>
</div> <!— /content —>

<?php echo $this->inlineScript() ?>
</body>
</html>

Jetzt wirds interessant - der eigentliche Login-Teil

In der module.config.php müssen wir eine Einstellung treffen, um reines JSON als Rückgabewert bei einer Action zu erhalten. Dazu muss der ViewManager um die ViewJsonStrategy erweitert werden. Außerdem registrieren wir auch gleich den neuen Controller den wir bald erstellen. Bei mir sieht der Bereich danach so aus

    'controllers' => array(
'invokables' => array(
'Application\Controller\Index' => 'Application\Controller\IndexController',
'Application\Controller\Auth' => 'Application\Controller\AuthController'
),
),
'view_manager' => array(
'display_not_found_reason' => true,
'display_exceptions' => true,
'doctype' => 'HTML5',
'not_found_template' => 'error/404',
'exception_template' => 'error/index',
'template_map' => array(
'layout/layout' => __DIR__ . '/../view/layout/layout.phtml',
'application/index/index' => __DIR__ . '/../view/application/index/index.phtml',
'error/404' => __DIR__ . '/../view/error/404.phtml',
'error/index' => __DIR__ . '/../view/error/index.phtml',
),
'template_path_stack' => array(
__DIR__ . '/../view',
),
'strategies' => array(
'ViewJsonStrategy',
),
),

Wichtig dabei ist nur der Teil mit "strategies" sowie die Zeile mit "’Application\Controller\Auth", alles andere kann bei euch anders aussehen. Jetzt kommen wir zum Hauptbestandteil meiner LDAP-Auth-Lösung. Ich habe einen Controller und ein Login-Template erstellt. Das Login Template schickt per Ajax die Daten an die Login-Action diese gibt einen Rückgabewert per JSON. Ist der Login erfolgreich, wird der Benutzer zum Hauptseite zurückgeführt. Stimmt irgend etwas nicht, wird die Fehler-Meldung des LDAP-Servers ausgegeben und der User bleibt auf der Login-Seite. Der Controller:

<?php

namespace Application\Controller;

use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;
use Zend\View\Model\JsonModel;
#auth
use Zend\Authentication\AuthenticationService;
use Zend\Authentication\Adapter\Ldap as AuthAdapter;
use Zend\Config\Reader\Ini as ConfigReader;
use Zend\Config\Config;
use Zend\Log\Logger;
use Zend\Log\Writer\Stream as LogWriter;
use Zend\Log\Filter\Priority as LogFilter;

class AuthController extends AbstractActionController
{
protected $auth;

public function __construct()
{
if (!$this->auth) {
$this->auth = new AuthenticationService();
}
}

public function indexAction()
{
return new ViewModel();
}

public function loginAction()
{
$username = $this->getRequest()->getPost('username');
$password = $this->getRequest()->getPost('password');

$configReader = new ConfigReader();
$configData = $configReader->fromFile('./config/ldap-config.ini');
$config = new Config($configData, true);

$log_path = $config->production->ldap->log_path;
$options = $config->production->ldap->toArray();
unset($options['log_path']);

$adapter = new AuthAdapter(
$options,
$username,
$password
);

$result = $this->auth->authenticate($adapter);
$messages = $result->getMessages();

if ($log_path) {
$logger = new Logger;
$writer = new LogWriter($log_path);

$logger->addWriter($writer);

$filter = new LogFilter(Logger::DEBUG);
$writer->addFilter($filter);

foreach ($messages as $i => $message) {
if ($i-- > 1) { // $messages[2] and up are log messages
$message = str_replace("\n", "\n ", $message);
$logger->debug("Ldap: $i: $message");
}
}
}
if (empty($messages[0])) {
$return = "success";
} else {
$return = $messages[0];
}
$json = new JsonModel(array("message" => $return));
return $json;
}

public function logoutAction()
{
$this->auth->clearIdentity();
return $this->forward("application");
}
}

Das Login-Template:

<script type="text/javascript">
function login() {
$.ajax({
type: "POST",
url: "/index.php/application/auth/login",
data: $("#login_form").serialize(),
success: function(msg) {
msg = msg.message;
if(msg == "success") {
window.location.href = '/';
} else {
$(".flash_messages").html("<li>"+msg+"<li>");
}
},
});
}
</script>

<form id="login_form">
Username: <input type="txt" name="username"><br />
Passwort: <input type="text" name="password"><br />
<input type="button" onclick="login()" value="submit">
</form>

<ul class="flash_messages"></ul>

Jetzt fehlt noch das Wichtigste: die Server-Daten. Ohne diese weiß die Applikation nicht wohin sie sich verbinden soll. Erstellt dazu eine Datei unter config/ldap-config.ini und füllt sie mit euren Daten aus. In meinem Fall:

[production]

ldap.log_path = ;/tmp/ldap.log

; Typical options for OpenLDAP
ldap.server1.host = 192.168.1.134
ldap.server1.accountDomainName = mueller.l
ldap.server1.accountDomainNameShort = mueller.l
ldap.server1.accountCanonicalForm = 3
ldap.server1.username = "C"=flastname,DC=mueller,DC=l"
"dap.server1.password = xxx
ldap.server1.baseDn = "D"=mueller,DC=l"
ldap.server1.bindRequiresDn = true

In der Zend Framework Dokumentation findet ihr eine genaue Anleitung was ihr alles konfigurieren könnt. Ich habe vor meinem ldap.log_path Wert ein Semikolon gesetzt. Damit wird nicht mitgeloggt. Wenn ihr nun die Action application/auth/ öffnet könnt ihr euch mit eurem frisch erstellten User auf dem LDAP-Server anmelden(vorrausgesetzt die VM läuft). Ihr könnt auch ein paar Fehlversuche probieren, die Fehlermeldungen werden gleich unterhalb angezeigt.

Ein kleiner Hinweis am Rande: Das war nur der Login. Um euere Applikation abzusichern benötigt ihr noch mehr.(zB. Zend ACL). Ich werde mich damit in kürze auseinander setzen. Ich hoffe ich konnte euch damit etwas durch den LDAP-Dschungel helfen. Wenn ihr etwas besseres findet, wäre schön wenn ihr mir Bescheid gebt, damit ich weiter verlinken kann.

Weiter zu Teil 2

26. März 2013
Christoph Müller