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

Hermann Himmelbauer dusty at qwer.tk
Fri Jul 18 11:00:50 EDT 2008


Am Freitag, 18. Juli 2008 03:55 schrieb Florian Friesdorf:
> 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.

Yes, that's o.k. In my specific application, I use an ORM, thus table rows are 
mapped to objects -> "user objects".

> > 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:

Maybe I was, I mixed some things up, it seems.

> zope.app.authentication.authentication.PluggableAuthentication.authenticate
> is the place to look at.
> 1. a PrincipalInfo object is got by calling authenticateCredentials from an
>    authplugin

Yes, true. For that reason, my AuthenticatorPlugin overwrites the 
authenticateCredentials method and retrieves the internal principal (=my user 
object), builds an appropriate PrincipalInfo object and returns that.

> 2. this is handed to an AuthenticatedPrincipalFactory, which returns a
> Principal object

Right. so I was wrong above: The Principal object is built out of the 
PrincipalInfo object. However, that's exactly the problem: The only data I 
have to create the Principal, is the PrincipalInfo object. Thus I lose any 
extra user information.

So, in order to transport needed information from the user -> Principal, I 
have two options:

1) Stuff out PrincipalInfo with all needed data that my Principal needs
2) In the AuthenticatedPrincipalFactory I would query for my internal user 
object (InternalPrincipal) and transfer all needed data to the Principal from 
there.

In my application, I chose (1) for performance reasons, although, I have to 
admit, is quite complicated, as I have to create a customer PrincipalInfo 
class etc., moreover, the IPrincipalInfo class states: 

class IPrincipalInfo(zope.interface.Interface):
"""Minimal information about a principal."""

For variant (1) it has to hold a lot more information, so it contradicts the 
meaning of this class.

> 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.

Yes, that's true.

> > - 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.

Hmmm, interesting.

> > - 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

Ok, it seems that the principal is really the representation of my user object 
and may hold anything I need.

> 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.

Hmmm, interesting approach. But would that also mean that the principal may 
hold also sensitive information, e.g. the password? Are there any security 
concerns about doing so?

> > 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?

I can send you my code, if you like. However, it's somehow a little bit ugly 
due to some Zope3 internals I could not understand/solve.

> > 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

Yes, these are somehow also my ideas.

> > 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.

Yes, that would imply setting up some sort of caching.

> > > > 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).

What I moreover think about is why not to pull the PAU stuff out of zope.app 
and create a package such as "zope.authentication". This may be more generic.

To sum it up, during authentication, user information is needed four times:

1) In customization of authplugin.authenticateCredentials (to check for 
correct login/pass)
2) In the Authenticated PrincipalFactory, or, better, in a subscriber to that 
event which stuffs out the principal with all needed data
3) For getPrincipal() calls (Two in my case for every request, no clue why and 
where they are called).

In case of a not-so-fast access to user data (such as in my case, where RDB 
queries have to be issued), caching can heavily improve the performance.

So, it seems, a good idea would be to create some caching-related package that 
integrates nicely with the PAU.

> 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...

Yes, many thanks to you, too. And I would also be very interested in hearing 
comments about this from others.

BTW. I also send this to Roger Ineichen, as he also suffered from shortcomings 
of zope.app.authentication and built his own "z3c.authentication", which is 
also an interesting read. (Although I could not yet find time to play around 
with it).

Best Regards,
Hermann

-- 
hermann at qwer.tk
GPG key ID: 299893C7 (on keyservers)
FP: 0124 2584 8809 EF2A DBF9  4902 64B4 D16B 2998 93C7


More information about the Zope-Dev mailing list