summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Authentication/AuthenticationManagerInterface.php35
-rw-r--r--Authentication/AuthenticationProviderManager.php120
-rw-r--r--Authentication/EntryPoint/AuthenticationEntryPointInterface.php31
-rw-r--r--Authentication/Provider/AnonymousAuthenticationProvider.php60
-rw-r--r--Authentication/Provider/AuthenticationProviderInterface.php34
-rw-r--r--Authentication/Provider/DaoAuthenticationProvider.php88
-rw-r--r--Authentication/Provider/PreAuthenticatedAuthenticationProvider.php80
-rw-r--r--Authentication/Provider/UserAuthenticationProvider.php110
-rw-r--r--Authentication/Token/AnonymousToken.php58
-rw-r--r--Authentication/Token/PreAuthenticatedToken.php44
-rw-r--r--Authentication/Token/Token.php156
-rw-r--r--Authentication/Token/TokenInterface.php69
-rw-r--r--Authentication/Token/UsernamePasswordToken.php56
-rw-r--r--Authorization/AccessDecisionManager.php237
-rw-r--r--Authorization/AccessDecisionManagerInterface.php53
-rw-r--r--Authorization/Voter/AuthenticatedVoter.php76
-rw-r--r--Authorization/Voter/RoleHierarchyVoter.php40
-rw-r--r--Authorization/Voter/RoleVoter.php79
-rw-r--r--Authorization/Voter/VoterInterface.php58
-rw-r--r--Encoder/BasePasswordEncoder.php65
-rw-r--r--Encoder/MessageDigestPasswordEncoder.php61
-rw-r--r--Encoder/PasswordEncoderInterface.php41
-rw-r--r--Encoder/PlaintextPasswordEncoder.php49
-rw-r--r--Exception/AccessDeniedException.php25
-rw-r--r--Exception/AccountExpiredException.php21
-rw-r--r--Exception/AccountStatusException.php22
-rw-r--r--Exception/AuthenticationCredentialsNotFoundException.php22
-rw-r--r--Exception/AuthenticationException.php41
-rw-r--r--Exception/AuthenticationServiceException.php21
-rw-r--r--Exception/BadCredentialsException.php25
-rw-r--r--Exception/CredentialsExpiredException.php21
-rw-r--r--Exception/DisabledException.php21
-rw-r--r--Exception/InsufficientAuthenticationException.php23
-rw-r--r--Exception/LockedException.php21
-rw-r--r--Exception/NonceExpiredException.php27
-rw-r--r--Exception/ProviderNotFoundException.php22
-rw-r--r--Exception/UsernameNotFoundException.php21
-rw-r--r--Role/Role.php40
-rw-r--r--Role/RoleHierarchy.php77
-rw-r--r--Role/RoleHierarchyInterface.php33
-rw-r--r--Role/RoleInterface.php36
-rw-r--r--Role/SwitchUserRole.php47
-rw-r--r--SecurityContext.php75
-rw-r--r--User/AccountChecker.php61
-rw-r--r--User/AccountCheckerInterface.php36
-rw-r--r--User/AccountInterface.php60
-rw-r--r--User/AdvancedAccountInterface.php48
-rw-r--r--User/InMemoryUserProvider.php78
-rw-r--r--User/User.php124
-rw-r--r--User/UserProviderInterface.php35
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);
+}