From a1a9fb7128c935848b17c0ce6586991098a17f07 Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Wed, 27 May 2009 04:52:51 +0000 Subject: Merge authchanges branch to trunk. --- docs/api/authentication.rst | 14 ++++ docs/api/authorization.rst | 11 +++ docs/api/security.rst | 27 +++---- docs/api/traversal.rst | 3 - docs/api/view.rst | 2 - docs/conf.py | 4 +- docs/glossary.rst | 26 ++++--- docs/index.rst | 4 +- docs/narr/hooks.rst | 71 +++++++++--------- docs/narr/security.rst | 175 ++++++++++++++++++++------------------------ docs/narr/traversal.rst | 14 ++-- docs/narr/urldispatch.rst | 14 ++-- docs/narr/views.rst | 24 +++--- 13 files changed, 197 insertions(+), 192 deletions(-) create mode 100644 docs/api/authentication.rst create mode 100644 docs/api/authorization.rst (limited to 'docs') diff --git a/docs/api/authentication.rst b/docs/api/authentication.rst new file mode 100644 index 000000000..f51eeb302 --- /dev/null +++ b/docs/api/authentication.rst @@ -0,0 +1,14 @@ +:mod:`repoze.bfg.authentication` +================================ + +.. automodule:: repoze.bfg.authentication + +.. _authentication_policies_api_section: + +Authentication Policies +~~~~~~~~~~~~~~~~~~~~~~~ + +.. autoclass:: RepozeWho1AuthenticationPolicy + +.. autoclass:: RemoteUserAuthenticationPolicy + diff --git a/docs/api/authorization.rst b/docs/api/authorization.rst new file mode 100644 index 000000000..e44b1e293 --- /dev/null +++ b/docs/api/authorization.rst @@ -0,0 +1,11 @@ +:mod:`repoze.bfg.authorization` +=============================== + +.. automodule:: repoze.bfg.authorization + +.. _authorization_policies_api_section: + +Authorization Policies +~~~~~~~~~~~~~~~~~~~~~~ + +.. autoclass:: ACLAuthorizationPolicy diff --git a/docs/api/security.rst b/docs/api/security.rst index 5990f1809..77b60f5d0 100644 --- a/docs/api/security.rst +++ b/docs/api/security.rst @@ -8,14 +8,21 @@ API Functions ~~~~~~~~~~~~~ -.. autofunction:: authenticated_userid +.. autofunction:: authenticated_userid(context, request) -.. autofunction:: effective_principals +.. autofunction:: effective_principals(context, request) .. autofunction:: has_permission .. autofunction:: principals_allowed_by_permission +.. autofunction:: forget + +.. autofunction:: remember + +.. autofunction:: view_execution_permitted + + Constants ~~~~~~~~~ @@ -29,8 +36,8 @@ Constants The special principal id named 'Authenticated'. This principal id is granted to all requests which contain any other non-Everyone - principal id (according to the security policy). Its actual value - is the string 'system.Authenticated'. + principal id (according to the :term:`authentication policy`). + Its actual value is the string 'system.Authenticated'. .. attribute:: ALL_PERMISSIONS @@ -73,15 +80,3 @@ Return Values .. autoclass:: Allowed :members: -.. _security_policies_api_section: - -Security Policies -~~~~~~~~~~~~~~~~~ - -.. autofunction:: WhoACLSecurityPolicy - -.. autofunction:: WhoInheritingACLSecurityPolicy - -.. autofunction:: RemoteUserACLSecurityPolicy - -.. autofunction:: RemoteUserInheritingACLSecurityPolicy diff --git a/docs/api/traversal.rst b/docs/api/traversal.rst index 2f57fd3aa..4b7ac41f5 100644 --- a/docs/api/traversal.rst +++ b/docs/api/traversal.rst @@ -21,9 +21,6 @@ .. autofunction:: traverse -.. note:: A function named ``model_url`` used to be present in this - module. It was moved to :ref:`url_module` in version 0.6.1. - Secondary APIs ~~~~~~~~~~~~~~ diff --git a/docs/api/view.rst b/docs/api/view.rst index 3b34b7e22..40c69d24b 100644 --- a/docs/api/view.rst +++ b/docs/api/view.rst @@ -13,8 +13,6 @@ .. autofunction:: is_response - .. autofunction:: view_execution_permitted - .. autoclass:: bfg_view :members: diff --git a/docs/conf.py b/docs/conf.py index ae594c7d3..bfb096b3b 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -51,9 +51,9 @@ copyright = '2008, Agendaless Consulting' # other places throughout the built documents. # # The short X.Y version. -version = '0.8.1' +version = '0.9dev' # The full version, including alpha/beta/rc tags. -release = '0.8.1' +release = '0.9dev' # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: diff --git a/docs/glossary.rst b/docs/glossary.rst index 92e2264f9..e6efcc565 100644 --- a/docs/glossary.rst +++ b/docs/glossary.rst @@ -141,14 +141,16 @@ Glossary perfom authentication: it leaves it up to an upstream component such as :term:`repoze.who`. :mod:`repoze.bfg` uses the :term:`authentication` data supplied by the upstream component as - one input during :term:`authorization`. + one input during :term:`authorization`. Authentication in + :mod:`repoze.bfg` is performed via an :term:`authentication + policy`. Authorization The act of determining whether a user can perform a specific action. In bfg terms, this means determining whether, for a given context, any :term:`principal` (or principals) associated with the request have the requisite :term:`permission` to allow the request to continue. Authorization in :mod:`repoze.bfg` is performed via - its :term:`security policy`. + its :term:`authorization policy`. Principal A *principal* is a string or unicode object representing a user or a user's membership in a group. It is provided by the @@ -158,14 +160,16 @@ Glossary bar", the request might have information attached to it that would indictate that Bob was represented by three principals: "bob", "group foo" and "group bar". - Security Policy - A security policy in :mod:`repoze.bfg` terms is a bit of code - which accepts a request, the :term:`ACL` associated with a - context, and the :term:`permission` associated with a particular - view, and subsequently determines whether or not the principals - associated with the request can perform the action associated with - the permission based on the ACL found on the :term:`context` (or - any of its parents). + Authorization Policy + An authorization policy in :mod:`repoze.bfg` terms is a bit of + code which has an API which determines whether or not the + principals associated with the request can perform an action + associated with a permission, based on the information found on the + :term:`context`. + Authentication Policy + An authentication policy in :mod:`repoze.bfg` terms is a bit of + code which has an API which determines the current + :term:`principal` (or principals) associated with a request. WSGI `Web Server Gateway Interface `_. This is a Python standard for connecting web applications to web servers, @@ -274,7 +278,7 @@ Glossary object. In :mod:`repoze.bfg`, an interface may be attached to an model object or a request object in order to identify that the object is "of a type". Interfaces are used internally by - :mod:`repoze.bfg` to perform view lookups and security policy + :mod:`repoze.bfg` to perform view lookups and other policy lookups. Interfaces are exposed to application programmers by the ``view`` ZCML directive or the corresponding ``bfg_view`` decorator in the form of both the ``for`` attribute and the diff --git a/docs/index.rst b/docs/index.rst index 20c564396..d86cd895e 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -50,15 +50,17 @@ Per-module :mod:`repoze.bfg` API documentation. .. toctree:: :maxdepth: 2 + api/authentication + api/authorization api/events api/interfaces + api/location api/push api/router api/security api/template api/testing api/traversal - api/location api/url api/view api/wsgi diff --git a/docs/narr/hooks.rst b/docs/narr/hooks.rst index 21906e466..657ad8a67 100644 --- a/docs/narr/hooks.rst +++ b/docs/narr/hooks.rst @@ -88,7 +88,7 @@ an object that implements any particular interface; it simply needs have a ``status`` attribute, a ``headerlist`` attribute, and and ``app_iter`` attribute. -Changing the NotFound application +Changing the NotFound Application --------------------------------- When :mod:`repoze.bfg` can't map a URL to code, it creates and invokes @@ -119,54 +119,55 @@ sample code that implements a minimal NotFound application factory: .. note:: When a NotFound application factory is invoked, it is passed the WSGI environ and the WSGI ``start_response`` handler by :mod:`repoze.bfg`. Within the WSGI environ will be a key named - ``message`` that has a value explaining why the not found error was - raised. This error will be different when the ``debug_notfound`` - environment setting is true than it is when it is false. + ``repoze.bfg.message`` that has a value explaining why the not + found error was raised. This error will be different when the + ``debug_notfound`` environment setting is true than it is when it + is false. -Changing the Unauthorized application -------------------------------------- +Changing the Forbidden Response +------------------------------- When :mod:`repoze.bfg` can't authorize execution of a view based on -the security policy in use, it creates and invokes an Unauthorized -WSGI application. The application it invokes can be customized by -placing something like the following ZCML in your ``configure.zcml`` -file. +the authorization policy in use, it invokes a "forbidden response +factory". Usually this forbidden response factory is a default 401 +response, but it can be overridden as necessary by placing something +like the following ZCML in your ``configure.zcml`` file. .. code-block:: xml :linenos: - + -Replace ``helloworld.factories.unauthorized_app_factory`` with the -Python dotted name to the request factory you want to use. Here's -some sample code that implements a minimal Unauthorized application -factory: +Replace ``helloworld.factories.forbidden_app_factory`` with the Python +dotted name to the forbidden response factory you want to use. The +response factory must accept two parameters: ``context`` and +``request``. The ``context`` is the context found by the router when +the view invocation was denied. The ``request`` is the current +:term:`request` representing the denied action. Here's some sample +code that implements a minimal forbidden response factory: .. code-block:: python - from webob.exc import HTTPUnauthorized + from repoze.bfg.chameleon_zpt import render_template_to_response - class MyUnauthorized(HTTPUnauthorized): - pass + def forbidden_response_factory(context, request): + return render_template_to_response('templates/login_form.pt') - def notfound_app_factory(): - return MyUnauthorized +.. note:: When an forbidden response factory is invoked, it is passed + the request as the second argument. An attribute of the request is + ``environ``, which is the WSGI environment. Within the WSGI + environ will be a key named ``repoze.bfg.message`` that has a value + explaining why the current view invocation was forbidden. This + error will be different when the ``debug_authorization`` + environment setting is true than it is when it is false. -.. note:: When an Unauthorized application factory is invoked, it is - passed the WSGI environ and the WSGI ``start_response`` handler by - :mod:`repoze.bfg`. Within the WSGI environ will be a key named - ``message`` that has a value explaining why the action was not - authorized. This error will be different when the - ``debug_authorization`` environment setting is true than it is when - it is false. - -.. note:: You can influence the status code of Unauthorized responses - by using an alterate unauthorized application factory. For - example, you may return an unauthorized application with a ``403 - Forbidden`` status code, rather than use the default unauthorized - application factory, which sends a response with a ``401 - Unauthorized`` status code. +.. warning:: the default forbidden application factory sends a + response with a ``401 Unauthorized`` status code for backwards + compatibility reasons. You can influence the status code of + Forbidden responses by using an alterate forbidden application + factory. For example, it would make sense to return an forbidden + application with a ``403 Forbidden`` status code. Changing the Default Routes Context Factory ------------------------------------------- diff --git a/docs/narr/security.rst b/docs/narr/security.rst index 3de7f9140..365f19010 100644 --- a/docs/narr/security.rst +++ b/docs/narr/security.rst @@ -9,53 +9,64 @@ from being invoked when the user represented by credentials in the :term:`request` does not have an appropriate level of access in a specific context. -Authorization is enabled by adding configuration to your -``configure.zcml`` which specifies a :term:`security policy`. +Authorization is enabled by modifying your application's invocation of +``repoze.bfg.router.make_app``, often located in the ``run.py`` module +of a :mod:`repoze.bfg` application. -Enabling a Security Policy --------------------------- +Enabling an Authorization Policy +-------------------------------- -By default, :mod:`repoze.bfg` enables no security policy. All views -are accessible by completely anonymous users. +By default, :mod:`repoze.bfg` enables no authorization policy. All +views are accessible by completely anonymous users. -However, if you add the following bit of code to your application's -``configure.zcml``, you will enable a security policy: +However, if you change the call to ``repoze.bfg.router.make_app`` +(usually found within the ``run.py`` module in your application), you +will enable an authorization policy. -.. code-block:: xml +You must enable a a :term:`authentication policy` in order to enable +an authorization policy. + +For example, to enable a policy which compares the ``REMOTE_USER`` +variable passed in the request's environment (as the sole +:term:`principal`) against the principals present in any :term:`ACL` +found in model data when attempting to call some :term:`view`, modify +your ``run.py`` to look something like this: + +.. code-block:: python :linenos: - - -The above inscrutable stanza enables the -``RemoteUserInheritingACLSecurityPolicy`` to be in effect for every -request to your application. The -``RemoteUserInheritingACLSecurityPolicy`` is a policy which compares -the ``REMOTE_USER`` variable passed in the request's environment (as -the sole :term:`principal`) against the principals present in any -:term:`ACL` found in model data when attempting to call some -:term:`view`. The policy either allows the view that the permission -was declared for to be called, or returns a ``401 Unathorized`` -response code to the upstream WSGI server. - -.. note:: Another "inheriting" security policy also exists: - ``WhoInheritingACLSecurityPolicy``. This policy uses principal - information found in the ``repoze.who.identity`` value set into the - WSGI environment by the :term:`repoze.who` middleware rather than - ``REMOTE_USER`` information. This policy only works properly when - :term:`repoze.who` middleware is present in the WSGI pipeline. - -.. note:: "non-inheriting" security policy variants of the - (``WhoACLSecurityPolicy`` and ``RemoteUserACLSecurityPolicy``) also - exist. These policies use the *first* ACL found as the canonical - ACL; they do not continue searching up the context lineage to find - "inherited" ACLs. It is recommended that you use the inheriting - variants unless you need this feature. - -.. note:: See :ref:`security_policies_api_section` for more - information about the features of the default security policies. + from repoze.bfg.router import make_app + from repoze.bfg.authentication import RemoteUserAuthenticationPolicy + + def app(global_config, **kw): + """ This function returns a repoze.bfg.router.Router object. It + is usually called by the PasteDeploy framework during ``paster + serve``""" + # paster app config callback + from myproject.models import get_root + import myproject + policy = RemoteUserAuthenticationPolicy() + return make_app(get_root, myproject, authentication_policy=policy, + options=kw) + +This injects an instance of the +``repoze.bfg.authentication.RemoteUserAuthenticationPolicy`` as the +:term:`authentication policy`. It is possible to use a different +authentication policy. :mod:`repoze.bfg` ships with a few prechewed +authentication policies that should prove useful (see +:ref:`authentication_policies_api_section`). It is also possible to +construct your own authentication policy. Any instance which +implements the interface defined in +``repoze.bfg.interfaces.IAuthenticationPolicy`` can be used. + +It's common but it is also possible to change the default +:term:`authorization policy` (to use some other persistent +authorization mechanism other than ACLs). To do so, pass an object +which implements the ``repoze.bfg.interfaces.IAuthorizationPolicy``) +to ``make_app`` as the ``authorization_policy`` value. +:mod:`repoze.who` ships with only one. See +:ref:`authorization_policies_api_section` for the details of the ACL +authorization policy which is the default Protecting Views with Permissions --------------------------------- @@ -91,9 +102,9 @@ your project's package """ Add blog entry code goes here """ pass -If a security policy is in place when this view is found during normal -application operations, the user will need to possess the ``add`` -permission against the context to be able to invoke the +If an authorization policy is in place when this view is found during +normal application operations, the user will need to possess the +``add`` permission against the context to be able to invoke the ``blog_entry_add_view`` view. Permission names are just strings. They hold no special significance @@ -192,18 +203,18 @@ ACE, as below. ] A principal is usually a user id, however it also may be a group id if -your authentication system provides group information and the security -policy is written to respect them. The -``RemoteUserInheritingACLSecurityPolicy`` does not respect group -information, but other security policies that come with -:mod:`repoze.bfg` do (see the :mod:`repoze.bfg.security` API docs for -more info). +your authentication system provides group information and the +effective :term:`authentication policy` policy is written to respect +group information. The ``RepozeWho1AuthenicationPolicy`` +authentication policy that comes with :mod:`repoze.bfg` respects group +information (see the :mod:`repoze.bfg.security` API docs for more +info on authentication policies). Each tuple within an ACL structure is known as a :term:`ACE`, which stands for "access control entry". For example, in the above ACL, ``(Allow, Everyone, 'view')`` is an ACE. Each ACE in an ACL is -processed by a security policy *in the order dictated by the ACL*. So -if you have an ACL like this: +processed by an authorization policy *in the order dictated by the +ACL*. So if you have an ACL like this: .. code-block:: python :linenos: @@ -217,9 +228,9 @@ if you have an ACL like this: (Deny, Everyone, 'view'), ] -The security policy will *allow* everyone the view permission, even -though later in the ACL you have an ACE that denies everyone the view -permission. On the other hand, if you have an ACL like this: +The authorization policy will *allow* everyone the view permission, +even though later in the ACL you have an ACE that denies everyone the +view permission. On the other hand, if you have an ACL like this: .. code-block:: python :linenos: @@ -233,7 +244,7 @@ permission. On the other hand, if you have an ACL like this: (Allow, Everyone, 'view'), ] -The security policy will deny Everyone the view permission, even +The authorization policy will deny Everyone the view permission, even though later in the ACL is an ACE that allows everyone. Special Principal Names @@ -271,8 +282,8 @@ module. These can be imported for use in ACLs. ACL like so: ``(Allow, 'fred', ALL_PERMISSIONS)``. The ``ALL_PERMISSIONS`` object is actually a standin object that has a ``__contains__`` method that always returns True, which, for all - known security policies, has the effect of indicating that a given - principal "has" any permission asked for by the system. + known authorization policies, has the effect of indicating that a + given principal "has" any permission asked for by the system. Special ACEs ------------ @@ -286,11 +297,11 @@ following: (Deny, Everyone, ALL_PERMISSIONS) This ACE is often used as the *last* ACE of an ACL to explicitly cause -inheriting security policies to "stop looking up the traversal tree" -(effectively breaking any inheritance). For example, an ACL which -allows *only* ``fred`` the view permission in a particular traversal -context despite what inherited ACLs may say when an inheriting -security policy is in effect might look like so: +inheriting authorization policies to "stop looking up the traversal +tree" (effectively breaking any inheritance). For example, an ACL +which allows *only* ``fred`` the view permission in a particular +traversal context despite what inherited ACLs may say when the default +authorization policy is in effect might look like so: .. code-block:: python :linenos: @@ -304,39 +315,11 @@ security policy is in effect might look like so: ACL Inheritance --------------- -While any security policy is in place, if a model object does not have -an ACL when it is the context, its *parent* is consulted for an ACL. -If that object does not have an ACL, *its* parent is consulted for an -ACL, ad infinitum, until we've reached the root and there are no more -parents left. - -With *non-inheriting* security policy variants -(e.g. ``WhoACLSecurityPolicy`` and ``RemoteUserACLSecurityPolicy``), -the *first* ACL found by the security policy will be used as the -effective ACL. No combination of ACLs found during traversal or -backtracking is done. - -With *inheriting* security policy variants -(e.g. ``WhoInheritingACLSecurityPolicy`` and -``RemoteUserInheritingACLSecurityPolicy``), *all* ACLs in the -context's :term:`lineage` are consulted when determining whether -access is allowed or denied. - -:ref:`security_policies_api_section` for more information about the -features of the default security policies and the difference between -the inheriting and non-inheriting variants. - -.. note:: It is recommended that you use the inheriting variant of a - security policy. Inheriting variants of security policies make it - possible for you to form a security strategy based on context ACL - "inheritance" rather than needing to keep all information about an - object's security state in a single ACL attached to that object. - It's much easier to code applications that dynamically change ACLs - if ACL inheritance is used. In reality, the non-inheriting - security policy variants exist only for backwards compatibility - with applications that used them in versions of :mod:`repoze.bfg` - before 0.8. If this backwards compatibility was not required, the - non-inheriting variants probably just wouldn't exist. +While the default :term:`authorization policy` is in place, if a model +object does not have an ACL when it is the context, its *parent* is +consulted for an ACL. If that object does not have an ACL, *its* +parent is consulted for an ACL, ad infinitum, until we've reached the +root and there are no more parents left. Location-Awareness ------------------ diff --git a/docs/narr/traversal.rst b/docs/narr/traversal.rst index 467916779..8eba623e4 100644 --- a/docs/narr/traversal.rst +++ b/docs/narr/traversal.rst @@ -135,13 +135,13 @@ code to execute: for the name ``b``, the router deems that the context is "object ``a``", the view name is ``b`` and the subpath is ``['c']``. -#. If a :term:`security policy` is configured, the router performs a - permission lookup. If a permission declaration is found for the - view name and context implied by the current request, the security - policy is consulted to see if the "current user" (also determined - by the security policy) can perform the action. If he can, - processing continues. If he cannot, an ``HTTPUnauthorized`` error - is raised. +#. If a :term:`authentication policy` is configured, the router + performs a permission lookup. If a permission declaration is + found for the view name and context implied by the current + request, an :term:`authorization policy` is consulted to see if + the "current user" (al determined by the the authentication + policy) can perform the action. If he can, processing continues. + If he cannot, an ``HTTPUnauthorized`` error is raised. #. Armed with the context, the view name, and the subpath, the router performs a view lookup. It attemtps to look up a view from the diff --git a/docs/narr/urldispatch.rst b/docs/narr/urldispatch.rst index c27165731..d43a344f2 100644 --- a/docs/narr/urldispatch.rst +++ b/docs/narr/urldispatch.rst @@ -420,14 +420,14 @@ Using :mod:`repoze.bfg` Security With URL Dispatch -------------------------------------------------- :mod:`repoze.bfg` provides its own security framework which consults a -:term:`security policy` before allowing any application code to be -called. This framework operates in terms of ACLs (Access Control +:term:`authorization policy` before allowing any application code to +be called. This framework operates in terms of ACLs (Access Control Lists, see :ref:`security_chapter` for more information about the -:mod:`repoze.bfg` security subsystem). A common thing to want to do -is to attach an ``__acl__`` to the context object dynamically for -declarative security purposes. You can use the ``factory`` -argument that points at a context factory which attaches a custom -``__acl__`` to an object at its creation time. +:mod:`repoze.bfg` authorization subsystem). A common thing to want to +do is to attach an ``__acl__`` to the context object dynamically for +declarative security purposes. You can use the ``factory`` argument +that points at a context factory which attaches a custom ``__acl__`` +to an object at its creation time. Such a ``factory`` might look like so: diff --git a/docs/narr/views.rst b/docs/narr/views.rst index ac5a8383f..9e9c55236 100644 --- a/docs/narr/views.rst +++ b/docs/narr/views.rst @@ -546,12 +546,12 @@ will be called. View Security ------------- -If a :term:`security policy` is active, any :term:`permission` -attached to a ``view`` declaration will be consulted to ensure -that the currently authenticated user possesses that permission -against the context before the view function is actually called. -Here's an example of specifying a permission in a ``view`` -declaration: +If a :term:`authentication policy` (and a :term:`authorization +policy`) is active, any :term:`permission` attached to a ``view`` +declaration will be consulted to ensure that the currently +authenticated user possesses that permission against the context +before the view function is actually called. Here's an example of +specifying a permission in a ``view`` declaration: .. code-block:: xml :linenos: @@ -563,16 +563,16 @@ declaration: permission="add" /> -When a security policy is enabled, this view will be protected with -the ``add`` permission. The view will *not be called* if the user -does not possess the ``add`` permission relative to the current -:term:`context` and a security policy is enabled. Instead an HTTP -``Unauthorized`` status will be returned to the client. +When an authentication policy is enabled, this view will be protected +with the ``add`` permission. The view will *not be called* if the +user does not possess the ``add`` permission relative to the current +:term:`context` and an authorization policy is enabled. Instead an +HTTP ``Unauthorized`` status will be returned to the client. .. note:: See the :ref:`security_chapter` chapter to find out how to turn on - a security policy. + an authentication policy. .. note:: -- cgit v1.2.3