[Grok-dev] Make self.request.principal to be an instance of MyOwnPrincipalInfo (my own class)
Hector Blanco
white.lists at gmail.com
Tue Jan 25 11:28:18 EST 2011
Thank you very much. It's been a great, great explanation. It has
allowed me to understand a bit better the interfaces, adapters and all
that. And, oh, well... It helped me realize that my idea won't
probably work... :D :D (it's sad, but I will sleep better at nights).
The adaptation worked properly. The process I followed is:
I made MyOwnPrincipalInfo implementing the interface IPrincipalInfo so
it can be used in the zope.pluggableauth mechanism as a good
(suitable) "PrincipalInfo" thing.
To achieve that, I had to move the MyOwnPrincipalInfo to app.py
because I was getting ComponentLookupErrors (I guess if I want it in
another file, I might have to register it somewhere, as I had to with
the permissions thing:
https://mail.zope.org/pipermail/grok-dev/2011-January/011028.html
)
But well... the way I have it now is:
================ app.py ==========
class MyOwnPrincipalInfo(grok.Adapter):
grok.context(User.User)
# My 'user' class, which doesn't have much to see
# with the pluggable auth mechanism
grok.implements(IPrincipalInfo)
grok.name("MyOwnPrincipalInfo")
def __init__(self, user):
if isinstance(user, User.User) and (user.id):
self.id = str(user.id)
self.title = user.userName
self.description = str(user.firstName) + " " + str(user.lastName)
else:
raise TypeError("Unable to provide a PrincipalInfo from a %s" % type(user))
def test(self):
log.debug("::test > Checkpoint!")
------
So I can use that in my own authenticator plugin (a few lines later in
the same app.py)
class UserAuthenticatorPlugin(grok.GlobalUtility):
grok.provides(IAuthenticatorPlugin)
grok.implements(IAuthenticatorPlugin)
grok.name('UserAuthenticatorPlugin')
grok.context(Server)
def __init__(self):
super(UserAuthenticatorPlugin, self).__init__()
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'])):
myOwnPrincipalInfo = component.getAdapter(user, IPrincipalInfo,
name="MyOwnPrincipalInfo")
log.debug("::UserAuthenticatorPlugin > getAccount > Got principal
%s" % myOwnPrincipalInfo)
return myOwnPrincipalInfo
return None
But here's when I realized I all that setup won't work (and well... I
might be wrong... I hope I'm wrong, but this is what I understand it
happens):
The pluggable authentication module
(~/.buildout/eggs/zope.pluggableauth-1.2-py2.6.egg/zope/pluggableauth/authentication.py)
does this:
-------------
def authenticate(self, request):
authenticatorPlugins = [p for n, p in self.getAuthenticatorPlugins()]
for name, credplugin in self.getCredentialsPlugins():
credentials = credplugin.extractCredentials(request)
for authplugin in authenticatorPlugins:
if authplugin is None:
continue
info = authplugin.authenticateCredentials(credentials)
if info is None:
continue
info.credentialsPlugin = credplugin
info.authenticatorPlugin = authplugin
principal = component.getMultiAdapter((info, request),
interfaces.IAuthenticatedPrincipalFactory)(self)
principal.id = self.prefix + info.id
return principal
return None
-------------
The line:
info = authplugin.authenticateCredentials(credentials)
Is the one that calls my plugin's "authenticateCredentials" method. At
that point, "info" is (finally!!, what I wanted), an instance of
MyOwnPrincipalInfo class.
Then that principalInfo is used in the method def getPrincipal(self,
id) of the same "authentication.py" file. And does this:
principal = interfaces.IFoundPrincipalFactory(info)(self)
I guessed (better said... I supposed) that it follows a kind of
'abstract factory' pattern. I went to check the implementation of that
Factory () and I saw this:
~/.buildout/eggs/zope.pluggableauth-1.2-py2.6.egg/zope/pluggableauth/factories.py:
class AuthenticatedPrincipalFactory(object):
# Yadda, yadda, yadda...
def __call__(self, authentication):
principal = Principal(authentication.prefix + self.info.id,
self.info.title,
self.info.description)
notify(interfaces.AuthenticatedPrincipalCreated(
authentication, principal, self.info, self.request))
return principal
It's not returning anything with 'MyOwnPrincipalInfo' inside but it's
just the required fields from said "info" instance and creating a
principal with them (Principal which is what "comes" in the request).
If I create a "Test View" (in my app.py) to sneak in the
request.principal, such as:
class Test(grok.View):
grok.context(Server)
grok.require('server.ViewSite')
def render(self):
print("::Test > Got self.request.principal: %s (of type %s)" %
(self.request.principal, type(self.request.principal)))
print("::Test > Got vars(self.request.principal): %s" %
vars(self.request.principal))
print("::Test > Got dir(self.request.principal): %s" %
dir(self.request.principal))
This is the output:
::Test > Got self.request.principal: Principal('3') (of type <class
'zope.pluggableauth.factories.Principal'>)
::Test > Got vars(self.request.principal): {'description': 'test
firstName test lastName', 'id': '3', 'groups': [], 'title': u'test'}
::Test > Got dir(self.request.principal): ['__class__', '__delattr__',
'__dict__', '__doc__', '__format__', '__getattribute__', '__hash__',
'__implemented__', '__init__', '__module__', '__new__',
'__providedBy__', '__provides__', '__reduce__', '__reduce_ex__',
'__repr__', '__setattr__', '__sizeof__', '__str__',
'__subclasshook__', '__weakref__', 'allGroups', 'description',
'groups', 'id', 'title']
So I guess I have to adapt the Principal, as well... (something like
Jan-Wijbrand Kolman already explained a few mails ago in this very
same thread)
https://mail.zope.org/pipermail/grok-dev/2011-January/010991.html
There might be hope for not having to adapt that, thoug...
Going back to the file
~/.buildout/eggs/zope.pluggableauth-1.2-py2.6.egg/zope/pluggableauth/factories.py:
and its "AuthenticatedPrincipalFactory" class:
The init looks like like this:
class AuthenticatedPrincipalFactory(object):
component.adapts(interfaces.IPrincipalInfo, IRequest)
interface.implements(interfaces.IAuthenticatedPrincipalFactory)
def __init__(self, info, request):
self.info = info
self.request = request
So my belowed "info" thing is inside the Factory object... Now I have
to see if there's a way to grab the instance of
AuthenticatedPrincipalFactory and access its "info" field.
Will keep posting...
:D
Thank you everyone for sheding some light to all this! (and well... if
you can shed some more... it'd be great!)
2011/1/25 Jan-Wijbrand Kolman <janwijbrand at gmail.com>:
> Hi,
>
> On 1/24/11 20:45 PM, Hector Blanco wrote:
>> Do I have to "grok"/register... my PrincipalInfo somewhere?
>>
>> Right now I have it like this:
>>
>> ========= MyOwnPrincipalInfo.py ===========
>>
>> class MyOwnPrincipalInfo(grok.MultiAdapter):
>> grok.adapts(IPrincipalInfo, IBrowserRequest)
>> grok.implements(IPrincipalInfo)
>>
>> def __init__(self, user):
>> super(MyOwnPrincipalInfo, self).__init__() #Just in case
>> #more initialization code
>>
>> def test(self):
>> log.debug("::test> Checkpoint!")
>>
>> And then, in app.py I have set up an AuthenticatorPlugin like this:
>>
>> ============ app.py ============
>> class UserAuthenticatorPlugin(grok.GlobalUtility):
>> grok.provides(IAuthenticatorPlugin)
>> grok.implements(IAuthenticatorPlugin)
>> grok.name('UserAuthenticatorPlugin')
>> grok.context(Server)
>>
>> def __init__(self):
>> super(UserAuthenticatorPlugin, self).__init__()
>>
>> 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'])):
>> return component.getMultiAdapter((user, ), IPrincipalInfo)
>>
>> return None
>> ================
>
> In app.py you have `getMultiAdapter((user, ), IPrincipalInfo)`, however,
> the `MyOwnPrincipalInfo` multi adapter class expects *two* objects to
> adapt, as defined in the `grok.adapts()` directive: an object providing
> `IPrincipalInfo` and an object providing `IBrowserRequest`. That's why
> it is called a *multi adapter* :)
>
> The `grok.implements()` then tells the component architecture what
> interface will be provided by instances of this adapter class.
>
> So, I think you need to rewrite the adapter class to this:
>
> class MyOwnPrincipalInfo(grok.MultiAdapter):
> grok.adapts(INTERFACE_OR_CLASS_FOR_THE_USER_OBJECT, IBrowserRequest)
> grok.implements(IPrincipalInfo)
>
> where INTERFACE_OR_CLASS_FOR_THE_USER_OBJECT is the interface, or class
> of the user objects you want to adapt.
>
> The `getMultiAdapter` call then would look like this::
>
> getMultiAdapter((user, request), IPrincipalInfo)
>
> But, you say, there's no request in scope in the
> `authenticateCredentials` call to use for the multi adapter lookup. So,
> maybe you do not really need the request in the `MyOwnPrincipalInfo`
> adapter class?
>
> In that case you might able to do this::
>
> class MyOwnPrincipalInfo(grok.Adapter):
> grok.context(INTERFACE_OR_CLASS_FOR_THE_USER_OBJECT)
> grok.implements(IPrincipalInfo)
>
> def __init__(self, user):
> # do your stuff
>
> And instead of the `getMultiAdapter` call, you do a `getAdapter(user,
> IPrincipalInfo)` call, which has a elegant shortcut::
>
> IPrincipalInfo(user)
>
> HTH, let us know how you fare.
>
> regards, jw
>
> _______________________________________________
> 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