Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Symfony\Config\Security\ProviderConfig broken during cache:clear #50713

Closed
k0d3r1s opened this issue Jun 20, 2023 · 5 comments
Closed

Symfony\Config\Security\ProviderConfig broken during cache:clear #50713

k0d3r1s opened this issue Jun 20, 2023 · 5 comments

Comments

@k0d3r1s
Copy link
Contributor

k0d3r1s commented Jun 20, 2023

Symfony version(s) affected

6.3.0

Description

image
Sometimes during cache:clear there is an error thrown about Symfony\Config\Security\ProviderConfig missing an entity method

How to reproduce

This is a tricky part as it is not happening every time but on random occasions.
Possible reproduction:

  1. have an entity config part in security.php config
  2. remove var/cache directory
  3. run bin/console cache:clear

Possible Solution

No response

Additional Context

php: 8.2.7

my config/packages/security.php:

<?php declare(strict_types = 1);

use App\Constants\Enum\Environment;
use App\Constants\Enum\Role;
use App\Entity\User;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
use Symfony\Component\Security\Core\Authorization\Voter\AuthenticatedVoter;
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
use Symfony\Config\SecurityConfig;

return static function (SecurityConfig $config, ContainerConfigurator $container): void {
    $config
        ->passwordHasher(PasswordAuthenticatedUserInterface::class, 'auto');

    $config
        ->provider('user_provider')
            ->entity()
                ->class(User::class)
                ->property('email');

    $config
        ->roleHierarchy(Role::ROLE_ADMIN->value, 'ROLE_ALLOWED_TO_SWITCH');

    $config
        ->accessControl()
        ->path('^/api/efconnect')
        ->roles(Role::ROLE_ADMIN->value);

    $config
        ->accessControl()
        ->path('^/api/elfinder')
        ->roles(Role::ROLE_ADMIN->value);

    $config
        ->accessControl()
        ->path('^/glide')
        ->roles(Role::ROLE_ADMIN->value);

    $config
        ->accessControl()
            ->path('^/')
            ->roles(AuthenticatedVoter::PUBLIC_ACCESS);

    $config
        ->firewall(Environment::DEV->value)
        ->pattern('/(_(wdt|profiler)|css|images|js|map|png|jpe?g|gif|bmp|ico|svg)/')
        ->security(false)
        ->lazy(true);

    $login = $config
        ->firewall('login');

    $login
        ->jsonLogin()
        ->usernamePath('email')
        ->checkPath('api_users_login');

    $login
        ->pattern('^/');

    $login
        ->logout()
        ->path('app_users_logout');

    $login
        ->switchUser()
        ->parameter('X-Switch-User');

    if (Environment::TEST->value === $container->env()) {
        $config
            ->passwordHasher(PasswordAuthenticatedUserInterface::class)
            ->algorithm($algo = 'xxh3')
            ->hashAlgorithm($algo)
            ->encodeAsBase64(false)
            ->iterations(0);
    }
};
@acirulis
Copy link

acirulis commented Jun 26, 2023

In existing Symony 6.3 project:
Step 1

bin/console make:entity EntityOne

Successfully created an entity,
Step 2

bin/console make:entity EntityTwo

Error:

Symfony\Component\ErrorHandler\Error\UndefinedMethodError^ {#438
  #message: "Attempted to call an undefined method named "entity" of class "Symfony\Config\Security\ProviderConfig"."
  #code: 0
  #file: "./config/packages/security.php"
  #line: 17
  trace: {
    ./config/packages/security.php:17 {
      Symfony\Component\DependencyInjection\Loader\ProtectedPhpFileLoader::{closure}^
      › ->provider('user_provider')
      ›     ->entity()
      ›         ->class(User::class)
    }
    ./vendor/symfony/dependency-injection/Loader/PhpFileLoader.php:146 { …}
    ./vendor/symfony/dependency-injection/Loader/PhpFileLoader.php:64 { …}
    ./vendor/symfony/config/Loader/FileLoader.php:167 { …}
    ./vendor/symfony/config/Loader/FileLoader.php:87 { …}
    ./vendor/symfony/dependency-injection/Loader/FileLoader.php:70 { …}
    ./vendor/symfony/dependency-injection/Loader/Configurator/ContainerConfigurator.php:64 { …}
    ./vendor/symfony/framework-bundle/Kernel/MicroKernelTrait.php:53 { …}
    ./vendor/symfony/framework-bundle/Kernel/MicroKernelTrait.php:180 { …}
    ./vendor/symfony/dependency-injection/Loader/ClosureLoader.php:36 { …}
    ./vendor/symfony/config/Loader/DelegatingLoader.php:37 { …}
    ./vendor/symfony/framework-bundle/Kernel/MicroKernelTrait.php:136 { …}
    ./vendor/symfony/http-kernel/Kernel.php:604 { …}
    ./vendor/symfony/http-kernel/Kernel.php:505 { …}
    ./vendor/symfony/http-kernel/Kernel.php:757 { …}
    ./vendor/symfony/http-kernel/Kernel.php:126 { …}
    ./vendor/symfony/framework-bundle/Console/Application.php:154 { …}
    ./vendor/symfony/framework-bundle/Console/Application.php:72 { …}
    ./vendor/symfony/console/Application.php:174 { …}
    ./vendor/symfony/runtime/Runner/Symfony/ConsoleApplicationRunner.php:54 { …}
    ./vendor/autoload_runtime.php:29 { …}
    ./bin/console:11 { …}
  }
}

