summaryrefslogtreecommitdiff
path: root/docs/tutorials/wiki2/authorization.rst
diff options
context:
space:
mode:
authorSteve Piercy <web@stevepiercy.com>2016-02-28 01:18:23 -0800
committerSteve Piercy <web@stevepiercy.com>2016-02-28 01:18:23 -0800
commitf99052e0790b8706161e432da0172b45a805d308 (patch)
treed81ede6be6b3415240ce9bd60f5d0bc3d3d21c4b /docs/tutorials/wiki2/authorization.rst
parentf63feb6c3f71f00389ce04f1100ae5675c748557 (diff)
downloadpyramid-f99052e0790b8706161e432da0172b45a805d308.tar.gz
pyramid-f99052e0790b8706161e432da0172b45a805d308.tar.bz2
pyramid-f99052e0790b8706161e432da0172b45a805d308.zip
wiki2 authorization (done)
- minor grammar and syntax - align order of bullet points for NewPage and PageResource with code - synch up "viewing app in browser" sections between authentication and authzn
Diffstat (limited to 'docs/tutorials/wiki2/authorization.rst')
-rw-r--r--docs/tutorials/wiki2/authorization.rst169
1 files changed, 91 insertions, 78 deletions
diff --git a/docs/tutorials/wiki2/authorization.rst b/docs/tutorials/wiki2/authorization.rst
index eb9269dff..a2865d8cd 100644
--- a/docs/tutorials/wiki2/authorization.rst
+++ b/docs/tutorials/wiki2/authorization.rst
@@ -5,39 +5,40 @@ Adding authorization
====================
In the last chapter we built :term:`authentication` into our wiki2. We also
-went one step further and used the ``request.user`` object to perform some explicit :term:`authorization` checks. This is fine for a lot of
-applications but :app:`Pyramid` provides some facilities for cleaning this
-up and decoupling the constraints from the view function itself.
+went one step further and used the ``request.user`` object to perform some
+explicit :term:`authorization` checks. This is fine for a lot of applications,
+but :app:`Pyramid` provides some facilities for cleaning this up and decoupling
+the constraints from the view function itself.
We will implement access control with the following steps:
-* Update the :term:`authentication policy` to break down the
- :term:`userid` into a list of :term:`principals <principal>`
- (``security.py``).
+* Update the :term:`authentication policy` to break down the :term:`userid`
+ into a list of :term:`principals <principal>` (``security.py``).
* Define an :term:`authorization policy` for mapping users, resources and
permissions (``security.py``).
-* Add new :term:`resource` definitions that will be used as the
- :term:`context` for the wiki pages (``routes.py``).
+* Add new :term:`resource` definitions that will be used as the :term:`context`
+ for the wiki pages (``routes.py``).
* Add an :term:`ACL` to each resource (``routes.py``).
* Replace the inline checks on the views with :term:`permission` declarations
(``views/default.py``).
+
Add user principals
-------------------
-A :term:`principal` is a level of abstraction on top of the raw
-:term:`userid` that describes the user in terms of capabilities, roles or
-other identifiers that are easier to generalize. The permissions are then
-written against the principals without focusing on the exact user involved.
+A :term:`principal` is a level of abstraction on top of the raw :term:`userid`
+that describes the user in terms of its capabilities, roles, or other
+identifiers that are easier to generalize. The permissions are then written
+against the principals without focusing on the exact user involved.
:app:`Pyramid` defines two builtin principals used in every application:
:attr:`pyramid.security.Everyone` and :attr:`pyramid.security.Authenticated`.
On top of these we have already mentioned the required principals for this
-application in the original design. The user has two possible roles:
-``editor`` and ``basic``. These will be prefixed by the ``role:``
-string to avoid clasing with any other types of principals.
+application in the original design. The user has two possible roles: ``editor``
+or ``basic``. These will be prefixed by the string ``role:`` to avoid clashing
+with any other types of principals.
-Open the file ``tutorial/security.py`` and edit the following lines:
+Open the file ``tutorial/security.py`` and edit it as follows:
.. literalinclude:: src/authorization/tutorial/security.py
:linenos:
@@ -46,19 +47,20 @@ Open the file ``tutorial/security.py`` and edit the following lines:
Only the highlighted lines need to be added.
-Note that the role comes from the ``User`` object and finally we also
-add the ``user.id`` as a principal for when we want to allow that exact
-user to edit page's they've created.
+Note that the role comes from the ``User`` object. We also add the ``user.id``
+as a principal for when we want to allow that exact user to edit pages which
+they have created.
+
Add the authorization policy
----------------------------
We already added the :term:`authorization policy` in the previous chapter
because :app:`Pyramid` requires one when adding an
-:term:`authentication policy`. However, it was not used anywhere and so we'll
+:term:`authentication policy`. However, it was not used anywhere, so we'll
mention it now.
-Open the file ``tutorial/security.py`` and notice the following lines:
+In the file ``tutorial/security.py``, notice the following lines:
.. literalinclude:: src/authorization/tutorial/security.py
:lines: 38-40
@@ -66,36 +68,38 @@ Open the file ``tutorial/security.py`` and notice the following lines:
:emphasize-lines: 2
:language: python
-We're using the :class:`pyramid.authorization.ACLAuthorizationPolicy` which
-will suffice for most applications. It uses the :term:`context` to define
-the mapping between a :term:`principal` and :term:`permission` for the
-current request via the ``__acl__``.
+We're using the :class:`pyramid.authorization.ACLAuthorizationPolicy`, which
+will suffice for most applications. It uses the :term:`context` to define the
+mapping between a :term:`principal` and :term:`permission` for the current
+request via the ``__acl__``.
+
Add resources and ACLs
----------------------
Resources are the hidden gem of :app:`Pyramid`. You've made it!
-Every URL in a web application is representing a :term:`resource`
-(the **R** in Uniform Resource Locator). Often the resource is something
-in your data model but it could also be an abstraction over many models.
+Every URL in a web application represents a :term:`resource` (the "R" in
+Uniform Resource Locator). Often the resource is something in your data model,
+but it could also be an abstraction over many models.
Our wiki has two resources:
-#. A ``PageResource``. Represents a ``Page`` that is to be viewed or edited.
- Only ``editor`` users as well as the original creator of the ``Page``
- may edit the ``PageResource`` but anyone may view it.
+#. A ``NewPage``. Represents a potential ``Page`` that does not exist. Any
+ logged-in user, having either role of ``basic`` or ``editor``, can create
+ pages.
-#. A ``NewPage``. Represents a potential ``Page`` that does not exist.
- Any logged-in user (roles ``basic`` or ``editor``) can create pages.
+#. A ``PageResource``. Represents a ``Page`` that is to be viewed or edited.
+ ``editor`` users, as well as the original creator of the ``Page``, may edit
+ the ``PageResource``. Anyone may view it.
.. note::
- The wiki data model is simple enough that the ``PageResource`` is
- actually mostly redundant with our ``models.Page`` SQLAlchemy class. It is
- completely valid to combine these into one class. However, for this
- tutorial they are explicitly separated to make it clear the
- parts that :app:`Pyramid` cares about versus application-defined objects.
+ The wiki data model is simple enough that the ``PageResource`` is mostly
+ redundant with our ``models.Page`` SQLAlchemy class. It is completely valid
+ to combine these into one class. However, for this tutorial, they are
+ explicitly separated to make clear the distinction between the parts about
+ which :app:`Pyramid` cares versus application-defined objects.
There are many ways to define these resources, and they can even be grouped
into collections with a hierarchy. However, we're keeping it simple here!
@@ -109,11 +113,11 @@ Open the file ``tutorial/routes.py`` and edit the following lines:
The highlighted lines need to be edited or added.
-The ``NewPage`` has an ``__acl__`` on it that returns a list of
-mappings from :term:`principal` to :term:`permission`. This defines **who**
-can do **what** with that :term:`resource`. In our case we want to only
-allow users with the principals ``role:editor`` and ``role:basic`` to
-have the ``create`` permission:
+The ``NewPage`` class has an ``__acl__`` on it that returns a list of mappings
+from :term:`principal` to :term:`permission`. This defines *who* can do *what*
+with that :term:`resource`. In our case we want to allow only those users with
+the principals of either ``role:editor`` or ``role:basic`` to have the
+``create`` permission:
.. literalinclude:: src/authorization/tutorial/routes.py
:lines: 20-32
@@ -121,8 +125,8 @@ have the ``create`` permission:
:emphasize-lines: 11,12
:language: python
-The ``NewPage`` is loaded as the :term:`context` of the ``add_page``
-route by declaring a ``factory`` on the route:
+The ``NewPage`` is loaded as the :term:`context` of the ``add_page`` route by
+declaring a ``factory`` on the route:
.. literalinclude:: src/authorization/tutorial/routes.py
:lines: 15-16
@@ -130,8 +134,8 @@ route by declaring a ``factory`` on the route:
:emphasize-lines: 2
:language: python
-The ``PageResource`` defines the :term:`ACL` for a ``Page``. It uses an
-actual ``Page`` object to determine **who** can do **what** to the page.
+The ``PageResource`` class defines the :term:`ACL` for a ``Page``. It uses an
+actual ``Page`` object to determine *who* can do *what* to the page.
.. literalinclude:: src/authorization/tutorial/routes.py
:lines: 34-50
@@ -139,8 +143,8 @@ actual ``Page`` object to determine **who** can do **what** to the page.
:emphasize-lines: 14-16
:language: python
-The ``PageResource`` is loaded as the :term:`context` of the ``view_page``
-and ``edit_page`` route by declaring a ``factory`` on the routes:
+The ``PageResource`` is loaded as the :term:`context` of the ``view_page`` and
+``edit_page`` routes by declaring a ``factory`` on the routes:
.. literalinclude:: src/authorization/tutorial/routes.py
:lines: 14-18
@@ -148,14 +152,15 @@ and ``edit_page`` route by declaring a ``factory`` on the routes:
:emphasize-lines: 1,4-5
:language: python
+
Add view permissions
--------------------
At this point we've modified our application to load the ``PageResource``,
including the actual ``Page`` model in the ``page_factory``. The
``PageResource`` is now the :term:`context` for all ``view_page`` and
-``edit_page`` views. Similarly the ``NewPage`` will be the context for
-the ``add_page`` view.
+``edit_page`` views. Similarly the ``NewPage`` will be the context for the
+``add_page`` view.
Open the file ``views/default.py``.
@@ -167,26 +172,27 @@ First, you can drop a few imports that are no longer necessary:
:emphasize-lines: 1
:language: python
-Edit the ``view_page`` view to declare the ``view`` permission and remove
-the explicit checks within the view:
+Edit the ``view_page`` view to declare the ``view`` permission, and remove the
+explicit checks within the view:
.. literalinclude:: src/authorization/tutorial/views/default.py
:lines: 18-23
:lineno-match:
- :emphasize-lines: 2,4
+ :emphasize-lines: 1-2,4
:language: python
-The work of loading the page has already been done in the factory so we
-can just pull the ``page`` object out of the ``PageResource`` loaded as
-``request.context``. Our factory also guarantees we will have a ``Page`` as it
-raises ``HTTPNotFound`` otherwise - again simplifying the view logic.
+The work of loading the page has already been done in the factory, so we can
+just pull the ``page`` object out of the ``PageResource``, loaded as
+``request.context``. Our factory also guarantees we will have a ``Page``, as it
+raises the ``HTTPNotFound`` exception if no ``Page`` exists, again simplifying
+the view logic.
Edit the ``edit_page`` view to declare the ``edit`` permission:
.. literalinclude:: src/authorization/tutorial/views/default.py
:lines: 38-42
:lineno-match:
- :emphasize-lines: 2,4
+ :emphasize-lines: 1-2,4
:language: python
Edit the ``add_page`` view to declare the ``create`` permission:
@@ -194,22 +200,21 @@ Edit the ``add_page`` view to declare the ``create`` permission:
.. literalinclude:: src/authorization/tutorial/views/default.py
:lines: 52-56
:lineno-match:
- :emphasize-lines: 2,4
+ :emphasize-lines: 1-2,4
:language: python
Note the ``pagename`` here is pulled off of the context instead of
``request.matchdict``. The factory has done a lot of work for us to hide the
actual route pattern.
-The ACLs defined on each :term:`resource` are used by the
-:term:`authorization policy` to determine if any
-:term:`principal` is allowed to have some :term:`permission`. If this check
-fails (for example, the user is not logged in) then a ``HTTPForbidden``
-exception will be raised automatically, thus we're able to drop those
-exceptions and checks from the views themselves. Rather we've defined them in
-terms of operations on a resource.
+The ACLs defined on each :term:`resource` are used by the :term:`authorization
+policy` to determine if any :term:`principal` is allowed to have some
+:term:`permission`. If this check fails (for example, the user is not logged
+in) then an ``HTTPForbidden`` exception will be raised automatically. Thus
+we're able to drop those exceptions and checks from the views themselves.
+Rather we've defined them in terms of operations on a resource.
-The final ``tutorial/views/default.py`` should look like the following:
+The final ``views/default.py`` should look like the following:
.. literalinclude:: src/authorization/tutorial/views/default.py
:linenos:
@@ -227,21 +232,29 @@ following URLs, checking that the result is as expected:
is executable by any user.
- http://localhost:6543/FrontPage invokes the ``view_page`` view of the
- ``FrontPage`` page object.
+ ``FrontPage`` page object. There is a "Login" link in the upper right corner
+ while the user is not authenticated, else it is a "Logout" link when the user
+ is authenticated.
- http://localhost:6543/FrontPage/edit_page 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.
+ ``FrontPage`` object. It is executable by only the ``editor`` user. If a
+ different user (or the anonymous user) invokes it, then a login form will be
+ displayed. Supplying the credentials with the username ``editor`` and
+ password ``editor`` will display the edit page form.
- http://localhost:6543/add_page/SomePageName 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.
+ It is executable by either the ``editor`` or ``basic`` user. If a different
+ user (or the anonymous user) invokes it, then a login form will be displayed.
+ Supplying the credentials with either the username ``editor`` and password
+ ``editor``, or username ``basic`` and password ``basic``, will display the
+ edit page form.
+
+- http://localhost:6543/SomePageName/edit_page is editable by the ``basic``
+ user if the page was created by that user in the previous step. If, instead,
+ the page was created by the ``editor`` user, then the login page should be
+ shown for the ``basic`` user.
- After logging 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 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.