Files
server/usr/share/psa-pear/pear/php/Horde/Auth/Msad.php
2026-01-07 20:52:11 +01:00

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'];
}
}