291 lines
9.5 KiB
PHP
291 lines
9.5 KiB
PHP
<?php
|
|
/**
|
|
* Copyright 2007-2017 Horde LLC (http://www.horde.org/)
|
|
*
|
|
* See the enclosed file COPYING for license information (LGPL). If you did
|
|
* not receive this file, see http://www.horde.org/licenses/lgpl21.
|
|
*
|
|
* @author Francois Helly <fhelly@bebop-design.net>
|
|
* @category Horde
|
|
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
|
|
* @package Auth
|
|
*/
|
|
|
|
/**
|
|
* The Horde_Auth_Msad class provides an experimental MSAD extension of the
|
|
* LDAP implementation of the Horde authentication system.
|
|
*
|
|
* @author Francois Helly <fhelly@bebop-design.net>
|
|
* @category Horde
|
|
* @copyright 2007-2017 Horde LLC
|
|
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
|
|
* @package Auth
|
|
* @todo Use Horde_Ldap
|
|
*/
|
|
class Horde_Auth_Msad extends Horde_Auth_Ldap
|
|
{
|
|
/**
|
|
* Constructor.
|
|
*
|
|
* @param array $params A hash containing connection parameters.
|
|
*
|
|
* @throws Horde_Auth_Exception
|
|
*/
|
|
public function __construct($params = array())
|
|
{
|
|
$params = array_merge(array(
|
|
'adduser' => true,
|
|
'authId' => 'initials',
|
|
'encryption' => 'msad',
|
|
'newuser_objectclass' => 'user',
|
|
'password_expiration' => 'no',
|
|
'port' => 389,
|
|
'ssl' => false,
|
|
'uid' => array('samaccountname')
|
|
), $params);
|
|
|
|
if (!is_array($params['uid'])) {
|
|
$params['uid'] = array($params['uid']);
|
|
}
|
|
|
|
/* Ensure we've been provided with all of the necessary parameters. */
|
|
//Horde::assertDriverConfig($params, 'auth',
|
|
// array('hostspec', 'basedn'), 'authentication MSAD');
|
|
|
|
/* Adjust capabilities: depending on if SSL encryption is
|
|
* enabled or not */
|
|
$this->_capabilities = array(
|
|
'add' => ($params['ssl'] || $params['adduser']),
|
|
'list' => true,
|
|
'remove' => true,
|
|
'resetpassword' => $params['ssl'],
|
|
'update' => $params['ssl']
|
|
);
|
|
|
|
parent::__construct($params);
|
|
}
|
|
|
|
/**
|
|
* Add a set of authentication credentials.
|
|
*
|
|
* @param string $accountName The user sAMAccountName to find.
|
|
* @param array $credentials The credentials to be set.
|
|
*
|
|
* @throws Horde_Auth_Exception
|
|
*/
|
|
public function addUser($accountName, $credentials)
|
|
{
|
|
/* Connect to the MSAD server. */
|
|
$this->_connect();
|
|
|
|
if (isset($credentials['ldap'])) {
|
|
$dn = $credentials['ldap']['dn'];
|
|
} else {
|
|
$basedn = isset($credentials['basedn'])
|
|
? $credentials['basedn']
|
|
: $this->_params['basedn'];
|
|
|
|
/* Set a default CN */
|
|
$dn = 'cn=' . $accountName . ',' . $basedn;
|
|
|
|
$entry['cn'] = $accountName;
|
|
$entry['samaccountname'] = $accountName;
|
|
|
|
$entry['objectclass'][0] = "top";
|
|
$entry['objectclass'][1] = "person";
|
|
$entry['objectclass'][2] = "organizationalPerson";
|
|
$entry['objectclass'][3] = "user";
|
|
|
|
$entry['description'] = (isset($credentials['description'])) ?
|
|
$credentials['description'] : 'New horde user';
|
|
|
|
if ($this->_params['ssl']) {
|
|
$entry["AccountDisabled"] = false;
|
|
}
|
|
$entry['userPassword'] = Horde_Auth::getCryptedPassword($credentials['password'],'',
|
|
$this->_params['encryption'],
|
|
false);
|
|
|
|
if (isset($this->_params['binddn'])) {
|
|
$entry['manager'] = $this->_params['binddn'];
|
|
}
|
|
|
|
}
|
|
|
|
$success = @ldap_add($this->_ds, $dn, $entry);
|
|
|
|
if (!$success) {
|
|
throw new Horde_Auth_Exception(sprintf(__CLASS__ . ': Unable to add user "%s". This is what the server said: ', $accountName) . ldap_error($this->_ds));
|
|
}
|
|
|
|
@ldap_close($this->_ds);
|
|
}
|
|
|
|
/**
|
|
* Remove a set of authentication credentials.
|
|
*
|
|
* @param string $accountName The user sAMAccountName to remove.
|
|
* @param string $dn TODO
|
|
*
|
|
* @throws Horde_Auth_Exception
|
|
*/
|
|
public function removeUser($accountName, $dn = null)
|
|
{
|
|
/* Connect to the MSAD server. */
|
|
$this->_connect();
|
|
|
|
if (is_null($dn)) {
|
|
/* Search for the user's full DN. */
|
|
$dn = $this->_findDN($accountName);
|
|
}
|
|
|
|
if (!@ldap_delete($this->_ds, $dn)) {
|
|
throw new Horde_Auth_Exception(sprintf(__CLASS__ . ': Unable to remove user "%s"', $accountName));
|
|
}
|
|
@ldap_close($this->_ds);
|
|
}
|
|
|
|
/**
|
|
* Update a set of authentication credentials.
|
|
*
|
|
* @param string $oldId The old userId.
|
|
* @param string $newId The new userId.
|
|
* @param array $credentials The new credentials.
|
|
* @param string $olddn The old user DN.
|
|
* @param string $newdn The new user DN.
|
|
*
|
|
* @throws Horde_Auth_Exception
|
|
*/
|
|
public function updateUser($oldId, $newId, $credentials, $olddn = null,
|
|
$newdn = null)
|
|
{
|
|
/* Connect to the MSAD server. */
|
|
$this->_connect();
|
|
|
|
if (isset($credentials['ldap'])) {
|
|
$olddn = $credentials['ldap']['dn'];
|
|
} else {
|
|
/* Search for the user's full DN. */
|
|
$dn = $this->_findDN($oldId);
|
|
|
|
/* Encrypt the new password */
|
|
if (isset($credentials['password'])) {
|
|
$entry['userpassword'] = Horde_Auth::getCryptedPassword($credentials['password'],'',
|
|
$this->_params['encryption'],
|
|
true);
|
|
}
|
|
}
|
|
|
|
if ($oldId != $newID) {
|
|
$newdn = str_replace($oldId, $newID, $dn);
|
|
ldap_rename($this->_ds, $olddn, $newdn, $this->_params['basedn'], true);
|
|
$success = @ldap_modify($this->_ds, $newdn, $entry);
|
|
} else {
|
|
$success = @ldap_modify($this->_ds, $olddn, $entry);
|
|
}
|
|
|
|
if (!$success) {
|
|
throw new Horde_Auth_Exception(sprintf(__CLASS__ . ': Unable to update user "%s"', $newID));
|
|
}
|
|
|
|
@ldap_close($this->_ds);
|
|
}
|
|
|
|
/**
|
|
* Reset a user's password. Used for example when the user does not
|
|
* remember the existing password.
|
|
*
|
|
* @param string $user_id The user id for which to reset the password.
|
|
*
|
|
* @return string The new password on success.
|
|
* @throws Horde_Auth_Exception
|
|
*/
|
|
public function resetPassword($user_id)
|
|
{
|
|
/* Get a new random password. */
|
|
$password = Horde_Auth::genRandomPassword() . '/';
|
|
$this->updateUser($user_id, $user_id, array('userPassword' => $password));
|
|
|
|
return $password;
|
|
}
|
|
|
|
/**
|
|
* Does an ldap connect and binds as the guest user.
|
|
*
|
|
* @throws Horde_Auth_Exception
|
|
*/
|
|
protected function _connect()
|
|
{
|
|
/* Connect to the MSAD server. */
|
|
$ssl = ($this->_params['ssl']) ? 'ldaps://' : '';
|
|
$this->_ds = ldap_connect($ssl . $this->_params['hostspec'], $this->_params['port']);
|
|
if (!$this->_ds) {
|
|
throw new Horde_Auth_Exception('Failed to connect to MSAD server.');
|
|
}
|
|
|
|
if ($this->_logger) {
|
|
if (!ldap_set_option($this->_ds, LDAP_OPT_PROTOCOL_VERSION, 3)) {
|
|
$this->_logger->log(sprintf('Set MSAD protocol version to %d failed: [%d] %s', 3, ldap_errno($conn), ldap_error($conn)));
|
|
}
|
|
if (!ldap_set_option($this->_ds, LDAP_OPT_REFERRALS, 0)) {
|
|
$this->_logger->log(sprintf('Set MSAD referrals option to %d failed: [%d] %s', 0, ldap_errno($conn), ldap_error($conn)));
|
|
}
|
|
}
|
|
|
|
if (isset($this->_params['binddn'])) {
|
|
$bind = ldap_bind($this->_ds,
|
|
$this->_params['binddn'],
|
|
$this->_params['password']);
|
|
} else {
|
|
$bind = ldap_bind($this->_ds);
|
|
}
|
|
|
|
if (!$bind) {
|
|
throw new Horde_Auth_Exception('Could not bind to MSAD server.');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Find the user dn
|
|
*
|
|
* @param string $userId The user UID to find.
|
|
*
|
|
* @return string The user's full DN
|
|
*/
|
|
protected function _findDN($userId)
|
|
{
|
|
/* Search for the user's full DN. */
|
|
foreach ($this->_params['uid'] as $uid) {
|
|
$entries = array($uid);
|
|
if ($uid != $this->_params['authId']) {
|
|
$entries[] = $this->_params['authId'];
|
|
}
|
|
$search = @ldap_search($this->_ds, $this->_params['basedn'],
|
|
$uid . '=' . $userId,
|
|
$entries
|
|
);
|
|
/* Searching the tree is not successful */
|
|
if (!$search) {
|
|
throw new Horde_Auth_Exception('Could not search the MSAD server.');
|
|
}
|
|
|
|
/* Fetch the search result */
|
|
$result = @ldap_get_entries($this->_ds, $search);
|
|
/* The result isn't empty: the DN was found */
|
|
if (is_array($result) && (count($result) > 1)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!is_array($result) || (count($result) <= 1)) {
|
|
throw new Horde_Auth_Exception('Empty result.');
|
|
}
|
|
|
|
/* Be sure the horde userId is the configured one */
|
|
$this->_credentials['userId'] = $result[0][$this->_params['authId']][0];
|
|
|
|
return $result[0]['dn'];
|
|
}
|
|
|
|
}
|