Skip to content

Commit 6736cdf

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

File tree

13 files changed

+609
-111
lines changed

13 files changed

+609
-111
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\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
@@ -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

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\LdapUserProvider" abstract="true">
179179
<argument /> <!-- security.ldap.ldap -->
180180
<argument /> <!-- base dn -->
181181
<argument /> <!-- search dn -->

src/Symfony/Bundle/SecurityBundle/composer.json

Lines 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\\": "" },
Lines 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+
}
Lines 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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy