Skip to content

Commit

Permalink
Merge pull request #1473 from deZinc/feature-password-restrictions
Browse files Browse the repository at this point in the history
[AdminBundle] Added optional password restrictions
  • Loading branch information
Devolicious authored Jun 28, 2017
2 parents 8440f50 + 61f27de commit e5ada00
Show file tree
Hide file tree
Showing 8 changed files with 517 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,15 @@ public function getConfigTreeBuilder()
->end()
->end()
->end()
->arrayNode('password_restrictions')
->addDefaultsIfNotSet()
->children()
->integerNode('min_digits')->defaultNull()->end()
->integerNode('min_uppercase')->defaultNull()->end()
->integerNode('min_special_characters')->defaultNull()->end()
->integerNode('min_length')->defaultNull()->end()
->integerNode('max_length')->defaultNull()->end()
->end()
->end();

return $treeBuilder;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,12 @@ public function load(array $configs, ContainerBuilder $container)
$container->setParameter('kunstmaan_admin.google_signin.client_secret', $config['google_signin']['client_secret']);
$container->setParameter('kunstmaan_admin.google_signin.hosted_domains', $config['google_signin']['hosted_domains']);

$container->setParameter('kunstmaan_admin.password_restrictions.min_digits' , $config['password_restrictions']['min_digits']);
$container->setParameter('kunstmaan_admin.password_restrictions.min_uppercase' , $config['password_restrictions']['min_uppercase']);
$container->setParameter('kunstmaan_admin.password_restrictions.min_special_characters' , $config['password_restrictions']['min_special_characters']);
$container->setParameter('kunstmaan_admin.password_restrictions.min_length' , $config['password_restrictions']['min_length']);
$container->setParameter('kunstmaan_admin.password_restrictions.max_length' , $config['password_restrictions']['max_length']);

$loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
$loader->load('services.yml');

Expand Down
9 changes: 8 additions & 1 deletion src/Kunstmaan/AdminBundle/Entity/BaseUser.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use Doctrine\ORM\Mapping as ORM;
use FOS\UserBundle\Model\GroupInterface;
use FOS\UserBundle\Model\User as AbstractUser;
use Kunstmaan\AdminBundle\Validator\Constraints\PasswordRestrictions;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Validator\Constraints\Email;
use Symfony\Component\Validator\Constraints\NotBlank;
Expand Down Expand Up @@ -173,7 +174,13 @@ public function setGoogleId($googleId)
public static function loadValidatorMetadata(ClassMetadata $metadata)
{
$metadata->addPropertyConstraint('username', new NotBlank());
$metadata->addPropertyConstraint('plainPassword', new NotBlank(array("groups" => array("Registration"))));
$metadata->addPropertyConstraints(
'plainPassword',
array(
new NotBlank(array("groups" => array("Registration"))),
new PasswordRestrictions(array("groups" => array("Registration","Default"))),
)
);
$metadata->addPropertyConstraint('email', new NotBlank());
$metadata->addPropertyConstraint('email', new Email());
$metadata->addConstraint(new UniqueEntity(array(
Expand Down
12 changes: 12 additions & 0 deletions src/Kunstmaan/AdminBundle/Resources/config/services.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ parameters:
kunstmaan_admin.password_check.listener.class: 'Kunstmaan\AdminBundle\EventListener\PasswordCheckListener'
kunstmaan_admin.firewall.provider_key: 'main'
kunstmaan_admin.domain_configuration.class: 'Kunstmaan\AdminBundle\Helper\DomainConfiguration'
kunstmaan_admin.validator.password_restrictions.class: 'Kunstmaan\AdminBundle\Validator\Constraints\PasswordRestrictionsValidator'

services:
kunstmaan_admin.menubuilder:
Expand Down Expand Up @@ -229,3 +230,14 @@ services:
- '%kunstmaan_admin.google_signin.client_id%'
tags:
- { name: twig.extension }

kunstmaan_admin.validator.password_restrictions:
class: '%kunstmaan_admin.validator.password_restrictions.class%'
arguments:
- '%kunstmaan_admin.password_restrictions.min_digits%'
- '%kunstmaan_admin.password_restrictions.min_uppercase%'
- '%kunstmaan_admin.password_restrictions.min_special_characters%'
- '%kunstmaan_admin.password_restrictions.min_length%'
- '%kunstmaan_admin.password_restrictions.max_length%'
tags:
- { name: validator.constraint_validator, alias: password_restrictions }
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
errors:
password:
dontmatch: The passwords you entered don't match
mindigits: 'The password should contain at least {{ min_digits }} digits.'
minuppercase: 'The password should contain at least {{ min_uppercase }} uppercase characters.'
minspecialcharacters: 'The password should contain at least {{ min_special_characters }} special characters.'
minlength: 'The password should be at least {{ min_length }} characters long.'
maxlength: 'The password should be maximum {{ max_length }} characters long.'

user:
loginexists: This username is already in use
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,265 @@
<?php
namespace Kunstmaan\MediaBundle\Tests\Validator\Constraints;

use Kunstmaan\AdminBundle\Validator\Constraints\PasswordRestrictions;
use Kunstmaan\AdminBundle\Validator\Constraints\PasswordRestrictionsValidator;
use Symfony\Component\Validator\Test\ConstraintValidatorTestCase;

/**
* Class PasswordRestrictionsValidatorTest
*
* Unit test for the password restrictions validator, will test with different sets of parameters
* for the validator is possible.
*/
class PasswordRestrictionsValidatorTest extends ConstraintValidatorTestCase
{

const PARAMETER_MIN_LENGTH = 8;
const PARAMETER_MAX_LENGTH = 16;
const PARAMETER_MIN_DIGITS = 3;
const PARAMETER_MIN_UPPERCASE = 2;
const PARAMETER_MIN_SPECIAL_CHARACTERS = 1;

/**
* @return PasswordRestrictionsValidator
*/
protected function createValidator()
{

return new PasswordRestrictionsValidator(self::PARAMETER_MIN_DIGITS,self::PARAMETER_MIN_UPPERCASE,self::PARAMETER_MIN_SPECIAL_CHARACTERS,self::PARAMETER_MIN_LENGTH,self::PARAMETER_MAX_LENGTH);
}

/**
* Set a validator with a limited number of parameters to overrule the default one from createValidator.
*
* @param int $minDigits
* @param int $minUppercase
* @param int $minSpecialCharacters
* @param int $minLength
* @param int $maxLength
*/
protected function setValidator($minDigits, $minUppercase, $minSpecialCharacters, $minLength, $maxLength)
{

$this->validator = new PasswordRestrictionsValidator($minDigits, $minUppercase, $minSpecialCharacters, $minLength, $maxLength);
$this->validator->initialize($this->context);

}

/**
* @param string $password
* @param null|string $message
* @param array $parameters
* @param null $code
*
* @dataProvider dataPasswordsWithAllParameters
*/
public function testPasswordWithAllParametersSet($password, $message = null, array $parameters = [], $code = null)
{
$this->buildAndTestPasswordRestrictions($password, $message, $parameters, $code);
}

/**
* @param string $password
* @param null|string $message
* @param array $parameters
* @param null $code
*
* @dataProvider dataPasswordsToShort
*/
public function testPasswordToShortOnlySet($password, $message = null, array $parameters = [], $code = null)
{
$this->setValidator(null, null, null, self::PARAMETER_MIN_LENGTH, null);
$this->buildAndTestPasswordRestrictions($password, $message, $parameters, $code);
}

/**
* @param string $password
* @param null|string $message
* @param array $parameters
* @param null $code
*
* @dataProvider dataPasswordsToLong
*/
public function testPasswordToLongOnlySet($password, $message = null, array $parameters = [], $code = null)
{

$this->setValidator(null, null, null, null, self::PARAMETER_MAX_LENGTH);
$this->buildAndTestPasswordRestrictions($password, $message, $parameters, $code);
}

/**
* @param string $password
* @param null|string $message
* @param array $parameters
* @param null $code
*
* @dataProvider dataPasswordsMinimumDigits
*/
public function testPasswordMinimumDigitsOnlySet($password, $message = null, array $parameters = [], $code = null)
{

$this->setValidator(self::PARAMETER_MIN_DIGITS, null, null, null, null);
$this->buildAndTestPasswordRestrictions($password, $message, $parameters, $code);
}

/**
* @param string $password
* @param null|string $message
* @param array $parameters
* @param null $code
*
* @dataProvider dataPasswordsMinimumUppercase
*/
public function testPasswordMinimumUppercaseOnlySet($password, $message = null, array $parameters = [], $code = null)
{

$this->setValidator(null, self::PARAMETER_MIN_UPPERCASE, null, null, null);
$this->buildAndTestPasswordRestrictions($password, $message, $parameters, $code);
}

/**
* @param string $password
* @param null|string $message
* @param array $parameters
* @param null $code
*
* @dataProvider dataPasswordsMinimumSpecialCharacters
*/
public function testPasswordMinimumSpecialCharactersOnlySet($password, $message = null, array $parameters = [], $code = null)
{

$this->setValidator(null, null, self::PARAMETER_MIN_SPECIAL_CHARACTERS, null, null);
$this->buildAndTestPasswordRestrictions($password, $message, $parameters, $code);
}

/**
* @param string $password
* @param null|string $message
* @param array $parameters
* @param null $code
*
* @dataProvider dataPasswordsLengthRange
*/
public function testPasswordLengthRangeSet($password, $message = null, array $parameters = [], $code = null)
{

$this->setValidator(null, null, null, self::PARAMETER_MIN_LENGTH, self::PARAMETER_MAX_LENGTH);
$this->buildAndTestPasswordRestrictions($password, $message, $parameters, $code);

}

/**
* Uses the set validator combined with data to assert.
*
* @param string $password
* @param null|string $message
* @param array $parameters
* @param null $code
*/
private function buildAndTestPasswordRestrictions( $password, $message = null, array $parameters = [], $code = null )
{
$constraint = new PasswordRestrictions();

$this->validator->validate($password,$constraint);

if ($message && $code) {
$this->buildViolation($message)
->setCode($code)
->setParameters($parameters)
->assertRaised();

} else {
$this->assertNoViolation();
}
}

/**
* @return array
*/
public function dataPasswordsWithAllParameters()
{

return [
['ABcdef789!'],
['AB123!q', PasswordRestrictions::MESSAGE_MIN_LENGTH, ['{{ min_length }}' => self::PARAMETER_MIN_LENGTH], PasswordRestrictions::INVALID_MIN_LENGTH_ERROR],
['AB123!q541kkjhghvhb451', PasswordRestrictions::MESSAGE_MAX_LENGTH, ['{{ max_length }}' => self::PARAMETER_MAX_LENGTH], PasswordRestrictions::INVALID_MAX_LENGTH_ERROR],
['abCDEFG*', PasswordRestrictions::MESSAGE_MIN_DIGITS, ['{{ min_digits }}' => self::PARAMETER_MIN_DIGITS], PasswordRestrictions::INVALID_MIN_DIGITS_ERROR],
['ab123efg!', PasswordRestrictions::MESSAGE_MIN_UPPERCASE, ['{{ min_uppercase }}' => self::PARAMETER_MIN_UPPERCASE], PasswordRestrictions::INVALID_MIN_UPPERCASE_ERROR],
['AB123efg', PasswordRestrictions::MESSAGE_MIN_SPECIAL_CHARACTERS, ['{{ min_special_characters }}' => self::PARAMETER_MIN_SPECIAL_CHARACTERS], PasswordRestrictions::INVALID_MIN_SPECIAL_CHARACTERS_ERROR],
];
}

/**
* @return array
*/
public function dataPasswordsToShort()
{
return [
['password'],
['ABC?123', PasswordRestrictions::MESSAGE_MIN_LENGTH, ['{{ min_length }}' => self::PARAMETER_MIN_LENGTH], PasswordRestrictions::INVALID_MIN_LENGTH_ERROR],
['admin', PasswordRestrictions::MESSAGE_MIN_LENGTH, ['{{ min_length }}' => self::PARAMETER_MIN_LENGTH], PasswordRestrictions::INVALID_MIN_LENGTH_ERROR],
];
}

/**
* @return array
*/
public function dataPasswordsToLong()
{
return [
['correct!'],
['thispasswordistolong', PasswordRestrictions::MESSAGE_MAX_LENGTH, ['{{ max_length }}' => self::PARAMETER_MAX_LENGTH], PasswordRestrictions::INVALID_MAX_LENGTH_ERROR],
];
}

/**
* @return array
*/
public function dataPasswordsMinimumDigits()
{

return [
['withdigits123'],
['nodigits', PasswordRestrictions::MESSAGE_MIN_DIGITS, ['{{ min_digits }}' => self::PARAMETER_MIN_DIGITS], PasswordRestrictions::INVALID_MIN_DIGITS_ERROR],
];
}

/**
* @return array
*/
public function dataPasswordsMinimumUppercase()
{

return [
['PassworD'],
['password', PasswordRestrictions::MESSAGE_MIN_UPPERCASE, ['{{ min_uppercase }}' => self::PARAMETER_MIN_UPPERCASE], PasswordRestrictions::INVALID_MIN_UPPERCASE_ERROR],
];
}

/**
* @return array
*/
public function dataPasswordsMinimumSpecialCharacters()
{

return [
['password!'],
['password', PasswordRestrictions::MESSAGE_MIN_SPECIAL_CHARACTERS, ['{{ min_special_characters }}' => self::PARAMETER_MIN_SPECIAL_CHARACTERS], PasswordRestrictions::INVALID_MIN_SPECIAL_CHARACTERS_ERROR],
];
}

/**
* Combine the data of the too long and too short test for an extra length
* range test.
*
* @return array
*/
public function dataPasswordsLengthRange()
{

return $this->dataPasswordsToLong() + $this->dataPasswordsToShort();

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php
namespace Kunstmaan\AdminBundle\Validator\Constraints;


use Symfony\Component\Validator\Constraint;

/**
* Class PasswordRestrictions
*/
class PasswordRestrictions extends Constraint
{

const INVALID_MIN_DIGITS_ERROR = 1;
const INVALID_MIN_UPPERCASE_ERROR = 2;
const INVALID_MIN_SPECIAL_CHARACTERS_ERROR = 3;
const INVALID_MIN_LENGTH_ERROR = 4;
const INVALID_MAX_LENGTH_ERROR = 5;

const MESSAGE_MIN_DIGITS = 'errors.password.mindigits';
const MESSAGE_MIN_UPPERCASE = 'errors.password.minuppercase';
const MESSAGE_MIN_SPECIAL_CHARACTERS = 'errors.password.minspecialcharacters';
const MESSAGE_MIN_LENGTH = 'errors.password.minlength';
const MESSAGE_MAX_LENGTH = 'errors.password.maxlength';

/**
* PasswordRestrictions constructor.
* @param null $options
*/
public function __construct($options = null)
{
parent::__construct($options);
}

}
Loading

0 comments on commit e5ada00

Please sign in to comment.