Skip to content

Allow rehashing passwords when possible and needed #389

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

Merged
merged 1 commit into from
Aug 18, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ env:
global:
- PHPUNIT_FLAGS="-v"
- SYMFONY_PHPUNIT_DIR="$HOME/symfony-bridge/.phpunit"
- SYMFONY_REQUIRE='>=3.4'

matrix:
fast_finish: true
Expand All @@ -23,6 +24,8 @@ matrix:
env: MAKER_TEST_VERSION=dev

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

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

Expand Down
2 changes: 0 additions & 2 deletions phpunit.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@
<php>
<ini name="error_reporting" value="-1" />
<env name="TEST_DATABASE_DSN" value="mysql://root:@127.0.0.1:3306/test_maker" />
<!-- See #237 -->
<env name="SYMFONY_DEPRECATIONS_HELPER" value="weak_vendors" />
</php>

<testsuites>
Expand Down
3 changes: 2 additions & 1 deletion src/Doctrine/EntityClassGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public function __construct(Generator $generator)
$this->generator = $generator;
}

public function generateEntityClass(ClassNameDetails $entityClassDetails, bool $apiResource): string
public function generateEntityClass(ClassNameDetails $entityClassDetails, bool $apiResource, bool $withPasswordUpgrade = false): string
{
$repoClassDetails = $this->generator->createClassNameDetails(
$entityClassDetails->getRelativeName(),
Expand All @@ -51,6 +51,7 @@ public function generateEntityClass(ClassNameDetails $entityClassDetails, bool $
'entity_full_class_name' => $entityClassDetails->getFullName(),
'entity_class_name' => $entityClassDetails->getShortName(),
'entity_alias' => $entityAlias,
'with_password_upgrade' => $withPasswordUpgrade,
]
);

Expand Down
1 change: 1 addition & 0 deletions src/Doctrine/EntityRegenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,7 @@ private function generateRepository(ClassMetadata $metadata)
'entity_full_class_name' => $metadata->name,
'entity_class_name' => $entityClassName,
'entity_alias' => strtolower($entityClassName[0]),
'with_password_upgrade' => false,
]
);

Expand Down
5 changes: 3 additions & 2 deletions src/Maker/MakeUser.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Security\Core\Encoder\Argon2iPasswordEncoder;
use Symfony\Component\Security\Core\Encoder\NativePasswordEncoder;
use Symfony\Component\Security\Core\User\PasswordUpgraderInterface;
use Symfony\Component\Yaml\Yaml;

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

