Authentication

4 views
Skip to first unread message

pierrebai

unread,
Oct 17, 2005, 12:49:51 PM10/17/05
to TurboGears
I saw there is a ticket open for authentication / authorization. I also
read that CatWalk has no security for now.

What I propose is not a design by simply a standardize high-level
function API to check authorization. I currently use the following:

def checkRole(role='admin'):
# checkRole implementation.

In simple word, a checkRole function taking a string for the role, by
default 'admin'. How this is mapped internally, how role and identities
are implemented, represented and accessed is hidden from the API.
Currently checkRole() sets a turbogears flash error then redirect to
the login page ("/" on my site). Maybe checkRole could be extended to
be:

def checkRole(role='admin',error='You are not
authorized.',redirect='/')
# checkRole implementation.

A few points of discussion:
- How to plugin the checker? By setting a pre-defined tg variable?
- Alternative check result: specifiable exception, callback?
- Check without raising error? This currently can be done by a try:
block.

I also think separating authentication API and authorization APi is a
good thing, although a given implementation can (and probably will)
make them common internally for obvious reasons.

Jeff Watkins

unread,
Oct 17, 2005, 1:15:11 PM10/17/05
to turbo...@googlegroups.com
I've been working on an Identity and Access control framework for
TurboGears. This will provide you with the following features for your
TurboGears-based Web applications:

* A require decorator that checks the current user's permissions and/or
group membership
* A SecureResource base class from which you can derive controllers --
to alleviate having to specify the same permission requirements for
every exposed method.
* An IdentityFilter to scour the incoming request and associate it with
a User
* A basic Identity model including classes for User, Group, and
Permission. This model includes a plug-in for the IdentityFilter so that
everything "just works".

Right now I'm stumped on some of the features of the Admin UI. But I'm
increasingly thinking that I should polish up the framework and release
it without the admin UI to get some feedback.

Just a few details:

* Your Identity is stored as a signed cookie. So there's no need for
sessions unless your app uses them. Plus, because the cookie is signed
by the server (SHA1 hash of identityID, expiration, and a "secret token"
provided by the Identity model), there's *less* worry about users faking
their identity.

* The IdentityFilter uses InternalRedirect exceptions within CherryPy to
access a login form that you expose from one of your controllers.
Ultimately, the Admin UI will include a default login form.

* The framework intends to, but does not currently, support HTTP auth.

* The Identity framework takes Ajax into account by returning pages
using appropriate status codes (403 Forbidden). Plus, because you're
going through the TurboGears expose decorator, if you requested JSON
output, you'll really get it. The JSON output contains meaningful fields
you'll be able to trigger off to "do the right thing".

* In the default model, Permissions are associated with Groups
(many-to-many) and Users are associated with Groups (many-to-many). So
it is not possible to directly assign a permission to an individual
user. Yes, this is a little more than necessary for small user
communities but absolutely essential as your community grows (speaking
from the experience of having done Identity Mgmt and Access Control
audits for large clients).


Jeff

--
Jeff Watkins
http://metrocat.org/

Matthew Bevan

unread,
Oct 17, 2005, 8:01:05 PM10/17/05
to TurboGears
I would be willing to see anything you have on the subject. It is one
of the few remaining issues which is proventing me from actually
deploying and developing further some of the projects I have started.
I have almost five projects on hold waiting for a few features. I'll
be hashing out the designs over the next week and posting as
appropriate, of course.

pierrebai

unread,
Oct 17, 2005, 8:21:55 PM10/17/05
to TurboGears
Ah well, if all this is "free" (as in complexity), i'm all for it, but
the reason I advocated a common high-level API for basic checking is
that it allowed choosing among various implementations depending on the
designer requirements. For the simple site I'm designing, your system
sounds "too full". I basically just need admin and non-admin.

I also think it's important to to force a UI down user throats. Your
design could be applied if I can use my existing main-page login form
as a front-end and have your system with a single "hard-coded" (at
deployment) permission.

Why do you feel a user/role many-to-many design is not enough? I don't
see what having group+permission provides above roles.

Jeff Watkins

unread,
Oct 18, 2005, 9:21:29 AM10/18/05
to turbo...@googlegroups.com

On 17 Oct, 2005, at 8:21 pm, pierrebai wrote:
> Ah well, if all this is "free" (as in complexity), i'm all for it, but
> the reason I advocated a common high-level API for basic checking is
> that it allowed choosing among various implementations depending on
> the
> designer requirements.

There is currently nothing about the implementation of the
IdentityFilter, function decorator, or base class that mandates any
particular data model. The require decorator allows you to specify
permissions (either as a single string or as an array) and also
allows you to specify groups (either as a single string or as an array).

Consider the following controller:

class MyController:

@turbogears.expose( html="project.templates.index" )
def index( self ):
pass

@turbogears.expose( html="project.templates.admin_only" )
@identity.require( group="admin" )
def admin( self ):
pass

@turbogears.expose( html="project.templates.change" )
@identity.require( permission="change-setting" )
def change( self ):
pass


This controller imposes the following constraints:

* anyone may access the index method
* only members of the admin group may access the admin method
* only users with the permission "change-setting" may access the
change method.

> For the simple site I'm designing, your system
> sounds "too full". I basically just need admin and non-admin.

I think the previous example shows that you can easily handle admin
and non-admin cases. This was a crucial aspect of my design. I wanted
the identity framework to fit cases where all you wanted was simple
group membership tests all the way up to sophisticated access control
lists.

> I also think it's important to to force a UI down user throats. Your
> design could be applied if I can use my existing main-page login form
> as a front-end and have your system with a single "hard-coded" (at
> deployment) permission.

Like the data model, there is no requirement for you to use the admin
console for the identity framework. It is provided for those who
simply want to get things up and running. Furthermore, the admin
console is *strictly* limited to working with the default (but by no
means required) data model.

You can configure the IdentityFilter to look for the username and
password fields you use (rather than changing your form to use the
one's it expects). And because the access control checks redirect to
an exposed method on one of your controllers, you are *definitely*
free to use your own login form.

> Why do you feel a user/role many-to-many design is not enough? I don't
> see what having group+permission provides above roles.

Basically, you can model a user/role access control system using a
user/group/permission access control system, but you can't go the
other way. Therefore, I felt that the additional work on my part to
support a more robust access control system was definitely worthwhile.

Because I'm building a content management system, I need permissions
like "edit-entity" which can be granted to members of the "author"
and "editor" groups, and "approve-entity" which can be limited to
members of the "editor" group.

Systems with less complex needs can ignore the whole permission
aspect and use only users and groups.

--
Jeff Watkins
http://metrocat.org/

"Not everything that can be counted counts, and not everything that
counts can be counted."
-- Albert Einstein

pierrebai

unread,
Oct 18, 2005, 9:41:51 AM10/18/05
to TurboGears
Sounds good!

I'd suggest that you break up your package into the decorator + filter
and your particular implementation anyway, so that it's easier to
deploy one without the other since you obviously already envisioned
such a scenario.

>Because I'm building a content management system, I need permissions
>like "edit-entity" which can be granted to members of the "author"
>and "editor" groups, and "approve-entity" which can be limited to
>members of the "editor" group.

That's what I thought. Permissions are more for CMS-like needs. Again,
separating the implementation for user+group and permission would be a
good idea in my opinion, but I suppose it may be hard to avoid any
permission reference in groups...?

Elvelind Grandin

unread,
Oct 18, 2005, 10:48:00 AM10/18/05
to turbo...@googlegroups.com
One thing that I think would be Sweet(tm) would be support cherrypy's
path based config system. So that you could put something like this in
the dev/prod.cfg

[/]
group = "users"
[/admin]
group = "admin
...
--
cheers
elvelind grandin

John Speno

unread,
Oct 18, 2005, 11:10:49 AM10/18/05
to turbo...@googlegroups.com

On Oct 18, 2005, at 10:48 AM, Elvelind Grandin wrote:

>
> One thing that I think would be Sweet(tm) would be support cherrypy's
> path based config system. So that you could put something like this in
> the dev/prod.cfg
>
> [/]
> group = "users"
> [/admin]
> group = "admin

I think I've mentioned Subway's auth module before. Here's the only
docs on it:

http://subway.python-hosting.com/wiki/BookErrorsSecurity

But it allows for basic control of authentication via the config
file. E.g.:

[/]
subway.auth.on = False

[/admin]
subway.auth.on = True
subway.auth.login_form_url = '/login?message=$message&url=$url'
subway.auth.required-roles = ['admin', 'super']

Then you could use'cherrypy.config.get('subway.auth.required-roles')'
in the method that
handles checking the username and password. Of course, those are
untested ideas written
in Mail.app so YMMV.

To see an example of a CMS that is using Subway's auth system, take a
look at Bikini:

http://subway.python-hosting.com/browser/sandbox/deelan/bikini/

Take care.


Leandro Lucarella

unread,
Oct 18, 2005, 12:33:02 PM10/18/05
to turbo...@googlegroups.com
Jeff Watkins, el martes 18 de octubre a las 09:21 me escribiste:
Is there any fine-grain access control in your mind? I mean, a page that
shows different data according to your permissions. Maybe a predefined
function in kid template to check for permissions on an if construct? (I'm
not familiar with kid but I guess it could do that.

I'm thinking something like:
<div py:if="identity.hasperm('some-perm')">
or:
<div py:if="identity.hasgroup('some-group')">

Maybe this will couple the view and the controller? I'm not sure this is a
issue, but I guess it could be fixed using something like:
<div py:if="has_perm_edit">
or
<div py:if="has_group_admin">
And make the identity.require decorator to put this extra varibles
available to the templates.


I'm keep thinking and this is the scenario I have in mind. I need a page
to first validate if the user have perm1 AND perm2 OR group1, then, in the
page, show some parts to users with perm1 and perm2 and other parts to
users with group1. The last part can be done with the solution I've talked
above, and for the first part some wrappers like SQLBuilder can be done,
so I can write the require decorator like this:
from identity import AND, Permission, Group
@turbogears.expose( html="project.templates.change" )
@identity.require(OR(AND(Permission("perm1"), Permission("perm2")), Group("group1")))
def change( self ):
pass

identity.Permission and .Group don't have to be real model objects, their
just wrappers for the boolean expression.


I think the authentication API has to be flexible enough to make this kind
of fine-grained access control, even if it's not used that frecuently (is
not?). And all the people who use simpler approaches will no suffer the
'extra features', they can use it in a very simple way (just don't use ifs
in the templates based on permissions and put just one permission in the
require decorator). Even so, the original format permission="perm1" could
be mantained for simple cases, identity.decorator can manage *args and
**kwargs to support both. And even more, a format like:
@identity.require(permissionAND=("perm1", "perm2"))
could be used for a simple AND. And
@identity.require(permissionOR=("perm1", "perm2"))
for a simple OR.

Well, there are really a lot of posibilities, and I'm sure there are
better than the one proposed here... =)

--
LUCA - Leandro Lucarella - JID: luca(en)lugmen.org.ar - Debian GNU/Linux
.------------------------------------------------------------------------,
\ GPG: 5F5A8D05 // F8CD F9A7 BF00 5431 4145 104C 949E BFB6 5F5A 8D05 /
'--------------------------------------------------------------------'
Si ella es el sol, yo soy la luna
Si ella es el mar, soy el desierto
Y estamos en eclipse total
Y estamos en eclipse total

Jeff Watkins

unread,
Oct 18, 2005, 2:44:59 PM10/18/05
to turbo...@googlegroups.com
Leandro Lucarella wrote:
> Is there any fine-grain access control in your mind? I mean, a page that
> shows different data according to your permissions. Maybe a predefined
> function in kid template to check for permissions on an if construct? (I'm
> not familiar with kid but I guess it could do that.

I'm also not terribly familiar with Kid (and perhaps that's why I don't
like it much).

> I'm thinking something like:
> <div py:if="identity.hasperm('some-perm')">
> or:
> <div py:if="identity.hasgroup('some-group')">

There is actually a current identity object which exposes properties for
permissions, groups, and user (where user is a model dependent object).
So you're templates could perform the equivalent of:

if 'some-perm' in identity.current.permissions:
pass

There are also several general-purpose functions (requireGroup and
requirePermission) which you can call directly from your controller code
if your requirements are too complicated for the simple decorator.

> I'm keep thinking and this is the scenario I have in mind. I need a page
> to first validate if the user have perm1 AND perm2 OR group1, then, in the
> page, show some parts to users with perm1 and perm2 and other parts to
> users with group1. The last part can be done with the solution I've talked
> above, and for the first part some wrappers like SQLBuilder can be done,
> so I can write the require decorator like this:
> from identity import AND, Permission, Group
> @turbogears.expose( html="project.templates.change" )
> @identity.require(OR(AND(Permission("perm1"), Permission("perm2")), Group("group1")))
> def change( self ):
> pass

Unfortunately, I think you'll run into conflict with the SQLBuilder AND
and OR functionality. But I think this is an interesting idea.
Unfortunately, you can't use the previously mentioned requireGroup and
requirePermission functions because they raise exceptions rather than
return result codes. I agree that something clever like this would be good.

> I think the authentication API has to be flexible enough to make this kind
> of fine-grained access control, even if it's not used that frecuently (is
> not?).

