From 0fd2c89db2a6fa07164fb01c49e303a66b162e10 Mon Sep 17 00:00:00 2001
From: Demian Katz <demian.katz@villanova.edu>
Date: Thu, 12 Jul 2012 10:23:27 -0400
Subject: [PATCH] Added LDAP authentication.

---
 module/VuFind/src/VuFind/Auth/LDAP.php | 229 +++++++++++++++++++++++++
 1 file changed, 229 insertions(+)
 create mode 100644 module/VuFind/src/VuFind/Auth/LDAP.php

diff --git a/module/VuFind/src/VuFind/Auth/LDAP.php b/module/VuFind/src/VuFind/Auth/LDAP.php
new file mode 100644
index 00000000000..37ed40a17c2
--- /dev/null
+++ b/module/VuFind/src/VuFind/Auth/LDAP.php
@@ -0,0 +1,229 @@
+<?php
+/**
+ * LDAP authentication class
+ *
+ * PHP version 5
+ *
+ * Copyright (C) Villanova University 2010.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * @category VuFind2
+ * @package  Authentication
+ * @author   Franck Borel <franck.borel@gbv.de>
+ * @author   Demian Katz <demian.katz@villanova.edu>
+ * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     http://vufind.org/wiki/building_an_authentication_handler Wiki
+ */
+namespace VuFind\Auth;
+use VuFind\Db\Table\User as UserTable, VuFind\Exception\Auth as AuthException;
+
+/**
+ * LDAP authentication class
+ *
+ * @category VuFind2
+ * @package  Authentication
+ * @author   Franck Borel <franck.borel@gbv.de>
+ * @author   Demian Katz <demian.katz@villanova.edu>
+ * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     http://vufind.org/wiki/building_an_authentication_handler Wiki
+ */
+class LDAP extends AbstractBase
+{
+    protected $username;
+    protected $password;
+
+    /**
+     * Constructor
+     *
+     * @param object $config Optional configuration object to pass through (loads
+     * default configuration if none specified).
+     *
+     * @throws AuthException
+     */
+    public function __construct($config = null)
+    {
+        parent::__construct($config);
+        $this->validateConfiguration();
+    }
+
+    /**
+     * Validate configuration parameters.
+     *
+     * @throws AuthException
+     * @return void
+     */
+    protected function validateConfiguration()
+    {
+        // Check for missing parameters:
+        $requiredParams = array('host', 'port', 'basedn', 'username');
+        foreach ($requiredParams as $param) {
+            if (!isset($this->config->LDAP->$param)
+                || empty($this->config->LDAP->$param)
+            ) {
+                throw new AuthException(
+                    "One or more LDAP parameter are missing. Check your config.ini!"
+                );
+            }
+        }
+    }
+
+    /**
+     * Get the requested configuration parameter (or blank string if unset).
+     *
+     * @param string $name Name of parameter to retrieve.
+     *
+     * @return string
+     */
+    protected function getConfig($name)
+    {
+        $value = isset($this->config->LDAP->$name)
+            ? $this->config->LDAP->$name : '';
+
+        // Normalize all values to lowercase except for potentially case-sensitive
+        // bind and basedn credentials.
+        $doNotLower = array('bind_username', 'bind_password', 'basedn');
+        return (in_array($name, $doNotLower)) ? $value : strtolower($value);
+    }
+
+    /**
+     * Attempt to authenticate the current user.  Throws exception if login fails.
+     *
+     * @param \Zend\Http\PhpEnvironment\Request $request Request object containing
+     * account credentials.
+     *
+     * @throws AuthException
+     * @return \VuFind\Db\Row\User Object representing logged-in user.
+     */
+    public function authenticate($request)
+    {
+        $this->username = trim($request->getPost()->get('username'));
+        $this->password = trim($request->getPost()->get('password'));
+        if ($this->username == '' || $this->password == '') {
+            throw new AuthException('authentication_error_blank');
+        }
+        return $this->bindUser();
+    }
+
+    /**
+     * Communicate with LDAP and obtain user details.
+     *
+     * @throws AuthException
+     * @return \VuFind\Db\Row\User Object representing logged-in user.
+     */
+    protected function bindUser()
+    {
+        // Try to connect to LDAP and die if we can't; note that some LDAP setups
+        // will successfully return a resource from ldap_connect even if the server
+        // is unavailable -- we need to check for bad return values again at search
+        // time!
+        $ldapConnection = @ldap_connect(
+            $this->getConfig('host'), $this->getConfig('port')
+        );
+        if (!$ldapConnection) {
+            throw new AuthException('authentication_error_technical');
+        }
+
+        // Set LDAP options -- use protocol version 3
+        @ldap_set_option($ldapConnection, LDAP_OPT_PROTOCOL_VERSION, 3);
+
+        // if the host parameter is not specified as ldaps://
+        // then we need to initiate TLS so we
+        // can have a secure connection over the standard LDAP port.
+        if (stripos($this->getConfig('host'), 'ldaps://') === false) {
+            if (!@ldap_start_tls($ldapConnection)) {
+                throw new AuthException('authentication_error_technical');
+            }
+        }
+
+        // If bind_username and bind_password were supplied in the config file, use
+        // them to access LDAP before proceeding.  In some LDAP setups, these
+        // settings can be excluded in order to skip this step.
+        if ($this->getConfig('bind_username') != ''
+            && $this->getConfig('bind_password') != ''
+        ) {
+            $ldapBind = @ldap_bind(
+                $ldapConnection, $this->getConfig('bind_username'),
+                $this->getConfig('bind_password')
+            );
+            if (!$ldapBind) {
+                throw new AuthException('authentication_error_technical');
+            }
+        }
+
+        // Search for username
+        $ldapFilter = $this->getConfig('username') . '=' . $this->username;
+        $ldapSearch = @ldap_search(
+            $ldapConnection, $this->getConfig('basedn'), $ldapFilter
+        );
+        if (!$ldapSearch) {
+            throw new AuthException('authentication_error_technical');
+        }
+
+        $info = ldap_get_entries($ldapConnection, $ldapSearch);
+        if ($info['count']) {
+            // Validate the user credentials by attempting to bind to LDAP:
+            $ldapBind = @ldap_bind(
+                $ldapConnection, $info[0]['dn'], $this->password
+            );
+            if ($ldapBind) {
+                // If the bind was successful, we can look up the full user info:
+                $ldapSearch = ldap_search(
+                    $ldapConnection, $this->getConfig('basedn'), $ldapFilter
+                );
+                $data = ldap_get_entries($ldapConnection, $ldapSearch);
+                return $this->processLDAPUser($data);
+            }
+        }
+
+        throw new AuthException('authentication_error_invalid');
+    }
+
+    /**
+     * Build a User object from details obtained via LDAP.
+     *
+     * @param array $data Details from ldap_get_entries call.
+     *
+     * @return \VuFind\Db\Row\User Object representing logged-in user.
+     */
+    protected function processLDAPUser($data)
+    {
+        // Database fields that we may be able to load from LDAP:
+        $fields = array(
+            'firstname', 'lastname', 'email', 'cat_username', 'cat_password',
+            'college', 'major'
+        );
+
+        // User object to populate from LDAP:
+        $table = new UserTable();
+        $user = $table->getByUsername($this->username);
+
+        // Loop through LDAP response and map fields to database object based
+        // on configuration settings:
+        for ($i = 0; $i < $data["count"]; $i++) {
+            for ($j = 0; $j < $data[$i]["count"]; $j++) {
+                foreach ($fields as $field) {
+                    $configValue = $this->getConfig($field);
+                    if ($data[$i][$j] == $configValue && !empty($configValue)) {
+                        $user->$field = $data[$i][$data[$i][$j]][0];
+                    }
+                }
+            }
+        }
+
+        // Update the user in the database, then return it to the caller:
+        $user->save();
+        return $user;
+    }
+}
\ No newline at end of file
-- 
GitLab