Actualización

This commit is contained in:
Xes
2025-04-10 12:24:57 +02:00
parent 8969cc929d
commit 45420b6f0d
39760 changed files with 4303286 additions and 0 deletions

View File

@@ -0,0 +1,3 @@
vendor/
composer.lock
phpunit.xml

View File

@@ -0,0 +1,123 @@
CHANGELOG
=========
3.2.0
-----
* Added the `SecurityUserValueResolver` to inject the security users in actions via
`Symfony\Component\Security\Core\User\UserInterface` in the method signature.
3.0.0
-----
* Removed the `security.context` service.
2.8.0
-----
* deprecated the `key` setting of `anonymous`, `remember_me` and `http_digest`
in favor of the `secret` setting.
* deprecated the `intention` firewall listener setting in favor of the `csrf_token_id`.
2.6.0
-----
* Added the possibility to override the default success/failure handler
to get the provider key and the options injected
* Deprecated the `security.context` service for the `security.token_storage` and
`security.authorization_checker` services.
2.4.0
-----
* Added 'host' option to firewall configuration
* Added 'csrf_token_generator' and 'csrf_token_id' options to firewall logout
listener configuration to supersede/alias 'csrf_provider' and 'intention'
respectively
* Moved 'security.secure_random' service configuration to FrameworkBundle
2.3.0
-----
* allowed for multiple IP address in security access_control rules
2.2.0
-----
* Added PBKDF2 Password encoder
* Added BCrypt password encoder
2.1.0
-----
* [BC BREAK] The custom factories for the firewall configuration are now
registered during the build method of bundles instead of being registered
by the end-user (you need to remove the 'factories' keys in your security
configuration).
* [BC BREAK] The Firewall listener is now registered after the Router one. This
means that specific Firewall URLs (like /login_check and /logout must now
have proper route defined in your routing configuration)
* [BC BREAK] refactored the user provider configuration. The configuration
changed for the chain provider and the memory provider:
Before:
``` yaml
security:
providers:
my_chain_provider:
providers: [my_memory_provider, my_doctrine_provider]
my_memory_provider:
users:
toto: { password: foobar, roles: [ROLE_USER] }
foo: { password: bar, roles: [ROLE_USER, ROLE_ADMIN] }
```
After:
``` yaml
security:
providers:
my_chain_provider:
chain:
providers: [my_memory_provider, my_doctrine_provider]
my_memory_provider:
memory:
users:
toto: { password: foobar, roles: [ROLE_USER] }
foo: { password: bar, roles: [ROLE_USER, ROLE_ADMIN] }
```
* [BC BREAK] Method `equals` was removed from `UserInterface` to its own new
`EquatableInterface`. The user class can now implement this interface to override
the default implementation of users equality test.
* added a validator for the user password
* added 'erase_credentials' as a configuration key (true by default)
* added new events: `security.authentication.success` and `security.authentication.failure`
fired on authentication success/failure, regardless of authentication method,
events are defined in new event class: `Symfony\Component\Security\Core\AuthenticationEvents`.
* Added optional CSRF protection to LogoutListener:
``` yaml
security:
firewalls:
default:
logout:
path: /logout_path
target: /
csrf_parameter: _csrf_token # Optional (defaults to "_csrf_token")
csrf_provider: security.csrf.token_generator # Required to enable protection
intention: logout # Optional (defaults to "logout")
```
If the LogoutListener has CSRF protection enabled but cannot validate a token,
then a LogoutException will be thrown.
* Added `logout_url` templating helper and Twig extension, which may be used to
generate logout URL's within templates. The security firewall's config key
must be specified. If a firewall's logout listener has CSRF protection
enabled, a token will be automatically added to the generated URL.

View File

@@ -0,0 +1,85 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\SecurityBundle\Command;
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Doctrine\DBAL\Schema\SchemaException;
/**
* Installs the tables required by the ACL system.
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
class InitAclCommand extends ContainerAwareCommand
{
/**
* {@inheritdoc}
*/
public function isEnabled()
{
if (!$this->getContainer()->has('security.acl.dbal.connection')) {
return false;
}
return parent::isEnabled();
}
/**
* {@inheritdoc}
*/
protected function configure()
{
$this
->setName('init:acl')
->setDescription('Mounts ACL tables in the database')
->setHelp(<<<'EOF'
The <info>%command.name%</info> command mounts ACL tables in the database.
<info>php %command.full_name%</info>
The name of the DBAL connection must be configured in your <info>app/config/security.yml</info> configuration file in the <info>security.acl.connection</info> variable.
<info>security:
acl:
connection: default</info>
EOF
)
;
}
/**
* {@inheritdoc}
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$container = $this->getContainer();
$connection = $container->get('security.acl.dbal.connection');
$schema = $container->get('security.acl.dbal.schema');
try {
$schema->addToSchema($connection->getSchemaManager()->createSchema());
} catch (SchemaException $e) {
$output->writeln('Aborting: '.$e->getMessage());
return 1;
}
foreach ($schema->toSql($connection->getDatabasePlatform()) as $sql) {
$connection->exec($sql);
}
$output->writeln('ACL tables have been initialized successfully.');
}
}

View File

@@ -0,0 +1,173 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\SecurityBundle\Command;
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Security\Acl\Domain\ObjectIdentity;
use Symfony\Component\Security\Acl\Domain\RoleSecurityIdentity;
use Symfony\Component\Security\Acl\Domain\UserSecurityIdentity;
use Symfony\Component\Security\Acl\Exception\AclAlreadyExistsException;
use Symfony\Component\Security\Acl\Permission\MaskBuilder;
use Symfony\Component\Security\Acl\Model\MutableAclProviderInterface;
/**
* Sets ACL for objects.
*
* @author Kévin Dunglas <kevin@les-tilleuls.coop>
*/
class SetAclCommand extends ContainerAwareCommand
{
/**
* {@inheritdoc}
*/
public function isEnabled()
{
if (!$this->getContainer()->has('security.acl.provider')) {
return false;
}
$provider = $this->getContainer()->get('security.acl.provider');
if (!$provider instanceof MutableAclProviderInterface) {
return false;
}
return parent::isEnabled();
}
/**
* {@inheritdoc}
*/
protected function configure()
{
$this
->setName('acl:set')
->setDescription('Sets ACL for objects')
->setHelp(<<<EOF
The <info>%command.name%</info> command sets ACL.
The ACL system must have been initialized with the <info>init:acl</info> command.
To set <comment>VIEW</comment> and <comment>EDIT</comment> permissions for the user <comment>kevin</comment> on the instance of
<comment>Acme\MyClass</comment> having the identifier <comment>42</comment>:
<info>php %command.full_name% --user=Symfony/Component/Security/Core/User/User:kevin VIEW EDIT Acme/MyClass:42</info>
Note that you can use <comment>/</comment> instead of <comment>\\ </comment>for the namespace delimiter to avoid any
problem.
To set permissions for a role, use the <info>--role</info> option:
<info>php %command.full_name% --role=ROLE_USER VIEW Acme/MyClass:1936</info>
To set permissions at the class scope, use the <info>--class-scope</info> option:
<info>php %command.full_name% --class-scope --user=Symfony/Component/Security/Core/User/User:anne OWNER Acme/MyClass:42</info>
EOF
)
->addArgument('arguments', InputArgument::IS_ARRAY | InputArgument::REQUIRED, 'A list of permissions and object identities (class name and ID separated by a column)')
->addOption('user', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'A list of security identities')
->addOption('role', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'A list of roles')
->addOption('class-scope', null, InputOption::VALUE_NONE, 'Use class-scope entries')
;
}
/**
* {@inheritdoc}
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
// Parse arguments
$objectIdentities = array();
$maskBuilder = $this->getMaskBuilder();
foreach ($input->getArgument('arguments') as $argument) {
$data = explode(':', $argument, 2);
if (count($data) > 1) {
$objectIdentities[] = new ObjectIdentity($data[1], strtr($data[0], '/', '\\'));
} else {
$maskBuilder->add($data[0]);
}
}
// Build permissions mask
$mask = $maskBuilder->get();
$userOption = $input->getOption('user');
$roleOption = $input->getOption('role');
$classScopeOption = $input->getOption('class-scope');
if (empty($userOption) && empty($roleOption)) {
throw new \InvalidArgumentException('A Role or a User must be specified.');
}
// Create security identities
$securityIdentities = array();
if ($userOption) {
foreach ($userOption as $user) {
$data = explode(':', $user, 2);
if (count($data) === 1) {
throw new \InvalidArgumentException('The user must follow the format "Acme/MyUser:username".');
}
$securityIdentities[] = new UserSecurityIdentity($data[1], strtr($data[0], '/', '\\'));
}
}
if ($roleOption) {
foreach ($roleOption as $role) {
$securityIdentities[] = new RoleSecurityIdentity($role);
}
}
/** @var $container \Symfony\Component\DependencyInjection\ContainerInterface */
$container = $this->getContainer();
/** @var $aclProvider MutableAclProviderInterface */
$aclProvider = $container->get('security.acl.provider');
// Sets ACL
foreach ($objectIdentities as $objectIdentity) {
// Creates a new ACL if it does not already exist
try {
$aclProvider->createAcl($objectIdentity);
} catch (AclAlreadyExistsException $e) {
}
$acl = $aclProvider->findAcl($objectIdentity, $securityIdentities);
foreach ($securityIdentities as $securityIdentity) {
if ($classScopeOption) {
$acl->insertClassAce($securityIdentity, $mask);
} else {
$acl->insertObjectAce($securityIdentity, $mask);
}
}
$aclProvider->updateAcl($acl);
}
}
/**
* Gets the mask builder.
*
* @return MaskBuilder
*/
protected function getMaskBuilder()
{
return new MaskBuilder();
}
}

View File

@@ -0,0 +1,169 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\SecurityBundle\Command;
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\Question;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\Security\Core\Encoder\BCryptPasswordEncoder;
/**
* Encode a user's password.
*
* @author Sarah Khalil <mkhalil.sarah@gmail.com>
*/
class UserPasswordEncoderCommand extends ContainerAwareCommand
{
/**
* {@inheritdoc}
*/
protected function configure()
{
$this
->setName('security:encode-password')
->setDescription('Encodes a password.')
->addArgument('password', InputArgument::OPTIONAL, 'The plain password to encode.')
->addArgument('user-class', InputArgument::OPTIONAL, 'The User entity class path associated with the encoder used to encode the password.', 'Symfony\Component\Security\Core\User\User')
->addOption('empty-salt', null, InputOption::VALUE_NONE, 'Do not generate a salt or let the encoder generate one.')
->setHelp(<<<EOF
The <info>%command.name%</info> command encodes passwords according to your
security configuration. This command is mainly used to generate passwords for
the <comment>in_memory</comment> user provider type and for changing passwords
in the database while developing the application.
Suppose that you have the following security configuration in your application:
<comment>
# app/config/security.yml
security:
encoders:
Symfony\Component\Security\Core\User\User: plaintext
AppBundle\Entity\User: bcrypt
</comment>
If you execute the command non-interactively, the default Symfony User class
is used and a random salt is generated to encode the password:
<info>php %command.full_name% --no-interaction [password]</info>
Pass the full user class path as the second argument to encode passwords for
your own entities:
<info>php %command.full_name% --no-interaction [password] AppBundle\Entity\User</info>
Executing the command interactively allows you to generate a random salt for
encoding the password:
<info>php %command.full_name% [password] AppBundle\Entity\User</info>
In case your encoder doesn't require a salt, add the <comment>empty-salt</comment> option:
<info>php %command.full_name% --empty-salt [password] AppBundle\Entity\User</info>
EOF
)
;
}
/**
* {@inheritdoc}
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$io = new SymfonyStyle($input, $output);
$input->isInteractive() ? $io->title('Symfony Password Encoder Utility') : $io->newLine();
$password = $input->getArgument('password');
$userClass = $input->getArgument('user-class');
$emptySalt = $input->getOption('empty-salt');
$encoder = $this->getContainer()->get('security.encoder_factory')->getEncoder($userClass);
$bcryptWithoutEmptySalt = !$emptySalt && $encoder instanceof BCryptPasswordEncoder;
if ($bcryptWithoutEmptySalt) {
$emptySalt = true;
}
if (!$password) {
if (!$input->isInteractive()) {
$io->error('The password must not be empty.');
return 1;
}
$passwordQuestion = $this->createPasswordQuestion();
$password = $io->askQuestion($passwordQuestion);
}
$salt = null;
if ($input->isInteractive() && !$emptySalt) {
$emptySalt = true;
$io->note('The command will take care of generating a salt for you. Be aware that some encoders advise to let them generate their own salt. If you\'re using one of those encoders, please answer \'no\' to the question below. '.PHP_EOL.'Provide the \'empty-salt\' option in order to let the encoder handle the generation itself.');
if ($io->confirm('Confirm salt generation ?')) {
$salt = $this->generateSalt();
$emptySalt = false;
}
} elseif (!$emptySalt) {
$salt = $this->generateSalt();
}
$encodedPassword = $encoder->encodePassword($password, $salt);
$rows = array(
array('Encoder used', get_class($encoder)),
array('Encoded password', $encodedPassword),
);
if (!$emptySalt) {
$rows[] = array('Generated salt', $salt);
}
$io->table(array('Key', 'Value'), $rows);
if (!$emptySalt) {
$io->note(sprintf('Make sure that your salt storage field fits the salt length: %s chars', strlen($salt)));
} elseif ($bcryptWithoutEmptySalt) {
$io->note('Bcrypt encoder used: the encoder generated its own built-in salt.');
}
$io->success('Password encoding succeeded');
}
/**
* Create the password question to ask the user for the password to be encoded.
*
* @return Question
*/
private function createPasswordQuestion()
{
$passwordQuestion = new Question('Type in your password to be encoded');
return $passwordQuestion->setValidator(function ($value) {
if ('' === trim($value)) {
throw new \Exception('The password must not be empty.');
}
return $value;
})->setHidden(true)->setMaxAttempts(20);
}
private function generateSalt()
{
return base64_encode(random_bytes(30));
}
}

View File

@@ -0,0 +1,302 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\SecurityBundle\DataCollector;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Core\Role\RoleHierarchyInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\DataCollector\DataCollector;
use Symfony\Component\Security\Core\Role\RoleInterface;
use Symfony\Component\Security\Http\Logout\LogoutUrlGenerator;
use Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface;
use Symfony\Component\Security\Core\Authorization\DebugAccessDecisionManager;
use Symfony\Component\VarDumper\Cloner\Data;
use Symfony\Component\Security\Http\FirewallMapInterface;
use Symfony\Bundle\SecurityBundle\Security\FirewallMap;
/**
* SecurityDataCollector.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class SecurityDataCollector extends DataCollector
{
private $tokenStorage;
private $roleHierarchy;
private $logoutUrlGenerator;
private $accessDecisionManager;
private $firewallMap;
/**
* Constructor.
*
* @param TokenStorageInterface|null $tokenStorage
* @param RoleHierarchyInterface|null $roleHierarchy
* @param LogoutUrlGenerator|null $logoutUrlGenerator
* @param AccessDecisionManagerInterface|null $accessDecisionManager
* @param FirewallMapInterface|null $firewallMap
*/
public function __construct(TokenStorageInterface $tokenStorage = null, RoleHierarchyInterface $roleHierarchy = null, LogoutUrlGenerator $logoutUrlGenerator = null, AccessDecisionManagerInterface $accessDecisionManager = null, FirewallMapInterface $firewallMap = null)
{
$this->tokenStorage = $tokenStorage;
$this->roleHierarchy = $roleHierarchy;
$this->logoutUrlGenerator = $logoutUrlGenerator;
$this->accessDecisionManager = $accessDecisionManager;
$this->firewallMap = $firewallMap;
}
/**
* {@inheritdoc}
*/
public function collect(Request $request, Response $response, \Exception $exception = null)
{
if (null === $this->tokenStorage) {
$this->data = array(
'enabled' => false,
'authenticated' => false,
'token' => null,
'token_class' => null,
'logout_url' => null,
'user' => '',
'roles' => array(),
'inherited_roles' => array(),
'supports_role_hierarchy' => null !== $this->roleHierarchy,
);
} elseif (null === $token = $this->tokenStorage->getToken()) {
$this->data = array(
'enabled' => true,
'authenticated' => false,
'token' => null,
'token_class' => null,
'logout_url' => null,
'user' => '',
'roles' => array(),
'inherited_roles' => array(),
'supports_role_hierarchy' => null !== $this->roleHierarchy,
);
} else {
$inheritedRoles = array();
$assignedRoles = $token->getRoles();
if (null !== $this->roleHierarchy) {
$allRoles = $this->roleHierarchy->getReachableRoles($assignedRoles);
foreach ($allRoles as $role) {
if (!in_array($role, $assignedRoles, true)) {
$inheritedRoles[] = $role;
}
}
}
$logoutUrl = null;
try {
if (null !== $this->logoutUrlGenerator) {
$logoutUrl = $this->logoutUrlGenerator->getLogoutPath();
}
} catch (\Exception $e) {
// fail silently when the logout URL cannot be generated
}
$this->data = array(
'enabled' => true,
'authenticated' => $token->isAuthenticated(),
'token' => $this->cloneVar($token),
'token_class' => get_class($token),
'logout_url' => $logoutUrl,
'user' => $token->getUsername(),
'roles' => $this->cloneVar(array_map(function (RoleInterface $role) { return $role->getRole(); }, $assignedRoles)),
'inherited_roles' => $this->cloneVar(array_unique(array_map(function (RoleInterface $role) { return $role->getRole(); }, $inheritedRoles))),
'supports_role_hierarchy' => null !== $this->roleHierarchy,
);
}
// collect voters and access decision manager information
if ($this->accessDecisionManager instanceof DebugAccessDecisionManager) {
$this->data['access_decision_log'] = array_map(function ($decision) {
$decision['object'] = $this->cloneVar($decision['object']);
return $decision;
}, $this->accessDecisionManager->getDecisionLog());
$this->data['voter_strategy'] = $this->accessDecisionManager->getStrategy();
foreach ($this->accessDecisionManager->getVoters() as $voter) {
$this->data['voters'][] = get_class($voter);
}
} else {
$this->data['access_decision_log'] = array();
$this->data['voter_strategy'] = 'unknown';
$this->data['voters'] = array();
}
// collect firewall context information
$this->data['firewall'] = null;
if ($this->firewallMap instanceof FirewallMap) {
$firewallConfig = $this->firewallMap->getFirewallConfig($request);
if (null !== $firewallConfig) {
$this->data['firewall'] = array(
'name' => $firewallConfig->getName(),
'allows_anonymous' => $firewallConfig->allowsAnonymous(),
'request_matcher' => $firewallConfig->getRequestMatcher(),
'security_enabled' => $firewallConfig->isSecurityEnabled(),
'stateless' => $firewallConfig->isStateless(),
'provider' => $firewallConfig->getProvider(),
'context' => $firewallConfig->getContext(),
'entry_point' => $firewallConfig->getEntryPoint(),
'access_denied_handler' => $firewallConfig->getAccessDeniedHandler(),
'access_denied_url' => $firewallConfig->getAccessDeniedUrl(),
'user_checker' => $firewallConfig->getUserChecker(),
'listeners' => $this->cloneVar($firewallConfig->getListeners()),
);
}
}
}
/**
* Checks if security is enabled.
*
* @return bool true if security is enabled, false otherwise
*/
public function isEnabled()
{
return $this->data['enabled'];
}
/**
* Gets the user.
*
* @return string The user
*/
public function getUser()
{
return $this->data['user'];
}
/**
* Gets the roles of the user.
*
* @return array The roles
*/
public function getRoles()
{
return $this->data['roles'];
}
/**
* Gets the inherited roles of the user.
*
* @return array The inherited roles
*/
public function getInheritedRoles()
{
return $this->data['inherited_roles'];
}
/**
* Checks if the data contains information about inherited roles. Still the inherited
* roles can be an empty array.
*
* @return bool true if the profile was contains inherited role information
*/
public function supportsRoleHierarchy()
{
return $this->data['supports_role_hierarchy'];
}
/**
* Checks if the user is authenticated or not.
*
* @return bool true if the user is authenticated, false otherwise
*/
public function isAuthenticated()
{
return $this->data['authenticated'];
}
/**
* Get the class name of the security token.
*
* @return string The token
*/
public function getTokenClass()
{
return $this->data['token_class'];
}
/**
* Get the full security token class as Data object.
*
* @return Data
*/
public function getToken()
{
return $this->data['token'];
}
/**
* Get the provider key (i.e. the name of the active firewall).
*
* @return string The provider key
*/
public function getLogoutUrl()
{
return $this->data['logout_url'];
}
/**
* Returns the FQCN of the security voters enabled in the application.
*
* @return string[]
*/
public function getVoters()
{
return $this->data['voters'];
}
/**
* Returns the strategy configured for the security voters.
*
* @return string
*/
public function getVoterStrategy()
{
return $this->data['voter_strategy'];
}
/**
* Returns the log of the security decisions made by the access decision manager.
*
* @return array
*/
public function getAccessDecisionLog()
{
return $this->data['access_decision_log'];
}
/**
* Returns the configuration of the current firewall context.
*
* @return array
*/
public function getFirewall()
{
return $this->data['firewall'];
}
/**
* {@inheritdoc}
*/
public function getName()
{
return 'security';
}
}

View File

@@ -0,0 +1,45 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\SecurityBundle\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\Compiler\PriorityTaggedServiceTrait;
use Symfony\Component\DependencyInjection\Exception\LogicException;
/**
* Adds all configured security voters to the access decision manager.
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
class AddSecurityVotersPass implements CompilerPassInterface
{
use PriorityTaggedServiceTrait;
/**
* {@inheritdoc}
*/
public function process(ContainerBuilder $container)
{
if (!$container->hasDefinition('security.access.decision_manager')) {
return;
}
$voters = $this->findAndSortTaggedServices('security.voter', $container);
if (!$voters) {
throw new LogicException('No security voters found. You need to tag at least one with "security.voter"');
}
$adm = $container->getDefinition($container->hasDefinition('debug.security.access.decision_manager') ? 'debug.security.access.decision_manager' : 'security.access.decision_manager');
$adm->addMethodCall('setVoters', array($voters));
}
}

View File

@@ -0,0 +1,39 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\SecurityBundle\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
/**
* Uses the session domain to restrict allowed redirection targets.
*
* @author Nicolas Grekas <p@tchwork.com>
*/
class AddSessionDomainConstraintPass implements CompilerPassInterface
{
/**
* {@inheritdoc}
*/
public function process(ContainerBuilder $container)
{
if (!$container->hasParameter('session.storage.options') || !$container->has('security.http_utils')) {
return;
}
$sessionOptions = $container->getParameter('session.storage.options');
$domainRegexp = empty($sessionOptions['cookie_domain']) ? '%s' : sprintf('(?:%%s|(?:.+\.)?%s)', preg_quote(trim($sessionOptions['cookie_domain'], '.')));
$domainRegexp = (empty($sessionOptions['cookie_secure']) ? 'https?://' : 'https://').$domainRegexp;
$container->findDefinition('security.http_utils')->addArgument(sprintf('{^%s$}i', $domainRegexp));
}
}

View File

