diff --git a/src/Maker/MakeUser.php b/src/Maker/MakeUser.php
index 302471cac..870debfd4 100644
--- a/src/Maker/MakeUser.php
+++ b/src/Maker/MakeUser.php
@@ -31,6 +31,7 @@
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\PasswordHasher\Hasher\NativePasswordHasher;
use Symfony\Component\Security\Core\Encoder\Argon2iPasswordEncoder;
use Symfony\Component\Security\Core\Encoder\NativePasswordEncoder;
use Symfony\Component\Security\Core\Exception\UserNotFoundException;
@@ -116,6 +117,11 @@ class_exists(DoctrineBundle::class)
$userWillHavePassword = $io->confirm('Does this app need to hash/check user passwords?');
$input->setOption('with-password', $userWillHavePassword);
+ $symfonyGte53 = class_exists(NativePasswordHasher::class);
+ if ($symfonyGte53) {
+ return;
+ }
+
if ($userWillHavePassword && !class_exists(NativePasswordEncoder::class) && Argon2iPasswordEncoder::isSupported()) {
$io->writeln('The newer Argon2i password hasher requires PHP 7.2, libsodium or paragonie/sodium_compat. Your system DOES support this algorithm.');
$io->writeln('You should use Argon2i unless your production system will not support it.');
diff --git a/src/Resources/skeleton/security/UserProvider.tpl.php b/src/Resources/skeleton/security/UserProvider.tpl.php
index 9f2bf8cf5..6b8161eac 100644
--- a/src/Resources/skeleton/security/UserProvider.tpl.php
+++ b/src/Resources/skeleton/security/UserProvider.tpl.php
@@ -72,13 +72,13 @@ public function supportsClass($class)
/**
- * Upgrades the encoded password of a user, typically for using a better hash algorithm.
+ * Upgrades the hashed password of a user, typically for using a better hash algorithm.
*/
- public function upgradePassword(UserInterface $user, string $newEncodedPassword): void
+ public function upgradePassword(UserInterface $user, string $newHashedPassword): void
{
- // TODO: when encoded passwords are in use, this method should:
+ // TODO: when hashed passwords are in use, this method should:
// 1. persist the new password in the user storage
- // 2. update the $user object with $user->setPassword($newEncodedPassword);
+ // 2. update the $user object with $user->setPassword($newHashedPassword);
}
}
diff --git a/src/Security/SecurityConfigUpdater.php b/src/Security/SecurityConfigUpdater.php
index 8355df15c..e4475894a 100644
--- a/src/Security/SecurityConfigUpdater.php
+++ b/src/Security/SecurityConfigUpdater.php
@@ -13,6 +13,7 @@
use Symfony\Bundle\MakerBundle\Util\YamlSourceManipulator;
use Symfony\Component\HttpKernel\Log\Logger;
+use Symfony\Component\PasswordHasher\Hasher\NativePasswordHasher;
use Symfony\Component\Security\Core\Encoder\NativePasswordEncoder;
/**
@@ -50,7 +51,8 @@ public function updateForUserClass(string $yamlSource, UserClassConfiguration $u
$this->updateProviders($userConfig, $userClass);
if ($userConfig->hasPassword()) {
- $this->updateEncoders($userConfig, $userClass);
+ $symfonyGte53 = class_exists(NativePasswordHasher::class);
+ $this->updatePasswordHashers($userConfig, $userClass, $symfonyGte53 ? 'password_hashers' : 'encoders');
}
$contents = $this->manipulator->getContents();
@@ -72,6 +74,10 @@ public function updateForAuthenticator(string $yamlSource, string $firewallName,
$newData = $this->manipulator->getData();
if (!isset($newData['security']['firewalls'])) {
+ if ($newData['security']) {
+ $newData['security']['_firewalls'] = $this->manipulator->createEmptyLine();
+ }
+
$newData['security']['firewalls'] = [];
}
@@ -153,6 +159,10 @@ private function updateProviders(UserClassConfiguration $userConfig, string $use
$this->removeMemoryProviderIfIsSingleConfigured();
$newData = $this->manipulator->getData();
+ if ($newData['security'] && !\array_key_exists('providers', $newData['security'])) {
+ $newData['security']['_providers'] = $this->manipulator->createEmptyLine();
+ }
+
$newData['security']['providers']['__'] = $this->manipulator->createCommentLine(
' used to reload user from session & other features (e.g. switch_user)'
);
@@ -175,18 +185,34 @@ private function updateProviders(UserClassConfiguration $userConfig, string $use
$this->manipulator->setData($newData);
}
- private function updateEncoders(UserClassConfiguration $userConfig, string $userClass)
+ private function updatePasswordHashers(UserClassConfiguration $userConfig, string $userClass, string $keyName = 'password_hashers')
{
$newData = $this->manipulator->getData();
- if (!isset($newData['security']['encoders'])) {
- // encoders is usually the first key, by convention
- $newData['security'] = ['encoders' => []] + $newData['security'];
+ if ('password_hashers' === $keyName && isset($newData['security']['encoders'])) {
+ // fallback to "encoders" if the user already defined encoder config
+ $this->updatePasswordHashers($userClass, $userClass, 'encoders');
+
+ return;
+ }
+
+ if (!isset($newData['security'][$keyName])) {
+ // by convention, password_hashers are put before the user provider option
+ $providersIndex = array_search('providers', array_keys($newData['security']));
+ if (false === $providersIndex) {
+ $newData['security'] = [$keyName => []] + $newData['security'];
+ } else {
+ $newData['security'] = array_merge(
+ \array_slice($newData['security'], 0, $providersIndex),
+ [$keyName => []],
+ \array_slice($newData['security'], $providersIndex)
+ );
+ }
}
- $newData['security']['encoders'][$userClass] = [
- 'algorithm' => $userConfig->shouldUseArgon2() ? 'argon2i' : (class_exists(NativePasswordEncoder::class) ? 'auto' : 'bcrypt'),
+ $newData['security'][$keyName][$userClass] = [
+ 'algorithm' => $userConfig->shouldUseArgon2() ? 'argon2i' : ((class_exists(NativePasswordHasher::class) || class_exists(NativePasswordEncoder::class)) ? 'auto' : 'bcrypt'),
];
- $newData['security']['encoders']['_'] = $this->manipulator->createEmptyLine();
+ $newData['security'][$keyName]['_'] = $this->manipulator->createEmptyLine();
$this->manipulator->setData($newData);
}
diff --git a/tests/Security/SecurityConfigUpdaterTest.php b/tests/Security/SecurityConfigUpdaterTest.php
index 05e43cc9e..03d413630 100644
--- a/tests/Security/SecurityConfigUpdaterTest.php
+++ b/tests/Security/SecurityConfigUpdaterTest.php
@@ -16,6 +16,7 @@
use Symfony\Bundle\MakerBundle\Security\SecurityConfigUpdater;
use Symfony\Bundle\MakerBundle\Security\UserClassConfiguration;
use Symfony\Component\HttpKernel\Log\Logger;
+use Symfony\Component\PasswordHasher\Hasher\NativePasswordHasher;
use Symfony\Component\Security\Core\Encoder\NativePasswordEncoder;
class SecurityConfigUpdaterTest extends TestCase
@@ -48,9 +49,10 @@ public function testUpdateForUserClass(UserClassConfiguration $userConfig, strin
$updater = new SecurityConfigUpdater($this->ysmLogger);
$source = file_get_contents(__DIR__.'/yaml_fixtures/source/'.$startingSourceFilename);
$actualSource = $updater->updateForUserClass($source, $userConfig, $userClass);
- $expectedSource = file_get_contents(__DIR__.'/yaml_fixtures/expected_user_class/'.$expectedSourceFilename);
+ $symfonyVersion = class_exists(NativePasswordHasher::class) ? '5.3' : 'legacy';
+ $expectedSource = file_get_contents(__DIR__.'/yaml_fixtures/expected_user_class/'.$symfonyVersion.'/'.$expectedSourceFilename);
- $bcryptOrAuto = class_exists(NativePasswordEncoder::class) ? 'auto' : 'bcrypt';
+ $bcryptOrAuto = ('5.3' === $symfonyVersion || class_exists(NativePasswordEncoder::class)) ? 'auto' : 'bcrypt';
$expectedSource = str_replace('{BCRYPT_OR_AUTO}', $bcryptOrAuto, $expectedSource);
$this->assertSame($expectedSource, $actualSource);
@@ -95,6 +97,18 @@ public function getUserClassTests()
'simple_security_with_single_memory_provider_configured.yaml',
'simple_security_with_single_memory_provider_configured.yaml',
];
+
+ yield 'security_52_empty_security' => [
+ new UserClassConfiguration(true, 'email', true),
+ 'security_52_entity_email_with_password.yaml',
+ 'security_52_empty_security.yaml',
+ ];
+
+ yield 'security_52_with_firewalls_and_logout' => [
+ new UserClassConfiguration(true, 'email', true),
+ 'security_52_complex_entity_email_with_password.yaml',
+ 'security_52_with_firewalls_and_logout.yaml',
+ ];
}
/**
diff --git a/tests/Security/yaml_fixtures/expected_authenticator/empty_source.yaml b/tests/Security/yaml_fixtures/expected_authenticator/empty_source.yaml
index 699784f62..43063acf5 100644
--- a/tests/Security/yaml_fixtures/expected_authenticator/empty_source.yaml
+++ b/tests/Security/yaml_fixtures/expected_authenticator/empty_source.yaml
@@ -3,4 +3,4 @@ security:
main:
anonymous: lazy
guard:
- authenticators: [App\Security\AppCustomAuthenticator]
\ No newline at end of file
+ authenticators: [App\Security\AppCustomAuthenticator]
diff --git a/tests/Security/yaml_fixtures/expected_authenticator/security_52_empty_source.yaml b/tests/Security/yaml_fixtures/expected_authenticator/security_52_empty_source.yaml
index 26d47aae2..82d0bdf16 100644
--- a/tests/Security/yaml_fixtures/expected_authenticator/security_52_empty_source.yaml
+++ b/tests/Security/yaml_fixtures/expected_authenticator/security_52_empty_source.yaml
@@ -1,5 +1,6 @@
security:
enable_authenticator_manager: true
+
firewalls:
main:
lazy: true
diff --git a/tests/Security/yaml_fixtures/expected_authenticator/security_52_with_multiple_authenticators.yaml b/tests/Security/yaml_fixtures/expected_authenticator/security_52_with_multiple_authenticators.yaml
index 77b3bb419..84241d8cf 100644
--- a/tests/Security/yaml_fixtures/expected_authenticator/security_52_with_multiple_authenticators.yaml
+++ b/tests/Security/yaml_fixtures/expected_authenticator/security_52_with_multiple_authenticators.yaml
@@ -1,5 +1,6 @@
security:
enable_authenticator_manager: true
+
firewalls:
main:
lazy: true
diff --git a/tests/Security/yaml_fixtures/expected_user_class/5.3/empty_source_model_email_with_password.yaml b/tests/Security/yaml_fixtures/expected_user_class/5.3/empty_source_model_email_with_password.yaml
new file mode 100644
index 000000000..732eea848
--- /dev/null
+++ b/tests/Security/yaml_fixtures/expected_user_class/5.3/empty_source_model_email_with_password.yaml
@@ -0,0 +1,9 @@
+security:
+ password_hashers:
+ App\Security\User:
+ algorithm: {BCRYPT_OR_AUTO}
+
+ providers:
+ # used to reload user from session & other features (e.g. switch_user)
+ app_user_provider:
+ id: App\Security\UserProvider
diff --git a/tests/Security/yaml_fixtures/expected_user_class/5.3/entity_email_with_password.yaml b/tests/Security/yaml_fixtures/expected_user_class/5.3/entity_email_with_password.yaml
new file mode 100644
index 000000000..47866590a
--- /dev/null
+++ b/tests/Security/yaml_fixtures/expected_user_class/5.3/entity_email_with_password.yaml
@@ -0,0 +1,16 @@
+security:
+ password_hashers:
+ App\Entity\User:
+ algorithm: {BCRYPT_OR_AUTO}
+
+ # https://symfony.com/doc/current/security.html#where-do-users-come-from-user-providers
+ providers:
+ # used to reload user from session & other features (e.g. switch_user)
+ app_user_provider:
+ entity:
+ class: App\Entity\User
+ property: email
+
+ firewalls:
+ dev: ~
+ main: ~
diff --git a/tests/Security/yaml_fixtures/expected_user_class/entity_username_no_password.yaml b/tests/Security/yaml_fixtures/expected_user_class/5.3/entity_username_no_password.yaml
similarity index 100%
rename from tests/Security/yaml_fixtures/expected_user_class/entity_username_no_password.yaml
rename to tests/Security/yaml_fixtures/expected_user_class/5.3/entity_username_no_password.yaml
diff --git a/tests/Security/yaml_fixtures/expected_user_class/5.3/model_email_password_existing_providers.yaml b/tests/Security/yaml_fixtures/expected_user_class/5.3/model_email_password_existing_providers.yaml
new file mode 100644
index 000000000..db129ff31
--- /dev/null
+++ b/tests/Security/yaml_fixtures/expected_user_class/5.3/model_email_password_existing_providers.yaml
@@ -0,0 +1,16 @@
+security:
+ password_hashers:
+ App\Security\User:
+ algorithm: {BCRYPT_OR_AUTO}
+
+ # https://symfony.com/doc/current/security.html#where-do-users-come-from-user-providers
+ providers:
+ in_memory: { memory: ~ }
+ other_provider: { foo: true }
+ # used to reload user from session & other features (e.g. switch_user)
+ app_user_provider:
+ id: App\Security\UserProvider
+
+ firewalls:
+ dev: ~
+ main: ~
diff --git a/tests/Security/yaml_fixtures/expected_user_class/5.3/model_email_with_password.yaml b/tests/Security/yaml_fixtures/expected_user_class/5.3/model_email_with_password.yaml
new file mode 100644
index 000000000..93623141e
--- /dev/null
+++ b/tests/Security/yaml_fixtures/expected_user_class/5.3/model_email_with_password.yaml
@@ -0,0 +1,14 @@
+security:
+ password_hashers:
+ App\Security\User:
+ algorithm: {BCRYPT_OR_AUTO}
+
+ # https://symfony.com/doc/current/security.html#where-do-users-come-from-user-providers
+ providers:
+ # used to reload user from session & other features (e.g. switch_user)
+ app_user_provider:
+ id: App\Security\UserProvider
+
+ firewalls:
+ dev: ~
+ main: ~
diff --git a/tests/Security/yaml_fixtures/expected_user_class/model_username_no_password.yaml b/tests/Security/yaml_fixtures/expected_user_class/5.3/model_username_no_password.yaml
similarity index 100%
rename from tests/Security/yaml_fixtures/expected_user_class/model_username_no_password.yaml
rename to tests/Security/yaml_fixtures/expected_user_class/5.3/model_username_no_password.yaml
diff --git a/tests/Security/yaml_fixtures/expected_user_class/5.3/security_52_complex_entity_email_with_password.yaml b/tests/Security/yaml_fixtures/expected_user_class/5.3/security_52_complex_entity_email_with_password.yaml
new file mode 100644
index 000000000..7b5119f4a
--- /dev/null
+++ b/tests/Security/yaml_fixtures/expected_user_class/5.3/security_52_complex_entity_email_with_password.yaml
@@ -0,0 +1,20 @@
+security:
+ enable_authenticator_manager: true
+ password_hashers:
+ App\Entity\User:
+ algorithm: {BCRYPT_OR_AUTO}
+
+ # https://symfony.com/doc/current/security.html#where-do-users-come-from-user-providers
+ providers:
+ # used to reload user from session & other features (e.g. switch_user)
+ app_user_provider:
+ entity:
+ class: App\Entity\User
+ property: email
+
+ firewalls:
+ dev:
+ pattern: ^/(_(profiler|wdt)|css|images|js)/
+ security: false
+ main:
+ lazy: true
diff --git a/tests/Security/yaml_fixtures/expected_user_class/5.3/security_52_entity_email_with_password.yaml b/tests/Security/yaml_fixtures/expected_user_class/5.3/security_52_entity_email_with_password.yaml
new file mode 100644
index 000000000..c8c91a92e
--- /dev/null
+++ b/tests/Security/yaml_fixtures/expected_user_class/5.3/security_52_entity_email_with_password.yaml
@@ -0,0 +1,13 @@
+security:
+ enable_authenticator_manager: true
+
+ password_hashers:
+ App\Entity\User:
+ algorithm: {BCRYPT_OR_AUTO}
+
+ providers:
+ # used to reload user from session & other features (e.g. switch_user)
+ app_user_provider:
+ entity:
+ class: App\Entity\User
+ property: email
diff --git a/tests/Security/yaml_fixtures/expected_user_class/5.3/simple_security_with_single_memory_provider_configured.yaml b/tests/Security/yaml_fixtures/expected_user_class/5.3/simple_security_with_single_memory_provider_configured.yaml
new file mode 100644
index 000000000..7ca690651
--- /dev/null
+++ b/tests/Security/yaml_fixtures/expected_user_class/5.3/simple_security_with_single_memory_provider_configured.yaml
@@ -0,0 +1,18 @@
+security:
+ password_hashers:
+ App\Entity\User:
+ algorithm: {BCRYPT_OR_AUTO}
+
+ # https://symfony.com/doc/current/security.html#where-do-users-come-from-user-providers
+ providers:
+ # used to reload user from session & other features (e.g. switch_user)
+ app_user_provider:
+ entity:
+ class: App\Entity\User
+ property: email
+
+ firewalls:
+ dev: ~
+ main:
+ anonymous: lazy
+ provider: app_user_provider
diff --git a/tests/Security/yaml_fixtures/expected_user_class/empty_source_model_email_with_password.yaml b/tests/Security/yaml_fixtures/expected_user_class/legacy/empty_source_model_email_with_password.yaml
similarity index 83%
rename from tests/Security/yaml_fixtures/expected_user_class/empty_source_model_email_with_password.yaml
rename to tests/Security/yaml_fixtures/expected_user_class/legacy/empty_source_model_email_with_password.yaml
index 69698e348..35cd17454 100644
--- a/tests/Security/yaml_fixtures/expected_user_class/empty_source_model_email_with_password.yaml
+++ b/tests/Security/yaml_fixtures/expected_user_class/legacy/empty_source_model_email_with_password.yaml
@@ -6,4 +6,4 @@ security:
providers:
# used to reload user from session & other features (e.g. switch_user)
app_user_provider:
- id: App\Security\UserProvider
\ No newline at end of file
+ id: App\Security\UserProvider
diff --git a/tests/Security/yaml_fixtures/expected_user_class/entity_email_with_password.yaml b/tests/Security/yaml_fixtures/expected_user_class/legacy/entity_email_with_password.yaml
similarity index 100%
rename from tests/Security/yaml_fixtures/expected_user_class/entity_email_with_password.yaml
rename to tests/Security/yaml_fixtures/expected_user_class/legacy/entity_email_with_password.yaml
diff --git a/tests/Security/yaml_fixtures/expected_user_class/legacy/entity_username_no_password.yaml b/tests/Security/yaml_fixtures/expected_user_class/legacy/entity_username_no_password.yaml
new file mode 100644
index 000000000..5cef6b8b0
--- /dev/null
+++ b/tests/Security/yaml_fixtures/expected_user_class/legacy/entity_username_no_password.yaml
@@ -0,0 +1,12 @@
+security:
+ # https://symfony.com/doc/current/security.html#where-do-users-come-from-user-providers
+ providers:
+ # used to reload user from session & other features (e.g. switch_user)
+ app_user_provider:
+ entity:
+ class: App\Entity\User
+ property: username
+
+ firewalls:
+ dev: ~
+ main: ~
diff --git a/tests/Security/yaml_fixtures/expected_user_class/model_email_password_existing_providers.yaml b/tests/Security/yaml_fixtures/expected_user_class/legacy/model_email_password_existing_providers.yaml
similarity index 100%
rename from tests/Security/yaml_fixtures/expected_user_class/model_email_password_existing_providers.yaml
rename to tests/Security/yaml_fixtures/expected_user_class/legacy/model_email_password_existing_providers.yaml
diff --git a/tests/Security/yaml_fixtures/expected_user_class/model_email_with_password.yaml b/tests/Security/yaml_fixtures/expected_user_class/legacy/model_email_with_password.yaml
similarity index 100%
rename from tests/Security/yaml_fixtures/expected_user_class/model_email_with_password.yaml
rename to tests/Security/yaml_fixtures/expected_user_class/legacy/model_email_with_password.yaml
diff --git a/tests/Security/yaml_fixtures/expected_user_class/legacy/model_username_no_password.yaml b/tests/Security/yaml_fixtures/expected_user_class/legacy/model_username_no_password.yaml
new file mode 100644
index 000000000..f66911320
--- /dev/null
+++ b/tests/Security/yaml_fixtures/expected_user_class/legacy/model_username_no_password.yaml
@@ -0,0 +1,10 @@
+security:
+ # https://symfony.com/doc/current/security.html#where-do-users-come-from-user-providers
+ providers:
+ # used to reload user from session & other features (e.g. switch_user)
+ app_user_provider:
+ id: App\Security\UserProvider
+
+ firewalls:
+ dev: ~
+ main: ~
diff --git a/tests/Security/yaml_fixtures/expected_user_class/legacy/security_52_complex_entity_email_with_password.yaml b/tests/Security/yaml_fixtures/expected_user_class/legacy/security_52_complex_entity_email_with_password.yaml
new file mode 100644
index 000000000..b43761262
--- /dev/null
+++ b/tests/Security/yaml_fixtures/expected_user_class/legacy/security_52_complex_entity_email_with_password.yaml
@@ -0,0 +1,20 @@
+security:
+ enable_authenticator_manager: true
+ encoders:
+ App\Entity\User:
+ algorithm: {BCRYPT_OR_AUTO}
+
+ # https://symfony.com/doc/current/security.html#where-do-users-come-from-user-providers
+ providers:
+ # used to reload user from session & other features (e.g. switch_user)
+ app_user_provider:
+ entity:
+ class: App\Entity\User
+ property: email
+
+ firewalls:
+ dev:
+ pattern: ^/(_(profiler|wdt)|css|images|js)/
+ security: false
+ main:
+ lazy: true
diff --git a/tests/Security/yaml_fixtures/expected_user_class/legacy/security_52_entity_email_with_password.yaml b/tests/Security/yaml_fixtures/expected_user_class/legacy/security_52_entity_email_with_password.yaml
new file mode 100644
index 000000000..8bf25fc6f
--- /dev/null
+++ b/tests/Security/yaml_fixtures/expected_user_class/legacy/security_52_entity_email_with_password.yaml
@@ -0,0 +1,13 @@
+security:
+ enable_authenticator_manager: true
+
+ encoders:
+ App\Entity\User:
+ algorithm: {BCRYPT_OR_AUTO}
+
+ providers:
+ # used to reload user from session & other features (e.g. switch_user)
+ app_user_provider:
+ entity:
+ class: App\Entity\User
+ property: email
diff --git a/tests/Security/yaml_fixtures/expected_user_class/simple_security_with_single_memory_provider_configured.yaml b/tests/Security/yaml_fixtures/expected_user_class/legacy/simple_security_with_single_memory_provider_configured.yaml
similarity index 100%
rename from tests/Security/yaml_fixtures/expected_user_class/simple_security_with_single_memory_provider_configured.yaml
rename to tests/Security/yaml_fixtures/expected_user_class/legacy/simple_security_with_single_memory_provider_configured.yaml
diff --git a/tests/Security/yaml_fixtures/source/empty_security.yaml b/tests/Security/yaml_fixtures/source/empty_security.yaml
index 0e241393c..6dbd9183e 100644
--- a/tests/Security/yaml_fixtures/source/empty_security.yaml
+++ b/tests/Security/yaml_fixtures/source/empty_security.yaml
@@ -1 +1 @@
-security: {}
\ No newline at end of file
+security: {}
diff --git a/tests/Security/yaml_fixtures/source/security_52_with_multiple_authenticators.yaml b/tests/Security/yaml_fixtures/source/security_52_with_multiple_authenticators.yaml
index 3de191b3a..5ca600e75 100644
--- a/tests/Security/yaml_fixtures/source/security_52_with_multiple_authenticators.yaml
+++ b/tests/Security/yaml_fixtures/source/security_52_with_multiple_authenticators.yaml
@@ -1,5 +1,6 @@
security:
enable_authenticator_manager: true
+
firewalls:
main:
lazy: true