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
-~----------~----~----~----~------~----~------~--~---

Reply via email to