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

--

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.

let's protect the index page:

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

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  recent mailing list post, 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

Extend - protect your sub-directory

To turn on identity login for an entire controller(restrict access to whole subdirectory), you should be able to derive 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.


Following contents haven't been re-processed yet

Specifying an 'or' type for group Access

There are two ways to handle this:

1 Derive your Controller from SecureResource? (in addition to Controller) and check the permissions explicitly. For example:

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.

2 Write your own decorator function. This is not for the faint at heart. But it gives you absolute flexibility. Take a look at the two 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.


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.


Note: Create the database

In stead of step 3, you can create database manually

$ tg-admin sql sql
In the past you decorated your methods as such:

     @turbogears.expose()
     @identity.require( group="admin", permission="foo,bar" )

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

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

     @identity.require( in_group( "admin" ) )
     @identity.require( in_all_groups( "admin", "editor" ) )
     @identity.require( in_any_group( "admin", "editor" ) )
     @identity.require( has_permission( "edit" ) )
     @identity.require( has_all_permissions( "edit", "delete",  
"update" ) )
     @identity.require( has_any_permission( "edit", "delete",  
"update" ) )

But most importantly, you can use decorators like theses:

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

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

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

I still haven't addressed the need for something like `is_owner`,  
because that seems *so* model specific. 

However, you may need to use the in_group, in_all_groups, etc. functions in the identity namespace. For example:

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

changes to

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

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.

...with a little changes to play nice with latest svn [as of 9th Jan 2006]

Attachments