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

Hector Blanco white.lists at gmail.com
Mon Jan 3 18:45:50 EST 2011


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


More information about the Grok-dev mailing list