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.
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:
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.
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.
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:
Our edit.pt template will look something like this when we're done:
Our view.pt template will look something like this when we're done:
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.