wiki:IdentityManagement
Warning: Can't synchronize with repository "(default)" (Unsupported version control system "svn": No module named svn). Look in the Trac log for more information.

Version 53 (modified by oefe, 13 years ago) (diff)

fixed list formatting

Identity Management

Work Version: > 0.92a4

The TurboGears identity management architecture was originally designed and written by  Jeff Watkins.

Identity Management can be used in controllers and in templates:

  • In controllers to implement access restrictions
  • In templates to adapt the appearance depending on the user's identity

Here is a Tutorial for getting TurboGears identity management support up and running. It consists of 3 parts:

  1. Identity Management
  2. Part I: Identity Basics
    1. A. Setting up Identity
    2. B. Using Identity in the Controller
    3. C. Identity Predicates
  3. Part II: Fine-Tuning Identity
    1. A. Using Identity in Templates
    2. B. More Access Checking Options
  4. Part III: Advanced Identity
    1. A. Using a Custom Identity Model
    2. B. Retrieve logged users' identity infomation
    3. C. Notes
    4. Add user and group by tg-admin shell

This Tutorial is written from the perspective of a fresh quick-started project, but almost everything applies for existing projects as well.

To begin with the tutorial, let's quick-start a learning project. Of course, everything you learn in the tutorial applies to existing projects as well.

$ tg-admin quickstart 

Name your project "identity_tutorial" and set the database uri in "identity_tutorial/dev.cfg" to point to a server and database you want to use. Refer to the  Wiki in 20 Minutes Tutorial for details.

Part I: Identity Basics

A. Setting up Identity

Step 1: Enabling Identity

By default, Identity is turned off in quickstarted templates. The first step is therefore to edit your app.cfg file to turn on identity management:

Edit identity_tutorial/config/app.cfg to turn on visit and identity. Edit the failure url as well:

# visit.on=False
visit.on=True

# identity.on=False
identity.on = True

# identity.failure_url=None
identity.failure_url = "/login"

Step 2 - Testing the login

Now let's check whether we have all set up correctly so far. Start the project as usual:

$ start-identity_tutorial

Visit the login page  http://localhost:8080/login. You should see a pretty login page with username and password fields. Try to log in. It should fail with an error message since you haven't added any users/groups/permisions yet.

You can access any other page without trouble, because we haven't protected those pages yet.

Step 3 - Import the identity models

Identity uses three tables to store user, group, and permission information. We need to import these tables into our model.

Modify identity_tutorial/model.py to import the TG_User, TG_Group, TG_Permission model classes:

# Uncomment the following line if you wish to use Identity and SO_Provider
from turbogears.identity.soprovider import TG_User, TG_Group, TG_Permission

Step 4 - Adding an initial user and group

Of course, we will need at least one group and one user account to work with. Let's create these with CatWalk

Start Turbogears toolbox

$tg-admin toolbox

Enter CatWalk. You should see the TG_Group, TG_Permission, and TG_User classes on the left side.

a) Create a user

Select TG_User. Click the "Add TG_User+" button, then enter the user information. For example:

displayName : Jane Doe
userId : jdoe
emailAddress : jdoe@example.com
password : xxx

Click "Save".

b) Create an admin group

Select TG_Group. Click the "Add TG_Group+" button. Enter the displayName and groupId, for example:

displayName : Administrators
groupId :admin

Click "Save".

c) Add the new user to the admin group

Note that we are now in the "Browse" tab, with the new group displayed. Click the expansion triangle in front of "groups". This will reveal a "Manage Relations" link. Click on it. CatWalk will display two lists. Select the new user on the right list and click on "Add Selected" to move it to the left list. Then click "Save" to confirm the change.

B. Using Identity in the Controller

If you want to protect any method, add an @identity.require(...) decorator to the method. This decorator has a single argument, the predicate, which specifies the conditions to check. The identity module provides many predicates that you can use.

For example to protect the index page so that only members of the admin group can access it, we can use the identity.in_group("admin") predicate:

    @turbogears.expose(template=".templates.welcome")
    @identity.require(identity.in_group("admin"))
    def index(self):
        ...

Let's try this now. Visit  http://localhost:8080/. As the index page is protected, you will be redirected to the login page. Log in using the name and password of the account which you previosly created. Now you should see the index page.

Note that identity has added a small "Welcome, Jane Doe. Logout" at the top of the page.

C. Identity Predicates

Single Permission Checks

Checking that the user is logged in

@identity.require(identity.not_anonymous())

Checking access groups

@identity.require(identity.in_group("admin"))

Many people wanted something more flexible, any of the following are valid require decorators:

@identity.require(identity.in_all_groups("admin", "editor"))

@identity.require(identity.in_any_group("admin", "editor"))

Checking access permissions

@identity.require(identity.has_permission("edit"))

@identity.require(identity.has_all_permissions("edit", "delete", "update"))

@identity.require(identity.has_any_permission("edit", "delete", "update"))

Checking hosts

@identity.require(identity.from_host("127.0.0.1"))

@identity.require(identity.from_any_host("127.0.0.1", "10.0.0.1"))

Combining Predicates

