From a8bae3175f3da3abadd237357ecd8c7d9ae01dfc Mon Sep 17 00:00:00 2001 From: Charles Sarrazin Date: Sun, 8 May 2016 21:21:13 +0200 Subject: [PATCH] Added the possibility to configure all Ldap options for connection --- .../Ldap/Adapter/AbstractConnection.php | 46 +++++++---- .../Ldap/Adapter/ExtLdap/Connection.php | 65 ++++++++++++++-- .../Adapter/ExtLdap/ConnectionOptions.php | 78 +++++++++++++++++++ .../Component/Ldap/Adapter/ExtLdap/Query.php | 4 +- .../Component/Ldap/Adapter/QueryInterface.php | 11 +-- 5 files changed, 175 insertions(+), 29 deletions(-) create mode 100644 src/Symfony/Component/Ldap/Adapter/ExtLdap/ConnectionOptions.php diff --git a/src/Symfony/Component/Ldap/Adapter/AbstractConnection.php b/src/Symfony/Component/Ldap/Adapter/AbstractConnection.php index 2f012a6663..67529b62e5 100644 --- a/src/Symfony/Component/Ldap/Adapter/AbstractConnection.php +++ b/src/Symfony/Component/Ldap/Adapter/AbstractConnection.php @@ -24,22 +24,40 @@ abstract class AbstractConnection implements ConnectionInterface public function __construct(array $config = array()) { $resolver = new OptionsResolver(); - $resolver->setDefaults(array( - 'host' => null, - 'port' => 389, - 'version' => 3, - 'useSsl' => false, - 'useStartTls' => false, - 'optReferrals' => false, - )); - $resolver->setNormalizer('host', function (Options $options, $value) { - if ($value && $options['useSsl']) { - return 'ldaps://'.$value; - } - return $value; - }); + $this->configureOptions($resolver); $this->config = $resolver->resolve($config); } + + /** + * Configures the adapter's options. + * + * @param OptionsResolver $resolver An OptionsResolver instance + */ + protected function configureOptions(OptionsResolver $resolver) + { + $resolver->setDefaults(array( + 'host' => 'localhost', + 'version' => 3, + 'connection_string' => null, + 'encryption' => 'none', + 'options' => array(), + )); + + $resolver->setDefault('port', function (Options $options) { + return 'ssl' === $options['encryption'] ? 636 : 389; + }); + + $resolver->setDefault('connection_string', function (Options $options) { + return sprintf('ldap%s://%s:%s', 'ssl' === $options['encryption'] ? 's' : '', $options['host'], $options['port']); + }); + + $resolver->setAllowedTypes('host', 'string'); + $resolver->setAllowedTypes('port', 'numeric'); + $resolver->setAllowedTypes('connection_string', 'string'); + $resolver->setAllowedTypes('version', 'numeric'); + $resolver->setAllowedValues('encryption', array('none', 'ssl', 'tls')); + $resolver->setAllowedTypes('options', 'array'); + } } diff --git a/src/Symfony/Component/Ldap/Adapter/ExtLdap/Connection.php b/src/Symfony/Component/Ldap/Adapter/ExtLdap/Connection.php index 1206a67f35..d705b3bce9 100644 --- a/src/Symfony/Component/Ldap/Adapter/ExtLdap/Connection.php +++ b/src/Symfony/Component/Ldap/Adapter/ExtLdap/Connection.php @@ -14,6 +14,8 @@ namespace Symfony\Component\Ldap\Adapter\ExtLdap; use Symfony\Component\Ldap\Adapter\AbstractConnection; use Symfony\Component\Ldap\Exception\ConnectionException; use Symfony\Component\Ldap\Exception\LdapException; +use Symfony\Component\OptionsResolver\Options; +use Symfony\Component\OptionsResolver\OptionsResolver; /** * @author Charles Sarrazin @@ -67,24 +69,75 @@ class Connection extends AbstractConnection return $this->connection; } + public function setOption($name, $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)); + } + } + + public function getOption($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; + } + + 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->setNormalizer('options', function (Options $options, $value) { + if (true === $options['debug']) { + $value['debug_level'] = 7; + } + + if (!isset($value['protocol_version'])) { + $value['protocol_version'] = $options['version']; + } + + if (!isset($value['referrals'])) { + $value['referrals'] = $options['referrals']; + } + + return $value; + }); + + $resolver->setAllowedValues('options', function (array $values) { + foreach ($values as $name => $value) { + if (!ConnectionOptions::isOption($name)) { + return false; + } + } + + return true; + }); + } + private function connect() { if ($this->connection) { return; } - $host = $this->config['host']; + $this->connection = ldap_connect($this->config['connection_string']); - ldap_set_option($this->connection, LDAP_OPT_PROTOCOL_VERSION, $this->config['version']); - ldap_set_option($this->connection, LDAP_OPT_REFERRALS, $this->config['optReferrals']); - - $this->connection = ldap_connect($host, $this->config['port']); + foreach ($this->config['options'] as $name => $value) { + $this->setOption($name, $value); + } if (false === $this->connection) { throw new LdapException(sprintf('Could not connect to Ldap server: %s', ldap_error($this->connection))); } - if ($this->config['useStartTls'] && false === ldap_start_tls($this->connection)) { + if ('tls' === $this->config['encryption'] && false === ldap_start_tls($this->connection)) { throw new LdapException(sprintf('Could not initiate TLS connection: %s', ldap_error($this->connection))); } } diff --git a/src/Symfony/Component/Ldap/Adapter/ExtLdap/ConnectionOptions.php b/src/Symfony/Component/Ldap/Adapter/ExtLdap/ConnectionOptions.php new file mode 100644 index 0000000000..95d65e020a --- /dev/null +++ b/src/Symfony/Component/Ldap/Adapter/ExtLdap/ConnectionOptions.php @@ -0,0 +1,78 @@ + + * + * 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 + * + * @internal + */ +final class ConnectionOptions +{ + const API_INFO = 0x00; + const DEREF = 0x02; + const SIZELIMIT = 0x03; + const TIMELIMIT = 0x04; + const REFERRALS = 0x08; + const RESTART = 0x09; + const PROTOCOL_VERSION = 0x11; + const SERVER_CONTROLS = 0x12; + const CLIENT_CONTROLS = 0x13; + const API_FEATURE_INFO = 0x15; + const HOST_NAME = 0x30; + const ERROR_NUMBER = 0x31; + const ERROR_STRING = 0x32; + const MATCHED_DN = 0x33; + const DEBUG_LEVEL = 0x5001; + const NETWORK_TIMEOUT = 0x5005; + const X_SASL_MECH = 0x6100; + const X_SASL_REALM = 0x6101; + const X_SASL_AUTHCID = 0x6102; + const X_SASL_AUTHZID = 0x6103; + + public static function getOptionName($name) + { + 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. + * + * @param string $name + * + * @return int + * + * @throws LdapException + */ + public static function getOption($name) + { + // Convert + $constantName = self::getOptionName($name); + + if (!defined($constantName)) { + throw new LdapException(sprintf('Unknown option "%s"', $name)); + } + + return constant($constantName); + } + + public static function isOption($name) + { + return defined(self::getOptionName($name)); + } +} diff --git a/src/Symfony/Component/Ldap/Adapter/ExtLdap/Query.php b/src/Symfony/Component/Ldap/Adapter/ExtLdap/Query.php index ebd7ea94cd..0e8eae7d21 100644 --- a/src/Symfony/Component/Ldap/Adapter/ExtLdap/Query.php +++ b/src/Symfony/Component/Ldap/Adapter/ExtLdap/Query.php @@ -35,7 +35,7 @@ class Query extends AbstractQuery $con = $this->connection->getResource(); $this->connection = null; - if (null === $this->search) { + if (null === $this->search || false === $this->search) { return; } @@ -60,7 +60,7 @@ class Query extends AbstractQuery $con = $this->connection->getResource(); - $this->search = ldap_search( + $this->search = @ldap_search( $con, $this->dn, $this->query, diff --git a/src/Symfony/Component/Ldap/Adapter/QueryInterface.php b/src/Symfony/Component/Ldap/Adapter/QueryInterface.php index db100a0688..ba26de791e 100644 --- a/src/Symfony/Component/Ldap/Adapter/QueryInterface.php +++ b/src/Symfony/Component/Ldap/Adapter/QueryInterface.php @@ -9,9 +9,6 @@ * file that was distributed with this source code. */ -/** - * @author Charles Sarrazin - */ namespace Symfony\Component\Ldap\Adapter; use Symfony\Component\Ldap\Entry; @@ -21,10 +18,10 @@ use Symfony\Component\Ldap\Entry; */ interface QueryInterface { - const DEREF_NEVER = 0; - const DEREF_SEARCHING = 1; - const DEREF_FINDING = 2; - const DEREF_ALWAYS = 3; + const DEREF_NEVER = 0x00; + const DEREF_SEARCHING = 0x01; + const DEREF_FINDING = 0x02; + const DEREF_ALWAYS = 0x03; /** * Executes a query and returns the list of Ldap entries.