diff options
Diffstat (limited to 'docs/tutorials/bfgwiki2/authorization.rst')
| -rw-r--r-- | docs/tutorials/bfgwiki2/authorization.rst | 280 |
1 files changed, 0 insertions, 280 deletions
diff --git a/docs/tutorials/bfgwiki2/authorization.rst b/docs/tutorials/bfgwiki2/authorization.rst deleted file mode 100644 index 709d0ba8e..000000000 --- a/docs/tutorials/bfgwiki2/authorization.rst +++ /dev/null @@ -1,280 +0,0 @@ -.. _wiki2_adding_authorization: - -==================== -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 only people whom possess a -specific username (`editor`) to add and edit wiki pages but we'll -continue allowing anyone with access to the server to view pages. -:mod:`repoze.bfg` 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 at -`docs.repoze.org -<http://docs.repoze.org/bfgwiki2-1.3/authorization>`_. - -Adding A Root Factory ---------------------- - -We're going to start to use a custom :term:`root factory` within our -``run.py`` file. The objects generated by the root factory will be -used as the :term:`context` of each request to our application. In -order for :mod:`repoze.bfg` declarative security to work properly, the -context object generated during a request must be decorated with -security declarations; when we begin to use a custom root factory to -generate our contexts, we can begin to make use of the declarative -security features of :mod:`repoze.bfg`. - -Let's modify our ``run.py``, passing in a :term:`root factory` to our -:term:`Configurator` constructor. We'll point it at a new class we -create inside our ``models.py`` file. Add the following statements to -your ``models.py`` file: - -.. code-block:: python - - from repoze.bfg.security import Allow - from repoze.bfg.security import Everyone - - class RootFactory(object): - __acl__ = [ (Allow, Everyone, 'view'), - (Allow, 'group:editors', 'edit') ] - def __init__(self, request): - self.__dict__.update(request.matchdict) - -The ``RootFactory`` class we've just added will be used by -:mod:`repoze.bfg` to construct a ``context`` object. The context is -attached to the request object passed to our view callables as the -``context`` attribute. - -All of our context objects will possess an ``__acl__`` attribute that -allows :data:`repoze.bfg.security.Everyone` (a special principal) to -view all pages, while allowing only a :term:`principal` named -``group:editors`` to edit and add pages. The ``__acl__`` attribute -attached to a context is interpreted specially by :mod:`repoze.bfg` as -an access control list during view callable execution. See -:ref:`assigning_acls` for more information about what an :term:`ACL` -represents. - -.. note: Although we don't use the functionality here, the ``factory`` - used to create route contexts may differ per-route as opposed to - globally. See the ``factory`` attribute in - :ref:`route_zcml_directive` for more info. - -We'll pass the ``RootFactory`` we created in the step above in as the -``root_factory`` argument to a :term:`Configurator`. When we're done, -your application's ``run.py`` will look like this. - -.. literalinclude:: src/authorization/tutorial/run.py - :linenos: - :language: python - -Configuring a ``repoze.bfg`` Authorization Policy -------------------------------------------------- - -For any :mod:`repoze.bfg` application to perform authorization, we -need to add a ``security.py`` module and we'll need to change our -``configure.zcml`` file to add an :term:`authentication policy` and an -: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 change -``configure.zcml`` to add a view stanza which points at our ``login`` -:term:`view callable`, also known as a :term:`forbidden view`. This -configures our newly created login view to show up when -:mod:`repoze.bfg` detects that a view invocation can not be -authorized. Also, we'll add ``view_permission`` attributes with the -value ``edit`` to the ``edit_page`` and ``add_page`` route -declarations. This indicates that the view callables which these -routes reference cannot be invoked without the authenticated user -possessing the ``edit`` permission with respect to the current -context. - -This makes 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`` 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 -views associated with the ``add_page`` or ``edit_page`` routes. - -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 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). - -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 -consults the ``GROUPS`` data structure, this will mean that, as a -result of the ACL attached to the root 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. - -Adding Login and Logout Views -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -We'll add a ``login`` view callable which renders a login form and -processes the post from the login form, checking credentials. - -We'll also add a ``logout`` view callable 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 view callables. 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 to -its template. We'll add something like this to each view body: - -.. ignore-next-block -.. code-block:: python - :linenos: - - from repoze.bfg.security import authenticated_userid - logged_in = authenticated_userid(request) - -We'll then change the return value of these views to pass the -`resulting `logged_in`` value to the template, e.g.: - -.. 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 -class="main_content">`` div: - -.. code-block:: xml - :linenos: - - <span tal:condition="logged_in"> - <a href="${request.application_url}/logout">Logout</a> - </span> - -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 FrontPage page object. - -- Visiting ``http://localhost:6543/FrontPage/edit_page`` in a browser - invokes the edit view for the FrontPage 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 - display the edit page form. - -- 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 - display the edit page form. - -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. - - - |