@@ -0,0 +1,420 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\SecurityBundle\DependencyInjection;
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\AbstractFactory;
use Symfony\Component\Security\Core\Authorization\AccessDecisionManager;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
use Symfony\Component\Config\Definition\ConfigurationInterface;
use Symfony\Component\Security\Http\Session\SessionAuthenticationStrategy;
/**
* This class contains the configuration information.
*
* This information is for the following tags:
*
* * security.config
* * security.acl
*
* This information is solely responsible for how the different configuration
* sections are normalized, and merged.
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
class MainConfiguration implements ConfigurationInterface
{
private $factories;
private $userProviderFactories;
/**
* Constructor.
*
* @param array $factories
* @param array $userProviderFactories
*/
public function __construct(array $factories, array $userProviderFactories)
{
$this->factories = $factories;
$this->userProviderFactories = $userProviderFactories;
}
/**
* Generates the configuration tree builder.
*
* @return TreeBuilder The tree builder
*/
public function getConfigTreeBuilder()
{
$tb = new TreeBuilder();
$rootNode = $tb->root('security');
$rootNode
->children()
->scalarNode('access_denied_url')->defaultNull()->example('/foo/error403')->end()
->enumNode('session_fixation_strategy')
->values(array(SessionAuthenticationStrategy::NONE, SessionAuthenticationStrategy::MIGRATE, SessionAuthenticationStrategy::INVALIDATE))
->defaultValue(SessionAuthenticationStrategy::MIGRATE)
->end()
->booleanNode('hide_user_not_found')->defaultTrue()->end()
->booleanNode('always_authenticate_before_granting')->defaultFalse()->end()
->booleanNode('erase_credentials')->defaultTrue()->end()
->arrayNode('access_decision_manager')
->addDefaultsIfNotSet()
->children()
->enumNode('strategy')
->values(array(AccessDecisionManager::STRATEGY_AFFIRMATIVE, AccessDecisionManager::STRATEGY_CONSENSUS, AccessDecisionManager::STRATEGY_UNANIMOUS))
->defaultValue(AccessDecisionManager::STRATEGY_AFFIRMATIVE)
->end()
->booleanNode('allow_if_all_abstain')->defaultFalse()->end()
->booleanNode('allow_if_equal_granted_denied')->defaultTrue()->end()
->end()
->end()
->end()
;
$this->addAclSection($rootNode);
$this->addEncodersSection($rootNode);
$this->addProvidersSection($rootNode);
$this->addFirewallsSection($rootNode, $this->factories);
$this->addAccessControlSection($rootNode);
$this->addRoleHierarchySection($rootNode);
return $tb;
}
private function addAclSection(ArrayNodeDefinition $rootNode)
{
$rootNode
->children()
->arrayNode('acl')
->children()
->scalarNode('connection')
->defaultNull()
->info('any name configured in doctrine.dbal section')
->end()
->arrayNode('cache')
->addDefaultsIfNotSet()
->children()
->scalarNode('id')->end()
->scalarNode('prefix')->defaultValue('sf2_acl_')->end()
->end()
->end()
->scalarNode('provider')->end()
->arrayNode('tables')
->addDefaultsIfNotSet()
->children()
->scalarNode('class')->defaultValue('acl_classes')->end()
->scalarNode('entry')->defaultValue('acl_entries')->end()
->scalarNode('object_identity')->defaultValue('acl_object_identities')->end()
->scalarNode('object_identity_ancestors')->defaultValue('acl_object_identity_ancestors')->end()
->scalarNode('security_identity')->defaultValue('acl_security_identities')->end()
->end()
->end()
->arrayNode('voter')
->addDefaultsIfNotSet()
->children()
->booleanNode('allow_if_object_identity_unavailable')->defaultTrue()->end()
->end()
->end()
->end()
->end()
->end()
;
}
private function addRoleHierarchySection(ArrayNodeDefinition $rootNode)
{
$rootNode
->fixXmlConfig('role', 'role_hierarchy')
->children()
->arrayNode('role_hierarchy')
->useAttributeAsKey('id')
->prototype('array')
->performNoDeepMerging()
->beforeNormalization()->ifString()->then(function ($v) { return array('value' => $v); })->end()
->beforeNormalization()
->ifTrue(function ($v) { return is_array($v) && isset($v['value']); })
->then(function ($v) { return preg_split('/\s*,\s*/', $v['value']); })
->end()
->prototype('scalar')->end()
->end()
->end()
->end()
;
}
private function addAccessControlSection(ArrayNodeDefinition $rootNode)
{
$rootNode
->fixXmlConfig('rule', 'access_control')
->children()
->arrayNode('access_control')
->cannotBeOverwritten()
->prototype('array')
->fixXmlConfig('ip')
->fixXmlConfig('method')
->children()
->scalarNode('requires_channel')->defaultNull()->end()
->scalarNode('path')
->defaultNull()
->info('use the urldecoded format')
->example('^/path to resource/')
->end()
->scalarNode('host')->defaultNull()->end()
->arrayNode('ips')
->beforeNormalization()->ifString()->then(function ($v) { return array($v); })->end()
->prototype('scalar')->end()
->end()
->arrayNode('methods')
->beforeNormalization()->ifString()->then(function ($v) { return preg_split('/\s*,\s*/', $v); })->end()
->prototype('scalar')->end()
->end()
->scalarNode('allow_if')->defaultNull()->end()
->end()
->fixXmlConfig('role')
->children()
->arrayNode('roles')
->beforeNormalization()->ifString()->then(function ($v) { return preg_split('/\s*,\s*/', $v); })->end()
->prototype('scalar')->end()
->end()
->end()
->end()
->end()
->end()
;
}
private function addFirewallsSection(ArrayNodeDefinition $rootNode, array $factories)
{
$firewallNodeBuilder = $rootNode
->fixXmlConfig('firewall')
->children()
->arrayNode('firewalls')
->isRequired()
->requiresAtLeastOneElement()
->disallowNewKeysInSubsequentConfigs()
->useAttributeAsKey('name')
->prototype('array')
->children()
;
$firewallNodeBuilder
->scalarNode('pattern')->end()
->scalarNode('host')->end()
->arrayNode('methods')
->beforeNormalization()->ifString()->then(function ($v) { return preg_split('/\s*,\s*/', $v); })->end()
->prototype('scalar')->end()
->end()
->booleanNode('security')->defaultTrue()->end()
->scalarNode('user_checker')
->defaultValue('security.user_checker')
->treatNullLike('security.user_checker')
->info('The UserChecker to use when authenticating users in this firewall.')
->end()
->scalarNode('request_matcher')->end()
->scalarNode('access_denied_url')->end()
->scalarNode('access_denied_handler')->end()
->scalarNode('entry_point')->end()
->scalarNode('provider')->end()
->booleanNode('stateless')->defaultFalse()->end()
->scalarNode('context')->cannotBeEmpty()->end()
->arrayNode('logout')
->treatTrueLike(array())
->canBeUnset()
->children()
->scalarNode('csrf_parameter')->defaultValue('_csrf_token')->end()
->scalarNode('csrf_token_generator')->cannotBeEmpty()->end()
->scalarNode('csrf_token_id')->defaultValue('logout')->end()
->scalarNode('path')->defaultValue('/logout')->end()
->scalarNode('target')->defaultValue('/')->end()
->scalarNode('success_handler')->end()
->booleanNode('invalidate_session')->defaultTrue()->end()
->end()
->fixXmlConfig('delete_cookie')
->children()
->arrayNode('delete_cookies')
->beforeNormalization()
->ifTrue(function ($v) { return is_array($v) && is_int(key($v)); })
->then(function ($v) { return array_map(function ($v) { return array('name' => $v); }, $v); })
->end()
->useAttributeAsKey('name')
->prototype('array')
->children()
->scalarNode('path')->defaultNull()->end()
->scalarNode('domain')->defaultNull()->end()
->end()
->end()
->end()
->end()
->fixXmlConfig('handler')
->children()
->arrayNode('handlers')
->prototype('scalar')->end()
->end()
->end()
->end()
->arrayNode('anonymous')
->canBeUnset()
->children()
->scalarNode('secret')->defaultValue(uniqid('', true))->end()
->end()
->end()
->arrayNode('switch_user')
->canBeUnset()
->children()
->scalarNode('provider')->end()
->scalarNode('parameter')->defaultValue('_switch_user')->end()
->scalarNode('role')->defaultValue('ROLE_ALLOWED_TO_SWITCH')->end()
->end()
->end()
;
$abstractFactoryKeys = array();
foreach ($factories as $factoriesAtPosition) {
foreach ($factoriesAtPosition as $factory) {
$name = str_replace('-', '_', $factory->getKey());
$factoryNode = $firewallNodeBuilder->arrayNode($name)
->canBeUnset()
;
if ($factory instanceof AbstractFactory) {
$abstractFactoryKeys[] = $name;
}
$factory->addConfiguration($factoryNode);
}
}
// check for unreachable check paths
$firewallNodeBuilder
->end()
->validate()
->ifTrue(function ($v) {
return true === $v['security'] && isset($v['pattern']) && !isset($v['request_matcher']);
})
->then(function ($firewall) use ($abstractFactoryKeys) {
foreach ($abstractFactoryKeys as $k) {
if (!isset($firewall[$k]['check_path'])) {
continue;
}
if (false !== strpos($firewall[$k]['check_path'], '/') && !preg_match('#'.$firewall['pattern'].'#', $firewall[$k]['check_path'])) {
throw new \LogicException(sprintf('The check_path "%s" for login method "%s" is not matched by the firewall pattern "%s".', $firewall[$k]['check_path'], $k, $firewall['pattern']));
}
}
return $firewall;
})
->end()
;
}
private function addProvidersSection(ArrayNodeDefinition $rootNode)
{
$providerNodeBuilder = $rootNode
->fixXmlConfig('provider')
->children()
->arrayNode('providers')
->example(array(
'my_memory_provider' => array(
'memory' => array(
'users' => array(
'foo' => array('password' => 'foo', 'roles' => 'ROLE_USER'),
'bar' => array('password' => 'bar', 'roles' => '[ROLE_USER, ROLE_ADMIN]'),
),
),
),
'my_entity_provider' => array('entity' => array('class' => 'SecurityBundle:User', 'property' => 'username')),
))
->isRequired()
->requiresAtLeastOneElement()
->useAttributeAsKey('name')
->prototype('array')
;
$providerNodeBuilder
->children()
->scalarNode('id')->end()
->arrayNode('chain')
->fixXmlConfig('provider')
->children()
->arrayNode('providers')
->beforeNormalization()
->ifString()
->then(function ($v) { return preg_split('/\s*,\s*/', $v); })
->end()
->prototype('scalar')->end()
->end()
->end()
->end()
->end()
;
foreach ($this->userProviderFactories as $factory) {
$name = str_replace('-', '_', $factory->getKey());
$factoryNode = $providerNodeBuilder->children()->arrayNode($name)->canBeUnset();
$factory->addConfiguration($factoryNode);
}
$providerNodeBuilder
->validate()
->ifTrue(function ($v) { return count($v) > 1; })
->thenInvalid('You cannot set multiple provider types for the same provider')
->end()
->validate()
->ifTrue(function ($v) { return count($v) === 0; })
->thenInvalid('You must set a provider definition for the provider.')
->end()
;
}
private function addEncodersSection(ArrayNodeDefinition $rootNode)
{
$rootNode
->fixXmlConfig('encoder')
->children()
->arrayNode('encoders')
->example(array(
'AppBundle\Entity\User1' => 'bcrypt',
'AppBundle\Entity\User2' => array(
'algorithm' => 'bcrypt',
'cost' => 13,
),
))
->requiresAtLeastOneElement()
->useAttributeAsKey('class')
->prototype('array')
->canBeUnset()
->performNoDeepMerging()
->beforeNormalization()->ifString()->then(function ($v) { return array('algorithm' => $v); })->end()
->children()
->scalarNode('algorithm')->cannotBeEmpty()->end()
->scalarNode('hash_algorithm')->info('Name of hashing algorithm for PBKDF2 (i.e. sha256, sha512, etc..) See hash_algos() for a list of supported algorithms.')->defaultValue('sha512')->end()
->scalarNode('key_length')->defaultValue(40)->end()
->booleanNode('ignore_case')->defaultFalse()->end()
->booleanNode('encode_as_base64')->defaultTrue()->end()
->scalarNode('iterations')->defaultValue(5000)->end()
->integerNode('cost')
->min(4)
->max(31)
->defaultValue(13)
->end()
->scalarNode('id')->end()
->end()
->end()
->end()
->end()
;
}
}

View File

@@ -0,0 +1,216 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory;
use Symfony\Component\Config\Definition\Builder\NodeDefinition;
use Symfony\Component\DependencyInjection\DefinitionDecorator;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;
/**
* AbstractFactory is the base class for all classes inheriting from
* AbstractAuthenticationListener.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Lukas Kahwe Smith <smith@pooteeweet.org>
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
abstract class AbstractFactory implements SecurityFactoryInterface
{
protected $options = array(
'check_path' => '/login_check',
'use_forward' => false,
'require_previous_session' => true,
);
protected $defaultSuccessHandlerOptions = array(
'always_use_default_target_path' => false,
'default_target_path' => '/',
'login_path' => '/login',
'target_path_parameter' => '_target_path',
'use_referer' => false,
);
protected $defaultFailureHandlerOptions = array(
'failure_path' => null,
'failure_forward' => false,
'login_path' => '/login',
'failure_path_parameter' => '_failure_path',
);
public function create(ContainerBuilder $container, $id, $config, $userProviderId, $defaultEntryPointId)
{
// authentication provider
$authProviderId = $this->createAuthProvider($container, $id, $config, $userProviderId);
// authentication listener
$listenerId = $this->createListener($container, $id, $config, $userProviderId);
// add remember-me aware tag if requested
if ($this->isRememberMeAware($config)) {
$container
->getDefinition($listenerId)
->addTag('security.remember_me_aware', array('id' => $id, 'provider' => $userProviderId))
;
}
// create entry point if applicable (optional)
$entryPointId = $this->createEntryPoint($container, $id, $config, $defaultEntryPointId);
return array($authProviderId, $listenerId, $entryPointId);
}
public function addConfiguration(NodeDefinition $node)
{
$builder = $node->children();
$builder
->scalarNode('provider')->end()
->booleanNode('remember_me')->defaultTrue()->end()
->scalarNode('success_handler')->end()
->scalarNode('failure_handler')->end()
;
foreach (array_merge($this->options, $this->defaultSuccessHandlerOptions, $this->defaultFailureHandlerOptions) as $name => $default) {
if (is_bool($default)) {
$builder->booleanNode($name)->defaultValue($default);
} else {
$builder->scalarNode($name)->defaultValue($default);
}
}
}
final public function addOption($name, $default = null)
{
$this->options[$name] = $default;
}
/**
* Subclasses must return the id of a service which implements the
* AuthenticationProviderInterface.
*
* @param ContainerBuilder $container
* @param string $id The unique id of the firewall
* @param array $config The options array for this listener
* @param string $userProviderId The id of the user provider
*
* @return string never null, the id of the authentication provider
*/
abstract protected function createAuthProvider(ContainerBuilder $container, $id, $config, $userProviderId);
/**
* Subclasses must return the id of the abstract listener template.
*
* Listener definitions should inherit from the AbstractAuthenticationListener
* like this:
*
* <service id="my.listener.id"
* class="My\Concrete\Classname"
* parent="security.authentication.listener.abstract"
* abstract="true" />
*
* In the above case, this method would return "my.listener.id".
*
* @return string
*/
abstract protected function getListenerId();
/**
* Subclasses may create an entry point of their as they see fit. The
* default implementation does not change the default entry point.
*
* @param ContainerBuilder $container
* @param string $id
* @param array $config
* @param string $defaultEntryPointId
*
* @return string the entry point id
*/
protected function createEntryPoint($container, $id, $config, $defaultEntryPointId)
{
return $defaultEntryPointId;
}
/**
* Subclasses may disable remember-me features for the listener, by
* always returning false from this method.
*
* @param array $config
*
* @return bool Whether a possibly configured RememberMeServices should be set for this listener
*/
protected function isRememberMeAware($config)
{
return $config['remember_me'];
}
protected function createListener($container, $id, $config, $userProvider)
{
$listenerId = $this->getListenerId();
$listener = new DefinitionDecorator($listenerId);
$listener->replaceArgument(4, $id);
$listener->replaceArgument(5, new Reference($this->createAuthenticationSuccessHandler($container, $id, $config)));
$listener->replaceArgument(6, new Reference($this->createAuthenticationFailureHandler($container, $id, $config)));
$listener->replaceArgument(7, array_intersect_key($config, $this->options));
$listenerId .= '.'.$id;
$container->setDefinition($listenerId, $listener);
return $listenerId;
}
protected function createAuthenticationSuccessHandler($container, $id, $config)
{
$successHandlerId = $this->getSuccessHandlerId($id);
$options = array_intersect_key($config, $this->defaultSuccessHandlerOptions);
if (isset($config['success_handler'])) {
$successHandler = $container->setDefinition($successHandlerId, new DefinitionDecorator('security.authentication.custom_success_handler'));
$successHandler->replaceArgument(0, new Reference($config['success_handler']));
$successHandler->replaceArgument(1, $options);
$successHandler->replaceArgument(2, $id);
} else {
$successHandler = $container->setDefinition($successHandlerId, new DefinitionDecorator('security.authentication.success_handler'));
$successHandler->addMethodCall('setOptions', array($options));
$successHandler->addMethodCall('setProviderKey', array($id));
}
return $successHandlerId;
}
protected function createAuthenticationFailureHandler($container, $id, $config)
{
$id = $this->getFailureHandlerId($id);
$options = array_intersect_key($config, $this->defaultFailureHandlerOptions);
if (isset($config['failure_handler'])) {
$failureHandler = $container->setDefinition($id, new DefinitionDecorator('security.authentication.custom_failure_handler'));
$failureHandler->replaceArgument(0, new Reference($config['failure_handler']));
$failureHandler->replaceArgument(1, $options);
} else {
$failureHandler = $container->setDefinition($id, new DefinitionDecorator('security.authentication.failure_handler'));
$failureHandler->addMethodCall('setOptions', array($options));
}
return $id;
}
protected function getSuccessHandlerId($id)
{
return 'security.authentication.success_handler.'.$id.'.'.str_replace('-', '_', $this->getKey());
}
protected function getFailureHandlerId($id)
{
return 'security.authentication.failure_handler.'.$id.'.'.str_replace('-', '_', $this->getKey());
}
}

View File

@@ -0,0 +1,99 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory;
use Symfony\Component\Config\Definition\Builder\NodeDefinition;
use Symfony\Component\DependencyInjection\DefinitionDecorator;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;
/**
* FormLoginFactory creates services for form login authentication.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
class FormLoginFactory extends AbstractFactory
{
public function __construct()
{
$this->addOption('username_parameter', '_username');
$this->addOption('password_parameter', '_password');
$this->addOption('csrf_parameter', '_csrf_token');
$this->addOption('csrf_token_id', 'authenticate');
$this->addOption('post_only', true);
}
public function getPosition()
{
return 'form';
}
public function getKey()
{
return 'form-login';
}
public function addConfiguration(NodeDefinition $node)
{
parent::addConfiguration($node);
$node
->children()
->scalarNode('csrf_token_generator')->cannotBeEmpty()->end()
->end()
;
}
protected function getListenerId()
{
return 'security.authentication.listener.form';
}
protected function createAuthProvider(ContainerBuilder $container, $id, $config, $userProviderId)
{
$provider = 'security.authentication.provider.dao.'.$id;
$container
->setDefinition($provider, new DefinitionDecorator('security.authentication.provider.dao'))
->replaceArgument(0, new Reference($userProviderId))
->replaceArgument(1, new Reference('security.user_checker.'.$id))
->replaceArgument(2, $id)
;
return $provider;
}
protected function createListener($container, $id, $config, $userProvider)
{
$listenerId = parent::createListener($container, $id, $config, $userProvider);
$container
->getDefinition($listenerId)
->addArgument(isset($config['csrf_token_generator']) ? new Reference($config['csrf_token_generator']) : null)
;
return $listenerId;
}
protected function createEntryPoint($container, $id, $config, $defaultEntryPoint)
{
$entryPointId = 'security.authentication.form_entry_point.'.$id;
$container
->setDefinition($entryPointId, new DefinitionDecorator('security.authentication.form_entry_point'))
->addArgument(new Reference('security.http_utils'))
->addArgument($config['login_path'])
->addArgument($config['use_forward'])
;
return $entryPointId;
}
}

View File

@@ -0,0 +1,58 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory;
use Symfony\Component\Config\Definition\Builder\NodeDefinition;
use Symfony\Component\DependencyInjection\DefinitionDecorator;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;
/**
* FormLoginLdapFactory creates services for form login ldap authentication.
*
* @author Grégoire Pineau <lyrixx@lyrixx.info>
* @author Charles Sarrazin <charles@sarraz.in>
*/
class FormLoginLdapFactory extends FormLoginFactory
{
protected function createAuthProvider(ContainerBuilder $container, $id, $config, $userProviderId)
{
$provider = 'security.authentication.provider.ldap_bind.'.$id;
$container
->setDefinition($provider, new DefinitionDecorator('security.authentication.provider.ldap_bind'))
->replaceArgument(0, new Reference($userProviderId))
->replaceArgument(1, new Reference('security.user_checker.'.$id))
->replaceArgument(2, $id)
->replaceArgument(3, new Reference($config['service']))
->replaceArgument(4, $config['dn_string'])
;
return $provider;
}
public function addConfiguration(NodeDefinition $node)
{
parent::addConfiguration($node);
$node
->children()
->scalarNode('service')->defaultValue('ldap')->end()
->scalarNode('dn_string')->defaultValue('{username}')->end()
->end()
;
}
public function getKey()
{
return 'form-login-ldap';
}
}

View File

@@ -0,0 +1,123 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory;
use Symfony\Component\Config\Definition\Builder\NodeDefinition;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\DefinitionDecorator;
use Symfony\Component\DependencyInjection\Reference;
/**
* Configures the "guard" authentication provider key under a firewall.
*
* @author Ryan Weaver <ryan@knpuniversity.com>
*/
class GuardAuthenticationFactory implements SecurityFactoryInterface
{
public function getPosition()
{
return 'pre_auth';
}
public function getKey()
{
return 'guard';
}
public function addConfiguration(NodeDefinition $node)
{
$node
->fixXmlConfig('authenticator')
->children()
->scalarNode('provider')
->info('A key from the "providers" section of your security config, in case your user provider is different than the firewall')
->end()
->scalarNode('entry_point')
->info('A service id (of one of your authenticators) whose start() method should be called when an anonymous user hits a page that requires authentication')
->defaultValue(null)
->end()
->arrayNode('authenticators')
->info('An array of service ids for all of your "authenticators"')
->requiresAtLeastOneElement()
->prototype('scalar')->end()
->end()
->end()
;
}
public function create(ContainerBuilder $container, $id, $config, $userProvider, $defaultEntryPoint)
{
$authenticatorIds = $config['authenticators'];
$authenticatorReferences = array();
foreach ($authenticatorIds as $authenticatorId) {
$authenticatorReferences[] = new Reference($authenticatorId);
}
// configure the GuardAuthenticationFactory to have the dynamic constructor arguments
$providerId = 'security.authentication.provider.guard.'.$id;
$container
->setDefinition($providerId, new DefinitionDecorator('security.authentication.provider.guard'))
->replaceArgument(0, $authenticatorReferences)
->replaceArgument(1, new Reference($userProvider))
->replaceArgument(2, $id)
->replaceArgument(3, new Reference('security.user_checker.'.$id))
;
// listener
$listenerId = 'security.authentication.listener.guard.'.$id;
$listener = $container->setDefinition($listenerId, new DefinitionDecorator('security.authentication.listener.guard'));
$listener->replaceArgument(2, $id);
$listener->replaceArgument(3, $authenticatorReferences);
// determine the entryPointId to use
$entryPointId = $this->determineEntryPoint($defaultEntryPoint, $config);
// this is always injected - then the listener decides if it should be used
$container
->getDefinition($listenerId)
->addTag('security.remember_me_aware', array('id' => $id, 'provider' => $userProvider));
return array($providerId, $listenerId, $entryPointId);
}
private function determineEntryPoint($defaultEntryPointId, array $config)
{
if ($defaultEntryPointId) {
// explode if they've configured the entry_point, but there is already one
if ($config['entry_point']) {
throw new \LogicException(sprintf(
'The guard authentication provider cannot use the "%s" entry_point because another entry point is already configured by another provider! Either remove the other provider or move the entry_point configuration as a root key under your firewall (i.e. at the same level as "guard").',
$config['entry_point']
));
}
return $defaultEntryPointId;
}
if ($config['entry_point']) {
// if it's configured explicitly, use it!
return $config['entry_point'];
}
$authenticatorIds = $config['authenticators'];
if (count($authenticatorIds) == 1) {
// if there is only one authenticator, use that as the entry point
return array_shift($authenticatorIds);
}
// we have multiple entry points - we must ask them to configure one
throw new \LogicException(sprintf(
'Because you have multiple guard configurators, you need to set the "guard.entry_point" key to one of you configurators (%s)',
implode(', ', $authenticatorIds)
));
}
}

View File

@@ -0,0 +1,82 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory;
use Symfony\Component\Config\Definition\Builder\NodeDefinition;
use Symfony\Component\DependencyInjection\DefinitionDecorator;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;
/**
* HttpBasicFactory creates services for HTTP basic authentication.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class HttpBasicFactory implements SecurityFactoryInterface
{
public function create(ContainerBuilder $container, $id, $config, $userProvider, $defaultEntryPoint)
{
$provider = 'security.authentication.provider.dao.'.$id;
$container
->setDefinition($provider, new DefinitionDecorator('security.authentication.provider.dao'))
->replaceArgument(0, new Reference($userProvider))
->replaceArgument(1, new Reference('security.user_checker.'.$id))
->replaceArgument(2, $id)
;
// entry point
$entryPointId = $this->createEntryPoint($container, $id, $config, $defaultEntryPoint);
// listener
$listenerId = 'security.authentication.listener.basic.'.$id;
$listener = $container->setDefinition($listenerId, new DefinitionDecorator('security.authentication.listener.basic'));
$listener->replaceArgument(2, $id);
$listener->replaceArgument(3, new Reference($entryPointId));
return array($provider, $listenerId, $entryPointId);
}
public function getPosition()
{
return 'http';
}
public function getKey()
{
return 'http-basic';
}
public function addConfiguration(NodeDefinition $node)
{
$node
->children()
->scalarNode('provider')->end()
->scalarNode('realm')->defaultValue('Secured Area')->end()
->end()
;
}
protected function createEntryPoint($container, $id, $config, $defaultEntryPoint)
{
if (null !== $defaultEntryPoint) {
return $defaultEntryPoint;
}
$entryPointId = 'security.authentication.basic_entry_point.'.$id;
$container
->setDefinition($entryPointId, new DefinitionDecorator('security.authentication.basic_entry_point'))
->addArgument($config['realm'])
;
return $entryPointId;
}
}

View File

@@ -0,0 +1,68 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory;
use Symfony\Component\Config\Definition\Builder\NodeDefinition;
use Symfony\Component\DependencyInjection\DefinitionDecorator;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;
/**
* HttpBasicFactory creates services for HTTP basic authentication.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Grégoire Pineau <lyrixx@lyrixx.info>
* @author Charles Sarrazin <charles@sarraz.in>
*/
class HttpBasicLdapFactory extends HttpBasicFactory
{
public function create(ContainerBuilder $container, $id, $config, $userProvider, $defaultEntryPoint)
{
$provider = 'security.authentication.provider.ldap_bind.'.$id;
$container
->setDefinition($provider, new DefinitionDecorator('security.authentication.provider.ldap_bind'))
->replaceArgument(0, new Reference($userProvider))
->replaceArgument(1, new Reference('security.user_checker.'.$id))
->replaceArgument(2, $id)
->replaceArgument(3, new Reference($config['service']))
->replaceArgument(4, $config['dn_string'])
;
// entry point
$entryPointId = $this->createEntryPoint($container, $id, $config, $defaultEntryPoint);
// listener
$listenerId = 'security.authentication.listener.basic.'.$id;
$listener = $container->setDefinition($listenerId, new DefinitionDecorator('security.authentication.listener.basic'));
$listener->replaceArgument(2, $id);
$listener->replaceArgument(3, new Reference($entryPointId));
return array($provider, $listenerId, $entryPointId);
}
public function addConfiguration(NodeDefinition $node)
{
parent::addConfiguration($node);
$node
->children()
->scalarNode('service')->defaultValue('ldap')->end()
->scalarNode('dn_string')->defaultValue('{username}')->end()
->end()
;
}
public function getKey()
{
return 'http-basic-ldap';
}
}

View File

@@ -0,0 +1,85 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory;
use Symfony\Component\Config\Definition\Builder\NodeDefinition;
use Symfony\Component\DependencyInjection\DefinitionDecorator;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;
/**
* HttpDigestFactory creates services for HTTP digest authentication.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class HttpDigestFactory implements SecurityFactoryInterface
{
public function create(ContainerBuilder $container, $id, $config, $userProvider, $defaultEntryPoint)
{
$provider = 'security.authentication.provider.dao.'.$id;
$container
->setDefinition($provider, new DefinitionDecorator('security.authentication.provider.dao'))
->replaceArgument(0, new Reference($userProvider))
->replaceArgument(1, new Reference('security.user_checker.'.$id))
->replaceArgument(2, $id)
;
// entry point
$entryPointId = $this->createEntryPoint($container, $id, $config, $defaultEntryPoint);
// listener
$listenerId = 'security.authentication.listener.digest.'.$id;
$listener = $container->setDefinition($listenerId, new DefinitionDecorator('security.authentication.listener.digest'));
$listener->replaceArgument(1, new Reference($userProvider));
$listener->replaceArgument(2, $id);
$listener->replaceArgument(3, new Reference($entryPointId));
return array($provider, $listenerId, $entryPointId);
}
public function getPosition()
{
return 'http';
}
public function getKey()
{
return 'http-digest';
}
public function addConfiguration(NodeDefinition $node)
{
$node
->children()
->scalarNode('provider')->end()
->scalarNode('realm')->defaultValue('Secured Area')->end()
->scalarNode('secret')->isRequired()->cannotBeEmpty()->end()
->end()
;
}
protected function createEntryPoint($container, $id, $config, $defaultEntryPoint)
{
if (null !== $defaultEntryPoint) {
return $defaultEntryPoint;
}
$entryPointId = 'security.authentication.digest_entry_point.'.$id;
$container
->setDefinition($entryPointId, new DefinitionDecorator('security.authentication.digest_entry_point'))
->addArgument($config['realm'])
->addArgument($config['secret'])
;
return $entryPointId;
}
}

View File

@@ -0,0 +1,149 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory;
use Symfony\Component\Config\Definition\Builder\NodeDefinition;
use Symfony\Component\DependencyInjection\DefinitionDecorator;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\ContainerBuilder;
class RememberMeFactory implements SecurityFactoryInterface
{
protected $options = array(
'name' => 'REMEMBERME',
'lifetime' => 31536000,
'path' => '/',
'domain' => null,
'secure' => false,
'httponly' => true,
'always_remember_me' => false,
'remember_me_parameter' => '_remember_me',
);
public function create(ContainerBuilder $container, $id, $config, $userProvider, $defaultEntryPoint)
{
// authentication provider
$authProviderId = 'security.authentication.provider.rememberme.'.$id;
$container
->setDefinition($authProviderId, new DefinitionDecorator('security.authentication.provider.rememberme'))
->replaceArgument(0, new Reference('security.user_checker.'.$id))
->addArgument($config['secret'])
->addArgument($id)
;
// remember me services
if (isset($config['token_provider'])) {
$templateId = 'security.authentication.rememberme.services.persistent';
$rememberMeServicesId = $templateId.'.'.$id;
} else {
$templateId = 'security.authentication.rememberme.services.simplehash';
$rememberMeServicesId = $templateId.'.'.$id;
}
if ($container->hasDefinition('security.logout_listener.'.$id)) {
$container
->getDefinition('security.logout_listener.'.$id)
->addMethodCall('addHandler', array(new Reference($rememberMeServicesId)))
;
}
$rememberMeServices = $container->setDefinition($rememberMeServicesId, new DefinitionDecorator($templateId));
$rememberMeServices->replaceArgument(1, $config['secret']);
$rememberMeServices->replaceArgument(2, $id);
if (isset($config['token_provider'])) {
$rememberMeServices->addMethodCall('setTokenProvider', array(
new Reference($config['token_provider']),
));
}
// remember-me options
$rememberMeServices->replaceArgument(3, array_intersect_key($config, $this->options));
// attach to remember-me aware listeners
$userProviders = array();
foreach ($container->findTaggedServiceIds('security.remember_me_aware') as $serviceId => $attributes) {
foreach ($attributes as $attribute) {
if (!isset($attribute['id']) || $attribute['id'] !== $id) {
continue;
}
if (!isset($attribute['provider'])) {
throw new \RuntimeException('Each "security.remember_me_aware" tag must have a provider attribute.');
}
$userProviders[] = new Reference($attribute['provider']);
$container
->getDefinition($serviceId)
->addMethodCall('setRememberMeServices', array(new Reference($rememberMeServicesId)))
;
}
}
if ($config['user_providers']) {
$userProviders = array();
foreach ($config['user_providers'] as $providerName) {
$userProviders[] = new Reference('security.user.provider.concrete.'.$providerName);
}
}
if (count($userProviders) === 0) {
throw new \RuntimeException('You must configure at least one remember-me aware listener (such as form-login) for each firewall that has remember-me enabled.');
}
$rememberMeServices->replaceArgument(0, array_unique($userProviders));
// remember-me listener
$listenerId = 'security.authentication.listener.rememberme.'.$id;
$listener = $container->setDefinition($listenerId, new DefinitionDecorator('security.authentication.listener.rememberme'));
$listener->replaceArgument(1, new Reference($rememberMeServicesId));
$listener->replaceArgument(5, $config['catch_exceptions']);
return array($authProviderId, $listenerId, $defaultEntryPoint);
}
public function getPosition()
{
return 'remember_me';
}
public function getKey()
{
return 'remember-me';
}
public function addConfiguration(NodeDefinition $node)
{
$builder = $node
->fixXmlConfig('user_provider')
->children()
;
$builder
->scalarNode('secret')->isRequired()->cannotBeEmpty()->end()
->scalarNode('token_provider')->end()
->arrayNode('user_providers')
->beforeNormalization()
->ifString()->then(function ($v) { return array($v); })
->end()
->prototype('scalar')->end()
->end()
->scalarNode('catch_exceptions')->defaultTrue()->end()
;
foreach ($this->options as $name => $value) {
if (is_bool($value)) {
$builder->booleanNode($name)->defaultValue($value);
} else {
$builder->scalarNode($name)->defaultValue($value);
}
}
}
}

View File

@@ -0,0 +1,64 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory;
use Symfony\Component\Config\Definition\Builder\NodeDefinition;
use Symfony\Component\DependencyInjection\DefinitionDecorator;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;
/**
* RemoteUserFactory creates services for REMOTE_USER based authentication.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Maxime Douailin <maxime.douailin@gmail.com>
*/
class RemoteUserFactory implements SecurityFactoryInterface
{
public function create(ContainerBuilder $container, $id, $config, $userProvider, $defaultEntryPoint)
{
$providerId = 'security.authentication.provider.pre_authenticated.'.$id;
$container
->setDefinition($providerId, new DefinitionDecorator('security.authentication.provider.pre_authenticated'))
->replaceArgument(0, new Reference($userProvider))
->replaceArgument(1, new Reference('security.user_checker.'.$id))
->addArgument($id)
;
$listenerId = 'security.authentication.listener.remote_user.'.$id;
$listener = $container->setDefinition($listenerId, new DefinitionDecorator('security.authentication.listener.remote_user'));
$listener->replaceArgument(2, $id);
$listener->replaceArgument(3, $config['user']);
return array($providerId, $listenerId, $defaultEntryPoint);
}
public function getPosition()
{
return 'pre_auth';
}
public function getKey()
{
return 'remote-user';
}
public function addConfiguration(NodeDefinition $node)
{
$node
->children()
->scalarNode('provider')->end()
->scalarNode('user')->defaultValue('REMOTE_USER')->end()
->end()
;
}
}

View File

@@ -0,0 +1,57 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory;
use Symfony\Component\Config\Definition\Builder\NodeDefinition;
use Symfony\Component\DependencyInjection\ContainerBuilder;
/**
* SecurityFactoryInterface is the interface for all security authentication listener.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
interface SecurityFactoryInterface
{
/**
* Configures the container services required to use the authentication listener.
*
* @param ContainerBuilder $container
* @param string $id The unique id of the firewall
* @param array $config The options array for the listener
* @param string $userProvider The service id of the user provider
* @param string $defaultEntryPoint
*
* @return array containing three values:
* - the provider id
* - the listener id
* - the entry point id
*/
public function create(ContainerBuilder $container, $id, $config, $userProvider, $defaultEntryPoint);
/**
* Defines the position at which the provider is called.
* Possible values: pre_auth, form, http, and remember_me.
*
* @return string
*/
public function getPosition();
/**
* Defines the configuration key used to reference the provider
* in the firewall configuration.
*
* @return string
*/
public function getKey();
public function addConfiguration(NodeDefinition $builder);
}

View File

@@ -0,0 +1,80 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory;
use Symfony\Component\Config\Definition\Builder\NodeDefinition;
use Symfony\Component\DependencyInjection\DefinitionDecorator;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;
/**
* @author Jordi Boggiano <j.boggiano@seld.be>
*/
class SimpleFormFactory extends FormLoginFactory
{
public function __construct()
{
parent::__construct();
$this->addOption('authenticator', null);
}
public function getKey()
{
return 'simple-form';
}
public function addConfiguration(NodeDefinition $node)
{
parent::addConfiguration($node);
$node->children()
->scalarNode('authenticator')->cannotBeEmpty()->end()
->end();
}
protected function getListenerId()
{
return 'security.authentication.listener.simple_form';
}
protected function createAuthProvider(ContainerBuilder $container, $id, $config, $userProviderId)
{
$provider = 'security.authentication.provider.simple_form.'.$id;
$container
->setDefinition($provider, new DefinitionDecorator('security.authentication.provider.simple'))
->replaceArgument(0, new Reference($config['authenticator']))
->replaceArgument(1, new Reference($userProviderId))
->replaceArgument(2, $id)
;
return $provider;
}
protected function createListener($container, $id, $config, $userProvider)
{
$listenerId = parent::createListener($container, $id, $config, $userProvider);
$simpleAuthHandlerId = 'security.authentication.simple_success_failure_handler.'.$id;
$simpleAuthHandler = $container->setDefinition($simpleAuthHandlerId, new DefinitionDecorator('security.authentication.simple_success_failure_handler'));
$simpleAuthHandler->replaceArgument(0, new Reference($config['authenticator']));
$simpleAuthHandler->replaceArgument(1, new Reference($this->getSuccessHandlerId($id)));
$simpleAuthHandler->replaceArgument(2, new Reference($this->getFailureHandlerId($id)));
$listener = $container->getDefinition($listenerId);
$listener->replaceArgument(5, new Reference($simpleAuthHandlerId));
$listener->replaceArgument(6, new Reference($simpleAuthHandlerId));
$listener->addArgument(new Reference($config['authenticator']));
return $listenerId;
}
}

View File

@@ -0,0 +1,62 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory;
use Symfony\Component\Config\Definition\Builder\NodeDefinition;
use Symfony\Component\DependencyInjection\DefinitionDecorator;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;
/**
* @author Jordi Boggiano <j.boggiano@seld.be>
*/
class SimplePreAuthenticationFactory implements SecurityFactoryInterface
{
public function getPosition()
{
return 'pre_auth';
}
public function getKey()
{
return 'simple-preauth';
}
public function addConfiguration(NodeDefinition $node)
{
$node
->children()
->scalarNode('provider')->end()
->scalarNode('authenticator')->cannotBeEmpty()->end()
->end()
;
}
public function create(ContainerBuilder $container, $id, $config, $userProvider, $defaultEntryPoint)
{
$provider = 'security.authentication.provider.simple_preauth.'.$id;
$container
->setDefinition($provider, new DefinitionDecorator('security.authentication.provider.simple'))
->replaceArgument(0, new Reference($config['authenticator']))
->replaceArgument(1, new Reference($userProvider))
->replaceArgument(2, $id)
;
// listener
$listenerId = 'security.authentication.listener.simple_preauth.'.$id;
$listener = $container->setDefinition($listenerId, new DefinitionDecorator('security.authentication.listener.simple_preauth'));
$listener->replaceArgument(2, $id);
$listener->replaceArgument(3, new Reference($config['authenticator']));
return array($provider, $listenerId, null);
}
}

View File

@@ -0,0 +1,66 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory;
use Symfony\Component\Config\Definition\Builder\NodeDefinition;
use Symfony\Component\DependencyInjection\DefinitionDecorator;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;
/**
* X509Factory creates services for X509 certificate authentication.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class X509Factory implements SecurityFactoryInterface
{
public function create(ContainerBuilder $container, $id, $config, $userProvider, $defaultEntryPoint)
{
$providerId = 'security.authentication.provider.pre_authenticated.'.$id;
$container
->setDefinition($providerId, new DefinitionDecorator('security.authentication.provider.pre_authenticated'))
->replaceArgument(0, new Reference($userProvider))
->replaceArgument(1, new Reference('security.user_checker.'.$id))
->addArgument($id)
;
// listener
$listenerId = 'security.authentication.listener.x509.'.$id;
$listener = $container->setDefinition($listenerId, new DefinitionDecorator('security.authentication.listener.x509'));
$listener->replaceArgument(2, $id);
$listener->replaceArgument(3, $config['user']);
$listener->replaceArgument(4, $config['credentials']);
return array($providerId, $listenerId, $defaultEntryPoint);
}
public function getPosition()
{
return 'pre_auth';
}
public function getKey()
{
return 'x509';
}
public function addConfiguration(NodeDefinition $node)
{
$node
->children()
->scalarNode('provider')->end()
->scalarNode('user')->defaultValue('SSL_CLIENT_S_DN_Email')->end()
->scalarNode('credentials')->defaultValue('SSL_CLIENT_S_DN')->end()
->end()
;
}
}

View File

@@ -0,0 +1,68 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\SecurityBundle\DependencyInjection\Security\UserProvider;
use Symfony\Component\Config\Definition\Builder\NodeDefinition;
use Symfony\Component\DependencyInjection\DefinitionDecorator;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;
/**
* InMemoryFactory creates services for the memory provider.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Christophe Coevoet <stof@notk.org>
*/
class InMemoryFactory implements UserProviderFactoryInterface
{
public function create(ContainerBuilder $container, $id, $config)
{
$definition = $container->setDefinition($id, new DefinitionDecorator('security.user.provider.in_memory'));
foreach ($config['users'] as $username => $user) {
$userId = $id.'_'.$username;
$container
->setDefinition($userId, new DefinitionDecorator('security.user.provider.in_memory.user'))
->setArguments(array($username, (string) $user['password'], $user['roles']))
;
$definition->addMethodCall('createUser', array(new Reference($userId)));
}
}
public function getKey()
{
return 'memory';
}
public function addConfiguration(NodeDefinition $node)
{
$node
->fixXmlConfig('user')
->children()
->arrayNode('users')
->useAttributeAsKey('name')
->prototype('array')
->children()
->scalarNode('password')->defaultValue(uniqid('', true))->end()
->arrayNode('roles')
->beforeNormalization()->ifString()->then(function ($v) { return preg_split('/\s*,\s*/', $v); })->end()
->prototype('scalar')->end()
->end()
->end()
->end()
->end()
->end()
;
}
}

View File

@@ -0,0 +1,66 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\SecurityBundle\DependencyInjection\Security\UserProvider;
use Symfony\Component\Config\Definition\Builder\NodeDefinition;
use Symfony\Component\DependencyInjection\DefinitionDecorator;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;
/**
* LdapFactory creates services for Ldap user provider.
*
* @author Grégoire Pineau <lyrixx@lyrixx.info>
* @author Charles Sarrazin <charles@sarraz.in>
*/
class LdapFactory implements UserProviderFactoryInterface
{
public function create(ContainerBuilder $container, $id, $config)
{
$container
->setDefinition($id, new DefinitionDecorator('security.user.provider.ldap'))
->replaceArgument(0, new Reference($config['service']))
->replaceArgument(1, $config['base_dn'])
->replaceArgument(2, $config['search_dn'])
->replaceArgument(3, $config['search_password'])
->replaceArgument(4, $config['default_roles'])
->replaceArgument(5, $config['uid_key'])
->replaceArgument(6, $config['filter'])
->replaceArgument(7, $config['password_attribute'])
;
}
public function getKey()
{
return 'ldap';
}
public function addConfiguration(NodeDefinition $node)
{
$node
->children()
->scalarNode('service')->isRequired()->cannotBeEmpty()->defaultValue('ldap')->end()
->scalarNode('base_dn')->isRequired()->cannotBeEmpty()->end()
->scalarNode('search_dn')->end()
->scalarNode('search_password')->end()
->arrayNode('default_roles')
->beforeNormalization()->ifString()->then(function ($v) { return preg_split('/\s*,\s*/', $v); })->end()
->requiresAtLeastOneElement()
->prototype('scalar')->end()
->end()
->scalarNode('uid_key')->defaultValue('sAMAccountName')->end()
->scalarNode('filter')->defaultValue('({uid_key}={username})')->end()
->scalarNode('password_attribute')->defaultNull()->end()
->end()
;
}
}

View File

@@ -0,0 +1,30 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\SecurityBundle\DependencyInjection\Security\UserProvider;
use Symfony\Component\Config\Definition\Builder\NodeDefinition;
use Symfony\Component\DependencyInjection\ContainerBuilder;
/**
* UserProviderFactoryInterface is the interface for all user provider factories.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Christophe Coevoet <stof@notk.org>
*/
interface UserProviderFactoryInterface
{
public function create(ContainerBuilder $container, $id, $config);
public function getKey();
public function addConfiguration(NodeDefinition $builder);
}

View File

@@ -0,0 +1,718 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\SecurityBundle\DependencyInjection;
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\SecurityFactoryInterface;
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\UserProvider\UserProviderFactoryInterface;
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
use Symfony\Component\DependencyInjection\DefinitionDecorator;
use Symfony\Component\DependencyInjection\Alias;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\Security\Core\Authorization\ExpressionLanguage;
/**
* SecurityExtension.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
class SecurityExtension extends Extension
{
private $requestMatchers = array();
private $expressions = array();
private $contextListeners = array();
private $listenerPositions = array('pre_auth', 'form', 'http', 'remember_me');
private $factories = array();
private $userProviderFactories = array();
private $expressionLanguage;
public function __construct()
{
foreach ($this->listenerPositions as $position) {
$this->factories[$position] = array();
}
}
public function load(array $configs, ContainerBuilder $container)
{
if (!array_filter($configs)) {
return;
}
$mainConfig = $this->getConfiguration($configs, $container);
$config = $this->processConfiguration($mainConfig, $configs);
// load services
$loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
$loader->load('security.xml');
$loader->load('security_listeners.xml');
$loader->load('security_rememberme.xml');
$loader->load('templating_php.xml');
$loader->load('templating_twig.xml');
$loader->load('collectors.xml');
$loader->load('guard.xml');
if ($container->hasParameter('kernel.debug') && $container->getParameter('kernel.debug')) {
$loader->load('security_debug.xml');
}
if (!class_exists('Symfony\Component\ExpressionLanguage\ExpressionLanguage')) {
$container->removeDefinition('security.expression_language');
$container->removeDefinition('security.access.expression_voter');
}
// set some global scalars
$container->setParameter('security.access.denied_url', $config['access_denied_url']);
$container->setParameter('security.authentication.manager.erase_credentials', $config['erase_credentials']);
$container->setParameter('security.authentication.session_strategy.strategy', $config['session_fixation_strategy']);
$container
->getDefinition('security.access.decision_manager')
->addArgument($config['access_decision_manager']['strategy'])
->addArgument($config['access_decision_manager']['allow_if_all_abstain'])
->addArgument($config['access_decision_manager']['allow_if_equal_granted_denied'])
;
$container->setParameter('security.access.always_authenticate_before_granting', $config['always_authenticate_before_granting']);
$container->setParameter('security.authentication.hide_user_not_found', $config['hide_user_not_found']);
$this->createFirewalls($config, $container);
$this->createAuthorization($config, $container);
$this->createRoleHierarchy($config, $container);
if ($config['encoders']) {
$this->createEncoders($config['encoders'], $container);
}
// load ACL
if (isset($config['acl'])) {
$this->aclLoad($config['acl'], $container);
}
// add some required classes for compilation
$this->addClassesToCompile(array(
'Symfony\Component\Security\Http\Firewall',
'Symfony\Component\Security\Core\User\UserProviderInterface',
'Symfony\Component\Security\Core\Authentication\AuthenticationProviderManager',
'Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage',
'Symfony\Component\Security\Core\Authorization\AccessDecisionManager',
'Symfony\Component\Security\Core\Authorization\AuthorizationChecker',
'Symfony\Component\Security\Core\Authorization\Voter\VoterInterface',
'Symfony\Bundle\SecurityBundle\Security\FirewallConfig',
'Symfony\Bundle\SecurityBundle\Security\FirewallMap',
'Symfony\Bundle\SecurityBundle\Security\FirewallContext',
'Symfony\Component\HttpFoundation\RequestMatcher',
));
}
private function aclLoad($config, ContainerBuilder $container)
{
if (!interface_exists('Symfony\Component\Security\Acl\Model\AclInterface')) {
throw new \LogicException('You must install symfony/security-acl in order to use the ACL functionality.');
}
$loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
$loader->load('security_acl.xml');
if (isset($config['cache']['id'])) {
$container->setAlias('security.acl.cache', $config['cache']['id']);
}
$container->getDefinition('security.acl.voter.basic_permissions')->addArgument($config['voter']['allow_if_object_identity_unavailable']);
// custom ACL provider
if (isset($config['provider'])) {
$container->setAlias('security.acl.provider', $config['provider']);
return;
}
$this->configureDbalAclProvider($config, $container, $loader);
}
private function configureDbalAclProvider(array $config, ContainerBuilder $container, $loader)
{
$loader->load('security_acl_dbal.xml');
if (null !== $config['connection']) {
$container->setAlias('security.acl.dbal.connection', sprintf('doctrine.dbal.%s_connection', $config['connection']));
}
$container
->getDefinition('security.acl.dbal.schema_listener')
->addTag('doctrine.event_listener', array(
'connection' => $config['connection'],
'event' => 'postGenerateSchema',
'lazy' => true,
))
;
$container->getDefinition('security.acl.cache.doctrine')->addArgument($config['cache']['prefix']);
$container->setParameter('security.acl.dbal.class_table_name', $config['tables']['class']);
$container->setParameter('security.acl.dbal.entry_table_name', $config['tables']['entry']);
$container->setParameter('security.acl.dbal.oid_table_name', $config['tables']['object_identity']);
$container->setParameter('security.acl.dbal.oid_ancestors_table_name', $config['tables']['object_identity_ancestors']);
$container->setParameter('security.acl.dbal.sid_table_name', $config['tables']['security_identity']);
}
/**
* Loads the web configuration.
*
* @param array $config An array of configuration settings
* @param ContainerBuilder $container A ContainerBuilder instance
*/
private function createRoleHierarchy($config, ContainerBuilder $container)
{
if (!isset($config['role_hierarchy']) || 0 === count($config['role_hierarchy'])) {
$container->removeDefinition('security.access.role_hierarchy_voter');
return;
}
$container->setParameter('security.role_hierarchy.roles', $config['role_hierarchy']);
$container->removeDefinition('security.access.simple_role_voter');
}
private function createAuthorization($config, ContainerBuilder $container)
{
if (!$config['access_control']) {
return;
}
$this->addClassesToCompile(array(
'Symfony\\Component\\Security\\Http\\AccessMap',
));
foreach ($config['access_control'] as $access) {
$matcher = $this->createRequestMatcher(
$container,
$access['path'],
$access['host'],
$access['methods'],
$access['ips']
);
$attributes = $access['roles'];
if ($access['allow_if']) {
$attributes[] = $this->createExpression($container, $access['allow_if']);
}
$container->getDefinition('security.access_map')
->addMethodCall('add', array($matcher, $attributes, $access['requires_channel']));
}
}
private function createFirewalls($config, ContainerBuilder $container)
{
if (!isset($config['firewalls'])) {
return;
}
$firewalls = $config['firewalls'];
$providerIds = $this->createUserProviders($config, $container);
// make the ContextListener aware of the configured user providers
$definition = $container->getDefinition('security.context_listener');
$arguments = $definition->getArguments();
$userProviders = array();
foreach ($providerIds as $userProviderId) {
$userProviders[] = new Reference($userProviderId);
}
$arguments[1] = $userProviders;
$definition->setArguments($arguments);
// load firewall map
$mapDef = $container->getDefinition('security.firewall.map');
$map = $authenticationProviders = array();
foreach ($firewalls as $name => $firewall) {
$configId = 'security.firewall.map.config.'.$name;
list($matcher, $listeners, $exceptionListener) = $this->createFirewall($container, $name, $firewall, $authenticationProviders, $providerIds, $configId);
$contextId = 'security.firewall.map.context.'.$name;
$context = $container->setDefinition($contextId, new DefinitionDecorator('security.firewall.context'));
$context
->replaceArgument(0, $listeners)
->replaceArgument(1, $exceptionListener)
->replaceArgument(2, new Reference($configId))
;
$map[$contextId] = $matcher;
}
$mapDef->replaceArgument(1, $map);
// add authentication providers to authentication manager
$authenticationProviders = array_map(function ($id) {
return new Reference($id);
}, array_values(array_unique($authenticationProviders)));
$container
->getDefinition('security.authentication.manager')
->replaceArgument(0, $authenticationProviders)
;
}
private function createFirewall(ContainerBuilder $container, $id, $firewall, &$authenticationProviders, $providerIds, $configId)
{
$config = $container->setDefinition($configId, new DefinitionDecorator('security.firewall.config'));
$config->replaceArgument(0, $id);
$config->replaceArgument(1, $firewall['user_checker']);
// Matcher
$matcher = null;
if (isset($firewall['request_matcher'])) {
$matcher = new Reference($firewall['request_matcher']);
} elseif (isset($firewall['pattern']) || isset($firewall['host'])) {
$pattern = isset($firewall['pattern']) ? $firewall['pattern'] : null;
$host = isset($firewall['host']) ? $firewall['host'] : null;
$methods = isset($firewall['methods']) ? $firewall['methods'] : array();
$matcher = $this->createRequestMatcher($container, $pattern, $host, $methods);
}
$config->replaceArgument(2, $matcher ? (string) $matcher : null);
$config->replaceArgument(3, $firewall['security']);
// Security disabled?
if (false === $firewall['security']) {
return array($matcher, array(), null);
}
$config->replaceArgument(4, $firewall['stateless']);
// Provider id (take the first registered provider if none defined)
if (isset($firewall['provider'])) {
$defaultProvider = $this->getUserProviderId($firewall['provider']);
} else {
$defaultProvider = reset($providerIds);
}
$config->replaceArgument(5, $defaultProvider);
// Register listeners
$listeners = array();
$listenerKeys = array();
// Channel listener
$listeners[] = new Reference('security.channel_listener');
$contextKey = null;
// Context serializer listener
if (false === $firewall['stateless']) {
$contextKey = $id;
if (isset($firewall['context'])) {
$contextKey = $firewall['context'];
}
$listeners[] = new Reference($this->createContextListener($container, $contextKey));
}
$config->replaceArgument(6, $contextKey);
// Logout listener
if (isset($firewall['logout'])) {
$listenerKeys[] = 'logout';
$listenerId = 'security.logout_listener.'.$id;
$listener = $container->setDefinition($listenerId, new DefinitionDecorator('security.logout_listener'));
$listener->replaceArgument(3, array(
'csrf_parameter' => $firewall['logout']['csrf_parameter'],
'csrf_token_id' => $firewall['logout']['csrf_token_id'],
'logout_path' => $firewall['logout']['path'],
));
$listeners[] = new Reference($listenerId);
// add logout success handler
if (isset($firewall['logout']['success_handler'])) {
$logoutSuccessHandlerId = $firewall['logout']['success_handler'];
} else {
$logoutSuccessHandlerId = 'security.logout.success_handler.'.$id;
$logoutSuccessHandler = $container->setDefinition($logoutSuccessHandlerId, new DefinitionDecorator('security.logout.success_handler'));
$logoutSuccessHandler->replaceArgument(1, $firewall['logout']['target']);
}
$listener->replaceArgument(2, new Reference($logoutSuccessHandlerId));
// add CSRF provider
if (isset($firewall['logout']['csrf_token_generator'])) {
$listener->addArgument(new Reference($firewall['logout']['csrf_token_generator']));
}
// add session logout handler
if (true === $firewall['logout']['invalidate_session'] && false === $firewall['stateless']) {
$listener->addMethodCall('addHandler', array(new Reference('security.logout.handler.session')));
}
// add cookie logout handler
if (count($firewall['logout']['delete_cookies']) > 0) {
$cookieHandlerId = 'security.logout.handler.cookie_clearing.'.$id;
$cookieHandler = $container->setDefinition($cookieHandlerId, new DefinitionDecorator('security.logout.handler.cookie_clearing'));
$cookieHandler->addArgument($firewall['logout']['delete_cookies']);
$listener->addMethodCall('addHandler', array(new Reference($cookieHandlerId)));
}
// add custom handlers
foreach ($firewall['logout']['handlers'] as $handlerId) {
$listener->addMethodCall('addHandler', array(new Reference($handlerId)));
}
// register with LogoutUrlGenerator
$container
->getDefinition('security.logout_url_generator')
->addMethodCall('registerListener', array(
$id,
$firewall['logout']['path'],
$firewall['logout']['csrf_token_id'],
$firewall['logout']['csrf_parameter'],
isset($firewall['logout']['csrf_token_generator']) ? new Reference($firewall['logout']['csrf_token_generator']) : null,
))
;
}
// Determine default entry point
$configuredEntryPoint = isset($firewall['entry_point']) ? $firewall['entry_point'] : null;
// Authentication listeners
list($authListeners, $defaultEntryPoint) = $this->createAuthenticationListeners($container, $id, $firewall, $authenticationProviders, $defaultProvider, $configuredEntryPoint);
$config->replaceArgument(7, $configuredEntryPoint ?: $defaultEntryPoint);
$listeners = array_merge($listeners, $authListeners);
// Switch user listener
if (isset($firewall['switch_user'])) {
$listenerKeys[] = 'switch_user';
$listeners[] = new Reference($this->createSwitchUserListener($container, $id, $firewall['switch_user'], $defaultProvider));
}
// Access listener
$listeners[] = new Reference('security.access_listener');
// Exception listener
$exceptionListener = new Reference($this->createExceptionListener($container, $firewall, $id, $configuredEntryPoint ?: $defaultEntryPoint, $firewall['stateless']));
$config->replaceArgument(8, isset($firewall['access_denied_handler']) ? $firewall['access_denied_handler'] : null);
$config->replaceArgument(9, isset($firewall['access_denied_url']) ? $firewall['access_denied_url'] : null);
$container->setAlias('security.user_checker.'.$id, new Alias($firewall['user_checker'], false));
foreach ($this->factories as $position) {
foreach ($position as $factory) {
$key = str_replace('-', '_', $factory->getKey());
if (array_key_exists($key, $firewall)) {
$listenerKeys[] = $key;
}
}
}
if (isset($firewall['anonymous'])) {
$listenerKeys[] = 'anonymous';
}
$config->replaceArgument(10, $listenerKeys);
return array($matcher, $listeners, $exceptionListener);
}
private function createContextListener($container, $contextKey)
{
if (isset($this->contextListeners[$contextKey])) {
return $this->contextListeners[$contextKey];
}
$listenerId = 'security.context_listener.'.count($this->contextListeners);
$listener = $container->setDefinition($listenerId, new DefinitionDecorator('security.context_listener'));
$listener->replaceArgument(2, $contextKey);
return $this->contextListeners[$contextKey] = $listenerId;
}
private function createAuthenticationListeners($container, $id, $firewall, &$authenticationProviders, $defaultProvider, $defaultEntryPoint)
{
$listeners = array();
$hasListeners = false;
foreach ($this->listenerPositions as $position) {
foreach ($this->factories[$position] as $factory) {
$key = str_replace('-', '_', $factory->getKey());
if (isset($firewall[$key])) {
$userProvider = isset($firewall[$key]['provider']) ? $this->getUserProviderId($firewall[$key]['provider']) : $defaultProvider;
list($provider, $listenerId, $defaultEntryPoint) = $factory->create($container, $id, $firewall[$key], $userProvider, $defaultEntryPoint);
$listeners[] = new Reference($listenerId);
$authenticationProviders[] = $provider;
$hasListeners = true;
}
}
}
// Anonymous
if (isset($firewall['anonymous'])) {
$listenerId = 'security.authentication.listener.anonymous.'.$id;
$container
->setDefinition($listenerId, new DefinitionDecorator('security.authentication.listener.anonymous'))
->replaceArgument(1, $firewall['anonymous']['secret'])
;
$listeners[] = new Reference($listenerId);
$providerId = 'security.authentication.provider.anonymous.'.$id;
$container
->setDefinition($providerId, new DefinitionDecorator('security.authentication.provider.anonymous'))
->replaceArgument(0, $firewall['anonymous']['secret'])
;
$authenticationProviders[] = $providerId;
$hasListeners = true;
}
if (false === $hasListeners) {
throw new InvalidConfigurationException(sprintf('No authentication listener registered for firewall "%s".', $id));
}
return array($listeners, $defaultEntryPoint);
}
private function createEncoders($encoders, ContainerBuilder $container)
{
$encoderMap = array();
foreach ($encoders as $class => $encoder) {
$encoderMap[$class] = $this->createEncoder($encoder, $container);
}
$container
->getDefinition('security.encoder_factory.generic')
->setArguments(array($encoderMap))
;
}
private function createEncoder($config, ContainerBuilder $container)
{
// a custom encoder service
if (isset($config['id'])) {
return new Reference($config['id']);
}
// plaintext encoder
if ('plaintext' === $config['algorithm']) {
$arguments = array($config['ignore_case']);
return array(
'class' => 'Symfony\Component\Security\Core\Encoder\PlaintextPasswordEncoder',
'arguments' => $arguments,
);
}
// pbkdf2 encoder
if ('pbkdf2' === $config['algorithm']) {
return array(
'class' => 'Symfony\Component\Security\Core\Encoder\Pbkdf2PasswordEncoder',
'arguments' => array(
$config['hash_algorithm'],
$config['encode_as_base64'],
$config['iterations'],
$config['key_length'],
),
);
}
// bcrypt encoder
if ('bcrypt' === $config['algorithm']) {
return array(
'class' => 'Symfony\Component\Security\Core\Encoder\BCryptPasswordEncoder',
'arguments' => array($config['cost']),
);
}
// run-time configured encoder
return $config;
}
// Parses user providers and returns an array of their ids
private function createUserProviders($config, ContainerBuilder $container)
{
$providerIds = array();
foreach ($config['providers'] as $name => $provider) {
$id = $this->createUserDaoProvider($name, $provider, $container);
$providerIds[] = $id;
}
return $providerIds;
}
// Parses a <provider> tag and returns the id for the related user provider service
private function createUserDaoProvider($name, $provider, ContainerBuilder $container)
{
$name = $this->getUserProviderId($name);
// Doctrine Entity and In-memory DAO provider are managed by factories
foreach ($this->userProviderFactories as $factory) {
$key = str_replace('-', '_', $factory->getKey());
if (!empty($provider[$key])) {
$factory->create($container, $name, $provider[$key]);
return $name;
}
}
// Existing DAO service provider
if (isset($provider['id'])) {
$container->setAlias($name, new Alias($provider['id'], false));
return $provider['id'];
}
// Chain provider
if (isset($provider['chain'])) {
$providers = array();
foreach ($provider['chain']['providers'] as $providerName) {
$providers[] = new Reference($this->getUserProviderId($providerName));
}
$container
->setDefinition($name, new DefinitionDecorator('security.user.provider.chain'))
->addArgument($providers);
return $name;
}
throw new InvalidConfigurationException(sprintf('Unable to create definition for "%s" user provider', $name));
}
private function getUserProviderId($name)
{
return 'security.user.provider.concrete.'.strtolower($name);
}
private function createExceptionListener($container, $config, $id, $defaultEntryPoint, $stateless)
{
$exceptionListenerId = 'security.exception_listener.'.$id;
$listener = $container->setDefinition($exceptionListenerId, new DefinitionDecorator('security.exception_listener'));
$listener->replaceArgument(3, $id);
$listener->replaceArgument(4, null === $defaultEntryPoint ? null : new Reference($defaultEntryPoint));
$listener->replaceArgument(8, $stateless);
// access denied handler setup
if (isset($config['access_denied_handler'])) {
$listener->replaceArgument(6, new Reference($config['access_denied_handler']));
} elseif (isset($config['access_denied_url'])) {
$listener->replaceArgument(5, $config['access_denied_url']);
}
return $exceptionListenerId;
}
private function createSwitchUserListener($container, $id, $config, $defaultProvider)
{
$userProvider = isset($config['provider']) ? $this->getUserProviderId($config['provider']) : $defaultProvider;
$switchUserListenerId = 'security.authentication.switchuser_listener.'.$id;
$listener = $container->setDefinition($switchUserListenerId, new DefinitionDecorator('security.authentication.switchuser_listener'));
$listener->replaceArgument(1, new Reference($userProvider));
$listener->replaceArgument(2, new Reference('security.user_checker.'.$id));
$listener->replaceArgument(3, $id);
$listener->replaceArgument(6, $config['parameter']);
$listener->replaceArgument(7, $config['role']);
return $switchUserListenerId;
}
private function createExpression($container, $expression)
{
if (isset($this->expressions[$id = 'security.expression.'.sha1($expression)])) {
return $this->expressions[$id];
}
$container
->register($id, 'Symfony\Component\ExpressionLanguage\SerializedParsedExpression')
->setPublic(false)
->addArgument($expression)
->addArgument(serialize($this->getExpressionLanguage()->parse($expression, array('token', 'user', 'object', 'roles', 'request', 'trust_resolver'))->getNodes()))
;
return $this->expressions[$id] = new Reference($id);
}
private function createRequestMatcher($container, $path = null, $host = null, $methods = array(), $ip = null, array $attributes = array())
{
if ($methods) {
$methods = array_map('strtoupper', (array) $methods);
}
$serialized = serialize(array($path, $host, $methods, $ip, $attributes));
$id = 'security.request_matcher.'.md5($serialized).sha1($serialized);
if (isset($this->requestMatchers[$id])) {
return $this->requestMatchers[$id];
}
// only add arguments that are necessary
$arguments = array($path, $host, $methods, $ip, $attributes);
while (count($arguments) > 0 && !end($arguments)) {
array_pop($arguments);
}
$container
->register($id, 'Symfony\Component\HttpFoundation\RequestMatcher')
->setPublic(false)
->setArguments($arguments)
;
return $this->requestMatchers[$id] = new Reference($id);
}
public function addSecurityListenerFactory(SecurityFactoryInterface $factory)
{
$this->factories[$factory->getPosition()][] = $factory;
}
public function addUserProviderFactory(UserProviderFactoryInterface $factory)
{
$this->userProviderFactories[] = $factory;
}
/**
* Returns the base path for the XSD files.
*
* @return string The XSD base path
*/
public function getXsdValidationBasePath()
{
return __DIR__.'/../Resources/config/schema';
}
public function getNamespace()
{
return 'http://symfony.com/schema/dic/security';
}
public function getConfiguration(array $config, ContainerBuilder $container)
{
// first assemble the factories
return new MainConfiguration($this->factories, $this->userProviderFactories);
}
private function getExpressionLanguage()
{
if (null === $this->expressionLanguage) {
if (!class_exists('Symfony\Component\ExpressionLanguage\ExpressionLanguage')) {
throw new \RuntimeException('Unable to use expressions as the Symfony ExpressionLanguage component is not installed.');
}
$this->expressionLanguage = new ExpressionLanguage();
}
return $this->expressionLanguage;
}
}

View File

