Overview
This plugin provides decorators to add access rules to controllers and controller actions, which have been created with extensibility in mind so that you can adapt them to suit your needs.
To set controller-wide access rules, you can use the ActionProtector class decorator as in the example below:
from repoze.what.predicates import has_permission
from repoze.what.plugins.pylonshq import ActionProtector
class RootController(YourBaseController):
# Place an expose() here if you use TG2
def index(self):
return nice_function_to_do_nothing()
# Place an expose() here if you use TG2
@ActionProtector(has_permission('edit-articles'))
def edit_article(self, article_id):
return get_article_edition_form(article_id)
With the controller and action controllers above, anyone who tries to access /edit_article will have to be granted the edit-articles permission. Otherwise, authorization will be denied.
Tip
TurboGears 2 provides the tg.require decorator to set the access rules in controller actions, which is a subclass of ActionProtector with additional functionality specific to TG2 applications.
To set controller-wide access rules, you can use the ControllerProtector class decorator as in the example below:
from repoze.what.predicates import has_permission
from repoze.what.plugins.pylonshq import ControllerProtector
@ControllerProtector(has_permission('manage'))
class ControlPanel(YourBaseController):
# Place an expose() here if you use TG2
def index(self):
return nice_function_to_do_nothing()
# Place an expose() here if you use TG2
def delete_user(self, user_id):
return nice_function_to_delete_a_user(user_id)
class RootController(YourBaseController):
panel = ControlPanel()
# Place an expose() here if you use TG2
def index(self):
return nice_function_to_do_nothing()
With the controllers and action controllers above, anyone who tries to access /panel/* will have to be granted the manage permission. Otherwise, authorization will be denied.
Tip
As of version 2.0b6, TurboGears provides the tg.allow_only decorator for controller-wide authorization, which is a subclass of ControllerProtector with additional functionality specific to TG2 applications.
Note
If you’re using Python v2.4 or v2.5, you will have to use the alternate syntax because class decorators are supported as of Python v2.6:
class ControlPanel(YourBaseController):
# ...
pass
ControlPanel = ControllerProtector(has_permission('manage'))(ControlPanel)
By default, an authorization denial triggers one of the following actions:
If you want to override the default behavior when authorization is denied, you have define a so-called “denial handler”. A denial handler is a callable which receives one positional argument (which is the message that describes why authorization is denied; this is, the relevant repoze.what predicate message) and is called only when authorization is denied.
The following is a denial handler:
# This is yourapplication.anotherpackage
from pylons import request, response
from pylons.controllers.util import abort
# nice_flash is a function that inserts a user-visible message in the
# template
from yourapplication.somepackage import nice_flash
def cool_denial_handler(reason):
# When this handler is called, response.status has two possible values:
# 401 or 403.
if response.status_int == 401:
message = 'Oops, you have to login: %s' % reason
message_type = 'warning'
else:
identity = request.environ['repoze.who.identity']
userid = identity['repoze.who.userid']
message = "Come on, %s, you know you can't do that: %s" % (userid,
reason)
message_type = 'error'
nice_flash(message, message_type)
abort(response.status_int, comment=reason)
Attention
The denial handler above must call abort(), otherwise we’d be granting access to the request denied by repoze.what. Note that this is a feature, not a bug: In some situations you may not want to abort (e.g., you may want to redirect).
And you can use it as in:
from repoze.what.predicates import has_permission
from repoze.what.plugins.pylonshq import ActionProtector, ControllerProtector
from yourapplication.anotherpackage import cool_denial_handler
@ControllerProtector(has_permission('manage'), cool_denial_handler)
class ControlPanel(YourBaseController):
# Place an expose() here if you use TG2
def index(self):
return nice_function_to_do_nothing()
# Place an expose() here if you use TG2
def delete_user(self, user_id):
return nice_function_to_delete_a_user(user_id)
class RootController(YourBaseController):
panel = ControlPanel()
# Place an expose() here if you use TG2
def index(self):
return nice_function_to_do_nothing()
# Place an expose() here if you use TG2
@ActionProtector(has_permission('edit-articles'), cool_denial_handler)
def edit_article(self, article_id):
return get_article_edition_form(article_id)
Then, when authorization is denied:
Sometimes you may need to customize the controller and controller action protectors in many places within your application (or in the whole application). All you have to do is subclass the relevant protector.
For example, if we use the cool_denial_handler function above very often, then we should create controller and controller action protectors which use that handler by default:
# This is yourapplication.yetanotherpackage
from repoze.what.plugins.pylonshq import ActionProtector, ControllerProtector
from yourapplication.anotherpackage import cool_denial_handler
class CoolActionProtector(ActionProtector):
default_denial_handler = staticmethod(cool_denial_handler)
class CoolControllerProtector(ControllerProtector):
protector = CoolActionProtector
# The following is an alternate way to define CoolControllerProtector:
# class CoolControllerProtector(ControllerProtector):
# default_denial_handler = staticmethod(cool_denial_handler)
Then our controllers would look like this:
from repoze.what.predicates import has_permission
from yourapplication.yetanotherpackage import CoolActionProtector, \
CoolControllerProtector
@CoolControllerProtector(has_permission('manage'))
class ControlPanel(YourBaseController):
# Place an expose() here if you use TG2
def index(self):
return nice_function_to_do_nothing()
# Place an expose() here if you use TG2
def delete_user(self, user_id):
return nice_function_to_delete_a_user(user_id)
class RootController(YourBaseController):
panel = ControlPanel()
# Place an expose() here if you use TG2
def index(self):
return nice_function_to_do_nothing()
# Place an expose() here if you use TG2
@CoolActionProtector(has_permission('edit-articles'))
def edit_article(self, article_id):
return get_article_edition_form(article_id)
And every time authorization is denied, the cool_denial_handler function will be called.