[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 12:43:12 EST 2011
Ok, I got it working... But I'd like to check your opinions... I don't
know if what I'm doing is right or not... secure or not... For the
moment, the login, logout... not allowing a user to access pages if
it's not logged in... All that seems to be working fine but I'm not
100% certain that what I've done is a good idea or it will be a
direct threat to the security of my server, the global warming and the
existance of Santa Claus... I'm very newbie and there are a lot of
concepts I don't fully understand.
Please, allow me to explain:
For the moment, I have everything in my app.py file. I'll try to
register the components later and try to move all this to separate
files.
I have implemented or extended three different elements involved in
the authentication process:
1) The "info" for the principal:
class MyOwnPrincipalInfo(grok.Adapter):
grok.context(User.User)
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)
self.credentialsPlugin = None
self.authenticatorPlugin = None
else:
raise TypeError("Unable to provide a PrincipalInfo from a %s" % type(user))
2) I have also extended (not implemented interfaces, but extended) the
principal itself, to which I have added a method "test" to see if I
could execute it (it's extended from
zope.pluggableauth.factories.Principal)
class MyOwnPrincipal(Principal):
grok.name("MyOwnPrincipal")
def __init__(self, id, title=u'', description=u'', info=None):
super(MyOwnPrincipal, self).__init__(id, title, description)
self.info = None #I'll deal with this later
def test(self):
log.debug("::Test > Checkpoint!")
I don't know if just "extending" it's a good idea, but when I checked
the AuthenticatedPrincipalFactory (in zope.plugabbleauth.factories)
and I saw what it did, it didn't seem to check for other
implementations of Principal... it just did:
class AuthenticatedPrincipalFactory(object):
def __call__(self, authentication):
principal = Principal(authentication.prefix + self.info.id,
self.info.title,
self.info.description)
#notify...
return Principal
That's why I also had to create MyOwnPrincipalFactory (detailed below)
and 3) And I have implemented my own version of the factory that
returns Principals of the MyOwnPrincipal class:
class MyOwnPrincipalFactory(grok.MultiAdapter):
grok.context(Server)
grok.adapts(IPrincipalInfo, IBrowserRequest)
grok.implements(IAuthenticatedPrincipalFactory)
def __init__(self, info, request):
self.info = info
self.request = request
def __call__(self, authentication):
principal = MyOwnPrincipal(authentication.prefix + self.info.id,
self.info.title,
self.info.description,
self.info)
#The line above is the key! :-)
notify(AuthenticatedPrincipalCreated(
authentication, principal, self.info, self.request))
return principal
I don't know... maybe extending the
zope.pluggableauth.factories.AuthenticatedPrincipalFactory would have
been enough... or even better idea...
Anyway... At least I've been able to, finally, do the following in a view:
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))
print("::Test > Trying to execute self.request.principal.test(): ")
self.request.principal.test()
print("::Test > If you're reading this, it's been successful!")
Which produces this output:
::Test > Got self.request.principal: Principal('3') (of type <class
'server.app.InvPrincipal'>)
::Test > Got vars(self.request.principal): {'info': None,
'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',
'grokcore.component.directive.name', 'groups', 'id', 'info', 'test',
'title']
::Test > Trying to execute self.request.principal.test():
::Test > Checkpoint!
::Test > If you're reading this, it's been successful!
Any recommendations, ideas or suggestions will be very appreciated...
(what? I'm scared... )
Thank you everyone!
2011/1/25 Hector Blanco <white.lists at gmail.com>:
> 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