Table of Contents
This page will show you how to use identity in your controller and templates to manage access to your application’s resources. If you don’t know how to enable identity for your project yet, please read the tutorial linked in the section “Further Reading” at the end of this end page first.
To restrict access to a controller method, add an @identity.require(...) decorator to that method. This decorator takes a single argument, the predicate that specifies the conditions that must be true for the access to be permitted. For example to protect the index page so that only members of the admin group can access it you would use the identity.in_group('admin') predicate:
class Root(controllers.RootController):
@expose()
@identity.require(identity.in_group('admin'))
def index(self):
...
After making the above change (your server should restart automatically when you save the controllers.py file), visit http://localhost:8080/. Since the index page is now protected, you will be redirected to the login page where you must authenticate yourself. Log in using the name and password of the account you created in the Getting Started With Identity tutorial. Now you should see the index page, with “Welcome, your name.” and a “Logout” link at the top of the page. This is created for you in the master.kid template.
Sometimes, just restricting access to individual methods (pages) isn’t enough. For example, you might want to protect an entire controller, or maybe your access permissions depend on the data viewed.
If you wrap an object with a identity.SecureObject object, you can restrict access to its attributes with the same predicates you can use for the identity.require decorator:
secure_obj = SecureObject(obj, identity.in_group('premium_users'))
This functionality is used by the SecureResource object discussed in the next section.
To restrict access to an entire controller (i.e. a whole URL subtree), add identity.SecureResource to the base classes of your Controller and add a require attribute at the class level:
class MySecureController(turbogears.Controller, identity.SecureResource):
require = identity.in_group('admin')
# etc ...
You can apply whatever decorators you want on the methods of the MySecureController instance. So each method could have additional restrictions. And MySecureController could have SecureObjects as well. However, access to exposed methods of MySecureController and any SecureObjects would have to satisfy the authorization requirements for MySecureController first.
Let’s say you are creating a web site where users can add their own content, like a blogging tool or a photo sharing site. Users should be able to edit their own content, but not the content added by other users. You can’t do these checks in a decorator, as you need access to the actual data, which is only loaded in the method body (and you don’t want to load the data twice). Instead of using a decorator you can perform the identity checks in the method body.
Again, derive your controller from identity.SecureResource. Perform your identity check at the method level. If the user doesn’t have the required permissions, throw a suitable IdentityException:
class GroupMembershipRequiredException(identity.IdentityException):
message = "Access denied: you must belong to one of these groups: %s"
def __init__(self, groups=None):
if groups is None:
groups = (,)
self.groups = groups
def __str__(self):
return self.message % ", ".join(self.groups)
class MyController(controllers.Controller, identity.SecureResource):
@turbogears.expose('mytemplate')
def myFunction(self):
if not ('admin' in identity.current.groups or
'super' in identity.current.groups):
raise GroupMembershipRequiredException(('admin', 'super'))
This will work because SecureResource wraps all exposed methods with code that checks permissions and traps (sub-classes of) IdentityException and throws an IdentityFailure exception if necessary, which will cause a redirect to the configured identity.failure_url (i.e the login page). So if your code raises an IdentityException or a sub-class thereof, everything will be handled correctly.
Of course, you can then pull your authorization logic out into a function that you call rather than copying and pasting it into each function that requires it.
Important
If you use SecureResource, it is important that all your sub-controllers inherit from turbogears.controllers.Controller, otherwise identity protection will not propagate down the controller object tree and methods of sub-controllers will be unprotected.
You can also use the identity predicates in your own code, which makes handling error messages a bit more comfortable:
errors = []
if (identity.in_group('admin', errors) and
identity.has_permission('edit', errors)):
# everything ok
else:
raise IdentityFailure(errors)
Identity checks can also be used in the templates to customize the appearance of the page depending on the user’s identity. For example, you might show links to administrative functions only if the user is an administrator. (You still need to check identity in the controllers that handle those links: just hiding them doesn’t prevent a knowledgeable user from accessing those URIs directly.)
Within a template, tg.identity is an alias for turbogears.identity.current.
Checking access groups:
<a py:if="'admin' in tg.identity.groups" href="/admin">This is a link for
admins</a>
Checking access permissions:
<div py:if="'write' in tg.identity.permissions">This is a write
permissions area</div>
Displaying user-specific information:
<div py:if="tg.identity.anonymous">Welcome, guest!</div>
<div py:if="not tg.identity.anonymous">Welcome,
${tg.identity.user.display_name}!</div>
Identity configuration is done in app.cfg. Normally, you shouldn’t need to change any of the identity configuration options if you use the default identity framework setup. But if you want to customize some aspects of the identity model or how identity failures are handled, please refer to the identity configuration reference for a thorough description of the available settings.
A User object instance has the following attributes, properties and methods:
A Group object instance has the following attributes, properties and methods:
A Permission object instance has the following attributes, properties and methods:
A visit identifies a unique visitor to the site for a certain timeframe (see the Visit documentation). A Visit object instance has the following attributes and methods:
A link between a Visit and an identity, i.e. an authenticated user or an anynymous identity. A VisitIdentity object instance has the following attributes, properties and methods:
The identity module provides the standard identity predicates listed below. If you need to define your own custom predicates, please refer to the identity recipes page.
Predicates are classes which specify the conditions which must be met to allow access to a resource. The standard predicates all live in the identity namespace and you need to create an instance of a predicate class and test it for its boolean value to perform the check of their condition against the identity of the current request. In most cases, you pass the predicate instance to the identity.require decorator, which does this implicitly before your controller method is called. Most predicates take arguments to specify the parameters of the condition.
For example:
from turbogears import identity
class Root(controllers.RootController):
@expose()
@identity.require(identity.in_group('admin'))
def index(self):
...
or:
from turbogears import identity
class Root(controllers.RootController):
@expose()
def index(self):
if identity.has_permission('delete'):
# permisssion granted
else:
# access denied
Checking group membership:
Checking access permissions:
Checking origin of the request:
You can combine several predicates using identity.Any, identity.NotAny and identity.All. Each of these takes multiple predicates as arguments.
Example usage:
@identity.require(identity.Any(identity.in_group('admin'),
identity.has_permission('edit')))
The decorator above grants access to members of the ‘admin’ group as well as any user who has the ‘edit’ permission. Also, a combination of host and permissions requirements is quite common:
@identity.require(identity.All(identity.from_host('127.0.0.1'),
identity.has_permission('edit')))
Grants access to anybody connecting from a host with the IP address 127.0.0.1 (the local host) and authenticating as a user with the 'edit' permission.
@identity.require(identity.All(
identity.from_any_host(('127.0.0.1', '10.0.0.1')),
identity.in_group('editor')))
Grants access to anybody connecting from a host with an IP address of either 127.0.0.1 or 10.0.0.1 (the local host) and authenticating as a user who belongs to the 'editor' group.
In the controller, you have access to the turbogears.identity.current object, which is a wrapper around the identity data for the current request. It provides the following properties and methods.
The identity module defines several exceptions:
Most of the above exceptions are raised by the TurboGears framework. Your code would normally subclass IdentityException and raise this if a user attempts to access a resource for which they do not possess the necessary permissions. If your controller inherits from identity.SecureResource, the identity framework will then take care of triggering an IdentityFailure exception. Normally you would not raise IdentityFailure directly in your controllers. See above under Explicit Permission Checking for an example.