simplified the Prng code
This commit is contained in:
parent
e5dc7afe90
commit
c0c89724b0
@ -1,68 +0,0 @@
|
||||
<?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\Bridge\Doctrine\Security;
|
||||
|
||||
use Doctrine\DBAL\Types\Type;
|
||||
use Doctrine\DBAL\Connection;
|
||||
use Symfony\Component\Security\Core\Util\SeedProviderInterface;
|
||||
|
||||
/**
|
||||
* Doctrine Seed Provider.
|
||||
*
|
||||
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class DoctrineSeedProvider implements SeedProviderInterface
|
||||
{
|
||||
private $con;
|
||||
private $seedTableName;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param Connection $con
|
||||
* @param string $tableName
|
||||
*/
|
||||
public function __construct(Connection $con, $tableName)
|
||||
{
|
||||
$this->con = $con;
|
||||
$this->seedTableName = $tableName;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function loadSeed()
|
||||
{
|
||||
$stmt = $this->con->executeQuery("SELECT seed, updated_at FROM {$this->seedTableName}");
|
||||
|
||||
if (false === $seed = $stmt->fetchColumn(0)) {
|
||||
throw new \RuntimeException('You need to initialize the generator by running the console command "init:prng".');
|
||||
}
|
||||
|
||||
$seedLastUpdatedAt = new \DateTime($stmt->fetchColumn(1));
|
||||
|
||||
return array($seed, $seedLastUpdatedAt);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function updateSeed($seed)
|
||||
{
|
||||
$params = array(':seed' => $seed, ':updatedAt' => new \DateTime());
|
||||
$types = array(':updatedAt' => Type::DATETIME);
|
||||
if (!$this->con->executeUpdate("UPDATE {$this->seedTableName} SET seed = :seed, updated_at = :updatedAt", $params, $types)) {
|
||||
$this->con->executeUpdate("INSERT INTO {$this->seedTableName} VALUES (:seed, :updatedAt)", $params, $types);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
<?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\Bridge\Doctrine\Security\EventListener;
|
||||
|
||||
use Symfony\Bridge\Doctrine\Security\PrngSchema;
|
||||
use Doctrine\ORM\Tools\Event\GenerateSchemaEventArgs;
|
||||
|
||||
/**
|
||||
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
|
||||
*/
|
||||
class PrngSchemaListener
|
||||
{
|
||||
private $schema;
|
||||
|
||||
public function __construct(PrngSchema $schema)
|
||||
{
|
||||
$this->schema = $schema;
|
||||
}
|
||||
|
||||
public function postGenerateSchema(GenerateSchemaEventArgs $args)
|
||||
{
|
||||
$this->schema->addToSchema($args->getSchema());
|
||||
}
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
<?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\Bridge\Doctrine\Security;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
|
||||
/**
|
||||
* The DBAL schema that will be used if you choose the database-based seed provider.
|
||||
*
|
||||
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
|
||||
*/
|
||||
final class PrngSchema extends Schema
|
||||
{
|
||||
public function __construct($tableName)
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$table = $this->createTable($tableName);
|
||||
$table->addColumn('seed', 'string', array(
|
||||
'length' => 88,
|
||||
'not_null' => true,
|
||||
));
|
||||
$table->addColumn('updated_at', 'datetime', array(
|
||||
'not_null' => true,
|
||||
));
|
||||
}
|
||||
|
||||
public function addToSchema(Schema $schema)
|
||||
{
|
||||
foreach ($this->getTables() as $table) {
|
||||
$schema->_addTable($table);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,48 +0,0 @@
|
||||
<?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\Bridge\Doctrine\Tests\Security;
|
||||
|
||||
use Symfony\Bridge\Doctrine\Security\DoctrineSeedProvider;
|
||||
use Symfony\Bridge\Doctrine\Security\PrngSchema;
|
||||
use Symfony\Component\Security\Core\Util\Prng;
|
||||
use Symfony\Component\Security\Tests\Core\Util\PrngTest;
|
||||
use Doctrine\DBAL\DriverManager;
|
||||
use Doctrine\DBAL\Connection;
|
||||
|
||||
class DoctrineSeedProviderTest extends PrngTest
|
||||
{
|
||||
public function getPrngs()
|
||||
{
|
||||
$con = DriverManager::getConnection(array(
|
||||
'driver' => 'pdo_sqlite',
|
||||
'memory' => true
|
||||
));
|
||||
|
||||
$schema = new PrngSchema('seed_table');
|
||||
foreach ($schema->toSql($con->getDatabasePlatform()) as $sql) {
|
||||
$con->executeQuery($sql);
|
||||
}
|
||||
$con->executeQuery("INSERT INTO seed_table VALUES (:seed, :updatedAt)", array(
|
||||
':seed' => base64_encode(hash('sha512', uniqid(mt_rand(), true), true)),
|
||||
':updatedAt' => date('Y-m-d H:i:s'),
|
||||
));
|
||||
|
||||
// no-openssl with database seed provider
|
||||
$prng = new Prng(new DoctrineSeedProvider($con, 'seed_table'));
|
||||
$this->disableOpenSsl($prng);
|
||||
|
||||
$prngs = parent::getPrngs();
|
||||
$prngs[] = array($prng);
|
||||
|
||||
return $prngs;
|
||||
}
|
||||
}
|
@ -1,57 +0,0 @@
|
||||
<?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\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
|
||||
/**
|
||||
* Initializes a custom PRNG seed provider.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class InitPrngCommand extends ContainerAwareCommand
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
->setName('init:prng');
|
||||
->addArgument('phrase', InputArgument::REQUIRED, 'A random string');
|
||||
->setDescription('Initialize a custom PRNG seed provider')
|
||||
->setHelp(<<<EOF
|
||||
The <info>%command.name%</info> command initializes a custom PRNG seed provider:
|
||||
|
||||
<info>php %command.full_name% ABCDE...</info>
|
||||
|
||||
The argument should be a random string, whatever comes to your mind right now.
|
||||
You do not need to remember it, it does not need to be cryptic, or long, and it
|
||||
will not be stored in a decipherable way. One restriction however, you should
|
||||
not let this be generated in an automated fashion.
|
||||
EOF
|
||||
)
|
||||
;
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
if (!$this->getContainer()->has('security.prng_seed_provider')) {
|
||||
throw new \RuntimeException('No seed provider has been configured under path "secure.prng".');
|
||||
}
|
||||
|
||||
$this->getContainer()->get('security.prng_seed_provider')->updateSeed(base64_encode(hash('sha512', $input->getArgument('phrase'), true)));
|
||||
|
||||
$output->writeln('The CSPRNG has been initialized successfully.');
|
||||
}
|
||||
}
|
@ -87,7 +87,6 @@ class MainConfiguration implements ConfigurationInterface
|
||||
{
|
||||
$rootNode
|
||||
->children()
|
||||
->scalarNode('prng_seed_provider')->defaultNull()->end()
|
||||
->arrayNode('acl')
|
||||
->children()
|
||||
->scalarNode('connection')
|
||||
|
@ -88,10 +88,6 @@ class SecurityExtension extends Extension
|
||||
$this->aclLoad($config['acl'], $container);
|
||||
}
|
||||
|
||||
if (null !== $config['prng_seed_provider']) {
|
||||
$container->setAlias('security.prng_seed_provider', $config['prng_seed_provider']);
|
||||
}
|
||||
|
||||
// add some required classes for compilation
|
||||
$this->addClassesToCompile(array(
|
||||
'Symfony\\Component\\Security\\Http\\Firewall',
|
||||
|
@ -142,7 +142,7 @@
|
||||
<!-- Pseudorandom Number Generator -->
|
||||
<service id="security.prng" class="Symfony\Component\Security\Core\Util\Prng">
|
||||
<tag name="monolog.logger" channel="security" />
|
||||
<argument type="service" id="security.prng_seed_provider" on-invalid="ignore" />
|
||||
<argument>%kernel.cache_dir%/prng.seed</argument>
|
||||
<argument type="service" id="logger" on-invalid="ignore" />
|
||||
</service>
|
||||
</services>
|
||||
|
@ -2,7 +2,6 @@
|
||||
|
||||
$container->loadFromExtension('security', array(
|
||||
'acl' => array(),
|
||||
'prng_seed_provider' => 'custom_seed_provider',
|
||||
'encoders' => array(
|
||||
'JMS\FooBundle\Entity\User1' => 'plaintext',
|
||||
'JMS\FooBundle\Entity\User2' => array(
|
||||
|
@ -5,7 +5,7 @@
|
||||
xmlns:srv="http://symfony.com/schema/dic/services"
|
||||
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
|
||||
|
||||
<config prng-seed-provider="custom_seed_provider">
|
||||
<config>
|
||||
<acl />
|
||||
|
||||
<encoder class="JMS\FooBundle\Entity\User1" algorithm="plaintext" />
|
||||
|
@ -1,6 +1,5 @@
|
||||
security:
|
||||
acl: ~
|
||||
prng_seed_provider: custom_seed_provider
|
||||
encoders:
|
||||
JMS\FooBundle\Entity\User1: plaintext
|
||||
JMS\FooBundle\Entity\User2:
|
||||
|
@ -168,13 +168,6 @@ abstract class SecurityExtensionTest extends \PHPUnit_Framework_TestCase
|
||||
$this->assertEquals('foo', (string) $container->getAlias('security.acl.provider'));
|
||||
}
|
||||
|
||||
public function testSeedProvider()
|
||||
{
|
||||
$container = $this->getContainer('container1');
|
||||
|
||||
$this->assertEquals('custom_seed_provider', (string) $container->getAlias('security.prng_seed_provider'));
|
||||
}
|
||||
|
||||
protected function getContainer($file)
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
|
@ -16,6 +16,7 @@ use Symfony\Component\HttpKernel\Log\LoggerInterface;
|
||||
/**
|
||||
* A secure random number generator implementation.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
|
||||
*/
|
||||
final class Prng
|
||||
@ -25,7 +26,7 @@ final class Prng
|
||||
private $seed;
|
||||
private $seedUpdated;
|
||||
private $seedLastUpdatedAt;
|
||||
private $seedProvider;
|
||||
private $seedFile;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
@ -33,12 +34,12 @@ final class Prng
|
||||
* Be aware that a guessable seed will severely compromise the PRNG
|
||||
* algorithm that is employed.
|
||||
*
|
||||
* @param SeedProviderInterface $provider
|
||||
* @param LoggerInterface $logger
|
||||
* @param string $seedFile
|
||||
* @param LoggerInterface $logger
|
||||
*/
|
||||
public function __construct(SeedProviderInterface $provider = null, LoggerInterface $logger = null)
|
||||
public function __construct($seedFile = null, LoggerInterface $logger = null)
|
||||
{
|
||||
$this->seedProvider = $provider;
|
||||
$this->seedFile = $seedFile;
|
||||
$this->logger = $logger;
|
||||
|
||||
// determine whether to use OpenSSL
|
||||
@ -77,11 +78,16 @@ final class Prng
|
||||
|
||||
// initialize seed
|
||||
if (null === $this->seed) {
|
||||
if (null === $this->seedProvider) {
|
||||
throw new \RuntimeException('You need to specify a custom seed provider.');
|
||||
if (null === $this->seedFile) {
|
||||
throw new \RuntimeException('You need to specify a file path to store the seed.');
|
||||
}
|
||||
|
||||
list($this->seed, $this->seedLastUpdatedAt) = $this->seedProvider->loadSeed();
|
||||
if (is_file($this->seedFile)) {
|
||||
list($this->seed, $this->seedLastUpdatedAt) = $this->readSeed();
|
||||
} else {
|
||||
$this->seed = uniqid(mt_rand(), true);
|
||||
$this->updateSeed();
|
||||
}
|
||||
}
|
||||
|
||||
$bytes = '';
|
||||
@ -89,16 +95,23 @@ final class Prng
|
||||
static $incr = 1;
|
||||
$bytes .= hash('sha512', $incr++.$this->seed.uniqid(mt_rand(), true).$nbBytes, true);
|
||||
$this->seed = base64_encode(hash('sha512', $this->seed.$bytes.$nbBytes, true));
|
||||
|
||||
if (!$this->seedUpdated && $this->seedLastUpdatedAt->getTimestamp() < time() - mt_rand(1, 10)) {
|
||||
if (null !== $this->seedProvider) {
|
||||
$this->seedProvider->updateSeed($this->seed);
|
||||
}
|
||||
|
||||
$this->seedUpdated = true;
|
||||
}
|
||||
$this->updateSeed();
|
||||
}
|
||||
|
||||
return substr($bytes, 0, $nbBytes);
|
||||
}
|
||||
|
||||
private function readSeed()
|
||||
{
|
||||
return json_decode(file_get_contents($this->seedFile));
|
||||
}
|
||||
|
||||
private function updateSeed()
|
||||
{
|
||||
if (!$this->seedUpdated && $this->seedLastUpdatedAt < time() - mt_rand(1, 10)) {
|
||||
file_put_contents($this->seedFile, json_encode(array($this->seed, microtime(true))));
|
||||
}
|
||||
|
||||
$this->seedUpdated = true;
|
||||
}
|
||||
}
|
||||
|
@ -1,37 +0,0 @@
|
||||
<?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\Component\Security\Core\Util;
|
||||
|
||||
/**
|
||||
* Seed Provider Interface.
|
||||
*
|
||||
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
|
||||
*/
|
||||
interface SeedProviderInterface
|
||||
{
|
||||
/**
|
||||
* Loads the initial seed.
|
||||
*
|
||||
* Whatever is returned from this method, it should not be guessable.
|
||||
*
|
||||
* @return array of the format array(string, DateTime) where string is the
|
||||
* initial seed, and DateTime is the last time it was updated
|
||||
*/
|
||||
function loadSeed();
|
||||
|
||||
/**
|
||||
* Updates the seed.
|
||||
*
|
||||
* @param string $seed
|
||||
*/
|
||||
function updateSeed($seed);
|
||||
}
|
@ -147,11 +147,11 @@ class PrngTest extends \PHPUnit_Framework_TestCase
|
||||
$prngs = array();
|
||||
|
||||
// openssl with fallback
|
||||
$prng = new Prng(new NullSeedProvider());
|
||||
$prng = new Prng();
|
||||
$prngs[] = array($prng);
|
||||
|
||||
// no-openssl with custom seed provider
|
||||
$prng = new Prng(new NullSeedProvider());
|
||||
$prng = new Prng(sys_get_temp_dir().'/_sf2.seed');
|
||||
$this->disableOpenSsl($prng);
|
||||
$prngs[] = array($prng);
|
||||
|
||||
|
Reference in New Issue
Block a user