summaryrefslogtreecommitdiff
path: root/docs/whatsnew-2.0.rst
diff options
context:
space:
mode:
Diffstat (limited to 'docs/whatsnew-2.0.rst')
-rw-r--r--docs/whatsnew-2.0.rst390
1 files changed, 336 insertions, 54 deletions
diff --git a/docs/whatsnew-2.0.rst b/docs/whatsnew-2.0.rst
index 906529d6b..e97198b23 100644
--- a/docs/whatsnew-2.0.rst
+++ b/docs/whatsnew-2.0.rst
@@ -1,69 +1,178 @@
What's New in Pyramid 2.0
=========================
-This article explains the new features in :app:`Pyramid` version 2.0 as
-compared to its predecessor, :app:`Pyramid` 1.10. It also documents backwards
-incompatibilities between the two versions and deprecations added to
-:app:`Pyramid` 2.0, as well as software dependency changes and notable
-documentation additions.
+This article explains the new features in :app:`Pyramid` version 2.0 as compared to its predecessor, :app:`Pyramid` 1.10.
+It also documents backwards incompatibilities between the two versions and deprecations added to :app:`Pyramid` 2.0, as well as software dependency changes and notable documentation additions.
+
+.. note::
+
+ This is the first release of :app:`Pyramid` that does not support Python 2, which is now End-of-Life and no longer receiving critical security updates by the PSF.
Feature Additions
-----------------
The feature additions in Pyramid 2.0 are as follows:
-- The authentication and authorization policies of Pyramid 1.x have been merged
- into a single :term:`security policy` in Pyramid 2.0. For details on how to
- migrate to the new security policy, see :ref:`upgrading_auth`.
- Authentication and authorization policies can still be used and will continue
- to function normally for the time being.
+- The authentication and authorization policies of Pyramid 1.x have been merged into a single :term:`security policy` in Pyramid 2.0.
+ For details on how to migrate to the new security policy, see :ref:`upgrading_auth_20`.
+ Authentication and authorization policies can still be used and will continue to function normally for the time being.
+
+ New security APIs have been added to support an overhaul of the authentication and authorization system.
+ Read :ref:`upgrading_auth_20` for information about using this new system.
+
+ - :meth:`pyramid.config.Configurator.set_security_policy`
+ - :class:`pyramid.interfaces.ISecurityPolicy`
+ - :attr:`pyramid.request.Request.identity`
+ - :class:`pyramid.authentication.AuthTktCookieHelper` (available in Pyramid 1.x)
+ - :class:`pyramid.authentication.SessionAuthenticationHelper`
+ - :class:`pyramid.authorization.ACLHelper`
+
+ See https://github.com/Pylons/pyramid/pull/3465
+
+- Exposed :data:`pyramid.authorization.ALL_PERMISSIONS` and :data:`pyramid.authorization.DENY_ALL` such that all of the ACL-related constants are now importable from the ``pyramid.authorization`` namespace.
+ See https://github.com/Pylons/pyramid/pull/3563
+
+- Changed the default ``serializer`` on :class:`pyramid.session.SignedCookieSessionFactory` to use :class:`pyramid.session.JSONSerializer` instead of :class:`pyramid.session.PickleSerializer`.
+ Read :ref:`upgrading_session_20` for more information about why this change was made.
+ See https://github.com/Pylons/pyramid/pull/3413
+
+- It is now possible to control whether a route pattern contains a trailing slash when it is composed with a route prefix using
+ ``config.include(..., route_prefix=...)`` or ``with config.route_prefix_context(...)``.
+ This can be done by specifying an empty pattern and setting the new argument ``inherit_slash=True``.
+ For example:
+
+ .. code-block:: python
+
+ with config.route_prefix_context('/users'):
+ config.add_route('users', '', inherit_slash=True)
+
+ In the example, the resulting pattern will be ``/users``.
+ Similarly, if the route prefix were ``/users/`` then the final pattern would be ``/users/``.
+ If the ``pattern`` was ``'/'``, then the final pattern would always be ``/users/``.
+ This new setting is only available if the pattern supplied to ``add_route`` is the empty string (``''``).
+ See https://github.com/Pylons/pyramid/pull/3420
+
+- A new parameter, ``allow_no_origin``, was added to :meth:`pyramid.config.Configurator.set_default_csrf_options` as well as :func:`pyramid.csrf.check_csrf_origin`.
+ This option controls whether a request is rejected if it has no ``Origin`` or ``Referer`` header - often the result of a user configuring their browser not to send a ``Referer`` header for privacy reasons even on same-domain requests.
+ The default is to reject requests without a known origin.
+ It is also possible to allow the special ``Origin: null`` header by adding it to the ``pyramid.csrf_trusted_origins`` list in the settings.
+ See https://github.com/Pylons/pyramid/pull/3512 and https://github.com/Pylons/pyramid/pull/3518
+
+- A new parameter, ``check_origin``, was added to :meth:`pyramid.config.Configurator.set_default_csrf_options` which disables origin checking entirely.
+ See https://github.com/Pylons/pyramid/pull/3518
+
+- Added :class:`pyramid.interfaces.IPredicateInfo` which defines the object passed to predicate factories as their second argument.
+ See https://github.com/Pylons/pyramid/pull/3514
+
+- Added support for serving pre-compressed static assets by using the ``content_encodings`` argument of :meth:`pyramid.config.Configurator.add_static_view` and :func:`pyramid.static.static_view`.
+ See https://github.com/Pylons/pyramid/pull/3537
+
+- Fix ``DeprecationWarning`` emitted by using the ``imp`` module.
+ See https://github.com/Pylons/pyramid/pull/3553
+
+- Properties created via ``config.add_request_method(..., property=True)`` or ``request.set_property`` used to be readonly.
+ They can now be overridden via ``request.foo = ...`` and until the value is deleted it will return the overridden value.
+ This is most useful when mocking request properties in testing.
+ See https://github.com/Pylons/pyramid/pull/3559
+
+- Finished callbacks are now executed as part of the ``closer`` that is invoked as part of :func:`pyramid.scripting.prepare` and :func:`pyramid.paster.bootstrap`.
+ See https://github.com/Pylons/pyramid/pull/3561
+
+- Added :class:`pyramid.request.RequestLocalCache` which can be used to create simple objects that are shared across requests and can be used to store per-request data.
+ This is useful when the source of data is external to the request itself.
+ Often a reified property is used on a request via :meth:`pyramid.config.Configurator.add_request_method`, or :class:`pyramid.decorator.reify`.
+ These work great when the data is generated on-demand when accessing the request property.
+ However, often the case is that the data is generated when accessing some other system and then we want to cache the data for the duration of the request.
+ See https://github.com/Pylons/pyramid/pull/3561
+
+- No longer define ``pyramid.request.Request.json_body`` which is already provided by WebOb.
+ This allows the attribute to now be settable.
+ See https://github.com/Pylons/pyramid/pull/3447
+
+- Improve debugging info from :class:`pyramid.view.view_config` decorator.
+ See https://github.com/Pylons/pyramid/pull/3483
+
+- ``pserve`` now outputs verbose messaging to `stderr` instead of `stdout` to circumvent buffering issues that exist by default on `stdout`.
+ See https://github.com/Pylons/pyramid/pull/3593
Deprecations
------------
-- Authentication and authorization policies have been deprecated in favor of
- the new :term:`security policy`.
+- Deprecated the authentication and authorization interfaces and principal-based support.
+ See :ref:`upgrading_auth_20` for information on equivalent APIs and notes on upgrading.
+ The following APIs are deprecated as a result of this change:
+
+ - :meth:`pyramid.config.Configurator.set_authentication_policy`
+ - :meth:`pyramid.config.Configurator.set_authorization_policy`
+ - :class:`pyramid.interfaces.IAuthenticationPolicy`
+ - :class:`pyramid.interfaces.IAuthorizationPolicy`
+ - :attr:`pyramid.request.Request.effective_principals`
+ - :attr:`pyramid.request.Request.unauthenticated_userid`
+ - :class:`pyramid.authentication.AuthTktAuthenticationPolicy`
+ - :class:`pyramid.authentication.RemoteUserAuthenticationPolicy`
+ - :class:`pyramid.authentication.RepozeWho1AuthenticationPolicy`
+ - :class:`pyramid.authentication.SessionAuthenticationPolicy`
+ - :class:`pyramid.authentication.BasicAuthAuthenticationPolicy`
+ - :class:`pyramid.authorization.ACLAuthorizationPolicy`
+ - The ``effective_principals`` view and route predicates.
+
+- Deprecated :func:`pyramid.security.principals_allowed_by_permission``.
+ This method continues to work with the deprecated :class:`pyramid.interfaces.IAuthorizationPolicy` interface but will not work with the new :class:`pyramid.interfaces.ISecurityPolicy`.
+ See https://github.com/Pylons/pyramid/pull/3465
+
+- Deprecated several ACL-related aspects of :mod:`pyramid.security`.
+ Equivalent objects should now be imported from the :mod:`pyramid.authorization` module.
+ This includes:
-.. _upgrading_auth:
+ - :attr:`pyramid.security.Everyone`
+ - :attr:`pyramid.security.Authenticated`
+ - :attr:`pyramid.security.ALL_PERMISSIONS`
+ - :attr:`pyramid.security.DENY_ALL`
+ - :attr:`pyramid.security.ACLAllowed`
+ - :attr:`pyramid.security.ACLDenied`
+
+ See https://github.com/Pylons/pyramid/pull/3563
+
+- Deprecated :class:`pyramid.session.PickleSerializer`.
+ See :ref:`upgrading_session_20` for more information, as well as
+ https://github.com/pylons/pyramid/issues/2709,
+ https://github.com/pylons/pyramid/pull/3353,
+ and https://github.com/pylons/pyramid/pull/3413
+
+.. _upgrading_auth_20:
Upgrading Authentication/Authorization
--------------------------------------
-The authentication and authorization policies of Pyramid 1.x have been merged
-into a single :term:`security policy` in Pyramid 2.0. Authentication and
-authorization policies can still be used and will continue to function
-normally, however they have been deprecated and support may be removed in
-upcoming versions.
-
-The new security policy should implement
-:class:`pyramid.interfaces.ISecurityPolicy` and can be set via the
-``security_policy`` argument of :class:`pyramid.config.Configurator` or
-:meth:`pyramid.config.Configurator.set_security_policy`.
-
-The policy contains ``authenticated_userid`` and ``remember``,
-with the same method signatures as in the legacy authentication policy. It
-also contains ``forget``, but now with keyword arguments in the method
-signature.
-
-The new security policy adds the concept of an :term:`identity`, which is an
-object representing the user associated with the current request. The identity
-can be accessed via :attr:`pyramid.request.Request.identity`.
+.. note::
+ It's important to note that the principal and ACL features within :app:`Pyramid` are not going away, nor deprecated, nor removed.
+ Most ACL features are deprecated in their current locations and moved into the :mod:`pyramid.authorization` module.
+ The main change is that they are now more optional than before and modifications were made to make the top-level APIs less opinionated as well as simpler.
+
+:app:`Pyramid` provides a simple set of APIs for plugging in allowed/denied semantics in your application.
+
+The authentication and authorization policies of Pyramid 1.x have been merged into a single :term:`security policy` in Pyramid 2.0.
+Authentication and authorization policies can still be used and will continue to function normally, however they have been deprecated and support may be removed in upcoming versions.
+
+The new security policy should implement :class:`pyramid.interfaces.ISecurityPolicy` and can be set via the ``security_policy`` argument of :class:`pyramid.config.Configurator` or :meth:`pyramid.config.Configurator.set_security_policy`.
+
+The policy contains :meth:`pyramid.interfaces.ISecurityPolicy.authenticated_userid` and :meth:`pyramid.interfaces.ISecurityPolicy.remember`, with the same method signatures as in the legacy authentication policy.
+It also contains :meth:`pyramid.interfaces.ISecurityPolicy.forget`, but now accepting keyword arguments in the method signature.
+
+The new security policy adds the concept of an :term:`identity`, which is an object representing the user associated with the current request.
+The identity can be accessed via :attr:`pyramid.request.Request.identity`.
The object can be of any shape, such as a simple ID string or an ORM object.
-The concept of :term:`principals <principal>` has been removed; the
-``permits`` method is passed an identity object. This change gives much more
-flexibility in authorization implementations, especially those that do not
-match the ACL pattern. If you were previously using
-:class:`pyramid.authorization.ACLAuthorizationPolicy`, you can achieve the same
-results by writing your own ``permits`` method using
-:class:`pyramid.authorization.ACLHelper`. For more details on implementing an
-ACL, see :ref:`assigning_acls`.
-
-Pyramid does not provide any built-in security policies. Similiar
-functionality of the authentication and authorization policies is now provided
-by helpers, which can be utilized to implement your own security policy. The
-functionality of the legacy authentication policies roughly correspond to the
-following helpers:
+The concept of :term:`principals <principal>` has been removed from the request object, security policy, and view/route predicates.
+Principals are replaced by ``identity``.
+The :meth:`pyramid.interfaces.ISecurityPolicy.permits` method is provided the ``request``, ``context``, and ``permissions`` and may now use the ``identity`` object, or derive principals, in any way it deems necessary for the application without being restricted to a list of principals represented by strings.
+This change gives much more flexibility in authorization implementations, especially those that do not match the ACL pattern.
+If you were previously using :class:`pyramid.authorization.ACLAuthorizationPolicy`, you can achieve the same results by writing your own ``permits`` method using :class:`pyramid.authorization.ACLHelper`.
+For more details on implementing an ACL, see :ref:`assigning_acls`.
+
+Pyramid does not provide any built-in security policies.
+Similiar functionality of the authentication and authorization policies is now provided by helpers, which can be utilized to implement your own security policy.
+The functionality of the legacy authentication policies roughly correspond to the following helpers:
+----------------------------------------------------------------+-------------------------------------------------------------------+
| Authentication Policy | Security Policy Helper |
@@ -81,16 +190,189 @@ following helpers:
| :class:`pyramid.authentication.RepozeWho1AuthenticationPolicy` | No equivalent. |
+----------------------------------------------------------------+-------------------------------------------------------------------+
-For further documentation on implementing security policies, see
-:ref:`writing_security_policy`.
+Upgrading from Built-in Policies
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Let's assume your application is using the built-in authentication and authorization policies, like :class:`pyramid.authentication.AuthTktAuthenticationPolicy`.
+For example:
+
+.. code-block:: python
+ :linenos:
+
+ def groupfinder(userid, request):
+ # do some db lookups to verify userid, then return
+ # None if not recognized, or a list of principals
+ if userid == 'editor':
+ return ['group:editor']
+
+ authn_policy = AuthTktAuthenticationPolicy('seekrit', callback=groupfinder)
+ authz_policy = ACLAuthorizationPolicy()
+ config.set_authentication_policy(authn_policy)
+ config.set_authorization_policy(authz_policy)
+
+We can easily write our own :class:`pyramid.interfaces.ISecurityPolicy` implementation:
-.. _behavior_of_legacy_auth:
+.. code-block:: python
+ :linenos:
-Behavior of the Legacy System
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ from pyramid.authentication import AuthTktCookieHelper
+ from pyramid.authorization import ACLHelper, Authenticated, Everyone
+
+ class MySecurityPolicy:
+ def __init__(self, secret):
+ self.helper = AuthTktCookieHelper(secret)
+
+ def identity(self, request):
+ # define our simple identity as None or a dict with userid and principals keys
+ identity = self.helper.identify(request)
+ if identity is None:
+ return None
+ userid = identity['userid'] # identical to the deprecated request.unauthenticated_userid
+
+ # verify the userid, just like we did before with groupfinder
+ principals = groupfinder(userid, request)
+
+ # assuming the userid is valid, return a map with userid and principals
+ if principals is not None:
+ return {
+ 'userid': userid,
+ 'principals': principals,
+ }
+
+ def authenticated_userid(self, request):
+ # defer to the identity logic to determine if the user id logged in
+ # and return None if they are not
+ identity = request.identity
+ if identity is not None:
+ return identity['userid']
+
+ def permits(self, request, context, permission):
+ # use the identity to build a list of principals, and pass them
+ # to the ACLHelper to determine allowed/denied
+ identity = request.identity
+ principals = set([Everyone])
+ if identity is not None:
+ principals.add(Authenticated)
+ principals.add(identity['userid'])
+ principals.update(identity['principals'])
+ return ACLHelper().permits(context, principals, permission)
+
+ def remember(self, request, userid, **kw):
+ return self.helper.remember(request, userid, **kw)
+
+ def forget(self, request, **kw):
+ return self.helper.forget(request, **kw)
+
+ config.set_security_policy(MySecurityPolicy('seekrit'))
+
+This is a little bit more verbose than before, but it is easy to write, and is significantly more extensible for more advanced applications.
+
+- Look at the new :class:`pyramid.request.RequestLocalCache` as well for help in caching the identity for improved performance.
+- Look at the improved :ref:`wiki2_adding_authorization` tutorial for another example of a security policy.
+
+For further documentation on implementing security policies, see :ref:`writing_security_policy`.
+
+Upgrading from Third-Party Policies
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+A generic :term:`security policy` can be written to work with legacy authentication and authorization policies.
+Note that some new features like the identity may not be as extensible and nice to use when taking this approach but it can be done to ease the transition:
+
+.. code-block:: python
+ :linenos:
+
+ class ShimSecurityPolicy:
+ def __init__(self, authn_policy, authz_policy):
+ self.authn_policy = authn_policy
+ self.authz_policy = authz_policy
+
+ def authenticated_userid(self, request):
+ return self.authn_policy.authenticated_userid(request)
+
+ def permits(self, request, context, permission):
+ principals = self.authn_policy.effective_principals(request)
+ return self.authz_policy.permits(context, principals, permission)
+
+ def remember(self, request, userid, **kw):
+ return self.authn_policy.remember(request, userid, **kw)
+
+ def forget(self, request, **kw):
+ return self.authz_policy.forget(request, **kw)
+
+Compatibility with Legacy Authentication/Authorization Policies and APIs
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+If you are upgrading from an application that is using the legacy authentication and authorization policies and APIs, things will continue to function normally.
+The new system is backward-compatible and the APIs still exist.
+It is highly encouraged to upgrade in order to embrace the new features.
+The legacy APIs are deprecated and may be removed in the future.
-Legacy authentication and authorization policies will continue to function as normal, as well as all related :class:`pyramid.request.Request` properties.
The new :attr:`pyramid.request.Request.identity` property will output the same result as :attr:`pyramid.request.Request.authenticated_userid`.
-If using a security policy, :attr:`pyramid.request.Request.unauthenticated_userid` will return the same value as :attr:`pyramid.request.Request.authenticated_userid`.
-:attr:`pyramid.request.Request.effective_principals` will always return a one-element list containing the :data:`pyramid.authorization.Everyone` principal, as there is no equivalent in the new security policy.
+If you try to use the new APIs with an application that is using the legacy authentication and authorization policies, then there are some issues to be aware of:
+
+- :attr:`pyramid.request.Request.unauthenticated_userid` will return the same value as :attr:`pyramid.request.Request.authenticated_userid`.
+- :attr:`pyramid.request.Request.effective_principals` will always return a one-element list containing the :data:`pyramid.authorization.Everyone` principal.
+
+.. index::
+ triple: pickle deprecation; JSON-serializable; ISession interface
+
+.. _upgrading_session_20:
+
+Upgrading Session Serialization
+-------------------------------
+
+In :app:`Pyramid` 2.0 the :class:`pyramid.interfaces.ISession` interface was changed to require that session implementations only need to support JSON-serializable data types.
+This is a stricter contract than the previous requirement that all objects be pickleable and it is being done for security purposes.
+This is a backward-incompatible change.
+Previously, if a client-side session implementation was compromised, it left the application vulnerable to remote code execution attacks using specially-crafted sessions that execute code when deserialized.
+
+Please reference the following tickets if detailed information on these changes is needed:
+
+- `2.0 feature request: Require that sessions are JSON serializable #2709 <https://github.com/pylons/pyramid/issues/2709>`_.
+- `deprecate pickleable sessions, recommend json #3353 <https://github.com/pylons/pyramid/pull/3353>`_.
+- `change to use JSONSerializer for SignedCookieSessionFactory #3413 <https://github.com/pylons/pyramid/pull/3413>`_.
+
+For users with compatibility concerns, it's possible to craft a serializer that can handle both formats until you are satisfied that clients have had time to reasonably upgrade.
+Remember that sessions should be short-lived and thus the number of clients affected should be small (no longer than an auth token, at a maximum).
+An example serializer:
+
+.. code-block:: python
+ :linenos:
+
+ import pickle
+ from pyramid.session import JSONSerializer
+ from pyramid.session import SignedCookieSessionFactory
+
+
+ class JSONSerializerWithPickleFallback(object):
+ def __init__(self):
+ self.json = JSONSerializer()
+
+ def dumps(self, appstruct):
+ """
+ Accept a Python object and return bytes.
+
+ During a migration, you may want to catch serialization errors here,
+ and keep using pickle while finding spots in your app that are not
+ storing JSON-serializable objects. You may also want to integrate
+ a fall-back to pickle serialization here as well.
+ """
+ return self.json.dumps(appstruct)
+
+ def loads(self, bstruct):
+ """Accept bytes and return a Python object."""
+ try:
+ return self.json.loads(bstruct)
+ except ValueError:
+ try:
+ return pickle.loads(bstruct)
+ except Exception:
+ # this block should catch at least:
+ # ValueError, AttributeError, ImportError; but more to be safe
+ raise ValueError
+
+ # somewhere in your configuration code
+ serializer = JSONSerializerWithPickleFallback()
+ session_factory = SignedCookieSessionFactory(..., serializer=serializer)
+ config.set_session_factory(session_factory)