From bdc5ef231332c89de76df38f0127c349868540a6 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Fri, 26 Jun 2015 10:21:44 +0200 Subject: [PATCH 01/19] Improved the explanation about the verbosity levels of the console --- components/console/introduction.rst | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/components/console/introduction.rst b/components/console/introduction.rst index 4868d70e069..3b1f26bd47f 100644 --- a/components/console/introduction.rst +++ b/components/console/introduction.rst @@ -173,22 +173,18 @@ Verbosity Levels The ``VERBOSITY_VERY_VERBOSE`` and ``VERBOSITY_DEBUG`` constants were introduced in version 2.3 -The console has 5 levels of verbosity. These are defined in the +The console has five levels of verbosity. These are defined in the :class:`Symfony\\Component\\Console\\Output\\OutputInterface`: -======================================= ================================== -Mode Value -======================================= ================================== -OutputInterface::VERBOSITY_QUIET Do not output any messages -OutputInterface::VERBOSITY_NORMAL The default verbosity level -OutputInterface::VERBOSITY_VERBOSE Increased verbosity of messages -OutputInterface::VERBOSITY_VERY_VERBOSE Informative non essential messages -OutputInterface::VERBOSITY_DEBUG Debug messages -======================================= ================================== - -You can specify the quiet verbosity level with the ``--quiet`` or ``-q`` -option. The ``--verbose`` or ``-v`` option is used when you want an increased -level of verbosity. +=========================================== ================================== ===================== +Value Meaning Console option +=========================================== ================================== ===================== +``OutputInterface::VERBOSITY_QUIET`` Do not output any messages ``-q`` or ``--quiet`` +``OutputInterface::VERBOSITY_NORMAL`` The default verbosity level (none) +``OutputInterface::VERBOSITY_VERBOSE`` Increased verbosity of messages ``-v`` +``OutputInterface::VERBOSITY_VERY_VERBOSE`` Informative non essential messages ``-vv`` +``OutputInterface::VERBOSITY_DEBUG`` Debug messages ``-vvv`` +=========================================== ================================== ====================== .. tip:: @@ -198,7 +194,7 @@ level of verbosity. It is possible to print a message in a command for only a specific verbosity level. For example:: - if (OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) { + if ($output->getVerbosity() >= OutputInterface::VERBOSITY_VERBOSE) { $output->writeln(...); } From 38bc075d0a5935b94f1834a1184dabf3837d586a Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Fri, 26 Jun 2015 10:33:13 +0200 Subject: [PATCH 02/19] Minor reword --- components/console/introduction.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/console/introduction.rst b/components/console/introduction.rst index 3b1f26bd47f..eb7ab40ad16 100644 --- a/components/console/introduction.rst +++ b/components/console/introduction.rst @@ -173,7 +173,7 @@ Verbosity Levels The ``VERBOSITY_VERY_VERBOSE`` and ``VERBOSITY_DEBUG`` constants were introduced in version 2.3 -The console has five levels of verbosity. These are defined in the +The console has five verbosity levels. These are defined in the :class:`Symfony\\Component\\Console\\Output\\OutputInterface`: =========================================== ================================== ===================== From a5f0eec0dfc3e147057f5de258f2cadb0c66006b Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Fri, 26 Jun 2015 11:24:24 +0200 Subject: [PATCH 03/19] Documented the "auto_alias" feature --- reference/dic_tags.rst | 100 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) diff --git a/reference/dic_tags.rst b/reference/dic_tags.rst index e4db9851aaa..84b7d96cd59 100644 --- a/reference/dic_tags.rst +++ b/reference/dic_tags.rst @@ -22,6 +22,7 @@ Tag Name Usage `assetic.formula_resource`_ Adds a resource to the current asset manager `assetic.templating.php`_ Remove this service if PHP templating is disabled `assetic.templating.twig`_ Remove this service if Twig templating is disabled +`auto_alias`_ Define aliases based on the value of container parameters `console.command`_ Add a command `data_collector`_ Create a class that collects custom data for the profiler `doctrine.event_listener`_ Add a Doctrine event listener @@ -227,6 +228,105 @@ assetic.templating.twig The tagged service will be removed from the container if ``framework.templating.engines`` config section does not contain ``twig``. +auto_alias +---------- + +**Purpose**: Define aliases based on the value of container parameters + +Consider the following configuration that defines three different but related +services: + +.. configuration-block:: + + .. code-block:: yaml + + services: + app.mysql_lock: + class: AppBundle\Lock\MysqlLock + app.postgresql_lock: + class: AppBundle\Lock\PostgresqlLock + app.sqlite_lock: + class: AppBundle\Lock\SqliteLock + + + .. code-block:: xml + + + + + + + + + + + + .. code-block:: php + + $container + ->register('app.mysql_lock', 'AppBundle\Lock\MysqlLock') + ->register('app.postgresql_lock', 'AppBundle\Lock\PostgresqlLock') + ->register('app.sqlite_lock', 'AppBundle\Lock\SqliteLock') + ; + +Instead of dealing with these three services, your application needs a generic +``app.lock`` service. This service must be an alias to any of the other services. +Thanks to the ``auto_alias`` option, you can automatically create that alias +based on the value of a configuration parameter. + +Considering that a configuration parameter called ``database_type`` exists, +the generic ``app.lock`` service can be defined as follows: + +.. configuration-block:: + + .. code-block:: yaml + + services: + app.mysql_lock: + class: AppBundle\Lock\MysqlLock + app.postgresql_lock: + class: AppBundle\Lock\PostgresqlLock + app.sqlite_lock: + class: AppBundle\Lock\SqliteLock + app.lock: + tags: + - { name: auto_alias, format: "%database_type%.lock" } + + .. code-block:: xml + + + + + + + + + + + + + + + + .. code-block:: php + + $container + ->register('app.mysql_lock', 'AppBundle\Lock\MysqlLock') + ->register('app.postgresql_lock', 'AppBundle\Lock\PostgresqlLock') + ->register('app.sqlite_lock', 'AppBundle\Lock\SqliteLock') + + ->register('app.lock') + ->addTag('auto_alias', array('format' => '%database_type%.lock')) + ; + +The ``format`` parameter defines the expression used to construct the name of +the service to alias. This expression can use any container parameter (as usual, +wrapping their names with ``%`` characters). + console.command --------------- From 69152e7036d1d309380c2022a8f85a662d3ad08b Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Fri, 26 Jun 2015 11:37:48 +0200 Subject: [PATCH 04/19] Added the "versionadded: 2.7" directive --- reference/dic_tags.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/reference/dic_tags.rst b/reference/dic_tags.rst index 84b7d96cd59..34a7952b3e7 100644 --- a/reference/dic_tags.rst +++ b/reference/dic_tags.rst @@ -231,6 +231,9 @@ The tagged service will be removed from the container if auto_alias ---------- +.. versionadded:: 2.7 + The ``auto_alias`` tag was introduced in Symfony 2.7. + **Purpose**: Define aliases based on the value of container parameters Consider the following configuration that defines three different but related From e24f77ed372b0086cd74279647de90dcf6842f7c Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Fri, 26 Jun 2015 11:38:31 +0200 Subject: [PATCH 05/19] Removed an extra blank line --- reference/dic_tags.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/reference/dic_tags.rst b/reference/dic_tags.rst index 34a7952b3e7..1a9cd187814 100644 --- a/reference/dic_tags.rst +++ b/reference/dic_tags.rst @@ -251,7 +251,6 @@ services: app.sqlite_lock: class: AppBundle\Lock\SqliteLock - .. code-block:: xml From 4c50cb07b999c5b58abb673484dd18114dcc8442 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Fri, 26 Jun 2015 12:09:09 +0200 Subject: [PATCH 06/19] Fixed some errors and added a new note --- reference/dic_tags.rst | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/reference/dic_tags.rst b/reference/dic_tags.rst index 1a9cd187814..20794ff3319 100644 --- a/reference/dic_tags.rst +++ b/reference/dic_tags.rst @@ -288,13 +288,16 @@ the generic ``app.lock`` service can be defined as follows: services: app.mysql_lock: class: AppBundle\Lock\MysqlLock + public: false app.postgresql_lock: class: AppBundle\Lock\PostgresqlLock + public: false app.sqlite_lock: class: AppBundle\Lock\SqliteLock + public: false app.lock: tags: - - { name: auto_alias, format: "%database_type%.lock" } + - { name: auto_alias, format: "app.%database_type%.lock" } .. code-block:: xml @@ -304,12 +307,15 @@ the generic ``app.lock`` service can be defined as follows: xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> - - - + + + - + @@ -317,18 +323,25 @@ the generic ``app.lock`` service can be defined as follows: .. code-block:: php $container - ->register('app.mysql_lock', 'AppBundle\Lock\MysqlLock') - ->register('app.postgresql_lock', 'AppBundle\Lock\PostgresqlLock') - ->register('app.sqlite_lock', 'AppBundle\Lock\SqliteLock') + ->register('app.mysql_lock', 'AppBundle\Lock\MysqlLock')->setPublic(false) + ->register('app.postgresql_lock', 'AppBundle\Lock\PostgresqlLock')->setPublic(false) + ->register('app.sqlite_lock', 'AppBundle\Lock\SqliteLock')->setPublic(false) ->register('app.lock') - ->addTag('auto_alias', array('format' => '%database_type%.lock')) + ->addTag('auto_alias', array('format' => 'app.%database_type%.lock')) ; The ``format`` parameter defines the expression used to construct the name of the service to alias. This expression can use any container parameter (as usual, wrapping their names with ``%`` characters). +.. note:: + + When using the ``auto_alias`` tag is not mandatory to define the aliased + services as private. However, doing that (like in the above example) makes + sense most of the times to prevent accessing those services directly instead + of using the generic service. + console.command --------------- From acf66f9161ea02df8ab885db682dab6a5851f96b Mon Sep 17 00:00:00 2001 From: WouterJ Date: Sat, 27 Jun 2015 20:13:50 +0200 Subject: [PATCH 07/19] Move access decision strategy section --- cookbook/security/voters.rst | 83 -------------------- cookbook/security/voters_data_permission.rst | 82 +++++++++++++++++++ 2 files changed, 82 insertions(+), 83 deletions(-) diff --git a/cookbook/security/voters.rst b/cookbook/security/voters.rst index 4bc7df40a3a..4a3cd926d83 100644 --- a/cookbook/security/voters.rst +++ b/cookbook/security/voters.rst @@ -149,86 +149,3 @@ and tag it as a ``security.voter``: configuration file (e.g. ``app/config/config.yml``). For more information see :ref:`service-container-imports-directive`. To read more about defining services in general, see the :doc:`/book/service_container` chapter. - -.. _security-voters-change-strategy: - -Changing the Access Decision Strategy -------------------------------------- - -In order for the new voter to take effect, you need to change the default access -decision strategy, which, by default, grants access if *any* voter grants -access. - -In this case, choose the ``unanimous`` strategy. Unlike the ``affirmative`` -strategy (the default), with the ``unanimous`` strategy, if only one voter -denies access (e.g. the ``ClientIpVoter``), access is not granted to the -end user. - -To do that, override the default ``access_decision_manager`` section of your -application configuration file with the following code. - -.. configuration-block:: - - .. code-block:: yaml - - # app/config/security.yml - security: - access_decision_manager: - # strategy can be: affirmative, unanimous or consensus - strategy: unanimous - - .. code-block:: xml - - - - - - - - .. code-block:: php - - // app/config/security.xml - $container->loadFromExtension('security', array( - // strategy can be: affirmative, unanimous or consensus - 'access_decision_manager' => array( - 'strategy' => 'unanimous', - ), - )); - -That's it! Now, when deciding whether or not a user should have access, -the new voter will deny access to any user in the list of blacklisted IPs. - -Note that the voters are only called, if any access is actually checked. So -you need at least something like - -.. configuration-block:: - - .. code-block:: yaml - - # app/config/security.yml - security: - access_control: - - { path: ^/, role: IS_AUTHENTICATED_ANONYMOUSLY } - - .. code-block:: xml - - - - - - - - - .. code-block:: php - - // app/config/security.xml - $container->loadFromExtension('security', array( - 'access_control' => array( - array('path' => '^/', 'role' => 'IS_AUTHENTICATED_ANONYMOUSLY'), - ), - )); - -.. seealso:: - - For a more advanced usage see - :ref:`components-security-access-decision-manager`. diff --git a/cookbook/security/voters_data_permission.rst b/cookbook/security/voters_data_permission.rst index 2376d393b53..240035c3289 100644 --- a/cookbook/security/voters_data_permission.rst +++ b/cookbook/security/voters_data_permission.rst @@ -220,3 +220,85 @@ from the security context is called. } It's that easy! + +.. _security-voters-change-strategy: + +Changing the Access Decision Strategy +------------------------------------- + +In order for the new voter to take effect, you need to change the default access +decision strategy, which, by default, grants access if *any* voter grants +access. + +In this case, choose the ``unanimous`` strategy. Unlike the ``affirmative`` +strategy (the default), with the ``unanimous`` strategy, if only one voter +denies access (e.g. the ``ClientIpVoter``), access is not granted to the +end user. + +To do that, override the default ``access_decision_manager`` section of your +application configuration file with the following code. + +.. configuration-block:: + + .. code-block:: yaml + + # app/config/security.yml + security: + access_decision_manager: + # strategy can be: affirmative, unanimous or consensus + strategy: unanimous + + .. code-block:: xml + + + + + + + + .. code-block:: php + + // app/config/security.xml + $container->loadFromExtension('security', array( + // strategy can be: affirmative, unanimous or consensus + 'access_decision_manager' => array( + 'strategy' => 'unanimous', + ), + )); + +That's it! Now, when deciding whether or not a user should have access, +the new voter will deny access to any user in the list of blacklisted IPs. + +Note that the voters are only called, if any access is actually checked. So +you need at least something like + +.. configuration-block:: + + .. code-block:: yaml + + # app/config/security.yml + security: + access_control: + - { path: ^/, role: IS_AUTHENTICATED_ANONYMOUSLY } + + .. code-block:: xml + + + + + + + + + .. code-block:: php + + // app/config/security.xml + $container->loadFromExtension('security', array( + 'access_control' => array( + array('path' => '^/', 'role' => 'IS_AUTHENTICATED_ANONYMOUSLY'), + ), + )); + +.. seealso:: + + For a more advanced usage see :ref:`components-security-access-decision-manager`. From 9c169c79b96eba90735a7e31488a0223aa443676 Mon Sep 17 00:00:00 2001 From: WouterJ Date: Sat, 27 Jun 2015 20:28:49 +0200 Subject: [PATCH 08/19] Rewrite new section --- cookbook/security/voters_data_permission.rst | 90 ++++++++------------ 1 file changed, 35 insertions(+), 55 deletions(-) diff --git a/cookbook/security/voters_data_permission.rst b/cookbook/security/voters_data_permission.rst index 240035c3289..3513b6dde0b 100644 --- a/cookbook/security/voters_data_permission.rst +++ b/cookbook/security/voters_data_permission.rst @@ -223,20 +223,30 @@ It's that easy! .. _security-voters-change-strategy: -Changing the Access Decision Strategy -------------------------------------- +Changing the Decision Strategy +------------------------------ -In order for the new voter to take effect, you need to change the default access -decision strategy, which, by default, grants access if *any* voter grants -access. +Imagine you have multiple voters for one action for an object. For instance, +you have one voter that checks if the user is a member of the site and a second +one checking if the user is older than 18. -In this case, choose the ``unanimous`` strategy. Unlike the ``affirmative`` -strategy (the default), with the ``unanimous`` strategy, if only one voter -denies access (e.g. the ``ClientIpVoter``), access is not granted to the -end user. +To handle these cases, the access decision manager uses a decision strategy. +You can configure this to suite your needs. There are three strategies +available: -To do that, override the default ``access_decision_manager`` section of your -application configuration file with the following code. +``affirmative`` (default) + This grants access as soon as there is *one* voter granting access; + +``consensus`` + This grants access if there are more voters granting access than denying; + +``unanimous`` + This only grants access once *all* voters grant access. + +In the above scenario, both voters should grant access in order to grant access +to the user to read the post. In this case, the default strategy is no longer +valid and ``unanimous`` should be used instead. You can set this in the +security configuration: .. configuration-block:: @@ -245,60 +255,30 @@ application configuration file with the following code. # app/config/security.yml security: access_decision_manager: - # strategy can be: affirmative, unanimous or consensus strategy: unanimous .. code-block:: xml - - - - + + + + + + .. code-block:: php - // app/config/security.xml + // app/config/security.php $container->loadFromExtension('security', array( - // strategy can be: affirmative, unanimous or consensus 'access_decision_manager' => array( 'strategy' => 'unanimous', ), )); - -That's it! Now, when deciding whether or not a user should have access, -the new voter will deny access to any user in the list of blacklisted IPs. - -Note that the voters are only called, if any access is actually checked. So -you need at least something like - -.. configuration-block:: - - .. code-block:: yaml - - # app/config/security.yml - security: - access_control: - - { path: ^/, role: IS_AUTHENTICATED_ANONYMOUSLY } - - .. code-block:: xml - - - - - - - - - .. code-block:: php - - // app/config/security.xml - $container->loadFromExtension('security', array( - 'access_control' => array( - array('path' => '^/', 'role' => 'IS_AUTHENTICATED_ANONYMOUSLY'), - ), - )); - -.. seealso:: - - For a more advanced usage see :ref:`components-security-access-decision-manager`. From 93484a707df22b92ed3e6ca478e7ce996b863284 Mon Sep 17 00:00:00 2001 From: WouterJ Date: Sat, 27 Jun 2015 20:39:20 +0200 Subject: [PATCH 09/19] Remove the old voter article --- book/security.rst | 10 +- cookbook/map.rst.inc | 1 - cookbook/security/acl.rst | 2 +- cookbook/security/index.rst | 1 - cookbook/security/voter_interface.rst.inc | 24 -- cookbook/security/voters.rst | 332 ++++++++++++++----- cookbook/security/voters_data_permission.rst | 284 ---------------- redirection_map | 1 + 8 files changed, 248 insertions(+), 407 deletions(-) delete mode 100644 cookbook/security/voter_interface.rst.inc delete mode 100644 cookbook/security/voters_data_permission.rst diff --git a/book/security.rst b/book/security.rst index 9bfe3c2809a..640152cfda2 100644 --- a/book/security.rst +++ b/book/security.rst @@ -929,10 +929,10 @@ other users. Also, as the admin user, you yourself want to be able to edit To accomplish this you have 2 options: -* :doc:`Voters ` allow you to - use business logic (e.g. the user can edit this post because they were - the creator) to determine access. You'll probably want this option - it's - flexible enough to solve the above situation. +* :doc:`Voters ` allow you to use business logic + (e.g. the user can edit this post because they were the creator) to determine + access. You'll probably want this option - it's flexible enough to solve the + above situation. * :doc:`ACLs ` allow you to create a database structure where you can assign *any* arbitrary user *any* access (e.g. EDIT, VIEW) @@ -1281,7 +1281,7 @@ Learn More from the Cookbook * :doc:`Forcing HTTP/HTTPS ` * :doc:`Impersonating a User ` -* :doc:`/cookbook/security/voters_data_permission` +* :doc:`/cookbook/security/voters` * :doc:`Access Control Lists (ACLs) ` * :doc:`/cookbook/security/remember_me` * :doc:`/cookbook/security/multiple_user_providers` diff --git a/cookbook/map.rst.inc b/cookbook/map.rst.inc index 6c37de6d5f7..135342503b7 100644 --- a/cookbook/map.rst.inc +++ b/cookbook/map.rst.inc @@ -151,7 +151,6 @@ * :doc:`/cookbook/security/remember_me` * :doc:`/cookbook/security/impersonating_user` * :doc:`/cookbook/security/voters` - * :doc:`/cookbook/security/voters_data_permission` * :doc:`/cookbook/security/acl` * :doc:`/cookbook/security/acl_advanced` * :doc:`/cookbook/security/force_https` diff --git a/cookbook/security/acl.rst b/cookbook/security/acl.rst index c6313167c40..507efe00dd7 100644 --- a/cookbook/security/acl.rst +++ b/cookbook/security/acl.rst @@ -14,7 +14,7 @@ the ACL system comes in. Using ACL's isn't trivial, and for simpler use cases, it may be overkill. If your permission logic could be described by just writing some code (e.g. to check if a Blog is owned by the current User), then consider using - :doc:`voters `. A voter is passed the object + :doc:`voters `. A voter is passed the object being voted on, which you can use to make complex decisions and effectively implement your own ACL. Enforcing authorization (e.g. the ``isGranted`` part) will look similar to what you see in this entry, but your voter diff --git a/cookbook/security/index.rst b/cookbook/security/index.rst index 5bf643c10e8..e666a2dcff4 100644 --- a/cookbook/security/index.rst +++ b/cookbook/security/index.rst @@ -9,7 +9,6 @@ Security remember_me impersonating_user voters - voters_data_permission acl acl_advanced force_https diff --git a/cookbook/security/voter_interface.rst.inc b/cookbook/security/voter_interface.rst.inc deleted file mode 100644 index 1a3cd989e3c..00000000000 --- a/cookbook/security/voter_interface.rst.inc +++ /dev/null @@ -1,24 +0,0 @@ -.. code-block:: php - - interface VoterInterface - { - public function supportsAttribute($attribute); - public function supportsClass($class); - public function vote(TokenInterface $token, $object, array $attributes); - } - -The :method:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\VoterInterface::supportsAttribute` -method is used to check if the voter supports the given user attribute (i.e: -a role like ``ROLE_USER``, an ACL ``EDIT``, etc.). - -The :method:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\VoterInterface::supportsClass` -method is used to check if the voter supports the class of the object whose -access is being checked. - -The :method:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\VoterInterface::vote` -method must implement the business logic that verifies whether or not the -user has access. This method must return one of the following values: - -* ``VoterInterface::ACCESS_GRANTED``: The authorization will be granted by this voter; -* ``VoterInterface::ACCESS_ABSTAIN``: The voter cannot decide if authorization should be granted; -* ``VoterInterface::ACCESS_DENIED``: The authorization will be denied by this voter. diff --git a/cookbook/security/voters.rst b/cookbook/security/voters.rst index 4a3cd926d83..7025d61ae3e 100644 --- a/cookbook/security/voters.rst +++ b/cookbook/security/voters.rst @@ -1,151 +1,301 @@ .. index:: - single: Security; Voters + single: Security; Data Permission + single: Security: Voters -How to Implement your own Voter to Blacklist IP Addresses -========================================================= +How to Use Voters to Check User Permissions +=========================================== -The Symfony Security component provides several layers to authorize users. -One of the layers is called a "voter". A voter is a dedicated class that checks -if the user has the rights to connect to the application or access a specific -resource/URL. For instance, Symfony provides a layer that checks if the user -is fully authorized or if it has some expected roles. +In Symfony, you can check the permission to access data by using the +:doc:`ACL module `, which is a bit overwhelming +for many applications. A much easier solution is to work with custom voters, +which are like simple conditional statements. -It is sometimes useful to create a custom voter to handle a specific case not -handled by the framework. In this section, you'll learn how to create a voter -that will allow you to blacklist users by their IP. +.. tip:: + + Take a look at the + :doc:`authorization ` + chapter for an even deeper understanding on voters. + +How Symfony Uses Voters +----------------------- + +In order to use voters, you have to understand how Symfony works with them. +All voters are called each time you use the ``isGranted()`` method on Symfony's +security context (i.e. the ``security.context`` service). Each one decides +if the current user should have access to some resource. + +Ultimately, Symfony uses one of three different approaches on what to do +with the feedback from all voters: affirmative, consensus and unanimous. + +For more information take a look at +:ref:`the section about access decision managers `. The Voter Interface ------------------- A custom voter must implement :class:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\VoterInterface`, -which requires the following three methods: +which has this structure:: -.. include:: /cookbook/security/voter_interface.rst.inc + interface VoterInterface + { + public function supportsAttribute($attribute); + public function supportsClass($class); + public function vote(TokenInterface $token, $object, array $attributes); + } -In this example, you'll check if the user's IP address matches against a list of -blacklisted addresses and "something" will be the application. If the user's IP is blacklisted, you'll return -``VoterInterface::ACCESS_DENIED``, otherwise you'll return -``VoterInterface::ACCESS_ABSTAIN`` as this voter's purpose is only to deny -access, not to grant access. +The :method:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\VoterInterface::supportsAttribute` +method is used to check if the voter supports the given user attribute (i.e: +a role like ``ROLE_USER``, an ACL ``EDIT``, etc.). -Creating a custom Voter ------------------------ +The :method:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\VoterInterface::supportsClass` +method is used to check if the voter supports the class of the object whose +access is being checked. -To blacklist a user based on its IP, you can use the ``request`` service -and compare the IP address against a set of blacklisted IP addresses: +The :method:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\VoterInterface::vote` +method must implement the business logic that verifies whether or not the +user has access. This method must return one of the following values: -.. code-block:: php +* ``VoterInterface::ACCESS_GRANTED``: The authorization will be granted by this voter; +* ``VoterInterface::ACCESS_ABSTAIN``: The voter cannot decide if authorization should be granted; +* ``VoterInterface::ACCESS_DENIED``: The authorization will be denied by this voter. + +In this example, the voter will check if the user has access to a specific +object according to your custom conditions (e.g. they must be the owner of +the object). If the condition fails, you'll return +``VoterInterface::ACCESS_DENIED``, otherwise you'll return +``VoterInterface::ACCESS_GRANTED``. In case the responsibility for this decision +does not belong to this voter, it will return ``VoterInterface::ACCESS_ABSTAIN``. + +Creating the custom Voter +------------------------- - // src/AppBundle/Security/Authorization/Voter/ClientIpVoter.php +The goal is to create a voter that checks if a user has access to view or +edit a particular object. Here's an example implementation:: + + // src/AppBundle/Security/Authorization/Voter/PostVoter.php namespace AppBundle\Security\Authorization\Voter; - use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; + use Symfony\Component\Security\Core\User\UserInterface; - class ClientIpVoter implements VoterInterface + class PostVoter implements VoterInterface { - private $container; - - private $blacklistedIp; - - public function __construct(ContainerInterface $container, array $blacklistedIp = array()) - { - $this->container = $container; - $this->blacklistedIp = $blacklistedIp; - } + const VIEW = 'view'; + const EDIT = 'edit'; public function supportsAttribute($attribute) { - // you won't check against a user attribute, so return true - return true; + return in_array($attribute, array( + self::VIEW, + self::EDIT, + )); } public function supportsClass($class) { - // your voter supports all type of token classes, so return true - return true; + $supportedClass = 'AppBundle\Entity\Post'; + + return $supportedClass === $class || is_subclass_of($class, $supportedClass); } - public function vote(TokenInterface $token, $object, array $attributes) + /** + * @var \AppBundle\Entity\Post $post + */ + public function vote(TokenInterface $token, $post, array $attributes) { - $request = $this->container->get('request'); - if (in_array($request->getClientIp(), $this->blacklistedIp)) { + // check if class of this object is supported by this voter + if (!$this->supportsClass(get_class($post))) { + return VoterInterface::ACCESS_ABSTAIN; + } + + // check if the voter is used correct, only allow one attribute + // this isn't a requirement, it's just one easy way for you to + // design your voter + if (1 !== count($attributes)) { + throw new \InvalidArgumentException( + 'Only one attribute is allowed for VIEW or EDIT' + ); + } + + // set the attribute to check against + $attribute = $attributes[0]; + + // check if the given attribute is covered by this voter + if (!$this->supportsAttribute($attribute)) { + return VoterInterface::ACCESS_ABSTAIN; + } + + // get current logged in user + $user = $token->getUser(); + + // make sure there is a user object (i.e. that the user is logged in) + if (!$user instanceof UserInterface) { return VoterInterface::ACCESS_DENIED; } - return VoterInterface::ACCESS_ABSTAIN; + switch($attribute) { + case self::VIEW: + // the data object could have for example a method isPrivate() + // which checks the boolean attribute $private + if (!$post->isPrivate()) { + return VoterInterface::ACCESS_GRANTED; + } + break; + + case self::EDIT: + // we assume that our data object has a method getOwner() to + // get the current owner user entity for this data object + if ($user->getId() === $post->getOwner()->getId()) { + return VoterInterface::ACCESS_GRANTED; + } + break; + } + + return VoterInterface::ACCESS_DENIED; } } That's it! The voter is done. The next step is to inject the voter into -the security layer. This can be done easily through the service container. - -.. tip:: - - Your implementation of the methods - :method:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\VoterInterface::supportsAttribute` - and :method:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\VoterInterface::supportsClass` - are not being called internally by the framework. Once you have registered your - voter the ``vote()`` method will always be called, regardless of whether - or not these two methods return true. Therefore you need to call those - methods in your implementation of the ``vote()`` method and return ``ACCESS_ABSTAIN`` - if your voter does not support the class or attribute. +the security layer. Declaring the Voter as a Service -------------------------------- -To inject the voter into the security layer, you must declare it as a service, -and tag it as a ``security.voter``: +To inject the voter into the security layer, you must declare it as a service +and tag it with ``security.voter``: .. configuration-block:: .. code-block:: yaml - # src/Acme/AcmeBundle/Resources/config/services.yml + # src/AppBundle/Resources/config/services.yml services: - security.access.blacklist_voter: - class: AppBundle\Security\Authorization\Voter\ClientIpVoter - arguments: ["@service_container", [123.123.123.123, 171.171.171.171]] - public: false + security.access.post_voter: + class: AppBundle\Security\Authorization\Voter\PostVoter + public: false tags: - - { name: security.voter } + - { name: security.voter } .. code-block:: xml - - - - - 123.123.123.123 - 171.171.171.171 - - - + + + + + + + + + .. code-block:: php - // src/Acme/AcmeBundle/Resources/config/services.php - use Symfony\Component\DependencyInjection\Definition; - use Symfony\Component\DependencyInjection\Reference; + // src/AppBundle/Resources/config/services.php + $container + ->register( + 'security.access.post_document_voter', + 'AppBundle\Security\Authorization\Voter\PostVoter' + ) + ->addTag('security.voter') + ; - $definition = new Definition( - 'AppBundle\Security\Authorization\Voter\ClientIpVoter', - array( - new Reference('service_container'), - array('123.123.123.123', '171.171.171.171'), - ), - ); - $definition->addTag('security.voter'); - $definition->setPublic(false); +How to Use the Voter in a Controller +------------------------------------ - $container->setDefinition('security.access.blacklist_voter', $definition); +The registered voter will then always be asked as soon as the method ``isGranted()`` +from the security context is called. -.. tip:: +.. code-block:: php + + // src/AppBundle/Controller/PostController.php + namespace AppBundle\Controller; + + use Symfony\Bundle\FrameworkBundle\Controller\Controller; + use Symfony\Component\HttpFoundation\Response; + use Symfony\Component\Security\Core\Exception\AccessDeniedException; + + class PostController extends Controller + { + public function showAction($id) + { + // get a Post instance + $post = ...; + + // keep in mind, this will call all registered security voters + if (false === $this->get('security.context')->isGranted('view', $post)) { + throw new AccessDeniedException('Unauthorised access!'); + } + + return new Response('