@@ -0,0 +1,36 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\SecurityBundle\EventListener;
use Symfony\Component\Security\Acl\Dbal\Schema;
use Doctrine\ORM\Tools\Event\GenerateSchemaEventArgs;
/**
* Merges ACL schema into the given schema.
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
class AclSchemaListener
{
private $schema;
public function __construct(Schema $schema)
{
$this->schema = $schema;
}
public function postGenerateSchema(GenerateSchemaEventArgs $args)
{
$schema = $args->getSchema();
$this->schema->addToSchema($schema);
}
}

19
vendor/symfony/security-bundle/LICENSE vendored Normal file
View File

@@ -0,0 +1,19 @@
Copyright (c) 2004-2017 Fabien Potencier
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@@ -0,0 +1,10 @@
SecurityBundle
==============
Resources
---------
* [Contributing](https://symfony.com/doc/current/contributing/index.html)
* [Report issues](https://github.com/symfony/symfony/issues) and
[send Pull Requests](https://github.com/symfony/symfony/pulls)
in the [main Symfony repository](https://github.com/symfony/symfony)

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<services>
<service id="data_collector.security" class="Symfony\Bundle\SecurityBundle\DataCollector\SecurityDataCollector" public="false">
<tag name="data_collector" template="@Security/Collector/security.html.twig" id="security" priority="270" />
<argument type="service" id="security.token_storage" on-invalid="ignore" />
<argument type="service" id="security.role_hierarchy" />
<argument type="service" id="security.logout_url_generator" />
<argument type="service" id="security.access.decision_manager" />
<argument type="service" id="security.firewall.map" />
</service>
</services>
</container>

View File

@@ -0,0 +1,40 @@
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<services>
<service id="security.authentication.guard_handler"
class="Symfony\Component\Security\Guard\GuardAuthenticatorHandler"
>
<argument type="service" id="security.token_storage" />
<argument type="service" id="event_dispatcher" on-invalid="null" />
</service>
<!-- See GuardAuthenticationFactory -->
<service id="security.authentication.provider.guard"
class="Symfony\Component\Security\Guard\Provider\GuardAuthenticationProvider"
abstract="true"
public="false"
>
<argument /> <!-- Simple Authenticator -->
<argument /> <!-- User Provider -->
<argument /> <!-- Provider-shared Key -->
<argument /> <!-- User Checker -->
</service>
<service id="security.authentication.listener.guard"
class="Symfony\Component\Security\Guard\Firewall\GuardAuthenticationListener"
public="false"
abstract="true"
>
<tag name="monolog.logger" channel="security" />
<argument type="service" id="security.authentication.guard_handler" />
<argument type="service" id="security.authentication.manager" />
<argument /> <!-- Provider-shared Key -->
<argument /> <!-- Authenticator -->
<argument type="service" id="logger" on-invalid="null" />
</service>
</services>
</container>

View File

@@ -0,0 +1,167 @@
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<parameters>
<parameter key="security.authentication.trust_resolver.anonymous_class">Symfony\Component\Security\Core\Authentication\Token\AnonymousToken</parameter>
<parameter key="security.authentication.trust_resolver.rememberme_class">Symfony\Component\Security\Core\Authentication\Token\RememberMeToken</parameter>
<parameter key="security.role_hierarchy.roles" type="collection" />
</parameters>
<services>
<service id="security.authorization_checker" class="Symfony\Component\Security\Core\Authorization\AuthorizationChecker">
<argument type="service" id="security.token_storage" />
<argument type="service" id="security.authentication.manager" />
<argument type="service" id="security.access.decision_manager" />
<argument>%security.access.always_authenticate_before_granting%</argument>
</service>
<service id="security.token_storage" class="Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage" />
<service id="security.user_value_resolver" class="Symfony\Bundle\SecurityBundle\SecurityUserValueResolver" public="false">
<argument type="service" id="security.token_storage" />
<tag name="controller.argument_value_resolver" priority="40" />
</service>
<!-- Authentication related services -->
<service id="security.authentication.manager" class="Symfony\Component\Security\Core\Authentication\AuthenticationProviderManager" public="false">
<argument type="collection" />
<argument>%security.authentication.manager.erase_credentials%</argument>
<call method="setEventDispatcher">
<argument type="service" id="event_dispatcher" />
</call>
</service>
<service id="security.authentication.trust_resolver" class="Symfony\Component\Security\Core\Authentication\AuthenticationTrustResolver" public="false">
<argument>%security.authentication.trust_resolver.anonymous_class%</argument>
<argument>%security.authentication.trust_resolver.rememberme_class%</argument>
</service>
<service id="security.authentication.session_strategy" class="Symfony\Component\Security\Http\Session\SessionAuthenticationStrategy" public="false">
<argument>%security.authentication.session_strategy.strategy%</argument>
</service>
<service id="security.encoder_factory.generic" class="Symfony\Component\Security\Core\Encoder\EncoderFactory" public="false">
<argument type="collection" />
</service>
<service id="security.encoder_factory" alias="security.encoder_factory.generic" />
<service id="security.user_password_encoder.generic" class="Symfony\Component\Security\Core\Encoder\UserPasswordEncoder" public="false">
<argument type="service" id="security.encoder_factory"></argument>
</service>
<service id="security.password_encoder" alias="security.user_password_encoder.generic"></service>
<service id="security.user_checker" class="Symfony\Component\Security\Core\User\UserChecker" public="false" />
<service id="security.expression_language" class="Symfony\Component\Security\Core\Authorization\ExpressionLanguage" public="false" />
<service id="security.authentication_utils" class="Symfony\Component\Security\Http\Authentication\AuthenticationUtils">
<argument type="service" id="request_stack" />
</service>
<!-- Authorization related services -->
<service id="security.access.decision_manager" class="Symfony\Component\Security\Core\Authorization\AccessDecisionManager" public="false">
<argument type="collection" />
</service>
<service id="security.role_hierarchy" class="Symfony\Component\Security\Core\Role\RoleHierarchy" public="false">
<argument>%security.role_hierarchy.roles%</argument>
</service>
<!-- Security Voters -->
<service id="security.access.simple_role_voter" class="Symfony\Component\Security\Core\Authorization\Voter\RoleVoter" public="false">
<tag name="security.voter" priority="245" />
</service>
<service id="security.access.authenticated_voter" class="Symfony\Component\Security\Core\Authorization\Voter\AuthenticatedVoter" public="false">
<argument type="service" id="security.authentication.trust_resolver" />
<tag name="security.voter" priority="250" />
</service>
<service id="security.access.role_hierarchy_voter" class="Symfony\Component\Security\Core\Authorization\Voter\RoleHierarchyVoter" public="false">
<argument type="service" id="security.role_hierarchy" />
<tag name="security.voter" priority="245" />
</service>
<service id="security.access.expression_voter" class="Symfony\Component\Security\Core\Authorization\Voter\ExpressionVoter" public="false">
<argument type="service" id="security.expression_language" />
<argument type="service" id="security.authentication.trust_resolver" />
<argument type="service" id="security.role_hierarchy" on-invalid="null" />
<tag name="security.voter" priority="245" />
</service>
<!-- Firewall related services -->
<service id="security.firewall" class="Symfony\Component\Security\Http\Firewall">
<tag name="kernel.event_subscriber" />
<argument type="service" id="security.firewall.map" />
<argument type="service" id="event_dispatcher" />
</service>
<service id="security.firewall.map" class="Symfony\Bundle\SecurityBundle\Security\FirewallMap" public="false">
<argument type="service" id="service_container" />
<argument type="collection" />
</service>
<service id="security.firewall.context" class="Symfony\Bundle\SecurityBundle\Security\FirewallContext" abstract="true">
<argument type="collection" />
<argument type="service" id="security.exception_listener" />
<argument /> <!-- FirewallConfig -->
</service>
<service id="security.firewall.config" class="Symfony\Bundle\SecurityBundle\Security\FirewallConfig" abstract="true" public="false">
<argument /> <!-- name -->
<argument /> <!-- user_checker -->
<argument /> <!-- request_matcher -->
<argument /> <!-- security enabled -->
<argument /> <!-- stateless -->
<argument /> <!-- provider -->
<argument /> <!-- context -->
<argument /> <!-- entry_point -->
<argument /> <!-- access_denied_handler -->
<argument /> <!-- access_denied_url -->
<argument type="collection" /> <!-- listeners -->
</service>
<service id="security.logout_url_generator" class="Symfony\Component\Security\Http\Logout\LogoutUrlGenerator" public="false">
<argument type="service" id="request_stack" on-invalid="null" />
<argument type="service" id="router" on-invalid="null" />
<argument type="service" id="security.token_storage" on-invalid="null" />
</service>
<!-- Provisioning -->
<service id="security.user.provider.in_memory" class="Symfony\Component\Security\Core\User\InMemoryUserProvider" abstract="true" public="false" />
<service id="security.user.provider.in_memory.user" class="Symfony\Component\Security\Core\User\User" abstract="true" public="false" />
<service id="security.user.provider.ldap" class="Symfony\Component\Security\Core\User\LdapUserProvider" abstract="true" public="false">
<argument /> <!-- security.ldap.ldap -->
<argument /> <!-- base dn -->
<argument /> <!-- search dn -->
<argument /> <!-- search password -->
<argument /> <!-- default_roles -->
<argument /> <!-- uid key -->
<argument /> <!-- filter -->
<argument /> <!-- password_attribute -->
</service>
<service id="security.user.provider.chain" class="Symfony\Component\Security\Core\User\ChainUserProvider" abstract="true" public="false" />
<service id="security.http_utils" class="Symfony\Component\Security\Http\HttpUtils" public="false">
<argument type="service" id="router" on-invalid="null" />
<argument type="service" id="router" on-invalid="null" />
</service>
<!-- Validator -->
<service id="security.validator.user_password" class="Symfony\Component\Security\Core\Validator\Constraints\UserPasswordValidator">
<tag name="validator.constraint_validator" alias="security.validator.user_password" />
<argument type="service" id="security.token_storage" />
<argument type="service" id="security.encoder_factory" />
</service>
</services>
</container>

View File

@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<services>
<service id="security.acl.object_identity_retrieval_strategy" class="Symfony\Component\Security\Acl\Domain\ObjectIdentityRetrievalStrategy" public="false" />
<service id="security.acl.security_identity_retrieval_strategy" class="Symfony\Component\Security\Acl\Domain\SecurityIdentityRetrievalStrategy" public="false">
<argument type="service" id="security.role_hierarchy" />
<argument type="service" id="security.authentication.trust_resolver" />
</service>
<service id="security.acl.permission_granting_strategy" class="Symfony\Component\Security\Acl\Domain\PermissionGrantingStrategy" public="false">
<call method="setAuditLogger">
<argument type="service" id="security.acl.audit_logger" on-invalid="ignore" />
</call>
</service>
<service id="security.acl.permission.map" class="Symfony\Component\Security\Acl\Permission\BasicPermissionMap" public="false" />
<service id="security.acl.voter.basic_permissions" class="Symfony\Component\Security\Acl\Voter\AclVoter" public="false">
<tag name="monolog.logger" channel="security" />
<argument type="service" id="security.acl.provider" />
<argument type="service" id="security.acl.object_identity_retrieval_strategy" />
<argument type="service" id="security.acl.security_identity_retrieval_strategy" />
<argument type="service" id="security.acl.permission.map" />
<argument type="service" id="logger" on-invalid="null" />
<tag name="security.voter" priority="255" />
</service>
</services>
</container>

View File

@@ -0,0 +1,46 @@
<?xml version="1.0" encoding="UTF-8"?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<services>
<service id="security.acl.dbal.connection" alias="database_connection" />
<service id="security.acl.dbal.provider" class="Symfony\Component\Security\Acl\Dbal\MutableAclProvider" public="false">
<argument type="service" id="security.acl.dbal.connection" />
<argument type="service" id="security.acl.permission_granting_strategy" />
<argument type="collection">
<argument key="class_table_name">%security.acl.dbal.class_table_name%</argument>
<argument key="entry_table_name">%security.acl.dbal.entry_table_name%</argument>
<argument key="oid_table_name">%security.acl.dbal.oid_table_name%</argument>
<argument key="oid_ancestors_table_name">%security.acl.dbal.oid_ancestors_table_name%</argument>
<argument key="sid_table_name">%security.acl.dbal.sid_table_name%</argument>
</argument>
<argument type="service" id="security.acl.cache" on-invalid="null" />
</service>
<service id="security.acl.dbal.schema" class="Symfony\Component\Security\Acl\Dbal\Schema">
<argument type="collection">
<argument key="class_table_name">%security.acl.dbal.class_table_name%</argument>
<argument key="entry_table_name">%security.acl.dbal.entry_table_name%</argument>
<argument key="oid_table_name">%security.acl.dbal.oid_table_name%</argument>
<argument key="oid_ancestors_table_name">%security.acl.dbal.oid_ancestors_table_name%</argument>
<argument key="sid_table_name">%security.acl.dbal.sid_table_name%</argument>
</argument>
<argument type="service" id="security.acl.dbal.connection" />
</service>
<service id="security.acl.dbal.schema_listener" class="Symfony\Bundle\SecurityBundle\EventListener\AclSchemaListener" public="false">
<argument type="service" id="security.acl.dbal.schema" />
</service>
<service id="security.acl.provider" alias="security.acl.dbal.provider" />
<service id="security.acl.cache.doctrine" class="Symfony\Component\Security\Acl\Domain\DoctrineAclCache" public="false">
<argument type="service" id="security.acl.cache.doctrine.cache_impl" />
<argument type="service" id="security.acl.permission_granting_strategy" />
</service>
<service id="security.acl.cache.doctrine.cache_impl" alias="doctrine.orm.default_result_cache" public="false" />
</services>
</container>

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<services>
<service id="debug.security.access.decision_manager" class="Symfony\Component\Security\Core\Authorization\DebugAccessDecisionManager" decorates="security.access.decision_manager" public="false">
<argument type="service" id="debug.security.access.decision_manager.inner" />
</service>
</services>
</container>

View File

@@ -0,0 +1,236 @@
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<services>
<service id="security.authentication.listener.anonymous" class="Symfony\Component\Security\Http\Firewall\AnonymousAuthenticationListener" public="false">
<tag name="monolog.logger" channel="security" />
<argument type="service" id="security.token_storage" />
<argument /> <!-- Key -->
<argument type="service" id="logger" on-invalid="null" />
<argument type="service" id="security.authentication.manager" />
</service>
<service id="security.authentication.provider.anonymous" class="Symfony\Component\Security\Core\Authentication\Provider\AnonymousAuthenticationProvider" public="false">
<argument /> <!-- Key -->
</service>
<service id="security.authentication.retry_entry_point" class="Symfony\Component\Security\Http\EntryPoint\RetryAuthenticationEntryPoint" public="false">
<argument>%request_listener.http_port%</argument>
<argument>%request_listener.https_port%</argument>
</service>
<service id="security.authentication.basic_entry_point" class="Symfony\Component\Security\Http\EntryPoint\BasicAuthenticationEntryPoint" public="false" />
<service id="security.authentication.digest_entry_point" class="Symfony\Component\Security\Http\EntryPoint\DigestAuthenticationEntryPoint" public="false" />
<service id="security.channel_listener" class="Symfony\Component\Security\Http\Firewall\ChannelListener" public="false">
<tag name="monolog.logger" channel="security" />
<argument type="service" id="security.access_map" />
<argument type="service" id="security.authentication.retry_entry_point" />
<argument type="service" id="logger" on-invalid="null" />
</service>
<service id="security.access_map" class="Symfony\Component\Security\Http\AccessMap" public="false" />
<service id="security.context_listener" class="Symfony\Component\Security\Http\Firewall\ContextListener" public="false">
<tag name="monolog.logger" channel="security" />
<argument type="service" id="security.token_storage" />
<argument type="collection" />
<argument /> <!-- Provider Key -->
<argument type="service" id="logger" on-invalid="null" />
<argument type="service" id="event_dispatcher" on-invalid="null" />
<argument type="service" id="security.authentication.trust_resolver" />
</service>
<service id="security.logout_listener" class="Symfony\Component\Security\Http\Firewall\LogoutListener" public="false" abstract="true">
<argument type="service" id="security.token_storage" />
<argument type="service" id="security.http_utils" />
<argument type="service" id="security.logout.success_handler" />
<argument /> <!-- Options -->
</service>
<service id="security.logout.handler.session" class="Symfony\Component\Security\Http\Logout\SessionLogoutHandler" public="false" />
<service id="security.logout.handler.cookie_clearing" class="Symfony\Component\Security\Http\Logout\CookieClearingLogoutHandler" public="false" abstract="true" />
<service id="security.logout.success_handler" class="Symfony\Component\Security\Http\Logout\DefaultLogoutSuccessHandler" public="false" abstract="true">
<argument type="service" id="security.http_utils" />
<argument>/</argument>
</service>
<service id="security.authentication.form_entry_point" class="Symfony\Component\Security\Http\EntryPoint\FormAuthenticationEntryPoint" public="false" abstract="true">
<argument type="service" id="http_kernel" />
</service>
<service id="security.authentication.listener.abstract" abstract="true" public="false">
<tag name="monolog.logger" channel="security" />
<argument type="service" id="security.token_storage" />
<argument type="service" id="security.authentication.manager" />
<argument type="service" id="security.authentication.session_strategy" />
<argument type="service" id="security.http_utils" />
<argument />
<argument type="service" id="security.authentication.success_handler" />
<argument type="service" id="security.authentication.failure_handler" />
<argument type="collection" />
<argument type="service" id="logger" on-invalid="null" />
<argument type="service" id="event_dispatcher" on-invalid="null" />
</service>
<service id="security.authentication.custom_success_handler" class="Symfony\Component\Security\Http\Authentication\CustomAuthenticationSuccessHandler" abstract="true" public="false">
<argument /> <!-- The custom success handler service id -->
<argument type="collection" /> <!-- Options -->
<argument /> <!-- Provider-shared Key -->
</service>
<service id="security.authentication.success_handler" class="Symfony\Component\Security\Http\Authentication\DefaultAuthenticationSuccessHandler" abstract="true" public="false">
<argument type="service" id="security.http_utils" />
<argument type="collection" /> <!-- Options -->
</service>
<service id="security.authentication.custom_failure_handler" class="Symfony\Component\Security\Http\Authentication\CustomAuthenticationFailureHandler" abstract="true" public="false">
<argument /> <!-- The custom failure handler service id -->
<argument type="collection" /> <!-- Options -->
</service>
<service id="security.authentication.failure_handler" class="Symfony\Component\Security\Http\Authentication\DefaultAuthenticationFailureHandler" abstract="true" public="false">
<tag name="monolog.logger" channel="security" />
<argument type="service" id="http_kernel" />
<argument type="service" id="security.http_utils" />
<argument type="collection" /> <!-- Options -->
<argument type="service" id="logger" on-invalid="null" />
</service>
<service id="security.authentication.listener.form"
class="Symfony\Component\Security\Http\Firewall\UsernamePasswordFormAuthenticationListener"
parent="security.authentication.listener.abstract"
abstract="true" />
<service id="security.authentication.listener.simple_form"
class="Symfony\Component\Security\Http\Firewall\SimpleFormAuthenticationListener"
parent="security.authentication.listener.abstract"
abstract="true">
</service>
<service id="security.authentication.simple_success_failure_handler" class="Symfony\Component\Security\Http\Authentication\SimpleAuthenticationHandler" public="false" abstract="true">
<tag name="monolog.logger" channel="security" />
<argument /> <!-- Authenticator -->
<argument type="service" id="security.authentication.success_handler" />
<argument type="service" id="security.authentication.failure_handler" />
<argument type="service" id="logger" on-invalid="null" />
</service>
<service id="security.authentication.listener.simple_preauth" class="Symfony\Component\Security\Http\Firewall\SimplePreAuthenticationListener" public="false" abstract="true">
<tag name="monolog.logger" channel="security" />
<argument type="service" id="security.token_storage" />
<argument type="service" id="security.authentication.manager" />
<argument /> <!-- Provider-shared Key -->
<argument /> <!-- Authenticator -->
<argument type="service" id="logger" on-invalid="null" />
<argument type="service" id="event_dispatcher" on-invalid="null"/>
</service>
<service id="security.authentication.listener.x509" class="Symfony\Component\Security\Http\Firewall\X509AuthenticationListener" public="false" abstract="true">
<tag name="monolog.logger" channel="security" />
<argument type="service" id="security.token_storage" />
<argument type="service" id="security.authentication.manager" />
<argument /> <!-- Provider-shared Key -->
<argument /> <!-- x509 user -->
<argument /> <!-- x509 credentials -->
<argument type="service" id="logger" on-invalid="null" />
<argument type="service" id="event_dispatcher" on-invalid="null"/>
</service>
<service id="security.authentication.listener.remote_user" class="Symfony\Component\Security\Http\Firewall\RemoteUserAuthenticationListener" public="false" abstract="true">
<tag name="monolog.logger" channel="security" />
<argument type="service" id="security.token_storage" />
<argument type="service" id="security.authentication.manager" />
<argument /> <!-- Provider-shared Key -->
<argument /> <!-- REMOTE_USER server env var -->
<argument type="service" id="logger" on-invalid="null" />
<argument type="service" id="event_dispatcher" on-invalid="null"/>
</service>
<service id="security.authentication.listener.basic" class="Symfony\Component\Security\Http\Firewall\BasicAuthenticationListener" public="false" abstract="true">
<tag name="monolog.logger" channel="security" />
<argument type="service" id="security.token_storage" />
<argument type="service" id="security.authentication.manager" />
<argument /> <!-- Provider-shared Key -->
<argument /> <!-- Entry Point -->
<argument type="service" id="logger" on-invalid="null" />
</service>
<service id="security.authentication.listener.digest" class="Symfony\Component\Security\Http\Firewall\DigestAuthenticationListener" public="false" abstract="true">
<tag name="monolog.logger" channel="security" />
<argument type="service" id="security.token_storage" />
<argument /> <!-- User Provider -->
<argument /> <!-- Provider-shared Key -->
<argument /> <!-- Entry Point -->
<argument type="service" id="logger" on-invalid="null" />
</service>
<service id="security.authentication.provider.dao" class="Symfony\Component\Security\Core\Authentication\Provider\DaoAuthenticationProvider" abstract="true" public="false">
<argument /> <!-- User Provider -->
<argument /> <!-- User Checker -->
<argument /> <!-- Provider-shared Key -->
<argument type="service" id="security.encoder_factory" />
<argument>%security.authentication.hide_user_not_found%</argument>
</service>
<service id="security.authentication.provider.ldap_bind" class="Symfony\Component\Security\Core\Authentication\Provider\LdapBindAuthenticationProvider" public="false" abstract="true">
<argument /> <!-- User Provider -->
<argument /> <!-- UserChecker -->
<argument /> <!-- Provider-shared Key -->
<argument /> <!-- LDAP -->
<argument /> <!-- Base DN -->
<argument>%security.authentication.hide_user_not_found%</argument>
</service>
<service id="security.authentication.provider.simple" class="Symfony\Component\Security\Core\Authentication\Provider\SimpleAuthenticationProvider" abstract="true" public="false">
<argument /> <!-- Simple Authenticator -->
<argument /> <!-- User Provider -->
<argument /> <!-- Provider-shared Key -->
</service>
<service id="security.authentication.provider.pre_authenticated" class="Symfony\Component\Security\Core\Authentication\Provider\PreAuthenticatedAuthenticationProvider" abstract="true" public="false">
<argument /> <!-- User Provider -->
<argument /> <!-- User Checker -->
</service>
<service id="security.exception_listener" class="Symfony\Component\Security\Http\Firewall\ExceptionListener" public="false" abstract="true">
<tag name="monolog.logger" channel="security" />
<argument type="service" id="security.token_storage" />
<argument type="service" id="security.authentication.trust_resolver" />
<argument type="service" id="security.http_utils" />
<argument />
<argument type="service" id="security.authentication.entry_point" on-invalid="null" />
<argument>%security.access.denied_url%</argument>
<argument type="service" id="security.access.denied_handler" on-invalid="null" />
<argument type="service" id="logger" on-invalid="null" />
<argument>false</argument> <!-- Stateless -->
</service>
<service id="security.authentication.switchuser_listener" class="Symfony\Component\Security\Http\Firewall\SwitchUserListener" public="false" abstract="true">
<tag name="monolog.logger" channel="security" />
<argument type="service" id="security.token_storage" />
<argument /> <!-- User Provider -->
<argument /> <!-- User Checker -->
<argument /> <!-- Provider Key -->
<argument type="service" id="security.access.decision_manager" />
<argument type="service" id="logger" on-invalid="null" />
<argument>_switch_user</argument>
<argument>ROLE_ALLOWED_TO_SWITCH</argument>
<argument type="service" id="event_dispatcher" on-invalid="null"/>
</service>
<service id="security.access_listener" class="Symfony\Component\Security\Http\Firewall\AccessListener" public="false">
<tag name="monolog.logger" channel="security" />
<argument type="service" id="security.token_storage" />
<argument type="service" id="security.access.decision_manager" />
<argument type="service" id="security.access_map" />
<argument type="service" id="security.authentication.manager" />
</service>
</services>
</container>

View File

@@ -0,0 +1,49 @@
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<services>
<service id="security.authentication.listener.rememberme" class="Symfony\Component\Security\Http\Firewall\RememberMeListener" public="false" abstract="true">
<tag name="monolog.logger" channel="security" />
<argument type="service" id="security.token_storage" />
<argument type="service" id="security.authentication.rememberme" />
<argument type="service" id="security.authentication.manager" />
<argument type="service" id="logger" on-invalid="null" />
<argument type="service" id="event_dispatcher" on-invalid="null"/>
<argument /> <!-- Catch exception flag set in RememberMeFactory -->
<argument type="service" id="security.authentication.session_strategy" />
</service>
<service id="security.authentication.provider.rememberme" class="Symfony\Component\Security\Core\Authentication\Provider\RememberMeAuthenticationProvider" abstract="true" public="false">
<argument /> <!-- User Checker -->
</service>
<service id="security.rememberme.token.provider.in_memory" class="Symfony\Component\Security\Core\Authentication\RememberMe\InMemoryTokenProvider" public="false" />
<service id="security.authentication.rememberme.services.abstract" abstract="true" public="false">
<tag name="monolog.logger" channel="security" />
<argument type="collection" /> <!-- User Providers -->
<argument /> <!-- Shared Token Key -->
<argument /> <!-- Shared Provider Key -->
<argument type="collection" /> <!-- Options -->
<argument type="service" id="logger" on-invalid="null" />
</service>
<service id="security.authentication.rememberme.services.persistent"
class="Symfony\Component\Security\Http\RememberMe\PersistentTokenBasedRememberMeServices"
parent="security.authentication.rememberme.services.abstract"
abstract="true" />
<service id="security.authentication.rememberme.services.simplehash"
class="Symfony\Component\Security\Http\RememberMe\TokenBasedRememberMeServices"
parent="security.authentication.rememberme.services.abstract"
abstract="true" />
<service id="security.rememberme.response_listener" class="Symfony\Component\Security\Http\RememberMe\ResponseListener">
<tag name="kernel.event_subscriber" />
</service>
</services>
</container>

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<services>
<service id="templating.helper.logout_url" class="Symfony\Bundle\SecurityBundle\Templating\Helper\LogoutUrlHelper">
<tag name="templating.helper" alias="logout_url" />
<argument type="service" id="security.logout_url_generator" />
</service>
<service id="templating.helper.security" class="Symfony\Bundle\SecurityBundle\Templating\Helper\SecurityHelper">
<tag name="templating.helper" alias="security" />
<argument type="service" id="security.authorization_checker" on-invalid="ignore" />
</service>
</services>
</container>

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<services>
<service id="twig.extension.logout_url" class="Symfony\Bridge\Twig\Extension\LogoutUrlExtension" public="false">
<tag name="twig.extension" />
<argument type="service" id="security.logout_url_generator" />
</service>
<service id="twig.extension.security" class="Symfony\Bridge\Twig\Extension\SecurityExtension" public="false">
<tag name="twig.extension" />
<argument type="service" id="security.authorization_checker" on-invalid="ignore" />
</service>
</services>
</container>

View File

@@ -0,0 +1,3 @@
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" width="24" height="24" viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve">
<path fill="#AAAAAA" d="M21,20.4V22H3v-1.6c0-3.7,2.4-6.9,5.8-8c-1.7-1.1-2.9-3-2.9-5.2c0-3.4,2.7-6.1,6.1-6.1s6.1,2.7,6.1,6.1c0,2.2-1.2,4.1-2.9,5.2C18.6,13.5,21,16.7,21,20.4z"/>
</svg>

After

Width:  |  Height:  |  Size: 356 B

View File

@@ -0,0 +1,273 @@
{% extends '@WebProfiler/Profiler/layout.html.twig' %}
{% block page_title 'Security' %}
{% block toolbar %}
{% if collector.token %}
{% set is_authenticated = collector.enabled and collector.authenticated %}
{% set color_code = is_authenticated ? '' : 'yellow' %}
{% else %}
{% set color_code = collector.enabled ? 'red' : '' %}
{% endif %}
{% set icon %}
{{ include('@Security/Collector/icon.svg') }}
<span class="sf-toolbar-value">{{ collector.user|default('n/a') }}</span>
{% endset %}
{% set text %}
{% if collector.enabled %}
{% if collector.token %}
<div class="sf-toolbar-info-piece">
<b>Logged in as</b>
<span>{{ collector.user }}</span>
</div>
<div class="sf-toolbar-info-piece">
<b>Authenticated</b>
<span class="sf-toolbar-status sf-toolbar-status-{{ is_authenticated ? 'green' : 'red' }}">{{ is_authenticated ? 'Yes' : 'No' }}</span>
</div>
<div class="sf-toolbar-info-piece">
<b>Token class</b>
<span>{{ collector.tokenClass|abbr_class }}</span>
</div>
{% else %}
<div class="sf-toolbar-info-piece">
<b>Authenticated</b>
<span class="sf-toolbar-status sf-toolbar-status-red">No</span>
</div>
{% endif %}
{% if collector.firewall %}
<div class="sf-toolbar-info-piece">
<b>Firewall name</b>
<span>{{ collector.firewall.name }}</span>
</div>
{% endif %}
{% if collector.token and collector.logoutUrl %}
<div class="sf-toolbar-info-piece">
<b>Actions</b>
<span><a href="{{ collector.logoutUrl }}">Logout</a></span>
</div>
{% endif %}
{% else %}
<div class="sf-toolbar-info-piece">
<span>The security is disabled.</span>
</div>
{% endif %}
{% endset %}
{{ include('@WebProfiler/Profiler/toolbar_item.html.twig', { link: profiler_url, status: color_code }) }}
{% endblock %}
{% block menu %}
<span class="label {{ not collector.enabled or not collector.token ? 'disabled' }}">
<span class="icon">{{ include('@Security/Collector/icon.svg') }}</span>
<strong>Security</strong>
</span>
{% endblock %}
{% block panel %}
<h2>Security Token</h2>
{% if collector.enabled %}
{% if collector.token %}
<div class="metrics">
<div class="metric">
<span class="value">{{ collector.user == 'anon.' ? 'Anonymous' : collector.user }}</span>
<span class="label">Username</span>
</div>
<div class="metric">
<span class="value">{{ include('@WebProfiler/Icon/' ~ (collector.authenticated ? 'yes' : 'no') ~ '.svg') }}</span>
<span class="label">Authenticated</span>
</div>
</div>
<table>
<thead>
<tr>
<th scope="col" class="key">Property</th>
<th scope="col">Value</th>
</tr>
</thead>
<tbody>
<tr>
<th>Roles</th>
<td>
{{ collector.roles is empty ? 'none' : profiler_dump(collector.roles, maxDepth=1) }}
{% if not collector.authenticated and collector.roles is empty %}
<p class="help">User is not authenticated probably because they have no roles.</p>
{% endif %}
</td>
</tr>
{% if collector.supportsRoleHierarchy %}
<tr>
<th>Inherited Roles</th>
<td>{{ collector.inheritedRoles is empty ? 'none' : profiler_dump(collector.inheritedRoles, maxDepth=1) }}</td>
</tr>
{% endif %}
{% if collector.token %}
<tr>
<th>Token</th>
<td>{{ profiler_dump(collector.token) }}</td>
</tr>
{% endif %}
</tbody>
</table>
{% elseif collector.enabled %}
<div class="empty">
<p>There is no security token.</p>
</div>
{% endif %}
<h2>Security Firewall</h2>
{% if collector.firewall %}
<div class="metrics">
<div class="metric">
<span class="value">{{ collector.firewall.name }}</span>
<span class="label">Name</span>
</div>
<div class="metric">
<span class="value">{{ include('@WebProfiler/Icon/' ~ (collector.firewall.security_enabled ? 'yes' : 'no') ~ '.svg') }}</span>
<span class="label">Security enabled</span>
</div>
<div class="metric">
<span class="value">{{ include('@WebProfiler/Icon/' ~ (collector.firewall.stateless ? 'yes' : 'no') ~ '.svg') }}</span>
<span class="label">Stateless</span>
</div>
<div class="metric">
<span class="value">{{ include('@WebProfiler/Icon/' ~ (collector.firewall.allows_anonymous ? 'yes' : 'no') ~ '.svg') }}</span>
<span class="label">Allows anonymous</span>
</div>
</div>
{% if collector.firewall.security_enabled %}
<table>
<thead>
<tr>
<th scope="col" class="key">Key</th>
<th scope="col">Value</th>
</tr>
</thead>
<tbody>
<tr>
<th>provider</th>
<td>{{ collector.firewall.provider ?: '(none)' }}</td>
</tr>
<tr>
<th>context</th>
<td>{{ collector.firewall.context ?: '(none)' }}</td>
</tr>
<tr>
<th>entry_point</th>
<td>{{ collector.firewall.entry_point ?: '(none)' }}</td>
</tr>
<tr>
<th>user_checker</th>
<td>{{ collector.firewall.user_checker ?: '(none)' }}</td>
</tr>
<tr>
<th>access_denied_handler</th>
<td>{{ collector.firewall.access_denied_handler ?: '(none)' }}</td>
</tr>
<tr>
<th>access_denied_url</th>
<td>{{ collector.firewall.access_denied_url ?: '(none)' }}</td>
</tr>
<tr>
<th>listeners</th>
<td>{{ collector.firewall.listeners is empty ? '(none)' : profiler_dump(collector.firewall.listeners, maxDepth=1) }}</td>
</tr>
</tbody>
</table>
{% endif %}
{% elseif collector.enabled %}
<div class="empty">
<p>This request was not covered by any firewall.</p>
</div>
{% endif %}
{% else %}
<div class="empty">
<p>The security component is disabled.</p>
</div>
{% endif %}
{% if collector.voters|default([]) is not empty %}
<h2>Security Voters <small>({{ collector.voters|length }})</small></h2>
<div class="metrics">
<div class="metric">
<span class="value">{{ collector.voterStrategy|default('unknown') }}</span>
<span class="label">Strategy</span>
</div>
</div>
<table class="voters">
<thead>
<tr>
<th>#</th>
<th>Voter class</th>
</tr>
</thead>
<tbody>
{% for voter in collector.voters %}
<tr>
<td class="font-normal text-small text-muted nowrap">{{ loop.index }}</td>
<td class="font-normal">{{ voter }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
{% if collector.accessDecisionLog|default([]) is not empty %}
<h2>Access decision log</h2>
<table class="decision-log">
<col style="width: 30px">
<col style="width: 120px">
<col style="width: 25%">
<col style="width: 60%">
<thead>
<tr>
<th>#</th>
<th>Result</th>
<th>Attributes</th>
<th>Object</th>
</tr>
</thead>
<tbody>
{% for decision in collector.accessDecisionLog %}
<tr>
<td class="font-normal text-small text-muted nowrap">{{ loop.index }}</td>
<td class="font-normal">
{{ decision.result
? '<span class="label status-success same-width">GRANTED</span>'
: '<span class="label status-error same-width">DENIED</span>'
}}
</td>
<td>
{% if decision.attributes|length == 1 %}
{{ decision.attributes|first }}
{% else %}
{{ profiler_dump(decision.attributes) }}
{% endif %}
</td>
<td>{{ profiler_dump(decision.object) }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
{% endblock %}

View File

@@ -0,0 +1,143 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\SecurityBundle\Security;
/**
* @author Robin Chalas <robin.chalas@gmail.com>
*/
final class FirewallConfig
{
private $name;
private $userChecker;
private $requestMatcher;
private $securityEnabled;
private $stateless;
private $provider;
private $context;
private $entryPoint;
private $accessDeniedHandler;
private $accessDeniedUrl;
private $listeners;
/**
* @param string $name
* @param string $userChecker
* @param string|null $requestMatcher
* @param bool $securityEnabled
* @param bool $stateless
* @param string|null $provider
* @param string|null $context
* @param string|null $entryPoint
* @param string|null $accessDeniedHandler
* @param string|null $accessDeniedUrl
* @param string[] $listeners
*/
public function __construct($name, $userChecker, $requestMatcher = null, $securityEnabled = true, $stateless = false, $provider = null, $context = null, $entryPoint = null, $accessDeniedHandler = null, $accessDeniedUrl = null, $listeners = array())
{
$this->name = $name;
$this->userChecker = $userChecker;
$this->requestMatcher = $requestMatcher;
$this->securityEnabled = $securityEnabled;
$this->stateless = $stateless;
$this->provider = $provider;
$this->context = $context;
$this->entryPoint = $entryPoint;
$this->accessDeniedHandler = $accessDeniedHandler;
$this->accessDeniedUrl = $accessDeniedUrl;
$this->listeners = $listeners;
}
public function getName()
{
return $this->name;
}
/**
* @return string|null The request matcher service id or null if neither the request matcher, pattern or host
* options were provided
*/
public function getRequestMatcher()
{
return $this->requestMatcher;
}
public function isSecurityEnabled()
{
return $this->securityEnabled;
}
public function allowsAnonymous()
{
return in_array('anonymous', $this->listeners, true);
}
public function isStateless()
{
return $this->stateless;
}
/**
* @return string|null The provider service id
*/
public function getProvider()
{
return $this->provider;
}
/**
* @return string|null The context key (will be null if the firewall is stateless)
*/
public function getContext()
{
return $this->context;
}
/**
* @return string|null The entry_point service id if configured, null otherwise
*/
public function getEntryPoint()
{
return $this->entryPoint;
}
/**
* @return string The user_checker service id
*/
public function getUserChecker()
{
return $this->userChecker;
}
/**
* @return string|null The access_denied_handler service id if configured, null otherwise
*/
public function getAccessDeniedHandler()
{
return $this->accessDeniedHandler;
}
/**
* @return string|null The access_denied_handler URL if configured, null otherwise
*/
public function getAccessDeniedUrl()
{
return $this->accessDeniedUrl;
}
/**
* @return string[] An array of listener keys
*/
public function getListeners()
{
return $this->listeners;
}
}

