Reputation: 45
I'm just trying to change default User
object for in-memory provider. Default User
object is Symfony\Component\Security\Core\User\User
. But this object doesn't fill my requirements, so i copied that object and added some properties for my requirements then named it as InMemoryUser
and used it like following:
encoders:
App\Component\Security\Core\User\InMemoryUser: plaintext
Then i got the following error when tried to run the app.
No encoder has been configured for account "App\Component\Security\Core\User\InMemoryUser"
I saw the problem after investigeting about half hour. Symfony is calling Symfony\Component\Security\Core\User\InMemoryUserProvider
by default and using Symfony\Component\Security\Core\User\User
object to create in-memory users in __construct
method.
Then i tryed to override default InMemoryUserProvider
as following.
services:
App\Component\Security\Core\User\CustomInMemoryUserProvider:
decorates: security.user.provider.concrete.api_user_provider
arguments:
$users: [] # this should be contains in-memory users defined in the security.yaml but i dont know how to do that.
This works fine except a little problem, does not inject $users
like InMemoryUserProvider
. Normally, $users
contains in-memory user list defined in security.yaml
.
Now, how can i inject $users
for my CustomInMemoryUserProvider
and is this a good practice to change User
object for InMemoryProvider
?
Thanks for your answers in advance.
I'm using symfony 5.1
by the way.
Upvotes: 2
Views: 1251
Reputation: 48865
I have never quite cracked the mystery of properly decorating a service whose constructor gets injected dynamically by a DI extension.
However, in this case, all you really need to do is to change the class of the in memory provider which can be done in a compiler pass in your kernel:
# src\Kernel.php
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use App\User\InMemoryUserProvider;
class Kernel extends BaseKernel implements CompilerPassInterface
...
public function process(ContainerBuilder $container)
{
$id = 'security.user.provider.in_memory';
$container->getDefinition($id)->setClass(InMemoryUserProvider::class);
}
When testing this I originally tried to extend the existing core in memory provider but it uses a private method which can't be overridden. So I just re-implemented the complete interface
namespace App\User;
class InMemoryUserProvider implements UserProviderInterface
{
public function __construct(array $users)
{
dump($users); // Confirms get the users from security.yaml
}
public function loadUserByUsername(string $username)
{
// TODO: Implement loadUserByUsername() method.
echo "Get User {$username}\n";
}
Confirm it has been wired up as expected:
bin/console debug:container | grep UserProv
App\User\InMemoryUserProvider = App\User\InMemoryUserProvider
Symfony\Component\Security\Core\User\UserProviderInterface = alias for "security.user.provider.concrete.users_in_memory"
security.user.provider.concrete.users_in_memory = App\User\InMemoryUserProvider
security.user.provider.in_memory = App\User\InMemoryUserProvider
And made a command just to verify it all worked as expected:
class UserProviderCommand extends Command
{
protected static $defaultName = 'user:provider';
private $userProvider;
public function __construct(UserProviderInterface $userProvider)
{
parent::__construct();
$this->userProvider = $userProvider;
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
echo "User Provider: " . get_class($this->userProvider) . "\n";
$this->userProvider->loadUserByUsername('xxx');
return 0;
}
}
Upvotes: 1