Hello all,
recently in a now pretty lengthy thread we discussed how you can ceck
authorization information in TG2 applications. We had some ideas which I
will summarize in the following, and we would like to get your feedback
concerning these ideas.
The problem: Restricting access with the require() decorator based on
repoze.what predicates such as has_permission('edit') is often not
sufficient; you want to e.g. check the same permissions in a py:if
directive of a Genshi template to show certain info only to editors, or
e.g. get the set of groups of which the current user is a member because
you want to use them as values in a drop down box etc.
In TG1, this was easily possible using the properties and methods of
turbogears.identity.current, which was also available as tg.identity
inside templates.
For instance, in a TG1 template you could write py:if="'edit' in
tg.identity.permissions".
In a TG2 template, this is actually still possible, since tg.identity is
in TG2 an alias for repoze.who.identity and (currently) repoze.what also
stores the permissions here. However, the goal is to clearly separate
identification and authentication from authorization, so that
tg.identity will not contain authorization information any more in the
future.
So we need to find another way to check this information which should be
very simple; at least as simple as it was in TG1.
Gustavo (the author of repoze.what) already added a method is_met() to
repoze.what predicates that evaluates the predicate and returns a
boolean (instead of logging the result and raising an exception if
necessary as predicates usually do). However, you still need to pass the
current environment to that method which makes usage not as
straightforward as in TG1.
In order to simplify this, we have discussed the following ideas so far.
Please let us know how you like them or if you have any better ideas:
1) Often the need for checking permissions in a template arises because
you want to hide links to restricted pages which may not be accessible.
You currently have to repeat the predicate that is used in the require
decorator of the restricted controller which is against DRY. The idea
here is to implement a function can_access() or accessible() that checks
for a given controller path whether it would be accsible
(http://trac.turbogears.org/ticket/2172), using the information from the
existing require() decorator. I think that's a good idea.
2) There should be another TG function evaluate() for evaluating
repoze.what predicates, currying an internal function that expects the
current environment (http://trac.turbogears.org/ticket/2173).
Personally I think that is still not simple enough. You will have to use
py:if="evaluate(has_permission('manage'))" in a template, with the need
to pass both the evaluate function and the predicates as template
variables (or make them standard variables which does not seem a good
idea either). Also, it's easy to forget the evaluate() call, thus
security holes would be preprogrammed.
3) Michael came up with the idea to overwrite the __nonzero__ method of
predicates. This could for example raise an error when used in the form
py:if="has_permission('manage')" without the evaluate call. But we could
also go a step further and let __nonzero__ automatically evaluate the
predicate. The predicates would have a dual use then, both for require
decorators (not immediately evaluated) and for py:if statements
(immediately evaluated).
The following simple monkey-patch would allow this double usage of all
repoze.what predicates in TG2:
-----------------------------------------------------------------
from tg import request
from repoze.what.predicates import Predicate
Predicate.__nonzero__ = lambda self: self.is_met(request.environ)
-----------------------------------------------------------------
Instead, we could also create a TG specific subclass of repoze.what
predicates that allows this double usage, and also create copies of the
existing predicates on the fly.
Both the monkey-patching (or subclassing and copying) and the double
usage is a bit hackish though and maybe a bit too much magic.
4) A different solution is to add an "access" object to TG2 and make it
a standard template variable that auto-evaluates predicates passed as
attributes. Here is a possible implementation:
-------------------------------------------------------------
from tg import request
from repoze.what import predicates
class Access(object):
"""Environ-aware predicates evaluating immediately."""
def __getattr__(self, name):
predicate = getattr(predicates, name)
if callable(predicate):
def predicate_is_met(*args, **kwargs):
return predicate(*args, *kwargs).is_met(
request.environ)
return predicate_is_met
else:
return predicate
access = Access() # make this a standard tg template variable
-------------------------------------------------------------
This would allow easy evaluation of all existing predicates in templates
in the form tg.acess.has_permission('edit'). We could also provide a
mechanism for including additional custom predicates in the access object.
5) Another idea was to make repoze.what.credentials publicly available
in TG2 templates with an alias tg.credentials. However, the problem here
is that Gustavo wants to keep repoze.what.credentials an implementation
detail and not part of the public API, because he wants the concept of
groups and permission flexible enough to evolve in the future; e.g.
groups may become hierarchical.
Instead, repoze.what v2 will have additional functions for getting the
current groups and permissions. I think that's good; and these functions
should be made available as standard template variables.
6) Just for the record, we also need replacements for the TG1 identity
predicates identity.from_host and identity.from_any_host in form of
repoze.what predicates. There will hopefully be a repoze.what network
plugin including such predicates soon. Until then, we need to write a
custom predicate checking REMOTE_ADDR.
-- Christoph
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups
"TurboGears Trunk" 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/turbogears-trunk?hl=en
-~----------~----~----~----~------~----~------~--~---