Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit d81a55b

Browse files
nicolas-grekasweaverryan
authored andcommitted
Allow rehashing passwords when possible and needed
1 parent 91fbc2d commit d81a55b

File tree

10 files changed

+62
-9
lines changed

10 files changed

+62
-9
lines changed

.travis.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ env:
1212
global:
1313
- PHPUNIT_FLAGS="-v"
1414
- SYMFONY_PHPUNIT_DIR="$HOME/symfony-bridge/.phpunit"
15+
- SYMFONY_REQUIRE='>=3.4'
1516

1617
matrix:
1718
fast_finish: true
@@ -23,6 +24,8 @@ matrix:
2324
env: MAKER_TEST_VERSION=dev
2425

2526
before_install:
27+
- find ~/.phpenv -name xdebug.ini -delete
28+
- composer global require --no-progress --no-scripts --no-plugins symfony/flex dev-master
2629
- composer update
2730

2831
install:

appveyor.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ install:
4949
- IF %PHP%==0 echo @php %%~dp0composer.phar %%* > composer.bat
5050
- appveyor-retry appveyor DownloadFile https://getcomposer.org/composer.phar
5151
- cd C:\projects\maker-bundle
52+
- composer global require --no-progress --no-scripts --no-plugins symfony/flex dev-master
5253
- IF %dependencies%==highest appveyor-retry composer update --no-progress --no-suggest --ansi
5354
- vendor/bin/simple-phpunit install
5455

phpunit.xml.dist

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@
1010
<php>
1111
<ini name="error_reporting" value="-1" />
1212
<env name="TEST_DATABASE_DSN" value="mysql://root:@127.0.0.1:3306/test_maker" />
13-
<!-- See #237 -->
14-
<env name="SYMFONY_DEPRECATIONS_HELPER" value="weak_vendors" />
1513
</php>
1614

1715
<testsuites>

src/Doctrine/EntityClassGenerator.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ public function __construct(Generator $generator)
2626
$this->generator = $generator;
2727
}
2828

29-
public function generateEntityClass(ClassNameDetails $entityClassDetails, bool $apiResource): string
29+
public function generateEntityClass(ClassNameDetails $entityClassDetails, bool $apiResource, bool $withPasswordUpgrade = false): string
3030
{
3131
$repoClassDetails = $this->generator->createClassNameDetails(
3232
$entityClassDetails->getRelativeName(),
@@ -51,6 +51,7 @@ public function generateEntityClass(ClassNameDetails $entityClassDetails, bool $
5151
'entity_full_class_name' => $entityClassDetails->getFullName(),
5252
'entity_class_name' => $entityClassDetails->getShortName(),
5353
'entity_alias' => $entityAlias,
54+
'with_password_upgrade' => $withPasswordUpgrade,
5455
]
5556
);
5657

src/Doctrine/EntityRegenerator.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,7 @@ private function generateRepository(ClassMetadata $metadata)
234234
'entity_full_class_name' => $metadata->name,
235235
'entity_class_name' => $entityClassName,
236236
'entity_alias' => strtolower($entityClassName[0]),
237+
'with_password_upgrade' => false,
237238
]
238239
);
239240

src/Maker/MakeUser.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
use Symfony\Component\Console\Input\InputOption;
3434
use Symfony\Component\Security\Core\Encoder\Argon2iPasswordEncoder;
3535
use Symfony\Component\Security\Core\Encoder\NativePasswordEncoder;
36+
use Symfony\Component\Security\Core\User\PasswordUpgraderInterface;
3637
use Symfony\Component\Yaml\Yaml;
3738

