Added a command to encode a password

This commit is contained in:
Saro0h 2014-12-03 01:06:56 +01:00 committed by sarah khalil
parent 59ca5b3af9
commit a7bd0fc98d
8 changed files with 417 additions and 2 deletions

View File

@ -0,0 +1,225 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\SecurityBundle\Command;
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\Question;
use Symfony\Component\Console\Helper\Table;
/**
* Encode a user's password.
*
* @author Sarah Khalil <mkhalil.sarah@gmail.com>
*/
class UserPasswordEncoderCommand extends ContainerAwareCommand
{
/**
* {@inheritdoc}
*/
protected function configure()
{
$this
->setName('security:encode-password')
->setDescription('Encode a password.')
->addArgument('password', InputArgument::OPTIONAL, 'Enter a password')
->addArgument('user-class', InputArgument::OPTIONAL, 'Enter the user class configured to find the encoder you need.')
->addArgument('salt', InputArgument::OPTIONAL, 'Enter the salt you want to use to encode your password.')
->setHelp(<<<EOF
The <info>%command.name%</info> command allows to encode a password using encoders
that are configured in the application configuration file, under the <comment>security.encoders</comment>.
For instance, if you have the following configuration for your application:
<comment>
security:
encoders:
Symfony\Component\Security\Core\User\User: plaintext
AppBundle\Model\User: bcrypt
</comment>
According to the response you will give to the question "<question>Provide your configured user class</question>" your
password will be encoded the way it was configured.
- If you answer "<comment>Symfony\Component\Security\Core\User\User</comment>", the password provided will be encoded
with the <comment>plaintext</comment> encoder.
- If you answer <comment>AppBundle\Model\User</comment>, the password provided will be encoded
with the <comment>bcrypt</comment> encoder.
The command allows you to provide your own <comment>salt</comment>. If you don't provide any,
the command will take care about that for you.
You can also use the non interactive way by typing the following command:
<info>php %command.full_name% [password] [salt] [user-class]</info>
EOF
)
;
}
/**
* {@inheritdoc}
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$this->writeIntroduction($output);
$password = $input->getArgument('password');
$salt = $input->getArgument('salt');
$userClass = $input->getArgument('user-class');
$helper = $this->getHelper('question');
if (!$password) {
$passwordQuestion = $this->createPasswordQuestion($input, $output);
$password = $helper->ask($input, $output, $passwordQuestion);
}
if (!$salt) {
$saltQuestion = $this->createSaltQuestion($input, $output);
$salt = $helper->ask($input, $output, $saltQuestion);
}
$output->writeln("\n <comment>Encoders are configured by user type in the security.yml file.</comment>");
if (!$userClass) {
$userClassQuestion = $this->createUserClassQuestion($input, $output);
$userClass = $helper->ask($input, $output, $userClassQuestion);
}
$encoder = $this->getContainer()->get('security.encoder_factory')->getEncoder($userClass);
$encodedPassword = $encoder->encodePassword($password, $salt);
$this->writeResult($output);
$table = new Table($output);
$table
->setHeaders(array('Key', 'Value'))
->addRow(array('Encoder used', get_class($encoder)))
->addRow(array('Encoded password', $encodedPassword))
;
$table->render();
}
/**
* Create the password question to ask the user for the password to be encoded.
*
* @param InputInterface $input
* @param OutputInterface $output
*
* @return Question
*/
private function createPasswordQuestion(InputInterface $input, OutputInterface $output)
{
$passwordQuestion = new Question("\n > <question>Type in your password to be encoded:</question> ");
$passwordQuestion->setValidator(function ($value) {
if ('' === trim($value)) {
throw new \Exception('The password must not be empty.');
}
return $value;
});
$passwordQuestion->setHidden(true);
$passwordQuestion->setMaxAttempts(20);
return $passwordQuestion;
}
/**
* Create the question that asks for the salt to perform the encoding.
* If there is no provided salt, a random one is automatically generated.
*
* @param InputInterface $input
* @param OutputInterface $output
*
* @return Question
*/
private function createSaltQuestion(InputInterface $input, OutputInterface $output)
{
$saltQuestion = new Question("\n > (Optional) <question>Provide a salt (press <enter> to generate one):</question> ");
$container = $this->getContainer();
$saltQuestion->setValidator(function ($value) use ($output, $container) {
if ('' === trim($value)) {
$value = hash('sha512', $container->get('security.secure_random')->nextBytes(30));
$output->writeln("\n<comment>The salt has been generated: </comment>".$value);
$output->writeln(sprintf("<comment>Make sure that your salt storage field fits this salt length: %s chars.</comment>\n", strlen($value)));
}
return $value;
});
return $saltQuestion;
}
/**
* Create the question that asks for the configured user class.
*
* @param InputInterface $input
* @param OutputInterface $output
*
* @return Question
*/
private function createUserClassQuestion(InputInterface $input, OutputInterface $output)
{
$userClassQuestion = new Question(" > <question>Provide your configured user class:</question> ");
$userClassQuestion->setAutocompleterValues(array('Symfony\Component\Security\Core\User\User'));
$userClassQuestion->setValidator(function ($value) use ($output) {
if ('' === trim($value)) {
$value = 'Symfony\Component\Security\Core\User\User';
$output->writeln("<info>You did not provide any user class.</info> <comment>The user class used is: Symfony\Component\Security\Core\User\User</comment> \n");
}
return $value;
});
return $userClassQuestion;
}
private function writeIntroduction(OutputInterface $output)
{
$output->writeln(array(
'',
$this->getHelperSet()->get('formatter')->formatBlock(
'Symfony Password Encoder Utility',
'bg=blue;fg=white',
true
),
'',
));
$output->writeln(array(
'',
'This command encodes any password you want according to the configuration you',
'made in your configuration file containing the <comment>security.encoders</comment> key.',
'',
));
}
private function writeResult(OutputInterface $output)
{
$output->writeln(array(
'',
$this->getHelperSet()->get('formatter')->formatBlock(
'✔ Password encoding succeeded',
'bg=green;fg=white',
true
),
'',
));
}
}

