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

Commit 6736cdf

Browse filesBrowse files
author
Robin Chalas
committed
[Ldap] Add security LdapUser and provider
1 parent f6b73e1 commit 6736cdf
Copy full SHA for 6736cdf

File tree

13 files changed

+609
-111
lines changed
Filter options

13 files changed

+609
-111
lines changed

‎UPGRADE-4.4.md

Copy file name to clipboardExpand all lines: UPGRADE-4.4.md
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ Routing
141141
Security
142142
--------
143143

144+
* The `LdapUserProvider` class has been deprecated, use `Symfony\Component\Ldap\Security\LdapUserProvider` instead.
144145
* Implementations of `PasswordEncoderInterface` and `UserPasswordEncoderInterface` should add a new `needsRehash()` method
145146

146147
Stopwatch

‎UPGRADE-5.0.md

Copy file name to clipboardExpand all lines: UPGRADE-5.0.md
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -376,6 +376,7 @@ Routing
376376
Security
377377
--------
378378

379+
* The `LdapUserProvider` class has been removed, use `Symfony\Component\Ldap\Security\LdapUserProvider` instead.
379380
* Implementations of `PasswordEncoderInterface` and `UserPasswordEncoderInterface` must have a new `needsRehash()` method
380381
* The `Role` and `SwitchUserRole` classes have been removed.
381382
* The `getReachableRoles()` method of the `RoleHierarchy` class has been removed. It has been replaced by the new

‎src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@
175175
<deprecated>The "%service_id%" service is deprecated since Symfony 4.1.</deprecated>
176176
</service>
177177

178-
<service id="security.user.provider.ldap" class="Symfony\Component\Security\Core\User\LdapUserProvider" abstract="true">
178+
<service id="security.user.provider.ldap" class="Symfony\Component\Ldap\Security\LdapUserProvider" abstract="true">
179179
<argument /> <!-- security.ldap.ldap -->
180180
<argument /> <!-- base dn -->
181181
<argument /> <!-- search dn -->

