Files
sysPass/lib/SP/Providers/Auth/Ldap/LdapActions.php
Rubén D 41703b50f9 * [MOD] Strict type checking (WIP)
Signed-off-by: Rubén D <nuxsmin@syspass.org>
2020-12-20 14:24:42 +01:00

305 lines
8.4 KiB
PHP

<?php
/*
* sysPass
*
* @author nuxsmin
* @link https://syspass.org
* @copyright 2012-2020, Rubén Domínguez nuxsmin@$syspass.org
*
* This file is part of sysPass.
*
* sysPass is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* sysPass 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 sysPass. If not, see <http://www.gnu.org/licenses/>.
*/
/** @noinspection PhpComposerExtensionStubsInspection */
namespace SP\Providers\Auth\Ldap;
use SP\Core\Events\Event;
use SP\Core\Events\EventDispatcher;
use SP\Core\Events\EventMessage;
/**
* Class LdapActions
*
* @package SP\Providers\Auth\Ldap
*/
final class LdapActions
{
/**
* Atributos de búsqueda
*/
const USER_ATTRIBUTES = [
'dn',
'displayname',
'samaccountname',
'mail',
'memberof',
'lockouttime',
'fullname',
'groupmembership',
'uid',
'givenname',
'sn',
'userprincipalname',
'cn'
];
const ATTRIBUTES_MAPPING = [
'dn' => 'dn',
'groupmembership' => 'group',
'memberof' => 'group',
'displayname' => 'fullname',
'fullname' => 'fullname',
'givenname' => 'name',
'sn' => 'sn',
'mail' => 'mail',
'lockouttime' => 'expire'
];
/**
* @var LdapParams
*/
private $ldapParams;
/**
* @var resource
*/
private $ldapHandler;
/**
* @var EventDispatcher
*/
private $eventDispatcher;
/**
* LdapActions constructor.
*
* @param LdapConnectionInterface $ldapConnection
* @param EventDispatcher $eventDispatcher
*
* @throws LdapException
*/
public function __construct(LdapConnectionInterface $ldapConnection, EventDispatcher $eventDispatcher)
{
$this->ldapHandler = $ldapConnection->connectAndBind();
$this->ldapParams = $ldapConnection->getLdapParams();
$this->eventDispatcher = $eventDispatcher;
}
/**
* Obtener el RDN del grupo.
*
* @param string $groupFilter
*
* @return array Groups' DN
* @throws LdapException
*/
public function searchGroupsDn(string $groupFilter): array
{
$filter = '(&(cn='
. ldap_escape($this->getGroupFromParams(), null, LDAP_ESCAPE_FILTER)
. ')'
. $groupFilter
. ')';
$searchResults = $this->getResults($filter, ['dn']);
if ((int)$searchResults['count'] === 0) {
$this->eventDispatcher->notifyEvent('ldap.search.group',
new Event($this, EventMessage::factory()
->addDescription(__u('Error while searching the group RDN'))
->addDetail(__u('Group'), $this->getGroupFromParams())
->addDetail('LDAP ERROR', LdapConnection::getLdapErrorMessage($this->ldapHandler))
->addDetail('LDAP FILTER', $filter))
);
throw new LdapException(
__u('Error while searching the group RDN'),
LdapException::ERROR,
null,
LdapCode::NO_SUCH_OBJECT
);
}
return array_filter(array_map(function ($value) {
if (is_array($value)) {
return $value['dn'];
}
return null;
}, $searchResults));
}
/**
* @return string
*/
protected function getGroupFromParams(): string
{
if (stripos($this->ldapParams->getGroup(), 'cn') === 0) {
return LdapUtil::getGroupName($this->ldapParams->getGroup());
}
return $this->ldapParams->getGroup();
}
/**
* Devolver los resultados de una paginación
*
* @param string $filter Filtro a utilizar
* @param array|null $attributes Atributos a devolver
* @param string|null $searchBase
*
* @return bool|array
*/
protected function getResults(
string $filter,
?array $attributes = null,
?string $searchBase = null
)
{
$cookie = '';
$results = [];
if (empty($searchBase)) {
$searchBase = $this->ldapParams->getSearchBase();
}
do {
ldap_control_paged_result(
$this->ldapHandler,
LdapInterface::PAGE_SIZE,
false,
$cookie
);
$searchRes = @ldap_search(
$this->ldapHandler,
$searchBase,
$filter,
$attributes
);
if (!$searchRes) {
return false;
}
$entries = @ldap_get_entries($this->ldapHandler, $searchRes);
if (!$entries) {
return false;
}
$results = array_merge($results, $entries);
ldap_control_paged_result_response(
$this->ldapHandler,
$searchRes,
$cookie
);
} while ($cookie !== null && $cookie != '');
return $results;
}
/**
* Obtener los atributos del usuario.
*
* @param string $filter
*
* @return AttributeCollection
* @throws LdapException
*/
public function getAttributes(string $filter): AttributeCollection
{
$searchResults = $this->getObjects($filter);
if ((int)$searchResults['count'] === 0) {
$this->eventDispatcher->notifyEvent('ldap.getAttributes',
new Event($this, EventMessage::factory()
->addDescription(__u('Error while searching the user on LDAP'))
->addDetail('LDAP FILTER', $filter))
);
throw new LdapException(
__u('Error while searching the user on LDAP'),
LdapException::ERROR,
null,
LdapCode::NO_SUCH_OBJECT
);
}
// Normalize keys for comparing
$results = array_change_key_case($searchResults[0], CASE_LOWER);
$attributeCollection = new AttributeCollection();
foreach (self::ATTRIBUTES_MAPPING as $attribute => $map) {
if (isset($results[$attribute])) {
if (is_array($results[$attribute])) {
if ((int)$results[$attribute]['count'] > 1) {
unset($results[$attribute]['count']);
// Store the whole array
$attributeCollection->set($map, $results[$attribute]);
} else {
// Store first value
$attributeCollection->set($map, trim($results[$attribute][0]));
}
} else {
$attributeCollection->set($map, trim($results[$attribute]));
}
}
}
return $attributeCollection;
}
/**
* Obtener los objetos según el filtro indicado
*
* @param string $filter
* @param array $attributes
* @param string|null $searchBase
*
* @return array
* @throws LdapException
*/
public function getObjects(
string $filter,
array $attributes = self::USER_ATTRIBUTES,
?string $searchBase = null
)
{
$searchResults = $this->getResults($filter, $attributes, $searchBase);
if ($searchResults === false) {
$this->eventDispatcher->notifyEvent('ldap.search',
new Event($this, EventMessage::factory()
->addDescription(__u('Error while searching objects in base DN'))
->addDetail('LDAP ERROR', LdapConnection::getLdapErrorMessage($this->ldapHandler))
->addDetail('LDAP FILTER', $filter))
);
throw new LdapException(
__u('Error while searching objects in base DN'),
LdapException::ERROR,
null,
LdapCode::OPERATIONS_ERROR
);
}
return $searchResults;
}
}