Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Symfony3 Docs WSSE fail "Cannot replace arguments if none have been configured yet"

Tags:

symfony

wsse

Following this

http://symfony.com/doc/current/security/custom_authentication_provider.html

Results in

Service "security.authentication.provider.wsse.wsse_secured": Cannot replace arguments if none have been configured yet.

I cannot find anything about this error anywhere. This uses the doc's WSSE code and it fails.

This repo shows it failing https://github.com/jakenoble/wsse_test

I want to get it working eventually with the FOS User Bundle. But I cannot get it to work with a basic Symfony3 install so FOS User Bundle is out of the question at the moment.

Having dug around a bit...

There is a an arg at element index_0 on class Symfony\Component\DependencyInjection\ChildDefinition the object for the arg at element index_0 has an id of fos_user.user_provider.username_email.

The replace call then attempts to get the arguments of fos_user.user_provider.username_email, but there are none. Then the error occurs.

Any ideas?

like image 960
Jake N Avatar asked Jan 04 '23 10:01

Jake N


2 Answers

TL;DR Move your service definitions below the autoloading definitions in services.yml and change the WsseFactory code to

    $container
        ->setDefinition($providerId, new ChildDefinition(WsseProvider::class))
        ->setArgument('$userProvider', new Reference($userProvider))
    ;

Full explanation. The first mistake in the supplied code is that the services definitions prepend the autoloading lines. The autoloading will just override the previous definitions and will cause the AuthenticationManagerInterface failure. Moving the definitions below will fix the issue. The other way to fix the issue is aliases as @yceruto and @gintko pointed.

But only that move will not make the code work despite on your answer. You probably didnt notice how changed something else to make it work.

The second issue, the failure of replaceArgument, is related to Symfony's order of the container compilation as was correctly supposed. The order is defined in the PassConfig class:

$this->optimizationPasses = array(array(
        new ExtensionCompilerPass(),
        new ResolveDefinitionTemplatesPass(),
        ...
        $autowirePass = new AutowirePass(false),
        ...
    ));

I omitted the irrelevant passes. The security.authentication.provider.wsse.wsse_secured definition created by the WsseFactory is produced first. Then ResolveDefinitionTemplatesPass will take place, and will try to replace the arguments of the definition and raise the Cannot replace arguments exception you got:

    foreach ($definition->getArguments() as $k => $v) {
        if (is_numeric($k)) {
            $def->addArgument($v);
        } elseif (0 === strpos($k, 'index_')) {
            $def->replaceArgument((int) substr($k, strlen('index_')), $v);
        } else {
            $def->setArgument($k, $v);
        }
    }

The issue will appear cause the pass will call Definition::replaceArgument for index_0. As the parent definition doesn't have an argument at position 0 neither in the former services.xml nor in the fixed one. AutowirePass wasn't executed yet, the autogenerated definitions has no arguments, the manual definition has the named $cachePool argument only.

So to fix the issue you could use rather:

->setArgument(0, new Reference($userProvider)); //proposed by @yceruto
->replaceArgument('$userProvider', new Reference($userProvider)); //proposed by @gintko
->setArgument('$userProvider', new Reference($userProvider)); // by me

All of them will entail the calls of Definition::addArgument or Definition::setArgument and will work out. There are only the little difference: - setArgument(0, ... could not work for some other scenarios; - I like ->setArgument('$userProvider' more than ->replaceArgument('$userProvider' due to semantics. Nothing to replace yet!

Hope the details why the issue appears now clear.

PS. There are also few other funny ways to overcome the issue.

Fix the config a bit:

AppBundle\Security\Authentication\Provider\WsseProvider:
    arguments:
        0: ''
        $cachePool: '@cache.app'
    public: false

Or set an alias of Symfony\Component\Security\Core\User\UserProviderInterface to let the autowire do the rest for you.

    $container
        ->setDefinition($providerId, new ChildDefinition(WsseProvider::class))
    //    ->replaceArgument(0, new Reference($userProvider))
    ;
    $container
        ->setAlias('Symfony\Component\Security\Core\User\UserProviderInterface',$userProvider)
    ;
like image 109
origaminal Avatar answered Jan 06 '23 03:01

origaminal


This look like at this moment the provider definition doesn't have the autowired arguments ready, maybe related to the order in which the "CompilerPass" are processed, by now you can solve it with these little tweaks:

change this line in WsseFactory.php:

->replaceArgument(0, new Reference($userProvider))

by:

->setArgument(0, new Reference($userProvider))

and add this alias to services.yml to complete the autowired arguments of the new provider:

Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface: '@security.authentication.manager'
like image 39
yceruto Avatar answered Jan 06 '23 02:01

yceruto