For the first release of the identity framework, I'll be content handle
the simple cases easily and make the more complex cases possible (via
real code). Otherwise, I'll probably never be able to get this done.
It's bad enough I've been delayed by bugs in json-py and mis-features in
cherrypy.

Leandro Lucarella

unread,
Oct 18, 2005, 2:54:21 PM10/18/05
to turbo...@googlegroups.com
Jeff Watkins, el martes 18 de octubre a las 14:44 me escribiste:
> There is actually a current identity object which exposes properties for
> permissions, groups, and user (where user is a model dependent object).
> So you're templates could perform the equivalent of:
>
> if 'some-perm' in identity.current.permissions:
> pass
>
> There are also several general-purpose functions (requireGroup and
> requirePermission) which you can call directly from your controller code
> if your requirements are too complicated for the simple decorator.

Great! I think kid could do that, so this part looks like it's covered.

> > from identity import AND, Permission, Group
> > @turbogears.expose( html="project.templates.change" )
> > @identity.require(OR(AND(Permission("perm1"), Permission("perm2")),
> > Group("group1")))
> > def change( self ):
> > pass
>
> Unfortunately, I think you'll run into conflict with the SQLBuilder AND
> and OR functionality.

Not necessarily, you have 2 separate implementations in different modules.
You have sqlobject.AND() (or sqlbuilder.AND? I don't remember) and
identity.AND().

> But I think this is an interesting idea.
> Unfortunately, you can't use the previously mentioned requireGroup and
> requirePermission functions because they raise exceptions rather than
> return result codes. I agree that something clever like this would be good.
>
> >I think the authentication API has to be flexible enough to make this kind
> >of fine-grained access control, even if it's not used that frecuently (is
> >not?).
>
> For the first release of the identity framework, I'll be content handle
> the simple cases easily and make the more complex cases possible (via
> real code). Otherwise, I'll probably never be able to get this done.
> It's bad enough I've been delayed by bugs in json-py and mis-features in
> cherrypy.

