Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

[Security][Ldap] Fixed issue with password attribute containing an array of values. #18881

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 26, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ public function create(ContainerBuilder $container, $id, $config)
->replaceArgument(4, $config['default_roles'])
->replaceArgument(5, $config['uid_key'])
->replaceArgument(6, $config['filter'])
->replaceArgument(7, $config['password_attribute'])
;
}

Expand All @@ -58,6 +59,7 @@ public function addConfiguration(NodeDefinition $node)
->end()
->scalarNode('uid_key')->defaultValue('sAMAccountName')->end()
->scalarNode('filter')->defaultValue('({uid_key}={username})')->end()
->scalarNode('password_attribute')->defaultNull()->end()
->end()
;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,10 @@ public function testLoadUserByUsernameFailsIfMoreThanOneLdapEntry()
$provider->loadUserByUsername('foo');
}

public function testSuccessfulLoadUserByUsername()
/**
* @expectedException \Symfony\Component\Security\Core\Exception\InvalidArgumentException
*/
public function testLoadUserByUsernameFailsIfMoreThanOneLdapPasswordsInEntry()
{
$result = $this->getMock(CollectionInterface::class);
$query = $this->getMock(QueryInterface::class);
Expand All @@ -120,8 +123,95 @@ public function testSuccessfulLoadUserByUsername()
->method('offsetGet')
->with(0)
->will($this->returnValue(new Entry('foo', array(
'sAMAccountName' => 'foo',
'userpassword' => 'bar',
'sAMAccountName' => array('foo'),
'userpassword' => array('bar', 'baz'),
)
)))
;
$result
->expects($this->once())
->method('count')
->will($this->returnValue(1))
;
$ldap
->expects($this->once())
->method('escape')
->will($this->returnValue('foo'))
;
$ldap
->expects($this->once())
->method('query')
->will($this->returnValue($query))
;

$provider = new LdapUserProvider($ldap, 'ou=MyBusiness,dc=symfony,dc=com', null, null, array(), 'sAMAccountName', '({uid_key}={username})', 'userpassword');
$this->assertInstanceOf(
'Symfony\Component\Security\Core\User\User',
$provider->loadUserByUsername('foo')
);
}

/**
* @expectedException \Symfony\Component\Security\Core\Exception\InvalidArgumentException
*/
public function testLoadUserByUsernameFailsIfEntryHasNoPasswordAttribute()
{
$result = $this->getMock(CollectionInterface::class);
$query = $this->getMock(QueryInterface::class);
$query
->expects($this->once())
->method('execute')
->will($this->returnValue($result))
;
$ldap = $this->getMock(LdapInterface::class);
$result
->expects($this->once())
->method('offsetGet')
->with(0)
->will($this->returnValue(new Entry('foo', array(
'sAMAccountName' => array('foo'),
)
)))
;
$result
->expects($this->once())
->method('count')
->will($this->returnValue(1))
;
$ldap
->expects($this->once())
->method('escape')
->will($this->returnValue('foo'))
;
$ldap
->expects($this->once())
->method('query')
->will($this->returnValue($query))
;

$provider = new LdapUserProvider($ldap, 'ou=MyBusiness,dc=symfony,dc=com', null, null, array(), 'sAMAccountName', '({uid_key}={username})', 'userpassword');
$this->assertInstanceOf(
'Symfony\Component\Security\Core\User\User',
$provider->loadUserByUsername('foo')
);
}

public function testLoadUserByUsernameIsSuccessfulWithoutPasswordAttribute()
{
$result = $this->getMock(CollectionInterface::class);
$query = $this->getMock(QueryInterface::class);
$query
->expects($this->once())
->method('execute')
->will($this->returnValue($result))
;
$ldap = $this->getMock(LdapInterface::class);
$result
->expects($this->once())
->method('offsetGet')
->with(0)
->will($this->returnValue(new Entry('foo', array(
'sAMAccountName' => array('foo'),
)
)))
;
Expand All @@ -147,4 +237,47 @@ public function testSuccessfulLoadUserByUsername()
$provider->loadUserByUsername('foo')
);
}