‎src/Symfony/Bundle/SecurityBundle/composer.json

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/SecurityBundle/composer.json
+2-1Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,8 @@
5151
"symfony/twig-bundle": "<4.4",
5252
"symfony/var-dumper": "<3.4",
5353
"symfony/framework-bundle": "<4.4",
54-
"symfony/console": "<3.4"
54+
"symfony/console": "<3.4",
55+
"symfony/ldap": "<4.4"
5556
},
5657
"autoload": {
5758
"psr-4": { "Symfony\\Bundle\\SecurityBundle\\": "" },
+91Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Ldap\Security;
13+
14+
use Symfony\Component\Ldap\Entry;
15+
use Symfony\Component\Security\Core\User\UserInterface;
16+
17+
/**
18+
* @author Robin Chalas <robin.chalas@gmail.com>
19+
*
20+
* @final
21+
*/
22+
class LdapUser implements UserInterface
23+
{
24+
private $entry;
25+
private $username;
26+
private $password;
27+
private $roles;
28+
private $extraFields;
29+
30+
public function __construct(Entry $entry, string $username, ?string $password, array $roles = [], array $extraFields = [])
31+
{
32+
if (!$username) {
33+
throw new \InvalidArgumentException('The username cannot be empty.');
34+
}
35+
36+
$this->entry = $entry;
37+
$this->username = $username;
38+
$this->password = $password;
39+
$this->roles = $roles;
40+
$this->extraFields = $extraFields;
41+
}
42+
43+
public function getEntry(): Entry
44+
{
45+
return $this->entry;
46+
}
47+
48+
/**
49+
* {@inheritdoc}
50+
*/
51+
public function getRoles()
52+
{
53+
return $this->roles;
54+
}
55+
56+
/**
57+
* {@inheritdoc}
58+
*/
59+
public function getPassword()
60+
{
61+
return $this->password;
62+
}
63+
64+
/**
65+
* {@inheritdoc}
66+
*/
67+
public function getSalt()
68+
{
69+
}
70+
71+
/**
72+
* {@inheritdoc}
73+
*/
74+
public function getUsername()
75+
{
76+
return $this->username;
77+
}
78+
79+
/**
80+
* {@inheritdoc}
81+
*/
82+
public function eraseCredentials()
83+
{
84+
$this->password = null;
85+
}
86+
87+
public function getExtraFields(): array
88+
{
89+
return $this->extraFields;
90+
}
91+
}
+155Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Ldap\Security;
13+
14+
use Symfony\Component\Ldap\Entry;
15+
use Symfony\Component\Ldap\Exception\ConnectionException;
16+
use Symfony\Component\Ldap\LdapInterface;
17+
use Symfony\Component\Security\Core\Exception\InvalidArgumentException;
18+
use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
19+
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
20+
use Symfony\Component\Security\Core\User\UserInterface;
21+
use Symfony\Component\Security\Core\User\UserProviderInterface;
22+
23+
/**
24+
* LdapUserProvider is a simple user provider on top of ldap.
25+
*
26+
* @author Grégoire Pineau <lyrixx@lyrixx.info>
27+
* @author Charles Sarrazin <charles@sarraz.in>
28+
* @author Robin Chalas <robin.chalas@gmail.com>
29+
*/
30+
class LdapUserProvider implements UserProviderInterface
31+
{
32+
private $ldap;
33+
private $baseDn;
34+
private $searchDn;
35+
private $searchPassword;
36+
private $defaultRoles;
37+
private $uidKey;
38+
private $defaultSearch;
39+
private $passwordAttribute;
40+
private $extraFields;
41+
42+
public function __construct(LdapInterface $ldap, string $baseDn, string $searchDn = null, string $searchPassword = null, array $defaultRoles = [], string $uidKey = null, string $filter = null, string $passwordAttribute = null, array $extraFields = [])
43+
{
44+
if (null === $uidKey) {
45+
$uidKey = 'sAMAccountName';
46+
}
47+
48+
if (null === $filter) {
49+
$filter = '({uid_key}={username})';
50+
}
51+
52+
$this->ldap = $ldap;
53+
$this->baseDn = $baseDn;
54+
$this->searchDn = $searchDn;
55+
$this->searchPassword = $searchPassword;
56+
$this->defaultRoles = $defaultRoles;
57+
$this->uidKey = $uidKey;
58+
$this->defaultSearch = str_replace('{uid_key}', $uidKey, $filter);
59+
$this->passwordAttribute = $passwordAttribute;
60+
$this->extraFields = $extraFields;
61+
}
62+
63+
/**
64+
* {@inheritdoc}
65+
*/
66+
public function loadUserByUsername($username)
67+
{
68+
try {
69+
$this->ldap->bind($this->searchDn, $this->searchPassword);
70+
$username = $this->ldap->escape($username, '', LdapInterface::ESCAPE_FILTER);
71+
$query = str_replace('{username}', $username, $this->defaultSearch);
72+
$search = $this->ldap->query($this->baseDn, $query);
73+
} catch (ConnectionException $e) {
74+
throw new UsernameNotFoundException(sprintf('User "%s" not found.', $username), 0, $e);
75+
}
76+
77+
$entries = $search->execute();
78+
$count = \count($entries);
79+
80+
if (!$count) {
81+
throw new UsernameNotFoundException(sprintf('User "%s" not found.', $username));
82+
}
83+
84+
if ($count > 1) {
85+
throw new UsernameNotFoundException('More than one user found');
86+
}
87+
88+
$entry = $entries[0];
89+
90+
try {
91+
if (null !== $this->uidKey) {
92+
$username = $this->getAttributeValue($entry, $this->uidKey);
93+
}
94+
} catch (InvalidArgumentException $e) {
95+
}
96+
97+
return $this->loadUser($username, $entry);
98+
}
99+
100+
/**
101+
* {@inheritdoc}
102+
*/
103+
public function refreshUser(UserInterface $user)
104+
{
105+
if (!$user instanceof LdapUser) {
106+
throw new UnsupportedUserException(sprintf('Instances of "%s" are not supported.', \get_class($user)));
107+
}
108+
109+
return new LdapUser($user->getEntry(), $user->getUsername(), $user->getPassword(), $user->getRoles());
110+
}
111+
112+
/**
113+
* {@inheritdoc}
114+
*/
115+
public function supportsClass($class)
116+
{
117+
return LdapUser::class === $class;
118+
}
119+
120+
/**
121+
* Loads a user from an LDAP entry.
122+
*
123+
* @return LdapUser
124+
*/
125+
protected function loadUser($username, Entry $entry)
126+
{
127+
$password = null;
128+
$extraFields = [];
129+
130+
if (null !== $this->passwordAttribute) {
131+
$password = $this->getAttributeValue($entry, $this->passwordAttribute);
132+
}
133+
134+
foreach ($this->extraFields as $field) {
135+
$extraFields[$field] = $this->getAttributeValue($entry, $field);
136+
}
137+
138+
return new LdapUser($entry, $username, $password, $this->defaultRoles, $extraFields);
139+
}
140+
141+
private function getAttributeValue(Entry $entry, string $attribute)
142+
{
143+
if (!$entry->hasAttribute($attribute)) {
144+
throw new InvalidArgumentException(sprintf('Missing attribute "%s" for user "%s".', $attribute, $entry->getDn()));
145+
}
146+
147+
$values = $entry->getAttribute($attribute);
148+
149+
if (1 !== \count($values)) {
150+
throw new InvalidArgumentException(sprintf('Attribute "%s" has multiple values.', $attribute));
151+
}
152+
153+
return $values[0];
154+
}
155+
}

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.