diff options
| author | Chris McDonough <chrism@plope.com> | 2012-04-04 05:25:51 -0400 |
|---|---|---|
| committer | Chris McDonough <chrism@plope.com> | 2012-04-04 05:25:51 -0400 |
| commit | 346d54130d4f33418f29ef4d529941ce722defea (patch) | |
| tree | 158d62fdde2f6a15eea9fc14fc2679d978929788 | |
| parent | 360f251a1765e53a15bec53c91068880f47e503f (diff) | |
| parent | 534ff3ca11c32d57b52d1924ecc0a4baa4a38fd0 (diff) | |
| download | pyramid-346d54130d4f33418f29ef4d529941ce722defea.tar.gz pyramid-346d54130d4f33418f29ef4d529941ce722defea.tar.bz2 pyramid-346d54130d4f33418f29ef4d529941ce722defea.zip | |
Merge branch 'master' of github.com:Pylons/pyramid
| -rw-r--r-- | docs/narr/views.rst | 11 | ||||
| -rw-r--r-- | docs/tutorials/wiki2/authorization.rst | 230 |
2 files changed, 143 insertions, 98 deletions
diff --git a/docs/narr/views.rst b/docs/narr/views.rst index c3bbbd50e..f6ee9a8d5 100644 --- a/docs/narr/views.rst +++ b/docs/narr/views.rst @@ -572,6 +572,17 @@ No matter which view calling convention is used, the view code always has access to the context via ``request.context``. .. index:: + single: Passing in configuration variables + +.. _passing_in_config_variables: + +Passing Configuration Variables to a View +----------------------------------------- + +For information on passing a variable from the configuration .ini files to a +view, see :ref:`deployment_settings`. + +.. index:: single: Pylons-style controller dispatch Pylons-1.0-Style "Controller" Dispatch 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 |
