summaryrefslogtreecommitdiff
path: root/docs/tutorials/wiki2/authorization.rst
diff options
context:
space:
mode:
Diffstat (limited to 'docs/tutorials/wiki2/authorization.rst')
-rw-r--r--docs/tutorials/wiki2/authorization.rst230
1 files changed, 132 insertions, 98 deletions
diff --git a/docs/tutorials/wiki2/authorization.rst b/docs/tutorials/wiki2/authorization.rst
index 8c1c50ff6..3573e06af 100644
--- a/docs/tutorials/wiki2/authorization.rst
+++ b/docs/tutorials/wiki2/authorization.rst
@@ -14,10 +14,12 @@ the server to view pages.
We will do the following steps:
-* Add a :term:`root factory` with an :term:`ACL` (``models.py``).
+* Add a :term:`root factory` with an :term:`ACL` (``models.py``,
+ ``__init__.py``).
* Add an :term:`authentication policy` and an :term:`authorization policy`
(``__init__.py``).
* Add an authentication policy callback (new ``security.py`` module).
+* Add routes for /login and /logout (``__init__.py``).
* Add ``login`` and ``logout`` views (``views.py``).
* Add :term:`permission` declarations to the ``edit_page`` and ``add_page``
views (``views.py``).
@@ -33,28 +35,40 @@ The source code for this tutorial stage can be browsed at
Adding A Root Factory
~~~~~~~~~~~~~~~~~~~~~
-Open ``models.py`` and add the following statements:
+Open ``tutorial/tutorial/models.py`` and add the following import
+statement at the head:
.. literalinclude:: src/authorization/tutorial/models.py
- :lines: 1-4,35-39
+ :lines: 1-4
:linenos:
:language: python
-We're going to start to use a custom :term:`root factory` within our
-``__init__.py`` file. The objects generated by the root factory will
-be used as the :term:`context` of each request to our application.
-Those context objects will be decorated with security
-declarations. When we use a custom root factory to generate
-our contexts, we can begin to make use of the declarative security
-features of :app:`Pyramid`.
+Add the following class definition:
-We'll modify our ``__init__.py``, passing in a :term:`root factory` to our
-:term:`Configurator` constructor. We'll point it at the new class we created
-inside our ``models.py`` file.
+.. literalinclude:: src/authorization/tutorial/models.py
+ :lines: 35-39
+ :linenos:
+ :language: python
+
+The ``RootFactory`` class is a :term:`root factory` that will be used by
+:app:`Pyramid` to construct the :term:`context` of each request to
+our application. The context is attached to the request
+object passed to our view callables as the ``context`` attribute,
+and will be decorated with security declarations. By using a custom
+root factory to generate our contexts, we can use the
+declarative security features of :app:`Pyramid`.
+
+Open ``tutorial/tutorial/__init__.py`` and add a ``root_factory``
+parameter to our :term:`Configurator` constructor, that points to
+the class we created above:
+
+.. literalinclude:: src/authorization/tutorial/__init__.py
+ :lines: 19-20
+ :linenos:
+ :emphasize-lines: 2
+ :language: python
-The ``RootFactory`` class we've just added will be used by :app:`Pyramid` to
-construct a ``context`` object. The context is attached to the request
-object passed to our view callables as the ``context`` attribute.
+(Only the highlighted line needs to be added.)
The context object generated by our root factory will possess an ``__acl__``
attribute that allows :data:`pyramid.security.Everyone` (a special principal)
@@ -71,15 +85,9 @@ information about what an :term:`ACL` represents.
the ``factory`` argument to
:meth:`pyramid.config.Configurator.add_route` for more info.
-We'll pass the ``RootFactory`` we created in the step above in as the
-``root_factory`` argument to a :term:`Configurator`.
-
Add an Authorization Policy and an Authentication Policy
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-We're going to be making several changes to our ``__init__.py`` file which
-will help us configure an authorization policy.
-
For any :app:`Pyramid` application to perform authorization, we need to add a
``security.py`` module (we'll do that shortly) and we'll need to change our
``__init__.py`` file to add an :term:`authentication policy` and an
@@ -100,8 +108,11 @@ Now add those policies to the configuration:
.. literalinclude:: src/authorization/tutorial/__init__.py
:lines: 16-22
:linenos:
+ :emphasize-lines: 1-3,6-7
:language: python
+(Only the highlighted lines need to be added.)
+
Note that the
:class:`pyramid.authentication.AuthTktAuthenticationPolicy` constructor
accepts two arguments: ``secret`` and ``callback``. ``secret`` is a string
@@ -110,49 +121,49 @@ represented by this policy: it is required. The ``callback`` is a
``groupfinder`` function in the current directory's ``security.py`` file. We
haven't added that module yet, but we're about to.
-Viewing Your Changes
---------------------
-
-When we're done configuring a root factory, adding a authentication and
-authorization policies, and adding routes for ``/login`` and ``/logout``,
-your application's ``__init__.py`` will look like this:
-
-.. literalinclude:: src/authorization/tutorial/__init__.py
- :linenos:
- :emphasize-lines: 2-3,7,16-18,20-22,25-26
- :language: python
-
Adding an authentication policy callback
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Add a ``tutorial/security.py`` module within your package (in the same
-directory as :file:`__init__.py`, :file:`views.py`, etc.) with the
+Create a new ``tutorial/tutorial/security.py`` module with the
following content:
.. literalinclude:: src/authorization/tutorial/security.py
:linenos:
:language: python
-The ``groupfinder`` function defined here is an :term:`authentication policy`
-"callback"; it is a callable that accepts a userid and a request. If
-the userid exists in the system, the callback will return a sequence
-of group identifiers (or an empty sequence if the user isn't a member
-of any groups). If the userid *does not* exist in the system, the
-callback will return ``None``. In a production system, user and group
-data will most often come from a database, but here we use "dummy"
-data to represent user and groups sources. Note that the ``editor``
-user is a member of the ``group:editors`` group in our dummy group
-data (the ``GROUPS`` data structure).
+``groupfinder()`` is an :term:`authentication policy`
+"callback"; it is a function that accepts a userid and a request and
+returns one of these values:
+
+- If the userid exists in the system, the callback will return a
+ sequence of group identifiers (or an empty sequence if the user
+ isn't a member of any groups).
+- If the userid *does not* exist in the system, the callback will
+ return ``None``.
We've given the ``editor`` user membership to the ``group:editors`` by
-mapping him to this group in the ``GROUPS`` data structure (``GROUPS =
-{'editor':['group:editors']}``). Since the ``groupfinder`` function
+mapping him to this group in the ``GROUPS`` data structure above.
+Since the ``groupfinder`` function
consults the ``GROUPS`` data structure, this will mean that, as a
result of the ACL attached to the :term:`context` object returned by
the root factory, and the permission associated with the ``add_page``
and ``edit_page`` views, the ``editor`` user should be able to add and
edit pages.
+In a production system, user and group
+data will most often come from a database, but here we use "dummy"
+data to represent user and groups sources.
+
+Add routes for /login and /logout
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Go back to ``tutorial/tutorial/__init__.py`` and add these two
+routes:
+
+.. literalinclude:: src/authorization/tutorial/__init__.py
+ :lines: 25-26
+ :linenos:
+ :language: python
+
Adding Login and Logout Views
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -186,7 +197,7 @@ one makes it a :term:`forbidden view`. The forbidden view is
displayed whenever Pyramid or your application raises an
:class:`pyramid.httpexceptions.HTTPForbidden` exception. In this
case, we'll be relying on the forbidden view to show the login form
-whenver someone attempts to execute an action which they're not yet
+whenever someone attempts to execute an action which they're not yet
authorized to perform.
The ``logout`` view callable is decorated with a ``@view_config`` decorator
@@ -197,86 +208,91 @@ We'll need to import some stuff to service the needs of these two functions:
the ``pyramid.view.forbidden_view_config`` class, a number of values from the
``pyramid.security`` module, and a value from our newly added
``tutorial.security`` package. Add the following import statements to the
-head of the ``views.py`` file:
+head of ``tutorial/tutorial/views.py``:
.. literalinclude:: src/authorization/tutorial/views.py
:lines: 9-18,24-25
:linenos:
+ :emphasize-lines: 3,7-8,12
:language: python
-Changing Existing Views
-~~~~~~~~~~~~~~~~~~~~~~~
+(Only the highlighted lines need to be added.)
-Add permision declarations
---------------------------
+Add permission declarations
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Then we need to change each of our ``view_page``, ``edit_page`` and
-``add_page`` view callables in ``views.py``. Within each of these views,
-we'll need to pass a "logged in" parameter to its template. We'll add
-something like this to each view body:
+Add a ``permission='edit'`` parameter to the ``@view_config``
+decorator for ``add_page()`` and ``edit_page()``, for example:
.. code-block:: python
:linenos:
+ :emphasize-lines: 2
+
+ @view_config(route_name='add_page', renderer='templates/edit.pt',
+ permission='edit')
- from pyramid.security import authenticated_userid
- logged_in = authenticated_userid(request)
+(Only the highlighted line needs to be added.)
+
+The result is that only users who possess the ``edit``
+permission at the time of the request may invoke those two views.
+
+We've granted the ``group:editors`` :term:`principal` the ``edit``
+permission in the :term:`root factory` via its ACL, so only a user who
+is a member of the group named ``group:editors`` will be able to
+invoke the views associated with the ``add_page`` or ``edit_page``
+routes.
Return a logged_in flag to the renderer
----------------------------------------
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-We'll then change the return value of these views to pass the resulting
-``logged_in`` value to the template, e.g.:
+Add the following import statement to the head of
+``tutorial/tutorial/views.py``:
.. code-block:: python
:linenos:
- return dict(page = page,
- content = content,
- logged_in = logged_in,
- edit_url = edit_url)
+ from pyramid.security import (
+ authenticated_userid,
+ )
-We'll also need to add a ``permission`` value to the ``@view_config``
-decorator for each of the ``add_page`` and ``edit_page`` view callables. For
-each, we'll add ``permission='edit'``, for example:
+
+Add a ``logged_in`` parameter to the return value of
+``view_page()``, ``edit_page()`` and ``add_page()``,
+like this:
.. code-block:: python
:linenos:
+ :emphasize-lines: 3
- @view_config(route_name='edit_page', renderer='templates/edit.pt',
- permission='edit')
+ return dict(page = page,
+ content = content,
+ logged_in = authenticated_userid(request),
+ edit_url = edit_url)
-See the ``permission='edit'`` we added there? This indicates that the view
-callables which these views reference cannot be invoked without the
-authenticated user possessing the ``edit`` permission with respect to the
-current :term:`context`.
+(Only the highlighted line needs to be added.)
-Adding these ``permission`` arguments causes Pyramid to make the
-assertion that only users who possess the effective ``edit``
-permission at the time of the request may invoke those two views.
-We've granted the ``group:editors`` :term:`principal` the ``edit``
-permission in the :term:`root factory` via its ACL, so only a user who
-is a member of the group named ``group:editors`` will be able to
-invoke the views associated with the ``add_page`` or ``edit_page``
-routes.
+:meth:`~pyramid.security.authenticated_userid()` will return None
+if the user is not authenticated, or some user id it the user
+is authenticated.
Adding the ``login.pt`` Template
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Add a ``login.pt`` template to your templates directory. It's
-referred to within the login view we just added to ``views.py``.
+Create ``tutorial/tutorial/templates/login.pt`` with the following
+content:
.. literalinclude:: src/authorization/tutorial/templates/login.pt
:language: xml
+The above template is referred to within the login view we just
+added to ``views.py``.
+
Add a "Logout" link when logged in
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-We'll also need to change our ``edit.pt`` and ``view.pt`` templates to
-display a "Logout" link if someone is logged in. This link will
-invoke the logout view.
-
-To do so we'll add this to both templates within the ``<div id="right"
-class="app-welcome align-right">`` div:
+Open ``tutorial/tutorial/templates/edit.pt`` and
+``tutorial/tutorial/templates/view.pt`` and add this within the
+``<div id="right" class="app-welcome align-right">`` div:
.. code-block:: xml
@@ -284,19 +300,36 @@ class="app-welcome align-right">`` div:
<a href="${request.application_url}/logout">Logout</a>
</span>
-Seeing Our Changes To ``views.py`` and our Templates
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+The attribute ``tal:condition="logged_in"`` will make the element be
+included when ``logged_in`` is any user id. The link will invoke
+the logout view. The above element will not be included if ``logged_in``
+is ``None``, such as when a user is not authenticated.
+
+Seeing Our Changes
+~~~~~~~~~~~~~~~~~~
+
+Our ``tutorial/tutorial/__init__.py`` will look something like this
+when we're done:
+
+.. literalinclude:: src/authorization/tutorial/__init__.py
+ :linenos:
+ :emphasize-lines: 2-3,7,16-18,20-22,25-26
+ :language: python
+
+(Only the highlighted lines need to be added.)
-Our ``views.py`` module will look something like this when we're done:
+Our ``tutorial/tutorial/views.py`` will look something like this
+when we're done:
.. literalinclude:: src/authorization/tutorial/views.py
:linenos:
- :emphasize-lines: 11,14-18,56,59,71,74,89-115,117-121
+ :emphasize-lines: 11,14-18,56,59,71,74,86,89-115,117-121
:language: python
(Only the highlighted lines need to be added.)
-Our ``edit.pt`` template will look something like this when we're done:
+Our ``tutorial/tutorial/templates/edit.pt`` template will look
+something like this when we're done:
.. literalinclude:: src/authorization/tutorial/templates/edit.pt
:emphasize-lines: 41-43
@@ -304,7 +337,8 @@ Our ``edit.pt`` template will look something like this when we're done:
(Only the highlighted lines need to be added.)
-Our ``view.pt`` template will look something like this when we're done:
+Our ``tutorial/tutorial/templates/view.pt`` template will look
+something like this when we're done:
.. literalinclude:: src/authorization/tutorial/templates/view.pt
:emphasize-lines: 41-43