diff options
50 files changed, 2783 insertions, 0 deletions
diff --git a/Authentication/AuthenticationManagerInterface.php b/Authentication/AuthenticationManagerInterface.php new file mode 100644 index 0000000..8ab2eda --- /dev/null +++ b/Authentication/AuthenticationManagerInterface.php @@ -0,0 +1,35 @@ +<?php + +namespace Symfony\Component\Security\Authentication; + +use Symfony\Component\Security\Authentication\Token\TokenInterface; +use Symfony\Component\Security\Exception\AuthenticationException; + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien.potencier@symfony-project.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * AuthenticationManagerInterface is the interface for authentication managers, + * which process Token authentication. + * + * @author Fabien Potencier <fabien.potencier@symfony-project.com> + */ +interface AuthenticationManagerInterface +{ + /** + * Attempts to authenticates a TokenInterface object. + * + * @param TokenInterface The TokenInterface instance to authenticate + * + * @return TokenInterface An authenticated TokenInterface instance + * + * @throws AuthenticationException if the authentication fails + */ + function authenticate(TokenInterface $token); +} diff --git a/Authentication/AuthenticationProviderManager.php b/Authentication/AuthenticationProviderManager.php new file mode 100644 index 0000000..1b50ccb --- /dev/null +++ b/Authentication/AuthenticationProviderManager.php @@ -0,0 +1,120 @@ +<?php + +namespace Symfony\Component\Security\Authentication; + +use Symfony\Component\Security\Exception\AccountStatusException; +use Symfony\Component\Security\Exception\AuthenticationException; +use Symfony\Component\Security\Exception\ProviderNotFoundException; +use Symfony\Component\Security\Authentication\Provider\AuthenticationProviderInterface; +use Symfony\Component\Security\Authentication\Token\TokenInterface; + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien.potencier@symfony-project.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * AuthenticationProviderManager uses a list of AuthenticationProviderInterface + * instances to authenticate a Token. + * + * @author Fabien Potencier <fabien.potencier@symfony-project.com> + */ +class AuthenticationProviderManager implements AuthenticationManagerInterface +{ + protected $providers; + protected $eraseCredentials; + + /** + * Constructor. + * + * @param AuthenticationProviderInterface[] $providers An array of AuthenticationProviderInterface instances + * @param Boolean $eraseCredentials Whether to erase credentials after authentication or not + */ + public function __construct(array $providers = array(), $eraseCredentials = true) + { + $this->setProviders($providers); + $this->eraseCredentials = $eraseCredentials; + } + + /** + * {@inheritdoc} + */ + public function authenticate(TokenInterface $token) + { + if (!count($this->providers)) { + throw new \LogicException('You must add at least one provider.'); + } + + $lastException = null; + $result = null; + + foreach ($this->providers as $provider) { + if (!$provider->supports($token)) { + continue; + } + + try { + $result = $provider->authenticate($token); + } catch (AccountStatusException $e) { + $e->setToken($token); + + throw $e; + } catch (AuthenticationException $e) { + $lastException = $e; + } + } + + if (null !== $result) { + if ($this->eraseCredentials) { + $result->eraseCredentials(); + } + + return $result; + } + + if (null === $lastException) { + $lastException = new ProviderNotFoundException(sprintf('No Authentication Provider found for token of class "%s".', get_class($token))); + } + + $lastException->setToken($token); + + throw $lastException; + } + + /** + * Returns the list of current providers. + * + * @return AuthenticationProviderInterface[] An array of AuthenticationProviderInterface instances + */ + public function getProviders() + { + return $this->providers; + } + + /** + * Sets the providers instances. + * + * @param AuthenticationProviderInterface[] $providers An array of AuthenticationProviderInterface instances + */ + public function setProviders(array $providers) + { + $this->providers = array(); + foreach ($providers as $provider) { + $this->addProvider($provider); + } + } + + /** + * Adds a provider. + * + * @param AuthenticationProviderInterface $provider A AuthenticationProviderInterface instance + */ + public function addProvider(AuthenticationProviderInterface $provider) + { + $this->providers[] = $provider; + } +} diff --git a/Authentication/EntryPoint/AuthenticationEntryPointInterface.php b/Authentication/EntryPoint/AuthenticationEntryPointInterface.php new file mode 100644 index 0000000..dc825c1 --- /dev/null +++ b/Authentication/EntryPoint/AuthenticationEntryPointInterface.php @@ -0,0 +1,31 @@ +<?php + +namespace Symfony\Component\Security\Authentication\EntryPoint; + +use Symfony\Component\Security\Exception\AuthenticationException; +use Symfony\Component\HttpFoundation\Request; + +/* + * This file is part of the Symfony framework. + * + * (c) Fabien Potencier <fabien.potencier@symfony-project.com> + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +/** + * AuthenticationEntryPointInterface is the interface used to start the authentication scheme. + * + * @author Fabien Potencier <fabien.potencier@symfony-project.com> + */ +interface AuthenticationEntryPointInterface +{ + /** + * Starts the authentication scheme. + * + * @param object $request The request that resulted in an AuthenticationException + * @param AuthenticationException $authException The exception that started the authentication process + */ + function start(Request $request, AuthenticationException $authException = null); +} diff --git a/Authentication/Provider/AnonymousAuthenticationProvider.php b/Authentication/Provider/AnonymousAuthenticationProvider.php new file mode 100644 index 0000000..67671b5 --- /dev/null +++ b/Authentication/Provider/AnonymousAuthenticationProvider.php @@ -0,0 +1,60 @@ +<?php + +namespace Symfony\Component\Security\Authentication\Provider; + +use Symfony\Component\Security\Authentication\Token\TokenInterface; +use Symfony\Component\Security\Exception\BadCredentialsException; +use Symfony\Component\Security\Authentication\Token\AnonymousToken; + +/* + * This file is part of the Symfony framework. + * + * (c) Fabien Potencier <fabien.potencier@symfony-project.com> + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +/** + * AnonymousAuthenticationProvider validates AnonymousToken instances. + * + * @author Fabien Potencier <fabien.potencier@symfony-project.com> + */ +class AnonymousAuthenticationProvider implements AuthenticationProviderInterface +{ + protected $key; + + /** + * Constructor. + * + * @param string $key The key shared with the authentication token + */ + public function __construct($key) + { + $this->key; + } + + /** + * {@inheritdoc} + */ + public function authenticate(TokenInterface $token) + { + if (!$this->supports($token)) { + return null; + } + + if ($this->key != $token->getKey()) { + throw new BadCredentialsException('The Token does not contain the expected key.'); + } + + return $token; + } + + /** + * {@inheritdoc} + */ + public function supports($token) + { + return $token instanceof AnonymousToken; + } +} diff --git a/Authentication/Provider/AuthenticationProviderInterface.php b/Authentication/Provider/AuthenticationProviderInterface.php new file mode 100644 index 0000000..61a428b --- /dev/null +++ b/Authentication/Provider/AuthenticationProviderInterface.php @@ -0,0 +1,34 @@ +<?php + +namespace Symfony\Component\Security\Authentication\Provider; + +use Symfony\Component\Security\Authentication\Token\TokenInterface; +use Symfony\Component\Security\Authentication\AuthenticationManagerInterface; + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien.potencier@symfony-project.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * AuthenticationProviderInterface is the interface for for all authentication providers. + * + * Concrete implementations processes specific Token instances. + * + * @author Fabien Potencier <fabien.potencier@symfony-project.com> + */ +interface AuthenticationProviderInterface extends AuthenticationManagerInterface +{ + /** + * Checks whether this provider supports the given token. + * + * @param TokenInterface $token A TokenInterface instance + * + * @return Boolean true if the implementation supports the Token, false otherwise + */ + function supports(TokenInterface $token); +} diff --git a/Authentication/Provider/DaoAuthenticationProvider.php b/Authentication/Provider/DaoAuthenticationProvider.php new file mode 100644 index 0000000..f814988 --- /dev/null +++ b/Authentication/Provider/DaoAuthenticationProvider.php @@ -0,0 +1,88 @@ +<?php + +namespace Symfony\Component\Security\Authentication\Provider; + +use Symfony\Component\Security\User\UserProviderInterface; +use Symfony\Component\Security\User\AccountCheckerInterface; +use Symfony\Component\Security\User\AccountInterface; +use Symfony\Component\Security\Encoder\PasswordEncoderInterface; +use Symfony\Component\Security\Encoder\PlaintextPasswordEncoder; +use Symfony\Component\Security\Exception\UsernameNotFoundException; +use Symfony\Component\Security\Exception\AuthenticationServiceException; +use Symfony\Component\Security\Exception\BadCredentialsException; +use Symfony\Component\Security\Authentication\Token\UsernamePasswordToken; + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien.potencier@symfony-project.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * DaoAuthenticationProvider uses a UserProviderInterface to retrieve the user for a UsernamePasswordToken. + * + * @author Fabien Potencier <fabien.potencier@symfony-project.com> + */ +class DaoAuthenticationProvider extends UserAuthenticationProvider +{ + protected $passwordEncoder; + protected $userProvider; + + /** + * Constructor. + * + * @param UserProviderInterface $userProvider A UserProviderInterface instance + * @param PasswordEncoderInterface $passwordEncoder A PasswordEncoderInterface instance + * @param AccountCheckerInterface $accountChecker An AccountCheckerInterface instance + */ + public function __construct(UserProviderInterface $userProvider, AccountCheckerInterface $accountChecker, PasswordEncoderInterface $passwordEncoder = null) + { + parent::__construct($accountChecker); + + if (null === $passwordEncoder) { + $passwordEncoder = new PlaintextPasswordEncoder(); + } + $this->passwordEncoder = $passwordEncoder; + $this->userProvider = $userProvider; + } + + /** + * {@inheritdoc} + */ + protected function checkAuthentication(AccountInterface $account, UsernamePasswordToken $token) + { + if (null === $token->getCredentials()) { + throw new BadCredentialsException('Bad credentials'); + } + + $presentedPassword = (string) $token->getCredentials(); + + if (!$this->passwordEncoder->isPasswordValid($account->getPassword(), $presentedPassword, $account->getSalt())) { + throw new BadCredentialsException('Bad credentials'); + } + } + + /** + * {@inheritdoc} + */ + protected function retrieveUser($username, UsernamePasswordToken $token) + { + $user = null; + try { + $user = $this->userProvider->loadUserByUsername($username); + } catch (UsernameNotFoundException $notFound) { + throw $notFound; + } catch (\Exception $repositoryProblem) { + throw new AuthenticationServiceException($repositoryProblem->getMessage(), $token, 0, $repositoryProblem); + } + + if (null === $user) { + throw new AuthenticationServiceException('UserProvider returned null.'); + } + + return $user; + } +} diff --git a/Authentication/Provider/PreAuthenticatedAuthenticationProvider.php b/Authentication/Provider/PreAuthenticatedAuthenticationProvider.php new file mode 100644 index 0000000..8617fdb --- /dev/null +++ b/Authentication/Provider/PreAuthenticatedAuthenticationProvider.php @@ -0,0 +1,80 @@ +<?php + +namespace Symfony\Component\Security\Authentication\Provider; + +use Symfony\Component\Security\User\UserProviderInterface; +use Symfony\Component\Security\User\AccountCheckerInterface; +use Symfony\Component\Security\Exception\BadCredentialsException; +use Symfony\Component\Security\Authentication\Token\PreAuthenticatedToken; +use Symfony\Component\Security\Authentication\Token\TokenInterface; + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien.potencier@symfony-project.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Processes a pre-authenticated authentication request. The request will + * typically originate from a {@link org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter} + * subclass. + * + * This authentication provider will not perform any checks on authentication + * requests, as they should already be pre-authenticated. However, the + * AuthenticationUserDetailsService implementation may still throw a UsernameNotFoundException, for example. + * + * @author Ruud Senden + * @since 2.0 + */ +class PreAuthenticatedAuthenticationProvider implements AuthenticationProviderInterface +{ + protected $userProvider; + protected $accountChecker; + + /** + * Constructor. + * + * @param UserProviderInterface $userProvider A UserProviderInterface instance + * @param AccountCheckerInterface $accountChecker An AccountCheckerInterface instance + */ + public function __construct(UserProviderInterface $userProvider, AccountCheckerInterface $accountChecker) + { + $this->userProvider = $userProvider; + $this->accountChecker = $accountChecker; + } + + /** + * {@inheritdoc} + */ + public function authenticate(TokenInterface $token) + { + if (!$this->supports($token)) { + return null; + } + + if (null === $token->getUser()) { + throw new BadCredentialsException('No pre-authenticated principal found in request.'); + } +/* + if (null === $token->getCredentials()) { + throw new BadCredentialsException('No pre-authenticated credentials found in request.'); + } +*/ + $user = $this->userProvider->loadUserByUsername($token->getUser()); + + $this->accountChecker->checkPostAuth($user); + + return new PreAuthenticatedToken($user, $token->getCredentials(), $user->getRoles()); + } + + /** + * {@inheritdoc} + */ + public function supports(TokenInterface $token) + { + return $token instanceof PreAuthenticatedToken; + } +} diff --git a/Authentication/Provider/UserAuthenticationProvider.php b/Authentication/Provider/UserAuthenticationProvider.php new file mode 100644 index 0000000..ddd98c3 --- /dev/null +++ b/Authentication/Provider/UserAuthenticationProvider.php @@ -0,0 +1,110 @@ +<?php + +namespace Symfony\Component\Security\Authentication\Provider; + +use Symfony\Component\Security\User\AccountInterface; +use Symfony\Component\Security\User\AccountCheckerInterface; +use Symfony\Component\Security\Exception\UsernameNotFoundException; +use Symfony\Component\Security\Exception\AuthenticationException; +use Symfony\Component\Security\Exception\BadCredentialsException; +use Symfony\Component\Security\Authentication\Token\UsernamePasswordToken; +use Symfony\Component\Security\Authentication\Token\TokenInterface; + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien.potencier@symfony-project.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * UserProviderInterface retrieves users for UsernamePasswordToken tokens. + * + * @author Fabien Potencier <fabien.potencier@symfony-project.com> + */ +abstract class UserAuthenticationProvider implements AuthenticationProviderInterface +{ + protected $hideUserNotFoundExceptions; + protected $accountChecker; + + /** + * Constructor. + * + * @param AccountCheckerInterface $accountChecker An AccountCheckerInterface interface + * @param Boolean $hideUserNotFoundExceptions Whether to hide user not found exception or not + */ + public function __construct(AccountCheckerInterface $accountChecker, $hideUserNotFoundExceptions = true) + { + $this->accountChecker = $accountChecker; + $this->hideUserNotFoundExceptions = $hideUserNotFoundExceptions; + } + + /** + * Does additional checks on the user and token (like validating the credentials). + * + * @param AccountInterface $account The retrieved AccountInterface instance + * @param UsernamePasswordToken $token The UsernamePasswordToken token to be authenticated + * + * @throws AuthenticationException if the credentials could not be validated + */ + abstract protected function checkAuthentication(AccountInterface $account, UsernamePasswordToken $token); + + /** + * {@inheritdoc} + */ + public function authenticate(TokenInterface $token) + { + if (!$this->supports($token)) { + return null; + } + + $username = null === $token->getUser() ? 'NONE_PROVIDED' : (string) $token; + + try { + $user = $this->retrieveUser($username, $token); + } catch (UsernameNotFoundException $notFound) { + if ($this->hideUserNotFoundExceptions) { + throw new BadCredentialsException('Bad credentials', 0, $notFound); + } + + throw $notFound; + } + + if (null === $user) { + throw new \LogicException('The retrieveUser() methods returned null which should not be possible.'); + } + + try { + $this->accountChecker->checkPreAuth($user); + $this->checkAuthentication($user, $token); + } catch (AuthenticationException $e) { + throw $e; + } + + $this->accountChecker->checkPostAuth($user); + + return new UsernamePasswordToken($user, $token->getCredentials(), $user->getRoles()); + } + + /** + * Retrieves the user from an implementation-specific location. + * + * @param string $username The username to retrieve + * @param UsernamePasswordToken $token The Token + * + * @return mixed The user + * + * @throws AuthenticationException if the credentials could not be validated + */ + abstract protected function retrieveUser($username, UsernamePasswordToken $token); + + /** + * {@inheritdoc} + */ + public function supports(TokenInterface $token) + { + return $token instanceof UsernamePasswordToken; + } +} diff --git a/Authentication/Token/AnonymousToken.php b/Authentication/Token/AnonymousToken.php new file mode 100644 index 0000000..c8fb1aa --- /dev/null +++ b/Authentication/Token/AnonymousToken.php @@ -0,0 +1,58 @@ +<?php + +namespace Symfony\Component\Security\Authentication\Token; + +/* + * This file is part of the Symfony framework. + * + * (c) Fabien Potencier <fabien.potencier@symfony-project.com> + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +/** + * AnonymousToken represents an anonymous token. + * + * @author Fabien Potencier <fabien.potencier@symfony-project.com> + */ +class AnonymousToken extends Token +{ + protected $user; + protected $key; + + /** + * Constructor. + * + * @param string $key The key shared with the authentication provider + * @param string $user The user + * @param Role[] $roles An array of roles + */ + public function __construct($key, $user, array $roles = array()) + { + parent::__construct($roles); + + $this->key = $key; + $this->user = $user; + + parent::setAuthenticated(true); + } + + /** + * {@inheritdoc} + */ + public function getCredentials() + { + return ''; + } + + /** + * Returns the key. + * + * @return string The Key + */ + public function getKey() + { + return $this->key; + } +} diff --git a/Authentication/Token/PreAuthenticatedToken.php b/Authentication/Token/PreAuthenticatedToken.php new file mode 100644 index 0000000..7466757 --- /dev/null +++ b/Authentication/Token/PreAuthenticatedToken.php @@ -0,0 +1,44 @@ +<?php + +namespace Symfony\Component\Security\Authentication\Token; + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien.potencier@symfony-project.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * PreAuthenticatedToken implements a pre-authenticated token. + * + * @author Fabien Potencier <fabien.potencier@symfony-project.com> + */ +class PreAuthenticatedToken extends Token +{ + /** + * Constructor. + */ + public function __construct($user, $credentials, array $roles = null) + { + if (null !== $roles) { + parent::__construct($roles); + $this->setAuthenticated(true); + } + + $this->user = $user; + $this->credentials = $credentials; + } + + /** + * {@inheritdoc} + */ + public function eraseCredentials() + { + parent::eraseCredentials(); + + $this->credentials = null; + } +} diff --git a/Authentication/Token/Token.php b/Authentication/Token/Token.php new file mode 100644 index 0000000..8279363 --- /dev/null +++ b/Authentication/Token/Token.php @@ -0,0 +1,156 @@ +<?php + +namespace Symfony\Component\Security\Authentication\Token; + +use Symfony\Component\Security\Role\RoleInterface; +use Symfony\Component\Security\Role\Role; +use Symfony\Component\Security\User\AccountInterface; + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien.potencier@symfony-project.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Base class for Token instances. + * + * @author Fabien Potencier <fabien.potencier@symfony-project.com> + */ +abstract class Token implements TokenInterface +{ + protected $roles; + protected $authenticated; + protected $user; + protected $credentials; + protected $immutable; + + /** + * Constructor. + * + * @param Role[] An array of roles + */ + public function __construct(array $roles = array()) + { + $this->roles = array(); + foreach ($roles as $role) { + if (is_string($role)) { + $role = new Role((string) $role); + } + $this->addRole($role); + } + } + + /** + * Adds a Role to the token. + * + * @param RoleInterface A RoleInterface instance + */ + public function addRole(RoleInterface $role) + { + $this->roles[] = $role; + } + + /** + * {@inheritdoc} + */ + public function getRoles() + { + return $this->roles; + } + + /** + * {@inheritdoc} + */ + public function __toString() + { + if (!is_object($this->user)) { + return (string) $this->user; + } else { + return $this->user->getUsername(); + } + } + + /** + * {@inheritdoc} + */ + public function isAuthenticated() + { + return $this->authenticated; + } + + /** + * {@inheritdoc} + */ + public function setAuthenticated($authenticated) + { + $this->authenticated = (Boolean) $authenticated; + } + + /** + * {@inheritdoc} + */ + public function getCredentials() + { + return $this->credentials; + } + + /** + * {@inheritdoc} + */ + public function getUser() + { + return $this->user; + } + + /** + * Removes sensitive information from the token. + */ + public function eraseCredentials() + { + if ($this->getCredentials() instanceof AccountInterface) { + $this->getCredentials()->eraseCredentials(); + } + + if ($this->getUser() instanceof AccountInterface) { + $this->getUser()->eraseCredentials(); + } + } + + /** + * {@inheritdoc} + */ + public function isImmutable() + { + return $this->immutable; + } + + /** + * {@inheritdoc} + */ + public function setImmutable($value) + { + $this->immutable = (Boolean) $value; + } + + /** + * {@inheritdoc} + */ + public function serialize() + { + // FIXME: don't serialize the user object, just the username (see ContextListener) + //return serialize(array((string) $this, $this->credentials, $this->authenticated, $this->roles, $this->immutable)); + return serialize(array($this->user, $this->credentials, $this->authenticated, $this->roles, $this->immutable)); + } + + /** + * {@inheritdoc} + */ + public function unserialize($serialized) + { + list($this->user, $this->credentials, $this->authenticated, $this->roles, $this->immutable) = unserialize($serialized); + } +} diff --git a/Authentication/Token/TokenInterface.php b/Authentication/Token/TokenInterface.php new file mode 100644 index 0000000..1300716 --- /dev/null +++ b/Authentication/Token/TokenInterface.php @@ -0,0 +1,69 @@ +<?php + +namespace Symfony\Component\Security\Authentication\Token; + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien.potencier@symfony-project.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * TokenInterface is the interface for the user authentication information. + * + * @author Fabien Potencier <fabien.potencier@symfony-project.com> + */ +interface TokenInterface extends \Serializable +{ + /** + * Returns a string representation of the token. + * + * @return string A string representation + */ + public function __toString(); + + /** + * Returns the user roles. + * + * @return Role[] An array of Role instances. + */ + function getRoles(); + + /** + * Returns the user credentials. + * + * @return mixed The user credentials + */ + function getCredentials(); + + /** + * Checks whether the token is immutable or not. + * + * @return Boolean true if the token is immutable, false otherwise + */ + function isImmutable(); + + /** + * Returns a user instance. + * + * @return object The User instance + */ + function getUser(); + + /** + * Checks if the user is authenticated or not. + * + * @return Boolean true if the token has been authenticated, false otherwise + */ + function isAuthenticated(); + + /** + * Sets the authenticated flag. + * + * @param Boolean The authenticated flag + */ + function setAuthenticated($isAuthenticated); +} diff --git a/Authentication/Token/UsernamePasswordToken.php b/Authentication/Token/UsernamePasswordToken.php new file mode 100644 index 0000000..5356f8d --- /dev/null +++ b/Authentication/Token/UsernamePasswordToken.php @@ -0,0 +1,56 @@ +<?php + +namespace Symfony\Component\Security\Authentication\Token; + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien.potencier@symfony-project.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * UsernamePasswordToken implements a username and password token. + * + * @author Fabien Potencier <fabien.potencier@symfony-project.com> + */ +class UsernamePasswordToken extends Token +{ + /** + * Constructor. + */ + public function __construct($user, $credentials, array $roles = array()) + { + parent::__construct($roles); + + $this->user = $user; + $this->credentials = $credentials; + + parent::setAuthenticated((Boolean) count($roles)); + } + + /** + * {@inheritdoc} + */ + public function setAuthenticated($isAuthenticated) + { + if ($isAuthenticated) + { + throw new \LogicException('Cannot set this token to trusted after instantiation.'); + } + + parent::setAuthenticated(false); + } + + /** + * {@inheritdoc} + */ + public function eraseCredentials() + { + parent::eraseCredentials(); + + $this->credentials = null; + } +} diff --git a/Authorization/AccessDecisionManager.php b/Authorization/AccessDecisionManager.php new file mode 100644 index 0000000..d458b89 --- /dev/null +++ b/Authorization/AccessDecisionManager.php @@ -0,0 +1,237 @@ +<?php + +namespace Symfony\Component\Security\Authorization; + +use Symfony\Component\Security\Authorization\Voter\VoterInterface; +use Symfony\Component\Security\Authentication\Token\TokenInterface; + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien.potencier@symfony-project.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * AccessDecisionManager is the base class for all access decision managers + * that use decision voters. + * + * @author Fabien Potencier <fabien.potencier@symfony-project.com> + */ +class AccessDecisionManager implements AccessDecisionManagerInterface +{ + protected $voters; + protected $allowIfAllAbstainDecisions; + protected $allowIfEqualGrantedDeniedDecisions; + protected $strategy; + + /** + * Constructor. + * + * @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 + */ + public function __construct(array $voters = array(), $strategy = 'affirmative', $allowIfAllAbstainDecisions = false, $allowIfEqualGrantedDeniedDecisions = true) + { + $this->voters = $voters; + $this->strategy = 'decide'.ucfirst($strategy); + $this->allowIfAllAbstainDecisions = (Boolean) $allowIfAllAbstainDecisions; + $this->allowIfEqualGrantedDeniedDecisions = (Boolean) $allowIfEqualGrantedDeniedDecisions; + } + + /** + * {@inheritdoc} + */ + public function decide(TokenInterface $token, array $attributes, $object = null) + { + return $this->{$this->strategy}($token, $attributes, $object); + } + + /** + * Returns all voters. + * + * @return VoterInterface[] $voters An array of VoterInterface instances + */ + public function getVoters() + { + return $this->voters; + } + + /** + * Sets voters. + * + * @param VoterInterface[] $voters An array of VoterInterface instances + */ + public function setVoters(array $voters) + { + if (!count($voters)) { + throw new \LogicException('You must have at least one voter.'); + } + + $this->voters = array(); + foreach ($voters as $voter) { + $this->addVoter($voter); + } + } + + /** + * Adds a voter. + * + * @param VoterInterface $voter A VoterInterface instance + */ + public function addVoter(VoterInterface $voter) + { + $this->voters[] = $voter; + } + + /** + * {@inheritdoc} + */ + public function supportsAttribute($attribute) + { + foreach ($this->voters as $voter) { + if ($voter->supportsAttribute($attribute)) { + return true; + } + } + + return false; + } + + /** + * {@inheritdoc} + */ + public function supportsClass($class) { + foreach ($this->voters as $voter) { + if ($voter->supportsClass($class)) { + return true; + } + } + + return false; + } + + /** + * Grants access if any voter returns an affirmative response. + * + * If all voters abstained from voting, the decision will be based on the + * allowIfAllAbstainDecisions property value (defaults to false). + */ + protected function decideAffirmative(TokenInterface $token, array $attributes, $object = null) + { + $deny = 0; + foreach ($this->voters as $voter) { + $result = $voter->vote($token, $object, $attributes); + switch ($result) { + case VoterInterface::ACCESS_GRANTED: + return true; + + case VoterInterface::ACCESS_DENIED: + ++$deny; + + break; + + default: + break; + } + } + + if ($deny > 0) { + return false; + } + + return $this->allowIfAllAbstainDecisions; + } + + /** + * Grants access if there is consensus of granted against denied responses. + * + * Consensus means majority-rule (ignoring abstains) rather than unanimous agreement (ignoring abstains). + * If you require unanimity, see UnanimousBased. + * + * If there were an equal number of grant and deny votes, the decision will be based on the + * allowIfEqualGrantedDeniedDecisions property value (defaults to true). + * + * If all voters abstained from voting, the decision will be based on the + * allowIfAllAbstainDecisions property value (defaults to false). + */ + public function decideConsensus(TokenInterface $token, array $attributes, $object = null) + { + $grant = 0; + $deny = 0; + $abstain = 0; + foreach ($this->voters as $voter) { + $result = $voter->vote($token, $object, $attributes); + + switch ($result) { + case VoterInterface::ACCESS_GRANTED: + ++$grant; + + break; + + case VoterInterface::ACCESS_DENIED: + ++$deny; + + break; + + default: + ++$abstain; + + break; + } + } + + if ($grant > $deny) { + return; + } + + if ($deny > $grant) { + return false; + } + + if ($grant == $deny && $grant != 0) { + return $this->allowIfEqualGrantedDeniedDecisions; + } + + return $this->allowIfAllAbstainDecisions; + } + + /** + * Grants access if only grant (or abstain) votes were received. + * + * If all voters abstained from voting, the decision will be based on the + * allowIfAllAbstainDecisions property value (defaults to false). + */ + public function decideUnanimous(TokenInterface $token, array $attributes, $object = null) + { + $grant = 0; + foreach ($attributes as $attribute) { + foreach ($this->voters as $voter) { + $result = $voter->vote($token, $object, array($attribute)); + + switch ($result) { + case VoterInterface::ACCESS_GRANTED: + ++$grant; + + break; + + case VoterInterface::ACCESS_DENIED: + return false; + + default: + break; + } + } + } + + // no deny votes + if ($grant > 0) { + return true; + } + + return $this->allowIfAllAbstainDecisions; + } +} diff --git a/Authorization/AccessDecisionManagerInterface.php b/Authorization/AccessDecisionManagerInterface.php new file mode 100644 index 0000000..5bb28b8 --- /dev/null +++ b/Authorization/AccessDecisionManagerInterface.php @@ -0,0 +1,53 @@ +<?php + +namespace Symfony\Component\Security\Authorization; + +use Symfony\Component\Security\Authentication\Token\TokenInterface; +use Symfony\Component\Security\Exception\AccessDeniedException; +use Symfony\Component\Security\Exception\InsufficientAuthenticationException; + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien.potencier@symfony-project.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * AccessDecisionManagerInterface makes authorization decisions. + * + * @author Fabien Potencier <fabien.potencier@symfony-project.com> + */ +interface AccessDecisionManagerInterface +{ + /** + * Decides whether the access is possible or not. + * + * @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 Boolean true if the access is granted, false otherwise + */ + function decide(TokenInterface $token, array $attributes, $object = null); + + /** + * Checks if the access decision manager supports the given attribute. + * + * @param string $attribute An attribute + * + * @return Boolean true if this decision manager supports the attribute, false otherwise + */ + function supportsAttribute($attribute); + + /** + * Checks if the access decision manager supports the given class. + * + * @param string $class A class name + * + * @return true if this decision manager can process the class + */ + function supportsClass($class); +} diff --git a/Authorization/Voter/AuthenticatedVoter.php b/Authorization/Voter/AuthenticatedVoter.php new file mode 100644 index 0000000..eb444c7 --- /dev/null +++ b/Authorization/Voter/AuthenticatedVoter.php @@ -0,0 +1,76 @@ +<?php + +namespace Symfony\Component\Security\Authorization\Voter; + +use Symfony\Component\Security\Authentication\Token\TokenInterface; +use Symfony\Component\Security\Authentication\Token\AnonymousToken; + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien.potencier@symfony-project.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * AuthenticatedVoter votes if an attribute like IS_AUTHENTICATED_FULLY or IS_AUTHENTICATED_ANONYMOUSLY is present. + * + * @author Fabien Potencier <fabien.potencier@symfony-project.com> + */ +class AuthenticatedVoter implements VoterInterface +{ + const IS_AUTHENTICATED_FULLY = "IS_AUTHENTICATED_FULLY"; + const IS_AUTHENTICATED_ANONYMOUSLY = "IS_AUTHENTICATED_ANONYMOUSLY"; + + /** + * {@inheritdoc} + */ + public function supportsAttribute($attribute) + { + return null !== $attribute && (self::IS_AUTHENTICATED_FULLY === $attribute || self::IS_AUTHENTICATED_ANONYMOUSLY === $attribute); + } + + /** + * {@inheritdoc} + */ + public function supportsClass($class) + { + return true; + } + + /** + * {@inheritdoc} + */ + public function vote(TokenInterface $token, $object, array $attributes) + { + $result = VoterInterface::ACCESS_ABSTAIN; + foreach ($attributes as $attribute) { + if (!$this->supportsAttribute($attribute)) { + continue; + } + + $result = VoterInterface::ACCESS_DENIED; + + if (self::IS_AUTHENTICATED_FULLY === $attribute) { + if ($this->isFullyAuthenticated($token)) { + return VoterInterface::ACCESS_GRANTED; + } + } + + if (self::IS_AUTHENTICATED_ANONYMOUSLY === $attribute) { + if (null === $token || $token instanceof AnonymousToken || $this->isFullyAuthenticated($token)) { + return VoterInterface::ACCESS_GRANTED; + } + } + } + + return $result; + } + + protected function isFullyAuthenticated(TokenInterface $token) + { + return null !== $token && !$token instanceof AnonymousToken; + } +} diff --git a/Authorization/Voter/RoleHierarchyVoter.php b/Authorization/Voter/RoleHierarchyVoter.php new file mode 100644 index 0000000..ac7ea46 --- /dev/null +++ b/Authorization/Voter/RoleHierarchyVoter.php @@ -0,0 +1,40 @@ +<?php + +namespace Symfony\Component\Security\Authorization\Voter; + +use Symfony\Component\Security\Authentication\Token\TokenInterface; +use Symfony\Component\Security\Role\RoleHierarchyInterface; + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien.potencier@symfony-project.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * RoleHierarchyVoter uses a RoleHierarchy to determine the roles granted to the user before voting. + * + * @author Fabien Potencier <fabien.potencier@symfony-project.com> + */ +class RoleHierarchyVoter extends RoleVoter +{ + protected $roleHierarchy; + + public function __construct(RoleHierarchyInterface $roleHierarchy, $prefix = 'ROLE_') + { + $this->roleHierarchy = $roleHierarchy; + + parent::__construct($prefix); + } + + /** + * {@inheritdoc} + */ + protected function extractRoles(TokenInterface $token) + { + return $this->roleHierarchy->getReachableRoles($token->getRoles()); + } +} diff --git a/Authorization/Voter/RoleVoter.php b/Authorization/Voter/RoleVoter.php new file mode 100644 index 0000000..7aa7ae9 --- /dev/null +++ b/Authorization/Voter/RoleVoter.php @@ -0,0 +1,79 @@ +<?php + +namespace Symfony\Component\Security\Authorization\Voter; + +use Symfony\Component\Security\Authentication\Token\TokenInterface; + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien.potencier@symfony-project.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * RoleVoter votes if any attribute starts with a given prefix. + * + * @author Fabien Potencier <fabien.potencier@symfony-project.com> + */ +class RoleVoter implements VoterInterface +{ + protected $prefix; + + /** + * Constructor. + * + * @param string $prefix The role prefix + */ + public function __construct($prefix = 'ROLE_') + { + $this->prefix = $prefix; + } + + /** + * {@inheritdoc} + */ + public function supportsAttribute($attribute) + { + return 0 === strpos($attribute, $this->prefix); + } + + /** + * {@inheritdoc} + */ + public function supportsClass($class) + { + return true; + } + + /** + * {@inheritdoc} + */ + public function vote(TokenInterface $token, $object, array $attributes) + { + $result = VoterInterface::ACCESS_ABSTAIN; + $roles = $this->extractRoles($token); + + foreach ($attributes as $attribute) { + if (!$this->supportsAttribute($attribute)) { + continue; + } + + $result = VoterInterface::ACCESS_DENIED; + foreach ($roles as $role) { + if ($attribute === $role->getRole()) { + return VoterInterface::ACCESS_GRANTED; + } + } + } + + return $result; + } + + protected function extractRoles(TokenInterface $token) + { + return $token->getRoles(); + } +} diff --git a/Authorization/Voter/VoterInterface.php b/Authorization/Voter/VoterInterface.php new file mode 100644 index 0000000..800b0fa --- /dev/null +++ b/Authorization/Voter/VoterInterface.php @@ -0,0 +1,58 @@ +<?php + +namespace Symfony\Component\Security\Authorization\Voter; + +use Symfony\Component\Security\Authentication\Token\TokenInterface; + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien.potencier@symfony-project.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * VoterInterface is the interface implemented by all voters. + * + * @author Fabien Potencier <fabien.potencier@symfony-project.com> + */ +interface VoterInterface +{ + const ACCESS_GRANTED = 1; + const ACCESS_ABSTAIN = 0; + const ACCESS_DENIED = -1; + + /** + * Checks if the voter supports the given attribute. + * + * @param string $attribute An attribute + * + * @return Boolean true if this Voter supports the attribute, false otherwise + */ + function supportsAttribute($attribute); + + /** + * Checks if the voter supports the given class. + * + * @param string $class A class name + * + * @return true if this Voter can process the class + */ + function supportsClass($class); + + /** + * Returns the vote for the given parameters. + * + * This method must return one of the following constant: + * ACCESS_GRANTED, ACCESS_DENIED, or 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 integer either ACCESS_GRANTED, ACCESS_ABSTAIN, or ACCESS_DENIED + */ + function vote(TokenInterface $token, $object, array $attributes); +} diff --git a/Encoder/BasePasswordEncoder.php b/Encoder/BasePasswordEncoder.php new file mode 100644 index 0000000..1378d56 --- /dev/null +++ b/Encoder/BasePasswordEncoder.php @@ -0,0 +1,65 @@ +<?php + +namespace Symfony\Component\Security\Encoder; + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien.potencier@symfony-project.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * BasePasswordEncoder is the base class for all password encoders. + * + * @author Fabien Potencier <fabien.potencier@symfony-project.com> + */ +abstract class BasePasswordEncoder implements PasswordEncoderInterface +{ + /** + * Demerges a merge password and salt string. + * + * @param string $mergedPasswordSalt The merged password and salt string + * + * @return array An array where the first element is the password and the second the salt + */ + protected function demergePasswordAndSalt($mergedPasswordSalt) + { + if (empty($mergedPasswordSalt)) { + return array('', ''); + } + + $password = $mergedPasswordSalt; + $salt = ''; + $saltBegins = strrpos($mergedPasswordSalt, '{'); + + if (false !== $saltBegins && $saltBegins + 1 < strlen($mergedPasswordSalt)) { + $salt = substr($mergedPasswordSalt, $saltBegins + 1, strlen($mergedPasswordSalt) - 1); + $password = substr($mergedPasswordSalt, 0, $saltBegins); + } + + return array($password, $salt); + } + + /** + * Merges a password and a salt. + * + * @param password the password to be used (can be <code>null</code>) + * @param salt the salt to be used (can be <code>null</code>) + * + * @return a merged password and salt <code>String</code> + */ + protected function mergePasswordAndSalt($password, $salt) { + if (empty($salt)) { + return $password; + } + + if (false !== strrpos($salt, '{') || false !== strrpos($salt, '}')) { + throw new \InvalidArgumentException('Cannot use { or } in salt.'); + } + + return $password.'{'.$salt.'}'; + } +} diff --git a/Encoder/MessageDigestPasswordEncoder.php b/Encoder/MessageDigestPasswordEncoder.php new file mode 100644 index 0000000..08efe00 --- /dev/null +++ b/Encoder/MessageDigestPasswordEncoder.php @@ -0,0 +1,61 @@ +<?php + +namespace Symfony\Component\Security\Encoder; + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien.potencier@symfony-project.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * MessageDigestPasswordEncoder uses a message digest algorithm. + * + * @author Fabien Potencier <fabien.potencier@symfony-project.com> + */ +class MessageDigestPasswordEncoder extends BasePasswordEncoder +{ + protected $algorithm; + protected $encodeHashAsBase64; + + /** + * Constructor. + * + * @param string $algorithm The digest algorithm to use + * @param Boolean $encodeHashAsBase64 Whether to base64 encode the password + * @param integer $iterations The number of iterations to use to stretch the password + */ + public function __construct($algorithm = 'sha1', $encodeHashAsBase64 = false, $iterations = 1) + { + $this->algorithm = $algorithm; + $this->encodeHashAsBase64 = $encodeHashAsBase64; + $this->iterations = $iterations; + } + + /** + * {@inheritdoc} + */ + public function encodePassword($raw, $salt) + { + $salted = $this->mergePasswordAndSalt($raw, $salt); + $digest = call_user_func($this->algorithm, $salted); + + // "stretch" the encoded value + for ($i = 1; $i < $this->iterations; $i++) { + $digest = call_user_func($this->algorithm, $digest); + } + + return $this->encodeHashAsBase64 ? base64_encode($digest) : $digest; + } + + /** + * {@inheritdoc} + */ + public function isPasswordValid($encoded, $raw, $salt) + { + return $encoded === $this->encodePassword($raw, $salt); + } +} diff --git a/Encoder/PasswordEncoderInterface.php b/Encoder/PasswordEncoderInterface.php new file mode 100644 index 0000000..c81ec25 --- /dev/null +++ b/Encoder/PasswordEncoderInterface.php @@ -0,0 +1,41 @@ +<?php + +namespace Symfony\Component\Security\Encoder; + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien.potencier@symfony-project.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * PasswordEncoderInterface is the interface for all encoders. + * + * @author Fabien Potencier <fabien.potencier@symfony-project.com> + */ +interface PasswordEncoderInterface +{ + /** + * Encodes the raw password. + * + * @param string $raw The password to encode + * @param stirng $salt The salt + * + * @return string The encoded password + */ + function encodePassword($raw, $salt); + + /** + * Checks a raw password against an encoded password. + * + * @param string $encoded An encoded password + * @param string $raw A raw password + * @param string $salt The salt + * + * @return Boolean true if the password is valid, false otherwise + */ + function isPasswordValid($encoded, $raw, $salt); +} diff --git a/Encoder/PlaintextPasswordEncoder.php b/Encoder/PlaintextPasswordEncoder.php new file mode 100644 index 0000000..256a4eb --- /dev/null +++ b/Encoder/PlaintextPasswordEncoder.php @@ -0,0 +1,49 @@ +<?php + +namespace Symfony\Component\Security\Encoder; + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien.potencier@symfony-project.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * PlaintextPasswordEncoder does not do any encoding. + * + * @author Fabien Potencier <fabien.potencier@symfony-project.com> + */ +class PlaintextPasswordEncoder extends BasePasswordEncoder +{ + protected $ignorePasswordCase; + + public function __construct($ignorePasswordCase = false) + { + $this->ignorePasswordCase = $ignorePasswordCase; + } + + /** + * {@inheritdoc} + */ + public function encodePassword($raw, $salt) + { + return $this->mergePasswordAndSalt($raw, $salt); + } + + /** + * {@inheritdoc} + */ + public function isPasswordValid($encoded, $raw, $salt) + { + $pass2 = $this->mergePasswordAndSalt($raw, $salt); + + if (!$this->ignorePasswordCase) { + return $encoded === $pass2; + } else { + return strtolower($encoded) === strtolower($pass2); + } + } +} diff --git a/Exception/AccessDeniedException.php b/Exception/AccessDeniedException.php new file mode 100644 index 0000000..6a48d89 --- /dev/null +++ b/Exception/AccessDeniedException.php @@ -0,0 +1,25 @@ +<?php + +namespace Symfony\Component\Security\Exception; + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien.potencier@symfony-project.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * AccessDeniedException is thrown when the account has not the required role. + * + * @author Fabien Potencier <fabien.potencier@symfony-project.com> + */ +class AccessDeniedException extends \RuntimeException +{ + public function __construct($message = '', $code = 403, \Exception $previous = null) + { + parent::__construct($message, 403, $previous); + } +} diff --git a/Exception/AccountExpiredException.php b/Exception/AccountExpiredException.php new file mode 100644 index 0000000..d61a191 --- /dev/null +++ b/Exception/AccountExpiredException.php @@ -0,0 +1,21 @@ +<?php + +namespace Symfony\Component\Security\Exception; + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien.potencier@symfony-project.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * AccountExpiredException is thrown when the user account has expired. + * + * @author Fabien Potencier <fabien.potencier@symfony-project.com> + */ +class AccountExpiredException extends AccountStatusException +{ +} diff --git a/Exception/AccountStatusException.php b/Exception/AccountStatusException.php new file mode 100644 index 0000000..4c06cbf --- /dev/null +++ b/Exception/AccountStatusException.php @@ -0,0 +1,22 @@ +<?php + +namespace Symfony\Component\Security\Exception; + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien.potencier@symfony-project.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * AccountStatusException is the base class for authentication exceptions + * caused by the user account status. + * + * @author Fabien Potencier <fabien.potencier@symfony-project.com> + */ +abstract class AccountStatusException extends AuthenticationException +{ +} diff --git a/Exception/AuthenticationCredentialsNotFoundException.php b/Exception/AuthenticationCredentialsNotFoundException.php new file mode 100644 index 0000000..248df79 --- /dev/null +++ b/Exception/AuthenticationCredentialsNotFoundException.php @@ -0,0 +1,22 @@ +<?php + +namespace Symfony\Component\Security\Exception; + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien.potencier@symfony-project.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * AuthenticationCredentialsNotFoundException is thrown when an authentication is rejected + * because no Token is available. + * + * @author Fabien Potencier <fabien.potencier@symfony-project.com> + */ +class AuthenticationCredentialsNotFoundException extends AuthenticationException +{ +} diff --git a/Exception/AuthenticationException.php b/Exception/AuthenticationException.php new file mode 100644 index 0000000..939139b --- /dev/null +++ b/Exception/AuthenticationException.php @@ -0,0 +1,41 @@ +<?php + +namespace Symfony\Component\Security\Exception; + +use Symfony\Component\Security\Authentication\Token\TokenInterface; + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien.potencier@symfony-project.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * AuthenticationException is the base class for all authentication exceptions. + * + * @author Fabien Potencier <fabien.potencier@symfony-project.com> + */ +class AuthenticationException extends \RuntimeException +{ + protected $token; + + public function __construct($message, TokenInterface $token = null, $code = 0, \Exception $previous = null) + { + parent::__construct($message, $code, $previous); + + $this->token = $token; + } + + public function getToken() + { + return $this->token; + } + + public function setToken(TokenInterface $token) + { + $this->token = $token; + } +} diff --git a/Exception/AuthenticationServiceException.php b/Exception/AuthenticationServiceException.php new file mode 100644 index 0000000..bf10449 --- /dev/null +++ b/Exception/AuthenticationServiceException.php @@ -0,0 +1,21 @@ +<?php + +namespace Symfony\Component\Security\Exception; + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien.potencier@symfony-project.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * AuthenticationServiceException is thrown when an authentication request could not be processed due to a system problem. + * + * @author Fabien Potencier <fabien.potencier@symfony-project.com> + */ +class AuthenticationServiceException extends AuthenticationException +{ +} diff --git a/Exception/BadCredentialsException.php b/Exception/BadCredentialsException.php new file mode 100644 index 0000000..3e6ddf8 --- /dev/null +++ b/Exception/BadCredentialsException.php @@ -0,0 +1,25 @@ +<?php + +namespace Symfony\Component\Security\Exception; + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien.potencier@symfony-project.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * BadCredentialsException is thrown when the user credentials are invalid. + * + * @author Fabien Potencier <fabien.potencier@symfony-project.com> + */ +class BadCredentialsException extends AuthenticationException +{ + public function __construct($message, $code = 0, \Exception $previous = null) + { + parent::__construct($message, null, $code, $previous); + } +} diff --git a/Exception/CredentialsExpiredException.php b/Exception/CredentialsExpiredException.php new file mode 100644 index 0000000..480031c --- /dev/null +++ b/Exception/CredentialsExpiredException.php @@ -0,0 +1,21 @@ +<?php + +namespace Symfony\Component\Security\Exception; + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien.potencier@symfony-project.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * CredentialsExpiredException is thrown when the user account credentials have expired. + * + * @author Fabien Potencier <fabien.potencier@symfony-project.com> + */ +class CredentialsExpiredException extends AccountStatusException +{ +} diff --git a/Exception/DisabledException.php b/Exception/DisabledException.php new file mode 100644 index 0000000..4d853d7 --- /dev/null +++ b/Exception/DisabledException.php @@ -0,0 +1,21 @@ +<?php + +namespace Symfony\Component\Security\Exception; + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien.potencier@symfony-project.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * DisabledException is thrown when the user account is disabled. + * + * @author Fabien Potencier <fabien.potencier@symfony-project.com> + */ +class DisabledException extends AccountStatusException +{ +} diff --git a/Exception/InsufficientAuthenticationException.php b/Exception/InsufficientAuthenticationException.php new file mode 100644 index 0000000..e2d2fcd --- /dev/null +++ b/Exception/InsufficientAuthenticationException.php @@ -0,0 +1,23 @@ +<?php + +namespace Symfony\Component\Security\Exception; + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien.potencier@symfony-project.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * InsufficientAuthenticationException is thrown if the user credentials are not sufficiently trusted. + * + * This is the case when a user is anonymous and the resource to be displayed has an access role. + * + * @author Fabien Potencier <fabien.potencier@symfony-project.com> + */ +class InsufficientAuthenticationException extends AuthenticationException +{ +} diff --git a/Exception/LockedException.php b/Exception/LockedException.php new file mode 100644 index 0000000..7e801e3 --- /dev/null +++ b/Exception/LockedException.php @@ -0,0 +1,21 @@ +<?php + +namespace Symfony\Component\Security\Exception; + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien.potencier@symfony-project.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * LockedException is thrown if the user account is locked. + * + * @author Fabien Potencier <fabien.potencier@symfony-project.com> + */ +class LockedException extends AccountStatusException +{ +} diff --git a/Exception/NonceExpiredException.php b/Exception/NonceExpiredException.php new file mode 100644 index 0000000..42fc21b --- /dev/null +++ b/Exception/NonceExpiredException.php @@ -0,0 +1,27 @@ +<?php + +namespace Symfony\Component\HttpKernel\Security\EntryPoint; + +use Symfony\Component\Security\Exception\AuthenticationException; +use Symfony\Component\Security\Authentication\EntryPoint\AuthenticationEntryPointInterface; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Log\LoggerInterface; + +/* + * This file is part of the Symfony framework. + * + * (c) Fabien Potencier <fabien.potencier@symfony-project.com> + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +/** + * NonceExpiredException is thrown when an authentication is rejected because + * the digest nonce has expired. + * + * @author Fabien Potencier <fabien.potencier@symfony-project.com> + */ +class NonceExpiredException extends AuthenticationException +{ +} diff --git a/Exception/ProviderNotFoundException.php b/Exception/ProviderNotFoundException.php new file mode 100644 index 0000000..257ebfa --- /dev/null +++ b/Exception/ProviderNotFoundException.php @@ -0,0 +1,22 @@ +<?php + +namespace Symfony\Component\Security\Exception; + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien.potencier@symfony-project.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * ProviderNotFoundException is thrown when no AuthenticationProviderInterface instance + * supports an authentication Token. + * + * @author Fabien Potencier <fabien.potencier@symfony-project.com> + */ +class ProviderNotFoundException extends AuthenticationException +{ +} diff --git a/Exception/UsernameNotFoundException.php b/Exception/UsernameNotFoundException.php new file mode 100644 index 0000000..f126c23 --- /dev/null +++ b/Exception/UsernameNotFoundException.php @@ -0,0 +1,21 @@ +<?php + +namespace Symfony\Component\Security\Exception; + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien.potencier@symfony-project.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * UsernameNotFoundException is thrown if a User cannot be found by its username. + * + * @author Fabien Potencier <fabien.potencier@symfony-project.com> + */ +class UsernameNotFoundException extends AuthenticationException +{ +} diff --git a/Role/Role.php b/Role/Role.php new file mode 100644 index 0000000..998fd1d --- /dev/null +++ b/Role/Role.php @@ -0,0 +1,40 @@ +<?php + +namespace Symfony\Component\Security\Role; + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien.potencier@symfony-project.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Role is a simple implementation of a RoleInterface where the role is a string. + * + * @author Fabien Potencier <fabien.potencier@symfony-project.com> + */ +class Role implements RoleInterface +{ + protected $role; + + /** + * Constructor. + * + * @param string $role The role name + */ + public function __construct($role) + { + $this->role = (string) $role; + } + + /** + * {@inheritdoc} + */ + public function getRole() + { + return $this->role; + } +} diff --git a/Role/RoleHierarchy.php b/Role/RoleHierarchy.php new file mode 100644 index 0000000..11864b3 --- /dev/null +++ b/Role/RoleHierarchy.php @@ -0,0 +1,77 @@ +<?php + +namespace Symfony\Component\Security\Role; + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien.potencier@symfony-project.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * RoleHierarchy defines a role hierarchy. + * + * @author Fabien Potencier <fabien.potencier@symfony-project.com> + */ +class RoleHierarchy implements RoleHierarchyInterface +{ + protected $hierarchy; + protected $map; + + /** + * Constructor. + * + * @param array $hierarchy An array defining the hierarchy + */ + public function __construct(array $hierarchy) + { + $this->hierarchy = $hierarchy; + + $this->buildRoleMap(); + } + + /** + * Returns an array of all roles reachable by the given ones. + * + * @param RoleInterface[] $roles An array of RoleInterface instances + * + * @return RoleInterface[] An array of RoleInterface instances + */ + public function getReachableRoles(array $roles) + { + $reachableRoles = $roles; + foreach ($roles as $role) { + if (!isset($this->map[$role->getRole()])) { + continue; + } + + foreach ($this->map[$role->getRole()] as $r) { + $reachableRoles[] = new Role($r); + } + } + + return $reachableRoles; + } + + protected function buildRoleMap() + { + $this->map = array(); + foreach ($this->hierarchy as $main => $roles) { + $this->map[$main] = $roles; + $visited = array(); + $additionalRoles = $roles; + while ($role = array_shift($additionalRoles)) { + if (!isset($this->hierarchy[$role])) { + continue; + } + + $visited[] = $role; + $this->map[$main] = array_unique(array_merge($this->map[$main], $this->hierarchy[$role])); + $additionalRoles = array_merge($additionalRoles, array_diff($this->hierarchy[$role], $visited)); + } + } + } +} diff --git a/Role/RoleHierarchyInterface.php b/Role/RoleHierarchyInterface.php new file mode 100644 index 0000000..b93a93c --- /dev/null +++ b/Role/RoleHierarchyInterface.php @@ -0,0 +1,33 @@ +<?php + +namespace Symfony\Component\Security\Role; + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien.potencier@symfony-project.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * RoleHierarchyInterface is the interface for a role hierarchy. + * + * @author Fabien Potencier <fabien.potencier@symfony-project.com> + */ +interface RoleHierarchyInterface +{ + /** + * Returns an array of all reachable roles. + * + * Reachable roles are the roles directly assigned but also all + * roles that are transitively reachable from them in the role + * hierarchy. + * + * @param array $roles An array of directly assigned roles + * + * @return array An array of all reachable roles + */ + public function getReachableRoles(array $roles); +} diff --git a/Role/RoleInterface.php b/Role/RoleInterface.php new file mode 100644 index 0000000..12bec01 --- /dev/null +++ b/Role/RoleInterface.php @@ -0,0 +1,36 @@ +<?php + +namespace Symfony\Component\Security\Role; + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien.potencier@symfony-project.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * RoleInterface represents a role granted to a user. + * + * A role must either have a string representation or + * it needs to be explicitely supported by an at least + * one AccessDecisionManager. + * + * @author Fabien Potencier <fabien.potencier@symfony-project.com> + */ +interface RoleInterface +{ + /** + * Returns the role. + * + * This method returns a string representation whenever possible. + * + * When the role cannot be represented with sufficient precision + * by a string, it should return null. + * + * @return string|null A string representation of the role, or null + */ + function getRole(); +} diff --git a/Role/SwitchUserRole.php b/Role/SwitchUserRole.php new file mode 100644 index 0000000..857898f --- /dev/null +++ b/Role/SwitchUserRole.php @@ -0,0 +1,47 @@ +<?php + +namespace Symfony\Component\Security\Role; + +use Symfony\Component\Security\Authentication\Token\TokenInterface; + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien.potencier@symfony-project.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * SwitchUserRole is used when the current user temporarly impersonates another one. + * + * @author Fabien Potencier <fabien.potencier@symfony-project.com> + */ +class SwitchUserRole extends Role +{ + protected $source; + + /** + * Constructor. + * + * @param string $role The role as a string + * @param TokenInterface $source The original token + */ + public function __construct($role, TokenInterface $source) + { + parent::__construct($role); + + $this->source = $source; + } + + /** + * Returns the original Token. + * + * @return TokenInterface The original TokenInterface instance + */ + public function getSource() + { + return $this->source; + } +} diff --git a/SecurityContext.php b/SecurityContext.php new file mode 100644 index 0000000..32f1962 --- /dev/null +++ b/SecurityContext.php @@ -0,0 +1,75 @@ +<?php + +namespace Symfony\Component\Security; + +use Symfony\Component\Security\Authentication\Token\TokenInterface; + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien.potencier@symfony-project.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * SecurityContext is the main entry point of the Security component. + * + * It gives access to the token representing the current user authentication. + * + * @author Fabien Potencier <fabien.potencier@symfony-project.com> + */ +class SecurityContext +{ + const ACCESS_DENIED_ERROR = '_security.403_error'; + const AUTHENTICATION_ERROR = '_security.last_error'; + const LAST_USERNAME = '_security.last_username'; + + protected $token; + protected $accessDecisionManager; + + public function __construct($accessDecisionManager = null) + { + $this->accessDecisionManager = $accessDecisionManager; + } + + protected function getUser() + { + return null === $this->token ? null : $this->token->getUser(); + } + + public function vote($attributes, $object = null) + { + if (null === $this->token || null === $this->accessDecisionManager) { + return false; + } + + return $this->accessDecisionManager->decide($this->token, (array) $attributes, $object); + } + + public function isAuthenticated() + { + return null === $this->token ? false : $this->token->isAuthenticated(); + } + + /** + * Gets the currently authenticated token. + * + * @return TokenInterface|null A TokenInterface instance or null if no authentication information is available + */ + function getToken() + { + return $this->token; + } + + /** + * Sets the currently authenticated token. + * + * @param TokenInterface $token A TokenInterface token, or null if no further authentication information should be stored + */ + function setToken(TokenInterface $token = null) + { + $this->token = $token; + } +} diff --git a/User/AccountChecker.php b/User/AccountChecker.php new file mode 100644 index 0000000..570f62b --- /dev/null +++ b/User/AccountChecker.php @@ -0,0 +1,61 @@ +<?php + +namespace Symfony\Component\Security\User; + +use Symfony\Component\Security\Exception\CredentialsExpiredException; +use Symfony\Component\Security\Exception\LockedException; +use Symfony\Component\Security\Exception\DisabledException; +use Symfony\Component\Security\Exception\AccountExpiredException; + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien.potencier@symfony-project.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * AccountChecker checks the user account flags. + * + * @author Fabien Potencier <fabien.potencier@symfony-project.com> + */ +class AccountChecker implements AccountCheckerInterface +{ + /** + * {@inheritdoc} + */ + public function checkPreAuth(AccountInterface $account) + { + if (!$account instanceof AdvancedAccountInterface) { + return; + } + + if (!$account->isCredentialsNonExpired()) { + throw new CredentialsExpiredException("User credentials have expired.", $account); + } + } + + /** + * {@inheritdoc} + */ + public function checkPostAuth(AccountInterface $account) + { + if (!$account instanceof AdvancedAccountInterface) { + return; + } + + if (!$account->isAccountNonLocked()) { + throw new LockedException("User account is locked.", $account); + } + + if (!$account->isEnabled()) { + throw new DisabledException("User account is disabled.", $account); + } + + if (!$account->isAccountNonExpired()) { + throw new AccountExpiredException("User account has expired.", $account); + } + } +} diff --git a/User/AccountCheckerInterface.php b/User/AccountCheckerInterface.php new file mode 100644 index 0000000..d3cfe0b --- /dev/null +++ b/User/AccountCheckerInterface.php @@ -0,0 +1,36 @@ +<?php + +namespace Symfony\Component\Security\User; + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien.potencier@symfony-project.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * AccountCheckerInterface checks user account when authentication occurs. + * + * This should not be used to make authentication decisions. + * + * @author Fabien Potencier <fabien.potencier@symfony-project.com> + */ +interface AccountCheckerInterface +{ + /** + * Checks the user account before authentication. + * + * @param AccountInterface $account An AccountInterface instance + */ + function checkPreAuth(AccountInterface $account); + + /** + * Checks the user account after authentication. + * + * @param AccountInterface $account An AccountInterface instance + */ + function checkPostAuth(AccountInterface $account); +} diff --git a/User/AccountInterface.php b/User/AccountInterface.php new file mode 100644 index 0000000..067366e --- /dev/null +++ b/User/AccountInterface.php @@ -0,0 +1,60 @@ +<?php + +namespace Symfony\Component\Security\User; + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien.potencier@symfony-project.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * AccountInterface is the interface that user classes must implement. + * + * @author Fabien Potencier <fabien.potencier@symfony-project.com> + */ +interface AccountInterface +{ + /** + * Returns a string representation of the User. + * + * @return string A string return of the User + */ + function __toString(); + + /** + * Returns the roles granted to the user. + * + * @return Role[] The user roles + */ + function getRoles(); + + /** + * Returns the password used to authenticate the user. + * + * @return string The password + */ + function getPassword(); + + /** + * Returns the salt. + * + * @return string The salt + */ + function getSalt(); + + /** + * Returns the username used to authenticate the user. + * + * @return string The username + */ + function getUsername(); + + /** + * Removes sensitive data from the user. + */ + function eraseCredentials(); +} diff --git a/User/AdvancedAccountInterface.php b/User/AdvancedAccountInterface.php new file mode 100644 index 0000000..7cdd547 --- /dev/null +++ b/User/AdvancedAccountInterface.php @@ -0,0 +1,48 @@ +<?php + +namespace Symfony\Component\Security\User; + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien.potencier@symfony-project.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * AdvancedAccountInterface adds status flags to a regular account. + * + * @author Fabien Potencier <fabien.potencier@symfony-project.com> + */ +interface AdvancedAccountInterface extends AccountInterface +{ + /** + * Checks whether the user's account has expired. + * + * @return Boolean true if the user's account is non expired, false otherwise + */ + function isAccountNonExpired(); + + /** + * Checks whether the user is locked. + * + * @return Boolean true if the user is not locked, false otherwise + */ + function isAccountNonLocked(); + + /** + * Checks whether the user's credentials (password) has expired. + * + * @return Boolean true if the user's credentials are non expired, false otherwise + */ + function isCredentialsNonExpired(); + + /** + * Checks whether the user is enabled. + * + * @return Boolean true if the user is enabled, false otherwise + */ + function isEnabled(); +} diff --git a/User/InMemoryUserProvider.php b/User/InMemoryUserProvider.php new file mode 100644 index 0000000..6e1febe --- /dev/null +++ b/User/InMemoryUserProvider.php @@ -0,0 +1,78 @@ +<?php + +namespace Symfony\Component\Security\User; + +use Symfony\Component\Security\Exception\UsernameNotFoundException; +use Symfony\Component\Security\Exception\AccessDeniedException; +use Symfony\Component\Security\Authentication\Token\UsernamePasswordToken; + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien.potencier@symfony-project.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * InMemoryUserProvider is a simple non persistent user provider. + * + * Useful for testing, demonstration, prototyping, and for + * simple needs (a backend with a unique admin for instance) + * + * @author Fabien Potencier <fabien.potencier@symfony-project.com> + */ +class InMemoryUserProvider implements UserProviderInterface +{ + protected $users; + + /** + * Constructor. + * + * The user array is hash where the keys are usernames and the values are + * an array of attributes: 'password', 'enabled', and 'roles'. + * + * @param array $users An array of users + */ + public function __construct(array $users = array()) + { + foreach ($users as $username => $attributes) { + $password = isset($attributes['password']) ? $attributes['password'] : null; + $enabled = isset($attributes['enabled']) ? $attributes['enabled'] : true; + $roles = isset($attributes['roles']) ? $attributes['roles'] : array(); + $user = new User($username, $password, $roles, $enabled, true, true, true); + + $this->createUser($user); + } + } + + /** + * Adds a new User to the provider. + * + * @param AccountInterface $user A AccountInterface instance + */ + public function createUser(AccountInterface $user) + { + if (isset($this->users[strtolower($user->getUsername())])) { + throw new \LogicException('Another user with the same username already exist.'); + } + + $this->users[strtolower($user->getUsername())] = $user; + } + + /** + * {@inheritdoc} + */ + public function loadUserByUsername($username) + { + if (!isset($this->users[strtolower($username)])) { + throw new UsernameNotFoundException(sprintf('Username "%s" does not exist.', $username)); + } + + $user = $this->users[strtolower($username)]; + + return new User($user->getUsername(), $user->getPassword(), $user->getRoles(), $user->isEnabled(), $user->isAccountNonExpired(), + $user->isCredentialsNonExpired(), $user->isAccountNonLocked()); + } +} diff --git a/User/User.php b/User/User.php new file mode 100644 index 0000000..39c25fc --- /dev/null +++ b/User/User.php @@ -0,0 +1,124 @@ +<?php + +namespace Symfony\Component\Security\User; + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien.potencier@symfony-project.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * User is the user implementation used by the in-memory user provider. + * + * This should not be used for anything else. + * + * @author Fabien Potencier <fabien.potencier@symfony-project.com> + */ +class User implements AdvancedAccountInterface +{ + protected $username; + protected $password; + protected $accountNonExpired; + protected $credentialsNonExpired; + protected $accountNonLocked; + protected $roles; + + public function __construct($username, $password, array $roles = array(), $enabled = true, $accountNonExpired = true, $credentialsNonExpired = true, $accountNonLocked = true) + { + if (empty($username)) { + throw new \InvalidArgumentException('The username cannot be empty.'); + } + + $this->username = $username; + $this->password = $password; + $this->enabled = $enabled; + $this->accountNonExpired = $accountNonExpired; + $this->credentialsNonExpired = $credentialsNonExpired; + $this->accountNonLocked = $accountNonLocked; + $this->roles = $roles; + } + + /** + * {@inheritdoc} + */ + public function __toString() + { + return $this->username; + } + + /** + * {@inheritdoc} + */ + public function getRoles() + { + return $this->roles; + } + + /** + * {@inheritdoc} + */ + public function getPassword() + { + return $this->password; + } + + /** + * {@inheritdoc} + */ + public function getSalt() + { + return null; + } + + /** + * {@inheritdoc} + */ + public function getUsername() + { + return $this->username; + } + + /** + * {@inheritdoc} + */ + public function isAccountNonExpired() + { + return $this->accountNonExpired; + } + + /** + * {@inheritdoc} + */ + public function isAccountNonLocked() + { + return $this->accountNonLocked; + } + + /** + * {@inheritdoc} + */ + public function isCredentialsNonExpired() + { + return $this->credentialsNonExpired; + } + + /** + * {@inheritdoc} + */ + public function isEnabled() + { + return $this->enabled; + } + + /** + * {@inheritdoc} + */ + public function eraseCredentials() + { + $this->password = null; + } +} diff --git a/User/UserProviderInterface.php b/User/UserProviderInterface.php new file mode 100644 index 0000000..0dd12ab --- /dev/null +++ b/User/UserProviderInterface.php @@ -0,0 +1,35 @@ +<?php + +namespace Symfony\Component\Security\User; + +use Symfony\Component\Security\Exception\UsernameNotFoundException; + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien.potencier@symfony-project.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * UserProviderInterface is the implementation that all user provider must implement. + * + * @author Fabien Potencier <fabien.potencier@symfony-project.com> + */ +interface UserProviderInterface +{ + /** + * Loads the user for the given username. + * + * This method must throw UsernameNotFoundException if the user is not found. + * + * @param string $username The username + * + * @return AccountInterface A user instance + * + * @throws UsernameNotFoundException if the user is not found + */ + function loadUserByUsername($username); +} |