'.$post->getName().'

'); + } + } + +It's that easy! + +.. _security-voters-change-strategy: + +Changing the Access Decision Strategy +------------------------------------- + +Imagine you have multiple voters for one action for an object. For instance, +you have one voter that checks if the user is a member of the site and a second +one checking if the user is older than 18. - Be sure to import this configuration file from your main application - configuration file (e.g. ``app/config/config.yml``). For more information - see :ref:`service-container-imports-directive`. To read more about defining - services in general, see the :doc:`/book/service_container` chapter. +To handle these cases, the access decision manager uses an access decision +strategy. You can configure this to suite your needs. There are three +strategies available: + +``affirmative`` (default) + This grants access as soon as there is *one* voter granting access; + +``consensus`` + This grants access if there are more voters granting access than denying; + +``unanimous`` + This only grants access once *all* voters grant access. + +In the above scenario, both voters should grant access in order to grant access +to the user to read the post. In this case, the default strategy is no longer +valid and ``unanimous`` should be used instead. You can set this in the +security configuration: + +.. configuration-block:: + + .. code-block:: yaml + + # app/config/security.yml + security: + access_decision_manager: + strategy: unanimous + + .. code-block:: xml + + + + + + + + + + .. code-block:: php + + // app/config/security.php + $container->loadFromExtension('security', array( + 'access_decision_manager' => array( + 'strategy' => 'unanimous', + ), + )); diff --git a/cookbook/security/voters_data_permission.rst b/cookbook/security/voters_data_permission.rst deleted file mode 100644 index 3513b6dde0b..00000000000 --- a/cookbook/security/voters_data_permission.rst +++ /dev/null @@ -1,284 +0,0 @@ -.. index:: - single: Security; Data Permission Voters - -How to Use Voters to Check User Permissions -=========================================== - -In Symfony, you can check the permission to access data by using the -:doc:`ACL module `, which is a bit overwhelming -for many applications. A much easier solution is to work with custom voters, -which are like simple conditional statements. - -.. seealso:: - - Voters can also be used in other ways, like, for example, blacklisting IP - addresses from the entire application: :doc:`/cookbook/security/voters`. - -.. tip:: - - Take a look at the - :doc:`authorization ` - chapter for an even deeper understanding on voters. - -How Symfony Uses Voters ------------------------ - -In order to use voters, you have to understand how Symfony works with them. -All voters are called each time you use the ``isGranted()`` method on Symfony's -security context (i.e. the ``security.context`` service). Each one decides -if the current user should have access to some resource. - -Ultimately, Symfony uses one of three different approaches on what to do -with the feedback from all voters: affirmative, consensus and unanimous. - -For more information take a look at -:ref:`the section about access decision managers `. - -The Voter Interface -------------------- - -A custom voter must implement -:class:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\VoterInterface`, -which has this structure: - -.. include:: /cookbook/security/voter_interface.rst.inc - -In this example, the voter will check if the user has access to a specific -object according to your custom conditions (e.g. they must be the owner of -the object). If the condition fails, you'll return -``VoterInterface::ACCESS_DENIED``, otherwise you'll return -``VoterInterface::ACCESS_GRANTED``. In case the responsibility for this decision -does not belong to this voter, it will return ``VoterInterface::ACCESS_ABSTAIN``. - -Creating the custom Voter -------------------------- - -The goal is to create a voter that checks if a user has access to view or -edit a particular object. Here's an example implementation:: - - // src/AppBundle/Security/Authorization/Voter/PostVoter.php - namespace AppBundle\Security\Authorization\Voter; - - use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface; - use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; - use Symfony\Component\Security\Core\User\UserInterface; - - class PostVoter implements VoterInterface - { - const VIEW = 'view'; - const EDIT = 'edit'; - - public function supportsAttribute($attribute) - { - return in_array($attribute, array( - self::VIEW, - self::EDIT, - )); - } - - public function supportsClass($class) - { - $supportedClass = 'AppBundle\Entity\Post'; - - return $supportedClass === $class || is_subclass_of($class, $supportedClass); - } - - /** - * @var \AppBundle\Entity\Post $post - */ - public function vote(TokenInterface $token, $post, array $attributes) - { - // check if class of this object is supported by this voter - if (!$this->supportsClass(get_class($post))) { - return VoterInterface::ACCESS_ABSTAIN; - } - - // check if the voter is used correct, only allow one attribute - // this isn't a requirement, it's just one easy way for you to - // design your voter - if (1 !== count($attributes)) { - throw new \InvalidArgumentException( - 'Only one attribute is allowed for VIEW or EDIT' - ); - } - - // set the attribute to check against - $attribute = $attributes[0]; - - // check if the given attribute is covered by this voter - if (!$this->supportsAttribute($attribute)) { - return VoterInterface::ACCESS_ABSTAIN; - } - - // get current logged in user - $user = $token->getUser(); - - // make sure there is a user object (i.e. that the user is logged in) - if (!$user instanceof UserInterface) { - return VoterInterface::ACCESS_DENIED; - } - - switch($attribute) { - case self::VIEW: - // the data object could have for example a method isPrivate() - // which checks the boolean attribute $private - if (!$post->isPrivate()) { - return VoterInterface::ACCESS_GRANTED; - } - break; - - case self::EDIT: - // we assume that our data object has a method getOwner() to - // get the current owner user entity for this data object - if ($user->getId() === $post->getOwner()->getId()) { - return VoterInterface::ACCESS_GRANTED; - } - break; - } - - return VoterInterface::ACCESS_DENIED; - } - } - -That's it! The voter is done. The next step is to inject the voter into -the security layer. - -Declaring the Voter as a Service --------------------------------- - -To inject the voter into the security layer, you must declare it as a service -and tag it with ``security.voter``: - -.. configuration-block:: - - .. code-block:: yaml - - # src/AppBundle/Resources/config/services.yml - services: - security.access.post_voter: - class: AppBundle\Security\Authorization\Voter\PostVoter - public: false - tags: - - { name: security.voter } - - .. code-block:: xml - - - - - - - - - - - - .. code-block:: php - - // src/AppBundle/Resources/config/services.php - $container - ->register( - 'security.access.post_document_voter', - 'AppBundle\Security\Authorization\Voter\PostVoter' - ) - ->addTag('security.voter') - ; - -How to Use the Voter in a Controller ------------------------------------- - -The registered voter will then always be asked as soon as the method ``isGranted()`` -from the security context is called. - -.. code-block:: php - - // src/AppBundle/Controller/PostController.php - namespace AppBundle\Controller; - - use Symfony\Bundle\FrameworkBundle\Controller\Controller; - use Symfony\Component\HttpFoundation\Response; - use Symfony\Component\Security\Core\Exception\AccessDeniedException; - - class PostController extends Controller - { - public function showAction($id) - { - // get a Post instance - $post = ...; - - // keep in mind, this will call all registered security voters - if (false === $this->get('security.context')->isGranted('view', $post)) { - throw new AccessDeniedException('Unauthorised access!'); - } - - return new Response('