You can combine several predicates using identity.Any and identity.All:

     @identity.require(identity.Any(identity.in_group("admin"), identity.has_permission("edit")))

This decorator grants acces to members of the "admin" group as well as any user who has the "edit" permission.

     @identity.require(identity.All(identity.from_host("127.0.0.1"), identity.has_permission("edit")))

     @identity.require(identity.All(identity.from_any_host("127.0.0.1", "10.0.0.1"), identity.in_group("editor")))

Part II: Fine-Tuning Identity

A. Using Identity in Templates

Identity checks can also be used in kid 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.

Note: Identity checks in the template don't provide any security, as it is always possible to enter URLs directly. Therefore you shouldn't rely on identity checks in the template for your site's security. Always check the identity in the controller as described above.

Step 1. Import turbogears.identity

Make sure you import turbogears.identity in your template (anywhere before you call the turbogears.identity)

<?python from turbogears import identity ?>

Step 2. Conditional display

Now we can use for example "py:if" attributes to display parts of the page depending on identity information:

Using access groups

<a py:if="'admin' in identity.current.groups" href="/admin">This is a link for admin</a>

Using access permissions

<div py:if="'write' in identity.current.permissions">This is a write permissions area</div>

#TODO: Describe how to display user information

Step 3. Displaying user information

B. More Access Checking Options

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.

1. Protect your sub-directory

To restrict access to an entire controller (subdirectory), derive your Controller from identity.SecureResource? 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 authorisation requirements for MySecureController?.

2. Check the permissions explicitly

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.) So you'll have to 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 MyController(controllers.Controller, identity.SecureResource):

     @turbogears.expose(html="mytemplate")
     def myFunction(self):
         if not ("admin" in identity.current.groups or
                 "super" in identity.current.groups):
             raise identity.GroupMembershipRequiredException(("admin", "super"))

This will work because SecureResource? wraps all exposed methods with code that checks permissions and traps IdentityExceptions?. So if your code, raises an IdentityException?, everything will be handled correctly.

Of course, you can then pull your authorisation logic out into a function that you call rather than copying and pasting it into each function that requires it.

You can also use the identity predicates in your own code:

     if identity.in_group("admin") and identity.has_permission("edit"):
         pass
     else:
         pass

3. Write your own decorator function

This is not for the faint at heart. But it gives you absolute flexibility.

Take a look at the decorators in turbogears/identity/conditions.py. They'll give you a head-start on what you'll have to do.


Part III: Advanced Identity

A. Using a Custom Identity Model

Sometimes, you want to use or own classes for users, groups, and/or permissions. This could be a subclass -- maybe you want to a few attributes to the user class, say an image of the user and a phone number -- or even a complete replacement.

1. Create SQL tables

If you are going to inherit the standard Identity classes, make sure that you run the application once before you implement your own classes. This will create the TG_* tables.

2. Edit identity_tutorial/config/app.cfg

Edit app.cfg, remove the comments from "identity.soprovider" lines.

# The classes you wish to use for your Identity model. Leave these commented out
# to use the default classes for SqlObjectIdentityProvider. Or set them to the
# classes in your model. NOTE: These aren't TG_* because the TG prefix is
# reserved for classes created by TurboGears.
identity.soprovider.model.user="identity_tutorial.model.User"
identity.soprovider.model.group="identity_tutorial.model.Group"
identity.soprovider.model.permission="identity_tutorial.model.Permission"

Change model."User", "Group", "Permission" to whatever you prefer.

3. Run the application

Run the application

$ start-identity_tutorial

Then you can use your classes to manipulate TurboGears Identity Management.


B. Retrieve logged users' identity infomation

1. Retrieve user identity infomation

We can access the entire User object by accessing "turbogears.identity.current.user" to get user identity infomation. This gives you access to the user_name, display_name, email_address, and creation date.

2. Retrieve users’ group identity infomation

There are two ways to access the group information.

1. Via the current identity object:

from turbogears import identity
if 'admin' in identity.current.groups:
    pass

2. Via the user object on the current identity:

from turbogears import identity
if 'admin' in [g.group_name for g in identity.current.user.groups]:
    pass

Option number 2 only works if your using a Model that supports groups on the user object. So, with the default model you’ll be set. Other models might not work so well.


C. Notes

Setting identity by config has not implement yet.

Using tg-admin shell to add identity user/group is not work under 0.9a2. patch is  here


#TODO using SQLAlchemy instead of SQLObject

#TODO LdapIdentityProvider?

Add user and group by tg-admin shell

Use "tg-admin shell" if you can't get Catwalk set up.

$ tg-admin shell

>>> from turbogears.identity.soprovider import *
>>> hub.begin()
>>> u=TG_User(userId="jeff", emailAddress="jeff@metrocat.org",
            displayName="Jeff Watkins", password="xxxxx")
>>> g=TG_Group(groupId="admin", displayName="Administrators")
>>> hub.commit()
>>>

Add the user to admin group

$ tg-admin shell

>>> from turbogears.identity.model.somodel import *
>>> hub.begin()
>>> u=TG_User.get(1)
>>> g=TG_Group.get(1)
>>> u.addTG_Group(g)
>>> hub.commit()
>>>

Attachments