diff options
| author | Chris McDonough <chrism@agendaless.com> | 2009-07-01 08:13:25 +0000 |
|---|---|---|
| committer | Chris McDonough <chrism@agendaless.com> | 2009-07-01 08:13:25 +0000 |
| commit | 0688dad3e51361e3274650f39897100063f89459 (patch) | |
| tree | 585ba59c6ddef0aef171116eb682a0a64220b756 | |
| parent | dd7614a8e486735b7106331ca6b86229115de249 (diff) | |
| download | pyramid-0688dad3e51361e3274650f39897100063f89459.tar.gz pyramid-0688dad3e51361e3274650f39897100063f89459.tar.bz2 pyramid-0688dad3e51361e3274650f39897100063f89459.zip | |
- Deprecate the ``authentication_policy`` and ``authorization_policy``
arguments to ``repoze.bfg.router.make_app``. Instead, developers
should use the various authentication policy ZCML directives
(``repozewho1authenticationpolicy``,
``remoteuserauthenticationpolicy`` and
``authtktauthenticationpolicy``) and the `aclauthorizationpolicy``
authorization policy directive as described in the changes to the
"Security" narrative documentation chapter and the wiki tutorials.
| -rw-r--r-- | CHANGES.txt | 9 | ||||
| -rw-r--r-- | docs/api/authentication.rst | 16 | ||||
| -rw-r--r-- | docs/api/authorization.rst | 11 | ||||
| -rw-r--r-- | docs/index.rst | 2 | ||||
| -rw-r--r-- | docs/narr/security.rst | 275 | ||||
| -rw-r--r-- | docs/tutorials/bfgwiki/authorization.rst | 72 | ||||
| -rw-r--r-- | docs/tutorials/bfgwiki/src/authorization/tutorial/configure.zcml | 6 | ||||
| -rw-r--r-- | docs/tutorials/bfgwiki/src/authorization/tutorial/run.py | 15 | ||||
| -rw-r--r-- | docs/tutorials/bfgwiki/src/authorization/tutorial/security.py | 8 | ||||
| -rw-r--r-- | docs/tutorials/bfgwiki2/authorization.rst | 82 | ||||
| -rw-r--r-- | docs/tutorials/bfgwiki2/src/authorization/tutorial/configure.zcml | 6 | ||||
| -rw-r--r-- | docs/tutorials/bfgwiki2/src/authorization/tutorial/run.py | 14 | ||||
| -rw-r--r-- | docs/tutorials/bfgwiki2/src/authorization/tutorial/security.py | 8 | ||||
| -rw-r--r-- | repoze/bfg/router.py | 33 | ||||
| -rw-r--r-- | repoze/bfg/tests/test_router.py | 15 |
15 files changed, 367 insertions, 205 deletions
diff --git a/CHANGES.txt b/CHANGES.txt index d590f63eb..bdaf17545 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,6 +1,15 @@ Next release ============ +- Deprecate the ``authentication_policy`` and ``authorization_policy`` + arguments to ``repoze.bfg.router.make_app``. Instead, developers + should use the various authentication policy ZCML directives + (``repozewho1authenticationpolicy``, + ``remoteuserauthenticationpolicy`` and + ``authtktauthenticationpolicy``) and the `aclauthorizationpolicy`` + authorization policy directive as described in the changes to the + "Security" narrative documentation chapter and the wiki tutorials. + - Add three new ZCML directives which configure authentication policies: diff --git a/docs/api/authentication.rst b/docs/api/authentication.rst deleted file mode 100644 index 6514de510..000000000 --- a/docs/api/authentication.rst +++ /dev/null @@ -1,16 +0,0 @@ -:mod:`repoze.bfg.authentication` -================================ - -.. automodule:: repoze.bfg.authentication - -.. _authentication_policies_api_section: - -Authentication Policies -~~~~~~~~~~~~~~~~~~~~~~~ - -.. autoclass:: AuthTktAuthenticationPolicy - -.. autoclass:: RemoteUserAuthenticationPolicy - -.. autoclass:: RepozeWho1AuthenticationPolicy - diff --git a/docs/api/authorization.rst b/docs/api/authorization.rst deleted file mode 100644 index e44b1e293..000000000 --- a/docs/api/authorization.rst +++ /dev/null @@ -1,11 +0,0 @@ -:mod:`repoze.bfg.authorization` -=============================== - -.. automodule:: repoze.bfg.authorization - -.. _authorization_policies_api_section: - -Authorization Policies -~~~~~~~~~~~~~~~~~~~~~~ - -.. autoclass:: ACLAuthorizationPolicy diff --git a/docs/index.rst b/docs/index.rst index 7691e2400..41d3409b3 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -52,8 +52,6 @@ Per-module :mod:`repoze.bfg` API documentation. .. toctree:: :maxdepth: 2 - api/authentication - api/authorization api/events api/interfaces api/location diff --git a/docs/narr/security.rst b/docs/narr/security.rst index bf60f10b0..e86451570 100644 --- a/docs/narr/security.rst +++ b/docs/narr/security.rst @@ -8,9 +8,8 @@ system that prevents a :term:`view` 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 modifying your application's invocation of -``repoze.bfg.router.make_app``, often located in the ``run.py`` module -of a :mod:`repoze.bfg` application. +Authorization is enabled by modifying your :term:`application +registry` (aka "configure.zcml"). Enabling an Authorization Policy -------------------------------- @@ -18,58 +17,50 @@ Enabling an Authorization Policy By default, :mod:`repoze.bfg` enables no authorization policy. All views are accessible by completely anonymous users. -However, if you modify how your application calls to -``repoze.bfg.router.make_app`` (usually found within the ``run.py`` -module in your application), you can enable an authorization policy. +However, if you modify the :term:`application registry` file in your +application's package (usually named ``configure.zcml``), you can +enable an authorization policy. -You must enable a a :term:`authentication policy` in order to enable -the default authorization policy (this is because authorization, in +You must also enable a a :term:`authentication policy` in order to +enable the an authorization policy (this is because authorization, in general, depends upon authentication). -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: +For example, to enable a policy which compares the value of an "auth +ticket" cookie passed in the request's environment which contains a +reference to a single :term:`principal` against the principals present +in any :term:`ACL` found in model data when attempting to call some +:term:`view`, modify your ``configure.zcml`` to look something like +this: -.. code-block:: python +.. code-block:: xml :linenos: - 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` used by this application. 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: see -:ref:`creating_an_authentication_policy`. - -When you pass any ``authentication_policy`` argument to the -``make_app`` function, and you don't also pass an -``authorization_policy`` argument you are instructing BFG to use the -*default* :term:`authorization policy`. The default authorization -policy compares :term:`ACL` information attached to :term:`context` -objects against the information rovided by the authentication policy. -See :ref:`authorization_policies_api_section` for the details of the -default authorization policy. - -.. note:: It's not common, but it is also possible for a developer to - change the :term:`authorization policy` used by a :mod:`repoze.bfg` - application. See :ref:`creating_an_authorization_policy`. + <configure xmlns="http://namespaces.repoze.org/bfg"> + + <!-- views and other directives before this... --> + + <authtktauthenticationpolicy + secret="iamsosecret"/> + + <aclauthorizationpolicy/> + + </configure> + +"Under the hood", these statements cause an instance of the class +``repoze.bfg.authentication.AuthTktAuthenticationPolicy`` to be +injected as the :term:`authentication policy` used by this application +and an instance of the class +``repoze.bfg.authorization.ACLAuthorizationPolicy`` to be injected as +the :term:`authorization policy` used by this application. + +:mod:`repoze.bfg` ships with a few prechewed authentication and +authorization policies that should prove useful. See +:ref:`authentication_policies_directives_section` and +:ref:`authorization_policies_directives_section` for more information. + +It is also possible to construct your own custom authentication policy +or authorization policy: see :ref:`creating_an_authentication_policy` +and :ref:`creating_an_authorization_policy`. Protecting Views with Permissions --------------------------------- @@ -210,10 +201,11 @@ 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 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.authentication` API docs for -more info on authentication policies). +group information. For example, the ``RepozeWho1AuthenicationPolicy`` +enabled by the ``repozewho1authenticationpolicy`` ZCML directive +respects group information if you configure it with a ``callback``. +See :ref:`authentication_policies_directives_section` for more +information about the ``callback`` attribute. Each tuple within an ACL structure is known as a :term:`ACE`, which stands for "access control entry". For example, in the above ACL, @@ -391,17 +383,161 @@ indicating why permission was denied or allowed. Introspecting this information in the debugger or via print statements when a ``has_permission`` fails is often useful. +.. _authentication_policies_directives_section: + +Built-In Authentication Policy Directives +----------------------------------------- + +:mod:`repoze.who` ships with a few "prechewed" authentication policy +implementations that you can make use of within your application. + +``repozewho1authenticationpolicy`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When this directive is used, authentication information is obtained +from a ``repoze.who.identity`` key in the WSGI environment, assumed to +be set by :term:`repoze.who` middleware. + +An example of its usage, with all attributes fully expanded: + +.. code-block:: xml + :linenos: + + <repozewho1authenticationpolicy + identifier_name="auth_tkt" + callback=".somemodule.somefunc" + /> + +The ``identifier_name`` controls the name used to look up the +:term:`repoze.who` "identifier" plugin within +``environ['repoze.who.plugins']`` which is used by this policy to +"remember" and "forget" credentials. It defaults to ``auth_tkt``. + +The ``callback`` is a Python dotted name to a function passed the +repoze.who identity and the request as positional arguments. The +callback is expected to return None if the user represented by the +identity doesn't exist or a sequence of group identifiers (possibly +empty) if the user does exist. If ``callback`` is None, the userid +will be assumed to exist with no groups. It defaults to ``None``. + +``remoteuserauthenticationpolicy`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When this directive is used, authentication information is obtained +from a ``REMOTE_USER`` key in the WSGI environment, assumed to +be set by a WSGI server or an upstream middleware component. + +An example of its usage, with all attributes fully expanded: + +.. code-block:: xml + :linenos: + + <remoteuserauthenticationpolicy + environ_key="REMOTE_USER" + callback=".somemodule.somefunc" + /> + +The ``environ_key`` is the name that will be used to obtain the remote +user value from the WSGI environment. It defaults to ``REMOTE_USER``. + +The ``callback`` is a Python dotted name to a function passed the +string representing the remote user and the request as positional +arguments. The callback is expected to return None if the user +represented by the string doesn't exist or a sequence of group +identifiers (possibly empty) if the user does exist. If ``callback`` +is None, the userid will be assumed to exist with no groups. It +defaults to ``None``. + +``authtktauthenticationpolicy`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When this directive is used, authentication information is obtained +from an "auth ticket" cookie value, assumed to be set by a custom +login form. + +An example of its usage, with all attributes fully expanded: + +.. code-block:: xml + :linenos: + + <authtktauthenticationpolicy + secret="goshiamsosecret" + callback=".somemodule.somefunc" + cookie_name="mycookiename" + secure="false" + include_ip="false" + timeout="86400" + reissue_time="600" + /> + +The ``secret`` is a string that will be used to encrypt the data +stored by the cookie. It is required and has no default. + +The ``callback`` is a Python dotted name to a function passed the +string representing the userid stored in the cookie and the request as +positional arguments. The callback is expected to return None if the +user represented by the string doesn't exist or a sequence of group +identifiers (possibly empty) if the user does exist. If ``callback`` +is None, the userid will be assumed to exist with no groups. It +defaults to ``None``. + +The ``cookie_name`` is the name used for the cookie that contains the +user information. It defaults to ``repoze.bfg.auth_tkt``. + +``secure`` is a boolean value. If it's set to "true", the cookie will +only be sent back by the browser over a secure (HTTPS) connection. +It defauls to "false". + +``include_ip`` is a boolean value. If it's set to true, the +requesting IP address is made part of the authentication data in the +cookie; if the IP encoded in the cookie differs from the IP of the +requesting user agent, the cookie is considered invalid. It defaults +to "false". + +``timeout`` is an integer value. It represents the maximum age in +seconds allowed for a cookie to live. If ``timeout`` is specified, +you must also set ``reissue_time`` to a lower value. It defaults to +``None``, meaning that the cookie will only live for the duration of +the user's browser session. + +``reissue_time`` is an integer value. If ``reissue_time`` is +specified, when we encounter a cookie that is older than the reissue +time (in seconds), but younger that the ``timeout``, a new cookie will +be issued. It defaults to ``None``, meaning that authentication +cookies are never reissued. + +.. _authorization_policies_directives_section: + +Built-In Authorization Policy Directives +---------------------------------------- + +``aclauthorizationpolicy`` + +When this directive is used, authorization information is obtained +from :term:`ACL` objects attached to model instances. + +An example of its usage, with all attributes fully expanded: + +.. code-block:: xml + :linenos: + + <aclauthorizationpolicy/> + +In other words, it has no configuration attributes; its existence in a +``configure.zcml`` file enables it. + .. _creating_an_authentication_policy: Creating Your Own Authentication Policy --------------------------------------- :mod:`repoze.bfg` ships with a number of useful out-of-the-box -security policies (see :ref:`authentication_policies_api_section`). -However, creating your own authentication policy is often necessary -when you want to control the "horizontal and vertical" of how your -users authenticate. Doing so is matter of creating an instance of -something that implements the following interface: +security policies (see +:ref:`authentication_policies_directives_section`). However, creating +your own authentication policy is often necessary when you want to +control the "horizontal and vertical" of how your users authenticate. +Doing so is matter of creating an instance of something that +implements the following interface: .. code-block:: python @@ -427,9 +563,13 @@ something that implements the following interface: """ Return a set of headers suitable for 'forgetting' the current user on subsequent requests. """ -Pass the object you create into the ``repoze.bfg.router.make_app`` -function as the ``authentication_policy`` argument at application -startup time (usually within a ``run.py`` module). +You will then need to create a ZCML directive which allows you to use +the authentication policy within a ZCML file. See the +``repoze.bfg.zcml`` module in the :mod:`repoze.bfg` source code for +examples of how to create a directive. Authorization policy ZCML +directives should use the ZCML discriminator value +"authentication_policy" in their actions to allow for conflict +detection. .. _creating_an_authorization_policy: @@ -465,9 +605,10 @@ that implements the following interface: """ Return a set of principal identifiers allowed by the permission """ -Pass the object you create into the ``repoze.bfg.router.make_app`` -function as the ``authorization_policy`` argument at application -startup time (usually within a ``run.py`` module). You must also pass -an ``authentication_policy`` if you pass an ``authorization_policy``. -If you pass only an ``authorization_policy`` argument, an error will -be raised at startup time. +You will then need to create a ZCML directive which allows you to use +the authorization policy within a ZCML file. See the +``repoze.bfg.zcml`` module in the :mod:`repoze.bfg` source for +examples of how to create a directive. Authorization policy ZCML +directives should use the ZCML discriminator value +"authorization_policy" in their actions to allow for conflict +detection. diff --git a/docs/tutorials/bfgwiki/authorization.rst b/docs/tutorials/bfgwiki/authorization.rst index e493852ec..304a3964b 100644 --- a/docs/tutorials/bfgwiki/authorization.rst +++ b/docs/tutorials/bfgwiki/authorization.rst @@ -15,34 +15,42 @@ Configuring a ``repoze.bfg`` Authentication Policy -------------------------------------------------- For any :mod:`repoze.bfg` application to perform authorization, we -need to change our ``run.py`` module to add an :term:`authentication -policy`. Adding an authentication policy actually causes the system -to begin to use :term:`authorization`. - -Changing ``run.py`` -~~~~~~~~~~~~~~~~~~~ - -Change your ``run.py`` module to import the -``AuthTktAuthenticationPolicy`` from ``repoze.bfg.authentication``. -Within the body of the ``make_app`` function, construct an instance of -the policy, and pass it as the ``authentication_policy`` argument to -the ``make_app`` function. The first positional argument of an -``AuthTktAuthenticationPolicy`` is a secret used to encrypt cookie -data. Its second argument ("callback") should be a callable that -accepts a userid ana a request. If the userid exists in the system, -the callback should 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 should return -``None``. We'll use "dummy" data to represent user and groups -sources. When we're done, your application's ``run.py`` will look -like this. - -.. literalinclude:: src/authorization/tutorial/run.py +need to add a ``secrity.py`` module and we'll need to change our +:term:`application registry` to add an :term:`authentication policy` +and a :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 add a ``forbidden`` +stanza. This configures our login view to show up when BFG detects +that a view invocation can not be authorized. When you're done, your +``configure.zcml`` will look like so: + +.. literalinclude:: src/authorization/tutorial/configure.zcml :linenos: - :language: python + :language: xml -BFG's ``make_app`` callable also can accept an authorization policy -parameter. We don't need to specify one, we'll use the default. + +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 be a callable that accepts a userid ana 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: + :language: python Adding Login and Logout Views ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -112,18 +120,6 @@ class="main_content">`` div: <span tal:condition="logged_in"><a href="${request.application_url}/logout">Logout</a></span> -Changing ``configure.zcml`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Change your application's ``configure.zcml`` to add a ``forbidden`` -stanza. This configures our login view to show up when BFG detects -that a view invocation can not be authorized. When you're done, your -``configure.zcml`` will look like so: - -.. literalinclude:: src/authorization/tutorial/configure.zcml - :linenos: - :language: xml - Giving Our Root Model Object an ACL ----------------------------------- diff --git a/docs/tutorials/bfgwiki/src/authorization/tutorial/configure.zcml b/docs/tutorials/bfgwiki/src/authorization/tutorial/configure.zcml index d13d812a8..660181918 100644 --- a/docs/tutorials/bfgwiki/src/authorization/tutorial/configure.zcml +++ b/docs/tutorials/bfgwiki/src/authorization/tutorial/configure.zcml @@ -8,4 +8,10 @@ <forbidden view=".login.login"/> + <authtktauthenticationpolicy + secret="sosecret" + /> + + <aclauthorizationpolicy/> + </configure> diff --git a/docs/tutorials/bfgwiki/src/authorization/tutorial/run.py b/docs/tutorials/bfgwiki/src/authorization/tutorial/run.py index 32faa5899..ebe114c6f 100644 --- a/docs/tutorials/bfgwiki/src/authorization/tutorial/run.py +++ b/docs/tutorials/bfgwiki/src/authorization/tutorial/run.py @@ -1,5 +1,4 @@ from repoze.bfg.router import make_app -from repoze.bfg.authentication import AuthTktAuthenticationPolicy from repoze.zodbconn.finder import PersistentApplicationFinder @@ -14,18 +13,6 @@ def app(global_config, **kw): zodb_uri = kw.get('zodb_uri') if zodb_uri is None: raise ValueError("No 'zodb_uri' in application configuration.") - - authpolicy = AuthTktAuthenticationPolicy('seekr!t', callback=groupfinder) - get_root = PersistentApplicationFinder(zodb_uri, appmaker) - return make_app(get_root, tutorial, authentication_policy=authpolicy, - options=kw) - -USERS = {'editor':'editor', - 'viewer':'viewer'} -GROUPS = {'editor':['group.editors']} - -def groupfinder(userid, request): - if userid in USERS: - return GROUPS.get(userid, []) + return make_app(get_root, tutorial, options=kw) diff --git a/docs/tutorials/bfgwiki/src/authorization/tutorial/security.py b/docs/tutorials/bfgwiki/src/authorization/tutorial/security.py new file mode 100644 index 000000000..791367183 --- /dev/null +++ b/docs/tutorials/bfgwiki/src/authorization/tutorial/security.py @@ -0,0 +1,8 @@ +USERS = {'editor':'editor', + 'viewer':'viewer'} +GROUPS = {'editor':['group.editors']} + +def groupfinder(userid, request): + if userid in USERS: + return GROUPS.get(userid, []) + diff --git a/docs/tutorials/bfgwiki2/authorization.rst b/docs/tutorials/bfgwiki2/authorization.rst index f3f5a6f95..d95f54127 100644 --- a/docs/tutorials/bfgwiki2/authorization.rst +++ b/docs/tutorials/bfgwiki2/authorization.rst @@ -61,39 +61,52 @@ Configuring a ``repoze.bfg`` Authentication Policy -------------------------------------------------- For any :mod:`repoze.bfg` application to perform authorization, we -need to change our ``run.py`` module to add an :term:`authentication -policy`. Adding an authentication policy actually causes the system -to begin to use :term:`authorization`. +need to add a ``secrity.py`` module and we'll need to change our +:term:`application registry` to add an :term:`authentication policy` +and a :term:`authorization policy`. Changing ``run.py`` ~~~~~~~~~~~~~~~~~~~ -Change your ``run.py`` module to import the -``AuthTktAuthenticationPolicy`` from ``repoze.bfg.authentication``. -Within the body of the ``make_app`` function, construct an instance of -the policy, and pass it as the ``authentication_policy`` argument to -the ``make_app`` function. The first positional argument of an -``AuthTktAuthenticationPolicy`` is a secret used to encrypt cookie -data. Its second argument ("callback") should be a callable that -accepts a userid (usually a string) and a request object. If the -userid exists in the system, the callback should 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 should return ``None``. We'll use "dummy" data to represent -user and groups sources within ``run.py``. In a "real" application -this information would almost certainly come from some database. - -We'll also use the opportunity to pass the ``RootFactory`` we created -in the step above in as the first argument to ``make_app``. When -we're done, your application's ``run.py`` will look like this. +We'll use the opportunity to pass the ``RootFactory`` we created in +the step above in as the first argument to ``make_app``. When we're +done, your application's ``run.py`` will look like this. .. literalinclude:: src/authorization/tutorial/run.py :linenos: :language: python -BFG's ``make_app`` callable also can accept an "authorization_policy" -parameter. We don't need to specify one, because we'll be using the -default; it is the policy that scans the context for ACLs. +Changing ``configure.zcml`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +We'' change our ``configure.zcml`` file to enable an +``AuthTktAuthenticationPolicy`` and an ``ACLAuthorizationPolicy`` to +enable declarative security checking. We'll also add a ``forbidden`` +stanza. This configures our login view to show up when BFG detects +that a view invocation can not be authorized. When you're done, your +``configure.zcml`` will look like so: + +.. literalinclude:: src/authorization/tutorial/configure.zcml + :linenos: + :language: xml + +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 be a callable that accepts a userid ana 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: + :language: python Adding Login and Logout Views ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -166,15 +179,18 @@ class="main_content">`` div: Changing ``configure.zcml`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Change your application's ``configure.zcml`` to add a ``forbidden`` -stanza which points at our login view. This configures our newly -created login view to show up when BFG detects that a view invocation -can not be authorized. Also, add ``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: +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 BFG detects that a view invocation can not be authorized. Also, +add ``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: .. literalinclude:: src/authorization/tutorial/configure.zcml :linenos: diff --git a/docs/tutorials/bfgwiki2/src/authorization/tutorial/configure.zcml b/docs/tutorials/bfgwiki2/src/authorization/tutorial/configure.zcml index 2904b0793..65b29019b 100644 --- a/docs/tutorials/bfgwiki2/src/authorization/tutorial/configure.zcml +++ b/docs/tutorials/bfgwiki2/src/authorization/tutorial/configure.zcml @@ -53,4 +53,10 @@ <forbidden view=".login.login"/> + <authtktauthenticationpolicy + secret="sosecret" + /> + + <aclauthorizationpolicy/> + </configure> diff --git a/docs/tutorials/bfgwiki2/src/authorization/tutorial/run.py b/docs/tutorials/bfgwiki2/src/authorization/tutorial/run.py index 301f00312..a8ab1ce82 100644 --- a/docs/tutorials/bfgwiki2/src/authorization/tutorial/run.py +++ b/docs/tutorials/bfgwiki2/src/authorization/tutorial/run.py @@ -1,5 +1,4 @@ from repoze.bfg.router import make_app -from repoze.bfg.authentication import AuthTktAuthenticationPolicy import tutorial from tutorial.models import DBSession @@ -26,16 +25,5 @@ def app(global_config, **kw): raise ValueError("No 'db_string' value in application configuration.") initialize_sql(db_string) - authpolicy = AuthTktAuthenticationPolicy('seekr!t', callback=groupfinder) - - return make_app(RootFactory, tutorial, authentication_policy=authpolicy, - options=kw) - -USERS = {'editor':'editor', - 'viewer':'viewer'} -GROUPS = {'editor':['group.editors']} - -def groupfinder(userid, request): - if userid in USERS: - return GROUPS.get(userid, []) + return make_app(RootFactory, tutorial, options=kw) diff --git a/docs/tutorials/bfgwiki2/src/authorization/tutorial/security.py b/docs/tutorials/bfgwiki2/src/authorization/tutorial/security.py new file mode 100644 index 000000000..791367183 --- /dev/null +++ b/docs/tutorials/bfgwiki2/src/authorization/tutorial/security.py @@ -0,0 +1,8 @@ +USERS = {'editor':'editor', + 'viewer':'viewer'} +GROUPS = {'editor':['group.editors']} + +def groupfinder(userid, request): + if userid in USERS: + return GROUPS.get(userid, []) + diff --git a/repoze/bfg/router.py b/repoze/bfg/router.py index 0a863f93a..4eb0fed81 100644 --- a/repoze/bfg/router.py +++ b/repoze/bfg/router.py @@ -239,7 +239,10 @@ def make_app(root_factory, package=None, filename='configure.zcml', authentication or authorization will be performed. Instead, BFG will ignore any view permission assertions in your application and imperative security checks performed by your application will - always return ``True``. + always return ``True``. This argument is deprecated in + :mod:`repoze.bfg` 1.0; use a ZCML directive such as + ``authtktauthenticationpolicy`` instead, as documented in the + Security chapter of the :mod:`repoze.bfg` documentation. ``authorization_policy`` is an object that implements the ``repoze.bfg.interfaces.IAuthorizationPoicy`` interface @@ -249,7 +252,10 @@ def make_app(root_factory, package=None, filename='configure.zcml', authenticate that user. If the ``authentication_policy`` argument is *not* ``None``, and the ``authorization_policy`` argument *is* ``None``, the authorization policy defaults to an authorization - implementation that uses ACLs. + implementation that uses ACLs. This argument is deprecated in + :mod:`repoze.bfg` 1.0; use a ZCML directive such as + ``aclauthorizationpolicy`` instead, as documented in the Security + chapter of the :mod:`repoze.bfg` documentation. ``options``, if used, should be a dictionary containing runtime options (e.g. the key/value pairs in an app section of a @@ -272,12 +278,6 @@ def make_app(root_factory, package=None, filename='configure.zcml', settings = Settings(get_options(options)) registry.registerUtility(settings, ISettings) - if authentication_policy: - registry.registerUtility(authentication_policy, IAuthenticationPolicy) - if authorization_policy is None: - authorization_policy = ACLAuthorizationPolicy() - registry.registerUtility(authorization_policy, IAuthorizationPolicy) - if root_factory is None: root_factory = DefaultRootFactory @@ -287,6 +287,23 @@ def make_app(root_factory, package=None, filename='configure.zcml', mapper = RoutesRootFactory(root_factory) registry.registerUtility(mapper, IRoutesMapper) + if authentication_policy: + debug_logger.warn( + 'The "authentication_policy" and "authorization_policy" ' + 'arguments to repoze.bfg.router.make_app have been deprecated ' + 'in repoze.bfg version 1.0. Instead of using these arguments to ' + 'configure an authorization/authentication policy pair, use ' + 'a pair of ZCML directives (such as "authtktauthenticationpolicy" ' + 'and "aclauthorizationpolicy" documented within the Security ' + 'chapter in the BFG documentation. If you need to use a custom ' + 'authentication or authorization policy, you should make a ZCML ' + 'directive for it and use that directive within your ' + 'application\'s ZCML') + registry.registerUtility(authentication_policy, IAuthenticationPolicy) + if authorization_policy is None: + authorization_policy = ACLAuthorizationPolicy() + registry.registerUtility(authorization_policy, IAuthorizationPolicy) + populateRegistry(registry, filename, package) if mapper.has_routes(): diff --git a/repoze/bfg/tests/test_router.py b/repoze/bfg/tests/test_router.py index 389920029..b0f75d899 100644 --- a/repoze/bfg/tests/test_router.py +++ b/repoze/bfg/tests/test_router.py @@ -780,8 +780,11 @@ class MakeAppTests(unittest.TestCase): from repoze.bfg.interfaces import IAuthorizationPolicy authzpolicy = DummyContext() from repoze.bfg.tests import routesapp - app = self._callFUT(None, routesapp, authorization_policy=authzpolicy) + logger = DummyLogger() + app = self._callFUT(None, routesapp, authorization_policy=authzpolicy, + debug_logger=logger) self.failIf(app.registry.queryUtility(IAuthorizationPolicy)) + self.assertEqual(logger.messages, []) def test_authentication_policy_no_authorization_policy(self): from repoze.bfg.interfaces import IAuthorizationPolicy @@ -789,12 +792,15 @@ class MakeAppTests(unittest.TestCase): from repoze.bfg.authorization import ACLAuthorizationPolicy authnpolicy = DummyContext() from repoze.bfg.tests import routesapp - app = self._callFUT(None, routesapp, authentication_policy=authnpolicy) + logger = DummyLogger() + app = self._callFUT(None, routesapp, authentication_policy=authnpolicy, + debug_logger=logger) self.assertEqual(app.registry.getUtility(IAuthenticationPolicy), authnpolicy) self.assertEqual( app.registry.getUtility(IAuthorizationPolicy).__class__, ACLAuthorizationPolicy) + self.assertEqual(len(logger.messages), 1) # deprecation warning def test_authentication_policy_and_authorization_policy(self): from repoze.bfg.interfaces import IAuthorizationPolicy @@ -802,12 +808,15 @@ class MakeAppTests(unittest.TestCase): authnpolicy = DummyContext() authzpolicy = DummyContext() from repoze.bfg.tests import routesapp + logger = DummyLogger() app = self._callFUT(None, routesapp, authentication_policy=authnpolicy, - authorization_policy = authzpolicy) + authorization_policy = authzpolicy, + debug_logger=logger) self.assertEqual(app.registry.getUtility(IAuthenticationPolicy), authnpolicy) self.assertEqual(app.registry.getUtility(IAuthorizationPolicy), authzpolicy) + self.assertEqual(len(logger.messages), 1) # deprecation warning class TestDefaultForbiddenView(unittest.TestCase): def _callFUT(self, context, request): |
