Skip to content

Commit

Permalink
feature #17560 [Ldap] Improving the LDAP component (csarrazi)
Browse files Browse the repository at this point in the history
This PR was merged into the 3.1-dev branch.

Discussion
----------

[Ldap] Improving the LDAP component

| Q             | A
| ------------- | ---
| Bug fix?      | no
| New feature?  | yes
| BC breaks?    | yes
| Deprecations? | no
| Tests pass?   | no
| Fixed tickets | #14602
| License       | MIT
| Doc PR        | not yet

This PR will address a few issues mentioned in #14602.

* [x] Integrate the Config component in order to simplify the client's configuration
* [x] Separate Connection handling from the Client
* [x] Support for multiple drivers
* [x] Add functional tests
* [x] Update Security component

Commits
-------

34d3c85 Added compatibility layer for previous version of the Security component
81cb79b Improved the Ldap Component
  • Loading branch information
fabpot committed Feb 14, 2016
2 parents 2d75718 + 34d3c85 commit 7848a46
Show file tree
Hide file tree
Showing 37 changed files with 1,394 additions and 191 deletions.
7 changes: 7 additions & 0 deletions .travis.yml
Expand Up @@ -6,6 +6,8 @@ addons:
apt_packages:
- parallel
- language-pack-fr-base
- ldap-utils
- slapd

env:
global:
Expand Down Expand Up @@ -48,6 +50,11 @@ before_install:
- if [[ $deps != skip ]]; then composer self-update; fi;
- if [[ $deps != skip ]]; then ./phpunit install; fi;
- export PHPUNIT=$(readlink -f ./phpunit)
- mkdir /tmp/slapd
- slapd -f src/Symfony/Component/Ldap/Tests/Fixtures/conf/slapd.conf -h ldap://localhost:3389 &
- sleep 3
- ldapadd -h localhost:3389 -D cn=admin,dc=symfony,dc=com -w symfony -f src/Symfony/Component/Ldap/Tests/Fixtures/data/base.ldif
- ldapadd -h localhost:3389 -D cn=admin,dc=symfony,dc=com -w symfony -f src/Symfony/Component/Ldap/Tests/Fixtures/data/fixtures.ldif

install:
- if [[ $deps != skip ]]; then COMPONENTS=$(find src/Symfony -mindepth 3 -type f -name phpunit.xml.dist -printf '%h\n'); fi;
Expand Down
1 change: 0 additions & 1 deletion appveyor.yml
Expand Up @@ -43,7 +43,6 @@ install:
- IF %PHP%==1 echo extension=php_mbstring.dll >> php.ini-max
- IF %PHP%==1 echo extension=php_fileinfo.dll >> php.ini-max
- IF %PHP%==1 echo extension=php_pdo_sqlite.dll >> php.ini-max
- IF %PHP%==1 echo extension=php_ldap.dll >> php.ini-max
- appveyor DownloadFile https://getcomposer.org/composer.phar
- copy /Y php.ini-max php.ini
- cd c:\projects\symfony
Expand Down
45 changes: 45 additions & 0 deletions src/Symfony/Component/Ldap/Adapter/AbstractConnection.php
@@ -0,0 +1,45 @@
<?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;

use Symfony\Component\OptionsResolver\Options;
use Symfony\Component\OptionsResolver\OptionsResolver;

/**
* @author Charles Sarrazin <charles@sarraz.in>
*/
abstract class AbstractConnection implements ConnectionInterface
{
protected $config;

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->config = $resolver->resolve($config);
}
}
48 changes: 48 additions & 0 deletions src/Symfony/Component/Ldap/Adapter/AbstractQuery.php
@@ -0,0 +1,48 @@
<?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;

use Symfony\Component\OptionsResolver\Options;
use Symfony\Component\OptionsResolver\OptionsResolver;

