Thanks to visit codestin.com
Credit goes to github.com

Skip to content

[Ldap] Deprecate '{username}' parameter use in favour of '{user_identifier}' in LDAP configuration #46683

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
Jul 29, 2022
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
9 changes: 7 additions & 2 deletions UPGRADE-6.2.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,15 @@ HttpFoundation

* Deprecate `Request::getContentType()`, use `Request::getContentTypeFormat()` instead

Ldap
----

* Deprecate `{username}` parameter use in favour of `{user_identifier}`

Mailer
--------
------

* Deprecate the `OhMySMTP` transport, use `MailPace` instead
* Deprecate the `OhMySMTP` transport, use `MailPace` instead

Security
--------
Expand Down
5 changes: 5 additions & 0 deletions src/Symfony/Component/Ldap/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
CHANGELOG
=========

6.2
---

* Deprecate `{username}` parameter use in favour of `{user_identifier}`

6.1
---

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,17 +83,17 @@ public function onCheckPassport(CheckPassportEvent $event)
} else {
throw new LogicException('Using the "query_string" config without using a "search_dn" and a "search_password" is not supported.');
}
$username = $ldap->escape($user->getUserIdentifier(), '', LdapInterface::ESCAPE_FILTER);
$query = str_replace('{username}', $username, $ldapBadge->getQueryString());
$identifier = $ldap->escape($user->getUserIdentifier(), '', LdapInterface::ESCAPE_FILTER);
$query = str_replace('{user_identifier}', $identifier, $ldapBadge->getQueryString());
$result = $ldap->query($ldapBadge->getDnString(), $query)->execute();
if (1 !== $result->count()) {
throw new BadCredentialsException('The presented username is invalid.');
throw new BadCredentialsException('The presented user identifier is invalid.');
}

$dn = $result[0]->getDn();
} else {
$username = $ldap->escape($user->getUserIdentifier(), '', LdapInterface::ESCAPE_DN);
$dn = str_replace('{username}', $username, $ldapBadge->getDnString());
$identifier = $ldap->escape($user->getUserIdentifier(), '', LdapInterface::ESCAPE_DN);
$dn = str_replace('{user_identifier}', $identifier, $ldapBadge->getDnString());
}

$ldap->bind($dn, $presentedPassword);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ class LdapAuthenticator implements AuthenticationEntryPointInterface, Interactiv
private string $searchPassword;
private string $queryString;

