Merge pull request #1760 from bburnichon/improvement/some-scrutinizers-fixups

Improvement/some scrutinizers fixups
This commit is contained in:
Benoît Burnichon
2016-03-15 13:52:46 +01:00
15 changed files with 277 additions and 264 deletions

View File

@@ -14,7 +14,6 @@ use Alchemy\Phrasea\Model\Entities\User;
trait AclAware trait AclAware
{ {
/** @var ACLProvider */
private $aclProvider; private $aclProvider;
/** /**
@@ -45,17 +44,20 @@ trait AclAware
return $this->aclProvider; return $this->aclProvider;
} }
if (null === $this->aclProvider && $this instanceof \Pimple && $this->offsetExists('acl')) { $locator = $this->aclProvider;
$this->aclProvider = function () {
if (null === $locator && $this instanceof \Pimple && $this->offsetExists('acl')) {
$locator = function () {
return $this['acl']; return $this['acl'];
}; };
} }
if (null === $this->aclProvider) { if (null === $locator) {
throw new \LogicException(ACLProvider::class . ' instance or locator was not set'); throw new \LogicException(ACLProvider::class . ' instance or locator was not set');
} }
$instance = call_user_func($this->aclProvider); $instance = $locator();
if (!$instance instanceof ACLProvider) { if (!$instance instanceof ACLProvider) {
throw new \LogicException(sprintf( throw new \LogicException(sprintf(
'Expects locator to return instance of "%s", got "%s"', 'Expects locator to return instance of "%s", got "%s"',

View File

@@ -13,24 +13,35 @@ namespace Alchemy\Phrasea\Authentication\Phrasea;
use Alchemy\Phrasea\Exception\InvalidArgumentException; use Alchemy\Phrasea\Exception\InvalidArgumentException;
use Alchemy\Phrasea\Authentication\Exception\RequireCaptchaException; use Alchemy\Phrasea\Authentication\Exception\RequireCaptchaException;
use Alchemy\Phrasea\Model\Repositories\AuthFailureRepository;
use Doctrine\ORM\EntityManager; use Doctrine\ORM\EntityManager;
use Alchemy\Phrasea\Model\Entities\AuthFailure; use Alchemy\Phrasea\Model\Entities\AuthFailure;
use Doctrine\ORM\EntityRepository;
use Neutron\ReCaptcha\ReCaptcha; use Neutron\ReCaptcha\ReCaptcha;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
class FailureManager class FailureManager
{ {
/** @var ReCaptcha */ /**
* @var ReCaptcha
*/
private $captcha; private $captcha;
/** @var EntityManager */
/**
* @var EntityManager
*/
private $em; private $em;
/** @var EntityRepository */
/**
* @var AuthFailureRepository
*/
private $repository; private $repository;
/** @var integer */
/**
* @var int
*/
private $trials; private $trials;
public function __construct(EntityRepository $repo, EntityManager $em, ReCaptcha $captcha, $trials) public function __construct(AuthFailureRepository $repo, EntityManager $em, ReCaptcha $captcha, $trials)
{ {
$this->captcha = $captcha; $this->captcha = $captcha;
$this->em = $em; $this->em = $em;
@@ -40,9 +51,12 @@ class FailureManager
throw new InvalidArgumentException('Trials number must be a positive integer'); throw new InvalidArgumentException('Trials number must be a positive integer');
} }
$this->trials = $trials; $this->trials = (int)$trials;
} }
/**
* @return int
*/
public function getTrials() public function getTrials()
{ {
return $this->trials; return $this->trials;
@@ -86,36 +100,38 @@ class FailureManager
$failures = $this->repository->findLockedFailuresMatching($username, $request->getClientIp()); $failures = $this->repository->findLockedFailuresMatching($username, $request->getClientIp());
if (0 === count($failures)) { if (0 === count($failures)) {
return; return $this;
} }
if ($this->trials < count($failures) && $this->captcha->isSetup()) { if ($this->trials < count($failures) && $this->captcha->isSetup()) {
$response = $this->captcha->bind($request); $response = $this->captcha->bind($request);
if ($response->isValid()) { if (!$response->isValid()) {
foreach ($failures as $failure) { throw new RequireCaptchaException('Too many failures, require captcha');
$failure->setLocked(false);
}
$this->em->flush();
} else {
throw new RequireCaptchaException('Too much failure, require captcha');
} }
foreach ($failures as $failure) {
$failure->setLocked(false);
}
$this->em->flush($failures);
} }
return $this; return $this;
} }
/**
* Removes failures older than 2 monthes
*/
private function removeOldFailures() private function removeOldFailures()
{ {
$failures = $this->repository->findOldFailures('-2 months'); $failures = $this->repository->findOldFailures('-2 months');
if (0 < count($failures)) { if (empty($failures)) {
foreach ($failures as $failure) { return;
$this->em->remove($failure);
}
} }
foreach ($failures as $failure) {
$this->em->remove($failure);
}
$this->em->flush($failures);
} }
} }

View File

