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

Skip to content

Commit 979d6af

Browse files
author
Robin Chalas
committed
[Ldap] Add security LdapUser and provider
1 parent b2dadc1 commit 979d6af

File tree

9 files changed

+615
-106
lines changed

9 files changed

+615
-106
lines changed

UPGRADE-4.4.md

Lines 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\User\LdapUserProvider` instead.
144145
* Implementations of `PasswordEncoderInterface` and `UserPasswordEncoderInterface` should add a new `needsRehash()` method
145146

146147
Stopwatch

UPGRADE-5.0.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,7 @@ Routing
372372
Security
373373
--------
374374

375+
* The `LdapUserProvider` class has been removed, use `Symfony\Component\Ldap\Security\User\LdapUserProvider` instead.
375376
* Implementations of `PasswordEncoderInterface` and `UserPasswordEncoderInterface` must have a new `needsRehash()` method
376377
* The `Role` and `SwitchUserRole` classes have been removed.
377378
* 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

Lines 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\User\LdapUserProvider" abstract="true">
179179
<argument /> <!-- security.ldap.ldap -->
180180
<argument /> <!-- base dn -->
181181
<argument /> <!-- search dn -->
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
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\User;
13+
14+
use Symfony\Component\Ldap\Entry;
15+
use Symfony\Component\Security\Core\User\UserInterface;
16+
17+
/**
18+
* @author Robin Chalas <[email protected]>
19+
*
20+
* @internal
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+
}
85+
86+
public function getExtraFields(): array
87+
{
88+
return $this->extraFields;
89+
}
90+
}
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
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\User;
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\User;
21+
use Symfony\Component\Security\Core\User\UserInterface;
22+
use Symfony\Component\Security\Core\User\UserProviderInterface;
23+
24+
/**
25+
* LdapUserProvider is a simple user provider on top of ldap.
26+
*
27+
* @author Grégoire Pineau <[email protected]>
28+
* @author Charles Sarrazin <[email protected]>
29+
* @author Robin Chalas <[email protected]>
30+
*/
31+
class LdapUserProvider implements UserProviderInterface
32+
{
33+
private $ldap;
34+
private $baseDn;
35+
private $searchDn;
36+
private $searchPassword;
37+
private $defaultRoles;
38+
private $uidKey;
39+
private $defaultSearch;
40+
private $passwordAttribute;
41+
private $extraFields;
42+
43+
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 = [])
44+
{
45+
if (null === $uidKey) {
46+
$uidKey = 'sAMAccountName';
47+
}
48+
49+
if (null === $filter) {
50+
$filter = '({uid_key}={username})';
51+
}
52+
53+
$this->ldap = $ldap;
54+
$this->baseDn = $baseDn;
55+
$this->searchDn = $searchDn;
56+
$this->searchPassword = $searchPassword;
57+
$this->defaultRoles = $defaultRoles;
58+
$this->uidKey = $uidKey;
59+
$this->defaultSearch = str_replace('{uid_key}', $uidKey, $filter);
60+
$this->passwordAttribute = $passwordAttribute;
61+
$this->extraFields = $extraFields;
62+
}
63+
64+
/**
65+
* {@inheritdoc}
66+
*/
67+
public function loadUserByUsername($username)
68+
{
69+
try {
70+
$this->ldap->bind($this->searchDn, $this->searchPassword);
71+
$username = $this->ldap->escape($username, '', LdapInterface::ESCAPE_FILTER);
72+
$query = str_replace('{username}', $username, $this->defaultSearch);
73+
$search = $this->ldap->query($this->baseDn, $query);
74+
} catch (ConnectionException $e) {
75+
throw new UsernameNotFoundException(sprintf('User "%s" not found.', $username), 0, $e);
76+
}
77+
78+
$entries = $search->execute();
79+
$count = \count($entries);
80+
81+
if (!$count) {
82+
throw new UsernameNotFoundException(sprintf('User "%s" not found.', $username));
83+
}
84+
85+
if ($count > 1) {
86+
throw new UsernameNotFoundException('More than one user found');
87+
}
88+
89+
$entry = $entries[0];
90+
91+
try {
92+
if (null !== $this->uidKey) {
93+
$username = $this->getAttributeValue($entry, $this->uidKey);
94+
}
95+
} catch (InvalidArgumentException $e) {
96+
}
97+
98+
return $this->loadUser($username, $entry);
99+
}
100+
101+
/**
102+
* {@inheritdoc}
103+
*/
104+
public function refreshUser(UserInterface $user)
105+
{
106+
if (!$user instanceof User) {
107+
throw new UnsupportedUserException(sprintf('Instances of "%s" are not supported.', \get_class($user)));
108+
}
109+
110+
return new User($user->getUsername(), null, $user->getRoles());
111+
}
112+
113+
/**
114+
* {@inheritdoc}
115+
*/
116+
public function supportsClass($class)
117+
{
118+
return 'Symfony\Component\Security\Core\User\User' === $class;
119+
}
120+
121+
/**
122+
* Loads a user from an LDAP entry.
123+
*
124+
* @return LdapUser
125+
*/
126+
protected function loadUser($username, Entry $entry)
127+
{
128+
$password = null;
129+
$extraFields = [];
130+
131+
if (null !== $this->passwordAttribute) {
132+
$password = $this->getAttributeValue($entry, $this->passwordAttribute);
133+
}
134+
135+
foreach ($this->extraFields as $field) {
136+
$extraFields[$field] = $this->getAttributeValue($entry, $field);
137+
}
138+
139+
return new LdapUser($entry, $username, $password, $this->defaultRoles, $extraFields);
140+
}
141+
142+
/**
143+
* Fetches a required unique attribute value from an LDAP entry.
144+
*
145+
* @param Entry|null $entry
146+
* @param string $attribute
147+
*/
148+
private function getAttributeValue(Entry $entry, $attribute)
149+
{
150+
if (!$entry->hasAttribute($attribute)) {
151+
throw new InvalidArgumentException(sprintf('Missing attribute "%s" for user "%s".', $attribute, $entry->getDn()));
152+
}
153+
154+
$values = $entry->getAttribute($attribute);
155+
156+
if (1 !== \count($values)) {
157+
throw new InvalidArgumentException(sprintf('Attribute "%s" has multiple values.', $attribute));
158+
}
159+
160+
return $values[0];
161+
}
162+
}

0 commit comments

Comments
 (0)