Skip to content

Commit

Permalink
Merge branch '2.1'
Browse files Browse the repository at this point in the history
  • Loading branch information
weaverryan committed Dec 24, 2012
2 parents 1cf2081 + b7d4d46 commit 279a6b9
Show file tree
Hide file tree
Showing 6 changed files with 165 additions and 80 deletions.
9 changes: 9 additions & 0 deletions book/_security-2012-6431.rst.inc
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
.. note::

Since Symfony 2.0.20/2.1.5, the Twig ``render`` tag now takes an absolute url
instead of a controller logical path. This fixes an important security
issue (`CVE-2012-6431`_) reported on the official blog. If your application
uses an older version of Symfony or still uses the previous ``render`` tag
syntax, you should upgrade as soon as possible.

.. _`CVE-2012-6431`: http://symfony.com/blog/security-release-symfony-2-0-20-and-2-1-5-released
95 changes: 41 additions & 54 deletions book/http_cache.rst
Original file line number Diff line number Diff line change
Expand Up @@ -881,17 +881,50 @@ matter), Symfony2 uses the standard ``render`` helper to configure ESI tags:

.. code-block:: jinja
{% render '...:news' with {}, {'standalone': true} %}
{% render url('latest_news', { 'max': 5 }), {'standalone': true} %}
.. code-block:: php
<?php echo $view['actions']->render('...:news', array(), array('standalone' => true)) ?>
<?php echo $view['actions']->render(
$view['router']->generate('latest_news', array('max' => 5), true),
array('standalone' => true)
) ?>
By setting ``standalone`` to ``true``, you tell Symfony2 that the action
should be rendered as an ESI tag. You might be wondering why you would want to
use a helper instead of just writing the ESI tag yourself. That's because
using a helper makes your application work even if there is no gateway cache
installed.
.. include:: /book/_security-2012-6431.rst.inc

The ``render`` tag takes the absolute url to the embedded action. This means
that you need to define a new route to the controller that you're embedding:

.. code-block:: yaml
# app/config/routing.yml
latest_news:
pattern: /esi/latest-news/{max}
defaults: { _controller: AcmeNewsBundle:News:news }
requirements: { max: \d+ }
.. caution::

Unless you want this URL to be accessible to the outside world, you
should use Symfony's firewall to secure it (by allowing access to your
reverse proxy's IP range). See the :ref:`Securing by IP<book-security-securing-ip>`
section of the :doc:`Security Chapter </book/security>` for more information
on how to do this.

.. tip::

The best practice is to mount all your ESI urls on a single prefix (e.g.
``/esi``) of your choice. This has two main advantages. First, it eases
the management of ESI urls as you can easily identify the routes used for ESI.
Second, it eases security management since securing all urls starting
with the same prefix is easier than securing each individual url. See
the above note for more details on securing ESI URLs.

By setting ``standalone`` to ``true`` in the ``render`` Twig tag, you tell
Symfony2 that the action should be rendered as an ESI tag. You might be
wondering why you would want to use a helper instead of just writing the ESI tag
yourself. That's because using a helper makes your application work even if
there is no gateway cache installed.

When standalone is ``false`` (the default), Symfony2 merges the included page
content within the main one before sending the response to the client. But
Expand All @@ -912,7 +945,7 @@ of the master page.

