first commit
This commit is contained in:
70
plugins/simplesaml/lib/vendor/symfony/ldap/Adapter/ExtLdap/Adapter.php
vendored
Normal file
70
plugins/simplesaml/lib/vendor/symfony/ldap/Adapter/ExtLdap/Adapter.php
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Ldap\Adapter\ExtLdap;
|
||||
|
||||
use Symfony\Component\Ldap\Adapter\AdapterInterface;
|
||||
use Symfony\Component\Ldap\Adapter\ConnectionInterface;
|
||||
use Symfony\Component\Ldap\Adapter\EntryManagerInterface;
|
||||
use Symfony\Component\Ldap\Adapter\QueryInterface;
|
||||
use Symfony\Component\Ldap\Exception\LdapException;
|
||||
|
||||
/**
|
||||
* @author Charles Sarrazin <charles@sarraz.in>
|
||||
*/
|
||||
class Adapter implements AdapterInterface
|
||||
{
|
||||
private array $config;
|
||||
private ConnectionInterface $connection;
|
||||
private EntryManagerInterface $entryManager;
|
||||
|
||||
public function __construct(array $config = [])
|
||||
{
|
||||
if (!\extension_loaded('ldap')) {
|
||||
throw new LdapException('The LDAP PHP extension is not enabled.');
|
||||
}
|
||||
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
public function getConnection(): ConnectionInterface
|
||||
{
|
||||
return $this->connection ??= new Connection($this->config);
|
||||
}
|
||||
|
||||
public function getEntryManager(): EntryManagerInterface
|
||||
{
|
||||
return $this->entryManager ??= new EntryManager($this->getConnection());
|
||||
}
|
||||
|
||||
public function createQuery(string $dn, string $query, array $options = []): QueryInterface
|
||||
{
|
||||
return new Query($this->getConnection(), $dn, $query, $options);
|
||||
}
|
||||
|
||||
public function escape(string $subject, string $ignore = '', int $flags = 0): string
|
||||
{
|
||||
$value = ldap_escape($subject, $ignore, $flags);
|
||||
|
||||
// Per RFC 4514, leading/trailing spaces should be encoded in DNs, as well as carriage returns.
|
||||
if ($flags & \LDAP_ESCAPE_DN) {
|
||||
if (!empty($value) && ' ' === $value[0]) {
|
||||
$value = '\\20'.substr($value, 1);
|
||||
}
|
||||
if (!empty($value) && ' ' === $value[\strlen($value) - 1]) {
|
||||
$value = substr($value, 0, -1).'\\20';
|
||||
}
|
||||
$value = str_replace("\r", '\0d', $value);
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
}
|
137
plugins/simplesaml/lib/vendor/symfony/ldap/Adapter/ExtLdap/Collection.php
vendored
Normal file
137
plugins/simplesaml/lib/vendor/symfony/ldap/Adapter/ExtLdap/Collection.php
vendored
Normal file
@@ -0,0 +1,137 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Ldap\Adapter\ExtLdap;
|
||||
|
||||
use Symfony\Component\Ldap\Adapter\CollectionInterface;
|
||||
use Symfony\Component\Ldap\Entry;
|
||||
use Symfony\Component\Ldap\Exception\LdapException;
|
||||
|
||||
/**
|
||||
* @author Charles Sarrazin <charles@sarraz.in>
|
||||
*/
|
||||
class Collection implements CollectionInterface
|
||||
{
|
||||
private Connection $connection;
|
||||
private Query $search;
|
||||
/** @var list<Entry> */
|
||||
private array $entries;
|
||||
|
||||
public function __construct(Connection $connection, Query $search)
|
||||
{
|
||||
$this->connection = $connection;
|
||||
$this->search = $search;
|
||||
}
|
||||
|
||||
public function toArray(): array
|
||||
{
|
||||
return $this->entries ??= iterator_to_array($this->getIterator(), false);
|
||||
}
|
||||
|
||||
public function count(): int
|
||||
{
|
||||
$con = $this->connection->getResource();
|
||||
$searches = $this->search->getResources();
|
||||
$count = 0;
|
||||
foreach ($searches as $search) {
|
||||
$searchCount = ldap_count_entries($con, $search);
|
||||
if (false === $searchCount) {
|
||||
throw new LdapException('Error while retrieving entry count: '.ldap_error($con));
|
||||
}
|
||||
$count += $searchCount;
|
||||
}
|
||||
|
||||
return $count;
|
||||
}
|
||||
|
||||
public function getIterator(): \Traversable
|
||||
{
|
||||
if (0 === $this->count()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$con = $this->connection->getResource();
|
||||
$searches = $this->search->getResources();
|
||||
foreach ($searches as $search) {
|
||||
$current = ldap_first_entry($con, $search);
|
||||
|
||||
if (false === $current) {
|
||||
throw new LdapException('Could not rewind entries array: '.ldap_error($con));
|
||||
}
|
||||
|
||||
yield $this->getSingleEntry($con, $current);
|
||||
|
||||
while (false !== $current = ldap_next_entry($con, $current)) {
|
||||
yield $this->getSingleEntry($con, $current);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function offsetExists(mixed $offset): bool
|
||||
{
|
||||
$this->toArray();
|
||||
|
||||
return isset($this->entries[$offset]);
|
||||
}
|
||||
|
||||
public function offsetGet(mixed $offset): ?Entry
|
||||
{
|
||||
$this->toArray();
|
||||
|
||||
return $this->entries[$offset] ?? null;
|
||||
}
|
||||
|
||||
public function offsetSet(mixed $offset, mixed $value): void
|
||||
{
|
||||
$this->toArray();
|
||||
|
||||
$this->entries[$offset] = $value;
|
||||
}
|
||||
|
||||
public function offsetUnset($offset): void
|
||||
{
|
||||
$this->toArray();
|
||||
|
||||
unset($this->entries[$offset]);
|
||||
}
|
||||
|
||||
private function getSingleEntry($con, $current): Entry
|
||||
{
|
||||
$attributes = ldap_get_attributes($con, $current);
|
||||
|
||||
if (false === $attributes) {
|
||||
throw new LdapException('Could not fetch attributes: '.ldap_error($con));
|
||||
}
|
||||
|
||||
$attributes = $this->cleanupAttributes($attributes);
|
||||
|
||||
$dn = ldap_get_dn($con, $current);
|
||||
|
||||
if (false === $dn) {
|
||||
throw new LdapException('Could not fetch DN: '.ldap_error($con));
|
||||
}
|
||||
|
||||
return new Entry($dn, $attributes);
|
||||
}
|
||||
|
||||
private function cleanupAttributes(array $entry): array
|
||||
{
|
||||
$attributes = array_diff_key($entry, array_flip(range(0, $entry['count'] - 1)) + [
|
||||
'count' => null,
|
||||
'dn' => null,
|
||||
]);
|
||||
array_walk($attributes, function (&$value) {
|
||||
unset($value['count']);
|
||||
});
|
||||
|
||||
return $attributes;
|
||||
}
|
||||
}
|
189
plugins/simplesaml/lib/vendor/symfony/ldap/Adapter/ExtLdap/Connection.php
vendored
Normal file
189
plugins/simplesaml/lib/vendor/symfony/ldap/Adapter/ExtLdap/Connection.php
vendored
Normal file
@@ -0,0 +1,189 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Ldap\Adapter\ExtLdap;
|
||||
|
||||
use LDAP\Connection as LDAPConnection;
|
||||
use Symfony\Component\Ldap\Adapter\AbstractConnection;
|
||||
use Symfony\Component\Ldap\Exception\AlreadyExistsException;
|
||||
use Symfony\Component\Ldap\Exception\ConnectionException;
|
||||
use Symfony\Component\Ldap\Exception\ConnectionTimeoutException;
|
||||
use Symfony\Component\Ldap\Exception\InvalidCredentialsException;
|
||||
use Symfony\Component\Ldap\Exception\LdapException;
|
||||
use Symfony\Component\OptionsResolver\Options;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
/**
|
||||
* @author Charles Sarrazin <charles@sarraz.in>
|
||||
*/
|
||||
class Connection extends AbstractConnection
|
||||
{
|
||||
private const LDAP_INVALID_CREDENTIALS = 0x31;
|
||||
private const LDAP_TIMEOUT = 0x55;
|
||||
private const LDAP_ALREADY_EXISTS = 0x44;
|
||||
private const PRECONNECT_OPTIONS = [
|
||||
ConnectionOptions::DEBUG_LEVEL,
|
||||
ConnectionOptions::X_TLS_CACERTDIR,
|
||||
ConnectionOptions::X_TLS_CACERTFILE,
|
||||
ConnectionOptions::X_TLS_REQUIRE_CERT,
|
||||
];
|
||||
|
||||
private bool $bound = false;
|
||||
private ?LDAPConnection $connection = null;
|
||||
|
||||
public function __sleep(): array
|
||||
{
|
||||
throw new \BadMethodCallException('Cannot serialize '.__CLASS__);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function __wakeup()
|
||||
{
|
||||
throw new \BadMethodCallException('Cannot unserialize '.__CLASS__);
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
{
|
||||
$this->disconnect();
|
||||
}
|
||||
|
||||
public function isBound(): bool
|
||||
{
|
||||
return $this->bound;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $password WARNING: When the LDAP server allows unauthenticated binds, a blank $password will always be valid
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function bind(?string $dn = null, #[\SensitiveParameter] ?string $password = null)
|
||||
{
|
||||
if (!$this->connection) {
|
||||
$this->connect();
|
||||
}
|
||||
|
||||
if (false === @ldap_bind($this->connection, $dn, $password)) {
|
||||
$error = ldap_error($this->connection);
|
||||
switch (ldap_errno($this->connection)) {
|
||||
case self::LDAP_INVALID_CREDENTIALS:
|
||||
throw new InvalidCredentialsException($error);
|
||||
case self::LDAP_TIMEOUT:
|
||||
throw new ConnectionTimeoutException($error);
|
||||
case self::LDAP_ALREADY_EXISTS:
|
||||
throw new AlreadyExistsException($error);
|
||||
}
|
||||
throw new ConnectionException($error);
|
||||
}
|
||||
|
||||
$this->bound = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public function getResource(): ?LDAPConnection
|
||||
{
|
||||
return $this->connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function setOption(string $name, array|string|int|bool $value)
|
||||
{
|
||||
if (!@ldap_set_option($this->connection, ConnectionOptions::getOption($name), $value)) {
|
||||
throw new LdapException(sprintf('Could not set value "%s" for option "%s".', $value, $name));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array|string|int|null
|
||||
*/
|
||||
public function getOption(string $name)
|
||||
{
|
||||
if (!@ldap_get_option($this->connection, ConnectionOptions::getOption($name), $ret)) {
|
||||
throw new LdapException(sprintf('Could not retrieve value for option "%s".', $name));
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
protected function configureOptions(OptionsResolver $resolver)
|
||||
{
|
||||
parent::configureOptions($resolver);
|
||||
|
||||
$resolver->setDefault('debug', false);
|
||||
$resolver->setAllowedTypes('debug', 'bool');
|
||||
$resolver->setDefault('referrals', false);
|
||||
$resolver->setAllowedTypes('referrals', 'bool');
|
||||
$resolver->setDefault('options', function (OptionsResolver $options, Options $parent) {
|
||||
$options->setDefined(array_map('strtolower', array_keys((new \ReflectionClass(ConnectionOptions::class))->getConstants())));
|
||||
|
||||
if (true === $parent['debug']) {
|
||||
$options->setDefault('debug_level', 7);
|
||||
}
|
||||
|
||||
if (!isset($parent['network_timeout'])) {
|
||||
$options->setDefault('network_timeout', \ini_get('default_socket_timeout'));
|
||||
}
|
||||
|
||||
$options->setDefaults([
|
||||
'protocol_version' => $parent['version'],
|
||||
'referrals' => $parent['referrals'],
|
||||
]);
|
||||
});
|
||||
}
|
||||
|
||||
private function connect(): void
|
||||
{
|
||||
if ($this->connection) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($this->config['options'] as $name => $value) {
|
||||
if (\in_array(ConnectionOptions::getOption($name), self::PRECONNECT_OPTIONS, true)) {
|
||||
$this->setOption($name, $value);
|
||||
}
|
||||
}
|
||||
|
||||
if (false === $connection = ldap_connect($this->config['connection_string'])) {
|
||||
throw new LdapException('Invalid connection string: '.$this->config['connection_string']);
|
||||
} else {
|
||||
$this->connection = $connection;
|
||||
}
|
||||
|
||||
foreach ($this->config['options'] as $name => $value) {
|
||||
if (!\in_array(ConnectionOptions::getOption($name), self::PRECONNECT_OPTIONS, true)) {
|
||||
$this->setOption($name, $value);
|
||||
}
|
||||
}
|
||||
|
||||
if ('tls' === $this->config['encryption'] && false === @ldap_start_tls($this->connection)) {
|
||||
throw new LdapException('Could not initiate TLS connection: '.ldap_error($this->connection));
|
||||
}
|
||||
}
|
||||
|
||||
private function disconnect(): void
|
||||
{
|
||||
if ($this->connection) {
|
||||
ldap_unbind($this->connection);
|
||||
}
|
||||
|
||||
$this->connection = null;
|
||||
$this->bound = false;
|
||||
}
|
||||
}
|
93
plugins/simplesaml/lib/vendor/symfony/ldap/Adapter/ExtLdap/ConnectionOptions.php
vendored
Normal file
93
plugins/simplesaml/lib/vendor/symfony/ldap/Adapter/ExtLdap/ConnectionOptions.php
vendored
Normal file
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Ldap\Adapter\ExtLdap;
|
||||
|
||||
use Symfony\Component\Ldap\Exception\LdapException;
|
||||
|
||||
/**
|
||||
* A class representing the Ldap extension's options, which can be used with
|
||||
* ldap_set_option or ldap_get_option.
|
||||
*
|
||||
* @author Charles Sarrazin <charles@sarraz.in>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
final class ConnectionOptions
|
||||
{
|
||||
public const API_INFO = 0x00;
|
||||
public const DEREF = 0x02;
|
||||
public const SIZELIMIT = 0x03;
|
||||
public const TIMELIMIT = 0x04;
|
||||
public const REFERRALS = 0x08;
|
||||
public const RESTART = 0x09;
|
||||
public const PROTOCOL_VERSION = 0x11;
|
||||
public const SERVER_CONTROLS = 0x12;
|
||||
public const CLIENT_CONTROLS = 0x13;
|
||||
public const API_FEATURE_INFO = 0x15;
|
||||
public const HOST_NAME = 0x30;
|
||||
public const ERROR_NUMBER = 0x31;
|
||||
public const ERROR_STRING = 0x32;
|
||||
public const MATCHED_DN = 0x33;
|
||||
public const DEBUG_LEVEL = 0x5001;
|
||||
public const TIMEOUT = 0x5002;
|
||||
public const NETWORK_TIMEOUT = 0x5005;
|
||||
public const X_TLS_CACERTFILE = 0x6002;
|
||||
public const X_TLS_CACERTDIR = 0x6003;
|
||||
public const X_TLS_CERTFILE = 0x6004;
|
||||
public const X_TLS_CRL_ALL = 0x02;
|
||||
public const X_TLS_CRL_NONE = 0x00;
|
||||
public const X_TLS_CRL_PEER = 0x01;
|
||||
public const X_TLS_KEYFILE = 0x6005;
|
||||
public const X_TLS_REQUIRE_CERT = 0x6006;
|
||||
public const X_TLS_PROTOCOL_MIN = 0x6007;
|
||||
public const X_TLS_CIPHER_SUITE = 0x6008;
|
||||
public const X_TLS_RANDOM_FILE = 0x6009;
|
||||
public const X_TLS_CRLFILE = 0x6010;
|
||||
public const X_TLS_PACKAGE = 0x6011;
|
||||
public const X_TLS_CRLCHECK = 0x600B;
|
||||
public const X_TLS_DHFILE = 0x600E;
|
||||
public const X_SASL_MECH = 0x6100;
|
||||
public const X_SASL_REALM = 0x6101;
|
||||
public const X_SASL_AUTHCID = 0x6102;
|
||||
public const X_SASL_AUTHZID = 0x6103;
|
||||
public const X_KEEPALIVE_IDLE = 0x6300;
|
||||
public const X_KEEPALIVE_PROBES = 0x6301;
|
||||
public const X_KEEPALIVE_INTERVAL = 0x6302;
|
||||
|
||||
public static function getOptionName(string $name): string
|
||||
{
|
||||
return sprintf('%s::%s', self::class, strtoupper($name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches an option's corresponding constant value from an option name.
|
||||
* The option name can either be in snake or camel case.
|
||||
*
|
||||
* @throws LdapException
|
||||
*/
|
||||
public static function getOption(string $name): int
|
||||
{
|
||||
// Convert
|
||||
$constantName = self::getOptionName($name);
|
||||
|
||||
if (!\defined($constantName)) {
|
||||
throw new LdapException(sprintf('Unknown option "%s".', $name));
|
||||
}
|
||||
|
||||
return \constant($constantName);
|
||||
}
|
||||
|
||||
public static function isOption(string $name): bool
|
||||
{
|
||||
return \defined(self::getOptionName($name));
|
||||
}
|
||||
}
|
189
plugins/simplesaml/lib/vendor/symfony/ldap/Adapter/ExtLdap/EntryManager.php
vendored
Normal file
189
plugins/simplesaml/lib/vendor/symfony/ldap/Adapter/ExtLdap/EntryManager.php
vendored
Normal file
@@ -0,0 +1,189 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Ldap\Adapter\ExtLdap;
|
||||
|
||||
use LDAP\Connection as LDAPConnection;
|
||||
use Symfony\Component\Ldap\Adapter\EntryManagerInterface;
|
||||
use Symfony\Component\Ldap\Entry;
|
||||
use Symfony\Component\Ldap\Exception\LdapException;
|
||||
use Symfony\Component\Ldap\Exception\NotBoundException;
|
||||
use Symfony\Component\Ldap\Exception\UpdateOperationException;
|
||||
|
||||
/**
|
||||
* @author Charles Sarrazin <charles@sarraz.in>
|
||||
* @author Bob van de Vijver <bobvandevijver@hotmail.com>
|
||||
*/
|
||||
class EntryManager implements EntryManagerInterface
|
||||
{
|
||||
public function __construct(
|
||||
private Connection $connection,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return $this
|
||||
*/
|
||||
public function add(Entry $entry)
|
||||
{
|
||||
$con = $this->getConnectionResource();
|
||||
|
||||
if (!@ldap_add($con, $entry->getDn(), $entry->getAttributes())) {
|
||||
throw new LdapException(sprintf('Could not add entry "%s": ', $entry->getDn()).ldap_error($con), ldap_errno($con));
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return $this
|
||||
*/
|
||||
public function update(Entry $entry)
|
||||
{
|
||||
$con = $this->getConnectionResource();
|
||||
|
||||
if (!@ldap_modify($con, $entry->getDn(), $entry->getAttributes())) {
|
||||
throw new LdapException(sprintf('Could not update entry "%s": ', $entry->getDn()).ldap_error($con), ldap_errno($con));
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return $this
|
||||
*/
|
||||
public function remove(Entry $entry)
|
||||
{
|
||||
$con = $this->getConnectionResource();
|
||||
|
||||
if (!@ldap_delete($con, $entry->getDn())) {
|
||||
throw new LdapException(sprintf('Could not remove entry "%s": ', $entry->getDn()).ldap_error($con), ldap_errno($con));
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds values to an entry's multi-valued attribute from the LDAP server.
|
||||
*
|
||||
* @return $this
|
||||
*
|
||||
* @throws NotBoundException
|
||||
* @throws LdapException
|
||||
*/
|
||||
public function addAttributeValues(Entry $entry, string $attribute, array $values)
|
||||
{
|
||||
$con = $this->getConnectionResource();
|
||||
|
||||
if (!@ldap_mod_add($con, $entry->getDn(), [$attribute => $values])) {
|
||||
throw new LdapException(sprintf('Could not add values to entry "%s", attribute "%s": ', $entry->getDn(), $attribute).ldap_error($con), ldap_errno($con));
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes values from an entry's multi-valued attribute from the LDAP server.
|
||||
*
|
||||
* @return $this
|
||||
*
|
||||
* @throws NotBoundException
|
||||
* @throws LdapException
|
||||
*/
|
||||
public function removeAttributeValues(Entry $entry, string $attribute, array $values)
|
||||
{
|
||||
$con = $this->getConnectionResource();
|
||||
|
||||
if (!@ldap_mod_del($con, $entry->getDn(), [$attribute => $values])) {
|
||||
throw new LdapException(sprintf('Could not remove values from entry "%s", attribute "%s": ', $entry->getDn(), $attribute).ldap_error($con), ldap_errno($con));
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return $this
|
||||
*/
|
||||
public function rename(Entry $entry, string $newRdn, bool $removeOldRdn = true)
|
||||
{
|
||||
$con = $this->getConnectionResource();
|
||||
|
||||
if (!@ldap_rename($con, $entry->getDn(), $newRdn, '', $removeOldRdn)) {
|
||||
throw new LdapException(sprintf('Could not rename entry "%s" to "%s": ', $entry->getDn(), $newRdn).ldap_error($con), ldap_errno($con));
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves an entry on the Ldap server.
|
||||
*
|
||||
* @return $this
|
||||
*
|
||||
* @throws NotBoundException if the connection has not been previously bound
|
||||
* @throws LdapException if an error is thrown during the rename operation
|
||||
*/
|
||||
public function move(Entry $entry, string $newParent)
|
||||
{
|
||||
$rdn = $this->parseRdnFromEntry($entry);
|
||||
$con = $this->getConnectionResource();
|
||||
// deleteOldRdn does not matter here, since the Rdn will not be changing in the move.
|
||||
if (!@ldap_rename($con, $entry->getDn(), $rdn, $newParent, true)) {
|
||||
throw new LdapException(sprintf('Could not move entry "%s" to "%s": ', $entry->getDn(), $newParent).ldap_error($con), ldap_errno($con));
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the connection resource, but first check if the connection is bound.
|
||||
*/
|
||||
private function getConnectionResource(): LDAPConnection
|
||||
{
|
||||
// If the connection is not bound, throw an exception. Users should use an explicit bind call first.
|
||||
if (!$this->connection->isBound()) {
|
||||
throw new NotBoundException('Query execution is not possible without binding the connection first.');
|
||||
}
|
||||
|
||||
return $this->connection->getResource();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param iterable<int, UpdateOperation> $operations An array or iterable of UpdateOperation instances
|
||||
*
|
||||
* @return $this
|
||||
*
|
||||
* @throws UpdateOperationException in case of an error
|
||||
*/
|
||||
public function applyOperations(string $dn, iterable $operations)
|
||||
{
|
||||
$operationsMapped = [];
|
||||
foreach ($operations as $modification) {
|
||||
$operationsMapped[] = $modification->toArray();
|
||||
}
|
||||
|
||||
$con = $this->getConnectionResource();
|
||||
if (!@ldap_modify_batch($con, $dn, $operationsMapped)) {
|
||||
throw new UpdateOperationException(sprintf('Error executing UpdateOperation on "%s": ', $dn).ldap_error($con), ldap_errno($con));
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
private function parseRdnFromEntry(Entry $entry): string
|
||||
{
|
||||
if (!preg_match('/(^[^,\\\\]*(?:\\\\.[^,\\\\]*)*),/', $entry->getDn(), $matches)) {
|
||||
throw new LdapException(sprintf('Entry "%s" malformed, could not parse RDN.', $entry->getDn()));
|
||||
}
|
||||
|
||||
return $matches[1];
|
||||
}
|
||||
}
|
209
plugins/simplesaml/lib/vendor/symfony/ldap/Adapter/ExtLdap/Query.php
vendored
Normal file
209
plugins/simplesaml/lib/vendor/symfony/ldap/Adapter/ExtLdap/Query.php
vendored
Normal file
@@ -0,0 +1,209 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Ldap\Adapter\ExtLdap;
|
||||
|
||||
use LDAP\Result;
|
||||
use Symfony\Component\Ldap\Adapter\AbstractQuery;
|
||||
use Symfony\Component\Ldap\Adapter\CollectionInterface;
|
||||
use Symfony\Component\Ldap\Exception\LdapException;
|
||||
use Symfony\Component\Ldap\Exception\NotBoundException;
|
||||
|
||||
/**
|
||||
* @author Charles Sarrazin <charles@sarraz.in>
|
||||
* @author Bob van de Vijver <bobvandevijver@hotmail.com>
|
||||
*/
|
||||
class Query extends AbstractQuery
|
||||
{
|
||||
public const PAGINATION_OID = \LDAP_CONTROL_PAGEDRESULTS;
|
||||
|
||||
/** @var Result[] */
|
||||
private array $results;
|
||||
|
||||
private array $serverctrls = [];
|
||||
|
||||
public function __sleep(): array
|
||||
{
|
||||
throw new \BadMethodCallException('Cannot serialize '.__CLASS__);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function __wakeup()
|
||||
{
|
||||
throw new \BadMethodCallException('Cannot unserialize '.__CLASS__);
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
{
|
||||
$con = $this->connection->getResource();
|
||||
|
||||
if (!isset($this->results)) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($this->results as $result) {
|
||||
if (false === $result || null === $result) {
|
||||
continue;
|
||||
}
|
||||
if (!ldap_free_result($result)) {
|
||||
throw new LdapException('Could not free results: '.ldap_error($con));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function execute(): CollectionInterface
|
||||
{
|
||||
if (!isset($this->results)) {
|
||||
// If the connection is not bound, throw an exception. Users should use an explicit bind call first.
|
||||
if (!$this->connection->isBound()) {
|
||||
throw new NotBoundException('Query execution is not possible without binding the connection first.');
|
||||
}
|
||||
|
||||
$this->results = [];
|
||||
$con = $this->connection->getResource();
|
||||
|
||||
$func = match ($this->options['scope']) {
|
||||
static::SCOPE_BASE => 'ldap_read',
|
||||
static::SCOPE_ONE => 'ldap_list',
|
||||
static::SCOPE_SUB => 'ldap_search',
|
||||
default => throw new LdapException(sprintf('Could not search in scope "%s".', $this->options['scope'])),
|
||||
};
|
||||
|
||||
$itemsLeft = $maxItems = $this->options['maxItems'];
|
||||
$pageSize = $this->options['pageSize'];
|
||||
// Deal with the logic to handle maxItems properly. If we can satisfy it in
|
||||
// one request based on pageSize, we don't need to bother sending page control
|
||||
// to the server so that it can determine what we already know.
|
||||
if (0 !== $maxItems && $pageSize > $maxItems) {
|
||||
$pageSize = 0;
|
||||
} elseif (0 !== $maxItems) {
|
||||
$pageSize = min($maxItems, $pageSize);
|
||||
}
|
||||
$pageControl = $this->options['scope'] != static::SCOPE_BASE && $pageSize > 0;
|
||||
$cookie = '';
|
||||
do {
|
||||
if ($pageControl) {
|
||||
$this->controlPagedResult($pageSize, true, $cookie);
|
||||
}
|
||||
$sizeLimit = $itemsLeft;
|
||||
if ($pageSize > 0 && $sizeLimit >= $pageSize) {
|
||||
$sizeLimit = 0;
|
||||
}
|
||||
$search = @$func($con, $this->dn, $this->query, $this->options['filter'], $this->options['attrsOnly'], $sizeLimit, $this->options['timeout'], $this->options['deref'], $this->serverctrls);
|
||||
|
||||
if (false === $search) {
|
||||
$ldapError = '';
|
||||
if ($errno = ldap_errno($con)) {
|
||||
$ldapError = sprintf(' LDAP error was [%d] %s', $errno, ldap_error($con));
|
||||
}
|
||||
if ($pageControl) {
|
||||
$this->resetPagination();
|
||||
}
|
||||
|
||||
throw new LdapException(sprintf('Could not complete search with dn "%s", query "%s" and filters "%s".%s.', $this->dn, $this->query, implode(',', $this->options['filter']), $ldapError), $errno);
|
||||
}
|
||||
|
||||
$this->results[] = $search;
|
||||
$itemsLeft -= min($itemsLeft, $pageSize);
|
||||
|
||||
if (0 !== $maxItems && 0 === $itemsLeft) {
|
||||
break;
|
||||
}
|
||||
if ($pageControl) {
|
||||
ldap_parse_result($con, $search, $errcode, $matcheddn, $errmsg, $referrals, $controls);
|
||||
|
||||
$cookie = $controls[\LDAP_CONTROL_PAGEDRESULTS]['value']['cookie'] ?? '';
|
||||
}
|
||||
} while (null !== $cookie && '' !== $cookie);
|
||||
|
||||
if ($pageControl) {
|
||||
$this->resetPagination();
|
||||
}
|
||||
}
|
||||
|
||||
return new Collection($this->connection, $this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an LDAP search resource. If this query resulted in multiple searches, only the first
|
||||
* page will be returned.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public function getResource(int $idx = 0): ?Result
|
||||
{
|
||||
return $this->results[$idx] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all LDAP search resources.
|
||||
*
|
||||
* @return Result[]
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public function getResources(): array
|
||||
{
|
||||
return $this->results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets pagination on the current connection.
|
||||
*/
|
||||
private function resetPagination(): void
|
||||
{
|
||||
$con = $this->connection->getResource();
|
||||
$this->controlPagedResult(0, false, '');
|
||||
$this->serverctrls = [];
|
||||
|
||||
// This is a workaround for a bit of a bug in the above invocation
|
||||
// of ldap_control_paged_result. Instead of indicating to extldap that
|
||||
// we no longer wish to page queries on this link, this invocation sets
|
||||
// the LDAP_CONTROL_PAGEDRESULTS OID with a page size of 0. This isn't
|
||||
// well defined by RFC 2696 if there is no cookie present, so some servers
|
||||
// will interpret it differently and do the wrong thing. Forcefully remove
|
||||
// the OID for now until a fix can make its way through the versions of PHP
|
||||
// the we support.
|
||||
//
|
||||
// This is not supported in PHP < 7.2, so these versions will remain broken.
|
||||
$ctl = [];
|
||||
ldap_get_option($con, \LDAP_OPT_SERVER_CONTROLS, $ctl);
|
||||
if (!empty($ctl)) {
|
||||
foreach ($ctl as $idx => $info) {
|
||||
if (static::PAGINATION_OID == $info['oid']) {
|
||||
unset($ctl[$idx]);
|
||||
}
|
||||
}
|
||||
ldap_set_option($con, \LDAP_OPT_SERVER_CONTROLS, $ctl);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets LDAP pagination controls.
|
||||
*/
|
||||
private function controlPagedResult(int $pageSize, bool $critical, string $cookie): bool
|
||||
{
|
||||
$this->serverctrls = [
|
||||
[
|
||||
'oid' => \LDAP_CONTROL_PAGEDRESULTS,
|
||||
'isCritical' => $critical,
|
||||
'value' => [
|
||||
'size' => $pageSize,
|
||||
'cookie' => $cookie,
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
62
plugins/simplesaml/lib/vendor/symfony/ldap/Adapter/ExtLdap/UpdateOperation.php
vendored
Normal file
62
plugins/simplesaml/lib/vendor/symfony/ldap/Adapter/ExtLdap/UpdateOperation.php
vendored
Normal file
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Ldap\Adapter\ExtLdap;
|
||||
|
||||
use Symfony\Component\Ldap\Exception\UpdateOperationException;
|
||||
|
||||
class UpdateOperation
|
||||
{
|
||||
private int $operationType;
|
||||
private ?array $values;
|
||||
private string $attribute;
|
||||
|
||||
private const VALID_OPERATION_TYPES = [
|
||||
\LDAP_MODIFY_BATCH_ADD,
|
||||
\LDAP_MODIFY_BATCH_REMOVE,
|
||||
\LDAP_MODIFY_BATCH_REMOVE_ALL,
|
||||
\LDAP_MODIFY_BATCH_REPLACE,
|
||||
];
|
||||
|
||||
/**
|
||||
* @param int $operationType An LDAP_MODIFY_BATCH_* constant
|
||||
* @param string $attribute The attribute to batch modify on
|
||||
*
|
||||
* @throws UpdateOperationException on consistency errors during construction
|
||||
*/
|
||||
public function __construct(int $operationType, string $attribute, ?array $values)
|
||||
{
|
||||
if (!\in_array($operationType, self::VALID_OPERATION_TYPES, true)) {
|
||||
throw new UpdateOperationException(sprintf('"%s" is not a valid modification type.', $operationType));
|
||||
}
|
||||
if (\LDAP_MODIFY_BATCH_REMOVE_ALL === $operationType && null !== $values) {
|
||||
throw new UpdateOperationException(sprintf('$values must be null for LDAP_MODIFY_BATCH_REMOVE_ALL operation, "%s" given.', get_debug_type($values)));
|
||||
}
|
||||
|
||||
$this->operationType = $operationType;
|
||||
$this->attribute = $attribute;
|
||||
$this->values = $values;
|
||||
}
|
||||
|
||||
public function toArray(): array
|
||||
{
|
||||
$op = [
|
||||
'attrib' => $this->attribute,
|
||||
'modtype' => $this->operationType,
|
||||
];
|
||||
|
||||
if (\LDAP_MODIFY_BATCH_REMOVE_ALL !== $this->operationType) {
|
||||
$op['values'] = $this->values;
|
||||
}
|
||||
|
||||
return $op;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user