summaryrefslogtreecommitdiff
path: root/docs
diff options
context:
space:
mode:
authorSteve Piercy <web@stevepiercy.com>2017-05-12 21:24:15 -0700
committerGitHub <noreply@github.com>2017-05-12 21:24:15 -0700
commit5f3b95cf3e1b3005007e739d0e008b21cff2babf (patch)
treec69983404d7b9b8db62640809e6b81c5e46677b4 /docs
parentb5444950d84c507f26764a16021db6e01d0461e3 (diff)
parenta7d60de001ba14023d20d701d9697ad7d8de1dd4 (diff)
downloadpyramid-5f3b95cf3e1b3005007e739d0e008b21cff2babf.tar.gz
pyramid-5f3b95cf3e1b3005007e739d0e008b21cff2babf.tar.bz2
pyramid-5f3b95cf3e1b3005007e739d0e008b21cff2babf.zip
Merge branch 'master' into change-to-localhost
Diffstat (limited to 'docs')
-rw-r--r--docs/api/config.rst1
-rw-r--r--docs/api/csrf.rst23
-rw-r--r--docs/api/interfaces.rst3
-rw-r--r--docs/api/paster.rst6
-rw-r--r--docs/api/session.rst4
-rw-r--r--docs/conf.py3
-rw-r--r--docs/glossary.rst13
-rw-r--r--docs/index.rst1
-rw-r--r--docs/narr/logging.rst2
-rw-r--r--docs/narr/myproject/README.txt2
-rw-r--r--docs/narr/paste.rst12
-rw-r--r--docs/narr/project.rst2
-rw-r--r--docs/narr/security.rst216
-rw-r--r--docs/narr/sessions.rst183
-rw-r--r--docs/narr/startup.rst9
-rw-r--r--docs/narr/templates.rst4
-rw-r--r--docs/quick_tour.rst4
-rw-r--r--docs/quick_tour/logging/README.txt2
-rw-r--r--docs/quick_tour/package/README.txt2
-rw-r--r--docs/quick_tour/sessions/README.txt2
-rw-r--r--docs/quick_tour/sqla_demo/README.txt2
-rw-r--r--docs/quick_tutorial/cookiecutters.rst2
-rw-r--r--docs/quick_tutorial/cookiecutters/README.txt2
-rw-r--r--docs/quick_tutorial/unit_testing.rst2
-rw-r--r--docs/tutorials/modwsgi/index.rst2
-rw-r--r--docs/tutorials/wiki/installation.rst2
-rw-r--r--docs/tutorials/wiki/src/authorization/README.txt2
-rw-r--r--docs/tutorials/wiki/src/basiclayout/README.txt2
-rw-r--r--docs/tutorials/wiki/src/installation/README.txt2
-rw-r--r--docs/tutorials/wiki/src/models/README.txt2
-rw-r--r--docs/tutorials/wiki/src/tests/README.txt2
-rw-r--r--docs/tutorials/wiki/src/views/README.txt2
-rw-r--r--docs/tutorials/wiki2/installation.rst2
-rw-r--r--docs/tutorials/wiki2/src/authentication/README.txt2
-rw-r--r--docs/tutorials/wiki2/src/authentication/tutorial/views/default.py6
-rw-r--r--docs/tutorials/wiki2/src/authorization/README.txt2
-rw-r--r--docs/tutorials/wiki2/src/authorization/tutorial/views/default.py6
-rw-r--r--docs/tutorials/wiki2/src/basiclayout/README.txt2
-rw-r--r--docs/tutorials/wiki2/src/installation/README.txt2
-rw-r--r--docs/tutorials/wiki2/src/models/README.txt2
-rw-r--r--docs/tutorials/wiki2/src/tests/README.txt2
-rw-r--r--docs/tutorials/wiki2/src/tests/tutorial/views/default.py6
-rw-r--r--docs/tutorials/wiki2/src/views/README.txt2
-rw-r--r--docs/tutorials/wiki2/src/views/tutorial/views/default.py6
-rw-r--r--docs/whatsnew-1.9.rst61
45 files changed, 380 insertions, 239 deletions
diff --git a/docs/api/config.rst b/docs/api/config.rst
index c76d3d5ff..a785b64ad 100644
--- a/docs/api/config.rst
+++ b/docs/api/config.rst
@@ -37,6 +37,7 @@
.. automethod:: set_authentication_policy
.. automethod:: set_authorization_policy
.. automethod:: set_default_csrf_options
+ .. automethod:: set_csrf_storage_policy
.. automethod:: set_default_permission
.. automethod:: add_permission
diff --git a/docs/api/csrf.rst b/docs/api/csrf.rst
new file mode 100644
index 000000000..38501546e
--- /dev/null
+++ b/docs/api/csrf.rst
@@ -0,0 +1,23 @@
+.. _csrf_module:
+
+:mod:`pyramid.csrf`
+-------------------
+
+.. automodule:: pyramid.csrf
+
+ .. autoclass:: LegacySessionCSRFStoragePolicy
+ :members:
+
+ .. autoclass:: SessionCSRFStoragePolicy
+ :members:
+
+ .. autoclass:: CookieCSRFStoragePolicy
+ :members:
+
+ .. autofunction:: get_csrf_token
+
+ .. autofunction:: new_csrf_token
+
+ .. autofunction:: check_csrf_origin
+
+ .. autofunction:: check_csrf_token
diff --git a/docs/api/interfaces.rst b/docs/api/interfaces.rst
index a212ba7a9..e542a6be0 100644
--- a/docs/api/interfaces.rst
+++ b/docs/api/interfaces.rst
@@ -44,6 +44,9 @@ Other Interfaces
.. autointerface:: IRoutePregenerator
:members:
+ .. autointerface:: ICSRFStoragePolicy
+ :members:
+
.. autointerface:: ISession
:members:
diff --git a/docs/api/paster.rst b/docs/api/paster.rst
index 27bc81a1f..f0784d0f8 100644
--- a/docs/api/paster.rst
+++ b/docs/api/paster.rst
@@ -7,8 +7,8 @@
.. autofunction:: bootstrap
- .. autofunction:: get_app(config_uri, name=None, options=None)
+ .. autofunction:: get_app
- .. autofunction:: get_appsettings(config_uri, name=None, options=None)
+ .. autofunction:: get_appsettings
- .. autofunction:: setup_logging(config_uri, global_conf=None)
+ .. autofunction:: setup_logging
diff --git a/docs/api/session.rst b/docs/api/session.rst
index 56c4f52d7..53bae7c52 100644
--- a/docs/api/session.rst
+++ b/docs/api/session.rst
@@ -9,10 +9,6 @@
.. autofunction:: signed_deserialize
- .. autofunction:: check_csrf_origin
-
- .. autofunction:: check_csrf_token
-
.. autofunction:: SignedCookieSessionFactory
.. autofunction:: UnencryptedCookieSessionFactoryConfig
diff --git a/docs/conf.py b/docs/conf.py
index df58064e5..e63019c63 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -67,9 +67,10 @@ intersphinx_mapping = {
'cookiecutter': ('https://cookiecutter.readthedocs.io/en/latest/', None),
'deform': ('http://docs.pylonsproject.org/projects/deform/en/latest', None),
'jinja2': ('http://docs.pylonsproject.org/projects/pyramid-jinja2/en/latest/', None),
+ 'plaster': ('http://docs.pylonsproject.org/projects/plaster/en/latest/', None),
'pylonswebframework': ('http://docs.pylonsproject.org/projects/pylons-webframework/en/latest/', None),
'python': ('https://docs.python.org/3', None),
- 'pytest': ('http://pytest.org/latest/', None),
+ 'pytest': ('https://pytest.org/en/latest/', None),
'sphinx': ('http://www.sphinx-doc.org/en/latest', None),
'sqla': ('http://docs.sqlalchemy.org/en/latest', None),
'tm': ('http://docs.pylonsproject.org/projects/pyramid-tm/en/latest/', None),
diff --git a/docs/glossary.rst b/docs/glossary.rst
index 0a46fac3b..2e5276554 100644
--- a/docs/glossary.rst
+++ b/docs/glossary.rst
@@ -366,6 +366,14 @@ Glossary
:term:`WSGI` components together declaratively within an ``.ini``
file. It was developed by Ian Bicking.
+ plaster
+ `plaster <http://docs.pylonsproject.org/projects/plaster/en/latest/>`_ is
+ a library used by :app:`Pyramid` which acts as an abstraction between
+ command-line scripts and the file format used to load the :term:`WSGI`
+ components and application settings. By default :app:`Pyramid` ships
+ with the ``plaster_pastedeploy`` library installed which provides
+ integrated support for loading a :term:`PasteDeploy` INI file.
+
Chameleon
`chameleon <https://chameleon.readthedocs.org/en/latest/>`_ is an
attribute language template compiler which supports the :term:`ZPT`
@@ -891,6 +899,11 @@ Glossary
:meth:`pyramid.config.Configurator.set_session_factory` for more
information.
+ CSRF storage policy
+ A utility that implements :class:`pyramid.interfaces.ICSRFStoragePolicy`
+ which is responsible for allocating CSRF tokens to a user and verifying
+ that a provided token is acceptable.
+
Mako
`Mako <http://www.makotemplates.org/>`_ is a template language
which refines the familiar ideas of componentized layout and inheritance
diff --git a/docs/index.rst b/docs/index.rst
index ed5b458ea..7d3393548 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -185,6 +185,7 @@ Change History
.. toctree::
:maxdepth: 1
+ whatsnew-1.9
whatsnew-1.8
whatsnew-1.7
whatsnew-1.6
diff --git a/docs/narr/logging.rst b/docs/narr/logging.rst
index 87682158b..9cc5b4ed8 100644
--- a/docs/narr/logging.rst
+++ b/docs/narr/logging.rst
@@ -16,7 +16,7 @@ to send log messages to loggers that you've configured.
cookiecutter which does not create these files, the configuration information in
this chapter may not be applicable.
-.. index:
+.. index::
pair: settings; logging
pair: .ini; logging
pair: logging; configuration
diff --git a/docs/narr/myproject/README.txt b/docs/narr/myproject/README.txt
index 41ef0ff91..2ffc0acba 100644
--- a/docs/narr/myproject/README.txt
+++ b/docs/narr/myproject/README.txt
@@ -1,5 +1,5 @@
MyProject
-===============================
+=========
Getting Started
---------------
diff --git a/docs/narr/paste.rst b/docs/narr/paste.rst
index 2d4e76e24..26cb1bfa5 100644
--- a/docs/narr/paste.rst
+++ b/docs/narr/paste.rst
@@ -26,12 +26,7 @@ documentation, see http://pythonpaste.org/deploy/.
PasteDeploy
-----------
-:term:`PasteDeploy` is the system that Pyramid uses to allow :term:`deployment
-settings` to be specified using an ``.ini`` configuration file format. It also
-allows the ``pserve`` command to work. Its configuration format provides a
-convenient place to define application :term:`deployment settings` and WSGI
-server settings, and its server runner allows you to stop and start a Pyramid
-application easily.
+:term:`plaster` is the system that Pyramid uses to load settings from configuration files. The most common format for these files is an ``.ini`` format structured in a way defined by :term:`PasteDeploy`. The format supports mechanisms to define WSGI app :term:`deployment settings`, WSGI server settings and logging. This allows the ``pserve`` command to work, allowing you to stop and start a Pyramid application easily.
.. _pastedeploy_entry_points:
@@ -96,3 +91,8 @@ applications, servers, and :term:`middleware` defined within the configuration
file. The values in a ``[DEFAULT]`` section will be passed to your
application's ``main`` function as ``global_config`` (see the reference to the
``main`` function in :ref:`init_py`).
+
+Alternative Configuration File Formats
+--------------------------------------
+
+It is possible to use different file formats with :app:`Pyramid` if you do not like :term:`PasteDeploy`. Under the hood all command-line scripts such as ``pserve`` and ``pshell`` pass the ``config_uri`` (e.g. ``development.ini`` or ``production.ini``) to the :term:`plaster` library which performs a lookup for an appropriate parser. For ``.ini`` files it uses PasteDeploy but you can register your own configuration formats that plaster will find instead.
diff --git a/docs/narr/project.rst b/docs/narr/project.rst
index ad27290f3..a150afc6b 100644
--- a/docs/narr/project.rst
+++ b/docs/narr/project.rst
@@ -94,7 +94,7 @@ If prompted for the first item, accept the default ``yes`` by hitting return.
You've cloned ~/.cookiecutters/pyramid-cookiecutter-starter before.
Is it okay to delete and re-clone it? [yes]: yes
project_name [Pyramid Scaffold]: myproject
- repo_name [scaffold]: myproject
+ repo_name [myproject]: myproject
Select template_language:
1 - jinja2
2 - chameleon
diff --git a/docs/narr/security.rst b/docs/narr/security.rst
index 77e7fd707..3a6bfa5e5 100644
--- a/docs/narr/security.rst
+++ b/docs/narr/security.rst
@@ -146,7 +146,7 @@ For example, the following view declaration protects the view named
# config is an instance of pyramid.config.Configurator
config.add_view('mypackage.views.blog_entry_add_view',
- name='add_entry.html',
+ name='add_entry.html',
context='mypackage.resources.Blog',
permission='add')
@@ -725,7 +725,7 @@ object that implements the following interface:
""" Return ``True`` if any of the ``principals`` is allowed the
``permission`` in the current ``context``, else return ``False``
"""
-
+
def principals_allowed_by_permission(self, context, permission):
""" Return a set of principal identifiers allowed by the
``permission`` in ``context``. This behavior is optional; if you
@@ -765,3 +765,215 @@ which would allow the attacker to control the content of the payload. Re-using
a secret across two different subsystems might drop the security of signing to
zero. Keys should not be re-used across different contexts where an attacker
has the possibility of providing a chosen plaintext.
+
+.. index::
+ single: preventing cross-site request forgery attacks
+ single: cross-site request forgery attacks, prevention
+
+Preventing Cross-Site Request Forgery Attacks
+---------------------------------------------
+
+`Cross-site request forgery
+<https://en.wikipedia.org/wiki/Cross-site_request_forgery>`_ attacks are a
+phenomenon whereby a user who is logged in to your website might inadvertantly
+load a URL because it is linked from, or embedded in, an attacker's website.
+If the URL is one that may modify or delete data, the consequences can be dire.
+
+You can avoid most of these attacks by issuing a unique token to the browser
+and then requiring that it be present in all potentially unsafe requests.
+:app:`Pyramid` provides facilities to create and check CSRF tokens.
+
+By default :app:`Pyramid` comes with a session-based CSRF implementation
+:class:`pyramid.csrf.SessionCSRFStoragePolicy`. To use it, you must first enable
+a :term:`session factory` as described in
+:ref:`using_the_default_session_factory` or
+:ref:`using_alternate_session_factories`. Alternatively, you can use
+a cookie-based implementation :class:`pyramid.csrf.CookieCSRFStoragePolicy` which gives
+some additional flexibility as it does not require a session for each user.
+You can also define your own implementation of
+:class:`pyramid.interfaces.ICSRFStoragePolicy` and register it with the
+:meth:`pyramid.config.Configurator.set_csrf_storage_policy` directive.
+
+For example:
+
+.. code-block:: python
+
+ from pyramid.config import Configurator
+
+ config = Configurator()
+ config.set_csrf_storage_policy(MyCustomCSRFPolicy())
+
+.. index::
+ single: csrf.get_csrf_token
+
+Using the ``csrf.get_csrf_token`` Method
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+To get the current CSRF token, use the
+:data:`pyramid.csrf.get_csrf_token` method.
+
+.. code-block:: python
+
+ from pyramid.csrf import get_csrf_token
+ token = get_csrf_token(request)
+
+The ``get_csrf_token()`` method accepts a single argument: the request. It
+returns a CSRF *token* string. If ``get_csrf_token()`` or ``new_csrf_token()``
+was invoked previously for this user, then the existing token will be returned.
+If no CSRF token previously existed for this user, then a new token will be set
+into the session and returned. The newly created token will be opaque and
+randomized.
+
+.. _get_csrf_token_in_templates:
+
+Using the ``get_csrf_token`` global in templates
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Templates have a ``get_csrf_token()`` method inserted into their globals, which
+allows you to get the current token without modifying the view code. This
+method takes no arguments and returns a CSRF token string. You can use the
+returned token as the value of a hidden field in a form that posts to a method
+that requires elevated privileges, or supply it as a request header in AJAX
+requests.
+
+For example, include the CSRF token as a hidden field:
+
+.. code-block:: html
+
+ <form method="post" action="/myview">
+ <input type="hidden" name="csrf_token" value="${get_csrf_token()}">
+ <input type="submit" value="Delete Everything">
+ </form>
+
+Or include it as a header in a jQuery AJAX request:
+
+.. code-block:: javascript
+
+ var csrfToken = "${get_csrf_token()}";
+ $.ajax({
+ type: "POST",
+ url: "/myview",
+ headers: { 'X-CSRF-Token': csrfToken }
+ }).done(function() {
+ alert("Deleted");
+ });
+
+The handler for the URL that receives the request should then require that the
+correct CSRF token is supplied.
+
+.. index::
+ single: csrf.new_csrf_token
+
+Using the ``csrf.new_csrf_token`` Method
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+To explicitly create a new CSRF token, use the ``csrf.new_csrf_token()``
+method. This differs only from ``csrf.get_csrf_token()`` inasmuch as it
+clears any existing CSRF token, creates a new CSRF token, sets the token into
+the user, and returns the token.
+
+.. code-block:: python
+
+ from pyramid.csrf import get_csrf_token
+ token = new_csrf_token()
+
+.. note::
+
+ It is not possible to force a new CSRF token from a template. If you
+ want to regenerate your CSRF token then do it in the view code and return
+ the new token as part of the context.
+
+Checking CSRF Tokens Manually
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+In request handling code, you can check the presence and validity of a CSRF
+token with :func:`pyramid.csrf.check_csrf_token`. If the token is valid, it
+will return ``True``, otherwise it will raise ``HTTPBadRequest``. Optionally,
+you can specify ``raises=False`` to have the check return ``False`` instead of
+raising an exception.
+
+By default, it checks for a POST parameter named ``csrf_token`` or a header
+named ``X-CSRF-Token``.
+
+.. code-block:: python
+
+ from pyramid.csrf import check_csrf_token
+
+ def myview(request):
+ # Require CSRF Token
+ check_csrf_token(request)
+
+ # ...
+
+.. _auto_csrf_checking:
+
+Checking CSRF Tokens Automatically
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. versionadded:: 1.7
+
+:app:`Pyramid` supports automatically checking CSRF tokens on requests with an
+unsafe method as defined by RFC2616. Any other request may be checked manually.
+This feature can be turned on globally for an application using the
+:meth:`pyramid.config.Configurator.set_default_csrf_options` directive.
+For example:
+
+.. code-block:: python
+
+ from pyramid.config import Configurator
+
+ config = Configurator()
+ config.set_default_csrf_options(require_csrf=True)
+
+CSRF checking may be explicitly enabled or disabled on a per-view basis using
+the ``require_csrf`` view option. A value of ``True`` or ``False`` will
+override the default set by ``set_default_csrf_options``. For example:
+
+.. code-block:: python
+
+ @view_config(route_name='hello', require_csrf=False)
+ def myview(request):
+ # ...
+
+When CSRF checking is active, the token and header used to find the
+supplied CSRF token will be ``csrf_token`` and ``X-CSRF-Token``, respectively,
+unless otherwise overridden by ``set_default_csrf_options``. The token is
+checked against the value in ``request.POST`` which is the submitted form body.
+If this value is not present, then the header will be checked.
+
+In addition to token based CSRF checks, if the request is using HTTPS then the
+automatic CSRF checking will also check the referrer of the request to ensure
+that it matches one of the trusted origins. By default the only trusted origin
+is the current host, however additional origins may be configured by setting
+``pyramid.csrf_trusted_origins`` to a list of domain names (and ports if they
+are non-standard). If a host in the list of domains starts with a ``.`` then
+that will allow all subdomains as well as the domain without the ``.``.
+
+If CSRF checks fail then a :class:`pyramid.exceptions.BadCSRFToken` or
+:class:`pyramid.exceptions.BadCSRFOrigin` exception will be raised. This
+exception may be caught and handled by an :term:`exception view` but, by
+default, will result in a ``400 Bad Request`` response being sent to the
+client.
+
+Checking CSRF Tokens with a View Predicate
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. deprecated:: 1.7
+ Use the ``require_csrf`` option or read :ref:`auto_csrf_checking` instead
+ to have :class:`pyramid.exceptions.BadCSRFToken` exceptions raised.
+
+A convenient way to require a valid CSRF token for a particular view is to
+include ``check_csrf=True`` as a view predicate. See
+:meth:`pyramid.config.Configurator.add_view`.
+
+.. code-block:: python
+
+ @view_config(request_method='POST', check_csrf=True, ...)
+ def myview(request):
+ ...
+
+.. note::
+ A mismatch of a CSRF token is treated like any other predicate miss, and the
+ predicate system, when it doesn't find a view, raises ``HTTPNotFound``
+ instead of ``HTTPBadRequest``, so ``check_csrf=True`` behavior is different
+ from calling :func:`pyramid.csrf.check_csrf_token`.
diff --git a/docs/narr/sessions.rst b/docs/narr/sessions.rst
index 5b24201a9..7e2469d54 100644
--- a/docs/narr/sessions.rst
+++ b/docs/narr/sessions.rst
@@ -12,8 +12,7 @@ application.
This chapter describes how to configure sessions, what session implementations
:app:`Pyramid` provides out of the box, how to store and retrieve data from
-sessions, and two session-specific features: flash messages, and cross-site
-request forgery attack prevention.
+sessions, and a session-specific feature: flash messages.
.. index::
single: session factory (default)
@@ -316,183 +315,3 @@ flash storage.
['info message']
>>> request.session.peek_flash()
[]
-
-.. index::
- single: preventing cross-site request forgery attacks
- single: cross-site request forgery attacks, prevention
-
-Preventing Cross-Site Request Forgery Attacks
----------------------------------------------
-
-`Cross-site request forgery
-<https://en.wikipedia.org/wiki/Cross-site_request_forgery>`_ attacks are a
-phenomenon whereby a user who is logged in to your website might inadvertantly
-load a URL because it is linked from, or embedded in, an attacker's website.
-If the URL is one that may modify or delete data, the consequences can be dire.
-
-You can avoid most of these attacks by issuing a unique token to the browser
-and then requiring that it be present in all potentially unsafe requests.
-:app:`Pyramid` sessions provide facilities to create and check CSRF tokens.
-
-To use CSRF tokens, you must first enable a :term:`session factory` as
-described in :ref:`using_the_default_session_factory` or
-:ref:`using_alternate_session_factories`.
-
-.. index::
- single: session.get_csrf_token
-
-Using the ``session.get_csrf_token`` Method
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-To get the current CSRF token from the session, use the
-``session.get_csrf_token()`` method.
-
-.. code-block:: python
-
- token = request.session.get_csrf_token()
-
-The ``session.get_csrf_token()`` method accepts no arguments. It returns a
-CSRF *token* string. If ``session.get_csrf_token()`` or
-``session.new_csrf_token()`` was invoked previously for this session, then the
-existing token will be returned. If no CSRF token previously existed for this
-session, then a new token will be set into the session and returned. The newly
-created token will be opaque and randomized.
-
-You can use the returned token as the value of a hidden field in a form that
-posts to a method that requires elevated privileges, or supply it as a request
-header in AJAX requests.
-
-For example, include the CSRF token as a hidden field:
-
-.. code-block:: html
-
- <form method="post" action="/myview">
- <input type="hidden" name="csrf_token" value="${request.session.get_csrf_token()}">
- <input type="submit" value="Delete Everything">
- </form>
-
-Or include it as a header in a jQuery AJAX request:
-
-.. code-block:: javascript
-
- var csrfToken = ${request.session.get_csrf_token()};
- $.ajax({
- type: "POST",
- url: "/myview",
- headers: { 'X-CSRF-Token': csrfToken }
- }).done(function() {
- alert("Deleted");
- });
-
-The handler for the URL that receives the request should then require that the
-correct CSRF token is supplied.
-
-.. index::
- single: session.new_csrf_token
-
-Using the ``session.new_csrf_token`` Method
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-To explicitly create a new CSRF token, use the ``session.new_csrf_token()``
-method. This differs only from ``session.get_csrf_token()`` inasmuch as it
-clears any existing CSRF token, creates a new CSRF token, sets the token into
-the session, and returns the token.
-
-.. code-block:: python
-
- token = request.session.new_csrf_token()
-
-Checking CSRF Tokens Manually
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-In request handling code, you can check the presence and validity of a CSRF
-token with :func:`pyramid.session.check_csrf_token`. If the token is valid, it
-will return ``True``, otherwise it will raise ``HTTPBadRequest``. Optionally,
-you can specify ``raises=False`` to have the check return ``False`` instead of
-raising an exception.
-
-By default, it checks for a POST parameter named ``csrf_token`` or a header
-named ``X-CSRF-Token``.
-
-.. code-block:: python
-
- from pyramid.session import check_csrf_token
-
- def myview(request):
- # Require CSRF Token
- check_csrf_token(request)
-
- # ...
-
-.. _auto_csrf_checking:
-
-Checking CSRF Tokens Automatically
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-.. versionadded:: 1.7
-
-:app:`Pyramid` supports automatically checking CSRF tokens on requests with an
-unsafe method as defined by RFC2616. Any other request may be checked manually.
-This feature can be turned on globally for an application using the
-:meth:`pyramid.config.Configurator.set_default_csrf_options` directive.
-For example:
-
-.. code-block:: python
-
- from pyramid.config import Configurator
-
- config = Configurator()
- config.set_default_csrf_options(require_csrf=True)
-
-CSRF checking may be explicitly enabled or disabled on a per-view basis using
-the ``require_csrf`` view option. A value of ``True`` or ``False`` will
-override the default set by ``set_default_csrf_options``. For example:
-
-.. code-block:: python
-
- @view_config(route_name='hello', require_csrf=False)
- def myview(request):
- # ...
-
-When CSRF checking is active, the token and header used to find the
-supplied CSRF token will be ``csrf_token`` and ``X-CSRF-Token``, respectively,
-unless otherwise overridden by ``set_default_csrf_options``. The token is
-checked against the value in ``request.POST`` which is the submitted form body.
-If this value is not present, then the header will be checked.
-
-In addition to token based CSRF checks, if the request is using HTTPS then the
-automatic CSRF checking will also check the referrer of the request to ensure
-that it matches one of the trusted origins. By default the only trusted origin
-is the current host, however additional origins may be configured by setting
-``pyramid.csrf_trusted_origins`` to a list of domain names (and ports if they
-are non standard). If a host in the list of domains starts with a ``.`` then
-that will allow all subdomains as well as the domain without the ``.``.
-
-If CSRF checks fail then a :class:`pyramid.exceptions.BadCSRFToken` or
-:class:`pyramid.exceptions.BadCSRFOrigin` exception will be raised. This
-exception may be caught and handled by an :term:`exception view` but, by
-default, will result in a ``400 Bad Request`` response being sent to the
-client.
-
-Checking CSRF Tokens with a View Predicate
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-.. deprecated:: 1.7
- Use the ``require_csrf`` option or read :ref:`auto_csrf_checking` instead
- to have :class:`pyramid.exceptions.BadCSRFToken` exceptions raised.
-
-A convenient way to require a valid CSRF token for a particular view is to
-include ``check_csrf=True`` as a view predicate. See
-:meth:`pyramid.config.Configurator.add_view`.
-
-.. code-block:: python
-
- @view_config(request_method='POST', check_csrf=True, ...)
- def myview(request):
- ...
-
-.. note::
- A mismatch of a CSRF token is treated like any other predicate miss, and the
- predicate system, when it doesn't find a view, raises ``HTTPNotFound``
- instead of ``HTTPBadRequest``, so ``check_csrf=True`` behavior is different
- from calling :func:`pyramid.session.check_csrf_token`.
diff --git a/docs/narr/startup.rst b/docs/narr/startup.rst
index 27a2f1919..5e7c7c871 100644
--- a/docs/narr/startup.rst
+++ b/docs/narr/startup.rst
@@ -38,7 +38,14 @@ Here's a high-level time-ordered overview of what happens when you press
begin to run and serve an application using the information contained
within the ``development.ini`` file.
-#. The framework finds a section named either ``[app:main]``,
+#. ``pserve`` passes the ``development.ini`` path to :term:`plaster` which
+ finds an available configuration loader that recognizes the ``ini`` format.
+
+#. :term:`plaster` finds the ``plaster_pastedeploy`` library which binds
+ the :term:`PasteDeploy` library and returns a parser that can understand
+ the format.
+
+#. The :term:`PasteDeploy` finds a section named either ``[app:main]``,
``[pipeline:main]``, or ``[composite:main]`` in the ``.ini`` file. This
section represents the configuration of a :term:`WSGI` application that will
be served. If you're using a simple application (e.g., ``[app:main]``), the
diff --git a/docs/narr/templates.rst b/docs/narr/templates.rst
index 6b3b5fcce..4eadbd2f0 100644
--- a/docs/narr/templates.rst
+++ b/docs/narr/templates.rst
@@ -228,6 +228,10 @@ These values are provided to the template:
provided if the template is rendered as the result of a ``renderer=``
argument to the view configuration being used.
+``get_csrf_token()``
+ A convenience function to access the current CSRF token. See
+ :ref:`get_csrf_token_in_templates` for more information.
+
``renderer_name``
The renderer name used to perform the rendering, e.g.,
``mypackage:templates/foo.pt``.
diff --git a/docs/quick_tour.rst b/docs/quick_tour.rst
index cd1598a1c..de896939a 100644
--- a/docs/quick_tour.rst
+++ b/docs/quick_tour.rst
@@ -519,7 +519,7 @@ If prompted for the first item, accept the default ``yes`` by hitting return.
You've cloned ~/.cookiecutters/pyramid-cookiecutter-starter before.
Is it okay to delete and re-clone it? [yes]: yes
project_name [Pyramid Scaffold]: hello_world
- repo_name [scaffold]: hello_world
+ repo_name [hello_world]: hello_world
Select template_language:
1 - jinja2
2 - chameleon
@@ -875,7 +875,7 @@ If prompted for the first item, accept the default ``yes`` by hitting return.
You've cloned ~/.cookiecutters/pyramid-cookiecutter-alchemy before.
Is it okay to delete and re-clone it? [yes]: yes
project_name [Pyramid Scaffold]: sqla_demo
- repo_name [scaffold]: sqla_demo
+ repo_name [sqla_demo]: sqla_demo
We then run through the following commands as before.
diff --git a/docs/quick_tour/logging/README.txt b/docs/quick_tour/logging/README.txt
index fb7bde0a7..ff70a1354 100644
--- a/docs/quick_tour/logging/README.txt
+++ b/docs/quick_tour/logging/README.txt
@@ -1,5 +1,5 @@
hello_world
-===============================
+===========
Getting Started
---------------
diff --git a/docs/quick_tour/package/README.txt b/docs/quick_tour/package/README.txt
index fb7bde0a7..ff70a1354 100644
--- a/docs/quick_tour/package/README.txt
+++ b/docs/quick_tour/package/README.txt
@@ -1,5 +1,5 @@
hello_world
-===============================
+===========
Getting Started
---------------
diff --git a/docs/quick_tour/sessions/README.txt b/docs/quick_tour/sessions/README.txt
index fb7bde0a7..ff70a1354 100644
--- a/docs/quick_tour/sessions/README.txt
+++ b/docs/quick_tour/sessions/README.txt
@@ -1,5 +1,5 @@
hello_world
-===============================
+===========
Getting Started
---------------
diff --git a/docs/quick_tour/sqla_demo/README.txt b/docs/quick_tour/sqla_demo/README.txt
index 1659e47ab..27bbff5a7 100644
--- a/docs/quick_tour/sqla_demo/README.txt
+++ b/docs/quick_tour/sqla_demo/README.txt
@@ -1,5 +1,5 @@
sqla_demo
-===============================
+=========
Getting Started
---------------
diff --git a/docs/quick_tutorial/cookiecutters.rst b/docs/quick_tutorial/cookiecutters.rst
index edfd8cd69..337a5c535 100644
--- a/docs/quick_tutorial/cookiecutters.rst
+++ b/docs/quick_tutorial/cookiecutters.rst
@@ -37,7 +37,7 @@ Steps
You've cloned ~/.cookiecutters/pyramid-cookiecutter-starter before.
Is it okay to delete and re-clone it? [yes]: yes
project_name [Pyramid Scaffold]: cc_starter
- repo_name [scaffold]: cc_starter
+ repo_name [cc_starter]: cc_starter
Select template_language:
1 - jinja2
2 - chameleon
diff --git a/docs/quick_tutorial/cookiecutters/README.txt b/docs/quick_tutorial/cookiecutters/README.txt
index 4b1f31bf3..55c5dcec6 100644
--- a/docs/quick_tutorial/cookiecutters/README.txt
+++ b/docs/quick_tutorial/cookiecutters/README.txt
@@ -1,5 +1,5 @@
cc_starter
-===============================
+==========
Getting Started
---------------
diff --git a/docs/quick_tutorial/unit_testing.rst b/docs/quick_tutorial/unit_testing.rst
index 7c85d5289..002c62fde 100644
--- a/docs/quick_tutorial/unit_testing.rst
+++ b/docs/quick_tutorial/unit_testing.rst
@@ -29,7 +29,7 @@ broken the code. As you're writing your code, you might find this more
convenient than changing to your browser constantly and clicking reload.
We'll also leave discussion of `pytest-cov
-<http://pytest-cov.readthedocs.org/en/latest/>`_ for another section.
+<http://pytest-cov.readthedocs.io/en/latest/>`_ for another section.
Objectives
diff --git a/docs/tutorials/modwsgi/index.rst b/docs/tutorials/modwsgi/index.rst
index 690266586..170f2ebc8 100644
--- a/docs/tutorials/modwsgi/index.rst
+++ b/docs/tutorials/modwsgi/index.rst
@@ -48,7 +48,7 @@ specific path information for commands and files.
You've cloned ~/.cookiecutters/pyramid-cookiecutter-starter before.
Is it okay to delete and re-clone it? [yes]: yes
project_name [Pyramid Scaffold]: myproject
- repo_name [scaffold]: myproject
+ repo_name [myproject]: myproject
Select template_language:
1 - jinja2
2 - chameleon
diff --git a/docs/tutorials/wiki/installation.rst b/docs/tutorials/wiki/installation.rst
index 6be826395..de057b1cc 100644
--- a/docs/tutorials/wiki/installation.rst
+++ b/docs/tutorials/wiki/installation.rst
@@ -50,7 +50,7 @@ If prompted for the first item, accept the default ``yes`` by hitting return.
You've cloned ~/.cookiecutters/pyramid-cookiecutter-zodb before.
Is it okay to delete and re-clone it? [yes]: yes
project_name [Pyramid Scaffold]: myproj
- repo_name [scaffold]: tutorial
+ repo_name [myproj]: tutorial
Change directory into your newly created project
------------------------------------------------
diff --git a/docs/tutorials/wiki/src/authorization/README.txt b/docs/tutorials/wiki/src/authorization/README.txt
index 98683bf8c..5ec53bf9d 100644
--- a/docs/tutorials/wiki/src/authorization/README.txt
+++ b/docs/tutorials/wiki/src/authorization/README.txt
@@ -1,5 +1,5 @@
myproj
-===============================
+======
Getting Started
---------------
diff --git a/docs/tutorials/wiki/src/basiclayout/README.txt b/docs/tutorials/wiki/src/basiclayout/README.txt
index 98683bf8c..5ec53bf9d 100644
--- a/docs/tutorials/wiki/src/basiclayout/README.txt
+++ b/docs/tutorials/wiki/src/basiclayout/README.txt
@@ -1,5 +1,5 @@
myproj
-===============================
+======
Getting Started
---------------
diff --git a/docs/tutorials/wiki/src/installation/README.txt b/docs/tutorials/wiki/src/installation/README.txt
index 98683bf8c..5ec53bf9d 100644
--- a/docs/tutorials/wiki/src/installation/README.txt
+++ b/docs/tutorials/wiki/src/installation/README.txt
@@ -1,5 +1,5 @@
myproj
-===============================
+======
Getting Started
---------------
diff --git a/docs/tutorials/wiki/src/models/README.txt b/docs/tutorials/wiki/src/models/README.txt
index 98683bf8c..5ec53bf9d 100644
--- a/docs/tutorials/wiki/src/models/README.txt
+++ b/docs/tutorials/wiki/src/models/README.txt
@@ -1,5 +1,5 @@
myproj
-===============================
+======
Getting Started
---------------
diff --git a/docs/tutorials/wiki/src/tests/README.txt b/docs/tutorials/wiki/src/tests/README.txt
index 98683bf8c..5ec53bf9d 100644
--- a/docs/tutorials/wiki/src/tests/README.txt
+++ b/docs/tutorials/wiki/src/tests/README.txt
@@ -1,5 +1,5 @@
myproj
-===============================
+======
Getting Started
---------------
diff --git a/docs/tutorials/wiki/src/views/README.txt b/docs/tutorials/wiki/src/views/README.txt
index 98683bf8c..5ec53bf9d 100644
--- a/docs/tutorials/wiki/src/views/README.txt
+++ b/docs/tutorials/wiki/src/views/README.txt
@@ -1,5 +1,5 @@
myproj
-===============================
+======
Getting Started
---------------
diff --git a/docs/tutorials/wiki2/installation.rst b/docs/tutorials/wiki2/installation.rst
index 9eeb1711d..c61d4360d 100644
--- a/docs/tutorials/wiki2/installation.rst
+++ b/docs/tutorials/wiki2/installation.rst
@@ -62,7 +62,7 @@ If prompted for the first item, accept the default ``yes`` by hitting return.
You've cloned ~/.cookiecutters/pyramid-cookiecutter-alchemy before.
Is it okay to delete and re-clone it? [yes]: yes
project_name [Pyramid Scaffold]: myproj
- repo_name [scaffold]: tutorial
+ repo_name [myproj]: tutorial
Change directory into your newly created project
------------------------------------------------
diff --git a/docs/tutorials/wiki2/src/authentication/README.txt b/docs/tutorials/wiki2/src/authentication/README.txt
index 5e21b8aa4..81102a869 100644
--- a/docs/tutorials/wiki2/src/authentication/README.txt
+++ b/docs/tutorials/wiki2/src/authentication/README.txt
@@ -1,5 +1,5 @@
myproj
-===============================
+======
Getting Started
---------------
diff --git a/docs/tutorials/wiki2/src/authentication/tutorial/views/default.py b/docs/tutorials/wiki2/src/authentication/tutorial/views/default.py
index 1b071434c..2d058d874 100644
--- a/docs/tutorials/wiki2/src/authentication/tutorial/views/default.py
+++ b/docs/tutorials/wiki2/src/authentication/tutorial/views/default.py
@@ -1,4 +1,4 @@
-import cgi
+from pyramid.compat import escape
import re
from docutils.core import publish_parts
@@ -32,10 +32,10 @@ def view_page(request):
exists = request.dbsession.query(Page).filter_by(name=word).all()
if exists:
view_url = request.route_url('view_page', pagename=word)
- return '<a href="%s">%s</a>' % (view_url, cgi.escape(word))
+ return '<a href="%s">%s</a>' % (view_url, escape(word))
else:
add_url = request.route_url('add_page', pagename=word)
- return '<a href="%s">%s</a>' % (add_url, cgi.escape(word))
+ return '<a href="%s">%s</a>' % (add_url, escape(word))
content = publish_parts(page.data, writer_name='html')['html_body']
content = wikiwords.sub(add_link, content)
diff --git a/docs/tutorials/wiki2/src/authorization/README.txt b/docs/tutorials/wiki2/src/authorization/README.txt
index 5e21b8aa4..81102a869 100644
--- a/docs/tutorials/wiki2/src/authorization/README.txt
+++ b/docs/tutorials/wiki2/src/authorization/README.txt
@@ -1,5 +1,5 @@
myproj
-===============================
+======
Getting Started
---------------
diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/views/default.py b/docs/tutorials/wiki2/src/authorization/tutorial/views/default.py
index 9358993ea..65c12ed3b 100644
--- a/docs/tutorials/wiki2/src/authorization/tutorial/views/default.py
+++ b/docs/tutorials/wiki2/src/authorization/tutorial/views/default.py
@@ -1,4 +1,4 @@
-import cgi
+from pyramid.compat import escape
import re
from docutils.core import publish_parts
@@ -25,10 +25,10 @@ def view_page(request):
exists = request.dbsession.query(Page).filter_by(name=word).all()
if exists:
view_url = request.route_url('view_page', pagename=word)
- return '<a href="%s">%s</a>' % (view_url, cgi.escape(word))
+ return '<a href="%s">%s</a>' % (view_url, escape(word))
else:
add_url = request.route_url('add_page', pagename=word)
- return '<a href="%s">%s</a>' % (add_url, cgi.escape(word))
+ return '<a href="%s">%s</a>' % (add_url, escape(word))
content = publish_parts(page.data, writer_name='html')['html_body']
content = wikiwords.sub(add_link, content)
diff --git a/docs/tutorials/wiki2/src/basiclayout/README.txt b/docs/tutorials/wiki2/src/basiclayout/README.txt
index 5e21b8aa4..81102a869 100644
--- a/docs/tutorials/wiki2/src/basiclayout/README.txt
+++ b/docs/tutorials/wiki2/src/basiclayout/README.txt
@@ -1,5 +1,5 @@
myproj
-===============================
+======
Getting Started
---------------
diff --git a/docs/tutorials/wiki2/src/installation/README.txt b/docs/tutorials/wiki2/src/installation/README.txt
index 5e21b8aa4..81102a869 100644
--- a/docs/tutorials/wiki2/src/installation/README.txt
+++ b/docs/tutorials/wiki2/src/installation/README.txt
@@ -1,5 +1,5 @@
myproj
-===============================
+======
Getting Started
---------------
diff --git a/docs/tutorials/wiki2/src/models/README.txt b/docs/tutorials/wiki2/src/models/README.txt
index 5e21b8aa4..81102a869 100644
--- a/docs/tutorials/wiki2/src/models/README.txt
+++ b/docs/tutorials/wiki2/src/models/README.txt
@@ -1,5 +1,5 @@
myproj
-===============================
+======
Getting Started
---------------
diff --git a/docs/tutorials/wiki2/src/tests/README.txt b/docs/tutorials/wiki2/src/tests/README.txt
index 5e21b8aa4..81102a869 100644
--- a/docs/tutorials/wiki2/src/tests/README.txt
+++ b/docs/tutorials/wiki2/src/tests/README.txt
@@ -1,5 +1,5 @@
myproj
-===============================
+======
Getting Started
---------------
diff --git a/docs/tutorials/wiki2/src/tests/tutorial/views/default.py b/docs/tutorials/wiki2/src/tests/tutorial/views/default.py
index 9358993ea..65c12ed3b 100644
--- a/docs/tutorials/wiki2/src/tests/tutorial/views/default.py
+++ b/docs/tutorials/wiki2/src/tests/tutorial/views/default.py
@@ -1,4 +1,4 @@
-import cgi
+from pyramid.compat import escape
import re
from docutils.core import publish_parts
@@ -25,10 +25,10 @@ def view_page(request):
exists = request.dbsession.query(Page).filter_by(name=word).all()
if exists:
view_url = request.route_url('view_page', pagename=word)
- return '<a href="%s">%s</a>' % (view_url, cgi.escape(word))
+ return '<a href="%s">%s</a>' % (view_url, escape(word))
else:
add_url = request.route_url('add_page', pagename=word)
- return '<a href="%s">%s</a>' % (add_url, cgi.escape(word))
+ return '<a href="%s">%s</a>' % (add_url, escape(word))
content = publish_parts(page.data, writer_name='html')['html_body']
content = wikiwords.sub(add_link, content)
diff --git a/docs/tutorials/wiki2/src/views/README.txt b/docs/tutorials/wiki2/src/views/README.txt
index 5e21b8aa4..81102a869 100644
--- a/docs/tutorials/wiki2/src/views/README.txt
+++ b/docs/tutorials/wiki2/src/views/README.txt
@@ -1,5 +1,5 @@
myproj
-===============================
+======
Getting Started
---------------
diff --git a/docs/tutorials/wiki2/src/views/tutorial/views/default.py b/docs/tutorials/wiki2/src/views/tutorial/views/default.py
index bb6300b75..3b95e0f59 100644
--- a/docs/tutorials/wiki2/src/views/tutorial/views/default.py
+++ b/docs/tutorials/wiki2/src/views/tutorial/views/default.py
@@ -1,4 +1,4 @@
-import cgi
+from pyramid.compat import escape
import re
from docutils.core import publish_parts
@@ -31,10 +31,10 @@ def view_page(request):
exists = request.dbsession.query(Page).filter_by(name=word).all()
if exists:
view_url = request.route_url('view_page', pagename=word)
- return '<a href="%s">%s</a>' % (view_url, cgi.escape(word))
+ return '<a href="%s">%s</a>' % (view_url, escape(word))
else:
add_url = request.route_url('add_page', pagename=word)
- return '<a href="%s">%s</a>' % (add_url, cgi.escape(word))
+ return '<a href="%s">%s</a>' % (add_url, escape(word))
content = publish_parts(page.data, writer_name='html')['html_body']
content = wikiwords.sub(add_link, content)
diff --git a/docs/whatsnew-1.9.rst b/docs/whatsnew-1.9.rst
new file mode 100644
index 000000000..0ba29625c
--- /dev/null
+++ b/docs/whatsnew-1.9.rst
@@ -0,0 +1,61 @@
+What's New in Pyramid 1.9
+=========================
+
+This article explains the new features in :app:`Pyramid` version 1.9 as compared to its predecessor, :app:`Pyramid` 1.8. It also documents backwards incompatibilities between the two versions and deprecations added to :app:`Pyramid` 1.9, as well as software dependency changes and notable documentation additions.
+
+Major Feature Additions
+-----------------------
+
+- The file format used by all ``p*`` command line scripts such as ``pserve`` and ``pshell``, as well as the :func:`pyramid.paster.bootstrap` function is now replaceable thanks to a new dependency on `plaster <http://docs.pylonsproject.org/projects/plaster/en/latest/>`_.
+
+ For now, Pyramid is still shipping with integrated support for the PasteDeploy INI format by depending on the `plaster_pastedeploy <https://github.com/Pylons/plaster_pastedeploy>`_ binding library. This may change in the future so it is recommended for applications to start depending on the appropriate plaster binding for their needs.
+
+ See https://github.com/Pylons/pyramid/pull/2985
+
+- Added an :term:`execution policy` hook to the request pipeline. An execution policy has the ability to control creation and execution of the request objects before they enter the rest of the pipeline. This means for a single request environ the policy may create more than one request object.
+
+ The execution policy can be replaced using the new :meth:`pyramid.config.Configurator.set_execution_policy` config directive.
+
+ The first library to use this feature is `pyramid_retry <http://docs.pylonsproject.org/projects/pyramid-retry/en/latest/>`_.
+
+ See https://github.com/Pylons/pyramid/pull/2964
+
+- CSRF support has been refactored out of sessions and into its own independent API in the :mod:`pyramid.csrf` module. It supports a pluggable :class:`pyramid.interfaces.ICSRFStoragePolicy` which can be used to define your own mechanism for generating and validating CSRF tokens. By default, Pyramid continues to use the :class:`pyramid.csrf.LegacySessionCSRFStoragePolicy` that uses the ``request.session.get_csrf_token`` and ``request.session.new_csrf_token`` APIs under the hood to preserve compatibility with older Pyramid applications. Two new policies are shipped as well, :class:`pyramid.csrf.SessionCSRFStoragePolicy` and :class:`pyramid.csrf.CookieCSRFStoragePolicy` which will store the CSRF tokens in the session and in a standalone cookie, respectively. The storage policy can be changed by using the new :meth:`pyramid.config.Configurator.set_csrf_storage_policy` config directive.
+
+ CSRF tokens should be used via the new :func:`pyramid.csrf.get_csrf_token`, :func:`pyramid.csrf.new_csrf_token` and :func:`pyramid.csrf.check_csrf_token` APIs in order to continue working if the storage policy is changed. Also, the :func:`pyramid.csrf.get_csrf_token` function is now injected into templates to be used conveniently in UI code.
+
+ See https://github.com/Pylons/pyramid/pull/2854 and https://github.com/Pylons/pyramid/pull/3019
+
+Minor Feature Additions
+-----------------------
+
+- Support an ``open_url`` config setting in the ``pserve`` section of the config file. This url is used to open a web browser when ``pserve --browser`` is invoked. When this setting is unavailable the ``pserve`` script will attempt to guess the port the server is using from the ``server:<server_name>`` section of the config file but there is no requirement that the server is being run in this format so it may fail. See https://github.com/Pylons/pyramid/pull/2984
+
+- The :class:`pyramid.config.Configurator` can now be used as a context manager which will automatically push/pop threadlocals (similar to :meth:`pyramid.config.Configurator.begin` and :meth:`pyramid.config.Configurator.end`). It will also automatically perform a :meth:`pyramid.config.Configurator.commit` at the end and thus it is only recommended to be used at the top-level of your app. See https://github.com/Pylons/pyramid/pull/2874
+
+- The threadlocals are now available inside any function invoked via :meth:`pyramid.config.Configurator.include`. This means the only config-time code that cannot rely on threadlocals is code executed from non-actions inside the main. This can be alleviated by invoking :meth:`pyramid.config.Configurator.begin` and :meth:`pyramid.config.Configurator.end` appropriately or using the new context manager feature of the configurator. See https://github.com/Pylons/pyramid/pull/2989
+
+Deprecations
+------------
+
+- Pyramid currently depends on ``plaster_pastedeploy`` to simplify the transition to ``plaster`` by maintaining integrated support for INI files. This dependency on ``plaster_pastedeploy`` should be considered subject to Pyramid's deprecation policy and may be removed in the future. Applications should depend on the appropriate plaster binding to satisfy their needs.
+
+- Retrieving CSRF token from the session has been deprecated in favor of equivalent methods in the :mod:`pyramid.csrf` module. The CSRF methods (``ISession.get_csrf_token`` and ``ISession.new_csrf_token``) are no longer required on the :class:`pyramid.interfaces.ISession` interface except when using the default :class:`pyramid.csrf.LegacySessionCSRFStoragePolicy`.
+
+ Also, ``pyramid.session.check_csrf_token`` is now located at :func:`pyramid.csrf.check_csrf_token`.
+
+ See https://github.com/Pylons/pyramid/pull/2854 and https://github.com/Pylons/pyramid/pull/3019
+
+Backward Incompatibilities
+--------------------------
+
+- ``request.exception`` and ``request.exc_info`` will only be set if the response was generated by the EXCVIEW tween. This is to avoid any confusion where a response was generated elsewhere in the pipeline and not in direct relation to the original exception. If anyone upstream wants to catch and render responses for exceptions they should set ``request.exception`` and ``request.exc_info`` themselves to indicate the exception that was squashed when generating the response.
+
+ Similar behavior occurs with :meth:`pyramid.request.Request.invoke_exception_view` in which the exception properties are set to reflect the exception if a response is successfully generated by the method.
+
+ This is a very minor incompatibility. Most tweens right now would give priority to the raised exception and ignore ``request.exception``. This change just improves and clarifies that bookkeeping by trying to be more clear about the relationship between the response and its squashed exception. See https://github.com/Pylons/pyramid/pull/3029 and https://github.com/Pylons/pyramid/pull/3031
+
+Documentation Enhancements
+--------------------------
+
+- Added the :term:`execution policy` to the routing diagram in :ref:`router_chapter`. See https://github.com/Pylons/pyramid/pull/2993