.. code-block:: php
public function newsAction()
public function newsAction($max)
{
// ...
Expand All @@ -922,52 +955,6 @@ of the master page.
With ESI, the full page cache will be valid for 600 seconds, but the news
component cache will only last for 60 seconds.

A requirement of ESI, however, is that the embedded action be accessible
via a URL so the gateway cache can fetch it independently of the rest of
the page. Of course, an action can't be accessed via a URL unless it has
a route that points to it. Symfony2 takes care of this via a generic route
and controller. For the ESI include tag to work properly, you must define
the ``_internal`` route:

.. configuration-block::

.. code-block:: yaml
# app/config/routing.yml
_internal:
resource: "@FrameworkBundle/Resources/config/routing/internal.xml"
prefix: /_internal
.. code-block:: xml
<!-- app/config/routing.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<routes xmlns="http://symfony.com/schema/routing"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/routing http://symfony.com/schema/routing/routing-1.0.xsd">
<import resource="@FrameworkBundle/Resources/config/routing/internal.xml" prefix="/_internal" />
</routes>
.. code-block:: php
// app/config/routing.php
use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\Route;
$collection->addCollection($loader->import('@FrameworkBundle/Resources/config/routing/internal.xml', '/_internal'));
return $collection;
.. tip::

Since this route allows all actions to be accessed via a URL, you might
want to protect it by using the Symfony2 firewall feature (by allowing
access to your reverse proxy's IP range). See the :ref:`Securing by IP<book-security-securing-ip>`
section of the :doc:`Security Chapter </book/security>` for more information
on how to do this.

One great advantage of this caching strategy is that you can make your
application as dynamic as needed and at the same time, hit the application as
little as possible.
Expand Down
24 changes: 14 additions & 10 deletions book/security.rst
Original file line number Diff line number Diff line change
Expand Up @@ -755,14 +755,16 @@ Securing by IP
~~~~~~~~~~~~~~

Certain situations may arise when you may need to restrict access to a given
route based on IP. This is particularly relevant in the case of :ref:`Edge Side Includes<edge-side-includes>`
(ESI), for example, which utilize a route named "_internal". When
ESI is used, the _internal route is required by the gateway cache to enable
different caching options for subsections within a given page. This route
comes with the ^/_internal prefix by default in the standard edition (assuming
you've uncommented those lines from the routing file).
route based on IP. This is particularly relevant in the case of
:ref:`Edge Side Includes<edge-side-includes>` (ESI), for example. When ESI is
enabled, it's recommended to secure access to ESI URLs. Indeed, some ESI may
contain some private contents like the current logged in user's information. To
prevent any direct access to these resources from a web browser by guessing the
URL pattern, the ESI route must be secured to be only visible from the trusted
reverse proxy cache.

Here is an example of how you might secure this route from outside access:
Here is an example of how you might secure all ESI routes that start with a
given prefix, ``/esi``, from outside access:

.. configuration-block::

Expand All @@ -772,22 +774,24 @@ Here is an example of how you might secure this route from outside access:
security:
# ...
access_control:
- { path: ^/_internal, roles: IS_AUTHENTICATED_ANONYMOUSLY, ip: 127.0.0.1 }
- { path: ^/esi, roles: IS_AUTHENTICATED_ANONYMOUSLY, ip: 127.0.0.1 }
.. code-block:: xml
<access-control>
<rule path="^/_internal" role="IS_AUTHENTICATED_ANONYMOUSLY" ip="127.0.0.1" />
<rule path="^/esi" role="IS_AUTHENTICATED_ANONYMOUSLY" ip="127.0.0.1" />
</access-control>
.. code-block:: php
'access_control' => array(
array('path' => '^/_internal', 'role' => 'IS_AUTHENTICATED_ANONYMOUSLY', 'ip' => '127.0.0.1'),
array('path' => '^/esi', 'role' => 'IS_AUTHENTICATED_ANONYMOUSLY', 'ip' => '127.0.0.1'),
),
.. _book-security-securing-channel:

.. include:: /book/_security-2012-6431.rst.inc

Securing by Channel
~~~~~~~~~~~~~~~~~~~

Expand Down
54 changes: 48 additions & 6 deletions book/templating.rst
Original file line number Diff line number Diff line change
Expand Up @@ -623,8 +623,43 @@ The ``recentList`` template is perfectly straightforward:
(e.g. ``/article/*slug*``). This is a bad practice. In the next section,
you'll learn how to do this correctly.

To include the controller, you'll need to refer to it using the standard string
syntax for controllers (i.e. **bundle**:**controller**:**action**):
Even though this controller will only be used internally, you'll need to
create a route that points to the controller:

.. configuration-block::

.. code-block:: yaml
latest_articles:
pattern: /articles/latest/{max}
defaults: { _controller: AcmeArticleBundle:Article:recentArticles }
.. code-block:: xml
<?xml version="1.0" encoding="UTF-8" ?>
<routes xmlns="http://symfony.com/schema/routing"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/routing http://symfony.com/schema/routing/routing-1.0.xsd">
<route id="latest_articles" pattern="/articles/latest/{max}">
<default key="_controller">AcmeArticleBundle:Article:recentArticles</default>
</route>
</routes>
.. code-block:: php
use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\Route;
$collection = new RouteCollection();
$collection->add('latest_articles', new Route('/articles/latest/{max}', array(
'_controller' => 'AcmeArticleBundle:Article:recentArticles',
)));
return $collection;
To include the controller, you'll need to refer to it using an absolute url:

.. configuration-block::

Expand All @@ -634,7 +669,7 @@ syntax for controllers (i.e. **bundle**:**controller**:**action**):

{# ... #}
<div id="sidebar">
{% render "AcmeArticleBundle:Article:recentArticles" with {'max': 3} %}
{% render url('latest_articles', { 'max': 3 }) %}
</div>

.. code-block:: html+php
Expand All @@ -643,9 +678,13 @@ syntax for controllers (i.e. **bundle**:**controller**:**action**):

<!-- ... -->
<div id="sidebar">
<?php echo $view['actions']->render('AcmeArticleBundle:Article:recentArticles', array('max' => 3)) ?>
<?php echo $view['actions']->render(
$view['router']->generate('latest_articles', array('max' => 3), true)
) ?>
</div>

.. include:: /book/_security-2012-6431.rst.inc

Whenever you find that you need a variable or a piece of information that
you don't have access to in a template, consider rendering a controller.
Controllers are fast to execute and promote good code organization and reuse.
Expand All @@ -664,11 +703,14 @@ Symfony2 uses the standard ``render`` helper to configure ``hinclude`` tags:

.. code-block:: jinja
{% render '...:news' with {}, {'standalone': 'js'} %}
{% render url('...'), {'standalone': 'js'} %}
.. code-block:: php
<?php echo $view['actions']->render('...:news', array(), array('standalone' => 'js')) ?>
<?php echo $view['actions']->render(
$view['router']->generate('...'),
array('standalone' => 'js')
) ?>
.. note::

Expand Down
58 changes: 50 additions & 8 deletions quick_tour/the_view.rst
Original file line number Diff line number Diff line change
Expand Up @@ -180,18 +180,57 @@ And what if you want to embed the result of another controller in a template?
That's very useful when working with Ajax, or when the embedded template needs
some variable not available in the main template.

Suppose you've created a ``fancy`` action, and you want to include it inside
the ``index`` template. To do this, use the ``render`` tag:
Suppose you've created a ``fancyAction`` controller method, and you want to "render"
it inside the ``index`` template. First, create a route to your new controller
in one of your application's routing configuration files.

.. configuration-block::

.. code-block:: yaml
# app/config/routing.yml
fancy:
pattern: /included/fancy/{name}/{color}
defaults: { _controller: AcmeDemoBundle:Demo:fancy }
.. code-block:: xml
<!-- app/config/routing.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<routes xmlns="http://symfony.com/schema/routing"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/routing http://symfony.com/schema/routing/routing-1.0.xsd">
<route id="fancy" pattern="/included/fancy/{name}/{color}">
<default key="_controller">AcmeDemoBundle:Demo:fancy</default>
</route>
</routes>
.. code-block:: php
// app/config/routing.php
use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\Route;
$collection = new RouteCollection();
$collection->add('fancy', new Route('/included/fancy/{name}/{color}', array(
'_controller' => 'AcmeDemoBundle:Demo:fancy',
)));
return $collection;
To include the result (e.g. ``HTML``) of the controller, use the ``render`` tag:

.. code-block:: jinja
{# src/Acme/DemoBundle/Resources/views/Demo/index.html.twig #}
{% render "AcmeDemoBundle:Demo:fancy" with {'name': name, 'color': 'green'} %}
{% render url('fancy', { 'name': name, 'color': 'green'}) %}
.. include:: /book/_security-2012-6431.rst.inc

Here, the ``AcmeDemoBundle:Demo:fancy`` string refers to the ``fancy`` action
of the ``Demo`` controller. The arguments (``name`` and ``color``) act like
simulated request variables (as if the ``fancyAction`` were handling a whole
new request) and are made available to the controller::
The ``render`` tag will execute the ``AcmeDemoBundle:Demo:fancy`` controller
and include its result. For example, your new ``fancyAction`` might look
like this::

// src/Acme/DemoBundle/Controller/DemoController.php

Expand All @@ -202,7 +241,10 @@ new request) and are made available to the controller::
// create some object, based on the $color variable
$object = ...;

return $this->render('AcmeDemoBundle:Demo:fancy.html.twig', array('name' => $name, 'object' => $object));
return $this->render('AcmeDemoBundle:Demo:fancy.html.twig', array(
'name' => $name,
'object' => $object
));
}

// ...
Expand Down
5 changes: 3 additions & 2 deletions reference/twig_reference.rst
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,9 @@ Tags
+---------------------------------------------------+-------------------------------------------------------------------+
| Tag Syntax | Usage |
+===================================================+===================================================================+
| ``{% render 'controller' with {parameters} %}`` | This will render the Response Content for the given controller, |
| | more information in :ref:`templating-embedding-controller`. |
| ``{% render url('route', {parameters}) %}`` | This will render the Response Content for the given controller |
| | that the URL points to. For more information, |
| | see :ref:`templating-embedding-controller`. |
+---------------------------------------------------+-------------------------------------------------------------------+
| ``{% form_theme form 'file' %}`` | This will look inside the given file for overridden form blocks, |
| | more information in :doc:`/cookbook/form/form_customization`. |
Expand Down

0 comments on commit 279a6b9

Please sign in to comment.