public function __construct(AuthenticatorInterface $authenticator, string $ldapServiceId, string $dnString = '{username}', string $searchDn = '', string $searchPassword = '', string $queryString = '')
public function __construct(AuthenticatorInterface $authenticator, string $ldapServiceId, string $dnString = '{user_identifier}', string $searchDn = '', string $searchPassword = '', string $queryString = '')
{
$this->authenticator = $authenticator;
$this->ldapServiceId = $ldapServiceId;
Expand Down
10 changes: 9 additions & 1 deletion src/Symfony/Component/Ldap/Security/LdapBadge.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,20 @@ class LdapBadge implements BadgeInterface
private string $searchPassword;
private ?string $queryString;

public function __construct(string $ldapServiceId, string $dnString = '{username}', string $searchDn = '', string $searchPassword = '', string $queryString = null)
public function __construct(string $ldapServiceId, string $dnString = '{user_identifier}', string $searchDn = '', string $searchPassword = '', string $queryString = null)
{
$this->ldapServiceId = $ldapServiceId;
$dnString = str_replace('{username}', '{user_identifier}', $dnString, $replaceCount);
if ($replaceCount > 0) {
trigger_deprecation('symfony/ldap', '6.2', 'Using "{username}" parameter in LDAP configuration is deprecated, consider using "{user_identifier}" instead.');
}
$this->dnString = $dnString;
$this->searchDn = $searchDn;
$this->searchPassword = $searchPassword;
$queryString = str_replace('{username}', '{user_identifier}', $queryString ?? '', $replaceCount);
if ($replaceCount > 0) {
trigger_deprecation('symfony/ldap', '6.2', 'Using "{username}" parameter in LDAP configuration is deprecated, consider using "{user_identifier}" instead.');
}
$this->queryString = $queryString;
}

Expand Down
10 changes: 5 additions & 5 deletions src/Symfony/Component/Ldap/Security/LdapUser.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,19 +24,19 @@
class LdapUser implements UserInterface, PasswordAuthenticatedUserInterface, EquatableInterface
{
private Entry $entry;
private string $username;
private string $identifier;
private ?string $password;
private array $roles;
private array $extraFields;

public function __construct(Entry $entry, string $username, #[\SensitiveParameter] ?string $password, array $roles = [], array $extraFields = [])
public function __construct(Entry $entry, string $identifier, #[\SensitiveParameter] ?string $password, array $roles = [], array $extraFields = [])
{
if (!$username) {
if (!$identifier) {
throw new \InvalidArgumentException('The username cannot be empty.');
}

$this->entry = $entry;
$this->username = $username;
$this->identifier = $identifier;
$this->password = $password;
$this->roles = $roles;
$this->extraFields = $extraFields;
Expand Down Expand Up @@ -81,7 +81,7 @@ public function getUsername(): string

public function getUserIdentifier(): string
{
return $this->username;
return $this->identifier;
}

/**
Expand Down
6 changes: 5 additions & 1 deletion src/Symfony/Component/Ldap/Security/LdapUserProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,11 @@ public function loadUserByIdentifier(string $identifier): UserInterface
}

$identifier = $this->ldap->escape($identifier, '', LdapInterface::ESCAPE_FILTER);
$query = str_replace(['{username}', '{user_identifier}'], $identifier, $this->defaultSearch);
$query = str_replace('{username}', '{user_identifier}', $this->defaultSearch, $replaceCount);
if ($replaceCount > 0) {
trigger_deprecation('symfony/ldap', '6.2', 'Using "{username}" parameter in LDAP configuration is deprecated, consider using "{user_identifier}" instead.');
}
$query = str_replace('{user_identifier}', $identifier, $query);
$search = $this->ldap->query($this->baseDn, $query, ['filter' => 0 == \count($this->extraFields) ? '*' : $this->extraFields]);

$entries = $search->execute();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,43 @@ public function testBindFailureShouldThrowAnException()
$listener->onCheckPassport($this->createEvent());
}

/**
* @group legacy
*
* @dataProvider queryForDnProvider
*/
public function testLegacyQueryForDn(string $dnString, string $queryString)
{
$collection = new class([new Entry('')]) extends \ArrayObject implements CollectionInterface {
public function toArray(): array
{
return $this->getArrayCopy();
}
};

$query = $this->createMock(QueryInterface::class);
$query->expects($this->once())->method('execute')->willReturn($collection);

$this->ldap
->method('bind')
->withConsecutive(
['elsa', 'test1234A$']
);
$this->ldap->expects($this->any())->method('escape')->with('Wouter', '', LdapInterface::ESCAPE_FILTER)->willReturn('wouter');
$this->ldap->expects($this->once())->method('query')->with('{user_identifier}', 'wouter_test')->willReturn($query);

$listener = $this->createListener();
$listener->onCheckPassport($this->createEvent('s3cr3t', new LdapBadge('app.ldap', $dnString, 'elsa', 'test1234A$', $queryString)));
}

public function queryForDnProvider(): iterable
{
yield ['{username}', '{username}_test'];
yield ['{user_identifier}', '{username}_test'];
yield ['{username}', '{user_identifier}_test'];
yield ['{user_identifier}', '{user_identifier}_test'];
}

public function testQueryForDn()
{
$collection = new class([new Entry('')]) extends \ArrayObject implements CollectionInterface {
Expand All @@ -145,16 +182,16 @@ public function toArray(): array
['elsa', 'test1234A$']
);
$this->ldap->expects($this->any())->method('escape')->with('Wouter', '', LdapInterface::ESCAPE_FILTER)->willReturn('wouter');
$this->ldap->expects($this->once())->method('query')->with('{username}', 'wouter_test')->willReturn($query);
$this->ldap->expects($this->once())->method('query')->with('{user_identifier}', 'wouter_test')->willReturn($query);

$listener = $this->createListener();
$listener->onCheckPassport($this->createEvent('s3cr3t', new LdapBadge('app.ldap', '{username}', 'elsa', 'test1234A$', '{username}_test')));
$listener->onCheckPassport($this->createEvent('s3cr3t', new LdapBadge('app.ldap', '{user_identifier}', 'elsa', 'test1234A$', '{user_identifier}_test')));
}

public function testEmptyQueryResultShouldThrowAnException()
{
$this->expectException(BadCredentialsException::class);
$this->expectExceptionMessage('The presented username is invalid.');
$this->expectExceptionMessage('The presented user identifier is invalid.');

$collection = $this->createMock(CollectionInterface::class);

Expand All @@ -170,7 +207,7 @@ public function testEmptyQueryResultShouldThrowAnException()
$this->ldap->expects($this->once())->method('query')->willReturn($query);

$listener = $this->createListener();
$listener->onCheckPassport($this->createEvent('s3cr3t', new LdapBadge('app.ldap', '{username}', 'elsa', 'test1234A$', '{username}_test')));
$listener->onCheckPassport($this->createEvent('s3cr3t', new LdapBadge('app.ldap', '{user_identifier}', 'elsa', 'test1234A$', '{user_identifier}_test')));
}

private function createEvent($password = 's3cr3t', $ldapBadge = null)
Expand Down
28 changes: 14 additions & 14 deletions src/Symfony/Component/Ldap/Tests/Security/LdapUserProviderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
*/
class LdapUserProviderTest extends TestCase
{
public function testLoadUserByUsernameFailsIfCantConnectToLdap()
public function testLoadUserByIdentifierFailsIfCantConnectToLdap()
{
$this->expectException(ConnectionException::class);

Expand All @@ -42,7 +42,7 @@ public function testLoadUserByUsernameFailsIfCantConnectToLdap()
$provider->loadUserByIdentifier('foo');
}

public function testLoadUserByUsernameFailsIfNoLdapEntries()
public function testLoadUserByIdentifierFailsIfNoLdapEntries()
{
$this->expectException(UserNotFoundException::class);

Expand Down Expand Up @@ -74,7 +74,7 @@ public function testLoadUserByUsernameFailsIfNoLdapEntries()
$provider->loadUserByIdentifier('foo');
}

public function testLoadUserByUsernameFailsIfMoreThanOneLdapEntry()
public function testLoadUserByIdentifierFailsIfMoreThanOneLdapEntry()
{
$this->expectException(UserNotFoundException::class);

Expand Down Expand Up @@ -106,7 +106,7 @@ public function testLoadUserByUsernameFailsIfMoreThanOneLdapEntry()
$provider->loadUserByIdentifier('foo');
}

public function testLoadUserByUsernameFailsIfMoreThanOneLdapPasswordsInEntry()
public function testLoadUserByIdentifierFailsIfMoreThanOneLdapPasswordsInEntry()
{
$this->expectException(InvalidArgumentException::class);

Expand Down Expand Up @@ -143,11 +143,11 @@ public function testLoadUserByUsernameFailsIfMoreThanOneLdapPasswordsInEntry()
->willReturn($query)
;

$provider = new LdapUserProvider($ldap, 'ou=MyBusiness,dc=symfony,dc=com', null, null, [], 'sAMAccountName', '({uid_key}={username})', 'userpassword');
$provider = new LdapUserProvider($ldap, 'ou=MyBusiness,dc=symfony,dc=com', null, null, [], 'sAMAccountName', '({uid_key}={user_identifier})', 'userpassword');
$this->assertInstanceOf(LdapUser::class, $provider->loadUserByIdentifier('foo'));
}

public function testLoadUserByUsernameShouldNotFailIfEntryHasNoUidKeyAttribute()
public function testLoadUserByIdentifierShouldNotFailIfEntryHasNoUidKeyAttribute()
{
$result = $this->createMock(CollectionInterface::class);
$query = $this->createMock(QueryInterface::class);
Expand Down Expand Up @@ -179,11 +179,11 @@ public function testLoadUserByUsernameShouldNotFailIfEntryHasNoUidKeyAttribute()
->willReturn($query)
;

$provider = new LdapUserProvider($ldap, 'ou=MyBusiness,dc=symfony,dc=com', null, null, [], 'sAMAccountName', '({uid_key}={username})');
$provider = new LdapUserProvider($ldap, 'ou=MyBusiness,dc=symfony,dc=com', null, null, [], 'sAMAccountName', '({uid_key}={user_identifier})');
$this->assertInstanceOf(LdapUser::class, $provider->loadUserByIdentifier('foo'));
}

public function testLoadUserByUsernameFailsIfEntryHasNoPasswordAttribute()
public function testLoadUserByIdentifierFailsIfEntryHasNoPasswordAttribute()
{
$this->expectException(InvalidArgumentException::class);

Expand Down Expand Up @@ -217,11 +217,11 @@ public function testLoadUserByUsernameFailsIfEntryHasNoPasswordAttribute()
->willReturn($query)
;

$provider = new LdapUserProvider($ldap, 'ou=MyBusiness,dc=symfony,dc=com', null, null, [], 'sAMAccountName', '({uid_key}={username})', 'userpassword');
$provider = new LdapUserProvider($ldap, 'ou=MyBusiness,dc=symfony,dc=com', null, null, [], 'sAMAccountName', '({uid_key}={user_identifier})', 'userpassword');
$this->assertInstanceOf(LdapUser::class, $provider->loadUserByIdentifier('foo'));
}

public function testLoadUserByUsernameIsSuccessfulWithoutPasswordAttribute()
public function testLoadUserByIdentifierIsSuccessfulWithoutPasswordAttribute()
{
$result = $this->createMock(CollectionInterface::class);
$query = $this->createMock(QueryInterface::class);
Expand Down Expand Up @@ -257,7 +257,7 @@ public function testLoadUserByUsernameIsSuccessfulWithoutPasswordAttribute()
$this->assertInstanceOf(LdapUser::class, $provider->loadUserByIdentifier('foo'));
}

public function testLoadUserByUsernameIsSuccessfulWithoutPasswordAttributeAndWrongCase()
public function testLoadUserByIdentifierIsSuccessfulWithoutPasswordAttributeAndWrongCase()
{
$result = $this->createMock(CollectionInterface::class);
$query = $this->createMock(QueryInterface::class);
Expand Down Expand Up @@ -293,7 +293,7 @@ public function testLoadUserByUsernameIsSuccessfulWithoutPasswordAttributeAndWro
$this->assertSame('foo', $provider->loadUserByIdentifier('Foo')->getUserIdentifier());
}

public function testLoadUserByUsernameIsSuccessfulWithPasswordAttribute()
public function testLoadUserByIdentifierIsSuccessfulWithPasswordAttribute()
{
$result = $this->createMock(CollectionInterface::class);
$query = $this->createMock(QueryInterface::class);
Expand Down Expand Up @@ -329,14 +329,14 @@ public function testLoadUserByUsernameIsSuccessfulWithPasswordAttribute()
->willReturn($query)
;

$provider = new LdapUserProvider($ldap, 'ou=MyBusiness,dc=symfony,dc=com', null, null, [], 'sAMAccountName', '({uid_key}={username})', 'userpassword', ['email']);
$provider = new LdapUserProvider($ldap, 'ou=MyBusiness,dc=symfony,dc=com', null, null, [], 'sAMAccountName', '({uid_key}={user_identifier})', 'userpassword', ['email']);
$this->assertInstanceOf(LdapUser::class, $provider->loadUserByIdentifier('foo'));
}

public function testRefreshUserShouldReturnUserWithSameProperties()
{
$ldap = $this->createMock(LdapInterface::class);
$provider = new LdapUserProvider($ldap, 'ou=MyBusiness,dc=symfony,dc=com', null, null, [], 'sAMAccountName', '({uid_key}={username})', 'userpassword', ['email']);
$provider = new LdapUserProvider($ldap, 'ou=MyBusiness,dc=symfony,dc=com', null, null, [], 'sAMAccountName', '({uid_key}={user_identifier})', 'userpassword', ['email']);

$user = new LdapUser(new Entry('foo'), 'foo', 'bar', ['ROLE_DUMMY'], ['email' => '[email protected]']);

Expand Down
1 change: 1 addition & 0 deletions src/Symfony/Component/Ldap/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
"require": {
"php": ">=8.1",
"ext-ldap": "*",
"symfony/deprecation-contracts": "^2.1|^3",
"symfony/options-resolver": "^5.4|^6.0"
},
"require-dev": {
Expand Down