Introducing the LDAP component
This commit is contained in:
parent
dc937f899e
commit
1c964b993f
@ -35,6 +35,7 @@ before_install:
|
||||
- if [[ "$TRAVIS_PHP_VERSION" = 5.* ]]; then (pecl install -f memcached-2.1.0 && echo "extension = memcache.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini) || echo "Let's continue without memcache extension"; fi;
|
||||
- if [[ "$TRAVIS_PHP_VERSION" = 5.* ]] && [ "$deps" = "no" ]; then (cd src/Symfony/Component/Debug/Resources/ext && phpize && ./configure && make && echo "extension = $(pwd)/modules/symfony_debug.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini); fi;
|
||||
- if [[ "$TRAVIS_PHP_VERSION" != "hhvm" ]]; then php -i; fi;
|
||||
- if [[ "$TRAVIS_PHP_VERSION" != "hhvm" ]]; then echo "extension = ldap.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini; fi;
|
||||
- ./phpunit install
|
||||
- export PHPUNIT="$(readlink -f ./phpunit)"
|
||||
|
||||
|
@ -45,6 +45,7 @@ install:
|
||||
- IF %PHP_EXT%==1 echo extension=php_mbstring.dll >> php.ini
|
||||
- IF %PHP_EXT%==1 echo extension=php_fileinfo.dll >> php.ini
|
||||
- IF %PHP_EXT%==1 echo extension=php_pdo_sqlite.dll >> php.ini
|
||||
- IF %PHP_EXT%==1 echo extension=php_ldap.dll >> php.ini
|
||||
- cd c:\projects\symfony
|
||||
- php phpunit install
|
||||
- IF %APPVEYOR_REPO_BRANCH%==master (SET COMPOSER_ROOT_VERSION=dev-master) ELSE (SET COMPOSER_ROOT_VERSION=%APPVEYOR_REPO_BRANCH%.x-dev)
|
||||
|
3
src/Symfony/Component/Ldap/.gitignore
vendored
Normal file
3
src/Symfony/Component/Ldap/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
vendor/
|
||||
composer.lock
|
||||
phpunit.xml
|
21
src/Symfony/Component/Ldap/Exception/ConnectionException.php
Normal file
21
src/Symfony/Component/Ldap/Exception/ConnectionException.php
Normal file
@ -0,0 +1,21 @@
|
||||
<?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\Exception;
|
||||
|
||||
/**
|
||||
* ConnectionException is throw if binding to ldap can not be established.
|
||||
*
|
||||
* @author Grégoire Pineau <lyrixx@lyrixx.info>
|
||||
*/
|
||||
class ConnectionException extends \RuntimeException
|
||||
{
|
||||
}
|
21
src/Symfony/Component/Ldap/Exception/LdapException.php
Normal file
21
src/Symfony/Component/Ldap/Exception/LdapException.php
Normal file
@ -0,0 +1,21 @@
|
||||
<?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\Exception;
|
||||
|
||||
/**
|
||||
* LdapException is throw if php ldap module is not loaded.
|
||||
*
|
||||
* @author Grégoire Pineau <lyrixx@lyrixx.info>
|
||||
*/
|
||||
class LdapException extends \RuntimeException
|
||||
{
|
||||
}
|
19
src/Symfony/Component/Ldap/LICENSE
Normal file
19
src/Symfony/Component/Ldap/LICENSE
Normal file
@ -0,0 +1,19 @@
|
||||
Copyright (c) 2004-2015 Fabien Potencier
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is furnished
|
||||
to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
220
src/Symfony/Component/Ldap/LdapClient.php
Normal file
220
src/Symfony/Component/Ldap/LdapClient.php
Normal file
@ -0,0 +1,220 @@
|
||||
<?php
|
||||
|
||||
namespace Symfony\Component\Ldap;
|
||||
|
||||
use Symfony\Component\Ldap\Exception\ConnectionException;
|
||||
use Symfony\Component\Ldap\Exception\LdapException;
|
||||
|
||||
/**
|
||||
* @author Grégoire Pineau <lyrixx@lyrixx.info>
|
||||
* @author Francis Besset <francis.besset@gmail.com>
|
||||
* @author Charles Sarrazin <charles@sarraz.in>
|
||||
*/
|
||||
class LdapClient implements LdapClientInterface
|
||||
{
|
||||
private $host;
|
||||
private $port;
|
||||
private $version;
|
||||
private $useSsl;
|
||||
private $useStartTls;
|
||||
private $optReferrals;
|
||||
private $connection;
|
||||
private $charmaps;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string $host
|
||||
* @param int $port
|
||||
* @param int $version
|
||||
* @param bool $useSsl
|
||||
* @param bool $useStartTls
|
||||
* @param bool $optReferrals
|
||||
*/
|
||||
public function __construct($host = null, $port = 389, $version = 3, $useSsl = false, $useStartTls = false, $optReferrals = false)
|
||||
{
|
||||
if (!extension_loaded('ldap')) {
|
||||
throw new LdapException('The ldap module is needed.');
|
||||
}
|
||||
|
||||
$this->host = $host;
|
||||
$this->port = $port;
|
||||
$this->version = $version;
|
||||
$this->useSsl = (bool) $useSsl;
|
||||
$this->useStartTls = (bool) $useStartTls;
|
||||
$this->optReferrals = (bool) $optReferrals;
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
{
|
||||
$this->disconnect();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function bind($dn = null, $password = null)
|
||||
{
|
||||
if (!$this->connection) {
|
||||
$this->connect();
|
||||
}
|
||||
|
||||
if (false === @ldap_bind($this->connection, $dn, $password)) {
|
||||
throw new ConnectionException(ldap_error($this->connection));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function find($dn, $query, $filter = '*')
|
||||
{
|
||||
if (!is_array($filter)) {
|
||||
$filter = array($filter);
|
||||
}
|
||||
|
||||
$search = ldap_search($this->connection, $dn, $query, $filter);
|
||||
$infos = ldap_get_entries($this->connection, $search);
|
||||
|
||||
if (0 === $infos['count']) {
|
||||
return;
|
||||
}
|
||||
|
||||
return $infos;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function escape($subject, $ignore = '', $flags = 0)
|
||||
{
|
||||
if (function_exists('ldap_escape')) {
|
||||
return ldap_escape($subject, $ignore, $flags);
|
||||
}
|
||||
|
||||
return $this->doEscape($subject, $ignore, $flags);
|
||||
}
|
||||
|
||||
private function connect()
|
||||
{
|
||||
if (!$this->connection) {
|
||||
$host = $this->host;
|
||||
|
||||
if ($this->useSsl) {
|
||||
$host = 'ldaps://'.$host;
|
||||
}
|
||||
|
||||
ldap_set_option($this->connection, LDAP_OPT_PROTOCOL_VERSION, $this->version);
|
||||
ldap_set_option($this->connection, LDAP_OPT_REFERRALS, $this->optReferrals);
|
||||
|
||||
$this->connection = ldap_connect($host, $this->port);
|
||||
|
||||
if ($this->useStartTls) {
|
||||
ldap_start_tls($this->connection);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function disconnect()
|
||||
{
|
||||
if ($this->connection && is_resource($this->connection)) {
|
||||
ldap_unbind($this->connection);
|
||||
}
|
||||
|
||||
$this->connection = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stub implementation of the {@link ldap_escape()} function of the ldap
|
||||
* extension.
|
||||
*
|
||||
* Escape strings for safe use in LDAP filters and DNs.
|
||||
*
|
||||
* @author Chris Wright <ldapi@daverandom.com>
|
||||
*
|
||||
* @param string $subject
|
||||
* @param string $ignore
|
||||
* @param int $flags
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @see http://stackoverflow.com/a/8561604
|
||||
*/
|
||||
private function doEscape($subject, $ignore = '', $flags = 0)
|
||||
{
|
||||
$charMaps = $this->getCharmaps();
|
||||
|
||||
// Create the base char map to escape
|
||||
$flags = (int) $flags;
|
||||
$charMap = array();
|
||||
|
||||
if ($flags & self::LDAP_ESCAPE_FILTER) {
|
||||
$charMap += $charMaps[self::LDAP_ESCAPE_FILTER];
|
||||
}
|
||||
|
||||
if ($flags & self::LDAP_ESCAPE_DN) {
|
||||
$charMap += $charMaps[self::LDAP_ESCAPE_DN];
|
||||
}
|
||||
|
||||
if (!$charMap) {
|
||||
$charMap = $charMaps[0];
|
||||
}
|
||||
|
||||
// Remove any chars to ignore from the list
|
||||
$ignore = (string) $ignore;
|
||||
|
||||
for ($i = 0, $l = strlen($ignore); $i < $l; ++$i) {
|
||||
unset($charMap[$ignore[$i]]);
|
||||
}
|
||||
|
||||
// Do the main replacement
|
||||
$result = strtr($subject, $charMap);
|
||||
|
||||
// Encode leading/trailing spaces if LDAP_ESCAPE_DN is passed
|
||||
if ($flags & self::LDAP_ESCAPE_DN) {
|
||||
if ($result[0] === ' ') {
|
||||
$result = '\\20'.substr($result, 1);
|
||||
}
|
||||
|
||||
if ($result[strlen($result) - 1] === ' ') {
|
||||
$result = substr($result, 0, -1).'\\20';
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
private function getCharmaps()
|
||||
{
|
||||
if (null !== $this->charmaps) {
|
||||
return $this->charmaps;
|
||||
}
|
||||
|
||||
$charMaps = array(
|
||||
self::LDAP_ESCAPE_FILTER => array('\\', '*', '(', ')', "\x00"),
|
||||
self::LDAP_ESCAPE_DN => array('\\', ',', '=', '+', '<', '>', ';', '"', '#'),
|
||||
);
|
||||
|
||||
$charMaps[0] = array();
|
||||
|
||||
for ($i = 0; $i < 256; ++$i) {
|
||||
$charMaps[0][chr($i)] = sprintf('\\%02x', $i);
|
||||
}
|
||||
|
||||
for ($i = 0, $l = count($charMaps[self::LDAP_ESCAPE_FILTER]); $i < $l; ++$i) {
|
||||
$chr = $charMaps[self::LDAP_ESCAPE_FILTER][$i];
|
||||
unset($charMaps[self::LDAP_ESCAPE_FILTER][$i]);
|
||||
$charMaps[self::LDAP_ESCAPE_FILTER][$chr] = $charMaps[0][$chr];
|
||||
}
|
||||
|
||||
for ($i = 0, $l = count($charMaps[self::LDAP_ESCAPE_DN]); $i < $l; ++$i) {
|
||||
$chr = $charMaps[self::LDAP_ESCAPE_DN][$i];
|
||||
unset($charMaps[self::LDAP_ESCAPE_DN][$i]);
|
||||
$charMaps[self::LDAP_ESCAPE_DN][$chr] = $charMaps[0][$chr];
|
||||
}
|
||||
|
||||
$this->charmaps = $charMaps;
|
||||
|
||||
return $this->charmaps;
|
||||
}
|
||||
}
|
49
src/Symfony/Component/Ldap/LdapClientInterface.php
Normal file
49
src/Symfony/Component/Ldap/LdapClientInterface.php
Normal file
@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
namespace Symfony\Component\Ldap;
|
||||
|
||||
use Symfony\Component\Ldap\Exception\ConnectionException;
|
||||
|
||||
/**
|
||||
* Ldap interface.
|
||||
*
|
||||
* @author Grégoire Pineau <lyrixx@lyrixx.info>
|
||||
* @author Charles Sarrazin <charles@sarraz.in>
|
||||
*/
|
||||
interface LdapClientInterface
|
||||
{
|
||||
const LDAP_ESCAPE_FILTER = 0x01;
|
||||
const LDAP_ESCAPE_DN = 0x02;
|
||||
|
||||
/**
|
||||
* Return a connection bound to the ldap.
|
||||
*
|
||||
* @param string $dn A LDAP dn
|
||||
* @param string $password A password
|
||||
*
|
||||
* @throws ConnectionException If dn / password could not be bound.
|
||||
*/
|
||||
public function bind($dn = null, $password = null);
|
||||
|
||||
/*
|
||||
* Find a username into ldap connection.
|
||||
*
|
||||
* @param string $dn
|
||||
* @param string $query
|
||||
* @param mixed $filter
|
||||
*
|
||||
* @return array|null
|
||||
*/
|
||||
public function find($dn, $query, $filter = '*');
|
||||
|
||||
/**
|
||||
* Escape a string for use in an LDAP filter or DN.
|
||||
*
|
||||
* @param string $subject
|
||||
* @param string $ignore
|
||||
* @param int $flags
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function escape($subject, $ignore = '', $flags = 0);
|
||||
}
|
23
src/Symfony/Component/Ldap/README.md
Normal file
23
src/Symfony/Component/Ldap/README.md
Normal file
@ -0,0 +1,23 @@
|
||||
Ldap Component
|
||||
=============
|
||||
|
||||
A Ldap client for PHP on top of PHP's ldap extension.
|
||||
|
||||
This component also provides a stub for the missing
|
||||
`ldap_escape` function in PHP versions lower than 5.6.
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
|
||||
The documentation for the component can be found [online] [0].
|
||||
|
||||
Resources
|
||||
---------
|
||||
|
||||
You can run the unit tests with the following command:
|
||||
|
||||
$ cd path/to/Symfony/Component/Ldap/
|
||||
$ composer install
|
||||
$ phpunit
|
||||
|
||||
[0]: https://symfony.com/doc/2.8/components/ldap.html
|
56
src/Symfony/Component/Ldap/Tests/LdapClientTest.php
Normal file
56
src/Symfony/Component/Ldap/Tests/LdapClientTest.php
Normal file
@ -0,0 +1,56 @@
|
||||
<?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\Security\Core\Tests\Authentication\Provider;
|
||||
|
||||
use Symfony\Component\Ldap\LdapClient;
|
||||
|
||||
class LdapClientTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
protected function setUp()
|
||||
{
|
||||
if (!extension_loaded('ldap')) {
|
||||
$this->markTestSkipped('The ldap extension is not available');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideLdapEscapeValues
|
||||
*/
|
||||
public function testLdapEscape($subject, $ignore, $flags, $expected)
|
||||
{
|
||||
$ldap = new LdapClient();
|
||||
$this->assertSame($expected, $ldap->escape($subject, $ignore, $flags));
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides values for the ldap_escape shim. These tests come from the official
|
||||
* extension.
|
||||
*
|
||||
* @see https://github.com/php/php-src/blob/master/ext/ldap/tests/ldap_escape_dn.phpt
|
||||
* @see https://github.com/php/php-src/blob/master/ext/ldap/tests/ldap_escape_all.phpt
|
||||
* @see https://github.com/php/php-src/blob/master/ext/ldap/tests/ldap_escape_both.phpt
|
||||
* @see https://github.com/php/php-src/blob/master/ext/ldap/tests/ldap_escape_filter.phpt
|
||||
* @see https://github.com/php/php-src/blob/master/ext/ldap/tests/ldap_escape_ignore.phpt
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function provideLdapEscapeValues()
|
||||
{
|
||||
return array(
|
||||
array('foo=bar(baz)*', null, LdapClient::LDAP_ESCAPE_DN, 'foo\3dbar(baz)*'),
|
||||
array('foo=bar(baz)*', null, null, '\66\6f\6f\3d\62\61\72\28\62\61\7a\29\2a'),
|
||||
array('foo=bar(baz)*', null, LdapClient::LDAP_ESCAPE_DN | LdapClient::LDAP_ESCAPE_FILTER, 'foo\3dbar\28baz\29\2a'),
|
||||
array('foo=bar(baz)*', null, LdapClient::LDAP_ESCAPE_FILTER, 'foo=bar\28baz\29\2a'),
|
||||
array('foo=bar(baz)*', 'ao', null, '\66oo\3d\62a\72\28\62a\7a\29\2a'),
|
||||
);
|
||||
}
|
||||
}
|
34
src/Symfony/Component/Ldap/composer.json
Normal file
34
src/Symfony/Component/Ldap/composer.json
Normal file
@ -0,0 +1,34 @@
|
||||
{
|
||||
"name": "symfony/ldap",
|
||||
"type": "library",
|
||||
"description": "An abstraction in front of PHP's LDAP functions, compatible with PHP 5.3.9 onwards.",
|
||||
"keywords": ["ldap", "active directory"],
|
||||
"homepage": "https://symfony.com",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Charles Sarrazin",
|
||||
"email": "charles@sarraz.in"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=5.3.9",
|
||||
"ext-ldap": "*"
|
||||
},
|
||||
"require-dev": {
|
||||
"symfony/phpunit-bridge": "~2.7|~3.0.0"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": { "Symfony\\Component\\Ldap\\": "" }
|
||||
},
|
||||
"minimum-stability": "dev",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.8-dev"
|
||||
}
|
||||
}
|
||||
}
|
28
src/Symfony/Component/Ldap/phpunit.xml.dist
Normal file
28
src/Symfony/Component/Ldap/phpunit.xml.dist
Normal file
@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.1/phpunit.xsd"
|
||||
backupGlobals="false"
|
||||
colors="true"
|
||||
bootstrap="vendor/autoload.php"
|
||||
>
|
||||
<php>
|
||||
<ini name="error_reporting" value="-1" />
|
||||
</php>
|
||||
|
||||
<testsuites>
|
||||
<testsuite name="Symfony Ldap Component Test Suite">
|
||||
<directory>./Tests/</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
|
||||
<filter>
|
||||
<whitelist>
|
||||
<directory>./</directory>
|
||||
<exclude>
|
||||
<directory>./Tests</directory>
|
||||
<directory>./vendor</directory>
|
||||
</exclude>
|
||||
</whitelist>
|
||||
</filter>
|
||||
</phpunit>
|
Reference in New Issue
Block a user