@@ -15,13 +15,13 @@ use Alchemy\Phrasea\Authentication\Exception\NotAuthenticatedException;
use Alchemy\Phrasea\Authentication\Provider\Token\Token; use Alchemy\Phrasea\Authentication\Provider\Token\Token;
use Alchemy\Phrasea\Authentication\Provider\Token\Identity; use Alchemy\Phrasea\Authentication\Provider\Token\Identity;
use Alchemy\Phrasea\Model\Entities\User; use Alchemy\Phrasea\Model\Entities\User;
use Doctrine\Common\Persistence\ObjectRepository; use Alchemy\Phrasea\Model\Repositories\UserRepository;
class SuggestionFinder class SuggestionFinder
{ {
private $repository; private $repository;
public function __construct(ObjectRepository $repository) public function __construct(UserRepository $repository)
{ {
$this->repository = $repository; $this->repository = $repository;
} }

View File

@@ -33,19 +33,16 @@ class Factory
*/ */
public static function getFileAttribute(Application $app, $name, $serialized) public static function getFileAttribute(Application $app, $name, $serialized)
{ {
switch ($name) { switch ($name)
{
case AttributeInterface::NAME_METADATA: case AttributeInterface::NAME_METADATA:
return Metadata::loadFromString($app, $serialized); return Metadata::loadFromString($app, $serialized);
break;
case AttributeInterface::NAME_STORY: case AttributeInterface::NAME_STORY:
return Story::loadFromString($app, $serialized); return Story::loadFromString($app, $serialized);
break;
case AttributeInterface::NAME_METAFIELD: case AttributeInterface::NAME_METAFIELD:
return MetaField::loadFromString($app, $serialized); return MetaField::loadFromString($app, $serialized);
break;
case AttributeInterface::NAME_STATUS: case AttributeInterface::NAME_STATUS:
return Status::loadFromString($app, $serialized); return Status::loadFromString($app, $serialized);
break;
} }
throw new \InvalidArgumentException(sprintf('Unknown attribute %s', $name)); throw new \InvalidArgumentException(sprintf('Unknown attribute %s', $name));

View File

@@ -12,7 +12,6 @@
namespace Alchemy\Phrasea\Border\Attribute; namespace Alchemy\Phrasea\Border\Attribute;
use Alchemy\Phrasea\Application; use Alchemy\Phrasea\Application;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/** /**
* Phraseanet Border MetaField Attribute * Phraseanet Border MetaField Attribute
@@ -23,13 +22,11 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
class MetaField implements AttributeInterface class MetaField implements AttributeInterface
{ {
/** /**
*
* @var \databox_field * @var \databox_field
*/ */
protected $databox_field; protected $databox_field;
/** /**
*
* @var array * @var array
*/ */
protected $value; protected $value;
@@ -46,14 +43,6 @@ class MetaField implements AttributeInterface
$this->value = $value; $this->value = $value;
} }
/**
* Destructor
*/
public function __destruct()
{
$this->metadata = $this->databox_field = null;
}
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
@@ -101,15 +90,18 @@ class MetaField implements AttributeInterface
*/ */
public static function loadFromString(Application $app, $string) public static function loadFromString(Application $app, $string)
{ {
if (!$datas = @unserialize($string)) { $data = @unserialize($string);
if (!is_array($data) || !isset($data['sbas_id']) || !isset($data['id']) || !isset($data['value'])) {
throw new \InvalidArgumentException('Unable to load metadata from string'); throw new \InvalidArgumentException('Unable to load metadata from string');
} }
try { try {
return new static($app->findDataboxById($datas['sbas_id']) $field = $app->findDataboxById($data['sbas_id'])->get_meta_structure()->get_element($data['id']);
->get_meta_structure()->get_element($datas['id']), $datas['value']);
} catch (NotFoundHttpException $e) { return new static($field, $data['value']);
throw new \InvalidArgumentException('Field does not exist anymore'); } catch (\Exception $exception) {
throw new \InvalidArgumentException('Field does not exist anymore', 0, $exception);
} }
} }
} }

View File

@@ -129,8 +129,6 @@ class Manager
$callable($element, $visa, $code); $callable($element, $visa, $code);
} }
$visa = null;
return $code; return $code;
} }

View File

