Symfony Ldap checkPassword

Tags: , , ,



I’m using Symfony 4.4 and I’m doing my own authenticator. Everything works fine, I just can’t figure out how to compare the password entered by the user and the one in Ldap. I would like to do this in the “checkCredentials” method in my LoginFormAuthenticator. Here is my LdapUserProvider:

class LdapUserProvider implements UserProviderInterface, PasswordUpgraderInterface
{
    private $ldap;
    private $baseDn;
    private $searchDn;
    private $searchPassword;
    private $defaultRoles;
    private $uidKey;
    private $defaultSearch;
    private $passwordAttribute;
    private $extraFields;
    //New
    private $em;

    public function __construct(Ldap $ldap, string $baseDn, EntityManagerInterface $em , string $searchDn = null, string $searchPassword = null, array $defaultRoles = [], string $uidKey = null, string $filter = null, string $passwordAttribute = null, array $extraFields = [])
    {
        if (null === $uidKey) {
            $uidKey = 'sAMAccountName';
        }

        if (null === $filter) {
            $filter = '({uid_key}={username})';
        }

        $this->ldap = $ldap;
        $this->baseDn = $baseDn;
        $this->searchDn = $searchDn;
        $this->searchPassword = $searchPassword;
        $this->defaultRoles = $defaultRoles;
        $this->uidKey = $uidKey;
        $this->defaultSearch = str_replace('{uid_key}', $uidKey, $filter);
        $this->passwordAttribute = $passwordAttribute;
        $this->extraFields = $extraFields;
        $this->em = $em;
    }

    /**
     * {@inheritdoc}
     */
    public function loadUserByUsername($username)
    {
        try {
            $this->ldap->bind($this->searchDn, $this->searchPassword);
            $username = $this->ldap->escape($username, '', LdapInterface::ESCAPE_FILTER);
            $query = str_replace('{username}', $username, $this->defaultSearch);
            $search = $this->ldap->query($this->baseDn, $query);
        } catch (ConnectionException $e) {
            throw new UsernameNotFoundException(sprintf('User "%s" not found.', $username), 0, $e);
        }

        $entries = $search->execute();
        $count = count($entries);

        if (!$count) {
            throw new UsernameNotFoundException(sprintf('User "%s" not found.', $username));
        }

        if ($count > 1) {
            throw new UsernameNotFoundException('More than one user found.');
        }

        return $this->loadUser($username, $entries[0]);
    }

    /**
     * {@inheritdoc}
     */
    public function refreshUser(UserInterface $user)
    {
        if (!$user instanceof LdapUser || !$user instanceof User) {
            throw new UnsupportedUserException(sprintf('Instances of "%s" are not supported.', get_class($user)));
        }

        //New
        $userRepository = $this->em->getRepository("AppBundle:User");
        $user = $userRepository->findOneBy(array("username" => $user->getUsername()));

        if($user === null){
            throw new UnsupportedUserException(sprintf('Instances of "%s" are not supported.', get_class($user)));
        }

        return new LdapUser($user->getEntry(), $user->getUsername(), $user->getPassword(), $user->getRoles(), $user->getExtraFields());
    }

    /**
     * {@inheritdoc}
     */
    public function upgradePassword(UserInterface $user, string $newEncodedPassword): void
    {
        if (!$user instanceof LdapUser) {
            throw new UnsupportedUserException(sprintf('Instances of "%s" are not supported.', get_class($user)));
        }

        if (null === $this->passwordAttribute) {
            return;
        }

        try {
            $user->getEntry()->setAttribute($this->passwordAttribute, [$newEncodedPassword]);
            $this->ldap->getEntryManager()->update($user->getEntry());
            $user->setPassword($newEncodedPassword);
        } catch (ExceptionInterface $e) {
            // ignore failed password upgrades
        }
    }

    /**
     * {@inheritdoc}
     */
    public function supportsClass($class)
    {
        return LdapUser::class === $class;
    }

    /**
     * Loads a user from an LDAP entry.
     *
     * @param $username
     * @param Entry $entry
     * @return UserInterface
     */
    protected function loadUser($username, Entry $entry)
    {
        /*
        $password = null;
        $extraFields = [];
        var_dump($this->passwordAttribute);
        if (null !== $this->passwordAttribute) {
            var_dump($this->passwordAttribute);
            $password = $this->getAttributeValue($entry, $this->passwordAttribute);
            var_dump($password);
        }

        foreach ($this->extraFields as $field) {
            $extraFields[$field] = $this->getAttributeValue($entry, $field);
        }
        exit();
        return new LdapUser($entry, $username, $password, $this->defaultRoles, $extraFields);*/

        $userRepository = $this->em->getRepository("App:User");
        //On récupère les infos de l'utilisateur qui se connecte
        $user = $userRepository->findOneBy(array("username" => $username));

        //Si l'utilisateur est null, donc pas présent en BDD mais OK niveau LDAP
        if ($user === null) {
            //Créé un User pour l'ajouter à la BDD une fois qu'on s'est assuré que c'était bien un utilisateur LDAP
            //Cas première connexion de l'utilisateur
            $user = new User();
            $user->setFirstname($entry->getAttribute("givenName")[0]);
            $user->setLastname($entry->getAttribute("sn")[0]);
            $user->setEmail($entry->getAttribute("mail")[0]);
            $user->setUsername($entry->getAttribute("uid")[0]);
            $user->setRoles($this->defaultRoles);

            $this->em->persist($user);
            $this->em->flush();
        } else {
            $this->em->flush();
        }

        return $user;
    }

    public function checkPassword($password){

    }

    /**
     * Fetches the password from an LDAP entry.
     *
     * @param null|Entry $entry
     */
    private function getPassword(Entry $entry)
    {
        if (null === $this->passwordAttribute) {
            return;
        }

        if (!$entry->hasAttribute($this->passwordAttribute)) {
            throw new InvalidArgumentException(sprintf('Missing attribute "%s" for user "%s".', $this->passwordAttribute, $entry->getDn()));
        }

        $values = $entry->getAttribute($this->passwordAttribute);

        if (1 !== count($values)) {
            throw new InvalidArgumentException(sprintf('Attribute "%s" has multiple values.', $this->passwordAttribute));
        }

        return $values[0];
    }

    private function getAttributeValue(Entry $entry, string $attribute)
    {
        var_dump("getAttributeValue ".$attribute);
        if (!$entry->hasAttribute($attribute)) {
            throw new InvalidArgumentException(sprintf('Missing attribute "%s" for user "%s".', $attribute, $entry->getDn()));
        }

        $values = $entry->getAttribute($attribute);

        if (1 !== count($values)) {
            throw new InvalidArgumentException(sprintf('Attribute "%s" has multiple values.', $attribute));
        }

        return $values[0];
    }
}

I first thought about using the getPassword method but it requires and Entry, and I don’t know how to get this Entry. Thanks

Answer

For anyone who might want the same, I actually used

$this->ldap->bind($dnUser, $password);

The $dnUser corresponds to the dn of the Entry of the user, you can get it with

$user->getEntry()->getDn();

And the password is the one entered by the User. It checks if the password entered by the user is the same than the one in the LDAP. If it’s good, nothing happens, but if it’s false it throws an InvalidCredentialsException. So I used it like that:

public function checkCredentials($password){
    try{
        $this->ldap->bind($dnUser, $password);
    } catch (InvalidCredentialsException $e){
        return false;
    }
    return true;
}


Source: stackoverflow