View File

@@ -0,0 +1,44 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\SecurityBundle\Security;
use Symfony\Component\Security\Http\Firewall\ExceptionListener;
/**
* This is a wrapper around the actual firewall configuration which allows us
* to lazy load the context for one specific firewall only when we need it.
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
class FirewallContext
{
private $listeners;
private $exceptionListener;
private $config;
public function __construct(array $listeners, ExceptionListener $exceptionListener = null, FirewallConfig $config = null)
{
$this->listeners = $listeners;
$this->exceptionListener = $exceptionListener;
$this->config = $config;
}
public function getConfig()
{
return $this->config;
}
public function getContext()
{
return array($this->listeners, $this->exceptionListener);
}
}

View File

@@ -0,0 +1,89 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\SecurityBundle\Security;
use Symfony\Bundle\SecurityBundle\Security\FirewallContext;
use Symfony\Component\Security\Http\FirewallMapInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* This is a lazy-loading firewall map implementation.
*
* Listeners will only be initialized if we really need them.
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
class FirewallMap implements FirewallMapInterface
{
protected $container;
protected $map;
public function __construct(ContainerInterface $container, array $map)
{
$this->container = $container;
$this->map = $map;
}
/**
* {@inheritdoc}
*/
public function getListeners(Request $request)
{
$context = $this->getFirewallContext($request);
if (null === $context) {
return array(array(), null);
}
return $context->getContext();
}
/**
* @return FirewallConfig|null
*/
public function getFirewallConfig(Request $request)
{
$context = $this->getFirewallContext($request);
if (null === $context) {
return;
}
return $context->getConfig();
}
/**
* @return FirewallContext
*/
private function getFirewallContext(Request $request)
{
if ($request->attributes->has('_firewall_context')) {
$storedContextId = $request->attributes->get('_firewall_context');
foreach ($this->map as $contextId => $requestMatcher) {
if ($contextId === $storedContextId) {
return $this->container->get($contextId);
}
}
$request->attributes->remove('_firewall_context');
}
foreach ($this->map as $contextId => $requestMatcher) {
if (null === $requestMatcher || $requestMatcher->matches($request)) {
$request->attributes->set('_firewall_context', $contextId);
return $this->container->get($contextId);
}
}
}
}

View File

@@ -0,0 +1,62 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\SecurityBundle;
use Symfony\Component\HttpKernel\Bundle\Bundle;
use Symfony\Component\DependencyInjection\Compiler\PassConfig;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Bundle\SecurityBundle\DependencyInjection\Compiler\AddSecurityVotersPass;
use Symfony\Bundle\SecurityBundle\DependencyInjection\Compiler\AddSessionDomainConstraintPass;
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\FormLoginFactory;
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\FormLoginLdapFactory;
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\HttpBasicFactory;
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\HttpBasicLdapFactory;
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\HttpDigestFactory;
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\RememberMeFactory;
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\X509Factory;
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\RemoteUserFactory;
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\SimplePreAuthenticationFactory;
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\SimpleFormFactory;
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\UserProvider\InMemoryFactory;
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\GuardAuthenticationFactory;
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\UserProvider\LdapFactory;
/**
* Bundle.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class SecurityBundle extends Bundle
{
public function build(ContainerBuilder $container)
{
parent::build($container);
$extension = $container->getExtension('security');
$extension->addSecurityListenerFactory(new FormLoginFactory());
$extension->addSecurityListenerFactory(new FormLoginLdapFactory());
$extension->addSecurityListenerFactory(new HttpBasicFactory());
$extension->addSecurityListenerFactory(new HttpBasicLdapFactory());
$extension->addSecurityListenerFactory(new HttpDigestFactory());
$extension->addSecurityListenerFactory(new RememberMeFactory());
$extension->addSecurityListenerFactory(new X509Factory());
$extension->addSecurityListenerFactory(new RemoteUserFactory());
$extension->addSecurityListenerFactory(new SimplePreAuthenticationFactory());
$extension->addSecurityListenerFactory(new SimpleFormFactory());
$extension->addSecurityListenerFactory(new GuardAuthenticationFactory());
$extension->addUserProviderFactory(new InMemoryFactory());
$extension->addUserProviderFactory(new LdapFactory());
$container->addCompilerPass(new AddSecurityVotersPass());
$container->addCompilerPass(new AddSessionDomainConstraintPass(), PassConfig::TYPE_AFTER_REMOVING);
}
}

View File

@@ -0,0 +1,57 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\SecurityBundle;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Controller\ArgumentValueResolverInterface;
use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\User\UserInterface;
/**
* Supports the argument type of {@see UserInterface}.
*
* @author Iltar van der Berg <kjarli@gmail.com>
*/
final class SecurityUserValueResolver implements ArgumentValueResolverInterface
{
private $tokenStorage;
public function __construct(TokenStorageInterface $tokenStorage)
{
$this->tokenStorage = $tokenStorage;
}
public function supports(Request $request, ArgumentMetadata $argument)
{
// only security user implementations are supported
if (UserInterface::class !== $argument->getType()) {
return false;
}
$token = $this->tokenStorage->getToken();
if (!$token instanceof TokenInterface) {
return false;
}
$user = $token->getUser();
// in case it's not an object we cannot do anything with it; E.g. "anon."
return $user instanceof UserInterface;
}
public function resolve(Request $request, ArgumentMetadata $argument)
{
yield $this->tokenStorage->getToken()->getUser();
}
}

View File

@@ -0,0 +1,68 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\SecurityBundle\Templating\Helper;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Security\Http\Logout\LogoutUrlGenerator;
use Symfony\Component\Templating\Helper\Helper;
/**
* LogoutUrlHelper provides generator functions for the logout URL.
*
* @author Jeremy Mikola <jmikola@gmail.com>
*/
class LogoutUrlHelper extends Helper
{
private $generator;
/**
* Constructor.
*
* @param LogoutUrlGenerator $generator A LogoutUrlGenerator instance
*/
public function __construct(LogoutUrlGenerator $generator)
{
$this->generator = $generator;
}
/**
* Generates the absolute logout path for the firewall.
*
* @param string|null $key The firewall key or null to use the current firewall key
*
* @return string The logout path
*/
public function getLogoutPath($key)
{
return $this->generator->getLogoutPath($key, UrlGeneratorInterface::ABSOLUTE_PATH);
}
/**
* Generates the absolute logout URL for the firewall.
*
* @param string|null $key The firewall key or null to use the current firewall key
*
* @return string The logout URL
*/
public function getLogoutUrl($key)
{
return $this->generator->getLogoutUrl($key, UrlGeneratorInterface::ABSOLUTE_URL);
}
/**
* {@inheritdoc}
*/
public function getName()
{
return 'logout_url';
}
}

View File

@@ -0,0 +1,52 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\SecurityBundle\Templating\Helper;
use Symfony\Component\Security\Acl\Voter\FieldVote;
use Symfony\Component\Templating\Helper\Helper;
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
/**
* SecurityHelper provides read-only access to the security checker.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class SecurityHelper extends Helper
{
private $securityChecker;
public function __construct(AuthorizationCheckerInterface $securityChecker = null)
{
$this->securityChecker = $securityChecker;
}
public function isGranted($role, $object = null, $field = null)
{
if (null === $this->securityChecker) {
return false;
}
if (null !== $field) {
$object = new FieldVote($object, $field);
}
return $this->securityChecker->isGranted($role, $object);
}
/**
* {@inheritdoc}
*/
public function getName()
{
return 'security';
}
}

View File

@@ -0,0 +1,200 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\SecurityBundle\Tests\DataCollector;
use PHPUnit\Framework\TestCase;
use Symfony\Bundle\SecurityBundle\DataCollector\SecurityDataCollector;
use Symfony\Bundle\SecurityBundle\Security\FirewallConfig;
use Symfony\Bundle\SecurityBundle\Security\FirewallMap;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Component\Security\Core\Role\Role;
use Symfony\Component\Security\Core\Role\RoleHierarchy;
use Symfony\Component\Security\Http\FirewallMapInterface;
class SecurityDataCollectorTest extends TestCase
{
public function testCollectWhenSecurityIsDisabled()
{
$collector = new SecurityDataCollector();
$collector->collect($this->getRequest(), $this->getResponse());
$this->assertSame('security', $collector->getName());
$this->assertFalse($collector->isEnabled());
$this->assertFalse($collector->isAuthenticated());
$this->assertNull($collector->getTokenClass());
$this->assertFalse($collector->supportsRoleHierarchy());
$this->assertCount(0, $collector->getRoles());
$this->assertCount(0, $collector->getInheritedRoles());
$this->assertEmpty($collector->getUser());
$this->assertNull($collector->getFirewall());
}
public function testCollectWhenAuthenticationTokenIsNull()
{
$tokenStorage = new TokenStorage();
$collector = new SecurityDataCollector($tokenStorage, $this->getRoleHierarchy());
$collector->collect($this->getRequest(), $this->getResponse());
$this->assertTrue($collector->isEnabled());
$this->assertFalse($collector->isAuthenticated());
$this->assertNull($collector->getTokenClass());
$this->assertTrue($collector->supportsRoleHierarchy());
$this->assertCount(0, $collector->getRoles());
$this->assertCount(0, $collector->getInheritedRoles());
$this->assertEmpty($collector->getUser());
$this->assertNull($collector->getFirewall());
}
/** @dataProvider provideRoles */
public function testCollectAuthenticationTokenAndRoles(array $roles, array $normalizedRoles, array $inheritedRoles)
{
$tokenStorage = new TokenStorage();
$tokenStorage->setToken(new UsernamePasswordToken('hhamon', 'P4$$w0rD', 'provider', $roles));
$collector = new SecurityDataCollector($tokenStorage, $this->getRoleHierarchy());
$collector->collect($this->getRequest(), $this->getResponse());
$this->assertTrue($collector->isEnabled());
$this->assertTrue($collector->isAuthenticated());
$this->assertSame('Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken', $collector->getTokenClass());
$this->assertTrue($collector->supportsRoleHierarchy());
$this->assertSame($normalizedRoles, $collector->getRoles()->getRawData()[1]);
if ($inheritedRoles) {
$this->assertSame($inheritedRoles, $collector->getInheritedRoles()->getRawData()[1]);
} else {
$this->assertSame($inheritedRoles, $collector->getInheritedRoles()->getRawData()[0][0]);
}
$this->assertSame('hhamon', $collector->getUser());
}
public function testGetFirewall()
{
$firewallConfig = new FirewallConfig('dummy', 'security.request_matcher.dummy', 'security.user_checker.dummy');
$request = $this->getRequest();
$firewallMap = $this
->getMockBuilder(FirewallMap::class)
->disableOriginalConstructor()
->getMock();
$firewallMap
->expects($this->once())
->method('getFirewallConfig')
->with($request)
->willReturn($firewallConfig);
$collector = new SecurityDataCollector(null, null, null, null, $firewallMap);
$collector->collect($request, $this->getResponse());
$collected = $collector->getFirewall();
$this->assertSame($firewallConfig->getName(), $collected['name']);
$this->assertSame($firewallConfig->allowsAnonymous(), $collected['allows_anonymous']);
$this->assertSame($firewallConfig->getRequestMatcher(), $collected['request_matcher']);
$this->assertSame($firewallConfig->isSecurityEnabled(), $collected['security_enabled']);
$this->assertSame($firewallConfig->isStateless(), $collected['stateless']);
$this->assertSame($firewallConfig->getProvider(), $collected['provider']);
$this->assertSame($firewallConfig->getContext(), $collected['context']);
$this->assertSame($firewallConfig->getEntryPoint(), $collected['entry_point']);
$this->assertSame($firewallConfig->getAccessDeniedHandler(), $collected['access_denied_handler']);
$this->assertSame($firewallConfig->getAccessDeniedUrl(), $collected['access_denied_url']);
$this->assertSame($firewallConfig->getUserChecker(), $collected['user_checker']);
$this->assertSame($firewallConfig->getListeners(), $collected['listeners']->getRawData()[0][0]);
}
public function testGetFirewallReturnsNull()
{
$request = $this->getRequest();
$response = $this->getResponse();
// Don't inject any firewall map
$collector = new SecurityDataCollector();
$collector->collect($request, $response);
$this->assertNull($collector->getFirewall());
// Inject an instance that is not context aware
$firewallMap = $this
->getMockBuilder(FirewallMapInterface::class)
->disableOriginalConstructor()
->getMock();
$collector = new SecurityDataCollector(null, null, null, null, $firewallMap);
$collector->collect($request, $response);
$this->assertNull($collector->getFirewall());
// Null config
$firewallMap = $this
->getMockBuilder(FirewallMap::class)
->disableOriginalConstructor()
->getMock();
$collector = new SecurityDataCollector(null, null, null, null, $firewallMap);
$collector->collect($request, $response);
$this->assertNull($collector->getFirewall());
}
public function provideRoles()
{
return array(
// Basic roles
array(
array('ROLE_USER'),
array('ROLE_USER'),
array(),
),
array(
array(new Role('ROLE_USER')),
array('ROLE_USER'),
array(),
),
// Inherited roles
array(
array('ROLE_ADMIN'),
array('ROLE_ADMIN'),
array('ROLE_USER', 'ROLE_ALLOWED_TO_SWITCH'),
),
array(
array(new Role('ROLE_ADMIN')),
array('ROLE_ADMIN'),
array('ROLE_USER', 'ROLE_ALLOWED_TO_SWITCH'),
),
array(
array('ROLE_ADMIN', 'ROLE_OPERATOR'),
array('ROLE_ADMIN', 'ROLE_OPERATOR'),
array('ROLE_USER', 'ROLE_ALLOWED_TO_SWITCH'),
),
);
}
private function getRoleHierarchy()
{
return new RoleHierarchy(array(
'ROLE_ADMIN' => array('ROLE_USER', 'ROLE_ALLOWED_TO_SWITCH'),
'ROLE_OPERATOR' => array('ROLE_USER'),
));
}
private function getRequest()
{
return $this
->getMockBuilder('Symfony\Component\HttpFoundation\Request')
->disableOriginalConstructor()
->getMock();
}
private function getResponse()
{
return $this
->getMockBuilder('Symfony\Component\HttpFoundation\Response')
->disableOriginalConstructor()
->getMock();
}
}

View File

@@ -0,0 +1,68 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\SecurityBundle\Tests\DependencyInjection\Compiler;
use PHPUnit\Framework\TestCase;
use Symfony\Bundle\SecurityBundle\DependencyInjection\Compiler\AddSecurityVotersPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;
class AddSecurityVotersPassTest extends TestCase
{
/**
* @expectedException \Symfony\Component\DependencyInjection\Exception\LogicException
*/
public function testNoVoters()
{
$container = new ContainerBuilder();
$container
->register('security.access.decision_manager', 'Symfony\Component\Security\Core\Authorization\AccessDecisionManager')
->addArgument(array())
;
$compilerPass = new AddSecurityVotersPass();
$compilerPass->process($container);
}
public function testThatSecurityVotersAreProcessedInPriorityOrder()
{
$container = new ContainerBuilder();
$container
->register('security.access.decision_manager', 'Symfony\Component\Security\Core\Authorization\AccessDecisionManager')
->addArgument(array())
;
$container
->register('no_prio_service')
->addTag('security.voter')
;
$container
->register('lowest_prio_service')
->addTag('security.voter', array('priority' => 100))
;
$container
->register('highest_prio_service')
->addTag('security.voter', array('priority' => 200))
;
$container
->register('zero_prio_service')
->addTag('security.voter', array('priority' => 0))
;
$compilerPass = new AddSecurityVotersPass();
$compilerPass->process($container);
$calls = $container->getDefinition('security.access.decision_manager')->getMethodCalls();
$refs = $calls[0][1][0];
$this->assertEquals(new Reference('highest_prio_service'), $refs[0]);
$this->assertEquals(new Reference('lowest_prio_service'), $refs[1]);
$this->assertCount(4, $refs);
}
}

View File

@@ -0,0 +1,131 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\SecurityBundle\Tests\DependencyInjection\Compiler;
use PHPUnit\Framework\TestCase;
use Symfony\Bundle\FrameworkBundle\DependencyInjection\FrameworkExtension;
use Symfony\Bundle\SecurityBundle\DependencyInjection\Compiler\AddSessionDomainConstraintPass;
use Symfony\Bundle\SecurityBundle\DependencyInjection\SecurityExtension;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpFoundation\Request;
class AddSessionDomainConstraintPassTest extends TestCase
{
public function testSessionCookie()
{
$container = $this->createContainer(array('cookie_domain' => '.symfony.com.', 'cookie_secure' => true));
$utils = $container->get('security.http_utils');
$request = Request::create('/', 'get');
$this->assertTrue($utils->createRedirectResponse($request, 'https://symfony.com/blog')->isRedirect('https://symfony.com/blog'));
$this->assertTrue($utils->createRedirectResponse($request, 'https://www.symfony.com/blog')->isRedirect('https://www.symfony.com/blog'));
$this->assertTrue($utils->createRedirectResponse($request, 'https://localhost/foo')->isRedirect('https://localhost/foo'));
$this->assertTrue($utils->createRedirectResponse($request, 'https://www.localhost/foo')->isRedirect('http://localhost/'));
$this->assertTrue($utils->createRedirectResponse($request, 'http://symfony.com/blog')->isRedirect('http://localhost/'));
$this->assertTrue($utils->createRedirectResponse($request, 'http://pirate.com/foo')->isRedirect('http://localhost/'));
}
public function testSessionNoDomain()
{
$container = $this->createContainer(array('cookie_secure' => true));
$utils = $container->get('security.http_utils');
$request = Request::create('/', 'get');
$this->assertTrue($utils->createRedirectResponse($request, 'https://symfony.com/blog')->isRedirect('http://localhost/'));
$this->assertTrue($utils->createRedirectResponse($request, 'https://www.symfony.com/blog')->isRedirect('http://localhost/'));
$this->assertTrue($utils->createRedirectResponse($request, 'https://localhost/foo')->isRedirect('https://localhost/foo'));
$this->assertTrue($utils->createRedirectResponse($request, 'https://www.localhost/foo')->isRedirect('http://localhost/'));
$this->assertTrue($utils->createRedirectResponse($request, 'http://symfony.com/blog')->isRedirect('http://localhost/'));
$this->assertTrue($utils->createRedirectResponse($request, 'http://pirate.com/foo')->isRedirect('http://localhost/'));
}
public function testSessionNoSecure()
{
$container = $this->createContainer(array('cookie_domain' => '.symfony.com.'));
$utils = $container->get('security.http_utils');
$request = Request::create('/', 'get');
$this->assertTrue($utils->createRedirectResponse($request, 'https://symfony.com/blog')->isRedirect('https://symfony.com/blog'));
$this->assertTrue($utils->createRedirectResponse($request, 'https://www.symfony.com/blog')->isRedirect('https://www.symfony.com/blog'));
$this->assertTrue($utils->createRedirectResponse($request, 'https://localhost/foo')->isRedirect('https://localhost/foo'));
$this->assertTrue($utils->createRedirectResponse($request, 'https://www.localhost/foo')->isRedirect('http://localhost/'));
$this->assertTrue($utils->createRedirectResponse($request, 'http://symfony.com/blog')->isRedirect('http://symfony.com/blog'));
$this->assertTrue($utils->createRedirectResponse($request, 'http://pirate.com/foo')->isRedirect('http://localhost/'));
}
public function testSessionNoSecureAndNoDomain()
{
$container = $this->createContainer(array());
$utils = $container->get('security.http_utils');
$request = Request::create('/', 'get');
$this->assertTrue($utils->createRedirectResponse($request, 'https://symfony.com/blog')->isRedirect('http://localhost/'));
$this->assertTrue($utils->createRedirectResponse($request, 'https://www.symfony.com/blog')->isRedirect('http://localhost/'));
$this->assertTrue($utils->createRedirectResponse($request, 'https://localhost/foo')->isRedirect('https://localhost/foo'));
$this->assertTrue($utils->createRedirectResponse($request, 'http://localhost/foo')->isRedirect('http://localhost/foo'));
$this->assertTrue($utils->createRedirectResponse($request, 'https://www.localhost/foo')->isRedirect('http://localhost/'));
$this->assertTrue($utils->createRedirectResponse($request, 'http://symfony.com/blog')->isRedirect('http://localhost/'));
$this->assertTrue($utils->createRedirectResponse($request, 'http://pirate.com/foo')->isRedirect('http://localhost/'));
}
public function testNoSession()
{
$container = $this->createContainer(null);
$utils = $container->get('security.http_utils');
$request = Request::create('/', 'get');
$this->assertTrue($utils->createRedirectResponse($request, 'https://symfony.com/blog')->isRedirect('https://symfony.com/blog'));
$this->assertTrue($utils->createRedirectResponse($request, 'https://www.symfony.com/blog')->isRedirect('https://www.symfony.com/blog'));
$this->assertTrue($utils->createRedirectResponse($request, 'https://localhost/foo')->isRedirect('https://localhost/foo'));
$this->assertTrue($utils->createRedirectResponse($request, 'https://www.localhost/foo')->isRedirect('https://www.localhost/foo'));
$this->assertTrue($utils->createRedirectResponse($request, 'http://symfony.com/blog')->isRedirect('http://symfony.com/blog'));
$this->assertTrue($utils->createRedirectResponse($request, 'http://pirate.com/foo')->isRedirect('http://pirate.com/foo'));
}
private function createContainer($sessionStorageOptions)
{
$container = new ContainerBuilder();
$container->setParameter('kernel.cache_dir', __DIR__);
$container->setParameter('kernel.charset', 'UTF-8');
$container->setParameter('kernel.container_class', 'cc');
$container->setParameter('kernel.debug', true);
$container->setParameter('kernel.root_dir', __DIR__);
$container->setParameter('kernel.secret', __DIR__);
if (null !== $sessionStorageOptions) {
$container->setParameter('session.storage.options', $sessionStorageOptions);
}
$container->setParameter('request_listener.http_port', 80);
$container->setParameter('request_listener.https_port', 443);
$config = array(
'security' => array(
'providers' => array('some_provider' => array('id' => 'foo')),
'firewalls' => array('some_firewall' => array('security' => false)),
),
);
$ext = new FrameworkExtension();
$ext->load(array(), $container);
$ext = new SecurityExtension();
$ext->load($config, $container);
(new AddSessionDomainConstraintPass())->process($container);
return $container;
}
}

View File