Sure, as I say, the good thing (tm) about this the model you propose can
be the ground to the features I propose with complete backward
compatibility. They are not mutually-exclusive and they can be implemented
'bottom-up' gradually.

I just wanted to keep this flexibility in mind so the actual framework
don't impose design limits to future enhancements.

--
LUCA - Leandro Lucarella - JID: luca(en)lugmen.org.ar - Debian GNU/Linux
.------------------------------------------------------------------------,
\ GPG: 5F5A8D05 // F8CD F9A7 BF00 5431 4145 104C 949E BFB6 5F5A 8D05 /
'--------------------------------------------------------------------'
El techo de mi cuarto lleno de universos

Curtis

unread,
Oct 19, 2005, 12:51:22 AM10/19/05
to TurboGears
Just thought I'd mention OpenID. It'd be cool to have that for the
identity portion at least. In addition, it looks like someone has
already written the code for a consumer and server at
http://openid.schtuff.com/. Although, it'd probably take a little work
to integrate nicely into TG.

I drool at the thought of not having to come up with another
username/password for every Web 2.0 site I try. ;-)

-Curtis

Jeff Watkins

unread,
Oct 19, 2005, 8:22:31 AM10/19/05
to turbo...@googlegroups.com
Projects like OpenID are cool, and they do a great job of solving
half of the puzzle: identity management. This still leaves access
control. You certainly wouldn't want to trust another system to
assign privileges to your users, would you?

