It works as follows: I register context factories per route (as named
utility in the registry) and also use the route names as permission
names. The only thing the context factories need to compute
permissions is the logged-in user which is attached to the request and
the context object which they will either retrieve from the db
according to the request's matchdict or lookup as attribute on the
request. So to compute whether a user has permission to edit task 1,
I'd retrieve task 1 from the db, lookup the context factory for
'task.edit', attach the task object to the current request (possibly
to route task.index) and check the permission for this artificial
request.
The code I use looks similar to below (i've stripped out further
complicating details). The concrete authorization context for
'task.edit' would subclass AuthzContext, setting __object_class__ to
Task, and actually implement the allow_if method.
class AuthzContext(object):
implements(IAuthzContext)
__object_class__ = None
def __init__(self, request):
self.request = request
self.object = None
# make sure the reified attribute 'user' is instantiated
self.user = request.user
#
# while it is the main responsibility of a AuthzContext to determine
# the correct context object and attach ACEs to it, there's a shortcut
# which allows passing in the context object as attribute of the
# request. this mechanism can be used to compute permissions
"in advance"
# i.e. without actually preforming a request. this allows us to keep
# view permissions and links in sync.
#
if getattr(request, 'authz_object', False):
self.object = request.authz_object
else:
self.object =
self.__object_class__.get(int(request.matchdict['id']), default=None)
if not self.object:
raise HTTPNotFound()
self.object.__acl__ = []#[(Allow, 'group:admin', ALL_PERMISSIONS)]
#
# call the convenience methods which may be used to set ACEs:
#
if self.allow_if():
self.allow()
else:
self.deny()
def allow_if(self):
return False
def allow(self, permission=None):
if self.request.user:
self.object.__acl__ = [(Allow, self.request.user.login,
permission or getattr(self, 'name', 'none'))]
else:
# no user logged in but we still have to allow access:
self.object.__acl__ = [(Allow, Everyone, permission or
getattr(self, 'name', 'none'))]
def deny(self, permission=None):
if self.request.user:
self.object.__acl__ = [(Deny, self.request.user.login,
permission or getattr(self, 'name', ALL_PERMISSIONS))]
else:
self.object.__acl__ = [(Deny, Everyone, permission or
getattr(self, 'name', 'none'))]
def context_factory(authz_context):
"""We must make sure a new AuthzContext instance is created for
each request.
Since __init__ must not return anything, we must use a helper function to
mediate.
"""
def context(request):
_authz_context = authz_context(request)
return _authz_context.object
return context
def has_permission_on_object(permission, request, object_):
"""Compute object-specific permissions ahead of time, i.e. without
the object
being determined from the request's properties. We still use the context
factory to assign the same __acl__ to the object as would happen
with a "real"
request. Thus, use this function to check whether a user (detemined by
request) will have access to a specific action (determined by permission) on
an object_.
"""
context = request.registry.queryUtility(IAuthzContext,
name=permission, default=None)
if context:
request.authz_object = object_
res = has_permission(permission,
context_factory(context)(request), request)
request.authz_object = None
else:
res = has_permission(permission, RootFactory, request)
return res
On Thu, Feb 16, 2012 at 8:44 PM, Mike Orr <[email protected]> wrote:
> On Thu, Feb 16, 2012 at 8:45 AM, Robert Forkel <[email protected]>
> wrote:
>> My context factories can be used in a hybrid way. As regular context
>> factories they retrieve the object according to matchdict. But they can be
>> passed the object as well and then just compute the permission. So in the
>> Index i'd loop over all possible tasks and ask the factories for each route
>> whether the User gas this permission.
>
> How do you do this?
>
> --
> Mike Orr <[email protected]>
>
> --
> You received this message because you are subscribed to the Google Groups
> "pylons-discuss" group.
> To post to this group, send email to [email protected].
> To unsubscribe from this group, send email to
> [email protected].
> For more options, visit this group at
> http://groups.google.com/group/pylons-discuss?hl=en.
>
--
You received this message because you are subscribed to the Google Groups
"pylons-discuss" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to
[email protected].
For more options, visit this group at
http://groups.google.com/group/pylons-discuss?hl=en.