Using Identity

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.

Using Identity in the Controller

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.

Protecting Resources

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.

Protecting Object Attribute Access

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.

Protecting a Controller

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.

Explicit Permission Checking

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)

Using Identity in Templates

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>

Reference

Configuration

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.

The Standard Identity Data Model

User

A User object instance has the following attributes, properties and methods:

user_id
The numerical ID of the user, which should never change as long as the user exists in the database (integer).
user_name
The short user name used e.g. to log in and reference the user. This must be unique within the application (unicode).
display_name
The long user name suitable for displaying to humans (unicode).
email_address
The email address of the user. This must be unique within the application (unicode).
password
The password used to authenticate the user. If the identity.*.encryption_algorithm configuration setting specifies a valid encryption (hashing) algorithm, the password is saved as a hash to the database transparently, i.e. it is transformed automatically when you set the attribute (unicode).
created
A timestamp of the time and date that the user was created in the database (datetime.datetime)
groups
A list of Group objects attached to the user. You can add/remove groups to/from the user by append-ing to or remove-ing from the list and test membership with in, e.g. group in user.groups.
permissions
A property returning the set of permissions the user has, i.e. all permisssions of all groups the user belongs to (set of Permission objects).
by_email_address(email_address)
A class method to retrieve the user from the database by email address lookup. Returns a User instance or None if no matching user is found.
by_user_name(user_name)
A class method to retrieve the user from the database by user name lookup. Returns a User instance or None if no matching user is found.
by_name(user_name)
An alias for by_user_name.

Group

A Group object instance has the following attributes, properties and methods:

group_id
The numerical ID of the group, which should never change as long as the group exists in the database (integer).
group_name
The short group name used e.g. to refer to the group in predicates. This must be unique within the application (unicode).
display_name
The long group name suitable for displaying to humans (unicode).
created
A timestamp of the time and date that the group was created in the database (datetime.datetime)
permissions
A list of all Permission objects attached to the group. You can add/delete permissions from the group by append-ing to or remove-ing from the list and test membership with in, e.g. permission in group.permissions.
users
A list of User objects attached to the group. You can add/delete users from the group by append-ing to or remove-ing from the list and test membership with in, e.g. user in group.users.
visit_identity
The VisitIdentity object currently attached to this user (if any) or None.
by_group_name(group_name)
A class method to retrieve the group from the database by group name lookup. Returns a Group instance or None if no matching group is found.
by_name(group_name)
An alias for by_group_name.

Permission

A Permission object instance has the following attributes, properties and methods:

permission_id
The numerical ID of the permission, which should never change as long as the permission exists in the database (integer).
permission_name
The short permission name used e.g. to refer to the permission in predicates. This must be unique within the application (unicode).
description
A longer textual description of the permission (unicode).
groups
A list of Group objects attached to the permission. You can add/remove groups to/from the permission by append-ing to or remove-ing from the list and test membership with in, e.g. group in permission.groups.
by_permission_name(permission_name)
A class method to retrieve the permission from the database by permission name lookup. Returns a Permission instance or None if no matching permission is found.
by_name(permission_name)
An alias for by_permission_name.

Visit

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:

visit_key
A unique string (40 chars) to identify a visitor (string).
created
A timestamp of the date and time the visit was created, i.e. the visitor was first registered (datetime.datetime).
expiry
A timestamp of the time and date this visit will (has) expire(d). This is renewed for certain period each time the same visitor is registered again before the expiry time is reached (datetime.datetime).
lookup_visit(visit_key)
A class method to look up a visit in the database by visit_key. Returns a Visit instance or None if no matching visit is found.

VisitIdentity

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:

visit_key
A unique string (40 chars) linking the visit-identity to the visit object with the same visit key (string).
user_id
A foreign key linking the visit-identity to the user object with the same user_id or None if the identity is anonymous (integer/None).
user
The User object currently attached to this visit-identity (if any) or None.
by_visit_key(visit_key)
A class method to look up a visit-identity in the database by visit_key. Returns a VisitIdentity instance or None if no matching visit-identity can be found.

Identity Predicates

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

Single Permission Checks

not_anonymous
Checks that the identity is not None, i.e. the current identity is authenticated, or in other words, the user is logged in. Takes no arguments.

Checking group membership:

identity.in_group(groupname)
Checks that the current identity is a member of the group groupname.
identity.in_all_groups(group1[, group2, ... , groupn])
Checks that the current identity is a member of all given groups.
identity.in_any_group(group1[, group2, ... , groupn])
Checks that the current identity is a member of at least one of the given groups.

Checking access permissions:

identity.has_permission(permissionname)
Checks that the current identity has the permision given by permissionname.
identity.has_all_permissions(perm1[, perm2, ... , permn])
Checks that the current identity has all the given permisions.
identity.has_any_permission(perm1[, perm2, ... , permn])
Checks that the current identity has at least one of the given permissions.

Checking origin of the request:

identity.from_host(ip_address)
Checks that the request originated from a host with the given ip_address. The IP address is specified in dotted-quad notation as a string.
identity.from_any_host((ip1[, ip2, ... , ipn]))
Checks that the request originated from a host with an ip_address which is among the ones specified. Accepts a list or tuple of IP addresses in dotted-quad notation.

Combining Predicates

You can combine several predicates using identity.Any, identity.NotAny and identity.All. Each of these takes multiple predicates as arguments.

identity.Any(predicate1[, predicate2, ... , predicaten])
Checks if any of the given predicates evaluates to True.
identity.NotAny(predicate1[, predicate2, ... , predicaten])
Checks that none of the given predicates evaluates to True.
identity.All(predicate1[, predicate2, ... , predicaten])
Checks that all of the given predicates evaluates to True.

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.

identity.current Interface

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.

Properties

visit_key
Returns a string with a unique visit key (from turbogears.visit).
user
Returns a User object if the visitor is authenticated, otherwise returns None.
user_name
Returns User.user_name if the visitor is authenticated, otherwise returns None.
user_id
Returns User.user_id if the visitor is authenticated, otherwise returns None.
anonymous
Returns True if the visitor is not authenticated, False otherwise.
permissions
Returns a sequence of Permission.permission_name strings if the visitor is authenticated. Returns an empty sequence for anonymous visitors.
groups
Returns a sequence of Group.group_name strings if the visitor is authenticated. Returns an empty sequence for anonymous visitors.
group_ids
Returns a sequence of Group.group_id numbers if the visitor is authenticated. Returns an empty sequence for anonymous visitors.
login_url
Returns the URL set by identity.failure_url configuration setting or - if this is a callable - the return value of this callable.

Methods

login()
Associates the identity with the current visit. See the identity recipes page for information on how to use this.
logout()
Removes authentication from the current visitor.

Exceptions

The identity module defines several exceptions:

IdentityException
Base exception type for all identity exceptions.
RequestRequiredException
Raised when identity features are used outside the scope of an HTTP request.
IdentityManagementNotEnabledException
Raised when identity features are used without being enabled in the configuration file.
IdentityConfigurationException
Raised when identity features are incorrectly configured (usually when failure_url is not set).
IdentityFailure
Raised when an access control check fails. As a result TurboGears will redirect to the URL set by the configuration item identity.failure_url. This exception is therfore also a subclass of cherrypy.InternalRedirect. If your configuration files do not set this item then an IdentityConfigurationException will be raised. See the documentation on setting the identity failure url for more details.

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.

Further Reading

  • Before you can use identity, you must set up your project accordingly. See Getting Started With Identity - A tutorial on using the identity system to secure your website - on how to do this.
  • Next, be sure to check out the Identity Recipes - A list of useful identity tips and tricks.