[Zope-dev] Re: [Plone-developers] PAULA: bringing Zope 3's authentication to Plone and beyond

Florian Friesdorf flo at chaoflow.net
Thu Jul 17 21:55:47 EDT 2008


On Wed, Jul 16, 2008 at 10:20:58AM +0200, Hermann Himmelbauer wrote:
> Am Mittwoch, 16. Juli 2008 05:48 schrieb Florian Friesdorf:
> > InternalPrincipal is a persistent object used to store the data of
> > principals in a PrincipalFolder, PrincipalInfo is returned upon successfull
> > authentication and handed to FoundPrincipalFactory, which extracts some
> > information and returns Principal objects.
> 
> Ok, this is how I see it, too. But let me describe my understanding more 
> specific:
> 
> I see it that way that applications with authentication will normally have 
> something like a "user" object, which stores all related authentication/user 
> information. This user object may reside in a RDB, a text file, or, as here, 
> in a PrincipalFolder (as an InternalPrincipal).

I would put the "object" in quotation, too, as for example in RDB it isnt an
object at all, but simply authentication data + properties and, yes, this
corresponds to InternalPrincipal.

> If I understand it right, a Principal is an entity that is the result of an 
> authentication process and holds all needed information for authorization. 
> However, it is not designed to store user specific information, e.g. login 
> names, user names and the like.
> 
> And PrincipalInfo is somehow a specific aggregation of user data, which can be 
> used throughout the application. This object may have to be inherited to an 
> extended MyPrincipalInfo object, as many applications may need extra info 
> from this object.
> 
> So, both the Principal and PrincipalInfo objects have to be created out of the 
> user object, it is not possible to create a Principal out of a PrincipalInfo 
> and reverse.

I think you are wrong here:

zope.app.authentication.authentication.PluggableAuthentication.authenticate is
the place to look at.
1. a PrincipalInfo object is got by calling authenticateCredentials from an
   authplugin
2. this is handed to an AuthenticatedPrincipalFactory, which returns a Principal
   object

So, PrincipalInfo is internal to PAU and as far as I figured does not leave it.
Objects that leave PAU are those created by AuthenticatedPrincipalFactories, for
authentication, and by FoundPrincipalFactories, for searches - In case of
PrincipalFolder that is Principal objects in both cases.

> Throughout my application, I have the following scenarios:
> 
> - I need to display some user data (e.g. in a "logged in" viewlet): I'd use 
> PrincipalInfo.

I would use the principal object

> - I need to check for authorization somewhere in my application: I'd use the 
> Principal.

yes

> - I need to administer the user, e.g. change password etc.: I'd use the user 
> object (InternalPrincipal) for that.

I would again use the principal object, which transparently performs the changes
wherever they are necessary, i.e all properties of the user are available from
the Principal object and changes to them will happen on their actual source.

> - ???
> 
> In my application, all I have is the principal (request.principal). So I need 
> a viable way to retrieve PrincipalInfo and User/internalPrincipal objects.
> 
> How would I do that?

principal is all you need, if it does not carry what you want, you can subscribe
to FoundPrincipalCreated and AuthenticatedPrincipalCreated events and stuff on
it whatever you need.

> - user object: There seems to be no zope3 support for that yet - so it seems I 
> have to do this manually (some getUser(login) function, get the login from 
> request.principal.id[lenAUTH_PREFIX):], not very pretty, though.).

principal is the user object

> - PrincipalInfo: Don't really know - perhaps by somehow getting the PAU object 
> and iterating over the authentication modules, calling some getPrincipalInfo 
> method?
> 
> I personally would favour some adapters, e.g.:
> 
> InternalPrincipal = IInternalPrincipal(request)
> PrincipalInfo = IPrincipalInfo(request)

In case you store your userdata in RDB, you do not need InternalPrincipal at
all. I currently see to options:

