summaryrefslogtreecommitdiff
path: root/docs
diff options
context:
space:
mode:
authorChris McDonough <chrism@plope.com>2015-03-08 14:44:18 -0400
committerChris McDonough <chrism@plope.com>2015-03-08 14:44:18 -0400
commit8ebde50fdc7322e82b96ad103ab168b92ca2b74a (patch)
tree3be6e2cae14d7fd3b9951111a9c98c6bb7bfb4de /docs
parent5fff455548067480c820da2f64bc0b9c16a916a0 (diff)
parent6c1a1c60123d150a41fef3062df9a64b995305c5 (diff)
downloadpyramid-8ebde50fdc7322e82b96ad103ab168b92ca2b74a.tar.gz
pyramid-8ebde50fdc7322e82b96ad103ab168b92ca2b74a.tar.bz2
pyramid-8ebde50fdc7322e82b96ad103ab168b92ca2b74a.zip
Merge branch 'master' of github.com:Pylons/pyramid
Diffstat (limited to 'docs')
-rw-r--r--docs/narr/security.rst60
-rw-r--r--docs/narr/urldispatch.rst58
2 files changed, 104 insertions, 14 deletions
diff --git a/docs/narr/security.rst b/docs/narr/security.rst
index 2dc0c76af..75f4dc7c5 100644
--- a/docs/narr/security.rst
+++ b/docs/narr/security.rst
@@ -341,9 +341,7 @@ third argument is a permission or sequence of permission names.
A principal is usually a user id, however it also may be a group id if your
authentication system provides group information and the effective
:term:`authentication policy` policy is written to respect group information.
-For example, the
-:class:`pyramid.authentication.RepozeWho1AuthenticationPolicy` respects group
-information if you configure it with a ``callback``.
+See :ref:`extending_default_authentication_policies`.
Each ACE in an ACL is processed by an authorization policy *in the
order dictated by the ACL*. So if you have an ACL like this:
@@ -583,6 +581,60 @@ via print statements when a call to
:meth:`~pyramid.request.Request.has_permission` fails is often useful.
.. index::
+ single: authentication policy (extending)
+
+.. _extending_default_authentication_policies:
+
+Extending Default Authentication Policies
+-----------------------------------------
+
+Pyramid ships with some builtin authentication policies for use in your
+applications. See :mod:`pyramid.authentication` for the available
+policies. They differ on their mechanisms for tracking authentication
+credentials between requests, however they all interface with your
+application in mostly the same way.
+
+Above you learned about :ref:`assigning_acls`. Each :term:`principal` used
+in the :term:`ACL` is matched against the list returned from
+:meth:`pyramid.interfaces.IAuthenticationPolicy.effective_principals`.
+Similarly, :meth:`pyramid.request.Request.authenticated_userid` maps to
+:meth:`pyramid.interfaces.IAuthenticationPolicy.authenticated_userid`.
+
+You may control these values by subclassing the default authentication
+policies. For example, below we subclass the
+:class:`pyramid.authentication.AuthTktAuthenticationPolicy` and define
+extra functionality to query our database before confirming that the
+:term:`userid` is valid in order to avoid blindly trusting the value in the
+cookie (what if the cookie is still valid but the user has deleted their
+account?). We then use that :term:`userid` to augment the
+``effective_principals`` with information about groups and other state for
+that user.
+
+.. code-block:: python
+ :linenos:
+
+ from pyramid.authentication import AuthTktAuthenticationPolicy
+
+ class MyAuthenticationPolicy(AuthTktAuthenticationPolicy):
+ def authenticated_userid(self, request):
+ userid = self.unauthenticated_userid(request)
+ if userid:
+ if request.verify_userid_is_still_valid(userid):
+ return userid
+
+ def effective_principals(self, request):
+ principals = [Everyone]
+ userid = self.authenticated_userid(request)
+ if userid:
+ principals += [Authenticated, str(userid)]
+ return principals
+
+In most instances ``authenticated_userid`` and ``effective_principals`` are
+application-specific whereas ``unauthenticated_userid``, ``remember`` and
+``forget`` are generic and focused on transport/serialization of data
+between consecutive requests.
+
+.. index::
single: authentication policy (creating)
.. _creating_an_authentication_policy:
@@ -653,7 +705,7 @@ that implements the following interface:
"""
After you do so, you can pass an instance of such a class into the
-:class:`~pyramid.config.Configurator.set_authentication_policy` method
+:class:`~pyramid.config.Configurator.set_authentication_policy` method at
configuration time to use it.
.. index::
diff --git a/docs/narr/urldispatch.rst b/docs/narr/urldispatch.rst
index 87a962a9a..ca6a55164 100644
--- a/docs/narr/urldispatch.rst
+++ b/docs/narr/urldispatch.rst
@@ -495,17 +495,21 @@ result in a particular view callable being invoked:
:linenos:
config.add_route('idea', 'site/{id}')
- config.add_view('mypackage.views.site_view', route_name='idea')
+ config.scan()
When a route configuration with a ``view`` attribute is added to the system,
and an incoming request matches the *pattern* of the route configuration, the
:term:`view callable` named as the ``view`` attribute of the route
configuration will be invoked.
-In the case of the above example, when the URL of a request matches
-``/site/{id}``, the view callable at the Python dotted path name
-``mypackage.views.site_view`` will be called with the request. In other
-words, we've associated a view callable directly with a route pattern.
+Recall that the ``@view_config`` is equivalent to calling ``config.add_view``,
+because the ``config.scan()`` call will import ``mypackage.views``, shown
+below, and execute ``config.add_view`` under the hood. Each view then maps the
+route name to the matching view callable. In the case of the above
+example, when the URL of a request matches ``/site/{id}``, the view callable at
+the Python dotted path name ``mypackage.views.site_view`` will be called with
+the request. In other words, we've associated a view callable directly with a
+route pattern.
When the ``/site/{id}`` route pattern matches during a request, the
``site_view`` view callable is invoked with that request as its sole
@@ -519,8 +523,10 @@ The ``mypackage.views`` module referred to above might look like so:
.. code-block:: python
:linenos:
+ from pyramid.view import view_config
from pyramid.response import Response
+ @view_config(route_name='idea')
def site_view(request):
return Response(request.matchdict['id'])
@@ -542,11 +548,30 @@ add to your application:
config.add_route('idea', 'ideas/{idea}')
config.add_route('user', 'users/{user}')
config.add_route('tag', 'tags/{tag}')
+ config.scan()
+
+Here is an example of a corresponding ``mypackage.views`` module:
- config.add_view('mypackage.views.idea_view', route_name='idea')
- config.add_view('mypackage.views.user_view', route_name='user')
- config.add_view('mypackage.views.tag_view', route_name='tag')
+.. code-block:: python
+ :linenos:
+
+ from pyramid.view import view_config
+ from pyramid.response import Response
+ @view_config(route_name='idea')
+ def idea_view(request):
+ return Response(request.matchdict['id'])
+
+ @view_config(route_name='user')
+ def user_view(request):
+ user = request.matchdict['user']
+ return Response(u'The user is {}.'.format(user))
+
+ @view_config(route_name='tag')
+ def tag_view(request):
+ tag = request.matchdict['tag']
+ return Response(u'The tag is {}.'.format(tag))
+
The above configuration will allow :app:`Pyramid` to service URLs in these
forms:
@@ -596,7 +621,7 @@ An example of using a route with a factory:
:linenos:
config.add_route('idea', 'ideas/{idea}', factory='myproject.resources.Idea')
- config.add_view('myproject.views.idea_view', route_name='idea')
+ config.scan()
The above route will manufacture an ``Idea`` resource as a :term:`context`,
assuming that ``mypackage.resources.Idea`` resolves to a class that accepts a
@@ -610,7 +635,20 @@ request in its ``__init__``. For example:
pass
In a more complicated application, this root factory might be a class
-representing a :term:`SQLAlchemy` model.
+representing a :term:`SQLAlchemy` model. The view ``mypackage.views.idea_view``
+might look like this:
+
+.. code-block:: python
+ :linenos:
+
+ @view_config(route_name='idea')
+ def idea_view(request):
+ idea = request.context
+ return Response(idea)
+
+Here, ``request.context`` is an instance of ``Idea``. If indeed the resource
+object is a SQLAlchemy model, you do not even have to perform a query in the
+view callable, since you have access to the resource via ``request.context``.
See :ref:`route_factories` for more details about how to use route factories.