@@ -45,8 +45,6 @@ class CLI extends Application
{ {
parent::__construct($environment); parent::__construct($environment);
$app = $this;
$this['session.test'] = true; $this['session.test'] = true;
$this['console'] = $this->share(function () use ($name, $version) { $this['console'] = $this->share(function () use ($name, $version) {

View File

@@ -92,7 +92,7 @@ EOT;
} }
/** /**
* @param $collectionId * @param int $collectionId
*/ */
public function setCollectionId($collectionId) public function setCollectionId($collectionId)
{ {
@@ -126,7 +126,7 @@ EOT;
} }
/** /**
* @return \string[] * @return string[]
*/ */
public function getLabels() public function getLabels()
{ {
@@ -134,7 +134,7 @@ EOT;
} }
/** /**
* @param \string[] $labels * @param string[] $labels
*/ */
public function setLabels($labels) public function setLabels($labels)
{ {
@@ -142,7 +142,7 @@ EOT;
} }
/** /**
* @param $lang * @param string $lang
* @param bool $substitute * @param bool $substitute
* @return string * @return string
*/ */
@@ -154,14 +154,14 @@ EOT;
if ($substitute) { if ($substitute) {
return isset($this->labels[$lang]) ? $this->labels[$lang] : $this->name; return isset($this->labels[$lang]) ? $this->labels[$lang] : $this->name;
} else {
return $this->labels[$lang];
} }
return $this->labels[$lang];
} }
/** /**
* @param $lang * @param string $lang
* @param $label * @param string $label
*/ */
public function setLabel($lang, $label) public function setLabel($lang, $label)
{ {
@@ -173,7 +173,7 @@ EOT;
} }
/** /**
* @return \int[]|string|null * @return int[]|string|null
*/ */
public function getLogo() public function getLogo()
{ {
@@ -181,7 +181,7 @@ EOT;
} }
/** /**
* @param \int[]|string $logo * @param int[]|string $logo
*/ */
public function setLogo($logo) public function setLogo($logo)
{ {

View File

@@ -13,7 +13,7 @@ class ArrayCacheCollectionRepository implements CollectionRepository
private $collectionRepository; private $collectionRepository;
/** /**
* @var \collection[] * @var \collection[]|null
*/ */
private $collectionCache = null; private $collectionCache = null;

View File

@@ -18,16 +18,13 @@ use Symfony\Component\Console\Output\OutputInterface;
class BuildMissingSubdefs extends Command class BuildMissingSubdefs extends Command
{ {
/** private $generator;
* Constructor
*/
public function __construct($name = null) public function __construct($name = null)
{ {
parent::__construct($name); parent::__construct($name);
$this->setDescription('Builds subviews that previously failed to be generated / did not exist when records were added'); $this->setDescription('Builds subviews that previously failed to be generated / did not exist when records were added');
return $this;
} }
/** /**
@@ -37,48 +34,61 @@ class BuildMissingSubdefs extends Command
{ {
$start = microtime(true); $start = microtime(true);
$progressBar = new ProgressBar($output); $progressBar = new ProgressBar($output);
$n = 0; $generatedSubdefs = 0;
/** @var SubdefGenerator $subdefGenerator */
$subdefGenerator = $this->container['subdef.generator'];
foreach ($this->container->getDataboxes() as $databox) { foreach ($this->container->getDataboxes() as $databox) {
$sql = 'SELECT record_id FROM record WHERE parent_record_id = 0'; $sql = 'SELECT record_id FROM record WHERE parent_record_id = 0';
$stmt = $databox->get_connection()->prepare($sql); $result = $databox->get_connection()->executeQuery($sql)->fetchAll(\PDO::FETCH_ASSOC);
$stmt->execute(); $progressBar->start(count($result));
$rs = $stmt->fetchAll(\PDO::FETCH_ASSOC);
$stmt->closeCursor();
$progressBar->start(count($rs));
foreach ($rs as $row) { foreach ($result as $row) {
$record = $databox->get_record($row['record_id']); $record = $databox->get_record($row['record_id']);
$wanted_subdefs = $record->get_missing_subdefs(); $generatedSubdefs += $this->generateRecordMissingSubdefs($record);
if (count($wanted_subdefs) > 0) {
$subdefGenerator->generateSubdefs($record, $wanted_subdefs);
foreach ($wanted_subdefs as $subdef) {
$this->container['monolog']->addInfo("generate " . $subdef . " for record " . $record->get_record_id());
$n++;
}
}
unset($record);
$progressBar->advance(); $progressBar->advance();
} }
$progressBar->finish(); $progressBar->finish();
} }
$this->container['monolog']->addInfo($n . " subdefs done"); $this->container['monolog']->addInfo($generatedSubdefs . " subdefs done");
$stop = microtime(true); $stop = microtime(true);
$duration = $stop - $start; $duration = $stop - $start;
$this->container['monolog']->addInfo(sprintf("process took %s, (%f sd/s.)", $this->getFormattedDuration($duration), round($n / $duration, 3))); $this->container['monolog']->addInfo(sprintf("process took %s, (%f sd/s.)", $this->getFormattedDuration($duration), round($generatedSubdefs / $duration, 3)));
$progressBar->finish(); $progressBar->finish();
return;
} }
/**
* Generate subdef generation and return number of subdef
* @param \record_adapter $record
* @return int
*/
protected function generateRecordMissingSubdefs(\record_adapter $record)
{
$wanted_subdefs = $record->get_missing_subdefs();
if (!empty($wanted_subdefs)) {
$this->getSubdefGenerator()->generateSubdefs($record, $wanted_subdefs);
foreach ($wanted_subdefs as $subdef) {
$this->container['monolog']->addInfo("generate " . $subdef . " for record " . $record->getRecordId());
}
}
return count($wanted_subdefs);
}
/**
* @return SubdefGenerator
*/
protected function getSubdefGenerator()
{
if (null === $this->generator) {
$this->generator = $this->container['subdef.generator'];
}
return $this->generator;
}
} }

View File

@@ -1,9 +1,8 @@
<?php <?php
/**
/*
* This file is part of Phraseanet * This file is part of Phraseanet
* *
* (c) 2005-2014 Alchemy * (c) 2005-2016 Alchemy
* *
* For the full copyright and license information, please view the LICENSE * For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code. * file that was distributed with this source code.
@@ -14,19 +13,15 @@ namespace Alchemy\Phrasea\Command;
use Alchemy\Phrasea\Exception\InvalidArgumentException; use Alchemy\Phrasea\Exception\InvalidArgumentException;
use Alchemy\Phrasea\Media\SubdefGenerator; use Alchemy\Phrasea\Media\SubdefGenerator;
use Doctrine\DBAL\Connection; use Doctrine\DBAL\Connection;
use Doctrine\DBAL\SQLParserUtils;
use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Helper\HelperSet;
use Symfony\Component\Console\Helper\ProgressBar; use Symfony\Component\Console\Helper\ProgressBar;
use media_subdef; use media_subdef;
class BuildSubdefs extends Command class BuildSubdefs extends Command
{ {
/**
* Constructor
*/
public function __construct($name = null) public function __construct($name = null)
{ {
parent::__construct($name); parent::__construct($name);
@@ -39,8 +34,6 @@ class BuildSubdefs extends Command
$this->addOption('min_record', 'min', InputOption::VALUE_OPTIONAL, 'Min record id'); $this->addOption('min_record', 'min', InputOption::VALUE_OPTIONAL, 'Min record id');
$this->addOption('with-substitution', 'wsubstit', InputOption::VALUE_NONE, 'Regenerate subdefs for substituted records as well'); $this->addOption('with-substitution', 'wsubstit', InputOption::VALUE_NONE, 'Regenerate subdefs for substituted records as well');
$this->addOption('substitution-only', 'substito', InputOption::VALUE_NONE, 'Regenerate subdefs for substituted records only'); $this->addOption('substitution-only', 'substito', InputOption::VALUE_NONE, 'Regenerate subdefs for substituted records only');
return $this;
} }
/** /**
@@ -70,111 +63,46 @@ class BuildSubdefs extends Command
return; return;
} }
$sqlCount = "SELECT COUNT(DISTINCT(r.record_id)) AS nb_records" $min = $input->getOption('min_record');
. " FROM record r LEFT JOIN subdef s ON (r.record_id = s.record_id AND s.name IN (?))" $max = $input->getOption('max_record');
. " WHERE r.type IN (?)";
$types = array(Connection::PARAM_STR_ARRAY, Connection::PARAM_STR_ARRAY);
$params = array($subdefsName, $recordsType);
if (null !== $min = $input->getOption('min_record')) {
$sqlCount .= " AND (r.record_id >= ?)";
$params[] = (int) $min;
$types[] = \PDO::PARAM_INT;
}
if (null !== $max = $input->getOption('max_record')) {
$sqlCount .= " AND (r.record_id <= ?)";
$params[] = (int) $max;
$types[] = \PDO::PARAM_INT;
}
$substitutionOnly = $input->getOption('substitution-only'); $substitutionOnly = $input->getOption('substitution-only');
$withSubstitution = $input->getOption('with-substitution'); $withSubstitution = $input->getOption('with-substitution');
if (false === $withSubstitution) { if (false !== $withSubstitution && false !== $substitutionOnly) {
if (true === $substitutionOnly) {
$sqlCount .= " AND (ISNULL(s.substit) OR s.substit = 1)";
} else {
$sqlCount .= " AND (ISNULL(s.substit) OR s.substit = 0)";
}
} elseif ($substitutionOnly) {
throw new InvalidArgumentException('Conflict, you can not ask for --substitution-only && --with-substitution parameters at the same time'); throw new InvalidArgumentException('Conflict, you can not ask for --substitution-only && --with-substitution parameters at the same time');
} }
list($sqlCount, $stmtParams) = SQLParserUtils::expandListParameters($sqlCount, $params, $types); list($sql, $params, $types) = $this->generateSQL($subdefsName, $recordsType, $min, $max, $withSubstitution, $substitutionOnly);
$databox = $this->container->findDataboxById($input->getArgument('databox')); $databox = $this->container->findDataboxById($input->getArgument('databox'));
$output->writeln($sqlCount);
$connection = $databox->get_connection(); $connection = $databox->get_connection();
$stmt = $connection->prepare($sqlCount);
$stmt->execute($stmtParams); $sqlCount = sprintf('SELECT COUNT(*) FROM (%s)', $sql);
$row = $stmt->fetch(); $output->writeln($sqlCount);
$totalRecords = $row['nb_records']; $totalRecords = (int)$connection->executeQuery($sqlCount, $params, $types)->fetchColumn();
if ($totalRecords === 0) { if ($totalRecords === 0) {
return; return;
} }
/** @var HelperSet $helperSet */ $progress = new ProgressBar($output, $totalRecords);
$helperSet = $this->getHelperSet();
/** @var ProgressBar $progress */ $progress->start();
$progress = $helperSet->get('progress');
$progress->start($output, $totalRecords);
$progress->display(); $progress->display();
$sql = "SELECT DISTINCT(r.record_id)" $rows = $connection->executeQuery($sql, $params, $types)->fetchAll(\PDO::FETCH_ASSOC);
. " FROM record r LEFT JOIN subdef s ON (r.record_id = s.record_id AND s.name IN (?))"
. " WHERE r.type IN (?)";
$types = array(Connection::PARAM_STR_ARRAY, Connection::PARAM_STR_ARRAY);
$params = array($subdefsName, $recordsType);
if ($min) {
$sql .= " AND (r.record_id >= ?)";
$params[] = (int) $min;
$types[] = \PDO::PARAM_INT;
}
if ($max) {
$sql .= " AND (r.record_id <= ?)";
$params[] = (int) $max;
$types[] = \PDO::PARAM_INT;
}
if (false === $withSubstitution) {
if (true === $substitutionOnly) {
$sql .= " AND (ISNULL(s.substit) OR s.substit = 1)";
} else {
$sql .= " AND (ISNULL(s.substit) OR s.substit = 0)";
}
}
list($sql, $stmtParams) = SQLParserUtils::expandListParameters($sql, $params, $types);
$connection = $databox->get_connection();
$stmt = $connection->prepare($sql);
$stmt->execute($stmtParams);
$rows = $stmt->fetchAll(\PDO::FETCH_ASSOC);
foreach ($rows as $row) { foreach ($rows as $row) {
$output->write(sprintf(' (#%s)', $row['record_id'])); $output->write(sprintf(' (#%s)', $row['record_id']));
$record = new \record_adapter($this->container, $databox->get_sbas_id(), $row['record_id']); $record = $databox->get_record($row['record_id']);
/** @var media_subdef[] $subdefs */
$subdefs = array_filter($record->get_subdefs(), function(media_subdef $subdef) use ($subdefsName) { $subdefs = array_filter($record->get_subdefs(), function(media_subdef $subdef) use ($subdefsName) {
return in_array($subdef->get_name(), $subdefsName); return in_array($subdef->get_name(), $subdefsName);
}); });
/** @var media_subdef $subdef */
foreach ($subdefs as $subdef) { foreach ($subdefs as $subdef) {
$subdef->remove_file(); $subdef->remove_file();
if (($withSubstitution && $subdef->is_substituted()) || $substitutionOnly) { if (($withSubstitution && $subdef->is_substituted()) || $substitutionOnly) {
@@ -186,13 +114,49 @@ class BuildSubdefs extends Command
$subdefGenerator = $this->container['subdef.generator']; $subdefGenerator = $this->container['subdef.generator'];
$subdefGenerator->generateSubdefs($record, $subdefsName); $subdefGenerator->generateSubdefs($record, $subdefsName);
$stmt->closeCursor();
$progress->advance(); $progress->advance();
} }
unset($rows, $record, $stmt, $connection);
$progress->finish(); $progress->finish();
} }
/**
* @param string[] $subdefNames
* @param string[] $recordTypes
* @param null|int $min
* @param null|int $max
* @param bool $withSubstitution
* @param bool $substitutionOnly
* @return array
*/
protected function generateSQL(array $subdefNames, array $recordTypes, $min, $max, $withSubstitution, $substitutionOnly)
{
$sql = "SELECT DISTINCT(r.record_id) AS record_id"
. " FROM record r LEFT JOIN subdef s ON (r.record_id = s.record_id AND s.name IN (?))"
. " WHERE r.type IN (?)";
$types = array(Connection::PARAM_STR_ARRAY, Connection::PARAM_STR_ARRAY);
$params = array($subdefNames, $recordTypes);
if (null !== $min) {
$sql .= " AND (r.record_id >= ?)";
$params[] = (int)$min;
$types[] = \PDO::PARAM_INT;
}
if (null !== $max) {
$sql .= " AND (r.record_id <= ?)";
$params[] = (int)$max;
$types[] = \PDO::PARAM_INT;
}
if (false === $withSubstitution) {
$sql .= " AND (ISNULL(s.substit) OR s.substit = ?)";
$params[] = $substitutionOnly ? 1 : 0;
$types[] = \PDO::PARAM_INT;
}
return array($sql, $params, $types);
}
} }

View File

@@ -1,9 +1,8 @@
<?php <?php
/**
/*
* This file is part of Phraseanet * This file is part of Phraseanet
* *
* (c) 2005-2014 Alchemy * (c) 2005-2016 Alchemy
* *
* For the full copyright and license information, please view the LICENSE * For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code. * file that was distributed with this source code.
@@ -11,16 +10,15 @@
namespace Alchemy\Phrasea\Model\Repositories; namespace Alchemy\Phrasea\Model\Repositories;
use Alchemy\Phrasea\Model\Entities\AuthFailure;
use Doctrine\ORM\EntityRepository; use Doctrine\ORM\EntityRepository;
/**
* AuthFailureRepository
*
* This class was generated by the Doctrine ORM. Add your own custom
* repository methods below.
*/
class AuthFailureRepository extends EntityRepository class AuthFailureRepository extends EntityRepository
{ {
/**
* @param string $limit
* @return AuthFailure[]
*/
public function findOldFailures($limit = '-2 months') public function findOldFailures($limit = '-2 months')
{ {
$date = new \DateTime($limit); $date = new \DateTime($limit);
@@ -37,6 +35,11 @@ class AuthFailureRepository extends EntityRepository
return $query->getResult(); return $query->getResult();
} }
/**
* @param string $username
* @param string $ip
* @return AuthFailure[]
*/
public function findLockedFailuresMatching($username, $ip) public function findLockedFailuresMatching($username, $ip)
{ {
$dql = 'SELECT f $dql = 'SELECT f
@@ -46,7 +49,7 @@ class AuthFailureRepository extends EntityRepository
$params = [ $params = [
'username' => $username, 'username' => $username,
'ip' => $ip, 'ip' => $ip,
]; ];
$query = $this->_em->createQuery($dql); $query = $this->_em->createQuery($dql);

View File

@@ -4,6 +4,7 @@ namespace Alchemy\Tests\Phrasea\Authentication\Phrasea;
use Alchemy\Phrasea\Authentication\Phrasea\FailureManager; use Alchemy\Phrasea\Authentication\Phrasea\FailureManager;
use Alchemy\Phrasea\Model\Entities\AuthFailure; use Alchemy\Phrasea\Model\Entities\AuthFailure;
use Alchemy\Phrasea\Model\Repositories\AuthFailureRepository;
use Gedmo\Timestampable\TimestampableListener; use Gedmo\Timestampable\TimestampableListener;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
@@ -18,7 +19,7 @@ class FailureManagerTest extends \PhraseanetTestCase
*/ */
public function testSaveFailure() public function testSaveFailure()
{ {
$repo = $this->getRepo(); $repo = $this->createAuthFailureRepositoryMock();
$em = $this->createEntityManagerMock(); $em = $this->createEntityManagerMock();
$recaptcha = $this->getReCaptchaMock(null); $recaptcha = $this->getReCaptchaMock(null);
@@ -31,8 +32,8 @@ class FailureManagerTest extends \PhraseanetTestCase
->will($this->returnValue($ip)); ->will($this->returnValue($ip));
$oldFailures = [ $oldFailures = [
$this->getMock('Alchemy\Phrasea\Model\Entities\AuthFailure'), $this->getMock(AuthFailure::class),
$this->getMock('Alchemy\Phrasea\Model\Entities\AuthFailure') $this->getMock(AuthFailure::class)
]; ];
$repo->expects($this->once()) $repo->expects($this->once())
@@ -41,12 +42,12 @@ class FailureManagerTest extends \PhraseanetTestCase
$em->expects($this->exactly(count($oldFailures))) $em->expects($this->exactly(count($oldFailures)))
->method('remove') ->method('remove')
->with($this->isInstanceOf('Alchemy\Phrasea\Model\Entities\AuthFailure')); ->with($this->isInstanceOf(AuthFailure::class));
$catchFailure = null; $catchFailure = null;
$em->expects($this->once()) $em->expects($this->once())
->method('persist') ->method('persist')
->with($this->isInstanceOf('Alchemy\Phrasea\Model\Entities\AuthFailure')) ->with($this->isInstanceOf(AuthFailure::class))
->will($this->returnCallback(function ($failure) use (&$catchFailure) { ->will($this->returnCallback(function ($failure) use (&$catchFailure) {
$catchFailure = $failure; $catchFailure = $failure;
})); }));
@@ -54,6 +55,8 @@ class FailureManagerTest extends \PhraseanetTestCase
$manager = new FailureManager($repo, $em, $recaptcha, 9); $manager = new FailureManager($repo, $em, $recaptcha, 9);
$manager->saveFailure($username, $request); $manager->saveFailure($username, $request);
/** @var null|AuthFailure $catchFailure */
$this->assertInstanceOf(AuthFailure::class, $catchFailure);
$this->assertEquals($ip, $catchFailure->getIp()); $this->assertEquals($ip, $catchFailure->getIp());
$this->assertEquals(true, $catchFailure->getLocked()); $this->assertEquals(true, $catchFailure->getLocked());
$this->assertEquals($username, $catchFailure->getUsername()); $this->assertEquals($username, $catchFailure->getUsername());
@@ -64,7 +67,7 @@ class FailureManagerTest extends \PhraseanetTestCase
*/ */
public function testCheckFailures() public function testCheckFailures()
{ {
$repo = $this->getRepo(); $repo = $this->createAuthFailureRepositoryMock();
$em = $this->createEntityManagerMock(); $em = $this->createEntityManagerMock();
$recaptcha = $this->getReCaptchaMock(null); $recaptcha = $this->getReCaptchaMock(null);
$request = $this->getRequestMock(); $request = $this->getRequestMock();
@@ -86,7 +89,7 @@ class FailureManagerTest extends \PhraseanetTestCase
*/ */
public function testCheckFailuresLessThan9() public function testCheckFailuresLessThan9()
{ {
$repo = $this->getRepo(); $repo = $this->createAuthFailureRepositoryMock();
$em = $this->createEntityManagerMock(); $em = $this->createEntityManagerMock();
$recaptcha = $this->getReCaptchaMock(null); $recaptcha = $this->getReCaptchaMock(null);
$request = $this->getRequestMock(); $request = $this->getRequestMock();
@@ -94,7 +97,7 @@ class FailureManagerTest extends \PhraseanetTestCase
$username = 'romainneutron'; $username = 'romainneutron';
$oldFailures = $this->ArrayIze(function () { $oldFailures = $this->ArrayIze(function () {
return $this->getMock('Alchemy\Phrasea\Model\Entities\AuthFailure'); return $this->getMock(AuthFailure::class);
}, 8); }, 8);
$repo->expects($this->once()) $repo->expects($this->once())
@@ -110,7 +113,7 @@ class FailureManagerTest extends \PhraseanetTestCase
*/ */
public function testCheckFailuresMoreThan9WithoutCaptcha() public function testCheckFailuresMoreThan9WithoutCaptcha()
{ {
$repo = $this->getRepo(); $repo = $this->createAuthFailureRepositoryMock();
$em = $this->createEntityManagerMock(); $em = $this->createEntityManagerMock();
$recaptcha = $this->getReCaptchaMock(false); $recaptcha = $this->getReCaptchaMock(false);
$request = $this->getRequestMock(); $request = $this->getRequestMock();
@@ -118,7 +121,7 @@ class FailureManagerTest extends \PhraseanetTestCase
$username = 'romainneutron'; $username = 'romainneutron';
$oldFailures = $this->ArrayIze(function () { $oldFailures = $this->ArrayIze(function () {
return $this->getMock('Alchemy\Phrasea\Model\Entities\AuthFailure'); return $this->getMock(AuthFailure::class);
}, 10); }, 10);
$repo->expects($this->once()) $repo->expects($this->once())
@@ -134,7 +137,7 @@ class FailureManagerTest extends \PhraseanetTestCase
*/ */
public function testCheckFailuresMoreThan9WithCorrectCaptcha() public function testCheckFailuresMoreThan9WithCorrectCaptcha()
{ {
$repo = $this->getRepo(); $repo = $this->createAuthFailureRepositoryMock();
$em = $this->createEntityManagerMock(); $em = $this->createEntityManagerMock();
$request = $this->getRequestMock(); $request = $this->getRequestMock();
$recaptcha = $this->getReCaptchaMock(true, $request, true); $recaptcha = $this->getReCaptchaMock(true, $request, true);
@@ -142,7 +145,7 @@ class FailureManagerTest extends \PhraseanetTestCase
$username = 'romainneutron'; $username = 'romainneutron';
$oldFailures = $this->ArrayIze(function () { $oldFailures = $this->ArrayIze(function () {
$failure = $this->getMock('Alchemy\Phrasea\Model\Entities\AuthFailure'); $failure = $this->getMock(AuthFailure::class);
$failure->expects($this->once()) $failure->expects($this->once())
->method('setLocked') ->method('setLocked')
->with($this->equalTo(false)); ->with($this->equalTo(false));
@@ -164,7 +167,7 @@ class FailureManagerTest extends \PhraseanetTestCase
*/ */
public function testCheckFailuresMoreThan9WithIncorrectCaptcha() public function testCheckFailuresMoreThan9WithIncorrectCaptcha()
{ {
$repo = $this->getRepo(); $repo = $this->createAuthFailureRepositoryMock();
$em = $this->createEntityManagerMock(); $em = $this->createEntityManagerMock();
$request = $this->getRequestMock(); $request = $this->getRequestMock();
$recaptcha = $this->getReCaptchaMock(true, $request, false); $recaptcha = $this->getReCaptchaMock(true, $request, false);
@@ -172,7 +175,7 @@ class FailureManagerTest extends \PhraseanetTestCase
$username = 'romainneutron'; $username = 'romainneutron';
$oldFailures = $this->ArrayIze(function () { $oldFailures = $this->ArrayIze(function () {
return $this->getMock('Alchemy\Phrasea\Model\Entities\AuthFailure'); return $this->getMock(AuthFailure::class);
}, 10); }, 10);
$repo->expects($this->once()) $repo->expects($this->once())
@@ -185,7 +188,7 @@ class FailureManagerTest extends \PhraseanetTestCase
public function testCheckFailuresTrialsIsConfigurableUnderThreshold() public function testCheckFailuresTrialsIsConfigurableUnderThreshold()
{ {
$repo = $this->getRepo(); $repo = $this->createAuthFailureRepositoryMock();
$em = $this->createEntityManagerMock(); $em = $this->createEntityManagerMock();
$recaptcha = $this->getReCaptchaMock(null); $recaptcha = $this->getReCaptchaMock(null);
$request = $this->getRequestMock(); $request = $this->getRequestMock();
@@ -193,7 +196,7 @@ class FailureManagerTest extends \PhraseanetTestCase
$username = 'romainneutron'; $username = 'romainneutron';
$oldFailures = $this->ArrayIze(function () { $oldFailures = $this->ArrayIze(function () {
return $this->getMock('Alchemy\Phrasea\Model\Entities\AuthFailure'); return $this->getMock(AuthFailure::class);
}, 2); }, 2);
$repo->expects($this->once()) $repo->expects($this->once())
@@ -207,10 +210,9 @@ class FailureManagerTest extends \PhraseanetTestCase
public function testTrialsIsConfigurable() public function testTrialsIsConfigurable()
{ {
$em = $this->createEntityManagerMock(); $em = $this->createEntityManagerMock();
$recaptcha = $this->getReCaptchaMock(null); $recaptcha = $this->getReCaptchaMock(null);
$manager = new FailureManager($this->createEntityRepositoryMock(), $em, $recaptcha, 2); $manager = new FailureManager($this->createAuthFailureRepositoryMock(), $em, $recaptcha, 2);
$this->assertEquals(2, $manager->getTrials()); $this->assertEquals(2, $manager->getTrials());
} }
@@ -220,7 +222,7 @@ class FailureManagerTest extends \PhraseanetTestCase
*/ */
public function testCheckFailuresTrialsIsConfigurableOverThreshold() public function testCheckFailuresTrialsIsConfigurableOverThreshold()
{ {
$repo = $this->getRepo(); $repo = $this->createAuthFailureRepositoryMock();
$em = $this->createEntityManagerMock(); $em = $this->createEntityManagerMock();
$request = $this->getRequestMock(); $request = $this->getRequestMock();
$recaptcha = $this->getReCaptchaMock(true, $request, false); $recaptcha = $this->getReCaptchaMock(true, $request, false);
@@ -228,7 +230,7 @@ class FailureManagerTest extends \PhraseanetTestCase
$username = 'romainneutron'; $username = 'romainneutron';
$oldFailures = $this->ArrayIze(function () { $oldFailures = $this->ArrayIze(function () {
return $this->getMock('Alchemy\Phrasea\Model\Entities\AuthFailure'); return $this->getMock(AuthFailure::class);
}, 3); }, 3);
$repo->expects($this->once()) $repo->expects($this->once())
@@ -311,14 +313,17 @@ class FailureManagerTest extends \PhraseanetTestCase
private function getRequestMock() private function getRequestMock()
{ {
return $this->getMockBuilder('Symfony\Component\HttpFoundation\Request') return $this->getMockBuilder(Request::class)
->disableOriginalConstructor() ->disableOriginalConstructor()
->getMock(); ->getMock();
} }
private function getRepo() /**
* @return AuthFailureRepository|\PHPUnit_Framework_MockObject_MockObject
*/
private function createAuthFailureRepositoryMock()
{ {
return $this->getMockBuilder('Alchemy\Phrasea\Model\Repositories\AuthFailureRepository') return $this->getMockBuilder(AuthFailureRepository::class)
->disableOriginalConstructor() ->disableOriginalConstructor()
->getMock(); ->getMock();
} }

View File

@@ -2,11 +2,26 @@
namespace Alchemy\Tests\Phrasea\Core\Provider; namespace Alchemy\Tests\Phrasea\Core\Provider;
use Alchemy\Phrasea\Authentication\AccountCreator;
use Alchemy\Phrasea\Authentication\Authenticator;
use Alchemy\Phrasea\Authentication\Manager as AuthenticationManager;
use Alchemy\Phrasea\Authentication\PersistentCookie\Manager as PersistentCookieManager;
use Alchemy\Phrasea\Authentication\Phrasea\FailureHandledNativeAuthentication;
use Alchemy\Phrasea\Authentication\Phrasea\FailureManager;
use Alchemy\Phrasea\Authentication\Phrasea\NativeAuthentication;
use Alchemy\Phrasea\Authentication\Phrasea\OldPasswordEncoder;
use Alchemy\Phrasea\Authentication\Phrasea\PasswordAuthenticationInterface;
use Alchemy\Phrasea\Authentication\Phrasea\PasswordEncoder;
use Alchemy\Phrasea\Authentication\Provider\Factory;
use Alchemy\Phrasea\Authentication\ProvidersCollection;
use Alchemy\Phrasea\Authentication\SuggestionFinder;
use Alchemy\Phrasea\Core\Provider\RepositoriesServiceProvider; use Alchemy\Phrasea\Core\Provider\RepositoriesServiceProvider;
use Alchemy\Phrasea\Core\Provider\TokensServiceProvider;
use Alchemy\Phrasea\Core\Provider\AuthenticationManagerServiceProvider; use Alchemy\Phrasea\Core\Provider\AuthenticationManagerServiceProvider;
use Alchemy\Phrasea\Core\Provider\ConfigurationServiceProvider; use Alchemy\Phrasea\Core\Provider\ConfigurationServiceProvider;
use Alchemy\Phrasea\Model\Entities\User;
use Alchemy\Phrasea\Model\Repositories\AuthFailureRepository;
use Alchemy\Phrasea\Model\Repositories\UserRepository; use Alchemy\Phrasea\Model\Repositories\UserRepository;
use Neutron\ReCaptcha\ReCaptcha;
/** /**
* @group functional * @group functional
@@ -19,59 +34,59 @@ class AuthenticationManagerServiceProviderTest extends ServiceProviderTestCase
{ {
return [ return [
[ [
'Alchemy\Phrasea\Core\Provider\AuthenticationManagerServiceProvider', AuthenticationManagerServiceProvider::class,
'authentication', 'authentication',
'Alchemy\\Phrasea\\Authentication\\Authenticator', Authenticator::class,
], ],
[ [
'Alchemy\Phrasea\Core\Provider\AuthenticationManagerServiceProvider', AuthenticationManagerServiceProvider::class,
'authentication.persistent-manager', 'authentication.persistent-manager',
'Alchemy\Phrasea\Authentication\PersistentCookie\Manager' PersistentCookieManager::class
], ],
[ [
'Alchemy\Phrasea\Core\Provider\AuthenticationManagerServiceProvider', AuthenticationManagerServiceProvider::class,
'authentication.suggestion-finder', 'authentication.suggestion-finder',
'Alchemy\Phrasea\Authentication\SuggestionFinder' SuggestionFinder::class
], ],
[ [
'Alchemy\Phrasea\Core\Provider\AuthenticationManagerServiceProvider', AuthenticationManagerServiceProvider::class,
'authentication.providers.factory', 'authentication.providers.factory',
'Alchemy\Phrasea\Authentication\Provider\Factory' Factory::class
], ],
[ [
'Alchemy\Phrasea\Core\Provider\AuthenticationManagerServiceProvider', AuthenticationManagerServiceProvider::class,
'authentication.providers', 'authentication.providers',
'Alchemy\Phrasea\Authentication\ProvidersCollection' ProvidersCollection::class
], ],
[ [
'Alchemy\Phrasea\Core\Provider\AuthenticationManagerServiceProvider', AuthenticationManagerServiceProvider::class,
'authentication.manager', 'authentication.manager',
'Alchemy\Phrasea\Authentication\Manager' AuthenticationManager::class
], ],
[ [
'Alchemy\Phrasea\Core\Provider\AuthenticationManagerServiceProvider', AuthenticationManagerServiceProvider::class,
'auth.password-encoder', 'auth.password-encoder',
'Alchemy\Phrasea\Authentication\Phrasea\PasswordEncoder' PasswordEncoder::class
], ],
[ [
'Alchemy\Phrasea\Core\Provider\AuthenticationManagerServiceProvider', AuthenticationManagerServiceProvider::class,
'auth.old-password-encoder', 'auth.old-password-encoder',
'Alchemy\Phrasea\Authentication\Phrasea\OldPasswordEncoder' OldPasswordEncoder::class
], ],
[ [
'Alchemy\Phrasea\Core\Provider\AuthenticationManagerServiceProvider', AuthenticationManagerServiceProvider::class,
'auth.native.failure-manager', 'auth.native.failure-manager',
'Alchemy\Phrasea\Authentication\Phrasea\FailureManager' FailureManager::class
], ],
[ [
'Alchemy\Phrasea\Core\Provider\AuthenticationManagerServiceProvider', AuthenticationManagerServiceProvider::class,
'auth.native', 'auth.native',
'Alchemy\Phrasea\Authentication\Phrasea\PasswordAuthenticationInterface' PasswordAuthenticationInterface::class
], ],
[ [
'Alchemy\Phrasea\Core\Provider\AuthenticationManagerServiceProvider', AuthenticationManagerServiceProvider::class,
'authentication.providers.account-creator', 'authentication.providers.account-creator',
'Alchemy\Phrasea\Authentication\AccountCreator' AccountCreator::class
], ],
]; ];
} }
@@ -83,9 +98,7 @@ class AuthenticationManagerServiceProviderTest extends ServiceProviderTestCase
$app['conf']->set(['authentication', 'captcha', 'trials-before-display'], 42); $app['conf']->set(['authentication', 'captcha', 'trials-before-display'], 42);
//$app['orm.em'] = $this->createEntityManagerMock(); //$app['orm.em'] = $this->createEntityManagerMock();
$app['recaptcha'] = $this->getMockBuilder('Neutron\ReCaptcha\ReCaptcha') $app['recaptcha'] = $this->createReCaptchaMock();
->disableOriginalConstructor()
->getMock();
$manager = $app['auth.native.failure-manager']; $manager = $app['auth.native.failure-manager'];
$this->assertEquals(42, $manager->getTrials()); $this->assertEquals(42, $manager->getTrials());
@@ -93,9 +106,10 @@ class AuthenticationManagerServiceProviderTest extends ServiceProviderTestCase
public function testFailureAccountCreator() public function testFailureAccountCreator()
{ {
self::$DI['app']->register(new ConfigurationServiceProvider()); $app = $this->getApplication();
self::$DI['app']['conf']->set(['authentication', 'auto-create'], ['templates' => []]); $app->register(new ConfigurationServiceProvider());
self::$DI['app']['authentication.providers.account-creator']; $app['conf']->set(['authentication', 'auto-create'], ['templates' => []]);
$app['authentication.providers.account-creator'];
} }
public function testAuthNativeWithCaptchaEnabled() public function testAuthNativeWithCaptchaEnabled()
@@ -111,12 +125,12 @@ class AuthenticationManagerServiceProviderTest extends ServiceProviderTestCase
$app['orm.em'] = $this->createEntityManagerMock(); $app['orm.em'] = $this->createEntityManagerMock();
$app['repo.users'] = $this->createUserRepositoryMock(); $app['repo.users'] = $this->createUserRepositoryMock();
$app['repo.auth-failures'] = $this->createEntityRepositoryMock(); $app['repo.auth-failures'] = $this->getMockBuilder(AuthFailureRepository::class)
$app['recaptcha'] = $this->getMockBuilder('Neutron\ReCaptcha\ReCaptcha')
->disableOriginalConstructor() ->disableOriginalConstructor()
->getMock(); ->getMock();
$app['recaptcha'] = $this->createReCaptchaMock();
$this->assertInstanceOf('Alchemy\Phrasea\Authentication\Phrasea\FailureHandledNativeAuthentication', $app['auth.native']); $this->assertInstanceOf(FailureHandledNativeAuthentication::class, $app['auth.native']);
} }
public function testAuthNativeWithCaptchaDisabled() public function testAuthNativeWithCaptchaDisabled()
@@ -131,26 +145,25 @@ class AuthenticationManagerServiceProviderTest extends ServiceProviderTestCase
$app['orm.em'] = $this->createEntityManagerMock(); $app['orm.em'] = $this->createEntityManagerMock();
$app['repo.users'] = $this->createUserRepositoryMock(); $app['repo.users'] = $this->createUserRepositoryMock();
$app['recaptcha'] = $this->getMockBuilder('Neutron\ReCaptcha\ReCaptcha') $app['recaptcha'] = $this->createReCaptchaMock();
->disableOriginalConstructor()
->getMock();
$this->assertInstanceOf('Alchemy\Phrasea\Authentication\Phrasea\NativeAuthentication', $app['auth.native']); $this->assertInstanceOf(NativeAuthentication::class, $app['auth.native']);
} }
public function testAccountCreator() public function testAccountCreator()
{ {
$template1 = $user = self::$DI['app']['manipulator.user']->createTemplate('template1', self::$DI['user']); $app = $this->getApplication();
$template2 = $user = self::$DI['app']['manipulator.user']->createTemplate('template2', self::$DI['user']); $template1 = $user = $app['manipulator.user']->createTemplate('template1', self::$DI['user']);
$template2 = $user = $app['manipulator.user']->createTemplate('template2', self::$DI['user']);
self::$DI['app']['conf']->set(['authentication', 'auto-create'], ['templates' => [$template1->getId(), $template2->getId()]]); $app['conf']->set(['authentication', 'auto-create'], ['templates' => [$template1->getId(), $template2->getId()]]);
$this->assertEquals([$template1->getLogin(), $template2->getLogin()], array_map(function ($u) { $this->assertEquals([$template1->getLogin(), $template2->getLogin()], array_map(function (User $user) {
return $u->getLogin(); return $user->getLogin();
}, self::$DI['app']['authentication.providers.account-creator']->getTemplates())); }, $app['authentication.providers.account-creator']->getTemplates()));
$this->removeUser(self::$DI['app'], $template1); $this->removeUser($app, $template1);
$this->removeUser(self::$DI['app'], $template2); $this->removeUser($app, $template2);
} }
private function createUserRepositoryMock() private function createUserRepositoryMock()
@@ -159,4 +172,14 @@ class AuthenticationManagerServiceProviderTest extends ServiceProviderTestCase
->disableOriginalConstructor() ->disableOriginalConstructor()
->getMock(); ->getMock();
} }
/**
* @return ReCaptcha|\PHPUnit_Framework_MockObject_MockObject
*/
protected function createReCaptchaMock()
{
return $this->getMockBuilder(ReCaptcha::class)
->disableOriginalConstructor()
->getMock();
}
} }

View File

@@ -336,6 +336,11 @@ abstract class PhraseanetTestCase extends WebTestCase
return $cli; return $cli;
} }
/**
* @param null|string $path
* @param string $environment
* @return Application
*/
protected function loadApp($path = null, $environment = Application::ENV_TEST) protected function loadApp($path = null, $environment = Application::ENV_TEST)
{ {
if (null !== $path) { if (null !== $path) {