While I didn't have OpenID in mind when I designed my identity
framework, I don't see any reason why it couldn't be integrated.
Essentially, you'd need a IdentityProvider class that implements the
OpenID protocol. The rest of the system remains largely the same --
although the User model would be drastically simplified.

Jeff

--
Jeff Watkins
http://metrocat.org/

"Just because you have the right to do something, doesn't mean it's
the right thing to do."
-- Fred Friendly, former president of CBS News

Kevin Dangoor

unread,
Oct 19, 2005, 1:43:15 PM10/19/05
to turbo...@googlegroups.com
On 10/18/05, Elvelind Grandin <elve...@gmail.com> wrote:
>
> One thing that I think would be Sweet(tm) would be support cherrypy's
> path based config system. So that you could put something like this in
> the dev/prod.cfg
>
> [/]
> group = "users"
> [/admin]
> group = "admin

I think there is a place for this, as well.

The nice thing about the decorator syntax and the secure base class is
that everything is all in one place. Just as CherryPy doesn't use a
separate file for URL mapping, this approach allows you to put
security requirements in there as well. This is nice when your
application is self contained.

The config file approach is good when your app is going to be
integrated with other system parts that have their own authentication
requirements. Being able to do it both ways seems important.

Kevin

Kevin Dangoor

unread,
Oct 19, 2005, 1:44:09 PM10/19/05
to turbo...@googlegroups.com
On 10/19/05, Kevin Dangoor <dan...@gmail.com> wrote:
> The config file approach is good when your app is going to be
> integrated with other system parts that have their own authentication
> requirements. Being able to do it both ways seems important.

I should say it's important ultimately. I don't want to discourage the
work Jeff is doing or his approach, because I think it's a good one.
We can extend it further as applications require...

Kevin

--
Kevin Dangoor
Author of the Zesty News RSS newsreader

email: k...@blazingthings.com
company: http://www.BlazingThings.com
blog: http://www.BlueSkyOnMars.com
Reply all
Reply to author
Forward
0 new messages