[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