3839
/**
@@ -107,7 +108,6 @@ class_exists(DoctrineBundle::class)
107108
$userWillHavePassword = $io->confirm('Does this app need to hash/check user passwords?');
108109
$input->setOption('with-password', $userWillHavePassword);
109110

110-
$useArgon2Encoder = false;
111111
if ($userWillHavePassword && !class_exists(NativePasswordEncoder::class) && Argon2iPasswordEncoder::isSupported()) {
112112
$io->writeln('The newer <comment>Argon2i</comment> password hasher requires PHP 7.2, libsodium or paragonie/sodium_compat. Your system DOES support this algorithm.');
113113
$io->writeln('You should use <comment>Argon2i</comment> unless your production system will not support it.');
@@ -138,7 +138,8 @@ public function generate(InputInterface $input, ConsoleStyle $io, Generator $gen
138138
$entityClassGenerator = new EntityClassGenerator($generator);
139139
$classPath = $entityClassGenerator->generateEntityClass(
140140
$userClassNameDetails,
141-
false // api resource
141+
false, // api resource
142+
$userClassConfiguration->hasPassword() && interface_exists(PasswordUpgraderInterface::class) // security user
142143
);
143144
} else {
144145
$classPath = $generator->generateClass($userClassNameDetails->getFullName(), 'Class.tpl.php');

src/Resources/skeleton/authenticator/LoginFormAuthenticator.tpl.php

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,10 @@
1717
use Symfony\Component\Security\Csrf\CsrfToken;
1818
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
1919
use Symfony\Component\Security\Guard\Authenticator\AbstractFormLoginAuthenticator;
20+
<?= ($password_authenticated = $user_needs_encoder && interface_exists('Symfony\Component\Security\Guard\PasswordAuthenticatedInterface')) ? "use Symfony\Component\Security\Guard\PasswordAuthenticatedInterface;\n" : '' ?>
2021
use Symfony\Component\Security\Http\Util\TargetPathTrait;
2122

22-
class <?= $class_name; ?> extends AbstractFormLoginAuthenticator
23+
class <?= $class_name; ?> extends AbstractFormLoginAuthenticator<?= $password_authenticated ? " implements PasswordAuthenticatedInterface\n" : "\n" ?>
2324
{
2425
use TargetPathTrait;
2526

@@ -85,6 +86,16 @@ public function checkCredentials($credentials, UserInterface $user)
8586
throw new \Exception('TODO: check the credentials inside '.__FILE__);\n" ?>
8687
}
8788

89+
<?php if ($password_authenticated): ?>
90+
/**
91+
* Used to upgrade (rehash) the user's password automatically over time.
92+
*/
93+
public function getPassword($credentials): ?string
94+
{
95+
return $credentials['password'];
96+
}
97+
98+
<?php endif ?>
8899
public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
89100
{
90101
if ($targetPath = $this->getTargetPath($request->getSession(), $providerKey)) {

src/Resources/skeleton/doctrine/Repository.tpl.php

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,39 @@
55
use <?= $entity_full_class_name; ?>;
66
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
77
use Doctrine\Common\Persistence\ManagerRegistry;
8+
<?= $with_password_upgrade ? "use Symfony\Component\Security\Core\Exception\UnsupportedUserException;\n" : '' ?>
9+
<?= $with_password_upgrade ? "use Symfony\Component\Security\Core\User\PasswordUpgraderInterface;\n" : '' ?>
10+
<?= $with_password_upgrade ? "use Symfony\Component\Security\Core\User\UserInterface;\n" : '' ?>
811

912
/**
1013
* @method <?= $entity_class_name; ?>|null find($id, $lockMode = null, $lockVersion = null)
1114
* @method <?= $entity_class_name; ?>|null findOneBy(array $criteria, array $orderBy = null)
1215
* @method <?= $entity_class_name; ?>[] findAll()
1316
* @method <?= $entity_class_name; ?>[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
1417
*/
15-
class <?= $class_name; ?> extends ServiceEntityRepository
18+
class <?= $class_name; ?> extends ServiceEntityRepository<?= $with_password_upgrade ? " implements PasswordUpgraderInterface\n" : "\n" ?>
1619
{
1720
public function __construct(ManagerRegistry $registry)
1821
{
1922
parent::__construct($registry, <?= $entity_class_name; ?>::class);
2023
}
2124

25+
<?php if ($with_password_upgrade): ?>
26+
/**
27+
* Used to upgrade (rehash) the user's password automatically over time.
28+
*/
29+
public function upgradePassword(UserInterface $user, string $newEncodedPassword): void
30+
{
31+
if (!$user instanceof User) {
32+
throw new UnsupportedUserException(sprintf('Instances of "%s" are not supported.', \get_class($user)));
33+
}
34+
35+
$user->setPassword($newEncodedPassword);
36+
$this->_em->persist($user);
37+
$this->_em->flush();
38+
}
39+
40+
<?php endif ?>
2241
// /**
2342
// * @return <?= $entity_class_name ?>[] Returns an array of <?= $entity_class_name ?> objects
2443
// */

src/Resources/skeleton/security/UserProvider.tpl.php

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,11 @@
44

55
use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
66
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
7+
<?= ($password_upgrader = interface_exists('Symfony\Component\Security\Core\User\PasswordUpgraderInterface')) ? "use Symfony\Component\Security\Core\User\PasswordUpgraderInterface;\n" : '' ?>
78
use Symfony\Component\Security\Core\User\UserInterface;
89
use Symfony\Component\Security\Core\User\UserProviderInterface;
910

10-
class <?= $class_name ?> implements UserProviderInterface
11+
class <?= $class_name ?> implements UserProviderInterface<?= $password_upgrader ? ", PasswordUpgraderInterface\n" : "\n" ?>
1112
{
1213
/**
1314
* Symfony calls this method if you use features like switch_user
@@ -60,4 +61,16 @@ public function supportsClass($class)
6061
{
6162
return <?= $user_short_name ?>::class === $class;
6263
}
64+
<?php if ($password_upgrader): ?>
65+
66+
/**
67+
* Upgrades the encoded password of a user, typically for using a better hash algorithm.
68+
*/
69+
public function upgradePassword(UserInterface $user, string $newEncodedPassword): void
70+
{
71+
// TODO: when encoded passwords are in use, this method should:
72+
// 1. persist the new password in the user storage
73+
// 2. update the $user object with $user->setPassword($newEncodedPassword);
74+
}
75+
<?php endif ?>
6376
}

src/Test/MakerTestCase.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,12 @@ protected function executeMakerCommand(MakerTestDetails $testDetails)
3636
if ('.php' === substr($file, -4)) {
3737
$csProcess = $testEnv->runPhpCSFixer($file);
3838

39-
$this->assertTrue($csProcess->isSuccessful(), sprintf('File "%s" has a php-cs problem: %s', $file, $csProcess->getOutput()));
39+
$this->assertTrue($csProcess->isSuccessful(), sprintf(
40+
"File '%s' has a php-cs problem: %s\n\n%s",
41+
$file,
42+
$csProcess->getErrorOutput(),
43+
file_get_contents($testEnv->getPath().'/'.$file)
44+
));
4045
}
4146

4247
if ('.twig' === substr($file, -5)) {

0 commit comments

Comments
 (0)