View File

@ -0,0 +1,96 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\SecurityBundle\Tests\Functional;
use Symfony\Bundle\FrameworkBundle\Console\Application;
use Symfony\Bundle\SecurityBundle\Command\UserPasswordEncoderCommand;
use Symfony\Component\Console\Tester\CommandTester;
/**
* Tests UserPasswordEncoderCommand
*
* @author Sarah Khalil <mkhalil.sarah@gmail.com>
*/
class UserPasswordEncoderCommandTest extends WebTestCase
{
private $passwordEncoderCommandTester;
public function testEncodePasswordPasswordPlainText()
{
$this->passwordEncoderCommandTester->execute(array(
'command' => 'security:encode-password',
'password' => 'password',
'user-class' => 'Symfony\Component\Security\Core\User\User',
'salt' => 'AZERTYUIOPOfghjklytrertyuiolnbcxdfghjkytrfghjk',
));
$expected = file_get_contents(__DIR__.'/app/PasswordEncode/plaintext.txt');
$this->assertEquals($expected, $this->passwordEncoderCommandTester->getDisplay());
}
public function testEncodePasswordBcrypt()
{
$this->passwordEncoderCommandTester->execute(array(
'command' => 'security:encode-password',
'password' => 'password',
'user-class' => 'Custom\Class\Bcrypt\User',
'salt' => 'AZERTYUIOPOfghjklytrertyuiolnbcxdfghjkytrfghjk',
));
$expected = file_get_contents(__DIR__.'/app/PasswordEncode/bcrypt.txt');
$this->assertEquals($expected, $this->passwordEncoderCommandTester->getDisplay());
}
public function testEncodePasswordPbkdf2()
{
$this->passwordEncoderCommandTester->execute(array(
'command' => 'security:encode-password',
'password' => 'password',
'user-class' => 'Custom\Class\Pbkdf2\User',
'salt' => 'AZERTYUIOPOfghjklytrertyuiolnbcxdfghjkytrfghjk',
));
$expected = file_get_contents(__DIR__.'/app/PasswordEncode/pbkdf2.txt');
$this->assertEquals($expected, $this->passwordEncoderCommandTester->getDisplay());
}
public function testEncodePasswordNoConfigForGivenUserClass()
{
$this->setExpectedException('\RuntimeException', 'No encoder has been configured for account "Wrong/User/Class".');
$this->passwordEncoderCommandTester->execute(array(
'command' => 'security:encode-password',
'password' => 'password',
'user-class' => 'Wrong/User/Class',
'salt' => 'AZERTYUIOPOfghjklytrertyuiolnbcxdfghjkytrfghjk',
));
}
protected function setUp()
{
$kernel = $this->createKernel(array('test_case' => 'PasswordEncode'));
$kernel->boot();
$application = new Application($kernel);
$application->add(new UserPasswordEncoderCommand());
$passwordEncoderCommand = $application->find('security:encode-password');
$this->passwordEncoderCommandTester = new CommandTester($passwordEncoderCommand);
}
protected function tearDown()
{
$this->passwordEncoderCommandTester = null;
}
}

