From a435dba13c6bc0fd0199d06fdbb3e43a4f1263c7 Mon Sep 17 00:00:00 2001 From: Patricio Paez Date: Sat, 7 Apr 2012 10:51:58 -0500 Subject: Normalize Authorization in both tutorials 1 - Sync the content of the introduction and the Viewing the Application in a Browser sections - Sync the section structure --- docs/tutorials/wiki/authorization.rst | 126 ++++++++++++++++++++-------------- 1 file changed, 73 insertions(+), 53 deletions(-) (limited to 'docs/tutorials/wiki/authorization.rst') diff --git a/docs/tutorials/wiki/authorization.rst b/docs/tutorials/wiki/authorization.rst index e599e7086..f8e730e11 100644 --- a/docs/tutorials/wiki/authorization.rst +++ b/docs/tutorials/wiki/authorization.rst @@ -2,31 +2,45 @@ 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 who 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. :app:`Pyramid` provides -facilities for :term:`authorization` and :term:`authentication`. We'll make -use of both features to provide security to our application. - -We will add an :term:`authentication policy` and an -:term:`authorization policy` to our :term:`application -registry`, add a ``security.py`` module and give our :term:`root` -resource an :term:`ACL`. - -Then we will add ``login`` and ``logout`` views, and modify the -existing views to make them return a ``logged_in`` flag to the -renderer and add :term:`permission` declarations to their ``view_config`` -decorators. - -Finally, we will add a ``login.pt`` template and change the existing -``view.pt`` and ``edit.pt`` to show a "Logout" link when not logged in. +:app:`Pyramid` provides facilities for :term:`authentication` and +:term:`authorization`. We'll make use of both features to provide security +to our application. Our application currently allows anyone with access to +the server to view, edit, and add pages to our wiki. We'll change that +to allow only people who 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. + +We will also add a login page and a logout link on all the +pages. The login page will be shown when a user is denied +access to any of the views that require a permission, instead of +a default "403 Forbidden" page. + +We will implement the access control with the following steps: + +* Add users and groups (``security.py``, a new module). +* Add an :term:`ACL` (``models.py`` and + ``__init__.py``). +* Add an :term:`authentication policy` and an :term:`authorization policy` + (``__init__.py``). +* Add :term:`permission` declarations to the ``edit_page`` and ``add_page`` + views (``views.py``). + +Then we will add the login and logout feature: + +* Add routes for /login and /logout (``__init__.py``). +* Add ``login`` and ``logout`` views (``views.py``). +* Add a login template (``login.pt``). +* Make the existing views return a ``logged_in`` flag to the renderer (``views.py``). +* Add a "Logout" link to be shown when logged in and viewing or editing a page + (``view.pt``, ``edit.pt``). The source code for this tutorial stage can be browsed via `http://github.com/Pylons/pyramid/tree/1.3-branch/docs/tutorials/wiki/src/authorization/ `_. +Access Control +-------------- + Add Authentication and Authorization Policies ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -60,8 +74,8 @@ look like so: :linenos: :language: python -Add ``security.py`` -~~~~~~~~~~~~~~~~~~~ +Add users and groups +~~~~~~~~~~~~~~~~~~~~ Add a ``security.py`` module within your package (in the same directory as ``__init__.py``, ``views.py``, etc.) with the following @@ -81,8 +95,8 @@ 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). -Give Our Root Resource an ACL -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Add an ACL +~~~~~~~~~~ We need to give our root resource object an :term:`ACL`. This ACL will be sufficient to provide enough information to the :app:`Pyramid` security @@ -166,8 +180,8 @@ Note that we're relying on some additional imports within the bodies of these views (e.g. ``remember`` and ``forget``). We'll see a rendering of the entire views.py file a little later here to show you where those come from. -Change Existing Views -~~~~~~~~~~~~~~~~~~~~~ +Return a logged_in flag to the renderer +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ In order to indicate whether the current user is logged in, we need to change each of our ``view_page``, ``edit_page`` and ``add_page`` views in @@ -192,8 +206,8 @@ template. For example: logged_in = logged_in, edit_url = edit_url) -Add ``permission`` Declarations to our ``view_config`` Decorators -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Add permission declarations +~~~~~~~~~~~~~~~~~~~~~~~~~~~ To protect each of our views with a particular permission, we need to pass a ``permission`` argument to each of our :class:`pyramid.view.view_config` @@ -224,6 +238,9 @@ decorators. To do so, within ``views.py``: function consults the ``GROUPS`` data structure. This means that the ``editor`` user can add and edit pages. +Login, Logout +------------- + Add the ``login.pt`` Template ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -233,7 +250,7 @@ referred to within the login view we just added to ``views.py``. .. literalinclude:: src/authorization/tutorial/templates/login.pt :language: xml -Change ``view.pt`` and ``edit.pt`` +Add a "Logout" link when logged in ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ We'll also need to change our ``edit.pt`` and ``view.pt`` templates to @@ -249,8 +266,8 @@ class="app-welcome align-right">`` div: Logout -See Our Changes To ``views.py`` and our Templates -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Seeing Our Changes +------------------ Our ``views.py`` module will look something like this when we're done: @@ -270,36 +287,39 @@ Our ``view.pt`` template will look something like this when we're done: :linenos: :language: xml -View the Application in a Browser -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -We can finally examine our application in a browser. The views we'll try are -as follows: +Viewing the Application in a Browser +------------------------------------ -- Visiting ``http://localhost:6543/`` in a browser invokes the ``view_wiki`` - view. This always redirects to the ``view_page`` view of the ``FrontPage`` - page resource. It is executable by any user. +We can finally examine our application in a browser (See +:ref:`wiki-start-the-application`). Launch a browser and visit +each of the following URLs, check that the result is as expected: -- Visiting ``http://localhost:6543/FrontPage/`` in a browser invokes the - ``view_page`` view of the ``FrontPage`` Page resource. This is because +- ``http://localhost:6543/`` invokes the + ``view_wiki`` view. This always redirects to the ``view_page`` view + of the ``FrontPage`` Page resource. It is executable by any user. + +- ``http://localhost:6543/FrontPage`` invokes + the ``view_page`` view of the ``FrontPage`` Page resource. This is because it's the :term:`default view` (a view without a ``name``) for ``Page`` resources. It is executable by any user. -- Visiting ``http://localhost:6543/FrontPage/edit_page`` in a browser invokes - the edit view for the ``FrontPage`` Page resource. 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. +- ``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. -- Visiting ``http://localhost:6543/add_page/SomePageName`` in a - browser invokes the add view for a page. It is executable by only +- ``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 - show the edit page form being displayed. + display the edit page form. -- 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 upper right hand corner. When we click it, - we're logged out, and redirected back to the front page. +- 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 upper right hand + corner. When we click it, we're logged out, and redirected + back to the front page. -- cgit v1.2.3 From 9168ec5a6b96824b35788bf7f1ab5cadb236b392 Mon Sep 17 00:00:00 2001 From: Patricio Paez Date: Sat, 7 Apr 2012 19:48:03 -0500 Subject: Ordered sections as per the summary --- docs/tutorials/wiki/authorization.rst | 155 +++++++++++++++++----------------- 1 file changed, 77 insertions(+), 78 deletions(-) (limited to 'docs/tutorials/wiki/authorization.rst') diff --git a/docs/tutorials/wiki/authorization.rst b/docs/tutorials/wiki/authorization.rst index f8e730e11..76a0db4fc 100644 --- a/docs/tutorials/wiki/authorization.rst +++ b/docs/tutorials/wiki/authorization.rst @@ -27,7 +27,6 @@ We will implement the access control with the following steps: Then we will add the login and logout feature: -* Add routes for /login and /logout (``__init__.py``). * Add ``login`` and ``logout`` views (``views.py``). * Add a login template (``login.pt``). * Make the existing views return a ``logged_in`` flag to the renderer (``views.py``). @@ -41,39 +40,6 @@ The source code for this tutorial stage can be browsed via Access Control -------------- -Add Authentication and Authorization Policies -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -We'll change our package's ``__init__.py`` file to enable an -``AuthTktAuthenticationPolicy`` and an ``ACLAuthorizationPolicy`` to enable -declarative security checking. We need to import the new policies: - -.. literalinclude:: src/authorization/tutorial/__init__.py - :lines: 4-5,8 - :linenos: - :language: python - -Then, we'll add those policies to the configuration: - -.. literalinclude:: src/authorization/tutorial/__init__.py - :lines: 17-22 - :linenos: - :language: python - -Note that the creation of an ``AuthTktAuthenticationPolicy`` requires two -arguments: ``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 reference to a -``groupfinder`` function in the ``tutorial`` package's ``security.py`` file. -We haven't added that module yet, but we're about to. - -When you're done, your ``__init__.py`` will -look like so: - -.. literalinclude:: src/authorization/tutorial/__init__.py - :linenos: - :language: python - Add users and groups ~~~~~~~~~~~~~~~~~~~~ @@ -132,6 +98,74 @@ Our resulting ``models.py`` file will now look like so: :linenos: :language: python +Add Authentication and Authorization Policies +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +We'll change our package's ``__init__.py`` file to enable an +``AuthTktAuthenticationPolicy`` and an ``ACLAuthorizationPolicy`` to enable +declarative security checking. We need to import the new policies: + +.. literalinclude:: src/authorization/tutorial/__init__.py + :lines: 4-5,8 + :linenos: + :language: python + +Then, we'll add those policies to the configuration: + +.. literalinclude:: src/authorization/tutorial/__init__.py + :lines: 17-22 + :linenos: + :language: python + +Note that the creation of an ``AuthTktAuthenticationPolicy`` requires two +arguments: ``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 reference to a +``groupfinder`` function in the ``tutorial`` package's ``security.py`` file. +We haven't added that module yet, but we're about to. + +When you're done, your ``__init__.py`` will +look like so: + +.. literalinclude:: src/authorization/tutorial/__init__.py + :linenos: + :language: python + +Add permission declarations +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To protect each of our views with a particular permission, we need to pass a +``permission`` argument to each of our :class:`pyramid.view.view_config` +decorators. To do so, within ``views.py``: + +- We add ``permission='view'`` to the decorator attached to the + ``view_wiki`` and ``view_page`` view functions. This makes the + assertion that only users who possess the ``view`` permission + against the context resource at the time of the request may + invoke these views. 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`` and ``view_page`` views. + +- We add ``permission='edit'`` to the decorator attached to the + ``add_page`` and ``edit_page`` view functions. This makes the + assertion that only users who possess the effective ``edit`` + permission against the context resource at the time of the + request may invoke these views. We've granted the + ``group:editors`` principal the ``edit`` permission at the + root model via its ACL, so only a user whom is a member of + the group named ``group:editors`` will able to invoke the + ``add_page`` or ``edit_page`` views. We've likewise given + the ``editor`` user membership to this group via the + ``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 and edit pages. + +Login, Logout +------------- + Add Login and Logout Views ~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -180,6 +214,15 @@ Note that we're relying on some additional imports within the bodies of these views (e.g. ``remember`` and ``forget``). We'll see a rendering of the entire views.py file a little later here to show you where those come from. +Add 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 ``views.py``. + +.. literalinclude:: src/authorization/tutorial/templates/login.pt + :language: xml + Return a logged_in flag to the renderer ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -206,50 +249,6 @@ template. For example: logged_in = logged_in, edit_url = edit_url) -Add permission declarations -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -To protect each of our views with a particular permission, we need to pass a -``permission`` argument to each of our :class:`pyramid.view.view_config` -decorators. To do so, within ``views.py``: - -- We add ``permission='view'`` to the decorator attached to the - ``view_wiki`` and ``view_page`` view functions. This makes the - assertion that only users who possess the ``view`` permission - against the context resource at the time of the request may - invoke these views. 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`` and ``view_page`` views. - -- We add ``permission='edit'`` to the decorator attached to the - ``add_page`` and ``edit_page`` view functions. This makes the - assertion that only users who possess the effective ``edit`` - permission against the context resource at the time of the - request may invoke these views. We've granted the - ``group:editors`` principal the ``edit`` permission at the - root model via its ACL, so only a user whom is a member of - the group named ``group:editors`` will able to invoke the - ``add_page`` or ``edit_page`` views. We've likewise given - the ``editor`` user membership to this group via the - ``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 and edit pages. - -Login, Logout -------------- - -Add 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 ``views.py``. - -.. literalinclude:: src/authorization/tutorial/templates/login.pt - :language: xml - Add a "Logout" link when logged in ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- cgit v1.2.3 From 6c3dd2f690c1a92aaf396d44f4b9450a477a67fc Mon Sep 17 00:00:00 2001 From: Patricio Paez Date: Sat, 7 Apr 2012 21:01:25 -0500 Subject: Normalize Authorization in both tutorials 2 - Sync content of Add users and groups, and Add an ACL. - Added yellow highlight to listings in Seeing our changes, added models.py --- docs/tutorials/wiki/authorization.rst | 97 ++++++++++++++++++++++------------- 1 file changed, 61 insertions(+), 36 deletions(-) (limited to 'docs/tutorials/wiki/authorization.rst') diff --git a/docs/tutorials/wiki/authorization.rst b/docs/tutorials/wiki/authorization.rst index 76a0db4fc..ba2d3b48c 100644 --- a/docs/tutorials/wiki/authorization.rst +++ b/docs/tutorials/wiki/authorization.rst @@ -18,8 +18,7 @@ a default "403 Forbidden" page. We will implement the access control with the following steps: * Add users and groups (``security.py``, a new module). -* Add an :term:`ACL` (``models.py`` and - ``__init__.py``). +* Add an :term:`ACL` (``models.py``). * Add an :term:`authentication policy` and an :term:`authorization policy` (``__init__.py``). * Add :term:`permission` declarations to the ``edit_page`` and ``add_page`` @@ -43,60 +42,76 @@ Access Control Add users and groups ~~~~~~~~~~~~~~~~~~~~ -Add a ``security.py`` module within your package (in the same -directory as ``__init__.py``, ``views.py``, etc.) with the following -content: +Create a new ``tutorial/tutorial/security.py`` module with the +following content: .. literalinclude:: src/authorization/tutorial/security.py :linenos: :language: python -The ``groupfinder`` function 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). +The ``groupfinder`` function accepts a userid and a request and +returns one of these values: + +- If the userid exists in the system, it 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, it will + return ``None``. + +For example, ``groupfinder('editor', request )`` returns ['group:editor'], +``groupfinder('viewer', request)`` returns [], and ``groupfinder('admin', request)`` +returns ``None``. We will use ``groupfinder()`` as an :term:`authentication policy` +"callback" that will provide the :term:`principal` or principals +for a user. + +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. Add an ACL ~~~~~~~~~~ -We need to give our root resource object an :term:`ACL`. This ACL will be -sufficient to provide enough information to the :app:`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. +Open ``tutorial/tutorial/models.py`` and add the following import +statements at the head: -We need to perform some imports at module scope in our ``models.py`` file: - -.. code-block:: python +.. literalinclude:: src/authorization/tutorial/models.py + :lines: 4-5 :linenos: + :language: python - from pyramid.security import Allow - from pyramid.security import Everyone - -Our root resource object is a ``Wiki`` instance. We'll add the following -line at class scope to our ``Wiki`` class: +Add the following lines at class scope to the ``Wiki`` class: -.. code-block:: python +.. literalinclude:: src/authorization/tutorial/models.py + :lines: 7-11 :linenos: + :emphasize-lines: 4-5 + :language: python + +We import :data:`~pyramid.security.Allow`, an action that +means that permission is allowed:, and +:data:`~pyramid.security.Everyone`, a special :term:`principal` +that is associated to all requests. Both are used in the +:term:`ACE` entries that make up the ACL. - __acl__ = [ (Allow, Everyone, 'view'), - (Allow, 'group:editors', 'edit') ] +The ACL is a list that needs to be named `__acl__` and be an +attribute of a class. We define an :term:`ACL` with two +:term:`ACE` entries: the first entry allows any user the `view` +permission. The second entry allows the ``group:editors`` +principal the `edit` permission. + +The ``Wiki`` class that contains the ACL is the :term:`resource` +constructor for the :term:`root` resource, which is +a ``Wiki`` instance. The ACL is +provided to each view in the :term:`context` of the request, as +the ``context`` attribute. 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 :app:`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 +simple, so this feature is not demonstrated. See +:ref:`assigning_acls` for more information about what an +:term:`ACL` represents. Add Authentication and Authorization Policies ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -268,22 +283,32 @@ class="app-welcome align-right">`` div: Seeing Our Changes ------------------ +Our ``models.py`` file will look like this: + +.. literalinclude:: src/authorization/tutorial/models.py + :linenos: + :emphasize-lines: 4-5,10-11 + :language: python + Our ``views.py`` module will look something like this when we're done: .. literalinclude:: src/authorization/tutorial/views.py :linenos: + :emphasize-lines: 8,11-15,23,28,49,53,70,74,84,86-119 :language: python Our ``edit.pt`` template will look something like this when we're done: .. literalinclude:: src/authorization/tutorial/templates/edit.pt :linenos: + :emphasize-lines: 41-43 :language: xml Our ``view.pt`` template will look something like this when we're done: .. literalinclude:: src/authorization/tutorial/templates/view.pt :linenos: + :emphasize-lines: 41-43 :language: xml -- cgit v1.2.3 From c226b1ae080aa7d19c47626b07fe6d8ef6bbba9e Mon Sep 17 00:00:00 2001 From: Patricio Paez Date: Sun, 8 Apr 2012 07:34:21 -0500 Subject: Normalize Authorization in both tutorials 3 - Sync content in Adding Authentication and Authorization policies, Add permission declarations sections - Added mising permission=view in SQL tutorial - Moved __init__.py listing to Seeing our changes --- docs/tutorials/wiki/authorization.rst | 98 +++++++++++++++++++---------------- 1 file changed, 54 insertions(+), 44 deletions(-) (limited to 'docs/tutorials/wiki/authorization.rst') diff --git a/docs/tutorials/wiki/authorization.rst b/docs/tutorials/wiki/authorization.rst index ba2d3b48c..516b104b2 100644 --- a/docs/tutorials/wiki/authorization.rst +++ b/docs/tutorials/wiki/authorization.rst @@ -116,67 +116,69 @@ simple, so this feature is not demonstrated. See Add Authentication and Authorization Policies ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -We'll change our package's ``__init__.py`` file to enable an -``AuthTktAuthenticationPolicy`` and an ``ACLAuthorizationPolicy`` to enable -declarative security checking. We need to import the new policies: +Open ``tutorial/__init__.py`` and +add these import statements: .. literalinclude:: src/authorization/tutorial/__init__.py :lines: 4-5,8 :linenos: :language: python -Then, we'll add those policies to the configuration: +Now add those policies to the configuration: .. literalinclude:: src/authorization/tutorial/__init__.py :lines: 17-22 :linenos: + :emphasize-lines: 1-3,5-6 :language: python -Note that the creation of an ``AuthTktAuthenticationPolicy`` requires two -arguments: ``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 reference to a -``groupfinder`` function in the ``tutorial`` package's ``security.py`` file. -We haven't added that module yet, but we're about to. +(Only the highlighted lines need to be added.) -When you're done, your ``__init__.py`` will -look like so: +We are enabling an ``AuthTktAuthenticationPolicy``, it is based in an auth +ticket that may be included in the request, and an ``ACLAuthorizationPolicy`` +that uses an ACL to determine the allow or deny outcome for a view. -.. literalinclude:: src/authorization/tutorial/__init__.py - :linenos: - :language: python +Note that the +:class:`pyramid.authentication.AuthTktAuthenticationPolicy` constructor +accepts two arguments: ``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 the +``groupfinder()`` function that we created before. Add permission declarations ~~~~~~~~~~~~~~~~~~~~~~~~~~~ -To protect each of our views with a particular permission, we need to pass a -``permission`` argument to each of our :class:`pyramid.view.view_config` -decorators. To do so, within ``views.py``: - -- We add ``permission='view'`` to the decorator attached to the - ``view_wiki`` and ``view_page`` view functions. This makes the - assertion that only users who possess the ``view`` permission - against the context resource at the time of the request may - invoke these views. 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`` and ``view_page`` views. - -- We add ``permission='edit'`` to the decorator attached to the - ``add_page`` and ``edit_page`` view functions. This makes the - assertion that only users who possess the effective ``edit`` - permission against the context resource at the time of the - request may invoke these views. We've granted the - ``group:editors`` principal the ``edit`` permission at the - root model via its ACL, so only a user whom is a member of - the group named ``group:editors`` will able to invoke the - ``add_page`` or ``edit_page`` views. We've likewise given - the ``editor`` user membership to this group via the - ``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 and edit pages. +Add a ``permission='edit'`` parameter to the ``@view_config`` +decorator for ``add_page()`` and ``edit_page()``, for example: + +.. code-block:: python + :linenos: + :emphasize-lines: 2 + + @view_config(route_name='add_page', renderer='templates/edit.pt', + permission='edit') + +(Only the highlighted line needs to be added.) + +The result is that only users who possess the ``edit`` +permission at the time of the request may invoke those two views. + +Add a ``permission='view'`` parameter to the ``@view_config`` +decorator for ``view_wiki()`` and ``view_page()``, like this: + +.. code-block:: python + :linenos: + :emphasize-lines: 2 + + @view_config(route_name='view_page', renderer='templates/view.pt', + permission='view') + +(Only the highlighted line needs to be added.) + +This allows anyone to invoke these two views. + +We are done with the changes needed to control access. The +changes that follow will add the login and logout feature. Login, Logout ------------- @@ -283,6 +285,14 @@ class="app-welcome align-right">`` div: Seeing Our Changes ------------------ +When you're done, your ``__init__.py`` will +look like so: + +.. literalinclude:: src/authorization/tutorial/__init__.py + :linenos: + :emphasize-lines: 4-5,8,17-19,21-22 + :language: python + Our ``models.py`` file will look like this: .. literalinclude:: src/authorization/tutorial/models.py @@ -294,7 +304,7 @@ Our ``views.py`` module will look something like this when we're done: .. literalinclude:: src/authorization/tutorial/views.py :linenos: - :emphasize-lines: 8,11-15,23,28,49,53,70,74,84,86-119 + :emphasize-lines: 8,11-15,24,29,50,54,71,75,85,87-120 :language: python Our ``edit.pt`` template will look something like this when we're done: -- cgit v1.2.3 From fad5003b4f0cba6217c23e2f3aa40bf7cb4f8200 Mon Sep 17 00:00:00 2001 From: Patricio Paez Date: Sun, 8 Apr 2012 09:13:06 -0500 Subject: Normalize Authorization in both tutorials 4 - Sync content of Add login and logout views, Add the login.pt template, Return a logged_in flag, Add a logout link sections - Normalize sections of views.py --- docs/tutorials/wiki/authorization.rst | 120 +++++++++++++++++++--------------- 1 file changed, 68 insertions(+), 52 deletions(-) (limited to 'docs/tutorials/wiki/authorization.rst') diff --git a/docs/tutorials/wiki/authorization.rst b/docs/tutorials/wiki/authorization.rst index 516b104b2..874041cc7 100644 --- a/docs/tutorials/wiki/authorization.rst +++ b/docs/tutorials/wiki/authorization.rst @@ -189,92 +189,103 @@ Add 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 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 these views to the existing ``views.py`` file we have in our -project. Here's what the ``login`` view callable will look like: +Add the following import statements to the +head of ``tutorial/tutorial/views.py``: .. literalinclude:: src/authorization/tutorial/views.py - :lines: 86-113 + :lines: 6-13,15-17 :linenos: + :emphasize-lines: 3,6-9,11 :language: python -Here's what the ``logout`` view callable will look like: +(Only the highlighted lines need to be added.) + +:meth:`~pyramid.view.forbidden_view_config` will be used +to customize the default 403 Forbidden page. +:meth:`~pyramid.security.remember` and +:meth:`~pyramid.security.forget` help to create and +expire an auth ticket cookie. + +Now add the ``login`` and ``logout`` views: .. literalinclude:: src/authorization/tutorial/views.py - :lines: 115-119 + :lines: 87-120 :linenos: :language: python -Note that the ``login`` view callable has *two* view configuration -decorators. The order of these decorators is unimportant. Each just adds a -different :term:`view configuration` for the ``login`` view callable. - -The first view configuration decorator configures the ``login`` view callable -so it will be invoked when someone visits ``/login`` (when the context is a -Wiki and the view name is ``login``). The second decorator, named -``forbidden_view_config`` specifies a :term:`forbidden view`. This -configures our login view to be presented to the user when :app:`Pyramid` -detects that a view invocation can not be authorized. Because we've -configured a forbidden view, the ``login`` view callable will be invoked -whenever one of our users tries to execute a view callable that they are not -allowed to invoke as determined by the :term:`authorization policy` in use. -In our application, for example, this means that if a user has not logged in, -and he tries to add or edit a Wiki page, he will be shown the login form. -Before being allowed to continue on to the add or edit form, he will have to -provide credentials that give him permission to add or edit via this login -form. - -Note that we're relying on some additional imports within the bodies of these -views (e.g. ``remember`` and ``forget``). We'll see a rendering of the -entire views.py file a little later here to show you where those come from. +``login()`` is decorated with two decorators: + +- a ``@view_config`` decorator which associates it with the + ``login`` route and makes it visible when we visit ``/login``, +- a ``@forbidden_view_config`` decorator which turns it into + an :term:`forbidden view`. ``login()`` will be invoked + when a users tries to execute a view callable that + they are not allowed to. For example, if a user has not logged in + and tries to add or edit a Wiki page, he will be shown the + login form before being allowed to continue on. + +The order of these two :term:`view configuration` decorators +is unimportant. + +``logout()`` is decorated with a ``@view_config`` decorator +which associates it with the ``logout`` route. It will be +invoked when we visit ``/logout``. Add 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 ``views.py``. +Create ``tutorial/tutorial/templates/login.pt`` with the following +content: .. literalinclude:: src/authorization/tutorial/templates/login.pt :language: xml +The above template is referred to within the login view we just +added to ``views.py``. + Return a logged_in flag to the renderer ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -In order to indicate whether the current user is logged in, 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 the following line to the import at the head of +``tutorial/tutorial/views.py``: -.. code-block:: python +.. literalinclude:: src/authorization/tutorial/views.py + :lines: 11-15 :linenos: + :emphasize-lines: 4 + :language: python - from pyramid.security import authenticated_userid - logged_in = authenticated_userid(request) +(Only the highlighted line needs to be added.) -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: +Add a ``logged_in`` parameter to the return value of +``view_page()``, ``edit_page()`` and ``add_page()``, +like this: .. code-block:: python :linenos: + :emphasize-lines: 4 - return dict(page = context, + return dict(page = page, content = content, - logged_in = logged_in, - edit_url = edit_url) + edit_url = edit_url, + logged_in = authenticated_userid(request)) + +(Only the highlighted line needs to be added.) + +:meth:`~pyramid.security.authenticated_userid()` will return None +if the user is not authenticated, or some user id it the user +is authenticated. Add a "Logout" link when logged in ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -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 ``