'.$post->getName().'

'); - } - } - -It's that easy! - -.. _security-voters-change-strategy: - -Changing the Decision Strategy ------------------------------- - -Imagine you have multiple voters for one action for an object. For instance, -you have one voter that checks if the user is a member of the site and a second -one checking if the user is older than 18. - -To handle these cases, the access decision manager uses a decision strategy. -You can configure this to suite your needs. There are three strategies -available: - -``affirmative`` (default) - This grants access as soon as there is *one* voter granting access; - -``consensus`` - This grants access if there are more voters granting access than denying; - -``unanimous`` - This only grants access once *all* voters grant access. - -In the above scenario, both voters should grant access in order to grant access -to the user to read the post. In this case, the default strategy is no longer -valid and ``unanimous`` should be used instead. You can set this in the -security configuration: - -.. configuration-block:: - - .. code-block:: yaml - - # app/config/security.yml - security: - access_decision_manager: - strategy: unanimous - - .. code-block:: xml - - - - - - - - - - .. code-block:: php - - // app/config/security.php - $container->loadFromExtension('security', array( - 'access_decision_manager' => array( - 'strategy' => 'unanimous', - ), - )); diff --git a/redirection_map b/redirection_map index 7e60794f8c6..1ebc69ced38 100644 --- a/redirection_map +++ b/redirection_map @@ -25,3 +25,4 @@ /components/yaml /components/yaml/introduction /components/templating /components/templating/introduction /cookbook/upgrading /cookbook/upgrade/index +/cookbook/security/voters_data_permission /cookbook/security/voters From 4d0f6ea1bcb2e197c388d822698c39bd66ebabe5 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 30 Jun 2015 16:04:27 +0200 Subject: [PATCH 10/19] Minor fixes --- reference/dic_tags.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/reference/dic_tags.rst b/reference/dic_tags.rst index 20794ff3319..ea752f9d6c1 100644 --- a/reference/dic_tags.rst +++ b/reference/dic_tags.rst @@ -337,10 +337,10 @@ wrapping their names with ``%`` characters). .. note:: - When using the ``auto_alias`` tag is not mandatory to define the aliased + When using the ``auto_alias`` tag it's not mandatory to define the aliased services as private. However, doing that (like in the above example) makes sense most of the times to prevent accessing those services directly instead - of using the generic service. + of using the generic service alias. console.command --------------- From bab745d6628e2694bba9ade1fd46a13b21b67726 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Thu, 2 Jul 2015 22:49:15 +0200 Subject: [PATCH 11/19] Minor grammar issue --- reference/dic_tags.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reference/dic_tags.rst b/reference/dic_tags.rst index ea752f9d6c1..9788888e43b 100644 --- a/reference/dic_tags.rst +++ b/reference/dic_tags.rst @@ -337,7 +337,7 @@ wrapping their names with ``%`` characters). .. note:: - When using the ``auto_alias`` tag it's not mandatory to define the aliased + When using the ``auto_alias`` tag, it's not mandatory to define the aliased services as private. However, doing that (like in the above example) makes sense most of the times to prevent accessing those services directly instead of using the generic service alias. From 0eac43d58096fe5e0611d4f1ea9508f94755330a Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Fri, 3 Jul 2015 17:51:33 +0200 Subject: [PATCH 12/19] Reverted an unneeded change --- components/console/introduction.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/console/introduction.rst b/components/console/introduction.rst index eb7ab40ad16..8e8e8b188b2 100644 --- a/components/console/introduction.rst +++ b/components/console/introduction.rst @@ -194,7 +194,7 @@ Value Meaning It is possible to print a message in a command for only a specific verbosity level. For example:: - if ($output->getVerbosity() >= OutputInterface::VERBOSITY_VERBOSE) { + if (OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) { $output->writeln(...); } From 7ca24eddbf22e45f848945548a660abcb31cde4c Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Fri, 3 Jul 2015 17:52:02 +0200 Subject: [PATCH 13/19] Fixed RST table syntax --- components/console/introduction.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/console/introduction.rst b/components/console/introduction.rst index 8e8e8b188b2..444a077731b 100644 --- a/components/console/introduction.rst +++ b/components/console/introduction.rst @@ -184,7 +184,7 @@ Value Meaning ``OutputInterface::VERBOSITY_VERBOSE`` Increased verbosity of messages ``-v`` ``OutputInterface::VERBOSITY_VERY_VERBOSE`` Informative non essential messages ``-vv`` ``OutputInterface::VERBOSITY_DEBUG`` Debug messages ``-vvv`` -=========================================== ================================== ====================== +=========================================== ================================== ===================== .. tip:: From 1524f5a66ec34bbaefad6802aec3163a4b2eedbd Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 14 Jul 2015 21:34:50 +0200 Subject: [PATCH 14/19] Fixed an error in the auto_alias format value --- reference/dic_tags.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/reference/dic_tags.rst b/reference/dic_tags.rst index 9788888e43b..56013b4a52e 100644 --- a/reference/dic_tags.rst +++ b/reference/dic_tags.rst @@ -297,7 +297,7 @@ the generic ``app.lock`` service can be defined as follows: public: false app.lock: tags: - - { name: auto_alias, format: "app.%database_type%.lock" } + - { name: auto_alias, format: "app.%database_type%_lock" } .. code-block:: xml @@ -315,7 +315,7 @@ the generic ``app.lock`` service can be defined as follows: class="AppBundle\Lock\SqliteLock" /> - + @@ -328,7 +328,7 @@ the generic ``app.lock`` service can be defined as follows: ->register('app.sqlite_lock', 'AppBundle\Lock\SqliteLock')->setPublic(false) ->register('app.lock') - ->addTag('auto_alias', array('format' => 'app.%database_type%.lock')) + ->addTag('auto_alias', array('format' => 'app.%database_type%_lock')) ; The ``format`` parameter defines the expression used to construct the name of From 577087e60d56fe3a52890170760c433543a06769 Mon Sep 17 00:00:00 2001 From: listerical85 Date: Wed, 15 Jul 2015 16:38:57 +0200 Subject: [PATCH 15/19] TYPO: missing closing parantheses of the array --- cookbook/serializer.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cookbook/serializer.rst b/cookbook/serializer.rst index 8da5c70a583..8b2d9b296d8 100644 --- a/cookbook/serializer.rst +++ b/cookbook/serializer.rst @@ -169,7 +169,7 @@ to your class and choose which groups to use when serializing:: $serializer = $this->get('serializer'); $json = $serializer->serialize( $someObject, - 'json', array('groups' => array('group1') + 'json', array('groups' => array('group1')) ); .. _cookbook-serializer-enabling-metadata-cache: From 8a0297ffe50c213c50bd4d1ef267765696cc86ad Mon Sep 17 00:00:00 2001 From: snroki Date: Wed, 15 Jul 2015 14:29:33 +0200 Subject: [PATCH 16/19] Custom voter example, fix missing curly brace | Q | A | ------------- | --- | Doc fix? | yes | New docs? | no | Applies to | 2.6 / 2.7 / 3.0 | Fixed tickets | --- cookbook/security/voters_data_permission.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cookbook/security/voters_data_permission.rst b/cookbook/security/voters_data_permission.rst index a9d5e2b9076..18ef7df9ef0 100644 --- a/cookbook/security/voters_data_permission.rst +++ b/cookbook/security/voters_data_permission.rst @@ -111,7 +111,8 @@ edit a particular object. Here's an example implementation: } break; - + } + return false; } } From c1e8453636ba1ea65677e6c8b4493a58d7f729a2 Mon Sep 17 00:00:00 2001 From: Alexander Schwenn Date: Wed, 15 Jul 2015 22:43:55 +0200 Subject: [PATCH 17/19] [Book][Routing] Change example to match multiple methods --- book/routing.rst | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/book/routing.rst b/book/routing.rst index 14968290474..4476031a7c9 100644 --- a/book/routing.rst +++ b/book/routing.rst @@ -833,36 +833,36 @@ be accomplished with the following route configuration: class MainController extends Controller { /** - * @Route("/contact") + * @Route("/news") * @Method("GET") */ - public function contactAction() + public function newsAction() { - // ... display contact form + // ... display your news } /** * @Route("/contact") - * @Method("POST") + * @Method({"GET", "POST"}) */ - public function processContactAction() + public function contactFormAction() { - // ... process contact form + // ... display and process a contact form } } .. code-block:: yaml # app/config/routing.yml - contact: - path: /contact - defaults: { _controller: AppBundle:Main:contact } + news: + path: /news + defaults: { _controller: AppBundle:Main:news } methods: [GET] - contact_process: + contact_form: path: /contact - defaults: { _controller: AppBundle:Main:processContact } - methods: [POST] + defaults: { _controller: AppBundle:Main:contactForm } + methods: [GET, POST] .. code-block:: xml @@ -873,12 +873,12 @@ be accomplished with the following route configuration: xsi:schemaLocation="http://symfony.com/schema/routing http://symfony.com/schema/routing/routing-1.0.xsd"> - - AppBundle:Main:contact + + AppBundle:Main:news - - AppBundle:Main:processContact + + AppBundle:Main:contactForm @@ -889,13 +889,13 @@ be accomplished with the following route configuration: use Symfony\Component\Routing\Route; $collection = new RouteCollection(); - $collection->add('contact', new Route('/contact', array( + $collection->add('news', new Route('/news', array( '_controller' => 'AppBundle:Main:contact', ), array(), array(), '', array(), array('GET'))); - $collection->add('contact_process', new Route('/contact', array( - '_controller' => 'AppBundle:Main:processContact', - ), array(), array(), '', array(), array('POST'))); + $collection->add('contact_form', new Route('/contact', array( + '_controller' => 'AppBundle:Main:contactForm', + ), array(), array(), '', array(), array('GET', 'POST'))); return $collection; From 131ea23fe23ee4c52b699d9823a298be86fd49a5 Mon Sep 17 00:00:00 2001 From: kenjis Date: Tue, 14 Jul 2015 18:11:36 +0900 Subject: [PATCH 18/19] Fix RST --- create_framework/routing.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/create_framework/routing.rst b/create_framework/routing.rst index 6bbb46f64b8..c9456b02b04 100644 --- a/create_framework/routing.rst +++ b/create_framework/routing.rst @@ -158,7 +158,7 @@ With this knowledge in mind, let's write the new version of our framework:: $response->send(); -There are a few new things in the code:: +There are a few new things in the code: * Route names are used for template names; From 0a4192cba0135400fad9e761cbd945976cad224b Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Wed, 15 Jul 2015 20:32:24 -0400 Subject: [PATCH 19/19] [#5444] Fixing missing public: false declarations and proofing --- reference/dic_tags.rst | 39 +++++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/reference/dic_tags.rst b/reference/dic_tags.rst index 56013b4a52e..f3097e577b5 100644 --- a/reference/dic_tags.rst +++ b/reference/dic_tags.rst @@ -246,10 +246,13 @@ services: services: app.mysql_lock: class: AppBundle\Lock\MysqlLock + public: false app.postgresql_lock: class: AppBundle\Lock\PostgresqlLock + public: false app.sqlite_lock: class: AppBundle\Lock\SqliteLock + public: false .. code-block:: xml @@ -259,26 +262,29 @@ services: xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> - - - + + + .. code-block:: php $container - ->register('app.mysql_lock', 'AppBundle\Lock\MysqlLock') - ->register('app.postgresql_lock', 'AppBundle\Lock\PostgresqlLock') - ->register('app.sqlite_lock', 'AppBundle\Lock\SqliteLock') + ->register('app.mysql_lock', 'AppBundle\Lock\MysqlLock')->setPublic(false) + ->register('app.postgresql_lock', 'AppBundle\Lock\PostgresqlLock')->setPublic(false) + ->register('app.sqlite_lock', 'AppBundle\Lock\SqliteLock')->setPublic(false) ; Instead of dealing with these three services, your application needs a generic -``app.lock`` service. This service must be an alias to any of the other services. -Thanks to the ``auto_alias`` option, you can automatically create that alias -based on the value of a configuration parameter. +``app.lock`` service that will be an alias to one of these services, depending on +some configuration. Thanks to the ``auto_alias`` option, you can automatically create +that alias based on the value of a configuration parameter. -Considering that a configuration parameter called ``database_type`` exists, +Considering that a configuration parameter called ``database_type`` exists. Then, the generic ``app.lock`` service can be defined as follows: .. configuration-block:: @@ -287,14 +293,11 @@ the generic ``app.lock`` service can be defined as follows: services: app.mysql_lock: - class: AppBundle\Lock\MysqlLock - public: false + # ... app.postgresql_lock: - class: AppBundle\Lock\PostgresqlLock - public: false + # ... app.sqlite_lock: - class: AppBundle\Lock\SqliteLock - public: false + # ... app.lock: tags: - { name: auto_alias, format: "app.%database_type%_lock" } @@ -331,8 +334,8 @@ the generic ``app.lock`` service can be defined as follows: ->addTag('auto_alias', array('format' => 'app.%database_type%_lock')) ; -The ``format`` parameter defines the expression used to construct the name of -the service to alias. This expression can use any container parameter (as usual, +The ``format`` option defines the expression used to construct the name of the service +to alias. This expression can use any container parameter (as usual, wrapping their names with ``%`` characters). .. note::