[Grok-dev] Putting together the authentication plugin and a Session. Is that possible?

Sebastian Ware sebastian at urbantalk.se
Tue Jan 4 11:19:19 EST 2011


If you intend to store large amounts of users in your cache you might want to use a grok.Container or a simple Btree. Every time you access the dict Zodb will fetch all items when accessing a dict, but only selected ones with a Btree based alternative.

Not sure where the tipping point is for this, but it could be worth considering.

Mvh Sebastian

4 jan 2011 kl. 00.45 skrev Hector Blanco:

> Hello list...
> 
> I have "fixed" it putting a very simple "cache" (...erm... ok, ok... a
> dict() with the shape <userName, User()>, but "cache" sounds better )
> in the "UserManager" class so if the user is not in the cache, queries
> the database, but if it is in the dict, return the value from the
> dict.
> 
> It seems to work...
> 
> 2010/12/28 Hector Blanco <white.lists at gmail.com>:
>> Oh, I see... Actually, that's how I have it done (good to see I wasn't
>> totally wrong)
>> 
>> class MySessionCredentialsPlugin(grok.GlobalUtility, SessionCredentialsPlugin):
>>    grok.provides(ICredentialsPlugin)
>>    grok.name('credentials')
>> 
>>    loginpagename = 'login'
>>    loginfield = 'form.login'
>>    passwordfield = 'form.hashedPwd'
>> 
>> ... and ...
>> 
>> class MyOwnPrincipalInfo(object):
>>        grok.implements(IPrincipalInfo)
>> 
>> The issue is that the getAccount() method seems to be called a lot of times
>> 
>> class UserAuthenticatorPlugin(grok.GlobalUtility):
>>        grok.provides(IAuthenticatorPlugin)
>>        grok.name('users')
>>        grok.context(Server)
>> 
>>        def getAccount(self, login):
>>                try:
>>                        return grok.getSite()["UserManager"].getByName(login, allData=False)
>>                except Exception, e:
>>                        log.warn("::UserAuthenticatorPlugin > getAccount > Got exception %s " % e)
>>                finally:
>>                        Database.session.close()
>> 
>>        def authenticateCredentials(self, credentials):
>>                if isinstance(credentials, dict):
>>                        if (("login" in credentials) and ("password" in credentials)):
>>                                user = self.getAccount(credentials['login'])
>>                                if user and (user.checkPassword(credentials['password'])):
>>                                        log.debug("::UserAuthenticatorPlugin > authenticateCredentials >
>>                                                   Credentials authenticated for user %s " % (user.userName))
>>                                        return MyOwnPrincipalInfo.MyOwnPrincipalInfo(user)
>>                return None
>> 
>> And I don't know how to avoid that...
>> 
>> For other views, I understand I can use the [self].request.principal.
>> For instance:
>> 
>> class OtherPage(grok.View):
>>        grok.context(Server)
>>        grok.require('server.ViewSite')
>> 
>>        def update(self):
>>                if self.request.principal.id == "peter":
>>                                # do stuff
>> 
>> , but I don't see how to access that information in the "getAccount"
>> method (or in authenticateCredentials, which seems to be the method
>> calling getAccount). In the "authenticateCredentials" itself, it'd be
>> nice to check if the user has already been authenticated and then
>> return the request.principal, instead of having to call getAccount()
>> (access the database) and then instantiate a new MyOwnPrincipalInfo()
>> object (which, if I understood properly, is in the request already
>> once that the user has been authenticated once)
>> 
>> Thanks!
>> 
>> 2010/12/28 Jeffrey Peterson <bgpete at gmail.com>:
>>> So you followed that tutorial.  In there it includes session based login.  it's outdated a bit but if you change a few imports still valid.
>>> 
>>> Your Credentials utility needs to inherit from SessionCredentialsPlugin:  from zope.pluggableauth.plugins.session import SessionCredentialsPlugin
>>> 
>>> MyOwnPrincipalInfo needs to implement IPrincipalInfo, and then the principal will be in the request:  self.request.principal it doesn't AFAIK access the DB multiple times.  The principal is in the request until logout, or session expiration.
>>> 
>>> Jeff.
>>> On Dec 27, 2010, at 6:14 PM, Hector Blanco wrote:
>>> 
>>>> Hello list:
>>>> 
>>>> I have set up a user authentication mechanism as explained in
>>>> http://grok.zope.org/documentation/how-to/authentication-with-grok
>>>> 
>>>> The users structure is serialized on a MySQL database.
>>>> 
>>>> I have setup the "authenticateCredentials" and "getAccount" methods
>>>> for the authenticator plugin like this
>>>> 
>>>> def authenticateCredentials(self, credentials):
>>>>       if isinstance(credentials, dict):
>>>>               if (("login" in credentials) and ("password" in credentials)):
>>>>                       user = self.getAccount(credentials['login'])
>>>>                       if user and (user.checkPassword(credentials['password'])):
>>>>                               log.debug("::UserAuthenticatorPlugin > authenticateCredentials >
>>>>                                          Credentials authenticated for user %s " % (user.userName))
>>>>                               return MyOwnPrincipalInfo.MyOwnPrincipalInfo(user)
>>>>       return None
>>>> 
>>>> def getAccount(self, login):
>>>> try:
>>>>       return grok.getSite()["UserManager"].getByName(login, allData=False)
>>>> except Exception, e:
>>>>       log.warn("::UserAuthenticatorPlugin > getAccount > Got exception %s " % e)
>>>>       log.debug("::UserAuthenticatorPlugin > getAccount > Showing
>>>> traceback:\n%s" % traceback.format_exc(limit=5))
>>>> finally:
>>>>       Database.session.close()
>>>> 
>>>> The "UserManager" is just a bunch of static methods that access the
>>>> database (using SqlAlchemy) and, in this case, tries to get the user
>>>> whose "userName" is the same in "login". That means having to access
>>>> the database many, many times.
>>>> 
>>>> So here's the question:
>>>> Is there any way of using the ISession object so I don't have to query
>>>> the database so often?
>>>> 
>>>> The idea would be putting "something" in the "authenticateCredentials"
>>>> method so if userName and password are correct, a new entry for that
>>>> user is created in the session object, so I can get it from there,
>>>> instead of having to access the database that often.
>>>> 
>>>> Something that would allow me to modify the getAccount() method to
>>>> something like this:
>>>> 
>>>> def getAccount(self, login):
>>>> try:
>>>>        if (ISession(self.request)['users'][self.request.principal.id]):
>>>>                  return =
>>>> ISession(self.request)['users'][self.request.principal.id]
>>>>        else:
>>>>                  return
>>>> grok.getSite()["UserManager"].getByName(login, allData=False)
>>>> except Exception, e:
>>>>       log.warn("::UserAuthenticatorPlugin > getAccount > Got exception %s " % e)
>>>>       log.debug("::UserAuthenticatorPlugin > getAccount > Showing
>>>> traceback:\n%s" % traceback.format_exc(limit=5))
>>>> finally:
>>>>       Database.session.close()
>>>> 
>>>> But that's the issue... in order to use the session, I need to have
>>>> access to a .request, right? And in getAccount(self...), self is an
>>>> instance of "UserAuthenticatorPlugin", which doesn't have any request
>>>> associated (at least, not that I have seen)
>>>> 
>>>> Is there any way to access the request from an instance of
>>>> "UserAuthenticatorPlugin"?  The idea would be "registering" the user
>>>> that is authenticated in the Session object so I don't have to access
>>>> the database that many times...
>>>> 
>>>> Thank you in advance!
>>>> _______________________________________________
>>>> Grok-dev mailing list
>>>> Grok-dev at zope.org
>>>> https://mail.zope.org/mailman/listinfo/grok-dev
>>> 
>>> 
>> 
> _______________________________________________
> Grok-dev mailing list
> Grok-dev at zope.org
> https://mail.zope.org/mailman/listinfo/grok-dev



More information about the Grok-dev mailing list