Reputation: 5
I work with Symfony 5.1.2 and my tests worked until I introduced a chain of user providers. I derive all of my test classes from a class I created in order to put common methods and properties. Among these methods there is a method that I use to connect a user.
public function connectUser(string $username)
{
$userProvider = static::$container->get(UserProviderInterface::class);
$user = $userProvider->loadUserByUsername($username);
$this->assertNotNull($user);
$this->kernelBrowser->loginUser($user);
}
With the following settings there were no problems
providers:
backend_users:
memory:
users:
[email protected]: { password: '$argon2id$v=19$m=65536,t=4,p=1$c0F2RmVYa21RclE4ZXJkTA$mvXd/skXaV9w1rqmWb6B5MTtgkP86inWSkj0E8hjtTA', roles: ['ROLE_ADMIN'] }
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
anonymous: true
lazy: true
logout:
path: security.authentication.logout
provider: backend_users
guard:
authenticators:
- App\Security\LoginFormAuthenticator
I then introduced a new user source ; a database.
With the following settings there is a problem
providers:
# used to reload user from session & other features (e.g. switch_user)
backend_users:
memory:
users:
[email protected]: { password: '$argon2id$v=19$m=65536,t=4,p=1$c0F2RmVYa21RclE4ZXJkTA$mvXd/skXaV9w1rqmWb6B5MTtgkP86inWSkj0E8hjtTA', roles: ['ROLE_ADMIN'] }
frontend_users:
entity:
class: App\Entity\User
property: email
all_users:
chain:
providers: ['backend_users','frontend_users']
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
anonymous: true
lazy: true
logout:
path: security.authentication.logout
guard:
authenticators:
- App\Security\LoginFormAuthenticator
provider: all_users
With this configuration, the service container no longer seems to contain the UserProviderInterface class. Because I receive this message :
You have requested a non-existent service "Symfony\Component\Security\Core\User\UserProviderInterface".
I figured I might need to implement a custom user provider, but after doing some research in the source code, I realized that there is a class that seems specific to user provider chains that is :Symfony\Component\Security\Core\User\ChainUserProvider
As the name suggests, the UserProviderInterface class is an interface, so I'm not supposed to worry about the internal implementation.
Why is this interface no longer in the service container ? How to reintroduce it properly ?
Thank you !
Upvotes: 0
Views: 138
Reputation: 48865
Suppose you have multiple providers all implementing UserProviderInterface. When you type hint against the interface, which service do you want injected and how would the container know? The container does not know anything about your firewalls so it can't guess that you want the chain provider. So things worked when you only had one provider but will fail when you have multiple providers.
The same question arises anytime you have multiple implementations of the same interface. You either need to typehint against a specific implementation or inject the desired service manually or create an alias which will tie the interface to one specific implementation.
In your case:
bin/console debug:container | grep UserProvider
doctrine.orm.security.user.provider Symfony\Bridge\Doctrine\Security\User\EntityUserProvider
security.user.provider.chain Symfony\Component\Security\Core\User\ChainUserProvider
security.user.provider.concrete.all_users Symfony\Component\Security\Core\User\ChainUserProvider
security.user.provider.concrete.backend_users Symfony\Component\Security\Core\User\InMemoryUserProvider
security.user.provider.concrete.frontend_users Symfony\Bridge\Doctrine\Security\User\EntityUserProvider
security.user.provider.in_memory Symfony\Component\Security\Core\User\InMemoryUserProvider
security.user.provider.ldap Symfony\Component\Ldap\Security\LdapUserProvider
security.user.provider.missing Symfony\Component\Security\Core\User\MissingUserProvider
security.user_providers Symfony\Component\Security\Core\User\ChainUserProvider
If you always want the all_users chain provider to be injected then add an alias:
config/services.yaml
Symfony\Component\Security\Core\User\UserProviderInterface : '@security.user.provider.concrete.all_users'
And you should be good to go.
Upvotes: 2