@@ -0,0 +1,359 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\SecurityBundle\Tests\DependencyInjection;
use PHPUnit\Framework\TestCase;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Bundle\SecurityBundle\SecurityBundle;
use Symfony\Bundle\SecurityBundle\DependencyInjection\SecurityExtension;
use Symfony\Component\DependencyInjection\ContainerBuilder;
abstract class CompleteConfigurationTest extends TestCase
{
private static $containerCache = array();
abstract protected function getLoader(ContainerBuilder $container);
abstract protected function getFileExtension();
public function testRolesHierarchy()
{
$container = $this->getContainer('container1');
$this->assertEquals(array(
'ROLE_ADMIN' => array('ROLE_USER'),
'ROLE_SUPER_ADMIN' => array('ROLE_USER', 'ROLE_ADMIN', 'ROLE_ALLOWED_TO_SWITCH'),
'ROLE_REMOTE' => array('ROLE_USER', 'ROLE_ADMIN'),
), $container->getParameter('security.role_hierarchy.roles'));
}
public function testUserProviders()
{
$container = $this->getContainer('container1');
$providers = array_values(array_filter($container->getServiceIds(), function ($key) { return 0 === strpos($key, 'security.user.provider.concrete'); }));
$expectedProviders = array(
'security.user.provider.concrete.default',
'security.user.provider.concrete.default_foo',
'security.user.provider.concrete.digest',
'security.user.provider.concrete.digest_foo',
'security.user.provider.concrete.basic',
'security.user.provider.concrete.basic_foo',
'security.user.provider.concrete.basic_bar',
'security.user.provider.concrete.service',
'security.user.provider.concrete.chain',
);
$this->assertEquals(array(), array_diff($expectedProviders, $providers));
$this->assertEquals(array(), array_diff($providers, $expectedProviders));
// chain provider
$this->assertEquals(array(array(
new Reference('security.user.provider.concrete.service'),
new Reference('security.user.provider.concrete.basic'),
)), $container->getDefinition('security.user.provider.concrete.chain')->getArguments());
}
public function testFirewalls()
{
$container = $this->getContainer('container1');
$arguments = $container->getDefinition('security.firewall.map')->getArguments();
$listeners = array();
$configs = array();
foreach (array_keys($arguments[1]) as $contextId) {
$contextDef = $container->getDefinition($contextId);
$arguments = $contextDef->getArguments();
$listeners[] = array_map('strval', $arguments['index_0']);
$configDef = $container->getDefinition((string) $arguments['index_2']);
$configs[] = array_values($configDef->getArguments());
}
$this->assertEquals(array(
array(
'simple',
'security.user_checker',
'security.request_matcher.707b20193d4cb9f2718114abcbebb32af48f948484fc166a03482f49bf14f25e271f72c7',
false,
),
array(
'secure',
'security.user_checker',
null,
true,
true,
'security.user.provider.concrete.default',
null,
'security.authentication.form_entry_point.secure',
null,
null,
array(
'logout',
'switch_user',
'x509',
'remote_user',
'form_login',
'http_basic',
'http_digest',
'remember_me',
'anonymous',
),
),
array(
'host',
'security.user_checker',
'security.request_matcher.dda8b565689ad8509623ee68fb2c639cd81cd4cb339d60edbaf7d67d30e6aa09bd8c63c3',
true,
false,
'security.user.provider.concrete.default',
'host',
'security.authentication.basic_entry_point.host',
null,
null,
array(
'http_basic',
'anonymous',
),
),
array(
'with_user_checker',
'app.user_checker',
null,
true,
false,
'security.user.provider.concrete.default',
'with_user_checker',
'security.authentication.basic_entry_point.with_user_checker',
null,
null,
array(
'http_basic',
'anonymous',
),
),
), $configs);
$this->assertEquals(array(
array(),
array(
'security.channel_listener',
'security.logout_listener.secure',
'security.authentication.listener.x509.secure',
'security.authentication.listener.remote_user.secure',
'security.authentication.listener.form.secure',
'security.authentication.listener.basic.secure',
'security.authentication.listener.digest.secure',
'security.authentication.listener.rememberme.secure',
'security.authentication.listener.anonymous.secure',
'security.authentication.switchuser_listener.secure',
'security.access_listener',
),
array(
'security.channel_listener',
'security.context_listener.0',
'security.authentication.listener.basic.host',
'security.authentication.listener.anonymous.host',
'security.access_listener',
),
array(
'security.channel_listener',
'security.context_listener.1',
'security.authentication.listener.basic.with_user_checker',
'security.authentication.listener.anonymous.with_user_checker',
'security.access_listener',
),
), $listeners);
}
public function testFirewallRequestMatchers()
{
$container = $this->getContainer('container1');
$arguments = $container->getDefinition('security.firewall.map')->getArguments();
$matchers = array();
foreach ($arguments[1] as $reference) {
if ($reference instanceof Reference) {
$definition = $container->getDefinition((string) $reference);
$matchers[] = $definition->getArguments();
}
}
$this->assertEquals(array(
array(
'/login',
),
array(
'/test',
'foo\\.example\\.org',
array('GET', 'POST'),
),
), $matchers);
}
public function testAccess()
{
$container = $this->getContainer('container1');
$rules = array();
foreach ($container->getDefinition('security.access_map')->getMethodCalls() as $call) {
if ($call[0] == 'add') {
$rules[] = array((string) $call[1][0], $call[1][1], $call[1][2]);
}
}
$matcherIds = array();
foreach ($rules as list($matcherId, $attributes, $channel)) {
$requestMatcher = $container->getDefinition($matcherId);
$this->assertFalse(isset($matcherIds[$matcherId]));
$matcherIds[$matcherId] = true;
$i = count($matcherIds);
if (1 === $i) {
$this->assertEquals(array('ROLE_USER'), $attributes);
$this->assertEquals('https', $channel);
$this->assertEquals(
array('/blog/524', null, array('GET', 'POST')),
$requestMatcher->getArguments()
);
} elseif (2 === $i) {
$this->assertEquals(array('IS_AUTHENTICATED_ANONYMOUSLY'), $attributes);
$this->assertNull($channel);
$this->assertEquals(
array('/blog/.*'),
$requestMatcher->getArguments()
);
} elseif (3 === $i) {
$this->assertEquals('IS_AUTHENTICATED_ANONYMOUSLY', $attributes[0]);
$expression = $container->getDefinition((string) $attributes[1])->getArgument(0);
$this->assertEquals("token.getUsername() matches '/^admin/'", $expression);
}
}
}
public function testMerge()
{
$container = $this->getContainer('merge');
$this->assertEquals(array(
'FOO' => array('MOO'),
'ADMIN' => array('USER'),
), $container->getParameter('security.role_hierarchy.roles'));
}
public function testEncoders()
{
$container = $this->getContainer('container1');
$this->assertEquals(array(array(
'JMS\FooBundle\Entity\User1' => array(
'class' => 'Symfony\Component\Security\Core\Encoder\PlaintextPasswordEncoder',
'arguments' => array(false),
),
'JMS\FooBundle\Entity\User2' => array(
'algorithm' => 'sha1',
'encode_as_base64' => false,
'iterations' => 5,
'hash_algorithm' => 'sha512',
'key_length' => 40,
'ignore_case' => false,
'cost' => 13,
),
'JMS\FooBundle\Entity\User3' => array(
'algorithm' => 'md5',
'hash_algorithm' => 'sha512',
'key_length' => 40,
'ignore_case' => false,
'encode_as_base64' => true,
'iterations' => 5000,
'cost' => 13,
),
'JMS\FooBundle\Entity\User4' => new Reference('security.encoder.foo'),
'JMS\FooBundle\Entity\User5' => array(
'class' => 'Symfony\Component\Security\Core\Encoder\Pbkdf2PasswordEncoder',
'arguments' => array('sha1', false, 5, 30),
),
'JMS\FooBundle\Entity\User6' => array(
'class' => 'Symfony\Component\Security\Core\Encoder\BCryptPasswordEncoder',
'arguments' => array(15),
),
)), $container->getDefinition('security.encoder_factory.generic')->getArguments());
}
public function testAcl()
{
$container = $this->getContainer('container1');
$this->assertTrue($container->hasDefinition('security.acl.dbal.provider'));
$this->assertEquals('security.acl.dbal.provider', (string) $container->getAlias('security.acl.provider'));
}
public function testCustomAclProvider()
{
$container = $this->getContainer('custom_acl_provider');
$this->assertFalse($container->hasDefinition('security.acl.dbal.provider'));
$this->assertEquals('foo', (string) $container->getAlias('security.acl.provider'));
}
public function testRememberMeThrowExceptionsDefault()
{
$container = $this->getContainer('container1');
$this->assertTrue($container->getDefinition('security.authentication.listener.rememberme.secure')->getArgument(5));
}
public function testRememberMeThrowExceptions()
{
$container = $this->getContainer('remember_me_options');
$service = $container->getDefinition('security.authentication.listener.rememberme.main');
$this->assertEquals('security.authentication.rememberme.services.persistent.main', $service->getArgument(1));
$this->assertFalse($service->getArgument(5));
}
public function testUserCheckerConfig()
{
$this->assertEquals('app.user_checker', $this->getContainer('container1')->getAlias('security.user_checker.with_user_checker'));
}
public function testUserCheckerConfigWithDefaultChecker()
{
$this->assertEquals('security.user_checker', $this->getContainer('container1')->getAlias('security.user_checker.host'));
}
public function testUserCheckerConfigWithNoCheckers()
{
$this->assertEquals('security.user_checker', $this->getContainer('container1')->getAlias('security.user_checker.secure'));
}
protected function getContainer($file)
{
$file = $file.'.'.$this->getFileExtension();
if (isset(self::$containerCache[$file])) {
return self::$containerCache[$file];
}
$container = new ContainerBuilder();
$security = new SecurityExtension();
$container->registerExtension($security);
$bundle = new SecurityBundle();
$bundle->build($container); // Attach all default factories
$this->getLoader($container)->load($file);
$container->getCompilerPassConfig()->setOptimizationPasses(array());
$container->getCompilerPassConfig()->setRemovingPasses(array());
$container->compile();
return self::$containerCache[$file] = $container;
}
}

View File

@@ -0,0 +1,23 @@
<?php
namespace Symfony\Bundle\SecurityBundle\Tests\DependencyInjection\Fixtures\UserProvider;
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\UserProvider\UserProviderFactoryInterface;
use Symfony\Component\Config\Definition\Builder\NodeDefinition;
use Symfony\Component\DependencyInjection\ContainerBuilder;
class DummyProvider implements UserProviderFactoryInterface
{
public function create(ContainerBuilder $container, $id, $config)
{
}
public function getKey()
{
return 'foo';
}
public function addConfiguration(NodeDefinition $node)
{
}
}

View File

@@ -0,0 +1,102 @@
<?php
$container->loadFromExtension('security', array(
'acl' => array(),
'encoders' => array(
'JMS\FooBundle\Entity\User1' => 'plaintext',
'JMS\FooBundle\Entity\User2' => array(
'algorithm' => 'sha1',
'encode_as_base64' => false,
'iterations' => 5,
),
'JMS\FooBundle\Entity\User3' => array(
'algorithm' => 'md5',
),
'JMS\FooBundle\Entity\User4' => array(
'id' => 'security.encoder.foo',
),
'JMS\FooBundle\Entity\User5' => array(
'algorithm' => 'pbkdf2',
'hash_algorithm' => 'sha1',
'encode_as_base64' => false,
'iterations' => 5,
'key_length' => 30,
),
'JMS\FooBundle\Entity\User6' => array(
'algorithm' => 'bcrypt',
'cost' => 15,
),
),
'providers' => array(
'default' => array(
'memory' => array(
'users' => array(
'foo' => array('password' => 'foo', 'roles' => 'ROLE_USER'),
),
),
),
'digest' => array(
'memory' => array(
'users' => array(
'foo' => array('password' => 'foo', 'roles' => 'ROLE_USER, ROLE_ADMIN'),
),
),
),
'basic' => array(
'memory' => array(
'users' => array(
'foo' => array('password' => '0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33', 'roles' => 'ROLE_SUPER_ADMIN'),
'bar' => array('password' => '0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33', 'roles' => array('ROLE_USER', 'ROLE_ADMIN')),
),
),
),
'service' => array(
'id' => 'user.manager',
),
'chain' => array(
'chain' => array(
'providers' => array('service', 'basic'),
),
),
),
'firewalls' => array(
'simple' => array('pattern' => '/login', 'security' => false),
'secure' => array('stateless' => true,
'http_basic' => true,
'http_digest' => array('secret' => 'TheSecret'),
'form_login' => true,
'anonymous' => true,
'switch_user' => true,
'x509' => true,
'remote_user' => true,
'logout' => true,
'remember_me' => array('secret' => 'TheSecret'),
'user_checker' => null,
),
'host' => array(
'pattern' => '/test',
'host' => 'foo\\.example\\.org',
'methods' => array('GET', 'POST'),
'anonymous' => true,
'http_basic' => true,
),
'with_user_checker' => array(
'user_checker' => 'app.user_checker',
'anonymous' => true,
'http_basic' => true,
),
),
'access_control' => array(
array('path' => '/blog/524', 'role' => 'ROLE_USER', 'requires_channel' => 'https', 'methods' => array('get', 'POST')),
array('path' => '/blog/.*', 'role' => 'IS_AUTHENTICATED_ANONYMOUSLY'),
array('path' => '/blog/524', 'role' => 'IS_AUTHENTICATED_ANONYMOUSLY', 'allow_if' => "token.getUsername() matches '/^admin/'"),
),
'role_hierarchy' => array(
'ROLE_ADMIN' => 'ROLE_USER',
'ROLE_SUPER_ADMIN' => array('ROLE_USER', 'ROLE_ADMIN', 'ROLE_ALLOWED_TO_SWITCH'),
'ROLE_REMOTE' => 'ROLE_USER,ROLE_ADMIN',
),
));

View File

@@ -0,0 +1,9 @@
<?php
$this->load('container1.php', $container);
$container->loadFromExtension('security', array(
'acl' => array(
'provider' => 'foo',
),
));

View File

@@ -0,0 +1,20 @@
<?php
$this->load('merge_import.php', $container);
$container->loadFromExtension('security', array(
'providers' => array(
'default' => array('id' => 'foo'),
),
'firewalls' => array(
'main' => array(
'form_login' => false,
'http_basic' => null,
),
),
'role_hierarchy' => array(
'FOO' => array('MOO'),
),
));

View File

@@ -0,0 +1,15 @@
<?php
$container->loadFromExtension('security', array(
'firewalls' => array(
'main' => array(
'form_login' => array(
'login_path' => '/login',
),
),
),
'role_hierarchy' => array(
'FOO' => 'BAR',
'ADMIN' => 'USER',
),
));

View File

@@ -0,0 +1,18 @@
<?php
$container->loadFromExtension('security', array(
'providers' => array(
'default' => array('id' => 'foo'),
),
'firewalls' => array(
'main' => array(
'form_login' => true,
'remember_me' => array(
'secret' => 'TheSecret',
'catch_exceptions' => false,
'token_provider' => 'token_provider_id',
),
),
),
));

View File

@@ -0,0 +1,82 @@
<?xml version="1.0" encoding="UTF-8"?>
<srv:container xmlns="http://symfony.com/schema/dic/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:srv="http://symfony.com/schema/dic/services"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<config>
<acl />
<encoder class="JMS\FooBundle\Entity\User1" algorithm="plaintext" />
<encoder class="JMS\FooBundle\Entity\User2" algorithm="sha1" encode-as-base64="false" iterations="5" />
<encoder class="JMS\FooBundle\Entity\User3" algorithm="md5" />
<encoder class="JMS\FooBundle\Entity\User4" id="security.encoder.foo" />
<encoder class="JMS\FooBundle\Entity\User5" algorithm="pbkdf2" hash-algorithm="sha1" encode-as-base64="false" iterations="5" key-length="30" />
<encoder class="JMS\FooBundle\Entity\User6" algorithm="bcrypt" cost="15" />
<provider name="default">
<memory>
<user name="foo" password="foo" roles="ROLE_USER" />
</memory>
</provider>
<provider name="digest">
<memory>
<user name="foo" password="foo" roles="ROLE_USER, ROLE_ADMIN" />
</memory>
</provider>
<provider name="basic">
<memory>
<user name="foo" password="0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33" roles="ROLE_SUPER_ADMIN" />
<user name="bar" password="0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33" roles="ROLE_USER, ROLE_ADMIN" />
</memory>
</provider>
<provider name="service" id="user.manager" />
<provider name="chain">
<chain providers="service, basic" />
</provider>
<firewall name="simple" pattern="/login" security="false" />
<firewall name="secure" stateless="true">
<http-basic />
<http-digest secret="TheSecret" />
<form-login />
<anonymous />
<switch-user />
<x509 />
<remote-user />
<user-checker />
<logout />
<remember-me secret="TheSecret"/>
</firewall>
<firewall name="host" pattern="/test" host="foo\.example\.org" methods="GET,POST">
<anonymous />
<http-basic />
</firewall>
<firewall name="with_user_checker">
<anonymous />
<http-basic />
<user-checker>app.user_checker</user-checker>
</firewall>
<role id="ROLE_ADMIN">ROLE_USER</role>
<role id="ROLE_SUPER_ADMIN">ROLE_USER,ROLE_ADMIN,ROLE_ALLOWED_TO_SWITCH</role>
<role id="ROLE_REMOTE">ROLE_USER,ROLE_ADMIN</role>
<rule path="/blog/524" role="ROLE_USER" requires-channel="https" methods="get,POST" />
<rule role='IS_AUTHENTICATED_ANONYMOUSLY' path="/blog/.*" />
<rule role='IS_AUTHENTICATED_ANONYMOUSLY' allow-if="token.getUsername() matches '/^admin/'" path="/blog/524" />
</config>
</srv:container>

View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:sec="http://symfony.com/schema/dic/security"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<imports>
<import resource="container1.xml"/>
</imports>
<sec:config>
<sec:acl provider="foo" />
</sec:config>
</container>

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:sec="http://symfony.com/schema/dic/security"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<imports>
<import resource="merge_import.xml"/>
</imports>
<sec:config>
<sec:provider name="default" id="foo" />
<sec:firewall name="main" form-login="false">
<sec:http-basic />
</sec:firewall>
<sec:role id="FOO" value="MOO" />
</sec:config>
</container>

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<srv:container xmlns="http://symfony.com/schema/dic/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:srv="http://symfony.com/schema/dic/services"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<config>
<firewall name="main">
<form-login login-path="/login" />
</firewall>
<role id="FOO" value="BAR" />
<role id="ADMIN" value="USER" />
</config>
</srv:container>

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:sec="http://symfony.com/schema/dic/security"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<sec:config>
<sec:providers>
<sec:default id="foo"/>
</sec:providers>
<sec:firewall name="main">
<sec:form-login/>
<sec:remember-me secret="TheSecret" catch-exceptions="false" token-provider="token_provider_id" />
</sec:firewall>
</sec:config>
</container>

View File

@@ -0,0 +1,83 @@
security:
acl: ~
encoders:
JMS\FooBundle\Entity\User1: plaintext
JMS\FooBundle\Entity\User2:
algorithm: sha1
encode_as_base64: false
iterations: 5
JMS\FooBundle\Entity\User3:
algorithm: md5
JMS\FooBundle\Entity\User4:
id: security.encoder.foo
JMS\FooBundle\Entity\User5:
algorithm: pbkdf2
hash_algorithm: sha1
encode_as_base64: false
iterations: 5
key_length: 30
JMS\FooBundle\Entity\User6:
algorithm: bcrypt
cost: 15
providers:
default:
memory:
users:
foo: { password: foo, roles: ROLE_USER }
digest:
memory:
users:
foo: { password: foo, roles: 'ROLE_USER, ROLE_ADMIN' }
basic:
memory:
users:
foo: { password: 0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33, roles: ROLE_SUPER_ADMIN }
bar: { password: 0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33, roles: [ROLE_USER, ROLE_ADMIN] }
service:
id: user.manager
chain:
chain:
providers: [service, basic]
firewalls:
simple: { pattern: /login, security: false }
secure:
stateless: true
http_basic: true
http_digest:
secret: TheSecret
form_login: true
anonymous: true
switch_user: true
x509: true
remote_user: true
logout: true
remember_me:
secret: TheSecret
user_checker: ~
host:
pattern: /test
host: foo\.example\.org
methods: [GET,POST]
anonymous: true
http_basic: true
with_user_checker:
anonymous: ~
http_basic: ~
user_checker: app.user_checker
role_hierarchy:
ROLE_ADMIN: ROLE_USER
ROLE_SUPER_ADMIN: [ROLE_USER, ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]
ROLE_REMOTE: ROLE_USER,ROLE_ADMIN
access_control:
- { path: /blog/524, role: ROLE_USER, requires_channel: https, methods: [get, POST]}
-
path: /blog/.*
role: IS_AUTHENTICATED_ANONYMOUSLY
- { path: /blog/524, role: IS_AUTHENTICATED_ANONYMOUSLY, allow_if: "token.getUsername() matches '/^admin/'" }

View File

@@ -0,0 +1,6 @@
imports:
- { resource: container1.yml }
security:
acl:
provider: foo

View File

@@ -0,0 +1,14 @@
imports:
- { resource: merge_import.yml }
security:
providers:
default: { id: foo }
firewalls:
main:
form_login: false
http_basic: ~
role_hierarchy:
FOO: [MOO]

View File

@@ -0,0 +1,9 @@
security:
firewalls:
main:
form_login:
login_path: /login
role_hierarchy:
FOO: BAR
ADMIN: USER

View File

@@ -0,0 +1,12 @@
security:
providers:
default:
id: foo
firewalls:
main:
form_login: true
remember_me:
secret: TheSecret
catch_exceptions: false
token_provider: token_provider_id

View File

@@ -0,0 +1,121 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\SecurityBundle\Tests\DependencyInjection;
use PHPUnit\Framework\TestCase;
use Symfony\Bundle\SecurityBundle\DependencyInjection\MainConfiguration;
use Symfony\Component\Config\Definition\Processor;
class MainConfigurationTest extends TestCase
{
/**
* The minimal, required config needed to not have any required validation
* issues.
*
* @var array
*/
protected static $minimalConfig = array(
'providers' => array(
'stub' => array(
'id' => 'foo',
),
),
'firewalls' => array(
'stub' => array(),
),
);
/**
* @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException
*/
public function testNoConfigForProvider()
{
$config = array(
'providers' => array(
'stub' => array(),
),
);
$processor = new Processor();
$configuration = new MainConfiguration(array(), array());
$processor->processConfiguration($configuration, array($config));
}
/**
* @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException
*/
public function testManyConfigForProvider()
{
$config = array(
'providers' => array(
'stub' => array(
'id' => 'foo',
'chain' => array(),
),
),
);
$processor = new Processor();
$configuration = new MainConfiguration(array(), array());
$processor->processConfiguration($configuration, array($config));
}
public function testCsrfAliases()
{
$config = array(
'firewalls' => array(
'stub' => array(
'logout' => array(
'csrf_token_generator' => 'a_token_generator',
'csrf_token_id' => 'a_token_id',
),
),
),
);
$config = array_merge(static::$minimalConfig, $config);
$processor = new Processor();
$configuration = new MainConfiguration(array(), array());
$processedConfig = $processor->processConfiguration($configuration, array($config));
$this->assertTrue(isset($processedConfig['firewalls']['stub']['logout']['csrf_token_generator']));
$this->assertEquals('a_token_generator', $processedConfig['firewalls']['stub']['logout']['csrf_token_generator']);
$this->assertTrue(isset($processedConfig['firewalls']['stub']['logout']['csrf_token_id']));
$this->assertEquals('a_token_id', $processedConfig['firewalls']['stub']['logout']['csrf_token_id']);
}
public function testDefaultUserCheckers()
{
$processor = new Processor();
$configuration = new MainConfiguration(array(), array());
$processedConfig = $processor->processConfiguration($configuration, array(static::$minimalConfig));
$this->assertEquals('security.user_checker', $processedConfig['firewalls']['stub']['user_checker']);
}
public function testUserCheckers()
{
$config = array(
'firewalls' => array(
'stub' => array(
'user_checker' => 'app.henk_checker',
),
),
);
$config = array_merge(static::$minimalConfig, $config);
$processor = new Processor();
$configuration = new MainConfiguration(array(), array());
$processedConfig = $processor->processConfiguration($configuration, array($config));
$this->assertEquals('app.henk_checker', $processedConfig['firewalls']['stub']['user_checker']);
}
}

View File

@@ -0,0 +1,29 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\SecurityBundle\Tests\DependencyInjection;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Loader\PhpFileLoader;
use Symfony\Component\Config\FileLocator;
class PhpCompleteConfigurationTest extends CompleteConfigurationTest
{
protected function getLoader(ContainerBuilder $container)
{
return new PhpFileLoader($container, new FileLocator(__DIR__.'/Fixtures/php'));
}
protected function getFileExtension()
{
return 'php';
}
}

View File

@@ -0,0 +1,157 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\SecurityBundle\Tests\DependencyInjection\Security\Factory;
use PHPUnit\Framework\TestCase;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\ContainerBuilder;
class AbstractFactoryTest extends TestCase
{
public function testCreate()
{
list($container, $authProviderId, $listenerId, $entryPointId) = $this->callFactory('foo', array(
'use_forward' => true,
'failure_path' => '/foo',
'success_handler' => 'custom_success_handler',
'failure_handler' => 'custom_failure_handler',
'remember_me' => true,
), 'user_provider', 'entry_point');
// auth provider
$this->assertEquals('auth_provider', $authProviderId);
// listener
$this->assertEquals('abstract_listener.foo', $listenerId);
$this->assertTrue($container->hasDefinition('abstract_listener.foo'));
$definition = $container->getDefinition('abstract_listener.foo');
$this->assertEquals(array(
'index_4' => 'foo',
'index_5' => new Reference('security.authentication.success_handler.foo.abstract_factory'),
'index_6' => new Reference('security.authentication.failure_handler.foo.abstract_factory'),
'index_7' => array(
'use_forward' => true,
),
), $definition->getArguments());
// entry point
$this->assertEquals('entry_point', $entryPointId, '->create() does not change the default entry point.');
}
/**
* @dataProvider getFailureHandlers
*/
public function testDefaultFailureHandler($serviceId, $defaultHandlerInjection)
{
$options = array(
'remember_me' => true,
'login_path' => '/bar',
);
if ($serviceId) {
$options['failure_handler'] = $serviceId;
}
list($container, $authProviderId, $listenerId, $entryPointId) = $this->callFactory('foo', $options, 'user_provider', 'entry_point');
$definition = $container->getDefinition('abstract_listener.foo');
$arguments = $definition->getArguments();
$this->assertEquals(new Reference('security.authentication.failure_handler.foo.abstract_factory'), $arguments['index_6']);
$failureHandler = $container->findDefinition((string) $arguments['index_6']);
$methodCalls = $failureHandler->getMethodCalls();
if ($defaultHandlerInjection) {
$this->assertEquals('setOptions', $methodCalls[0][0]);
$this->assertEquals(array('login_path' => '/bar'), $methodCalls[0][1][0]);
} else {
$this->assertCount(0, $methodCalls);
}
}
public function getFailureHandlers()
{
return array(
array(null, true),
array('custom_failure_handler', false),
);
}
/**
* @dataProvider getSuccessHandlers
*/
public function testDefaultSuccessHandler($serviceId, $defaultHandlerInjection)
{
$options = array(
'remember_me' => true,
'default_target_path' => '/bar',
);
if ($serviceId) {
$options['success_handler'] = $serviceId;
}
list($container, $authProviderId, $listenerId, $entryPointId) = $this->callFactory('foo', $options, 'user_provider', 'entry_point');
$definition = $container->getDefinition('abstract_listener.foo');
$arguments = $definition->getArguments();
$this->assertEquals(new Reference('security.authentication.success_handler.foo.abstract_factory'), $arguments['index_5']);
$successHandler = $container->findDefinition((string) $arguments['index_5']);
$methodCalls = $successHandler->getMethodCalls();
if ($defaultHandlerInjection) {
$this->assertEquals('setOptions', $methodCalls[0][0]);
$this->assertEquals(array('default_target_path' => '/bar'), $methodCalls[0][1][0]);
$this->assertEquals('setProviderKey', $methodCalls[1][0]);
$this->assertEquals(array('foo'), $methodCalls[1][1]);
} else {
$this->assertCount(0, $methodCalls);
}
}
public function getSuccessHandlers()
{
return array(
array(null, true),
array('custom_success_handler', false),
);
}
protected function callFactory($id, $config, $userProviderId, $defaultEntryPointId)
{
$factory = $this->getMockForAbstractClass('Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\AbstractFactory', array());
$factory
->expects($this->once())
->method('createAuthProvider')
->will($this->returnValue('auth_provider'))
;
$factory
->expects($this->atLeastOnce())
->method('getListenerId')
->will($this->returnValue('abstract_listener'))
;
$factory
->expects($this->any())
->method('getKey')
->will($this->returnValue('abstract_factory'))
;
$container = new ContainerBuilder();
$container->register('auth_provider');
$container->register('custom_success_handler');
$container->register('custom_failure_handler');
list($authProviderId, $listenerId, $entryPointId) = $factory->create($container, $id, $config, $userProviderId, $defaultEntryPointId);
return array($container, $authProviderId, $listenerId, $entryPointId);
}
}

