From e53e13423685eac190676c4be32716c3a42603e4 Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Mon, 25 Oct 2010 17:40:43 -0400 Subject: rename bfgwiki to wiki --- docs/tutorials/wiki/authorization.rst | 300 ++++++++++++++++++++++++++++++++++ 1 file changed, 300 insertions(+) create mode 100644 docs/tutorials/wiki/authorization.rst (limited to 'docs/tutorials/wiki/authorization.rst') diff --git a/docs/tutorials/wiki/authorization.rst b/docs/tutorials/wiki/authorization.rst new file mode 100644 index 000000000..48908a97b --- /dev/null +++ b/docs/tutorials/wiki/authorization.rst @@ -0,0 +1,300 @@ +==================== +Adding Authorization +==================== + +Our application currently allows anyone with access to the server to +view, edit, and add pages to our wiki. For purposes of demonstration +we'll change our application to allow people whom are members of a +*group* named ``group:editors`` to add and edit wiki pages but we'll +continue allowing anyone with access to the server to view pages. +:mod:`pyramid` provides facilities for *authorization* and +*authentication*. We'll make use of both features to provide security +to our application. + +The source code for this tutorial stage can be browsed via +`http://github.com/Pylons/pyramid/tree/master/docs/tutorials/wiki/src/authorization/ +`_. + + +The source code for this tutorial stage can be browsed at +`docs.repoze.org `_. + +Configuring a ``pyramid`` Authentication Policy +-------------------------------------------------- + +For any :mod:`pyramid` application to perform authorization, we +need to add a ``security.py`` module and we'll need to change our +:term:`application registry` to add an :term:`authentication policy` +and a :term:`authorization policy`. + +Changing ``configure.zcml`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +We'll change our ``configure.zcml`` file to enable an +``AuthTktAuthenticationPolicy`` and an ``ACLAuthorizationPolicy`` to +enable declarative security checking. We'll also add a new view +stanza, which specifies a :term:`forbidden view`. This configures our +login view to show up when :mod:`pyramid` detects that a view +invocation can not be authorized. When you're done, your +``configure.zcml`` will look like so: + +.. literalinclude:: src/authorization/tutorial/configure.zcml + :linenos: + :language: xml + +Note that the ``authtktauthenticationpolicy`` tag has two attributes: +``secret`` and ``callback``. ``secret`` is a string representing an +encryption key used by the "authentication ticket" machinery +represented by this policy: it is required. The ``callback`` is a +string, representing a :term:`dotted Python name`, which points at the +``groupfinder`` function in the current directory's ``security.py`` +file. We haven't added that module yet, but we're about to. + +Adding ``security.py`` +~~~~~~~~~~~~~~~~~~~~~~ + +Add a ``security.py`` module within your package (in the same +directory as ``run.py``, ``views.py``, etc) with the following +content: + +.. literalinclude:: src/authorization/tutorial/security.py + :linenos: + :language: python + +The ``groupfinder`` function defined here is an authorization policy +"callback"; it is a callable that accepts a userid and a request. If +the userid exists in the set of users known by 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 this 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). + +Adding Login and Logout Views +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +We'll add a ``login`` view which renders a login form and processes +the post from the login form, checking credentials. + +We'll also add a ``logout`` view to our application and provide a link +to it. This view will clear the credentials of the logged in user and +redirect back to the front page. + +We'll add a different file (for presentation convenience) to add login +and logout views. Add a file named ``login.py`` to your application +(in the same directory as ``views.py``) with the following content: + +.. literalinclude:: src/authorization/tutorial/login.py + :linenos: + :language: python + +Changing Existing Views +~~~~~~~~~~~~~~~~~~~~~~~ + +Then we need to change each of our ``view_page``, ``edit_page`` and +``add_page`` views in ``views.py`` to pass a "logged in" parameter +into its template. We'll add something like this to each view body: + +.. ignore-next-block +.. code-block:: python + :linenos: + + from pyramid.security import authenticated_userid + logged_in = authenticated_userid(request) + +We'll then change the return value of each view that has an associated +``renderer`` to pass the `resulting `logged_in`` value to the +template. For example: + +.. ignore-next-block +.. code-block:: python + :linenos: + + return dict(page = context, + content = content, + logged_in = logged_in, + edit_url = edit_url) + +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 ``login.py``. + +.. literalinclude:: src/authorization/tutorial/templates/login.pt + :linenos: + :language: xml + +Change ``view.pt`` and ``edit.pt`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +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: + +.. code-block:: xml + :linenos: + + + Logout + + +Giving Our Root Model Object an ACL +----------------------------------- + +We need to give our root model object an :term:`ACL`. This ACL will +be sufficient to provide enough information to the :mod:`pyramid` +security machinery to challenge a user who doesn't have appropriate +credentials when he attempts to invoke the ``add_page`` or +``edit_page`` views. + +We need to perform some imports at module scope in our ``models.py`` +file: + +.. code-block:: python + :linenos: + + from pyramid.security import Allow + from pyramid.security import Everyone + +Our root model is a ``Wiki`` object. We'll add the following line at +class scope to our ``Wiki`` class: + +.. code-block:: python + :linenos: + + __acl__ = [ (Allow, Everyone, 'view'), + (Allow, 'group:editors', 'edit') ] + +It's only happenstance that we're assigning this ACL at class scope. +An ACL can be attached to an object *instance* too; this is how "row +level security" can be achieved in :mod:`pyramid` applications. We +actually only need *one* ACL for the entire system, however, because +our security requirements are simple, so this feature is not +demonstrated. + +Our resulting ``models.py`` file will now look like so: + +.. literalinclude:: src/authorization/tutorial/models.py + :linenos: + :language: python + +Adding ``permission`` Declarations to our ``bfg_view`` Decorators +----------------------------------------------------------------- + +To protect each of our views with a particular permission, we need to +pass a ``permission`` argument to each of our +:class:`pyramid.view.bfg_view` decorators. To do so, within +``views.py``: + +- We add ``permission='view'`` to the decorator attached to the + ``view_wiki`` view function. This makes the assertion that only + users who possess the effective ``view`` permission at the time of + the request may invoke this view. We've granted + :data:`pyramid.security.Everyone` the view permission at the root + model via its ACL, so everyone will be able to invoke the + ``view_wiki`` view. + +- We add ``permission='view'`` to the decorator attached to the + ``view_page`` view function. This makes the assertion that only + users who possess the effective ``view`` permission at the time of + the request may invoke this view. We've granted + :data:`pyramid.security.Everyone` the view permission at the root + model via its ACL, so everyone will be able to invoke the + ``view_page`` view. + +- We add ``permission='edit'`` to the decorator attached to the + ``add_page`` view function. This makes the assertion that only + users who possess the effective ``edit`` permission at the time of + the request may invoke this view. We've granted the + ``group:editors`` principal the ``edit`` permission at the root + model via its ACL, so only the a user whom is a member of the group + named ``group:editors`` will able to invoke the ``add_page`` view. + We've likewise given the ``editor`` user membership to this group + via thes ``security.py`` file by mapping him to the + ``group:editors`` group in the ``GROUPS`` data structure (``GROUPS = + {'editor':['group:editors']}``); the ``groupfinder`` function + consults the ``GROUPS`` data structure. This means that the + ``editor`` user can add pages. + +- We add ``permission='edit'`` to the ``bfg_view`` decorator attached + to the ``edit_page`` view function. This makes the assertion that + only users who possess the effective ``edit`` permission at the time + of the request may invoke this view. We've granted the + ``group:editors`` principal the ``edit`` permission at the root + model via its ACL, so only the a user whom is a member of the group + named ``group:editors`` will able to invoke the ``edit_page`` view. + We've likewise given the ``editor`` user membership to this group + via thes ``security.py`` file by mapping him to the + ``group:editors`` group in the ``GROUPS`` data structure (``GROUPS = + {'editor':['group:editors']}``); the ``groupfinder`` function + consults the ``GROUPS`` data structure. This means that the + ``editor`` user can edit pages. + +Viewing the Application in a Browser +------------------------------------ + +We can finally examine our application in a browser. The views we'll +try are as follows: + +- Visiting ``http://localhost:6543/`` in a browser invokes the + ``view_wiki`` view. This always redirects to the ``view_page`` view + of the FrontPage page object. It is executable by any user. + +- Visiting ``http://localhost:6543/FrontPage/`` in a browser invokes + the ``view_page`` view of the front page page object. This is + because it's the :term:`default view` (a view without a ``name``) + for ``Page`` objects. It is executable by any user. + +- Visiting ``http://localhost:6543/FrontPage/edit_page`` in a browser + invokes the edit view for the front page object. It is executable + by only the ``editor`` user. If a different user (or the anonymous + user) invokes it, a login form will be displayed. Supplying the + credentials with the username ``editor``, password ``editor`` will + show the edit page form being displayed. + +- Visiting ``http://localhost:6543/add_page/SomePageName`` in a + browser invokes the add view for a page. It is executable by only + the ``editor`` user. If a different user (or the anonymous user) + invokes it, a login form will be displayed. Supplying the + credentials with the username ``editor``, password ``editor`` will + show the edit page form being displayed. + +Seeing Our Changes To ``views.py`` and our Templates +---------------------------------------------------- + +Our ``views.py`` module will look something like this when we're done: + +.. literalinclude:: src/authorization/tutorial/views.py + :linenos: + :language: python + +Our ``edit.pt`` template will look something like this when we're done: + +.. literalinclude:: src/authorization/tutorial/templates/edit.pt + :linenos: + :language: xml + +Our ``view.pt`` template will look something like this when we're done: + +.. literalinclude:: src/authorization/tutorial/templates/view.pt + :linenos: + :language: xml + +Revisiting the Application +--------------------------- + +When we revisit the application in a browser, and log in (as a result +of hitting an edit or add page and submitting the login form with the +``editor`` credentials), we'll see a Logout link in the upper right +hand corner. When we click it, we're logged out, and redirected back +to the front page. + + + -- cgit v1.2.3