$useArgon2Encoder = false;
if ($userWillHavePassword && !class_exists(NativePasswordEncoder::class) && Argon2iPasswordEncoder::isSupported()) {
$io->writeln('The newer <comment>Argon2i</comment> password hasher requires PHP 7.2, libsodium or paragonie/sodium_compat. Your system DOES support this algorithm.');
$io->writeln('You should use <comment>Argon2i</comment> unless your production system will not support it.');
Expand Down Expand Up @@ -138,7 +138,8 @@ public function generate(InputInterface $input, ConsoleStyle $io, Generator $gen
$entityClassGenerator = new EntityClassGenerator($generator);
$classPath = $entityClassGenerator->generateEntityClass(
$userClassNameDetails,
false // api resource
false, // api resource
$userClassConfiguration->hasPassword() && interface_exists(PasswordUpgraderInterface::class) // security user
);
} else {
$classPath = $generator->generateClass($userClassNameDetails->getFullName(), 'Class.tpl.php');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@
use Symfony\Component\Security\Csrf\CsrfToken;
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
use Symfony\Component\Security\Guard\Authenticator\AbstractFormLoginAuthenticator;
<?= ($password_authenticated = $user_needs_encoder && interface_exists('Symfony\Component\Security\Guard\PasswordAuthenticatedInterface')) ? "use Symfony\Component\Security\Guard\PasswordAuthenticatedInterface;\n" : '' ?>
use Symfony\Component\Security\Http\Util\TargetPathTrait;

class <?= $class_name; ?> extends AbstractFormLoginAuthenticator
class <?= $class_name; ?> extends AbstractFormLoginAuthenticator<?= $password_authenticated ? " implements PasswordAuthenticatedInterface\n" : "\n" ?>
{
use TargetPathTrait;

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

<?php if ($password_authenticated): ?>
/**
* Used to upgrade (rehash) the user's password automatically over time.
*/
public function getPassword($credentials): ?string
{
return $credentials['password'];
}

<?php endif ?>
public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
{
if ($targetPath = $this->getTargetPath($request->getSession(), $providerKey)) {
Expand Down
21 changes: 20 additions & 1 deletion src/Resources/skeleton/doctrine/Repository.tpl.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,39 @@
use <?= $entity_full_class_name; ?>;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Common\Persistence\ManagerRegistry;
<?= $with_password_upgrade ? "use Symfony\Component\Security\Core\Exception\UnsupportedUserException;\n" : '' ?>
<?= $with_password_upgrade ? "use Symfony\Component\Security\Core\User\PasswordUpgraderInterface;\n" : '' ?>
<?= $with_password_upgrade ? "use Symfony\Component\Security\Core\User\UserInterface;\n" : '' ?>

/**
* @method <?= $entity_class_name; ?>|null find($id, $lockMode = null, $lockVersion = null)
* @method <?= $entity_class_name; ?>|null findOneBy(array $criteria, array $orderBy = null)
* @method <?= $entity_class_name; ?>[] findAll()
* @method <?= $entity_class_name; ?>[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
class <?= $class_name; ?> extends ServiceEntityRepository
class <?= $class_name; ?> extends ServiceEntityRepository<?= $with_password_upgrade ? " implements PasswordUpgraderInterface\n" : "\n" ?>
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, <?= $entity_class_name; ?>::class);
}

<?php if ($with_password_upgrade): ?>
/**
* Used to upgrade (rehash) the user's password automatically over time.
*/
public function upgradePassword(UserInterface $user, string $newEncodedPassword): void
{
if (!$user instanceof User) {
throw new UnsupportedUserException(sprintf('Instances of "%s" are not supported.', \get_class($user)));
}

$user->setPassword($newEncodedPassword);
$this->_em->persist($user);
$this->_em->flush();
}

<?php endif ?>
// /**
// * @return <?= $entity_class_name ?>[] Returns an array of <?= $entity_class_name ?> objects
// */
Expand Down
15 changes: 14 additions & 1 deletion src/Resources/skeleton/security/UserProvider.tpl.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@

use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
<?= ($password_upgrader = interface_exists('Symfony\Component\Security\Core\User\PasswordUpgraderInterface')) ? "use Symfony\Component\Security\Core\User\PasswordUpgraderInterface;\n" : '' ?>
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;

class <?= $class_name ?> implements UserProviderInterface
class <?= $class_name ?> implements UserProviderInterface<?= $password_upgrader ? ", PasswordUpgraderInterface\n" : "\n" ?>
{
/**
* Symfony calls this method if you use features like switch_user
Expand Down Expand Up @@ -60,4 +61,16 @@ public function supportsClass($class)
{
return <?= $user_short_name ?>::class === $class;
}
<?php if ($password_upgrader): ?>

/**
* Upgrades the encoded password of a user, typically for using a better hash algorithm.
*/
public function upgradePassword(UserInterface $user, string $newEncodedPassword): void
{
// TODO: when encoded 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);
}
<?php endif ?>
}
7 changes: 6 additions & 1 deletion src/Test/MakerTestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,12 @@ protected function executeMakerCommand(MakerTestDetails $testDetails)
if ('.php' === substr($file, -4)) {
$csProcess = $testEnv->runPhpCSFixer($file);

$this->assertTrue($csProcess->isSuccessful(), sprintf('File "%s" has a php-cs problem: %s', $file, $csProcess->getOutput()));
$this->assertTrue($csProcess->isSuccessful(), sprintf(
"File '%s' has a php-cs problem: %s\n\n%s",
$file,
$csProcess->getErrorOutput(),
file_get_contents($testEnv->getPath().'/'.$file)
));
}

if ('.twig' === substr($file, -5)) {
Expand Down
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy