diff options
Diffstat (limited to 'Core')
74 files changed, 1111 insertions, 285 deletions
diff --git a/Core/Authentication/AuthenticationProviderManager.php b/Core/Authentication/AuthenticationProviderManager.php index 8b7474b..f713e8f 100644 --- a/Core/Authentication/AuthenticationProviderManager.php +++ b/Core/Authentication/AuthenticationProviderManager.php @@ -38,7 +38,7 @@ class AuthenticationProviderManager implements AuthenticationManagerInterface * Constructor. * * @param AuthenticationProviderInterface[] $providers An array of AuthenticationProviderInterface instances - * @param Boolean $eraseCredentials Whether to erase credentials after authentication or not + * @param bool $eraseCredentials Whether to erase credentials after authentication or not * * @throws \InvalidArgumentException */ @@ -49,7 +49,7 @@ class AuthenticationProviderManager implements AuthenticationManagerInterface } $this->providers = $providers; - $this->eraseCredentials = (Boolean) $eraseCredentials; + $this->eraseCredentials = (bool) $eraseCredentials; } public function setEventDispatcher(EventDispatcherInterface $dispatcher) diff --git a/Core/Authentication/AuthenticationTrustResolver.php b/Core/Authentication/AuthenticationTrustResolver.php index 9b3ff3d..d030459 100644 --- a/Core/Authentication/AuthenticationTrustResolver.php +++ b/Core/Authentication/AuthenticationTrustResolver.php @@ -36,7 +36,7 @@ class AuthenticationTrustResolver implements AuthenticationTrustResolverInterfac } /** - * {@inheritDoc} + * {@inheritdoc} */ public function isAnonymous(TokenInterface $token = null) { @@ -48,7 +48,7 @@ class AuthenticationTrustResolver implements AuthenticationTrustResolverInterfac } /** - * {@inheritDoc} + * {@inheritdoc} */ public function isRememberMe(TokenInterface $token = null) { @@ -60,7 +60,7 @@ class AuthenticationTrustResolver implements AuthenticationTrustResolverInterfac } /** - * {@inheritDoc} + * {@inheritdoc} */ public function isFullFledged(TokenInterface $token = null) { diff --git a/Core/Authentication/AuthenticationTrustResolverInterface.php b/Core/Authentication/AuthenticationTrustResolverInterface.php index ac07db0..03b48e9 100644 --- a/Core/Authentication/AuthenticationTrustResolverInterface.php +++ b/Core/Authentication/AuthenticationTrustResolverInterface.php @@ -28,7 +28,7 @@ interface AuthenticationTrustResolverInterface * * @param TokenInterface $token * - * @return Boolean + * @return bool */ public function isAnonymous(TokenInterface $token = null); @@ -38,7 +38,7 @@ interface AuthenticationTrustResolverInterface * * @param TokenInterface $token * - * @return Boolean + * @return bool */ public function isRememberMe(TokenInterface $token = null); @@ -47,7 +47,7 @@ interface AuthenticationTrustResolverInterface * * @param TokenInterface $token * - * @return Boolean + * @return bool */ public function isFullFledged(TokenInterface $token = null); } diff --git a/Core/Authentication/Provider/AnonymousAuthenticationProvider.php b/Core/Authentication/Provider/AnonymousAuthenticationProvider.php index ea91075..7fbbf85 100644 --- a/Core/Authentication/Provider/AnonymousAuthenticationProvider.php +++ b/Core/Authentication/Provider/AnonymousAuthenticationProvider.php @@ -40,7 +40,7 @@ class AnonymousAuthenticationProvider implements AuthenticationProviderInterface public function authenticate(TokenInterface $token) { if (!$this->supports($token)) { - return null; + return; } if ($this->key !== $token->getKey()) { diff --git a/Core/Authentication/Provider/AuthenticationProviderInterface.php b/Core/Authentication/Provider/AuthenticationProviderInterface.php index f63a924..23724db 100644 --- a/Core/Authentication/Provider/AuthenticationProviderInterface.php +++ b/Core/Authentication/Provider/AuthenticationProviderInterface.php @@ -29,7 +29,7 @@ interface AuthenticationProviderInterface extends AuthenticationManagerInterface * * @param TokenInterface $token A TokenInterface instance * - * @return Boolean true if the implementation supports the Token, false otherwise + * @return bool true if the implementation supports the Token, false otherwise */ public function supports(TokenInterface $token); } diff --git a/Core/Authentication/Provider/DaoAuthenticationProvider.php b/Core/Authentication/Provider/DaoAuthenticationProvider.php index a9a2205..4913be8 100644 --- a/Core/Authentication/Provider/DaoAuthenticationProvider.php +++ b/Core/Authentication/Provider/DaoAuthenticationProvider.php @@ -38,7 +38,7 @@ class DaoAuthenticationProvider extends UserAuthenticationProvider * @param UserCheckerInterface $userChecker An UserCheckerInterface instance * @param string $providerKey The provider key * @param EncoderFactoryInterface $encoderFactory An EncoderFactoryInterface instance - * @param Boolean $hideUserNotFoundExceptions Whether to hide user not found exception or not + * @param bool $hideUserNotFoundExceptions Whether to hide user not found exception or not */ public function __construct(UserProviderInterface $userProvider, UserCheckerInterface $userChecker, $providerKey, EncoderFactoryInterface $encoderFactory, $hideUserNotFoundExceptions = true) { diff --git a/Core/Authentication/Provider/PreAuthenticatedAuthenticationProvider.php b/Core/Authentication/Provider/PreAuthenticatedAuthenticationProvider.php index f4d0959..11c3cda 100644 --- a/Core/Authentication/Provider/PreAuthenticatedAuthenticationProvider.php +++ b/Core/Authentication/Provider/PreAuthenticatedAuthenticationProvider.php @@ -53,8 +53,9 @@ class PreAuthenticatedAuthenticationProvider implements AuthenticationProviderIn public function authenticate(TokenInterface $token) { if (!$this->supports($token)) { - return null; + return; } + if (!$user = $token->getUser()) { throw new BadCredentialsException('No pre-authenticated principal found in request.'); } diff --git a/Core/Authentication/Provider/UserAuthenticationProvider.php b/Core/Authentication/Provider/UserAuthenticationProvider.php index 18c3e70..4371abf 100644 --- a/Core/Authentication/Provider/UserAuthenticationProvider.php +++ b/Core/Authentication/Provider/UserAuthenticationProvider.php @@ -37,7 +37,7 @@ abstract class UserAuthenticationProvider implements AuthenticationProviderInter * * @param UserCheckerInterface $userChecker An UserCheckerInterface interface * @param string $providerKey A provider key - * @param Boolean $hideUserNotFoundExceptions Whether to hide user not found exception or not + * @param bool $hideUserNotFoundExceptions Whether to hide user not found exception or not * * @throws \InvalidArgumentException */ @@ -58,7 +58,7 @@ abstract class UserAuthenticationProvider implements AuthenticationProviderInter public function authenticate(TokenInterface $token) { if (!$this->supports($token)) { - return null; + return; } $username = $token->getUsername(); @@ -70,7 +70,7 @@ abstract class UserAuthenticationProvider implements AuthenticationProviderInter $user = $this->retrieveUser($username, $token); } catch (UsernameNotFoundException $notFound) { if ($this->hideUserNotFoundExceptions) { - throw new BadCredentialsException('Bad credentials', 0, $notFound); + throw new BadCredentialsException('Bad credentials.', 0, $notFound); } $notFound->setUsername($username); @@ -87,7 +87,7 @@ abstract class UserAuthenticationProvider implements AuthenticationProviderInter $this->userChecker->checkPostAuth($user); } catch (BadCredentialsException $e) { if ($this->hideUserNotFoundExceptions) { - throw new BadCredentialsException('Bad credentials', 0, $e); + throw new BadCredentialsException('Bad credentials.', 0, $e); } throw $e; diff --git a/Core/Authentication/Token/AbstractToken.php b/Core/Authentication/Token/AbstractToken.php index 5160fc5..b54d25e 100644 --- a/Core/Authentication/Token/AbstractToken.php +++ b/Core/Authentication/Token/AbstractToken.php @@ -127,7 +127,7 @@ abstract class AbstractToken implements TokenInterface */ public function setAuthenticated($authenticated) { - $this->authenticated = (Boolean) $authenticated; + $this->authenticated = (bool) $authenticated; } /** @@ -150,7 +150,7 @@ abstract class AbstractToken implements TokenInterface is_object($this->user) ? clone $this->user : $this->user, $this->authenticated, $this->roles, - $this->attributes + $this->attributes, ) ); } @@ -188,7 +188,7 @@ abstract class AbstractToken implements TokenInterface * * @param string $name The attribute name * - * @return Boolean true if the attribute exists, false otherwise + * @return bool true if the attribute exists, false otherwise */ public function hasAttribute($name) { @@ -225,7 +225,7 @@ abstract class AbstractToken implements TokenInterface } /** - * {@inheritDoc} + * {@inheritdoc} */ public function __toString() { @@ -247,7 +247,7 @@ abstract class AbstractToken implements TokenInterface } if ($this->user instanceof EquatableInterface) { - return ! (Boolean) $this->user->isEqualTo($user); + return ! (bool) $this->user->isEqualTo($user); } if ($this->user->getPassword() !== $user->getPassword()) { diff --git a/Core/Authentication/Token/AnonymousToken.php b/Core/Authentication/Token/AnonymousToken.php index d39fec8..571816c 100644 --- a/Core/Authentication/Token/AnonymousToken.php +++ b/Core/Authentication/Token/AnonymousToken.php @@ -57,7 +57,7 @@ class AnonymousToken extends AbstractToken } /** - * {@inheritDoc} + * {@inheritdoc} */ public function serialize() { @@ -65,7 +65,7 @@ class AnonymousToken extends AbstractToken } /** - * {@inheritDoc} + * {@inheritdoc} */ public function unserialize($serialized) { diff --git a/Core/Authentication/Token/Storage/TokenStorage.php b/Core/Authentication/Token/Storage/TokenStorage.php new file mode 100644 index 0000000..4b6c11f --- /dev/null +++ b/Core/Authentication/Token/Storage/TokenStorage.php @@ -0,0 +1,43 @@ +<?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\Authentication\Token\Storage; + +use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; + +/** + * TokenStorage contains a TokenInterface + * + * It gives access to the token representing the current user authentication. + * + * @author Fabien Potencier <fabien@symfony.com> + * @author Johannes M. Schmitt <schmittjoh@gmail.com> + */ +class TokenStorage implements TokenStorageInterface +{ + private $token; + + /** + * {@inheritdoc} + */ + public function getToken() + { + return $this->token; + } + + /** + * {@inheritdoc} + */ + public function setToken(TokenInterface $token = null) + { + $this->token = $token; + } +} diff --git a/Core/Authentication/Token/Storage/TokenStorageInterface.php b/Core/Authentication/Token/Storage/TokenStorageInterface.php new file mode 100644 index 0000000..218d750 --- /dev/null +++ b/Core/Authentication/Token/Storage/TokenStorageInterface.php @@ -0,0 +1,36 @@ +<?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\Authentication\Token\Storage; + +use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; + +/** + * The TokenStorageInterface. + * + * @author Johannes M. Schmitt <schmittjoh@gmail.com> + */ +interface TokenStorageInterface +{ + /** + * Returns the current security token. + * + * @return TokenInterface|null A TokenInterface instance or null if no authentication information is available + */ + public function getToken(); + + /** + * Sets the authentication token. + * + * @param TokenInterface $token A TokenInterface token, or null if no further authentication information should be stored + */ + public function setToken(TokenInterface $token = null); +} diff --git a/Core/Authentication/Token/TokenInterface.php b/Core/Authentication/Token/TokenInterface.php index 11f69da..8f7d03f 100644 --- a/Core/Authentication/Token/TokenInterface.php +++ b/Core/Authentication/Token/TokenInterface.php @@ -69,14 +69,14 @@ interface TokenInterface extends \Serializable /** * Returns whether the user is authenticated or not. * - * @return Boolean true if the token has been authenticated, false otherwise + * @return bool true if the token has been authenticated, false otherwise */ public function isAuthenticated(); /** * Sets the authenticated flag. * - * @param Boolean $isAuthenticated The authenticated flag + * @param bool $isAuthenticated The authenticated flag */ public function setAuthenticated($isAuthenticated); @@ -104,7 +104,7 @@ interface TokenInterface extends \Serializable * * @param string $name The attribute name * - * @return Boolean true if the attribute exists, false otherwise + * @return bool true if the attribute exists, false otherwise */ public function hasAttribute($name); diff --git a/Core/Authorization/AccessDecisionManager.php b/Core/Authorization/AccessDecisionManager.php index 9445440..84856f6 100644 --- a/Core/Authorization/AccessDecisionManager.php +++ b/Core/Authorization/AccessDecisionManager.php @@ -36,8 +36,8 @@ class AccessDecisionManager implements AccessDecisionManagerInterface * * @param VoterInterface[] $voters An array of VoterInterface instances * @param string $strategy The vote strategy - * @param Boolean $allowIfAllAbstainDecisions Whether to grant access if all voters abstained or not - * @param Boolean $allowIfEqualGrantedDeniedDecisions Whether to grant access if result are equals + * @param bool $allowIfAllAbstainDecisions Whether to grant access if all voters abstained or not + * @param bool $allowIfEqualGrantedDeniedDecisions Whether to grant access if result are equals * * @throws \InvalidArgumentException */ @@ -54,8 +54,8 @@ class AccessDecisionManager implements AccessDecisionManagerInterface $this->voters = $voters; $this->strategy = $strategyMethod; - $this->allowIfAllAbstainDecisions = (Boolean) $allowIfAllAbstainDecisions; - $this->allowIfEqualGrantedDeniedDecisions = (Boolean) $allowIfEqualGrantedDeniedDecisions; + $this->allowIfAllAbstainDecisions = (bool) $allowIfAllAbstainDecisions; + $this->allowIfEqualGrantedDeniedDecisions = (bool) $allowIfEqualGrantedDeniedDecisions; } /** diff --git a/Core/Authorization/AccessDecisionManagerInterface.php b/Core/Authorization/AccessDecisionManagerInterface.php index 742ea74..ec82800 100644 --- a/Core/Authorization/AccessDecisionManagerInterface.php +++ b/Core/Authorization/AccessDecisionManagerInterface.php @@ -27,7 +27,7 @@ interface AccessDecisionManagerInterface * @param array $attributes An array of attributes associated with the method being invoked * @param object $object The object to secure * - * @return Boolean true if the access is granted, false otherwise + * @return bool true if the access is granted, false otherwise */ public function decide(TokenInterface $token, array $attributes, $object = null); @@ -36,7 +36,7 @@ interface AccessDecisionManagerInterface * * @param string $attribute An attribute * - * @return Boolean true if this decision manager supports the attribute, false otherwise + * @return bool true if this decision manager supports the attribute, false otherwise */ public function supportsAttribute($attribute); diff --git a/Core/Authorization/AuthorizationChecker.php b/Core/Authorization/AuthorizationChecker.php new file mode 100644 index 0000000..23c190c --- /dev/null +++ b/Core/Authorization/AuthorizationChecker.php @@ -0,0 +1,70 @@ +<?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\Authorization; + +use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface; +use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; +use Symfony\Component\Security\Core\Exception\AuthenticationCredentialsNotFoundException; + +/** + * AuthorizationChecker is the main authorization point of the Security component. + * + * It gives access to the token representing the current user authentication. + * + * @author Fabien Potencier <fabien@symfony.com> + * @author Johannes M. Schmitt <schmittjoh@gmail.com> + */ +class AuthorizationChecker implements AuthorizationCheckerInterface +{ + private $tokenStorage; + private $accessDecisionManager; + private $authenticationManager; + private $alwaysAuthenticate; + + /** + * Constructor. + * + * @param TokenStorageInterface $tokenStorage + * @param AuthenticationManagerInterface $authenticationManager An AuthenticationManager instance + * @param AccessDecisionManagerInterface $accessDecisionManager An AccessDecisionManager instance + * @param bool $alwaysAuthenticate + */ + public function __construct(TokenStorageInterface $tokenStorage, AuthenticationManagerInterface $authenticationManager, AccessDecisionManagerInterface $accessDecisionManager, $alwaysAuthenticate = false) + { + $this->tokenStorage = $tokenStorage; + $this->authenticationManager = $authenticationManager; + $this->accessDecisionManager = $accessDecisionManager; + $this->alwaysAuthenticate = $alwaysAuthenticate; + } + + /** + * {@inheritdoc} + * + * @throws AuthenticationCredentialsNotFoundException when the token storage has no authentication token. + */ + final public function isGranted($attributes, $object = null) + { + if (null === ($token = $this->tokenStorage->getToken())) { + throw new AuthenticationCredentialsNotFoundException('The token storage contains no authentication token. One possible reason may be that there is no firewall configured for this URL.'); + } + + if ($this->alwaysAuthenticate || !$token->isAuthenticated()) { + $this->tokenStorage->setToken($token = $this->authenticationManager->authenticate($token)); + } + + if (!is_array($attributes)) { + $attributes = array($attributes); + } + + return $this->accessDecisionManager->decide($token, $attributes, $object); + } +} diff --git a/Core/Authorization/AuthorizationCheckerInterface.php b/Core/Authorization/AuthorizationCheckerInterface.php new file mode 100644 index 0000000..bd24d6f --- /dev/null +++ b/Core/Authorization/AuthorizationCheckerInterface.php @@ -0,0 +1,30 @@ +<?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\Authorization; + +/** + * The AuthorizationCheckerInterface. + * + * @author Johannes M. Schmitt <schmittjoh@gmail.com> + */ +interface AuthorizationCheckerInterface +{ + /** + * Checks if the attributes are granted against the current authentication token and optionally supplied object. + * + * @param mixed $attributes + * @param mixed $object + * + * @return bool + */ + public function isGranted($attributes, $object = null); +} diff --git a/Core/Authorization/Voter/AbstractVoter.php b/Core/Authorization/Voter/AbstractVoter.php new file mode 100644 index 0000000..61c928e --- /dev/null +++ b/Core/Authorization/Voter/AbstractVoter.php @@ -0,0 +1,113 @@ +<?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\Authorization\Voter; + +use Symfony\Component\Security\Core\User\UserInterface; +use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; + +/** + * Abstract Voter implementation that reduces boilerplate code required to create a custom Voter + * + * @author Roman Marintšenko <inoryy@gmail.com> + */ +abstract class AbstractVoter implements VoterInterface +{ + /** + * {@inheritdoc} + */ + public function supportsAttribute($attribute) + { + return in_array($attribute, $this->getSupportedAttributes()); + } + + /** + * {@inheritdoc} + */ + public function supportsClass($class) + { + foreach ($this->getSupportedClasses() as $supportedClass) { + if ($supportedClass === $class || is_subclass_of($class, $supportedClass)) { + return true; + } + } + + return false; + } + + /** + * Iteratively check all given attributes by calling isGranted + * + * This method terminates as soon as it is able to return ACCESS_GRANTED + * If at least one attribute is supported, but access not granted, then ACCESS_DENIED is returned + * Otherwise it will return ACCESS_ABSTAIN + * + * @param TokenInterface $token A TokenInterface instance + * @param object $object The object to secure + * @param array $attributes An array of attributes associated with the method being invoked + * + * @return int either ACCESS_GRANTED, ACCESS_ABSTAIN, or ACCESS_DENIED + */ + public function vote(TokenInterface $token, $object, array $attributes) + { + if (!$object || !$this->supportsClass(get_class($object))) { + return self::ACCESS_ABSTAIN; + } + + // abstain vote by default in case none of the attributes are supported + $vote = self::ACCESS_ABSTAIN; + + foreach ($attributes as $attribute) { + if (!$this->supportsAttribute($attribute)) { + continue; + } + + // as soon as at least one attribute is supported, default is to deny access + $vote = self::ACCESS_DENIED; + + if ($this->isGranted($attribute, $object, $token->getUser())) { + // grant access as soon as at least one voter returns a positive response + return self::ACCESS_GRANTED; + } + } + + return $vote; + } + + /** + * Return an array of supported classes. This will be called by supportsClass + * + * @return array an array of supported classes, i.e. ['\Acme\DemoBundle\Model\Product'] + */ + abstract protected function getSupportedClasses(); + + /** + * Return an array of supported attributes. This will be called by supportsAttribute + * + * @return array an array of supported attributes, i.e. ['CREATE', 'READ'] + */ + abstract protected function getSupportedAttributes(); + + /** + * Perform a single access check operation on a given attribute, object and (optionally) user + * It is safe to assume that $attribute and $object's class pass supportsAttribute/supportsClass + * $user can be one of the following: + * a UserInterface object (fully authenticated user) + * a string (anonymously authenticated user) + * + * @param string $attribute + * @param object $object + * @param UserInterface|string $user + * + * @return bool + */ + abstract protected function isGranted($attribute, $object, $user = null); +} diff --git a/Core/Authorization/Voter/ExpressionVoter.php b/Core/Authorization/Voter/ExpressionVoter.php index bf6683d..3263803 100644 --- a/Core/Authorization/Voter/ExpressionVoter.php +++ b/Core/Authorization/Voter/ExpressionVoter.php @@ -34,7 +34,7 @@ class ExpressionVoter implements VoterInterface * * @param ExpressionLanguage $expressionLanguage * @param AuthenticationTrustResolverInterface $trustResolver - * @param RoleHierarchyInterface $roleHierarchy + * @param RoleHierarchyInterface|null $roleHierarchy */ public function __construct(ExpressionLanguage $expressionLanguage, AuthenticationTrustResolverInterface $trustResolver, RoleHierarchyInterface $roleHierarchy = null) { diff --git a/Core/Authorization/Voter/VoterInterface.php b/Core/Authorization/Voter/VoterInterface.php index 0840c1c..79fa69f 100644 --- a/Core/Authorization/Voter/VoterInterface.php +++ b/Core/Authorization/Voter/VoterInterface.php @@ -29,7 +29,7 @@ interface VoterInterface * * @param string $attribute An attribute * - * @return Boolean true if this Voter supports the attribute, false otherwise + * @return bool true if this Voter supports the attribute, false otherwise */ public function supportsAttribute($attribute); @@ -38,7 +38,7 @@ interface VoterInterface * * @param string $class A class name * - * @return Boolean true if this Voter can process the class + * @return bool true if this Voter can process the class */ public function supportsClass($class); @@ -49,10 +49,10 @@ interface VoterInterface * ACCESS_GRANTED, ACCESS_DENIED, or ACCESS_ABSTAIN. * * @param TokenInterface $token A TokenInterface instance - * @param object $object The object to secure + * @param object|null $object The object to secure * @param array $attributes An array of attributes associated with the method being invoked * - * @return integer either ACCESS_GRANTED, ACCESS_ABSTAIN, or ACCESS_DENIED + * @return int either ACCESS_GRANTED, ACCESS_ABSTAIN, or ACCESS_DENIED */ public function vote(TokenInterface $token, $object, array $attributes); } diff --git a/Core/Encoder/BCryptPasswordEncoder.php b/Core/Encoder/BCryptPasswordEncoder.php index 5a0f122..27a7334 100644 --- a/Core/Encoder/BCryptPasswordEncoder.php +++ b/Core/Encoder/BCryptPasswordEncoder.php @@ -27,7 +27,7 @@ class BCryptPasswordEncoder extends BasePasswordEncoder /** * Constructor. * - * @param integer $cost The algorithmic cost that should be used + * @param int $cost The algorithmic cost that should be used * * @throws \RuntimeException When no BCrypt encoder is available * @throws \InvalidArgumentException if cost is out of range @@ -61,6 +61,8 @@ class BCryptPasswordEncoder extends BasePasswordEncoder * * @return string The encoded password * + * @throws BadCredentialsException when the given password is too long + * * @link http://lxr.php.net/xref/PHP_5_5/ext/standard/password.c#111 */ public function encodePassword($raw, $salt) diff --git a/Core/Encoder/BasePasswordEncoder.php b/Core/Encoder/BasePasswordEncoder.php index aa29876..0d29631 100644 --- a/Core/Encoder/BasePasswordEncoder.php +++ b/Core/Encoder/BasePasswordEncoder.php @@ -79,7 +79,7 @@ abstract class BasePasswordEncoder implements PasswordEncoderInterface * @param string $password1 The first password * @param string $password2 The second password * - * @return Boolean true if the two passwords are the same, false otherwise + * @return bool true if the two passwords are the same, false otherwise */ protected function comparePasswords($password1, $password2) { @@ -89,9 +89,9 @@ abstract class BasePasswordEncoder implements PasswordEncoderInterface /** * Checks if the password is too long. * - * @param string $password The password + * @param string $password The password to check * - * @return Boolean true if the password is too long, false otherwise + * @return bool true if the password is too long, false otherwise */ protected function isPasswordTooLong($password) { diff --git a/Core/Encoder/EncoderFactory.php b/Core/Encoder/EncoderFactory.php index bb5ba91..77021ec 100644 --- a/Core/Encoder/EncoderFactory.php +++ b/Core/Encoder/EncoderFactory.php @@ -26,7 +26,7 @@ class EncoderFactory implements EncoderFactoryInterface } /** - * {@inheritDoc} + * {@inheritdoc} */ public function getEncoder($user) { diff --git a/Core/Encoder/MessageDigestPasswordEncoder.php b/Core/Encoder/MessageDigestPasswordEncoder.php index a7e5546..9aa240a 100644 --- a/Core/Encoder/MessageDigestPasswordEncoder.php +++ b/Core/Encoder/MessageDigestPasswordEncoder.php @@ -28,8 +28,8 @@ class MessageDigestPasswordEncoder extends BasePasswordEncoder * Constructor. * * @param string $algorithm The digest algorithm to use - * @param Boolean $encodeHashAsBase64 Whether to base64 encode the password hash - * @param integer $iterations The number of iterations to use to stretch the password hash + * @param bool $encodeHashAsBase64 Whether to base64 encode the password hash + * @param int $iterations The number of iterations to use to stretch the password hash */ public function __construct($algorithm = 'sha512', $encodeHashAsBase64 = true, $iterations = 5000) { diff --git a/Core/Encoder/PasswordEncoderInterface.php b/Core/Encoder/PasswordEncoderInterface.php index 78b4e42..23acaf3 100644 --- a/Core/Encoder/PasswordEncoderInterface.php +++ b/Core/Encoder/PasswordEncoderInterface.php @@ -35,7 +35,7 @@ interface PasswordEncoderInterface * @param string $raw A raw password * @param string $salt The salt * - * @return Boolean true if the password is valid, false otherwise + * @return bool true if the password is valid, false otherwise */ public function isPasswordValid($encoded, $raw, $salt); } diff --git a/Core/Encoder/Pbkdf2PasswordEncoder.php b/Core/Encoder/Pbkdf2PasswordEncoder.php index 8a5a958..55b5261 100644 --- a/Core/Encoder/Pbkdf2PasswordEncoder.php +++ b/Core/Encoder/Pbkdf2PasswordEncoder.php @@ -37,9 +37,9 @@ class Pbkdf2PasswordEncoder extends BasePasswordEncoder * Constructor. * * @param string $algorithm The digest algorithm to use - * @param Boolean $encodeHashAsBase64 Whether to base64 encode the password hash - * @param integer $iterations The number of iterations to use to stretch the password hash - * @param integer $length Length of derived key to create + * @param bool $encodeHashAsBase64 Whether to base64 encode the password hash + * @param int $iterations The number of iterations to use to stretch the password hash + * @param int $length Length of derived key to create */ public function __construct($algorithm = 'sha512', $encodeHashAsBase64 = true, $iterations = 1000, $length = 40) { diff --git a/Core/Encoder/PlaintextPasswordEncoder.php b/Core/Encoder/PlaintextPasswordEncoder.php index 22f3da4..bdb058a 100644 --- a/Core/Encoder/PlaintextPasswordEncoder.php +++ b/Core/Encoder/PlaintextPasswordEncoder.php @@ -25,7 +25,7 @@ class PlaintextPasswordEncoder extends BasePasswordEncoder /** * Constructor. * - * @param Boolean $ignorePasswordCase Compare password case-insensitive + * @param bool $ignorePasswordCase Compare password case-insensitive */ public function __construct($ignorePasswordCase = false) { diff --git a/Core/Encoder/UserPasswordEncoder.php b/Core/Encoder/UserPasswordEncoder.php new file mode 100644 index 0000000..13ee835 --- /dev/null +++ b/Core/Encoder/UserPasswordEncoder.php @@ -0,0 +1,55 @@ +<?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\Encoder; + +use Symfony\Component\Security\Core\User\UserInterface; + +/** + * A generic password encoder + * + * @author Ariel Ferrandini <arielferrandini@gmail.com> + */ +class UserPasswordEncoder implements UserPasswordEncoderInterface +{ + /** + * @var EncoderFactoryInterface + */ + private $encoderFactory; + + /** + * @param EncoderFactoryInterface $encoderFactory The encoder factory + */ + public function __construct(EncoderFactoryInterface $encoderFactory) + { + $this->encoderFactory = $encoderFactory; + } + + /** + * {@inheritdoc} + */ + public function encodePassword(UserInterface $user, $plainPassword) + { + $encoder = $this->encoderFactory->getEncoder($user); + + return $encoder->encodePassword($plainPassword, $user->getSalt()); + } + + /** + * {@inheritdoc} + */ + public function isPasswordValid(UserInterface $user, $raw) + { + $encoder = $this->encoderFactory->getEncoder($user); + + return $encoder->isPasswordValid($user->getPassword(), $raw, $user->getSalt()); + } +} diff --git a/Core/Encoder/UserPasswordEncoderInterface.php b/Core/Encoder/UserPasswordEncoderInterface.php new file mode 100644 index 0000000..39e906a --- /dev/null +++ b/Core/Encoder/UserPasswordEncoderInterface.php @@ -0,0 +1,41 @@ +<?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\Encoder; + +use Symfony\Component\Security\Core\User\UserInterface; + +/** + * UserPasswordEncoderInterface is the interface for the password encoder service. + * + * @author Ariel Ferrandini <arielferrandini@gmail.com> + */ +interface UserPasswordEncoderInterface +{ + /** + * + * Encodes the plain password. + * + * @param UserInterface $user The user + * @param string $plainPassword The password to encode + * + * @return string The encoded password + */ + public function encodePassword(UserInterface $user, $plainPassword); + + /** + * @param UserInterface $user The user + * @param string $raw A raw password + * + * @return bool true if the password is valid, false otherwise + */ + public function isPasswordValid(UserInterface $user, $raw); +} diff --git a/Core/Exception/AccountExpiredException.php b/Core/Exception/AccountExpiredException.php index a5618ce..4a71263 100644 --- a/Core/Exception/AccountExpiredException.php +++ b/Core/Exception/AccountExpiredException.php @@ -20,7 +20,7 @@ namespace Symfony\Component\Security\Core\Exception; class AccountExpiredException extends AccountStatusException { /** - * {@inheritDoc} + * {@inheritdoc} */ public function getMessageKey() { diff --git a/Core/Exception/AccountStatusException.php b/Core/Exception/AccountStatusException.php index 7819e4d..9b29f63 100644 --- a/Core/Exception/AccountStatusException.php +++ b/Core/Exception/AccountStatusException.php @@ -45,7 +45,7 @@ abstract class AccountStatusException extends AuthenticationException } /** - * {@inheritDoc} + * {@inheritdoc} */ public function serialize() { @@ -56,7 +56,7 @@ abstract class AccountStatusException extends AuthenticationException } /** - * {@inheritDoc} + * {@inheritdoc} */ public function unserialize($str) { diff --git a/Core/Exception/AuthenticationCredentialsNotFoundException.php b/Core/Exception/AuthenticationCredentialsNotFoundException.php index 633b2be..8595bed 100644 --- a/Core/Exception/AuthenticationCredentialsNotFoundException.php +++ b/Core/Exception/AuthenticationCredentialsNotFoundException.php @@ -21,7 +21,7 @@ namespace Symfony\Component\Security\Core\Exception; class AuthenticationCredentialsNotFoundException extends AuthenticationException { /** - * {@inheritDoc} + * {@inheritdoc} */ public function getMessageKey() { diff --git a/Core/Exception/AuthenticationServiceException.php b/Core/Exception/AuthenticationServiceException.php index 758a4f0..66f051d 100644 --- a/Core/Exception/AuthenticationServiceException.php +++ b/Core/Exception/AuthenticationServiceException.php @@ -20,7 +20,7 @@ namespace Symfony\Component\Security\Core\Exception; class AuthenticationServiceException extends AuthenticationException { /** - * {@inheritDoc} + * {@inheritdoc} */ public function getMessageKey() { diff --git a/Core/Exception/BadCredentialsException.php b/Core/Exception/BadCredentialsException.php index 5deecca..be061c7 100644 --- a/Core/Exception/BadCredentialsException.php +++ b/Core/Exception/BadCredentialsException.php @@ -20,7 +20,7 @@ namespace Symfony\Component\Security\Core\Exception; class BadCredentialsException extends AuthenticationException { /** - * {@inheritDoc} + * {@inheritdoc} */ public function getMessageKey() { diff --git a/Core/Exception/CookieTheftException.php b/Core/Exception/CookieTheftException.php index 8d9e154..af97168 100644 --- a/Core/Exception/CookieTheftException.php +++ b/Core/Exception/CookieTheftException.php @@ -21,7 +21,7 @@ namespace Symfony\Component\Security\Core\Exception; class CookieTheftException extends AuthenticationException { /** - * {@inheritDoc} + * {@inheritdoc} */ public function getMessageKey() { diff --git a/Core/Exception/CredentialsExpiredException.php b/Core/Exception/CredentialsExpiredException.php index b9bf2d1..bcc1267 100644 --- a/Core/Exception/CredentialsExpiredException.php +++ b/Core/Exception/CredentialsExpiredException.php @@ -20,7 +20,7 @@ namespace Symfony\Component\Security\Core\Exception; class CredentialsExpiredException extends AccountStatusException { /** - * {@inheritDoc} + * {@inheritdoc} */ public function getMessageKey() { diff --git a/Core/Exception/DisabledException.php b/Core/Exception/DisabledException.php index 5571ab1..e9b784f 100644 --- a/Core/Exception/DisabledException.php +++ b/Core/Exception/DisabledException.php @@ -20,7 +20,7 @@ namespace Symfony\Component\Security\Core\Exception; class DisabledException extends AccountStatusException { /** - * {@inheritDoc} + * {@inheritdoc} */ public function getMessageKey() { diff --git a/Core/Exception/InsufficientAuthenticationException.php b/Core/Exception/InsufficientAuthenticationException.php index 74fc2b9..e33ef6a 100644 --- a/Core/Exception/InsufficientAuthenticationException.php +++ b/Core/Exception/InsufficientAuthenticationException.php @@ -22,7 +22,7 @@ namespace Symfony\Component\Security\Core\Exception; class InsufficientAuthenticationException extends AuthenticationException { /** - * {@inheritDoc} + * {@inheritdoc} */ public function getMessageKey() { diff --git a/Core/Exception/InvalidCsrfTokenException.php b/Core/Exception/InvalidCsrfTokenException.php index ce0e1f4..84be855 100644 --- a/Core/Exception/InvalidCsrfTokenException.php +++ b/Core/Exception/InvalidCsrfTokenException.php @@ -20,7 +20,7 @@ namespace Symfony\Component\Security\Core\Exception; class InvalidCsrfTokenException extends AuthenticationException { /** - * {@inheritDoc} + * {@inheritdoc} */ public function getMessageKey() { diff --git a/Core/Exception/LockedException.php b/Core/Exception/LockedException.php index 6532f70..fffae74 100644 --- a/Core/Exception/LockedException.php +++ b/Core/Exception/LockedException.php @@ -20,7 +20,7 @@ namespace Symfony\Component\Security\Core\Exception; class LockedException extends AccountStatusException { /** - * {@inheritDoc} + * {@inheritdoc} */ public function getMessageKey() { diff --git a/Core/Exception/NonceExpiredException.php b/Core/Exception/NonceExpiredException.php index 2f6681f..998e987 100644 --- a/Core/Exception/NonceExpiredException.php +++ b/Core/Exception/NonceExpiredException.php @@ -21,7 +21,7 @@ namespace Symfony\Component\Security\Core\Exception; class NonceExpiredException extends AuthenticationException { /** - * {@inheritDoc} + * {@inheritdoc} */ public function getMessageKey() { diff --git a/Core/Exception/ProviderNotFoundException.php b/Core/Exception/ProviderNotFoundException.php index ea2b1fd..af2e1b5 100644 --- a/Core/Exception/ProviderNotFoundException.php +++ b/Core/Exception/ProviderNotFoundException.php @@ -21,7 +21,7 @@ namespace Symfony\Component\Security\Core\Exception; class ProviderNotFoundException extends AuthenticationException { /** - * {@inheritDoc} + * {@inheritdoc} */ public function getMessageKey() { diff --git a/Core/Exception/SessionUnavailableException.php b/Core/Exception/SessionUnavailableException.php index 4b47b18..90b858a 100644 --- a/Core/Exception/SessionUnavailableException.php +++ b/Core/Exception/SessionUnavailableException.php @@ -26,7 +26,7 @@ namespace Symfony\Component\Security\Core\Exception; class SessionUnavailableException extends AuthenticationException { /** - * {@inheritDoc} + * {@inheritdoc} */ public function getMessageKey() { diff --git a/Core/Exception/TokenNotFoundException.php b/Core/Exception/TokenNotFoundException.php index fb85abf..b050302 100644 --- a/Core/Exception/TokenNotFoundException.php +++ b/Core/Exception/TokenNotFoundException.php @@ -20,7 +20,7 @@ namespace Symfony\Component\Security\Core\Exception; class TokenNotFoundException extends AuthenticationException { /** - * {@inheritDoc} + * {@inheritdoc} */ public function getMessageKey() { diff --git a/Core/Exception/UsernameNotFoundException.php b/Core/Exception/UsernameNotFoundException.php index f656bac..6979389 100644 --- a/Core/Exception/UsernameNotFoundException.php +++ b/Core/Exception/UsernameNotFoundException.php @@ -22,7 +22,7 @@ class UsernameNotFoundException extends AuthenticationException private $username; /** - * {@inheritDoc} + * {@inheritdoc} */ public function getMessageKey() { @@ -50,7 +50,7 @@ class UsernameNotFoundException extends AuthenticationException } /** - * {@inheritDoc} + * {@inheritdoc} */ public function serialize() { @@ -61,7 +61,7 @@ class UsernameNotFoundException extends AuthenticationException } /** - * {@inheritDoc} + * {@inheritdoc} */ public function unserialize($str) { @@ -69,4 +69,12 @@ class UsernameNotFoundException extends AuthenticationException parent::unserialize($parentData); } + + /** + * {@inheritdoc} + */ + public function getMessageData() + { + return array('{{ username }}' => $this->username); + } } diff --git a/Core/README.md b/Core/README.md index 4585a5d..66c323e 100644 --- a/Core/README.md +++ b/Core/README.md @@ -11,7 +11,7 @@ Resources Documentation: -http://symfony.com/doc/2.5/book/security.html +http://symfony.com/doc/2.6/book/security.html Tests ----- diff --git a/Core/SecurityContext.php b/Core/SecurityContext.php index c55cecf..1f46cd6 100644 --- a/Core/SecurityContext.php +++ b/Core/SecurityContext.php @@ -11,10 +11,13 @@ namespace Symfony\Component\Security\Core; -use Symfony\Component\Security\Core\Exception\AuthenticationCredentialsNotFoundException; -use Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface; use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface; +use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage; +use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; +use Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface; +use Symfony\Component\Security\Core\Authorization\AuthorizationChecker; +use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface; /** * SecurityContext is the main entry point of the Security component. @@ -23,63 +26,76 @@ use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; * * @author Fabien Potencier <fabien@symfony.com> * @author Johannes M. Schmitt <schmittjoh@gmail.com> + * @deprecated Deprecated since version 2.6, to be removed in 3.0. */ class SecurityContext implements SecurityContextInterface { - private $token; - private $accessDecisionManager; - private $authenticationManager; - private $alwaysAuthenticate; + /** + * @var TokenStorageInterface + */ + private $tokenStorage; /** - * Constructor. - * - * @param AuthenticationManagerInterface $authenticationManager An AuthenticationManager instance - * @param AccessDecisionManagerInterface|null $accessDecisionManager An AccessDecisionManager instance - * @param Boolean $alwaysAuthenticate + * @var AuthorizationCheckerInterface */ - public function __construct(AuthenticationManagerInterface $authenticationManager, AccessDecisionManagerInterface $accessDecisionManager, $alwaysAuthenticate = false) - { - $this->authenticationManager = $authenticationManager; - $this->accessDecisionManager = $accessDecisionManager; - $this->alwaysAuthenticate = $alwaysAuthenticate; - } + private $authorizationChecker; /** - * {@inheritdoc} + * For backwords compatibility, the signature of sf <2.6 still works * - * @throws AuthenticationCredentialsNotFoundException when the security context has no authentication token. + * @param TokenStorageInterface|AuthenticationManagerInterface $tokenStorage + * @param AuthorizationCheckerInterface|AccessDecisionManagerInterface $authorizationChecker + * @param bool $alwaysAuthenticate only applicable with old signature */ - final public function isGranted($attributes, $object = null) + public function __construct($tokenStorage, $authorizationChecker, $alwaysAuthenticate = false) { - if (null === $this->token) { - throw new AuthenticationCredentialsNotFoundException('The security context contains no authentication token. One possible reason may be that there is no firewall configured for this URL.'); - } + $oldSignature = $tokenStorage instanceof AuthenticationManagerInterface && $authorizationChecker instanceof AccessDecisionManagerInterface; + $newSignature = $tokenStorage instanceof TokenStorageInterface && $authorizationChecker instanceof AuthorizationCheckerInterface; - if ($this->alwaysAuthenticate || !$this->token->isAuthenticated()) { - $this->token = $this->authenticationManager->authenticate($this->token); + // confirm possible signatures + if (!$oldSignature && !$newSignature) { + throw new \BadMethodCallException('Unable to construct SecurityContext, please provide the correct arguments'); } - if (!is_array($attributes)) { - $attributes = array($attributes); + if ($oldSignature) { + // renamed for clearity + $authenticationManager = $tokenStorage; + $accessDecisionManager = $authorizationChecker; + $tokenStorage = new TokenStorage(); + $authorizationChecker = new AuthorizationChecker($tokenStorage, $authenticationManager, $accessDecisionManager, $alwaysAuthenticate); } - return $this->accessDecisionManager->decide($this->token, $attributes, $object); + $this->tokenStorage = $tokenStorage; + $this->authorizationChecker = $authorizationChecker; } /** + * @deprecated Deprecated since version 2.6, to be removed in 3.0. Use TokenStorageInterface::getToken() instead. + * * {@inheritdoc} */ public function getToken() { - return $this->token; + return $this->tokenStorage->getToken(); } /** + * @deprecated Deprecated since version 2.6, to be removed in 3.0. Use TokenStorageInterface::setToken() instead. + * * {@inheritdoc} */ public function setToken(TokenInterface $token = null) { - $this->token = $token; + return $this->tokenStorage->setToken($token); + } + + /** + * @deprecated Deprecated since version 2.6, to be removed in 3.0. Use AuthorizationCheckerInterface::isGranted() instead. + * + * {@inheritdoc} + */ + public function isGranted($attributes, $object = null) + { + return $this->authorizationChecker->isGranted($attributes, $object); } } diff --git a/Core/SecurityContextInterface.php b/Core/SecurityContextInterface.php index 434f9a5..844482b 100644 --- a/Core/SecurityContextInterface.php +++ b/Core/SecurityContextInterface.php @@ -11,40 +11,15 @@ namespace Symfony\Component\Security\Core; -use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; +use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; +use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface; /** * The SecurityContextInterface. * * @author Johannes M. Schmitt <schmittjoh@gmail.com> + * @deprecated Deprecated since version 2.6, to be removed in 3.0. */ -interface SecurityContextInterface +interface SecurityContextInterface extends TokenStorageInterface, AuthorizationCheckerInterface, SecuritySessionStorageInterface { - const ACCESS_DENIED_ERROR = '_security.403_error'; - const AUTHENTICATION_ERROR = '_security.last_error'; - const LAST_USERNAME = '_security.last_username'; - - /** - * Returns the current security token. - * - * @return TokenInterface|null A TokenInterface instance or null if no authentication information is available - */ - public function getToken(); - - /** - * Sets the authentication token. - * - * @param TokenInterface $token A TokenInterface token, or null if no further authentication information should be stored - */ - public function setToken(TokenInterface $token = null); - - /** - * Checks if the attributes are granted against the current authentication token and optionally supplied object. - * - * @param mixed $attributes - * @param mixed $object - * - * @return Boolean - */ - public function isGranted($attributes, $object = null); } diff --git a/Core/SecuritySessionStorageInterface.php b/Core/SecuritySessionStorageInterface.php new file mode 100644 index 0000000..47c0bbe --- /dev/null +++ b/Core/SecuritySessionStorageInterface.php @@ -0,0 +1,24 @@ +<?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; + +/** + * The SecuritySessionStorageInterface. + * + * @author Johannes M. Schmitt <schmittjoh@gmail.com> + */ +interface SecuritySessionStorageInterface +{ + const ACCESS_DENIED_ERROR = '_security.403_error'; + const AUTHENTICATION_ERROR = '_security.last_error'; + const LAST_USERNAME = '_security.last_username'; +} diff --git a/Core/Tests/Authentication/AuthenticationProviderManagerTest.php b/Core/Tests/Authentication/AuthenticationProviderManagerTest.php index f63ef79..df25874 100644 --- a/Core/Tests/Authentication/AuthenticationProviderManagerTest.php +++ b/Core/Tests/Authentication/AuthenticationProviderManagerTest.php @@ -129,7 +129,7 @@ class AuthenticationProviderManagerTest extends \PHPUnit_Framework_TestCase } elseif (null !== $exception) { $provider->expects($this->once()) ->method('authenticate') - ->will($this->throwException($this->getMock($exception, null, array(), '', true))) + ->will($this->throwException($this->getMock($exception, null, array(), ''))) ; } diff --git a/Core/Tests/Authentication/Token/AbstractTokenTest.php b/Core/Tests/Authentication/Token/AbstractTokenTest.php index 098017e..6f2b6ed 100644 --- a/Core/Tests/Authentication/Token/AbstractTokenTest.php +++ b/Core/Tests/Authentication/Token/AbstractTokenTest.php @@ -52,7 +52,9 @@ class ConcreteToken extends AbstractToken parent::unserialize($parentStr); } - public function getCredentials() {} + public function getCredentials() + { + } } class AbstractTokenTest extends \PHPUnit_Framework_TestCase @@ -227,13 +229,13 @@ class AbstractTokenTest extends \PHPUnit_Framework_TestCase 'foo', $user, ), array( - 'foo', $advancedUser + 'foo', $advancedUser, ), array( - $user, 'foo' + $user, 'foo', ), array( - $advancedUser, 'foo' + $advancedUser, 'foo', ), array( $user, new TestUser('foo'), @@ -254,10 +256,10 @@ class AbstractTokenTest extends \PHPUnit_Framework_TestCase new TestUser('foo'), $advancedUser, ), array( - $user, $advancedUser + $user, $advancedUser, ), array( - $advancedUser, $user + $advancedUser, $user, ), ); } diff --git a/Core/Tests/Authentication/Token/Storage/TokenStorageTest.php b/Core/Tests/Authentication/Token/Storage/TokenStorageTest.php new file mode 100644 index 0000000..d06e3f0 --- /dev/null +++ b/Core/Tests/Authentication/Token/Storage/TokenStorageTest.php @@ -0,0 +1,26 @@ +<?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\Tests\Authentication\Token\Storage; + +use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage; + +class TokenStorageTest extends \PHPUnit_Framework_TestCase +{ + public function testGetSetToken() + { + $tokenStorage = new TokenStorage(); + $this->assertNull($tokenStorage->getToken()); + $token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface'); + $tokenStorage->setToken($token); + $this->assertSame($token, $tokenStorage->getToken()); + } +} diff --git a/Core/Tests/Authorization/AccessDecisionManagerTest.php b/Core/Tests/Authorization/AccessDecisionManagerTest.php index e182838..bf0ad11 100644 --- a/Core/Tests/Authorization/AccessDecisionManagerTest.php +++ b/Core/Tests/Authorization/AccessDecisionManagerTest.php @@ -73,6 +73,48 @@ class AccessDecisionManagerTest extends \PHPUnit_Framework_TestCase $this->assertSame($expected, $manager->decide($token, array('ROLE_FOO'))); } + /** + * @dataProvider getStrategiesWith2RolesTests + */ + public function testStrategiesWith2Roles($token, $strategy, $voter, $expected) + { + $manager = new AccessDecisionManager(array($voter), $strategy); + + $this->assertSame($expected, $manager->decide($token, array('ROLE_FOO', 'ROLE_BAR'))); + } + + public function getStrategiesWith2RolesTests() + { + $token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface'); + + return array( + array($token, 'affirmative', $this->getVoter(VoterInterface::ACCESS_DENIED), false), + array($token, 'affirmative', $this->getVoter(VoterInterface::ACCESS_GRANTED), true), + + array($token, 'consensus', $this->getVoter(VoterInterface::ACCESS_DENIED), false), + array($token, 'consensus', $this->getVoter(VoterInterface::ACCESS_GRANTED), true), + + array($token, 'unanimous', $this->getVoterFor2Roles($token, VoterInterface::ACCESS_DENIED, VoterInterface::ACCESS_DENIED), false), + array($token, 'unanimous', $this->getVoterFor2Roles($token, VoterInterface::ACCESS_DENIED, VoterInterface::ACCESS_GRANTED), false), + array($token, 'unanimous', $this->getVoterFor2Roles($token, VoterInterface::ACCESS_GRANTED, VoterInterface::ACCESS_DENIED), false), + array($token, 'unanimous', $this->getVoterFor2Roles($token, VoterInterface::ACCESS_GRANTED, VoterInterface::ACCESS_GRANTED), true), + ); + } + + protected function getVoterFor2Roles($token, $vote1, $vote2) + { + $voter = $this->getMock('Symfony\Component\Security\Core\Authorization\Voter\VoterInterface'); + $voter->expects($this->exactly(2)) + ->method('vote') + ->will($this->returnValueMap(array( + array($token, null, array("ROLE_FOO"),$vote1), + array($token, null, array("ROLE_BAR"),$vote2), + ))) + ; + + return $voter; + } + public function getStrategyTests() { return array( @@ -130,7 +172,6 @@ class AccessDecisionManagerTest extends \PHPUnit_Framework_TestCase $voter->expects($this->any()) ->method('vote') ->will($this->returnValue($vote)); - ; return $voter; } @@ -141,7 +182,6 @@ class AccessDecisionManagerTest extends \PHPUnit_Framework_TestCase $voter->expects($this->any()) ->method('supportsClass') ->will($this->returnValue($ret)); - ; return $voter; } @@ -152,7 +192,6 @@ class AccessDecisionManagerTest extends \PHPUnit_Framework_TestCase $voter->expects($this->any()) ->method('supportsAttribute') ->will($this->returnValue($ret)); - ; return $voter; } diff --git a/Core/Tests/Authorization/AuthorizationCheckerTest.php b/Core/Tests/Authorization/AuthorizationCheckerTest.php new file mode 100644 index 0000000..64de6ef --- /dev/null +++ b/Core/Tests/Authorization/AuthorizationCheckerTest.php @@ -0,0 +1,99 @@ +<?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\Tests\Authorization; + +use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage; +use Symfony\Component\Security\Core\Authorization\AuthorizationChecker; + +class AuthorizationCheckerTest extends \PHPUnit_Framework_TestCase +{ + private $authenticationManager; + private $accessDecisionManager; + private $authorizationChecker; + private $tokenStorage; + + public function setUp() + { + $this->authenticationManager = $this->getMock('Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface'); + $this->accessDecisionManager = $this->getMock('Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface'); + $this->tokenStorage = new TokenStorage(); + + $this->authorizationChecker = new AuthorizationChecker( + $this->tokenStorage, + $this->authenticationManager, + $this->accessDecisionManager + ); + } + + public function testVoteAuthenticatesTokenIfNecessary() + { + $token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface'); + $this->tokenStorage->setToken($token); + + $newToken = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface'); + + $this->authenticationManager + ->expects($this->once()) + ->method('authenticate') + ->with($this->equalTo($token)) + ->will($this->returnValue($newToken)); + + // default with() isn't a strict check + $tokenComparison = function ($value) use ($newToken) { + // make sure that the new token is used in "decide()" and not the old one + return $value === $newToken; + }; + + $this->accessDecisionManager + ->expects($this->once()) + ->method('decide') + ->with($this->callback($tokenComparison)) + ->will($this->returnValue(true)); + + // first run the token has not been re-authenticated yet, after isGranted is called, it should be equal + $this->assertFalse($newToken === $this->tokenStorage->getToken()); + $this->assertTrue($this->authorizationChecker->isGranted('foo')); + $this->assertTrue($newToken === $this->tokenStorage->getToken()); + } + + /** + * @expectedException \Symfony\Component\Security\Core\Exception\AuthenticationCredentialsNotFoundException + */ + public function testVoteWithoutAuthenticationToken() + { + $this->authorizationChecker->isGranted('ROLE_FOO'); + } + + /** + * @dataProvider isGrantedProvider + */ + public function testIsGranted($decide) + { + $token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface'); + $token + ->expects($this->once()) + ->method('isAuthenticated') + ->will($this->returnValue(true)); + + $this->accessDecisionManager + ->expects($this->once()) + ->method('decide') + ->will($this->returnValue($decide)); + $this->tokenStorage->setToken($token); + $this->assertTrue($decide === $this->authorizationChecker->isGranted('ROLE_FOO')); + } + + public function isGrantedProvider() + { + return array(array(true), array(false)); + } +} diff --git a/Core/Tests/Authorization/Voter/RoleVoterTest.php b/Core/Tests/Authorization/Voter/RoleVoterTest.php index 62e3013..03ab2da 100644 --- a/Core/Tests/Authorization/Voter/RoleVoterTest.php +++ b/Core/Tests/Authorization/Voter/RoleVoterTest.php @@ -55,7 +55,6 @@ class RoleVoterTest extends \PHPUnit_Framework_TestCase $token->expects($this->once()) ->method('getRoles') ->will($this->returnValue($roles)); - ; return $token; } diff --git a/Core/Tests/Encoder/EncoderFactoryTest.php b/Core/Tests/Encoder/EncoderFactoryTest.php index 3d34d04..a8ca2f0 100644 --- a/Core/Tests/Encoder/EncoderFactoryTest.php +++ b/Core/Tests/Encoder/EncoderFactoryTest.php @@ -84,7 +84,7 @@ class EncoderFactoryTest extends \PHPUnit_Framework_TestCase { $factory = new EncoderFactory(array( 'Symfony\Component\Security\Core\Tests\Encoder\EncAwareUser' => new MessageDigestPasswordEncoder('sha256'), - 'encoder_name' => new MessageDigestPasswordEncoder('sha1') + 'encoder_name' => new MessageDigestPasswordEncoder('sha1'), )); $encoder = $factory->getEncoder(new EncAwareUser('user', 'pass')); @@ -96,7 +96,7 @@ class EncoderFactoryTest extends \PHPUnit_Framework_TestCase { $factory = new EncoderFactory(array( 'Symfony\Component\Security\Core\Tests\Encoder\EncAwareUser' => new MessageDigestPasswordEncoder('sha1'), - 'encoder_name' => new MessageDigestPasswordEncoder('sha256') + 'encoder_name' => new MessageDigestPasswordEncoder('sha256'), )); $user = new EncAwareUser('user', 'pass'); @@ -113,7 +113,7 @@ class EncoderFactoryTest extends \PHPUnit_Framework_TestCase { $factory = new EncoderFactory(array( 'Symfony\Component\Security\Core\Tests\Encoder\EncAwareUser' => new MessageDigestPasswordEncoder('sha1'), - 'encoder_name' => new MessageDigestPasswordEncoder('sha256') + 'encoder_name' => new MessageDigestPasswordEncoder('sha256'), )); $user = new EncAwareUser('user', 'pass'); @@ -125,7 +125,7 @@ class EncoderFactoryTest extends \PHPUnit_Framework_TestCase { $factory = new EncoderFactory(array( 'Symfony\Component\Security\Core\Tests\Encoder\EncAwareUser' => new MessageDigestPasswordEncoder('sha1'), - 'encoder_name' => new MessageDigestPasswordEncoder('sha256') + 'encoder_name' => new MessageDigestPasswordEncoder('sha256'), )); $encoder = $factory->getEncoder('Symfony\Component\Security\Core\Tests\Encoder\EncAwareUser'); @@ -136,11 +136,21 @@ class EncoderFactoryTest extends \PHPUnit_Framework_TestCase class SomeUser implements UserInterface { - public function getRoles() {} - public function getPassword() {} - public function getSalt() {} - public function getUsername() {} - public function eraseCredentials() {} + public function getRoles() + { + } + public function getPassword() + { + } + public function getSalt() + { + } + public function getUsername() + { + } + public function eraseCredentials() + { + } } class SomeChildUser extends SomeUser diff --git a/Core/Tests/Encoder/UserPasswordEncoderTest.php b/Core/Tests/Encoder/UserPasswordEncoderTest.php new file mode 100644 index 0000000..590652d --- /dev/null +++ b/Core/Tests/Encoder/UserPasswordEncoderTest.php @@ -0,0 +1,70 @@ +<?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\Tests\Encoder; + +use Symfony\Component\Security\Core\Encoder\UserPasswordEncoder; + +class UserPasswordEncoderTest extends \PHPUnit_Framework_TestCase +{ + public function testEncodePassword() + { + $userMock = $this->getMock('Symfony\Component\Security\Core\User\UserInterface'); + $userMock->expects($this->any()) + ->method('getSalt') + ->will($this->returnValue('userSalt')); + + $mockEncoder = $this->getMock('Symfony\Component\Security\Core\Encoder\PasswordEncoderInterface'); + $mockEncoder->expects($this->any()) + ->method('encodePassword') + ->with($this->equalTo('plainPassword'), $this->equalTo('userSalt')) + ->will($this->returnValue('encodedPassword')); + + $mockEncoderFactory = $this->getMock('Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface'); + $mockEncoderFactory->expects($this->any()) + ->method('getEncoder') + ->with($this->equalTo($userMock)) + ->will($this->returnValue($mockEncoder)); + + $passwordEncoder = new UserPasswordEncoder($mockEncoderFactory); + + $encoded = $passwordEncoder->encodePassword($userMock, 'plainPassword'); + $this->assertEquals('encodedPassword', $encoded); + } + + public function testIsPasswordValid() + { + $userMock = $this->getMock('Symfony\Component\Security\Core\User\UserInterface'); + $userMock->expects($this->any()) + ->method('getSalt') + ->will($this->returnValue('userSalt')); + $userMock->expects($this->any()) + ->method('getPassword') + ->will($this->returnValue('encodedPassword')); + + $mockEncoder = $this->getMock('Symfony\Component\Security\Core\Encoder\PasswordEncoderInterface'); + $mockEncoder->expects($this->any()) + ->method('isPasswordValid') + ->with($this->equalTo('encodedPassword'), $this->equalTo('plainPassword'), $this->equalTo('userSalt')) + ->will($this->returnValue(true)); + + $mockEncoderFactory = $this->getMock('Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface'); + $mockEncoderFactory->expects($this->any()) + ->method('getEncoder') + ->with($this->equalTo($userMock)) + ->will($this->returnValue($mockEncoder)); + + $passwordEncoder = new UserPasswordEncoder($mockEncoderFactory); + + $isValid = $passwordEncoder->isPasswordValid($userMock, 'plainPassword'); + $this->assertTrue($isValid); + } +} diff --git a/Core/Tests/Exception/UsernameNotFoundExceptionTest.php b/Core/Tests/Exception/UsernameNotFoundExceptionTest.php new file mode 100644 index 0000000..98ea374 --- /dev/null +++ b/Core/Tests/Exception/UsernameNotFoundExceptionTest.php @@ -0,0 +1,25 @@ +<?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\Tests\Exception; + +use Symfony\Component\Security\Core\Exception\UsernameNotFoundException; + +class UsernameNotFoundExceptionTest extends \PHPUnit_Framework_TestCase +{ + public function testGetMessageData() + { + $exception = new UsernameNotFoundException('Username could not be found.'); + $this->assertEquals(array('{{ username }}' => null), $exception->getMessageData()); + $exception->setUsername('username'); + $this->assertEquals(array('{{ username }}' => 'username'), $exception->getMessageData()); + } +} diff --git a/Core/Tests/SecurityContextTest.php b/Core/Tests/SecurityContextTest.php index dd0e2e3..886c596 100644 --- a/Core/Tests/SecurityContextTest.php +++ b/Core/Tests/SecurityContextTest.php @@ -11,82 +11,109 @@ namespace Symfony\Component\Security\Core\Tests; +use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage; +use Symfony\Component\Security\Core\Authorization\AuthorizationChecker; use Symfony\Component\Security\Core\SecurityContext; class SecurityContextTest extends \PHPUnit_Framework_TestCase { - public function testVoteAuthenticatesTokenIfNecessary() + private $tokenStorage; + private $authorizationChecker; + private $securityContext; + + public function setUp() { - $authManager = $this->getMock('Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface'); - $decisionManager = $this->getMock('Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface'); + $this->tokenStorage = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface'); + $this->authorizationChecker = $this->getMock('Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface'); + $this->securityContext = new SecurityContext($this->tokenStorage, $this->authorizationChecker); + } - $context = new SecurityContext($authManager, $decisionManager); - $context->setToken($token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface')); + public function testGetTokenDelegation() + { + $token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface'); - $authManager + $this->tokenStorage ->expects($this->once()) - ->method('authenticate') - ->with($this->equalTo($token)) - ->will($this->returnValue($newToken = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface'))) - ; + ->method('getToken') + ->will($this->returnValue($token)); - $decisionManager + $this->assertTrue($token === $this->securityContext->getToken()); + } + + public function testSetTokenDelegation() + { + $token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface'); + + $this->tokenStorage ->expects($this->once()) - ->method('decide') - ->will($this->returnValue(true)) - ; + ->method('setToken') + ->with($token); - $this->assertTrue($context->isGranted('foo')); - $this->assertSame($newToken, $context->getToken()); + $this->securityContext->setToken($token); } /** - * @expectedException \Symfony\Component\Security\Core\Exception\AuthenticationCredentialsNotFoundException + * @dataProvider isGrantedDelegationProvider */ - public function testVoteWithoutAuthenticationToken() + public function testIsGrantedDelegation($attributes, $object, $return) + { + $this->authorizationChecker + ->expects($this->once()) + ->method('isGranted') + ->with($attributes, $object) + ->will($this->returnValue($return)); + + $this->assertEquals($return, $this->securityContext->isGranted($attributes, $object)); + } + + public function isGrantedDelegationProvider() { - $context = new SecurityContext( - $this->getMock('Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface'), - $this->getMock('Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface') + return array( + array(array(), new \stdClass(), true), + array(array('henk'), new \stdClass(), false), + array(null, new \stdClass(), false), + array('henk', null, true), + array(array(1), 'henk', true), ); + } - $context->isGranted('ROLE_FOO'); + /** + * Test dedicated to check if the backwards compatibility is still working + */ + public function testOldConstructorSignature() + { + $authenticationManager = $this->getMock('Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface'); + $accessDecisionManager = $this->getMock('Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface'); + new SecurityContext($authenticationManager, $accessDecisionManager); } - public function testIsGranted() + /** + * @dataProvider oldConstructorSignatureFailuresProvider + * @expectedException \BadMethodCallException + */ + public function testOldConstructorSignatureFailures($first, $second) { - $manager = $this->getMock('Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface'); - $manager->expects($this->once())->method('decide')->will($this->returnValue(false)); - $context = new SecurityContext($this->getMock('Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface'), $manager); - $context->setToken($token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface')); - $token - ->expects($this->once()) - ->method('isAuthenticated') - ->will($this->returnValue(true)) - ; - $this->assertFalse($context->isGranted('ROLE_FOO')); - - $manager = $this->getMock('Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface'); - $manager->expects($this->once())->method('decide')->will($this->returnValue(true)); - $context = new SecurityContext($this->getMock('Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface'), $manager); - $context->setToken($token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface')); - $token - ->expects($this->once()) - ->method('isAuthenticated') - ->will($this->returnValue(true)) - ; - $this->assertTrue($context->isGranted('ROLE_FOO')); + new SecurityContext($first, $second); } - public function testGetSetToken() + public function oldConstructorSignatureFailuresProvider() { - $context = new SecurityContext( - $this->getMock('Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface'), - $this->getMock('Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface') - ); - $this->assertNull($context->getToken()); + $tokenStorage = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface'); + $authorizationChecker = $this->getMock('Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface'); + $authenticationManager = $this->getMock('Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface'); + $accessDecisionManager = $this->getMock('Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface'); - $context->setToken($token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface')); - $this->assertSame($token, $context->getToken()); + return array( + array(new \stdClass(), new \stdClass()), + array($tokenStorage, $accessDecisionManager), + array($accessDecisionManager, $tokenStorage), + array($authorizationChecker, $accessDecisionManager), + array($accessDecisionManager, $authorizationChecker), + array($tokenStorage, $accessDecisionManager), + array($authenticationManager, $authorizationChecker), + array('henk', 'hans'), + array(null, false), + array(true, null), + ); } } diff --git a/Core/Tests/Util/StringUtilsTest.php b/Core/Tests/Util/StringUtilsTest.php index 89da98d..e0366a5 100644 --- a/Core/Tests/Util/StringUtilsTest.php +++ b/Core/Tests/Util/StringUtilsTest.php @@ -13,11 +13,49 @@ namespace Symfony\Component\Security\Core\Tests\Util; use Symfony\Component\Security\Core\Util\StringUtils; +/** + * Data from PHP.net's hash_equals tests + */ class StringUtilsTest extends \PHPUnit_Framework_TestCase { - public function testEquals() + public function dataProviderTrue() + { + return array( + array('same', 'same'), + array('', ''), + array(123, 123), + array(null, ''), + array(null, null), + ); + } + + public function dataProviderFalse() + { + return array( + array('not1same', 'not2same'), + array('short', 'longer'), + array('longer', 'short'), + array('', 'notempty'), + array('notempty', ''), + array(123, 'NaN'), + array('NaN', 123), + array(null, 123), + ); + } + + /** + * @dataProvider dataProviderTrue + */ + public function testEqualsTrue($known, $user) + { + $this->assertTrue(StringUtils::equals($known, $user)); + } + + /** + * @dataProvider dataProviderFalse + */ + public function testEqualsFalse($known, $user) { - $this->assertTrue(StringUtils::equals('password', 'password')); - $this->assertFalse(StringUtils::equals('password', 'foo')); + $this->assertFalse(StringUtils::equals($known, $user)); } } diff --git a/Core/Tests/Validator/Constraints/LegacyUserPasswordValidator2Dot4ApiTest.php b/Core/Tests/Validator/Constraints/LegacyUserPasswordValidator2Dot4ApiTest.php new file mode 100644 index 0000000..4cba363 --- /dev/null +++ b/Core/Tests/Validator/Constraints/LegacyUserPasswordValidator2Dot4ApiTest.php @@ -0,0 +1,26 @@ +<?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\Tests\Validator\Constraints; + +use Symfony\Component\Validator\Validation; + +/** + * @since 2.5.4 + * @author Bernhard Schussek <bschussek@gmail.com> + */ +class LegacyUserPasswordValidator2Dot4ApiTest extends UserPasswordValidatorTest +{ + protected function getApiVersion() + { + return Validation::API_VERSION_2_4; + } +} diff --git a/Core/Tests/Validator/Constraints/LegacyUserPasswordValidatorLegacyApiTest.php b/Core/Tests/Validator/Constraints/LegacyUserPasswordValidatorLegacyApiTest.php new file mode 100644 index 0000000..5092a79 --- /dev/null +++ b/Core/Tests/Validator/Constraints/LegacyUserPasswordValidatorLegacyApiTest.php @@ -0,0 +1,26 @@ +<?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\Tests\Validator\Constraints; + +use Symfony\Component\Validator\Validation; + +/** + * @since 2.5.4 + * @author Bernhard Schussek <bschussek@gmail.com> + */ +class LegacyUserPasswordValidatorLegacyApiTest extends UserPasswordValidatorTest +{ + protected function getApiVersion() + { + return Validation::API_VERSION_2_5_BC; + } +} diff --git a/Core/Tests/Validator/Constraints/UserPasswordValidatorTest.php b/Core/Tests/Validator/Constraints/UserPasswordValidatorTest.php index 53eeb5f..10f692c 100644 --- a/Core/Tests/Validator/Constraints/UserPasswordValidatorTest.php +++ b/Core/Tests/Validator/Constraints/UserPasswordValidatorTest.php @@ -11,77 +11,102 @@ namespace Symfony\Component\Security\Core\Tests\Validator\Constraints; +use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface; +use Symfony\Component\Security\Core\Encoder\PasswordEncoderInterface; +use Symfony\Component\Security\Core\SecurityContextInterface; use Symfony\Component\Security\Core\Validator\Constraints\UserPassword; use Symfony\Component\Security\Core\Validator\Constraints\UserPasswordValidator; +use Symfony\Component\Validator\Tests\Constraints\AbstractConstraintValidatorTest; +use Symfony\Component\Validator\Validation; -class UserPasswordValidatorTest extends \PHPUnit_Framework_TestCase +/** + * @author Bernhard Schussek <bschussek@gmail.com> + */ +class UserPasswordValidatorTest extends AbstractConstraintValidatorTest { - const PASSWORD_VALID = true; - const PASSWORD_INVALID = false; + const PASSWORD = 's3Cr3t'; - protected $context; + const SALT = '^S4lt$'; - protected function setUp() + /** + * @var SecurityContextInterface + */ + protected $securityContext; + + /** + * @var PasswordEncoderInterface + */ + protected $encoder; + + /** + * @var EncoderFactoryInterface + */ + protected $encoderFactory; + + protected function getApiVersion() { - $this->context = $this->getMock('Symfony\Component\Validator\ExecutionContext', array(), array(), '', false); + return Validation::API_VERSION_2_5; } - protected function tearDown() + protected function createValidator() { - $this->context = null; + return new UserPasswordValidator($this->securityContext, $this->encoderFactory); } - public function testPasswordIsValid() + protected function setUp() { $user = $this->createUser(); - $securityContext = $this->createSecurityContext($user); + $this->securityContext = $this->createSecurityContext($user); + $this->encoder = $this->createPasswordEncoder(); + $this->encoderFactory = $this->createEncoderFactory($this->encoder); - $encoder = $this->createPasswordEncoder(static::PASSWORD_VALID); - $encoderFactory = $this->createEncoderFactory($encoder); + parent::setUp(); + } + + public function testPasswordIsValid() + { + $constraint = new UserPassword(array( + 'message' => 'myMessage', + )); - $validator = new UserPasswordValidator($securityContext, $encoderFactory); - $validator->initialize($this->context); + $this->encoder->expects($this->once()) + ->method('isPasswordValid') + ->with(static::PASSWORD, 'secret', static::SALT) + ->will($this->returnValue(true)); - $this - ->context - ->expects($this->never()) - ->method('addViolation') - ; + $this->validator->validate('secret', $constraint); - $validator->validate('secret', new UserPassword()); + $this->assertNoViolation(); } public function testPasswordIsNotValid() { - $user = $this->createUser(); - $securityContext = $this->createSecurityContext($user); - - $encoder = $this->createPasswordEncoder(static::PASSWORD_INVALID); - $encoderFactory = $this->createEncoderFactory($encoder); + $constraint = new UserPassword(array( + 'message' => 'myMessage', + )); - $validator = new UserPasswordValidator($securityContext, $encoderFactory); - $validator->initialize($this->context); + $this->encoder->expects($this->once()) + ->method('isPasswordValid') + ->with(static::PASSWORD, 'secret', static::SALT) + ->will($this->returnValue(false)); - $this - ->context - ->expects($this->once()) - ->method('addViolation') - ; + $this->validator->validate('secret', $constraint); - $validator->validate('secret', new UserPassword()); + $this->assertViolation('myMessage'); } + /** + * @expectedException \Symfony\Component\Validator\Exception\ConstraintDefinitionException + */ public function testUserIsNotValid() { - $this->setExpectedException('Symfony\Component\Validator\Exception\ConstraintDefinitionException'); - $user = $this->getMock('Foo\Bar\User'); - $encoderFactory = $this->getMock('Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface'); - $securityContext = $this->createSecurityContext($user); - $validator = new UserPasswordValidator($securityContext, $encoderFactory); - $validator->initialize($this->context); - $validator->validate('secret', new UserPassword()); + $this->securityContext = $this->createSecurityContext($user); + $this->validator = $this->createValidator(); + $this->validator->initialize($this->context); + + $this->validator->validate('secret', new UserPassword()); } protected function createUser() @@ -89,15 +114,15 @@ class UserPasswordValidatorTest extends \PHPUnit_Framework_TestCase $mock = $this->getMock('Symfony\Component\Security\Core\User\UserInterface'); $mock - ->expects($this->once()) + ->expects($this->any()) ->method('getPassword') - ->will($this->returnValue('s3Cr3t')) + ->will($this->returnValue(static::PASSWORD)) ; $mock - ->expects($this->once()) + ->expects($this->any()) ->method('getSalt') - ->will($this->returnValue('^S4lt$')) + ->will($this->returnValue(static::SALT)) ; return $mock; @@ -105,15 +130,7 @@ class UserPasswordValidatorTest extends \PHPUnit_Framework_TestCase protected function createPasswordEncoder($isPasswordValid = true) { - $mock = $this->getMock('Symfony\Component\Security\Core\Encoder\PasswordEncoderInterface'); - - $mock - ->expects($this->once()) - ->method('isPasswordValid') - ->will($this->returnValue($isPasswordValid)) - ; - - return $mock; + return $this->getMock('Symfony\Component\Security\Core\Encoder\PasswordEncoderInterface'); } protected function createEncoderFactory($encoder = null) @@ -121,7 +138,7 @@ class UserPasswordValidatorTest extends \PHPUnit_Framework_TestCase $mock = $this->getMock('Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface'); $mock - ->expects($this->once()) + ->expects($this->any()) ->method('getEncoder') ->will($this->returnValue($encoder)) ; @@ -135,7 +152,7 @@ class UserPasswordValidatorTest extends \PHPUnit_Framework_TestCase $mock = $this->getMock('Symfony\Component\Security\Core\SecurityContextInterface'); $mock - ->expects($this->once()) + ->expects($this->any()) ->method('getToken') ->will($this->returnValue($token)) ; @@ -147,7 +164,7 @@ class UserPasswordValidatorTest extends \PHPUnit_Framework_TestCase { $mock = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface'); $mock - ->expects($this->once()) + ->expects($this->any()) ->method('getUser') ->will($this->returnValue($user)) ; diff --git a/Core/User/AdvancedUserInterface.php b/Core/User/AdvancedUserInterface.php index 5b3a51a..19775c0 100644 --- a/Core/User/AdvancedUserInterface.php +++ b/Core/User/AdvancedUserInterface.php @@ -43,7 +43,7 @@ interface AdvancedUserInterface extends UserInterface * Internally, if this method returns false, the authentication system * will throw an AccountExpiredException and prevent login. * - * @return Boolean true if the user's account is non expired, false otherwise + * @return bool true if the user's account is non expired, false otherwise * * @see AccountExpiredException */ @@ -55,7 +55,7 @@ interface AdvancedUserInterface extends UserInterface * Internally, if this method returns false, the authentication system * will throw a LockedException and prevent login. * - * @return Boolean true if the user is not locked, false otherwise + * @return bool true if the user is not locked, false otherwise * * @see LockedException */ @@ -67,7 +67,7 @@ interface AdvancedUserInterface extends UserInterface * Internally, if this method returns false, the authentication system * will throw a CredentialsExpiredException and prevent login. * - * @return Boolean true if the user's credentials are non expired, false otherwise + * @return bool true if the user's credentials are non expired, false otherwise * * @see CredentialsExpiredException */ @@ -79,7 +79,7 @@ interface AdvancedUserInterface extends UserInterface * Internally, if this method returns false, the authentication system * will throw a DisabledException and prevent login. * - * @return Boolean true if the user is enabled, false otherwise + * @return bool true if the user is enabled, false otherwise * * @see DisabledException */ diff --git a/Core/User/ChainUserProvider.php b/Core/User/ChainUserProvider.php index fc72074..6e14a4f 100644 --- a/Core/User/ChainUserProvider.php +++ b/Core/User/ChainUserProvider.php @@ -40,7 +40,7 @@ class ChainUserProvider implements UserProviderInterface } /** - * {@inheritDoc} + * {@inheritdoc} */ public function loadUserByUsername($username) { @@ -58,7 +58,7 @@ class ChainUserProvider implements UserProviderInterface } /** - * {@inheritDoc} + * {@inheritdoc} */ public function refreshUser(UserInterface $user) { @@ -85,7 +85,7 @@ class ChainUserProvider implements UserProviderInterface } /** - * {@inheritDoc} + * {@inheritdoc} */ public function supportsClass($class) { diff --git a/Core/User/EquatableInterface.php b/Core/User/EquatableInterface.php index 645b77c..c6082ce 100644 --- a/Core/User/EquatableInterface.php +++ b/Core/User/EquatableInterface.php @@ -31,7 +31,7 @@ interface EquatableInterface * * @param UserInterface $user * - * @return Boolean + * @return bool */ public function isEqualTo(UserInterface $user); } diff --git a/Core/User/InMemoryUserProvider.php b/Core/User/InMemoryUserProvider.php index 074c21e..624eb3d 100644 --- a/Core/User/InMemoryUserProvider.php +++ b/Core/User/InMemoryUserProvider.php @@ -81,7 +81,7 @@ class InMemoryUserProvider implements UserProviderInterface } /** - * {@inheritDoc} + * {@inheritdoc} */ public function refreshUser(UserInterface $user) { @@ -93,7 +93,7 @@ class InMemoryUserProvider implements UserProviderInterface } /** - * {@inheritDoc} + * {@inheritdoc} */ public function supportsClass($class) { diff --git a/Core/User/User.php b/Core/User/User.php index b378e1b..ea2c6a4 100644 --- a/Core/User/User.php +++ b/Core/User/User.php @@ -64,7 +64,6 @@ final class User implements AdvancedUserInterface */ public function getSalt() { - return null; } /** diff --git a/Core/User/UserProviderInterface.php b/Core/User/UserProviderInterface.php index 6669c43..6b7895c 100644 --- a/Core/User/UserProviderInterface.php +++ b/Core/User/UserProviderInterface.php @@ -70,7 +70,7 @@ interface UserProviderInterface * * @param string $class * - * @return Boolean + * @return bool */ public function supportsClass($class); } diff --git a/Core/Util/ClassUtils.php b/Core/Util/ClassUtils.php index 26bf1a1..1d40c8d 100644 --- a/Core/Util/ClassUtils.php +++ b/Core/Util/ClassUtils.php @@ -39,7 +39,9 @@ class ClassUtils /** * This class should not be instantiated */ - private function __construct() {} + private function __construct() + { + } /** * Gets the real class name of a class name that could be a proxy. diff --git a/Core/Util/SecureRandomInterface.php b/Core/Util/SecureRandomInterface.php index 2c35a72..2cf7779 100644 --- a/Core/Util/SecureRandomInterface.php +++ b/Core/Util/SecureRandomInterface.php @@ -21,7 +21,7 @@ interface SecureRandomInterface /** * Generates the specified number of secure random bytes. * - * @param integer $nbBytes + * @param int $nbBytes * * @return string */ diff --git a/Core/Util/StringUtils.php b/Core/Util/StringUtils.php index 2e8925d..01441cb 100644 --- a/Core/Util/StringUtils.php +++ b/Core/Util/StringUtils.php @@ -21,37 +21,43 @@ class StringUtils /** * This class should not be instantiated */ - private function __construct() {} + private function __construct() + { + } /** * Compares two strings. * * This method implements a constant-time algorithm to compare strings. + * Regardless of the used implementation, it will leak length information. * * @param string $knownString The string of known length to compare against * @param string $userInput The string that the user can control * - * @return Boolean true if the two strings are the same, false otherwise + * @return bool true if the two strings are the same, false otherwise */ public static function equals($knownString, $userInput) { - // Prevent issues if string length is 0 - $knownString .= chr(0); - $userInput .= chr(0); + $knownString = (string) $knownString; + $userInput = (string) $userInput; + + if (function_exists('hash_equals')) { + return hash_equals($knownString, $userInput); + } $knownLen = strlen($knownString); $userLen = strlen($userInput); + // Extend the known string to avoid uninitialized string offsets + $knownString .= $userInput; + // Set the result to the difference between the lengths $result = $knownLen - $userLen; // Note that we ALWAYS iterate over the user-supplied length - // This is to prevent leaking length information + // This is to mitigate leaking length information for ($i = 0; $i < $userLen; $i++) { - // Using % here is a trick to prevent notices - // It's safe, since if the lengths are different - // $result is already non-0 - $result |= (ord($knownString[$i % $knownLen]) ^ ord($userInput[$i])); + $result |= (ord($knownString[$i]) ^ ord($userInput[$i])); } // They are only identical strings if $result is exactly 0... diff --git a/Core/Validator/Constraints/UserPassword.php b/Core/Validator/Constraints/UserPassword.php index 76c4b3b..35537b3 100644 --- a/Core/Validator/Constraints/UserPassword.php +++ b/Core/Validator/Constraints/UserPassword.php @@ -15,10 +15,11 @@ use Symfony\Component\Validator\Constraint; /** * @Annotation + * @Target({"PROPERTY", "METHOD", "ANNOTATION"}) */ class UserPassword extends Constraint { - public $message = 'This value should be the user current password.'; + public $message = 'This value should be the user\'s current password.'; public $service = 'security.validator.user_password'; /** diff --git a/Core/composer.json b/Core/composer.json index 249d4c1..54a76dc 100644 --- a/Core/composer.json +++ b/Core/composer.json @@ -40,7 +40,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.5-dev" + "dev-master": "2.6-dev" } } } |