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 33 (modified by fredlin, 13 years ago) (diff)

APIs

Identity Management

Work Version: > 0.92a2

It's a short How To for getting TurboGears identity management support up and running.

This How To is written from the perspective of a fresh quick-started project, but most everything applies for existing projects.

Quick start example

Step 1 - Create new project

$ tg-admin quickstart 

Name your project idtest and set the dburi in dev.cfg to point to a server and database you want to use.

Step 2 - Edit project_name/config/app.cfg

Edit app.cfg. Under the "IDENTITY" heading (around line 68), uncomment and edit the following to turn on identity management. Edit the failure url as well.

visit.on=True
identity.on = True
identity.failure_url = "/login"

Step 3 - Testing the login

Start the project:

$ start-project_name

The database will be created by this commands, visit login page  http://localhost:8080/login and login with any username and password. It should fail with the message since you haven't add user/group/permision.

You can access any other pages without trouble, because we haven't "protect" those pages by identity decorator.

Step 4 - protect your pages

Edit controllers.py,

Add this code to the top of the file:

from turbogears import identity

If you want protect any page(a python method), add an identity decorator over the page.

The following example use "@identity.require()" to protect the index page:

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

The require decorator checked whether the visitor was a member of the admin group AND had the permission foo AND had the permission bar.

Let's visit the  http://localhost:8080/, now the index page is protected.

Note: You may need to revise the above code for the @identity.require decorator. In a  mailing list, Jeff Watkins writes the most of usages.(see the end of the post)

Step 5 - Create a user and group

We just learn howto protect our page, but now we can't access those pages any more. We need add some user/group/admission to login to the protected pages.

Using Catwalk is probably the easiest way to create user/group/permissions(But it doen't work in 0.92a) Use this method if you can't get Catwalk set up.

(The following section won't work under 0.9a2. patch is  here)

$ tg-admin shell

Python 2.4.1 (#2, Mar 31 2005, 00:05:10) 
[GCC 3.3 20030304 (Apple Computer, Inc. build 1666)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> 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()
>>>

Step 6 - Add the user to admin group

$ tg-admin shell

Python 2.4.1 (#2, Mar 31 2005, 00:05:10) 
[GCC 3.3 20030304 (Apple Computer, Inc. build 1666)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from turbogears.identity.model.somodel import *
>>> hub.begin()
>>> u=TG_User.get(1)
>>> g=TG_Group.get(1)
>>> u.addTG_Group(g)
>>> hub.commit()
>>>

Step 7 - Revisit protected page and login

Browse to  http://localhost:8080/ again and login, this time you should see the content of index page


API

Single Permission Control

You need to use the permission control functions in the identity namespace. For example:

@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" ) )

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

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

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

Combination Permission Control

You can use decorators like theses:

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

The require decorator checked whether the visitor was a member of the admin group AND had the permission foo AND had the permission bar.

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

You can also use these same predicates in your own code:

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


Extend

Strict the group Access

There are three ways to Strict the group Access:

1 Protect your sub-directory

To turn on identity login for an entire controller(restrict access to whole subdirectory), you should be able to derive your Controller from identity.SecureResource? and define 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 Toxicologia instance. So each method could have additional restrictions. And Toxicologia could have SecureObjects? as well. However, access to exposed methods of Toxicologia and any SecureObjects? would have to satisfy the authorisation requirements for Toxicologia.

2 Check the permissions explicitly

Derive your Controller from identity.SecureResource? and define a require attribute at the method level

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 throws, er, 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 into each function that requires it.

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.


Identity and Kid templates

In addition to restricting access to methods in controller files, identity checks can also be used to limit what links(or any other element, for that matter) show up in kid templates. This is done using py:if="" statements, like so:

<a py:if="'admin' in turbogears.identity.current.groups" href="/test">This is a test</a>
<a py:if="'write' in turbogears.identity.current.permissions" href="/test">This is a test</a>

Make sure you import turbogears somewhere in your template for those identity checks to work.

<?python import turbogears ?>

or, to save on typing,

<?python from turbogears import identity ?>

and omit the "turbogears" part of the py:if statement.


Use different classes

You can use your own class on Identity Management.

1 Create SQL tables

Run the application as Step 3. All TG_* tables will be created

2 Edit project_name/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="project_name.model.User"
identity.soprovider.model.group="project_name.model.Group"
identity.soprovider.model.permission="project_name.model.Permission"

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

3 Run the application

Run the application again as in Step 3. Then you can use your classes to manipulate TurboGear? Identity Management.


Following contents haven't been re-processed yet

Applying security settings, not from source code, but from configuration data

You should be able to specify security settings not only from source code but via some other means. The goal is to allow an administrator to set the security policy, not the programmer.


FAQ's

How do I retrieve the userId in my application code?

Actually you can access the entire User object by accessing turbogears.identity.current.user. This gives you access to the userId, displayName, emailAddress, and creation date.

So, if I wanted to access the users’ group info, how would I do that?

There are two ways you can 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.groupId 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.


TurboGears identity management architecture was originally from  Jeff Watkins' blog.

Attachments