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