View File

@@ -0,0 +1,182 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\SecurityBundle\Tests\DependencyInjection\Security\Factory;
use PHPUnit\Framework\TestCase;
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\GuardAuthenticationFactory;
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;
class GuardAuthenticationFactoryTest extends TestCase
{
/**
* @dataProvider getValidConfigurationTests
*/
public function testAddValidConfiguration(array $inputConfig, array $expectedConfig)
{
$factory = new GuardAuthenticationFactory();
$nodeDefinition = new ArrayNodeDefinition('guard');
$factory->addConfiguration($nodeDefinition);
$node = $nodeDefinition->getNode();
$normalizedConfig = $node->normalize($inputConfig);
$finalizedConfig = $node->finalize($normalizedConfig);
$this->assertEquals($expectedConfig, $finalizedConfig);
}
/**
* @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException
* @dataProvider getInvalidConfigurationTests
*/
public function testAddInvalidConfiguration(array $inputConfig)
{
$factory = new GuardAuthenticationFactory();
$nodeDefinition = new ArrayNodeDefinition('guard');
$factory->addConfiguration($nodeDefinition);
$node = $nodeDefinition->getNode();
$normalizedConfig = $node->normalize($inputConfig);
// will validate and throw an exception on invalid
$node->finalize($normalizedConfig);
}
public function getValidConfigurationTests()
{
$tests = array();
// completely basic
$tests[] = array(
array(
'authenticators' => array('authenticator1', 'authenticator2'),
'provider' => 'some_provider',
'entry_point' => 'the_entry_point',
),
array(
'authenticators' => array('authenticator1', 'authenticator2'),
'provider' => 'some_provider',
'entry_point' => 'the_entry_point',
),
);
// testing xml config fix: authenticator -> authenticators
$tests[] = array(
array(
'authenticator' => array('authenticator1', 'authenticator2'),
),
array(
'authenticators' => array('authenticator1', 'authenticator2'),
'entry_point' => null,
),
);
return $tests;
}
public function getInvalidConfigurationTests()
{
$tests = array();
// testing not empty
$tests[] = array(
array('authenticators' => array()),
);
return $tests;
}
public function testBasicCreate()
{
// simple configuration
$config = array(
'authenticators' => array('authenticator123'),
'entry_point' => null,
);
list($container, $entryPointId) = $this->executeCreate($config, null);
$this->assertEquals('authenticator123', $entryPointId);
$providerDefinition = $container->getDefinition('security.authentication.provider.guard.my_firewall');
$this->assertEquals(array(
'index_0' => array(new Reference('authenticator123')),
'index_1' => new Reference('my_user_provider'),
'index_2' => 'my_firewall',
'index_3' => new Reference('security.user_checker.my_firewall'),
), $providerDefinition->getArguments());
$listenerDefinition = $container->getDefinition('security.authentication.listener.guard.my_firewall');
$this->assertEquals('my_firewall', $listenerDefinition->getArgument(2));
$this->assertEquals(array(new Reference('authenticator123')), $listenerDefinition->getArgument(3));
}
public function testExistingDefaultEntryPointUsed()
{
// any existing default entry point is used
$config = array(
'authenticators' => array('authenticator123'),
'entry_point' => null,
);
list(, $entryPointId) = $this->executeCreate($config, 'some_default_entry_point');
$this->assertEquals('some_default_entry_point', $entryPointId);
}
/**
* @expectedException \LogicException
*/
public function testCannotOverrideDefaultEntryPoint()
{
// any existing default entry point is used
$config = array(
'authenticators' => array('authenticator123'),
'entry_point' => 'authenticator123',
);
$this->executeCreate($config, 'some_default_entry_point');
}
/**
* @expectedException \LogicException
*/
public function testMultipleAuthenticatorsRequiresEntryPoint()
{
// any existing default entry point is used
$config = array(
'authenticators' => array('authenticator123', 'authenticatorABC'),
'entry_point' => null,
);
$this->executeCreate($config, null);
}
public function testCreateWithEntryPoint()
{
// any existing default entry point is used
$config = array(
'authenticators' => array('authenticator123', 'authenticatorABC'),
'entry_point' => 'authenticatorABC',
);
list($container, $entryPointId) = $this->executeCreate($config, null);
$this->assertEquals('authenticatorABC', $entryPointId);
}
private function executeCreate(array $config, $defaultEntryPointId)
{
$container = new ContainerBuilder();
$container->register('security.authentication.provider.guard');
$container->register('security.authentication.listener.guard');
$id = 'my_firewall';
$userProviderId = 'my_user_provider';
$factory = new GuardAuthenticationFactory();
list($providerId, $listenerId, $entryPointId) = $factory->create($container, $id, $config, $userProviderId, $defaultEntryPointId);
return array($container, $entryPointId);
}
}

View File

@@ -0,0 +1,144 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\SecurityBundle\Tests\DependencyInjection;
use PHPUnit\Framework\TestCase;
use Symfony\Bundle\SecurityBundle\DependencyInjection\SecurityExtension;
use Symfony\Bundle\SecurityBundle\SecurityBundle;
use Symfony\Bundle\SecurityBundle\Tests\DependencyInjection\Fixtures\UserProvider\DummyProvider;
use Symfony\Component\DependencyInjection\ContainerBuilder;
class SecurityExtensionTest extends TestCase
{
/**
* @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException
* @expectedExceptionMessage The check_path "/some_area/login_check" for login method "form_login" is not matched by the firewall pattern "/secured_area/.*".
*/
public function testInvalidCheckPath()
{
$container = $this->getRawContainer();
$container->loadFromExtension('security', array(
'providers' => array(
'default' => array('id' => 'foo'),
),
'firewalls' => array(
'some_firewall' => array(
'pattern' => '/secured_area/.*',
'form_login' => array(
'check_path' => '/some_area/login_check',
),
),
),
));
$container->compile();
}
/**
* @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException
* @expectedExceptionMessage No authentication listener registered for firewall "some_firewall"
*/
public function testFirewallWithoutAuthenticationListener()
{
$container = $this->getRawContainer();
$container->loadFromExtension('security', array(
'providers' => array(
'default' => array('id' => 'foo'),
),
'firewalls' => array(
'some_firewall' => array(
'pattern' => '/.*',
),
),
));
$container->compile();
}
/**
* @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException
* @expectedExceptionMessage Unable to create definition for "security.user.provider.concrete.my_foo" user provider
*/
public function testFirewallWithInvalidUserProvider()
{
$container = $this->getRawContainer();
$extension = $container->getExtension('security');
$extension->addUserProviderFactory(new DummyProvider());
$container->loadFromExtension('security', array(
'providers' => array(
'my_foo' => array('foo' => array()),
),
'firewalls' => array(
'some_firewall' => array(
'pattern' => '/.*',
'http_basic' => array(),
),
),
));
$container->compile();
}
public function testDisableRoleHierarchyVoter()
{
$container = $this->getRawContainer();
$container->loadFromExtension('security', array(
'providers' => array(
'default' => array('id' => 'foo'),
),
'role_hierarchy' => null,
'firewalls' => array(
'some_firewall' => array(
'pattern' => '/.*',
'http_basic' => null,
),
),
));
$container->compile();
$this->assertFalse($container->hasDefinition('security.access.role_hierarchy_voter'));
}
protected function getRawContainer()
{
$container = new ContainerBuilder();
$security = new SecurityExtension();
$container->registerExtension($security);
$bundle = new SecurityBundle();
$bundle->build($container);
$container->getCompilerPassConfig()->setOptimizationPasses(array());
$container->getCompilerPassConfig()->setRemovingPasses(array());
return $container;
}
protected function getContainer()
{
$container = $this->getRawContainer();
$container->compile();
return $container;
}
}

View File

@@ -0,0 +1,29 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\SecurityBundle\Tests\DependencyInjection;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
use Symfony\Component\Config\FileLocator;
class XmlCompleteConfigurationTest extends CompleteConfigurationTest
{
protected function getLoader(ContainerBuilder $container)
{
return new XmlFileLoader($container, new FileLocator(__DIR__.'/Fixtures/xml'));
}
protected function getFileExtension()
{
return 'xml';
}
}

View File

@@ -0,0 +1,29 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\SecurityBundle\Tests\DependencyInjection;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
use Symfony\Component\Config\FileLocator;
class YamlCompleteConfigurationTest extends CompleteConfigurationTest
{
protected function getLoader(ContainerBuilder $container)
{
return new YamlFileLoader($container, new FileLocator(__DIR__.'/Fixtures/yml'));
}
protected function getFileExtension()
{
return 'yml';
}
}

View File

@@ -0,0 +1,23 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\SecurityBundle\Tests\Functional;
class AuthenticationCommencingTest extends WebTestCase
{
public function testAuthenticationIsCommencingIfAccessDeniedExceptionIsWrapped()
{
$client = $this->createClient(array('test_case' => 'StandardFormLogin', 'root_config' => 'config.yml'));
$client->request('GET', '/secure-but-not-covered-by-access-control');
$this->assertRedirect($client->getResponse(), '/login');
}
}

View File

@@ -0,0 +1,21 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\AclBundle;
use Symfony\Component\HttpKernel\Bundle\Bundle;
/**
* @author Kévin Dunglas <kevin@les-tilleuls.coop>
*/
class AclBundle extends Bundle
{
}

View File

@@ -0,0 +1,22 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\AclBundle\Entity;
/**
* Car.
*
* @author Kévin Dunglas <kevin@les-tilleuls.coop>
*/
class Car
{
public $id;
}

View File

@@ -0,0 +1,46 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\CsrfFormLoginBundle\Controller;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerAwareTrait;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
class LoginController implements ContainerAwareInterface
{
use ContainerAwareTrait;
public function loginAction()
{
$form = $this->container->get('form.factory')->create('Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\CsrfFormLoginBundle\Form\UserLoginType');
return $this->container->get('templating')->renderResponse('CsrfFormLoginBundle:Login:login.html.twig', array(
'form' => $form->createView(),
));
}
public function afterLoginAction()
{
return $this->container->get('templating')->renderResponse('CsrfFormLoginBundle:Login:after_login.html.twig');
}
public function loginCheckAction()
{
return new Response('', 400);
}
public function secureAction()
{
throw new \Exception('Wrapper', 0, new \Exception('Another Wrapper', 0, new AccessDeniedException()));
}
}

View File

@@ -0,0 +1,18 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\CsrfFormLoginBundle;
use Symfony\Component\HttpKernel\Bundle\Bundle;
class CsrfFormLoginBundle extends Bundle
{
}

View File

@@ -0,0 +1,87 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\CsrfFormLoginBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormError;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Security\Core\Security;
/**
* Form type for use with the Security component's form-based authentication
* listener.
*
* @author Henrik Bjornskov <henrik@bjrnskov.dk>
* @author Jeremy Mikola <jmikola@gmail.com>
*/
class UserLoginType extends AbstractType
{
private $requestStack;
public function __construct(RequestStack $requestStack)
{
$this->requestStack = $requestStack;
}
/**
* {@inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('username', 'Symfony\Component\Form\Extension\Core\Type\TextType')
->add('password', 'Symfony\Component\Form\Extension\Core\Type\PasswordType')
->add('_target_path', 'Symfony\Component\Form\Extension\Core\Type\HiddenType')
;
$request = $this->requestStack->getCurrentRequest();
/* Note: since the Security component's form login listener intercepts
* the POST request, this form will never really be bound to the
* request; however, we can match the expected behavior by checking the
* session for an authentication error and last username.
*/
$builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) use ($request) {
if ($request->attributes->has(Security::AUTHENTICATION_ERROR)) {
$error = $request->attributes->get(Security::AUTHENTICATION_ERROR);
} else {
$error = $request->getSession()->get(Security::AUTHENTICATION_ERROR);
}
if ($error) {
$event->getForm()->addError(new FormError($error->getMessage()));
}
$event->setData(array_replace((array) $event->getData(), array(
'username' => $request->getSession()->get(Security::LAST_USERNAME),
)));
});
}
/**
* {@inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
/* Note: the form's csrf_token_id must correspond to that for the form login
* listener in order for the CSRF token to validate successfully.
*/
$resolver->setDefaults(array(
'csrf_token_id' => 'authenticate',
));
}
}

View File

@@ -0,0 +1,30 @@
form_login:
path: /login
defaults: { _controller: CsrfFormLoginBundle:Login:login }
form_login_check:
path: /login_check
defaults: { _controller: CsrfFormLoginBundle:Login:loginCheck }
form_login_homepage:
path: /
defaults: { _controller: CsrfFormLoginBundle:Login:afterLogin }
form_login_custom_target_path:
path: /foo
defaults: { _controller: CsrfFormLoginBundle:Login:afterLogin }
form_login_default_target_path:
path: /profile
defaults: { _controller: CsrfFormLoginBundle:Login:afterLogin }
form_login_redirect_to_protected_resource_after_login:
path: /protected-resource
defaults: { _controller: CsrfFormLoginBundle:Login:afterLogin }
form_logout:
path: /logout_path
form_secure_action:
path: /secure-but-not-covered-by-access-control
defaults: { _controller: CsrfFormLoginBundle:Login:secure }

View File

@@ -0,0 +1,8 @@
{% extends "::base.html.twig" %}
{% block body %}
Hello {{ app.user.username }}!<br /><br />
You're browsing to path "{{ app.request.pathInfo }}".<br /><br />
<a href="{{ logout_path('default') }}">Log out</a>.
<a href="{{ logout_url('default') }}">Log out</a>.
{% endblock %}

View File

@@ -0,0 +1,12 @@
{% extends "::base.html.twig" %}
{% block body %}
<form action="{{ path('form_login_check') }}" method="post">
{{ form_widget(form) }}
{# Note: ensure the submit name does not conflict with the form's name or it may clobber field data #}
<input type="submit" name="login" />
</form>
{% endblock %}

View File

@@ -0,0 +1,26 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\FirewallEntryPointBundle\DependencyInjection;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Extension\Extension;
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
class FirewallEntryPointExtension extends Extension
{
public function load(array $configs, ContainerBuilder $container)
{
$loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
$loader->load('services.xml');
}
}

View File

@@ -0,0 +1,18 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\FirewallEntryPointBundle;
use Symfony\Component\HttpKernel\Bundle\Bundle;
class FirewallEntryPointBundle extends Bundle
{
}

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<services>
<service id="firewall_entry_point.entry_point.stub"
class="Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\FirewallEntryPointBundle\Security\EntryPointStub"
/>
</services>
</container>

View File

@@ -0,0 +1,27 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\FirewallEntryPointBundle\Security;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface;
class EntryPointStub implements AuthenticationEntryPointInterface
{
const RESPONSE_TEXT = '2be8e651259189d841a19eecdf37e771e2431741';
public function start(Request $request, AuthenticationException $authException = null)
{
return new Response(self::RESPONSE_TEXT);
}
}

View File

@@ -0,0 +1,64 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\FormLoginBundle\Controller;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerAwareTrait;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
class LocalizedController implements ContainerAwareInterface
{
use ContainerAwareTrait;
public function loginAction(Request $request)
{
// get the login error if there is one
if ($request->attributes->has(Security::AUTHENTICATION_ERROR)) {
$error = $request->attributes->get(Security::AUTHENTICATION_ERROR);
} else {
$error = $request->getSession()->get(Security::AUTHENTICATION_ERROR);
}
return $this->container->get('templating')->renderResponse('FormLoginBundle:Localized:login.html.twig', array(
// last username entered by the user
'last_username' => $request->getSession()->get(Security::LAST_USERNAME),
'error' => $error,
));
}
public function loginCheckAction()
{
throw new \RuntimeException('loginCheckAction() should never be called.');
}
public function logoutAction()
{
throw new \RuntimeException('logoutAction() should never be called.');
}
public function secureAction()
{
throw new \RuntimeException('secureAction() should never be called.');
}
public function profileAction()
{
return new Response('Profile');
}
public function homepageAction()
{
return new Response('Homepage');
}
}

View File

@@ -0,0 +1,56 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\FormLoginBundle\Controller;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerAwareTrait;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Security\Core\User\UserInterface;
class LoginController implements ContainerAwareInterface
{
use ContainerAwareTrait;
public function loginAction(Request $request, UserInterface $user = null)
{
// get the login error if there is one
if ($request->attributes->has(Security::AUTHENTICATION_ERROR)) {
$error = $request->attributes->get(Security::AUTHENTICATION_ERROR);
} else {
$error = $request->getSession()->get(Security::AUTHENTICATION_ERROR);
}
return $this->container->get('templating')->renderResponse('FormLoginBundle:Login:login.html.twig', array(
// last username entered by the user
'last_username' => $request->getSession()->get(Security::LAST_USERNAME),
'error' => $error,
));
}
public function afterLoginAction(UserInterface $user)
{
return $this->container->get('templating')->renderResponse('FormLoginBundle:Login:after_login.html.twig', array('user' => $user));
}
public function loginCheckAction()
{
return new Response('', 400);
}
public function secureAction()
{
throw new \Exception('Wrapper', 0, new \Exception('Another Wrapper', 0, new AccessDeniedException()));
}
}

View File

@@ -0,0 +1,27 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\FormLoginBundle\DependencyInjection;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Extension\Extension;
class FormLoginExtension extends Extension
{
public function load(array $configs, ContainerBuilder $container)
{
$container
->register('localized_form_failure_handler', 'Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\FormLoginBundle\Security\LocalizedFormFailureHandler')
->addArgument(new Reference('router'))
;
}
}

View File

@@ -0,0 +1,18 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\FormLoginBundle;
use Symfony\Component\HttpKernel\Bundle\Bundle;
class FormLoginBundle extends Bundle
{
}

View File

@@ -0,0 +1,29 @@
localized_login_path:
path: /{_locale}/login
defaults: { _controller: FormLoginBundle:Localized:login }
requirements: { _locale: "^[a-z]{2}$" }
localized_check_path:
path: /{_locale}/login_check
defaults: { _controller: FormLoginBundle:Localized:loginCheck }
requirements: { _locale: "^[a-z]{2}$" }
localized_default_target_path:
path: /{_locale}/profile
defaults: { _controller: FormLoginBundle:Localized:profile }
requirements: { _locale: "^[a-z]{2}$" }
localized_logout_path:
path: /{_locale}/logout
defaults: { _controller: FormLoginBundle:Localized:logout }
requirements: { _locale: "^[a-z]{2}$" }
localized_logout_target_path:
path: /{_locale}/
defaults: { _controller: FormLoginBundle:Localized:homepage }
requirements: { _locale: "^[a-z]{2}$" }
localized_secure_path:
path: /{_locale}/secure/
defaults: { _controller: FormLoginBundle:Localized:secure }
requirements: { _locale: "^[a-z]{2}$" }

View File

@@ -0,0 +1,42 @@
form_login:
path: /login
defaults: { _controller: FormLoginBundle:Login:login }
form_login_check:
path: /login_check
defaults: { _controller: FormLoginBundle:Login:loginCheck }
form_login_homepage:
path: /
defaults: { _controller: FormLoginBundle:Login:afterLogin }
form_login_custom_target_path:
path: /foo
defaults: { _controller: FormLoginBundle:Login:afterLogin }
form_login_default_target_path:
path: /profile
defaults: { _controller: FormLoginBundle:Login:afterLogin }
form_login_redirect_to_protected_resource_after_login:
path: /protected_resource
defaults: { _controller: FormLoginBundle:Login:afterLogin }
highly_protected_resource:
path: /highly_protected_resource
secured-by-one-ip:
path: /secured-by-one-ip
secured-by-two-ips:
path: /secured-by-two-ips
form_logout:
path: /logout_path
form_secure_action:
path: /secure-but-not-covered-by-access-control
defaults: { _controller: FormLoginBundle:Login:secure }
protected-via-expression:
path: /protected-via-expression

View File

@@ -0,0 +1,21 @@
{% extends "::base.html.twig" %}
{% block body %}
{% if error %}
<div>{{ error.message }}</div>
{% endif %}
<form action="{{ path('localized_check_path') }}" method="post">
<label for="username">Username:</label>
<input type="text" id="username" name="_username" value="{{ last_username }}" />
<label for="password">Password:</label>
<input type="password" id="password" name="_password" />
<input type="hidden" name="_target_path" value="" />
<input type="submit" name="login" />
</form>
{% endblock %}

View File

@@ -0,0 +1,16 @@
{% extends "::base.html.twig" %}
{% block body %}
Hello {{ user.username }}!<br /><br />
You're browsing to path "{{ app.request.pathInfo }}".
<a href="{{ logout_path('default') }}">Log out</a>.
<a href="{{ logout_url('default') }}">Log out</a>.
<a href="{{ logout_path('second_area') }}">Log out</a>.
<a href="{{ logout_url('second_area') }}">Log out</a>.
<a href="{{ logout_path() }}">Log out</a>.
<a href="{{ logout_url() }}">Log out</a>.
{% endblock %}

View File

@@ -0,0 +1,21 @@
{% extends "::base.html.twig" %}
{% block body %}
{% if error %}
<div>{{ error.message }}</div>
{% endif %}
<form action="{{ path('form_login_check') }}" method="post">
<label for="username">Username:</label>
<input type="text" id="username" name="_username" value="{{ last_username }}" />
<label for="password">Password:</label>
<input type="password" id="password" name="_password" />
<input type="hidden" name="_target_path" value="" />
<input type="submit" name="login" />
</form>
{% endblock %}

View File

@@ -0,0 +1,34 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\FormLoginBundle\Security;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Http\Authentication\AuthenticationFailureHandlerInterface;
class LocalizedFormFailureHandler implements AuthenticationFailureHandlerInterface
{
private $router;
public function __construct(RouterInterface $router)
{
$this->router = $router;
}
public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
{
return new RedirectResponse($this->router->generate('localized_login_path', array(), UrlGeneratorInterface::ABSOLUTE_URL));
}
}

View File

@@ -0,0 +1,111 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\SecurityBundle\Tests\Functional;
class CsrfFormLoginTest extends WebTestCase
{
/**
* @dataProvider getConfigs
*/
public function testFormLoginAndLogoutWithCsrfTokens($config)
{
$client = $this->createClient(array('test_case' => 'CsrfFormLogin', 'root_config' => $config));
$form = $client->request('GET', '/login')->selectButton('login')->form();
$form['user_login[username]'] = 'johannes';
$form['user_login[password]'] = 'test';
$client->submit($form);
$this->assertRedirect($client->getResponse(), '/profile');
$crawler = $client->followRedirect();
$text = $crawler->text();
$this->assertContains('Hello johannes!', $text);
$this->assertContains('You\'re browsing to path "/profile".', $text);
$logoutLinks = $crawler->selectLink('Log out')->links();
$this->assertCount(2, $logoutLinks);
$this->assertContains('_csrf_token=', $logoutLinks[0]->getUri());
$this->assertSame($logoutLinks[0]->getUri(), $logoutLinks[1]->getUri());
$client->click($logoutLinks[0]);
$this->assertRedirect($client->getResponse(), '/');
}
/**
* @dataProvider getConfigs
*/
public function testFormLoginWithInvalidCsrfToken($config)
{
$client = $this->createClient(array('test_case' => 'CsrfFormLogin', 'root_config' => $config));
$form = $client->request('GET', '/login')->selectButton('login')->form();
$form['user_login[_token]'] = '';
$client->submit($form);
$this->assertRedirect($client->getResponse(), '/login');
$text = $client->followRedirect()->text();
$this->assertContains('Invalid CSRF token.', $text);
}
/**
* @dataProvider getConfigs
*/
public function testFormLoginWithCustomTargetPath($config)
{
$client = $this->createClient(array('test_case' => 'CsrfFormLogin', 'root_config' => $config));
$form = $client->request('GET', '/login')->selectButton('login')->form();
$form['user_login[username]'] = 'johannes';
$form['user_login[password]'] = 'test';
$form['user_login[_target_path]'] = '/foo';
$client->submit($form);
$this->assertRedirect($client->getResponse(), '/foo');
$text = $client->followRedirect()->text();
$this->assertContains('Hello johannes!', $text);
$this->assertContains('You\'re browsing to path "/foo".', $text);
}
/**
* @dataProvider getConfigs
*/
public function testFormLoginRedirectsToProtectedResourceAfterLogin($config)
{
$client = $this->createClient(array('test_case' => 'CsrfFormLogin', 'root_config' => $config));
$client->request('GET', '/protected-resource');
$this->assertRedirect($client->getResponse(), '/login');
$form = $client->followRedirect()->selectButton('login')->form();
$form['user_login[username]'] = 'johannes';
$form['user_login[password]'] = 'test';
$client->submit($form);
$this->assertRedirect($client->getResponse(), '/protected-resource');
$text = $client->followRedirect()->text();
$this->assertContains('Hello johannes!', $text);
$this->assertContains('You\'re browsing to path "/protected-resource".', $text);
}
public function getConfigs()
{
return array(
array('config.yml'),
array('routes_as_path.yml'),
);
}
}

View File

@@ -0,0 +1,46 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\SecurityBundle\Tests\Functional;
use Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\FirewallEntryPointBundle\Security\EntryPointStub;
class FirewallEntryPointTest extends WebTestCase
{
public function testItUsesTheConfiguredEntryPointWhenUsingUnknownCredentials()
{
$client = $this->createClient(array('test_case' => 'FirewallEntryPoint'));
$client->request('GET', '/secure/resource', array(), array(), array(
'PHP_AUTH_USER' => 'unknown',
'PHP_AUTH_PW' => 'credentials',
));
$this->assertEquals(
EntryPointStub::RESPONSE_TEXT,
$client->getResponse()->getContent(),
"Custom entry point wasn't started"
);
}
public function testItUsesTheConfiguredEntryPointFromTheExceptionListenerWithFormLoginAndNoCredentials()
{
$client = $this->createClient(array('test_case' => 'FirewallEntryPoint', 'root_config' => 'config_form_login.yml'));
$client->request('GET', '/secure/resource');
$this->assertEquals(
EntryPointStub::RESPONSE_TEXT,
$client->getResponse()->getContent(),
"Custom entry point wasn't started"
);
}
}

Some files were not shown because too many files have changed in this diff Show More