summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris McDonough <chrism@agendaless.com>2009-12-24 10:55:28 +0000
committerChris McDonough <chrism@agendaless.com>2009-12-24 10:55:28 +0000
commit5b01ac1141f0d47d6b166c9903cb8b1883ded39e (patch)
tree5628381abcb56e04566653754d8d60943cddda0f
parent059618aa9aa28969621fa9885622fb9d15c3d286 (diff)
downloadpyramid-5b01ac1141f0d47d6b166c9903cb8b1883ded39e.tar.gz
pyramid-5b01ac1141f0d47d6b166c9903cb8b1883ded39e.tar.bz2
pyramid-5b01ac1141f0d47d6b166c9903cb8b1883ded39e.zip
Roles and refresh.
-rw-r--r--docs/tutorials/bfgwiki2/authorization.rst92
-rw-r--r--docs/tutorials/bfgwiki2/background.rst2
-rw-r--r--docs/tutorials/bfgwiki2/basiclayout.rst61
-rw-r--r--docs/tutorials/bfgwiki2/definingmodels.rst12
-rw-r--r--docs/tutorials/bfgwiki2/definingviews.rst106
5 files changed, 145 insertions, 128 deletions
diff --git a/docs/tutorials/bfgwiki2/authorization.rst b/docs/tutorials/bfgwiki2/authorization.rst
index 5257bbe5f..9aeb98505 100644
--- a/docs/tutorials/bfgwiki2/authorization.rst
+++ b/docs/tutorials/bfgwiki2/authorization.rst
@@ -6,9 +6,9 @@ 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 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.
+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.
@@ -42,15 +42,16 @@ your ``models.py`` file:
The ``RootFactory`` class we've just added will be used by
:mod:`repoze.bfg` to construct a ``context`` object. The context is
-attached to our request as the ``context`` attribute.
+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 "Everyone" (a special principal) to view all pages, while
-allowing only a user named ``editor`` 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 execution.
-See :ref:`assigning_acls` for more information about what an
-:term:`ACL` represents.
+allows :data:`repoze.bfg.security.Everyone` (a special principal) to
+view all pages, while allowing only a user named ``editor`` 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
@@ -65,29 +66,31 @@ your application's ``run.py`` will look like this.
:linenos:
:language: python
-Configuring a ``repoze.bfg`` Authentication Policy
---------------------------------------------------
+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
-:term:`application registry` to add an :term:`authentication policy`
-and an :term:`authorization policy`.
+``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 ``forbidden`` stanza which points at our
-login 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`` routes. This indicates that the views
-which these routes reference cannot be invoked without the
-authenticated user possessing the ``edit`` permission with respect to
-the current context. When you're done, your ``configure.zcml`` will
-look like so
+:class:`repoze.bfg.authentication.AuthTktAuthenticationPolicy` and an
+:class:`repoze.bfg.authorization.ACLAuthorizationPolicy` to enable
+declarative security checking. We'll also change ``configure.zcml``
+to add a ``forbidden`` 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. When you're done, your ``configure.zcml`` will look like so
.. literalinclude:: src/authorization/tutorial/configure.zcml
:linenos:
@@ -98,14 +101,14 @@ 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:
-The groupfinder defined here is an authorization policy "callback"; it
-is a 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``. We'll use "dummy" data to represent user and
-groups sources. When we're done, your application's ``security.py``
-will look like this.
+The groupfinder defined here is an :term:`authentication policy`
+"callback"; it is a 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``. We'll use "dummy" data to
+represent user and groups sources. When we're done, your
+application's ``security.py`` will look like this.
.. literalinclude:: src/authorization/tutorial/security.py
:linenos:
@@ -114,16 +117,17 @@ will look like this.
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 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 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 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 views. Add a file named ``login.py`` to your application
-(in the same directory as ``views.py``) with the following content:
+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:
@@ -133,8 +137,8 @@ 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:
+``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:
.. code-block:: python
:linenos:
@@ -180,8 +184,8 @@ class="main_content">`` div:
Viewing the Application in a Browser
------------------------------------
-Once we've set up the WSGI pipeline properly, we can finally examine
-our application in a browser. The views we'll try are as follows:
+We can finally examine our application in a browser. The views we'll
+try are as follows:
- Visiting `http://localhost:6543/ <http://localhost:6543/>`_ in a
browser invokes the ``view_wiki`` view. This always redirects to
diff --git a/docs/tutorials/bfgwiki2/background.rst b/docs/tutorials/bfgwiki2/background.rst
index 31cecdf96..09dc8de64 100644
--- a/docs/tutorials/bfgwiki2/background.rst
+++ b/docs/tutorials/bfgwiki2/background.rst
@@ -12,6 +12,6 @@ To code along with this tutorial, the developer will need a UNIX
machine with development tools (Mac OS X with XCode, any Linux or BSD
variant, etc) *or* he will need a Windows system of any kind.
-This tutorial is known to work under :mod:`repoze.bfg` version 1.1a5.
+This tutorial is targeted at :mod:`repoze.bfg` version 1.2.
Have fun!
diff --git a/docs/tutorials/bfgwiki2/basiclayout.rst b/docs/tutorials/bfgwiki2/basiclayout.rst
index b8d0ddcc2..b7c6b795d 100644
--- a/docs/tutorials/bfgwiki2/basiclayout.rst
+++ b/docs/tutorials/bfgwiki2/basiclayout.rst
@@ -25,29 +25,30 @@ following:
:linenos:
:language: xml
-#. *Line 1*. The root ``<configure>`` element, in a ``bfg``
- namespace.
+#. *Line 1*. The root ``<configure>`` element, using the
+ ``http://namespaces.repoze.org/bfg`` namespace.
#. *Line 4*. Boilerplate, the comment explains.
#. *Lines 6-7*. Register a :term:`subscriber` that tears down the
SQLAlchemy connection after a request is finished.
-#. *Lines 9-13*. Register a ``<route>`` that will be used when the
- URL is ``/``. Since this ``<route>`` has an empty ``path``
- attribute, it is the "default" route. The attribute named ``view``
- with the value ``.views.my_view`` is the dotted name to a
- *function* we write (generated by the ``bfg_routesalchemy``
- template) that is given a ``request`` object and which returns a
- response or a dictionary. You will use mostly ``<route>``
- statements in a :term:`URL dispatch` based application to map URLs
- to code. This ``route`` also names a ``view_renderer``, which is a
- template which lives in the ``templates`` subdirectory of the
- package. When the ``.views.my_view`` view returns a dictionary, a
- "renderer" will use this template to create a response.
+#. *Lines 9-13*. Register a ``<route>`` :term:`route configuration`
+ that will be used when the URL is ``/``. Since this ``<route>``
+ has an empty ``path`` attribute, it is the "default" route. The
+ attribute named ``view`` with the value ``.views.my_view`` is the
+ dotted name to a *function* we write (generated by the
+ ``bfg_routesalchemy`` template) that is given a ``request`` object
+ and which returns a response or a dictionary. You will use mostly
+ ``<route>`` statements in a :term:`URL dispatch` based application
+ to map URLs to code. This ``route`` also names a
+ ``view_renderer``, which is a template which lives in the
+ ``templates`` subdirectory of the package. When the
+ ``.views.my_view`` view returns a dictionary, a :term:`renderer`
+ will use this template to create a response.
#. *Lines 15-18*. Register a ``<static>`` directive that will match
- any URL hat starts with ``/static/``. This will serve up static
+ any URL that starts with ``/static/``. This will serve up static
resources for us, in this case, at
``http://localhost:6543/static/`` and below. With this
declaration, we're saying that any URL that starts with ``/static``
@@ -104,40 +105,46 @@ Here is the source for ``models.py``:
App Startup with ``run.py``
---------------------------
-How does a :mod:`repoze.bfg` application start up? When you run under
-``paster`` using the ``tutorial.ini`` generated config file, the
-application area points at an entry point. Our entry point happens to
-be in ``run.py`` and its ``app`` function:
+When you run the application using the ``paster`` command using the
+``tutorial.ini`` generated config file, the application configuration
+points at an Setuptools *entry point* described as
+``egg:tutorial#app``. In our application, because the application's
+``setup.py`` file says so, this entry point happens to be the ``app``
+function within the file named ``run.py``:
.. literalinclude:: src/basiclayout/tutorial/run.py
:linenos:
:language: py
-#. *Lines 1-5*. Imports to support later code.
+#. *Lines 1-4*. Imports to support later code.
-#. *Lines 7-11*. We define a ``Cleanup`` class which has a
+#. *Lines 6-10*. We define a ``Cleanup`` class which has a
``__del__`` method (the method called at Python object
destruction), which calls a function.
-#. *Lines 13-15*. An event :term:`subscriber` which adds a
+#. *Lines 12-14*. An event :term:`subscriber` which adds a
``Cleanup`` instance to the WSGI environment as
``tutorial.sasession``. As a result of registering this event
subscriber, when the WSGI environment is cleaned up, our database
connection will be removed.
-#. *Lines 17-24*. Get the database configuration string from the
+#. *Lines 21-23*. Get the database configuration string from the
``tutorial.ini`` file's ``[app:sql]`` section. This will be a URI
(something like ``sqlite://``).
-#. Line *25*. We initialize our SQL database using SQLAlchemy, passing
+#. Line *24*. We initialize our SQL database using SQLAlchemy, passing
it the db string.
-#. Line *26*. We construct a :term:`Configurator`. The first
+#. *Line 25*. We construct a :term:`Configurator`. The first
argument provided to the configurator is the :term:`root factory`,
which is used by the :mod:`repoze.bfg` :term:`traversal` mechanism.
Since this is a URL dispatch application, the root factory is
``None``. The second argument ``settings`` is passed as a keyword
argument. It contains a dictionary of settings parsed by
- PasteDeploy. We then use the ``make_wsgi_app`` method of the
- :term:`Configurator` to return a :term:`WSGI` application.
+ PasteDeploy.
+
+# *Lines 26-29*. We then load a ZCML file to do application
+ configuration, and use the
+ :meth:`repoze.bfg.configuration.Configurator.make_wsgi_app` method
+ to return a :term:`WSGI` application.
diff --git a/docs/tutorials/bfgwiki2/definingmodels.rst b/docs/tutorials/bfgwiki2/definingmodels.rst
index 982865a80..0cae00ede 100644
--- a/docs/tutorials/bfgwiki2/definingmodels.rst
+++ b/docs/tutorials/bfgwiki2/definingmodels.rst
@@ -24,19 +24,19 @@ sample and we're not going to use it.
Then, we'll add a ``Page`` class. Because this is a SQLAlchemy
application, this class should inherit from an instance of
-``sqlalchemy.ext.declarative.declarative_base``. Declarative
+:class:`sqlalchemy.ext.declarative.declarative_base`. Declarative
SQLAlchemy models are easier to use than directly-mapped ones. The
code generated by our ``routesalchemy`` paster template does not use
-declarative SQLAlchemy syntax, so we'll need to change various things to
-begin to use declarative syntax.
+declarative SQLAlchemy syntax, so we'll need to change various things
+to begin to use declarative syntax.
Our ``Page`` class will have a class level attribute ``__tablename__``
which equals the string ``pages``. This means that SQLAlchemy will
store our wiki data in a SQL table named ``pages``. Our Page class
will also have class-level attributes named ``id``, ``pagename`` and
-``data`` (all instances of ``sqlalchemy.Column``). These will map to
-columns in the ``pages`` table. The ``id`` attribute will be the
-primary key in the table. The ``name`` attribute will be a text
+``data`` (all instances of :class:`sqlalchemy.Column`). These will
+map to columns in the ``pages`` table. The ``id`` attribute will be
+the primary key in the table. The ``name`` attribute will be a text
attribute, each value of which needs to be unique within the column.
The ``data`` attribute is a text attribute that will hold the body of
each page.
diff --git a/docs/tutorials/bfgwiki2/definingviews.rst b/docs/tutorials/bfgwiki2/definingviews.rst
index c4c609ff2..416b431be 100644
--- a/docs/tutorials/bfgwiki2/definingviews.rst
+++ b/docs/tutorials/bfgwiki2/definingviews.rst
@@ -2,16 +2,17 @@
Defining Views
==============
-A :term:`view` in a :term:`url dispatch` -based :mod:`repoze.bfg`
-application is typically a simple Python function that accepts a
-single parameter named :term:`request`. A view is assumed to return a
-:term:`response` object.
+A :term:`view callable` in a :term:`url dispatch` -based
+:mod:`repoze.bfg` application is typically a simple Python function
+that accepts a single parameter named :term:`request`. A view
+callable is assumed to return a :term:`response` object.
.. note:: A :mod:`repoze.bfg` view can also be defined as callable
which accepts *two* arguments: a :term:`context` and a
:term:`request`. You'll see this two-argument pattern used in
other :mod:`repoze.bfg` tutorials and applications. Either calling
- convention will work in any :mod:`repoze.bfg` application. In
+ convention will work in any :mod:`repoze.bfg` application; the
+ calling conventions can be used interchangeably as necessary. In
:term:`url dispatch` based applications, however, the context
object is rarely used in the view body itself, so within this
tutorial we define views as callables that accept only a request to
@@ -56,31 +57,32 @@ Adding View Functions
We'll get rid of our ``my_view`` view function in our ``views.py``
file. It's only an example and isn't relevant to our application.
-Then we're going to add four :term:`view` functions to our
-``views.py`` module. One view (named ``view_wiki``) will display the
-wiki itself (it will answer on the root URL), another named
-``view_page`` will display an individual page, another named
-``add_page`` will allow a page to be added, and a final view named
-``edit_page`` will allow a page to be edited. We'll describe each one
-briefly and show the resulting ``views.py`` file afterwards.
+Then we're going to add four :term:`view callable` functions to our
+``views.py`` module. One view callable (named ``view_wiki``) will
+display the wiki itself (it will answer on the root URL), another
+named ``view_page`` will display an individual page, another named
+``add_page`` will allow a page to be added, and a final view callable
+named ``edit_page`` will allow a page to be edited. We'll describe
+each one briefly and show the resulting ``views.py`` file afterwards.
.. note::
There is nothing special about the filename ``views.py``. A project
- may have many views throughout its codebase in arbitrarily-named
- files. Files implementing views often have ``view`` in their
- filenames (or may live in a Python subpackage of your application
- package named ``views``), but this is only by convention.
+ may have many view callables throughout its codebase in
+ arbitrarily-named files. Files implementing view callables often
+ have ``view`` in their filenames (or may live in a Python subpackage
+ of your application package named ``views``), but this is only by
+ convention.
The ``view_wiki`` view function
-------------------------------
-The ``view_wiki`` function will respond as the default view of a
-``Wiki`` model object. It always redirects to a URL which represents
-the path to our "FrontPage". It returns an instance of the
-``webob.exc.HTTPFound`` class (instances of which implement the WebOb
-:term:`response` interface), It will use the
-``repoze.bfg.url.route_url`` API to construct a URL to the
+The ``view_wiki`` function will respond as the :term:`default view` of
+a ``Wiki`` model object. It always redirects to a URL which
+represents the path to our "FrontPage". It returns an instance of the
+:class:`webob.exc.HTTPFound` class (instances of which implement the
+WebOb :term:`response` interface), It will use the
+:func:`repoze.bfg.url.route_url` API to construct a URL to the
``FrontPage`` page (e.g. ``http://localhost:6543/FrontPage``), and
will use it as the "location" of the HTTPFound response, forming an
HTTP redirect.
@@ -88,8 +90,8 @@ HTTP redirect.
The ``view_page`` view function
-------------------------------
-The ``view_page`` function will respond as the default view of a
-``Page`` object. The ``view_page`` function renders the
+The ``view_page`` function will respond as the :term:`default view` of
+a ``Page`` object. The ``view_page`` function renders the
:term:`ReStructuredText` body of a page (stored as the ``data``
attribute of a Page object) as HTML. Then it substitutes an HTML
anchor for each *WikiWord* reference in the rendered HTML using a
@@ -134,22 +136,23 @@ e.g. ``http://localhost:6543/add_page/SomeName``, the ``pagename``
value in the matchdict will be ``SomeName``.
If the view execution is *not* a result of a form submission (if the
-expression ``'form.submitted' in request.params`` is False), the view
-renders a template. To do so, it generates a "save url" which the
-template use as the form post URL during rendering. We're lazy here,
-so we're trying to use the same template (``templates/edit.pt``) for
-the add view as well as the page edit view, so we create a dummy Page
-object in order to satisfy the edit form's desire to have *some* page
-object exposed as ``page``, and BFG will render the template
-associated with this view to a response.
+expression ``'form.submitted' in request.params`` is ``False``), the
+view callable renders a template. To do so, it generates a "save url"
+which the template use as the form post URL during rendering. We're
+lazy here, so we're trying to use the same template
+(``templates/edit.pt``) for the add view as well as the page edit
+view, so we create a dummy Page object in order to satisfy the edit
+form's desire to have *some* page object exposed as ``page``, and
+:mod:`repoze.bfg` will render the template associated with this view
+to a response.
If the view execution *is* a result of a form submission (if the
-expression ``'form.submitted' in request.params`` is True), we scrape
-the page body from the form data, create a Page object using the name
-in the matchdict ``pagename``, and obtain the page body from the
-request, and save it into the database using ``session.add``. We then
-redirect back to the ``view_page`` view (the default view for a page)
-for the newly created page.
+expression ``'form.submitted' in request.params`` is ``True``), we
+scrape the page body from the form data, create a Page object using
+the name in the matchdict ``pagename``, and obtain the page body from
+the request, and save it into the database using ``session.add``. We
+then redirect back to the ``view_page`` view (the :term:`default view`
+for a Page) for the newly created page.
The ``edit_page`` view function
-------------------------------
@@ -162,15 +165,17 @@ will have a ``pagename`` key matching the name of the page the user
wants to edit.
If the view execution is *not* a result of a form submission (if the
-expression ``'form.submitted' in request.params`` is False), the view
-simply renders the edit form, passing the request, the page object,
-and a save_url which will be used as the action of the generated form.
+expression ``'form.submitted' in request.params`` is ``False``), the
+view simply renders the edit form, passing the request, the page
+object, and a save_url which will be used as the action of the
+generated form.
If the view execution *is* a result of a form submission (if the
-expression ``'form.submitted' in request.params`` is True), the view
-grabs the ``body`` element of the request parameter and sets it as the
-``data`` key in the matchdict. It then redirects to the default view
-of the wiki page, which will always be the ``view_page`` view.
+expression ``'form.submitted' in request.params`` is ``True``), the
+view grabs the ``body`` element of the request parameter and sets it
+as the ``data`` key in the matchdict. It then redirects to the
+default view of the wiki page, which will always be the ``view_page``
+view.
Viewing the Result of Our Edits to ``views.py``
===============================================
@@ -187,9 +192,9 @@ Adding Templates
The views we've added all reference a :term:`template`. Each template
is a :term:`Chameleon` template. The default templating system in
-:mod:`repoze.bfg` is a variant of :term:`ZPT` provided by Chameleon.
-These templates will live in the ``templates`` directory of our
-tutorial package.
+:mod:`repoze.bfg` is a variant of :term:`ZPT` provided by
+:term:`Chameleon`. These templates will live in the ``templates``
+directory of our tutorial package.
The ``view.pt`` Template
------------------------
@@ -270,7 +275,8 @@ Note that the *ordering* of these declarations is very important.
#. Add a declaration which maps the empty path (signifying the root
URL) to the view named ``view_wiki`` in our ``views.py`` file with
- the name ``view_wiki``. This is the default view for the wiki.
+ the name ``view_wiki``. This is the :term:`default view` for the
+ wiki.
#. Add a declaration which maps the path pattern ``:pagename`` to the
view named ``view_page`` in our ``views.py`` file with the view
@@ -316,7 +322,7 @@ Let's add a piece of middleware to the WSGI pipeline. We'll add
``egg:Paste#evalerror`` middleware which displays debuggable errors in
the browser while you're developing (this is *not* recommended for
deployment as it is a security risk). Let's insert evalerror into the
-pipeline right above "egg:repoze.tm2#tm", making our resulting
+pipeline right above ``egg:repoze.tm2#tm``, making our resulting
``tutorial.ini`` file look like so:
.. literalinclude:: src/views/tutorial.ini