public function testLoadUserByUsernameIsSuccessfulWithPasswordAttribute()
{
$result = $this->getMock(CollectionInterface::class);
$query = $this->getMock(QueryInterface::class);
$query
->expects($this->once())
->method('execute')
->will($this->returnValue($result))
;
$ldap = $this->getMock(LdapInterface::class);
$result
->expects($this->once())
->method('offsetGet')
->with(0)
->will($this->returnValue(new Entry('foo', array(
'sAMAccountName' => array('foo'),
'userpassword' => array('bar'),
)
)))
;
$result
->expects($this->once())
->method('count')
->will($this->returnValue(1))
;
$ldap
->expects($this->once())
->method('escape')
->will($this->returnValue('foo'))
;
$ldap
->expects($this->once())
->method('query')
->will($this->returnValue($query))
;

$provider = new LdapUserProvider($ldap, 'ou=MyBusiness,dc=symfony,dc=com', null, null, array(), 'sAMAccountName', '({uid_key}={username})', 'userpassword');
$this->assertInstanceOf(
'Symfony\Component\Security\Core\User\User',
$provider->loadUserByUsername('foo')
);
}
}
41 changes: 39 additions & 2 deletions 41 src/Symfony/Component/Security/Core/User/LdapUserProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
namespace Symfony\Component\Security\Core\User;

use Symfony\Component\Ldap\Entry;
use Symfony\Component\Security\Core\Exception\InvalidArgumentException;
use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
use Symfony\Component\Ldap\Exception\ConnectionException;
Expand All @@ -31,6 +32,7 @@ class LdapUserProvider implements UserProviderInterface
private $searchPassword;
private $defaultRoles;
private $defaultSearch;
private $passwordAttribute;

/**
* @param LdapInterface $ldap
Expand All @@ -41,14 +43,15 @@ class LdapUserProvider implements UserProviderInterface
* @param string $uidKey
* @param string $filter
*/
public function __construct(LdapInterface $ldap, $baseDn, $searchDn = null, $searchPassword = null, array $defaultRoles = array(), $uidKey = 'sAMAccountName', $filter = '({uid_key}={username})')
public function __construct(LdapInterface $ldap, $baseDn, $searchDn = null, $searchPassword = null, array $defaultRoles = array(), $uidKey = 'sAMAccountName', $filter = '({uid_key}={username})', $passwordAttribute = null)
{
$this->ldap = $ldap;
$this->baseDn = $baseDn;
$this->searchDn = $searchDn;
$this->searchPassword = $searchPassword;
$this->defaultRoles = $defaultRoles;
$this->defaultSearch = str_replace('{uid_key}', $uidKey, $filter);
$this->passwordAttribute = $passwordAttribute;
}

/**
Expand Down Expand Up @@ -99,8 +102,42 @@ public function supportsClass($class)
return $class === 'Symfony\Component\Security\Core\User\User';
}

/**
* Loads a user from an LDAP entry.
*
* @param string $username
* @param Entry $entry
*
* @return User
*/
private function loadUser($username, Entry $entry)
{
return new User($username, $entry->getAttribute('userpassword'), $this->defaultRoles);
$password = $this->getPassword($entry);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could be inlined


return new User($username, $password, $this->defaultRoles);
}

/**
* Fetches the password from an LDAP entry.
*
* @param null|Entry $entry
*/
private function getPassword(Entry $entry)
{
if (null === $this->passwordAttribute) {
return;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

under what circumstance does it make sense to return null here? from my testing this then eventually leads to an error with password hashing not allowing a null parameter. so my question is, should we not consider this an exception?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It makes sense when you use LDAP Bind based authentication mechanisms, which do not rely on the password being provided as an LDAP attribute.

}

if (!$entry->hasAttribute($this->passwordAttribute)) {
throw new InvalidArgumentException(sprintf('Missing attribute "%s" for user "%s".', $this->passwordAttribute, $entry->getDn()));
}

$values = $entry->getAttribute($this->passwordAttribute);

if (1 !== count($values)) {
throw new InvalidArgumentException(sprintf('Attribute "%s" has multiple values.', $this->passwordAttribute));
}

return $values[0];
}
}
Morty Proxy This is a proxified and sanitized view of the page, visit original site.