0

I've been using this Userprovider with Symfony, which extends the original LdapUserProvider and only adds some roles, depending on the ActiveDirectory groups the user is in. It was working fine, but since Symfony 4.4 Symfony\Component\Security\Core\User\LdapUserProvider is deprecated and Symfony\Component\Ldap\Security\LdapUserProvider should be used instead.

src/Security/LdapUserProvider.php

namespace App\Security;

use Symfony\Component\Ldap\Entry;
use Symfony\Component\Security\Core\User\LdapUserProvider as SymfonyLdapUserProvider;
#use Symfony\Component\Ldap\Security\LdapUserProvider as SymfonyLdapUserProvider;
use Symfony\Component\Security\Core\User\User;

class LdapUserProvider extends SymfonyLdapUserProvider
{
    private static $roles = [
        'ROLE_MANAGEMENT' => [
            'name' => 'Verwaltung',
            'groups' => [
                'CN=Verwaltung,OU=Personen,DC=example,DC=com',
            ],
        ],
        'ROLE_SC' => [
            'name' => 'IT',
            'groups' => [
                'CN=IT-MA,OU=Gruppen,DC=example,DC=com',
            ],
        ],
        'ROLE_DOMAINADMIN' => [
            'name' => 'Domain Admin',
            'groups' => [
                'CN=Domain Admins,CN=Users,DC=example,DC=com',
            ],
        ],
        // some more roles ...
    ];

    protected function loadUser($username, Entry $entry)
    {
        $roles = ['ROLE_USER'];
        if ($entry->hasAttribute('memberOf')) {
            $roles = array_merge($roles, $this->getRolesFromGroups($entry->getAttribute('memberOf')));
        }
        $dn = $entry->getAttribute('distinguishedName')[0];
        $elements = explode(',', $dn);
        $basePath = array_slice($elements, ($_ENV['ADLDAP_BASEDN_DEPTH']*-1));
        $_SESSION['currentUser']['baseDn'] = implode(',', $basePath);
        return new User($username, null, $roles);
    }

    private function getRolesFromGroups(array $userGroups)
    {
        $roles = [];
        foreach ($this::$roles as $key => $role) {
            foreach ($role['groups'] as $group) {
                if (in_array($group, $userGroups)) {
                    $roles[] = $key;
                    break;
                }
            }
        }
        return $roles;
    }

    public function supportsClass($class)
    {
        $test = User::class === $class || is_subclass_of($class, User::class);
        dump($test);
        return $test;
    }
}

However, when I replace Symfony\Component\Security\Core\User\LdapUserProvider with Symfony\Component\Ldap\Security\LdapUserProvider I get the following Exception:

There is no user provider for user "Symfony\Component\Security\Core\User\User". Shouldn't the "supportsClass()" method of your user provider return true for this classname?

The funny thing is: I tested the return value of function supportsClass($class) and it returns true.

I found some questions like this one:

but they deal with custom User entities, which I don't have. Can anybody give me a hint where this error is coming from, if not from my UserProvider?

Symfony is at version 4.4.29. Excerpts from other relevant files:

config/services.yaml

Symfony\Component\Ldap\Ldap:
    arguments: ['@Symfony\Component\Ldap\Adapter\ExtLdap\Adapter']
Symfony\Component\Ldap\Adapter\ExtLdap\Adapter:
    arguments:
        -   host: '%env(ADLDAP_HOST)%'
            port: '%env(ADLDAP_PORT)%'
            options:
                protocol_version: 3
                referrals: false

config/packages/security.yaml

security:
    providers:
        ad:
            ldap:
                service: Symfony\Component\Ldap\Ldap
                base_dn: '%env(ADLDAP_BASEDN)%'
                search_dn: '%env(ADLDAP_USERDN)%'
                search_password: '%env(ADLDAP_PASSWORD)%'
                default_roles: ROLE_USER
                uid_key: 'samaccountname'
                extra_fields: ['distinguishedName']
    firewalls:
        dev:
            pattern: ^/(_(profiler|wdt)|css|images|js)/
            security: false
        main:
            anonymous: ~
            form_login_ldap:
                service: Symfony\Component\Ldap\Ldap
                login_path: login
                check_path: login
                dn_string: '%env(ADLDAP_BASEDN)%'
                query_string: '(samaccountname={username})'
            logout:
                path: /logout
                target: login

    access_control:
        - { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/, roles: ROLE_USER }
Gerald Schneider
  • 17,416
  • 9
  • 60
  • 78
  • I can't really tell what is wrong but the Core\User should not be used for anything but the in-memory user provider. It is also deprecated since 5.3. Might consider running make:user. I'm not familiar with ldap but I would think you would explicitly specify the user provider somewhere in there. And finally, using $_SESSION directly seems very suspicious. Did this code used to work? – Cerad Aug 06 '21 at 12:50
  • Yes, this used wo work. Moving `$_SESSION` out of it is on my todo list, but I wanted to solve this first. Knowing that Core\User is deprecated in 5.3 is really helpful. I didn't want to create my own User class because I don't need anything extra, but if I need to do this anyway (I'm preparing for 5.4) I can just do it now. – Gerald Schneider Aug 06 '21 at 12:56
  • In that case I would suggest switching to the new [authentication system](https://symfony.com/blog/new-in-symfony-5-3-guard-component-deprecation) as well. form_login_ldap should still work as before but as long as you have enable_authenticator_manager: true in security.yaml then it will use the new system. Might also consider starting a fresh 5.3 project and just getting the authentication to work instead of trying to update an existing project. Once you have a working example then updating your legacy app might be easier. – Cerad Aug 06 '21 at 13:03
  • So much for going from one LTS version to the next ... I guess I will do just that. Thank you very much for the tips. – Gerald Schneider Aug 06 '21 at 13:11
  • Just to be clear the deprecated code will continue to work just fine for 5.4 LTS. Next 5 years or so. Just have those annoying deprecated messages. And be on the lookout for https://symfony.com/blog/new-in-symfony-5-3-runtime-component as well in case you decide to update your recipes. public/index.php has changed significantly and you might need to manually install symfony/runtime while upgrading. Good luck. – Cerad Aug 06 '21 at 13:26

0 Answers0