View File

@ -0,0 +1,22 @@
Symfony Password Encoder Utility
This command encodes any password you want according to the configuration you
made in your configuration file containing the security.encoders key.
Encoders are configured by user type in the security.yml file.
✔ Password encoding succeeded
+------------------+---------------------------------------------------------------+
| Key | Value |
+------------------+---------------------------------------------------------------+
| Encoder used | Symfony\Component\Security\Core\Encoder\BCryptPasswordEncoder |
| Encoded password | $2y$13$AZERTYUIOPOfghjklytreeBTRM4Wd.D3IW7dtnQ6xGA7z3fY8zg4. |
+------------------+---------------------------------------------------------------+

View File

@ -0,0 +1,6 @@
<?php
return array(
new Symfony\Bundle\SecurityBundle\SecurityBundle(),
new Symfony\Bundle\FrameworkBundle\FrameworkBundle(),
);

View File

@ -0,0 +1,21 @@
imports:
- { resource: ./../config/framework.yml }
security:
encoders:
Symfony\Component\Security\Core\User\User: plaintext
Custom\Class\Bcrypt\User: bcrypt
Custom\Class\Pbkdf2\User: pbkdf2
Custom\Class\Test\User: test
providers:
in_memory:
memory:
users:
user: { password: userpass, roles: [ 'ROLE_USER' ] }
admin: { password: adminpass, roles: [ 'ROLE_ADMIN' ] }
firewalls:
test:
pattern: ^/
security: false

View File

@ -0,0 +1,22 @@
Symfony Password Encoder Utility
This command encodes any password you want according to the configuration you
made in your configuration file containing the security.encoders key.
Encoders are configured by user type in the security.yml file.
✔ Password encoding succeeded
+------------------+---------------------------------------------------------------+
| Key | Value |
+------------------+---------------------------------------------------------------+
| Encoder used | Symfony\Component\Security\Core\Encoder\Pbkdf2PasswordEncoder |
| Encoded password | nvGk/kUwqj6PHzmqUqXxJA6GEhxD1TSJziV8P4ThqsEi4ZHF6yHp6g== |
+------------------+---------------------------------------------------------------+

View File

@ -0,0 +1,22 @@
Symfony Password Encoder Utility
This command encodes any password you want according to the configuration you
made in your configuration file containing the security.encoders key.
Encoders are configured by user type in the security.yml file.
✔ Password encoding succeeded
+------------------+------------------------------------------------------------------+
| Key | Value |
+------------------+------------------------------------------------------------------+
| Encoder used | Symfony\Component\Security\Core\Encoder\PlaintextPasswordEncoder |
| Encoded password | password{AZERTYUIOPOfghjklytrertyuiolnbcxdfghjkytrfghjk} |
+------------------+------------------------------------------------------------------+

View File

@ -23,7 +23,7 @@
"require-dev": {
"symfony/phpunit-bridge": "~2.7|~3.0.0",
"symfony/browser-kit": "~2.4|~3.0.0",
"symfony/console": "~2.3|~3.0.0",
"symfony/console": "~2.5|~3.0.0",
"symfony/css-selector": "~2.0,>=2.0.5|~3.0.0",
"symfony/dependency-injection": "~2.3|~3.0.0",
"symfony/dom-crawler": "~2.0,>=2.0.5|~3.0.0",
@ -37,7 +37,8 @@
"symfony/yaml": "~2.0,>=2.0.5|~3.0.0",
"symfony/expression-language": "~2.6|~3.0.0",
"doctrine/doctrine-bundle": "~1.2",
"twig/twig": "~1.12"
"twig/twig": "~1.12",
"ircmaxell/password-compat": "~1.0"
},
"autoload": {
"psr-0": { "Symfony\\Bundle\\SecurityBundle\\": "" }