File ./config/packages/security.php:

<?php declare(strict_types = 1);

use App\Constants\Enum\Environment;
use App\Constants\Enum\Role;
use App\Entity\User;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
use Symfony\Component\Security\Core\Authorization\Voter\AuthenticatedVoter;
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
use Symfony\Config\SecurityConfig;

return static function (SecurityConfig $config, ContainerConfigurator $container): void {
    $config
        ->passwordHasher(PasswordAuthenticatedUserInterface::class, 'auto');

    $config
        ->provider('user_provider')
            ->entity()
                ->class(User::class)
                ->property('email');
// .....

bin/console cache:clear --no-warmup did not help. Only removal of cache/dev folder. After that, everything worked again.

@k0d3r1s
Copy link
Contributor Author

k0d3r1s commented Jun 29, 2023

This is what ProviderConfig contain when error occurs:

<?php

namespace Symfony\Config\Security;

require_once __DIR__.\DIRECTORY_SEPARATOR.'ProviderConfig'.\DIRECTORY_SEPARATOR.'ChainConfig.php';

use Symfony\Component\Config\Loader\ParamConfigurator;
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;

/**
 * This class is automatically generated to help in creating a config.
 */
class ProviderConfig 
{
    private $id;
    private $chain;
    private $_usedProperties = [];

    /**
     * @default null
     * @param ParamConfigurator|mixed $value
     * @return $this
     */
    public function id($value): static
    {
        $this->_usedProperties['id'] = true;
        $this->id = $value;

        return $this;
    }

    public function chain(array $value = []): \Symfony\Config\Security\ProviderConfig\ChainConfig
    {
        if (null === $this->chain) {
            $this->_usedProperties['chain'] = true;
            $this->chain = new \Symfony\Config\Security\ProviderConfig\ChainConfig($value);
        } elseif (0 < \func_num_args()) {
            throw new InvalidConfigurationException('The node created by "chain()" has already been initialized. You cannot pass values the second time you call chain().');
        }

        return $this->chain;
    }

    public function __construct(array $value = [])
    {
        if (array_key_exists('id', $value)) {
            $this->_usedProperties['id'] = true;
            $this->id = $value['id'];
            unset($value['id']);
        }

        if (array_key_exists('chain', $value)) {
            $this->_usedProperties['chain'] = true;
            $this->chain = new \Symfony\Config\Security\ProviderConfig\ChainConfig($value['chain']);
            unset($value['chain']);
        }

        if ([] !== $value) {
            throw new InvalidConfigurationException(sprintf('The following keys are not supported by "%s": ', __CLASS__).implode(', ', array_keys($value)));
        }
    }

    public function toArray(): array
    {
        $output = [];
        if (isset($this->_usedProperties['id'])) {
            $output['id'] = $this->id;
        }
        if (isset($this->_usedProperties['chain'])) {
            $output['chain'] = $this->chain->toArray();
        }

        return $output;
    }

}

When it works correctly, it contains:

<?php

namespace Symfony\Config\Security;

require_once __DIR__.\DIRECTORY_SEPARATOR.'ProviderConfig'.\DIRECTORY_SEPARATOR.'ChainConfig.php';
require_once __DIR__.\DIRECTORY_SEPARATOR.'ProviderConfig'.\DIRECTORY_SEPARATOR.'MemoryConfig.php';
require_once __DIR__.\DIRECTORY_SEPARATOR.'ProviderConfig'.\DIRECTORY_SEPARATOR.'LdapConfig.php';
require_once __DIR__.\DIRECTORY_SEPARATOR.'ProviderConfig'.\DIRECTORY_SEPARATOR.'EntityConfig.php';

use Symfony\Component\Config\Loader\ParamConfigurator;
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;

/**
 * This class is automatically generated to help in creating a config.
 */
class ProviderConfig 
{
    private $id;
    private $chain;
    private $memory;
    private $ldap;
    private $entity;
    private $_usedProperties = [];

    /**
     * @default null
     * @param ParamConfigurator|mixed $value
     * @return $this
     */
    public function id($value): static
    {
        $this->_usedProperties['id'] = true;
        $this->id = $value;

        return $this;
    }

    public function chain(array $value = []): \Symfony\Config\Security\ProviderConfig\ChainConfig
    {
        if (null === $this->chain) {
            $this->_usedProperties['chain'] = true;
            $this->chain = new \Symfony\Config\Security\ProviderConfig\ChainConfig($value);
        } elseif (0 < \func_num_args()) {
            throw new InvalidConfigurationException('The node created by "chain()" has already been initialized. You cannot pass values the second time you call chain().');
        }

        return $this->chain;
    }

    public function memory(array $value = []): \Symfony\Config\Security\ProviderConfig\MemoryConfig
    {
        if (null === $this->memory) {
            $this->_usedProperties['memory'] = true;
            $this->memory = new \Symfony\Config\Security\ProviderConfig\MemoryConfig($value);
        } elseif (0 < \func_num_args()) {
            throw new InvalidConfigurationException('The node created by "memory()" has already been initialized. You cannot pass values the second time you call memory().');
        }

        return $this->memory;
    }

    public function ldap(array $value = []): \Symfony\Config\Security\ProviderConfig\LdapConfig
    {
        if (null === $this->ldap) {
            $this->_usedProperties['ldap'] = true;
            $this->ldap = new \Symfony\Config\Security\ProviderConfig\LdapConfig($value);
        } elseif (0 < \func_num_args()) {
            throw new InvalidConfigurationException('The node created by "ldap()" has already been initialized. You cannot pass values the second time you call ldap().');
        }

        return $this->ldap;
    }

    public function entity(array $value = []): \Symfony\Config\Security\ProviderConfig\EntityConfig
    {
        if (null === $this->entity) {
            $this->_usedProperties['entity'] = true;
            $this->entity = new \Symfony\Config\Security\ProviderConfig\EntityConfig($value);
        } elseif (0 < \func_num_args()) {
            throw new InvalidConfigurationException('The node created by "entity()" has already been initialized. You cannot pass values the second time you call entity().');
        }

        return $this->entity;
    }

    public function __construct(array $value = [])
    {
        if (array_key_exists('id', $value)) {
            $this->_usedProperties['id'] = true;
            $this->id = $value['id'];
            unset($value['id']);
        }

        if (array_key_exists('chain', $value)) {
            $this->_usedProperties['chain'] = true;
            $this->chain = new \Symfony\Config\Security\ProviderConfig\ChainConfig($value['chain']);
            unset($value['chain']);
        }

        if (array_key_exists('memory', $value)) {
            $this->_usedProperties['memory'] = true;
            $this->memory = new \Symfony\Config\Security\ProviderConfig\MemoryConfig($value['memory']);
            unset($value['memory']);
        }

        if (array_key_exists('ldap', $value)) {
            $this->_usedProperties['ldap'] = true;
            $this->ldap = new \Symfony\Config\Security\ProviderConfig\LdapConfig($value['ldap']);
            unset($value['ldap']);
        }

        if (array_key_exists('entity', $value)) {
            $this->_usedProperties['entity'] = true;
            $this->entity = new \Symfony\Config\Security\ProviderConfig\EntityConfig($value['entity']);
            unset($value['entity']);
        }

        if ([] !== $value) {
            throw new InvalidConfigurationException(sprintf('The following keys are not supported by "%s": ', __CLASS__).implode(', ', array_keys($value)));
        }
    }

    public function toArray(): array
    {
        $output = [];
        if (isset($this->_usedProperties['id'])) {
            $output['id'] = $this->id;
        }
        if (isset($this->_usedProperties['chain'])) {
            $output['chain'] = $this->chain->toArray();
        }
        if (isset($this->_usedProperties['memory'])) {
            $output['memory'] = $this->memory->toArray();
        }
        if (isset($this->_usedProperties['ldap'])) {
            $output['ldap'] = $this->ldap->toArray();
        }
        if (isset($this->_usedProperties['entity'])) {
            $output['entity'] = $this->entity->toArray();
        }

        return $output;
    }

}

@stephanvierkant
Copy link
Contributor

Same issue with Symfony 6.3.5 here, but also with another library (LiipImagine):

Uncaught Error: Call to undefined method Symfony\Config\LiipImagine\ResolversConfig::webPath()

@carsonbot
Copy link

Hey, thanks for your report!
There has not been a lot of activity here for a while. Is this bug still relevant? Have you managed to find a workaround?

@HypeMC
Copy link
Contributor

HypeMC commented Apr 18, 2024

This was possibly fixed in #53989.

@carsonbot carsonbot removed the Stalled label Apr 18, 2024
@xabbuh xabbuh closed this as completed Apr 18, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants