vendor/symfony/security-core/Encoder/EncoderFactory.php line 29

Open in your IDE?
  1. <?php
  2. /*
  3.  * This file is part of the Symfony package.
  4.  *
  5.  * (c) Fabien Potencier <fabien@symfony.com>
  6.  *
  7.  * For the full copyright and license information, please view the LICENSE
  8.  * file that was distributed with this source code.
  9.  */
  10. namespace Symfony\Component\Security\Core\Encoder;
  11. use Symfony\Component\PasswordHasher\Hasher\PasswordHasherAwareInterface;
  12. use Symfony\Component\PasswordHasher\Hasher\PasswordHasherFactory;
  13. use Symfony\Component\PasswordHasher\LegacyPasswordHasherInterface;
  14. use Symfony\Component\PasswordHasher\PasswordHasherInterface;
  15. use Symfony\Component\Security\Core\Exception\LogicException;
  16. trigger_deprecation('symfony/security-core''5.3''The "%s" class is deprecated, use "%s" instead.'EncoderFactory::class, PasswordHasherFactory::class);
  17. /**
  18.  * A generic encoder factory implementation.
  19.  *
  20.  * @author Johannes M. Schmitt <schmittjoh@gmail.com>
  21.  *
  22.  * @deprecated since Symfony 5.3, use {@link PasswordHasherFactory} instead
  23.  */
  24. class EncoderFactory implements EncoderFactoryInterface
  25. {
  26.     private $encoders;
  27.     public function __construct(array $encoders)
  28.     {
  29.         $this->encoders $encoders;
  30.     }
  31.     /**
  32.      * {@inheritdoc}
  33.      */
  34.     public function getEncoder($user)
  35.     {
  36.         $encoderKey null;
  37.         if (($user instanceof PasswordHasherAwareInterface && null !== $encoderName $user->getPasswordHasherName()) || ($user instanceof EncoderAwareInterface && null !== $encoderName $user->getEncoderName())) {
  38.             if (!\array_key_exists($encoderName$this->encoders)) {
  39.                 throw new \RuntimeException(sprintf('The encoder "%s" was not configured.'$encoderName));
  40.             }
  41.             $encoderKey $encoderName;
  42.         } else {
  43.             foreach ($this->encoders as $class => $encoder) {
  44.                 if ((\is_object($user) && $user instanceof $class) || (!\is_object($user) && (is_subclass_of($user$class) || $user == $class))) {
  45.                     $encoderKey $class;
  46.                     break;
  47.                 }
  48.             }
  49.         }
  50.         if (null === $encoderKey) {
  51.             throw new \RuntimeException(sprintf('No encoder has been configured for account "%s".', \is_object($user) ? get_debug_type($user) : $user));
  52.         }
  53.         if (!$this->encoders[$encoderKey] instanceof PasswordEncoderInterface) {
  54.             if ($this->encoders[$encoderKey] instanceof LegacyPasswordHasherInterface) {
  55.                 $this->encoders[$encoderKey] = new LegacyPasswordHasherEncoder($this->encoders[$encoderKey]);
  56.             } elseif ($this->encoders[$encoderKey] instanceof PasswordHasherInterface) {
  57.                 $this->encoders[$encoderKey] = new PasswordHasherEncoder($this->encoders[$encoderKey]);
  58.             } else {
  59.                 $this->encoders[$encoderKey] = $this->createEncoder($this->encoders[$encoderKey]);
  60.             }
  61.         }
  62.         return $this->encoders[$encoderKey];
  63.     }
  64.     /**
  65.      * Creates the actual encoder instance.
  66.      *
  67.      * @throws \InvalidArgumentException
  68.      */
  69.     private function createEncoder(array $configbool $isExtra false): PasswordEncoderInterface
  70.     {
  71.         if (isset($config['algorithm'])) {
  72.             $rawConfig $config;
  73.             $config $this->getEncoderConfigFromAlgorithm($config);
  74.         }
  75.         if (!isset($config['class'])) {
  76.             throw new \InvalidArgumentException('"class" must be set in '.json_encode($config));
  77.         }
  78.         if (!isset($config['arguments'])) {
  79.             throw new \InvalidArgumentException('"arguments" must be set in '.json_encode($config));
  80.         }
  81.         $encoder = new $config['class'](...$config['arguments']);
  82.         if ($isExtra || !\in_array($config['class'], [NativePasswordEncoder::class, SodiumPasswordEncoder::class], true)) {
  83.             return $encoder;
  84.         }
  85.         if ($rawConfig ?? null) {
  86.             $extraEncoders array_map(function (string $algo) use ($rawConfig): PasswordEncoderInterface {
  87.                 $rawConfig['algorithm'] = $algo;
  88.                 return $this->createEncoder($rawConfig);
  89.             }, ['pbkdf2'$rawConfig['hash_algorithm'] ?? 'sha512']);
  90.         } else {
  91.             $extraEncoders = [new Pbkdf2PasswordEncoder(), new MessageDigestPasswordEncoder()];
  92.         }
  93.         return new MigratingPasswordEncoder($encoder, ...$extraEncoders);
  94.     }
  95.     private function getEncoderConfigFromAlgorithm(array $config): array
  96.     {
  97.         if ('auto' === $config['algorithm']) {
  98.             $encoderChain = [];
  99.             // "plaintext" is not listed as any leaked hashes could then be used to authenticate directly
  100.             foreach ([SodiumPasswordEncoder::isSupported() ? 'sodium' 'native''pbkdf2'$config['hash_algorithm']] as $algo) {
  101.                 $config['algorithm'] = $algo;
  102.                 $encoderChain[] = $this->createEncoder($configtrue);
  103.             }
  104.             return [
  105.                 'class' => MigratingPasswordEncoder::class,
  106.                 'arguments' => $encoderChain,
  107.             ];
  108.         }
  109.         if ($fromEncoders = ($config['migrate_from'] ?? false)) {
  110.             unset($config['migrate_from']);
  111.             $encoderChain = [$this->createEncoder($configtrue)];
  112.             foreach ($fromEncoders as $name) {
  113.                 if ($encoder $this->encoders[$name] ?? false) {
  114.                     $encoder $encoder instanceof PasswordEncoderInterface $encoder $this->createEncoder($encodertrue);
  115.                 } else {
  116.                     $encoder $this->createEncoder(['algorithm' => $name], true);
  117.                 }
  118.                 $encoderChain[] = $encoder;
  119.             }
  120.             return [
  121.                 'class' => MigratingPasswordEncoder::class,
  122.                 'arguments' => $encoderChain,
  123.             ];
  124.         }
  125.         switch ($config['algorithm']) {
  126.             case 'plaintext':
  127.                 return [
  128.                     'class' => PlaintextPasswordEncoder::class,
  129.                     'arguments' => [$config['ignore_case']],
  130.                 ];
  131.             case 'pbkdf2':
  132.                 return [
  133.                     'class' => Pbkdf2PasswordEncoder::class,
  134.                     'arguments' => [
  135.                         $config['hash_algorithm'] ?? 'sha512',
  136.                         $config['encode_as_base64'] ?? true,
  137.                         $config['iterations'] ?? 1000,
  138.                         $config['key_length'] ?? 40,
  139.                     ],
  140.                 ];
  141.             case 'bcrypt':
  142.                 $config['algorithm'] = 'native';
  143.                 $config['native_algorithm'] = \PASSWORD_BCRYPT;
  144.                 return $this->getEncoderConfigFromAlgorithm($config);
  145.             case 'native':
  146.                 return [
  147.                     'class' => NativePasswordEncoder::class,
  148.                     'arguments' => [
  149.                         $config['time_cost'] ?? null,
  150.                         (($config['memory_cost'] ?? 0) << 10) ?: null,
  151.                         $config['cost'] ?? null,
  152.                     ] + (isset($config['native_algorithm']) ? [=> $config['native_algorithm']] : []),
  153.                 ];
  154.             case 'sodium':
  155.                 return [
  156.                     'class' => SodiumPasswordEncoder::class,
  157.                     'arguments' => [
  158.                         $config['time_cost'] ?? null,
  159.                         (($config['memory_cost'] ?? 0) << 10) ?: null,
  160.                     ],
  161.                 ];
  162.             case 'argon2i':
  163.                 if (SodiumPasswordEncoder::isSupported() && !\defined('SODIUM_CRYPTO_PWHASH_ALG_ARGON2ID13')) {
  164.                     $config['algorithm'] = 'sodium';
  165.                 } elseif (\defined('PASSWORD_ARGON2I')) {
  166.                     $config['algorithm'] = 'native';
  167.                     $config['native_algorithm'] = \PASSWORD_ARGON2I;
  168.                 } else {
  169.                     throw new LogicException(sprintf('Algorithm "argon2i" is not available. Either use %s"auto" or upgrade to PHP 7.2+ instead.', \defined('SODIUM_CRYPTO_PWHASH_ALG_ARGON2ID13') ? '"argon2id", ' ''));
  170.                 }
  171.                 return $this->getEncoderConfigFromAlgorithm($config);
  172.             case 'argon2id':
  173.                 if (($hasSodium SodiumPasswordEncoder::isSupported()) && \defined('SODIUM_CRYPTO_PWHASH_ALG_ARGON2ID13')) {
  174.                     $config['algorithm'] = 'sodium';
  175.                 } elseif (\defined('PASSWORD_ARGON2ID')) {
  176.                     $config['algorithm'] = 'native';
  177.                     $config['native_algorithm'] = \PASSWORD_ARGON2ID;
  178.                 } else {
  179.                     throw new LogicException(sprintf('Algorithm "argon2id" is not available. Either use %s"auto", upgrade to PHP 7.3+ or use libsodium 1.0.15+ instead.', \defined('PASSWORD_ARGON2I') || $hasSodium '"argon2i", ' ''));
  180.                 }
  181.                 return $this->getEncoderConfigFromAlgorithm($config);
  182.         }
  183.         return [
  184.             'class' => MessageDigestPasswordEncoder::class,
  185.             'arguments' => [
  186.                 $config['algorithm'],
  187.                 $config['encode_as_base64'] ?? true,
  188.                 $config['iterations'] ?? 5000,
  189.             ],
  190.         ];
  191.     }
  192. }