/**
* @author Charles Sarrazin <charles@sarraz.in>
*/
abstract class AbstractQuery implements QueryInterface
{
protected $connection;
protected $dn;
protected $query;
protected $options;

public function __construct(ConnectionInterface $connection, $dn, $query, array $options = array())
{
$resolver = new OptionsResolver();
$resolver->setDefaults(array(
'filter' => '*',
'maxItems' => 0,
'sizeLimit' => 0,
'timeout' => 0,
'deref' => static::DEREF_NEVER,
'attrsOnly' => 0,
));
$resolver->setAllowedValues('deref', array(static::DEREF_ALWAYS, static::DEREF_NEVER, static::DEREF_FINDING, static::DEREF_SEARCHING));
$resolver->setNormalizer('filter', function (Options $options, $value) {
return is_array($value) ? $value : array($value);
});

$this->connection = $connection;
$this->dn = $dn;
$this->query = $query;
$this->options = $resolver->resolve($options);
}
}
47 changes: 47 additions & 0 deletions src/Symfony/Component/Ldap/Adapter/AdapterInterface.php
@@ -0,0 +1,47 @@
<?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;

/**
* @author Charles Sarrazin <charles@sarraz.in>
*/
interface AdapterInterface
{
/**
* Returns the current connection.
*
* @return ConnectionInterface
*/
public function getConnection();

/**
* Creates a new Query.
*
* @param $dn
* @param $query
* @param array $options
*
* @return QueryInterface
*/
public function createQuery($dn, $query, array $options = array());

/**
* 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);
}
25 changes: 25 additions & 0 deletions src/Symfony/Component/Ldap/Adapter/CollectionInterface.php
@@ -0,0 +1,25 @@
<?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;

use Symfony\Component\Ldap\Entry;

/**
* @author Charles Sarrazin <charles@sarraz.in>
*/
interface CollectionInterface extends \Countable, \IteratorAggregate, \ArrayAccess

This comment has been minimized.

Copy link
@mickaelandrieu

mickaelandrieu Feb 14, 2016

Contributor

implements you mean?

This comment has been minimized.

Copy link
@xabbuh

xabbuh Feb 14, 2016

Member

interface cannot implement other interfaces but only extend existing ones

This comment has been minimized.

Copy link
@mickaelandrieu

mickaelandrieu Feb 14, 2016

Contributor

hmm I didn't know multiple extends was possible in PHP, nevermind :)

edit: contradiction between http://php.net/manual/en/keyword.extends.php and http://php.net/manual/en/language.oop5.interfaces.php

Thank you for hint @xabbuh

{
/**
* @return Entry[]
*/
public function toArray();
}
33 changes: 33 additions & 0 deletions src/Symfony/Component/Ldap/Adapter/ConnectionInterface.php
@@ -0,0 +1,33 @@
<?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;

/**
* @author Charles Sarrazin <charles@sarraz.in>
*/
interface ConnectionInterface
{
/**
* Checks whether the connection was already bound or not.
*
* @return bool
*/
public function isBound();

/**
* Binds the connection against a DN and password.
*
* @param string $dn The user's DN
* @param string $password The associated password
*/
public function bind($dn = null, $password = null);
}
74 changes: 74 additions & 0 deletions src/Symfony/Component/Ldap/Adapter/ExtLdap/Adapter.php
@@ -0,0 +1,74 @@
<?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\Exception\LdapException;

/**
* @author Charles Sarrazin <charles@sarraz.in>
*/
class Adapter implements AdapterInterface
{
private $config;
private $connection;

public function __construct(array $config = array())
{
if (!extension_loaded('ldap')) {
throw new LdapException('The LDAP PHP extension is not enabled.');
}

$this->config = $config;
}

/**
* {@inheritdoc}
*/
public function getConnection()
{
if (null === $this->connection) {
$this->connection = new Connection($this->config);
}

return $this->connection;
}

/**
* {@inheritdoc}
*/
public function createQuery($dn, $query, array $options = array())
{
return new Query($this->getConnection(), $dn, $query, $options);
}

/**
* {@inheritdoc}
*/
public function escape($subject, $ignore = '', $flags = 0)
{
$value = ldap_escape($subject, $ignore, $flags);

// Per RFC 4514, leading/trailing spaces should be encoded in DNs, as well as carriage returns.
if ((int) $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;
}
}

0 comments on commit 7848a46

Please sign in to comment.