1. custom AuthenticatorPlugin, AuthenticatedPrincipalFactory and
FoundPrincipalFactory from principalfolder.py. Your AuthenticatorPlugin would
need to return PrincipalInfo objects. Further, you write an event handler that
listens for FoundPrincipalCreated and AuthenticatedPrincipalCreated, which puts
all needed/wanted properties onto the Principal object, which was created by one
of the factories. Those properties can be real python properties with
get,set,del methods, which you can use to get,set,del the actual propety values
wherever they may come from, e.g. an RDB.

2. you could further write your own factories and return whatever object you
would like to represent your user in case of searches and in case of
authentication.

I think option1 is optimal for your use case, based on what you described so
far.

> The current documentation leaves many of the above points open, or at least, 
> did not describe them in a way so that I could understand them.

Read the README.txt, look at the doctest in authentication.py and
principalfolder.py and look at the code itself - it is very clean and to the
point.

> Sure it's possible, that's what I did. But it was quite complicated and I had 
> to dig very deep into Zope3 code to do that. This is definitely no option for 
> a Zope developer who needs to quickly set up a cookie-less application.

Did you make your cookie-less credentials plugin available somewhere?

> > > How excactly should I cache user data so that a single browser request
> > > does not lead to multiple RDB queries? And where in the big picture is
> > > the "User" entity? (It's probably the InternalPrincipal object, I
> > > assume)...
> >
> > You don't need InternalPrincipal objects, they are specific to
> > PrincipalFolder, IMHO.
> 
> Well, yes and no - I need some user entity (which is located in the RDB), 
> which somehow resembles the InternalPrincipal object.

see above

> > I think you need:
> > - custom authenticator plugin, that authenticates against RDB and has a
> >   dictionary as cache: key = login, value = password;
> > - custom foundprinciplefactory, that generates Principal objects from RDB
> > data, again using a simple key=login,value=Principal dictionary as cache; -
> > eventually a custom credentials plugin, that for your point 3.
> 
> That's basically what I did. However, I did not yet implement caching, as some 
> things are not yet solved:
> 
> - What if the user changes his password? How would the cache update his data?

make the cache available as an utility, the factories query the utility and the
principal objects (see above), update the cache upon change.

> - What if a user is deleted?

remove the user from the cache

But honestly, I am a cache novice, so there might be way better approaches...

> So, it seems there needs to be some method that flushes the cache (e.g. if 
> user data changes). Maybe it makes sense to create an extra UserCache utility 
> that provides such methods. 

I agree that maybe it makes sense (see above)

> This should be very well laid out before implementing, I think.
>
> Currently, every simple HTTP request leads to 3 select statements on my user 
> table (due to authentication, prinipalInfo creation and the like). So, this 
> is something that needs to be solved by caching.

That's not nice. You could:
1. your custom authplugin.authenticateCredentials, checks the credentials and if
positive places your custom Principal object into the cache utility and returns
just the login name as info, i.e. not PrincipalInfo.
2. your custom AuthenticatedPrincipalFactory receives the login name, retrieves
the principal object and returns it
3. your FoundPrincipalFactory does the same.

Changing user properties is done through your custom principal object.
For adding you need a special control panel, that adds the user in the RDB.
For deleteing you need a special control panel, that deletes the user in the RDB
and from the cache utility.

> > > So I would very, very much suggest to dig into PAU first and fix those
> > > shortcomings before porting it to Plone/Zope2.
> >
> > Exactly what I am doing :)
> 
> That's great!
> If you have time, please don't forget scenarios, where user data is stored 
> outside Zope3 (e.g. RDB). I would very much appreciate, if Zope3 has some 
> built in support for that.

Zope3 currently with PrincipalFolder does support this already. All you need is
to listen to the corresponding events (see above).


florian

PS: Thank you very much for this conversation, it made things a lot clearer to
me and will help me with my project. As I am new to the subject, too, it'll be
great if someone being really familiar with PAU could shortly give an opinion of
whether I got things right...
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 197 bytes
Desc: not available
Url : http://mail.zope.org/pipermail/zope-dev/attachments/20080717/a80116d4/attachment.bin


More information about the Zope-Dev mailing list