Hello.
>From time to time I get (mostly private) emails from TG users who complained
because there was no way to evaluate predicate checkers implicitly (i.e.,
without passing the environ explicitly) and now complain because, although
it's possible, I strongly discourage it.
So here I'll elaborate on the warning I put on the repoze.what-pylons docs, so
I won't have to repeat the explanation over and over and over again in
different ways, and keep receiving complaints (even from the same people)
nevertheless.
First of all, keep in mind that this has nothing to do with this functionality
being handy or not. I agree it's handy, but that's out of the scope of this
thread. My goal is _not_ to discuss if it's handy or not, but why it should be
avoided.
OK, so here I go...
The warning reads:
"This functionality was implemented by popular demand, but it’s strongly
discouraged by the author of repoze.what because it’s a monkey-patch which
brings serious side-effects when enabled:
1.- Third-party components which handle repoze.what predicates may get
erroneous values after evaluating a predicate checker (even if they don’t use
this functionality!).
2.- If another non-Pylons-based application uses the same monkey-patch and you
mount it on your application (or vice versa), the predicates used by both
application will share the same WSGI environ.
In the two scenarios above, it will lead to serious security flaws. So avoid
it by all means! Use predicate evaluators instead."
I'll start explaining side-effect #2 because I believe it'd be the most
important one for TG2 users. Then I'll explain side-effect #1.
===== Predicates booleanized in multiple repoze.what-powered apps =====
This is how *all* TG2 applications behave:
"""
# Keep in mind that this is *pseudo-code* to illustrate how things work!
...
from repoze.what.predicates import Predicate
from tg import request
...
class TurboGearsApp(object):
def __init__(self, ...):
...
Predicate.__nonzero__ = lambda self: self.is_met(request.environ)
...
def __call__(self, environ, start_response):
...
"""
Through repoze.what-pylons, TurboGears 2 applications set the .__nonzero__
attribute of the base Predicate class, so predicate checkers can be used as
booleans without passing the environ explicitly.
Then it's perfectly possible for other non-Pylons based applications to offer
this handy functionality as well if they use repoze.what, so they'd do:
"""
# Pseudo code here too.
...
from repoze.what.predicates import Predicate
from myframework import environ # <-- Assuming they use thread-locals too
...
class MyFrameworkApp(object):
"""
This doesn't "have" to come from a framework. It can be a raw WSGI app.
"""
def __init__(self, ...):
...
Predicate.__nonzero__ = lambda self: self.is_met(environ)
...
def __call__(self, environ, start_response):
...
"""
In both WSGI applications, the __init__ method will be called just once and
its object will be shared among threads.
Now, the problem arises when both applications are used together; this is, one
mounted on the other. When they're used independently one another, there's no
problem at all.
When they are mounted one on the other, Predicate.__nonzero__ will equal:
lambda self: self.is_met(myframework.environ)
or,
lambda self: self.is_met(tg.request.environ)
forever, in every thread, in every request.
How come? When MyFrameworkApp.__init__ is called first and
TurboGearsApp.__init__ is called next, Predicate.__nonzero__ takes the
following values, respectively:
lambda self: self.is_met(myframework.environ)
lambda self: self.is_met(tg.request.environ)
Likewise, when TurboGears.__init__ is called first and MyFrameworkApp.__init__
is called next, Predicate.__nonzero__ takes the following values,
respectively:
lambda self: self.is_met(tg.request.environ)
lambda self: self.is_met(myframework.environ)
It will certainly cause problems. In the best case scenario, an exception will
be raised and security won't be compromised. In the worse one, the environ
will have a default value and thus security could be compromised. Either way,
the application will be broken.
And it's a big mistake to believe that it would be solved if repoze.what
itself set Predicate.__nonzero__. It won't change anything at all. It'd be the
same module-level change, just that instead of being dynamic (like the monkey-
patch), it'd be static.
And before somebody argues "But it's not a common scenario!": So what? The
point is that it's possible. There's nothing weird in having two repoze.what
powered applications, one with TG2 and the other non-TG2 based.
===== Third party components =====
Now, this will be quite annoying for people who write plugins/routines that
deal with predicate checkers, and pretty dangerous for TG2 users.
Predicate checkers don't act as booleans, since the only officially supported
way to evaluate them is to pass the WSGI environ to the
.is_met()/.check_authorization() methods. But then we have "the TurboGears
way" to evaluate predicate checkers as plain old bool.
So, someone who wrote something that deals with predicate checkers could have
this:
if predicate:
# There's a predicate defined
else:
# There's no predicate defined (predicate is None)
But if a TurboGears app is being used, it'd actually mean:
if predicate:
# The predicate exists AND evaluates to True
else:
# There's no predicate OR it exists but is not met
Which is horribly dangerous for TG2 users. It'd be a critical security flaw.
Even I had to update repoze.what itself to take into account "the TurboGears
way" to evaluate predicates. But non-TG2 users who write stuff to deal with
predicates may not be aware of this and TG2 users who use such software will
get in a trouble... Until they find it and ask the maintainer to update the
original TG2-independent code.
=====
So, to sum up:
1.- Predicate checkers won't ever act as bool officially (i.e., with
Predicate.__nonzero__ defined by default), unless somebody comes up with a
solution to side-effect #2.
2.- TG2 users are *strongly* encouraged to disable this monkey-patch, unless
they are 500% sure that their applications *won't* *ever*:
A.- Mount or be mounted on another repoze.what-powered application which
is not based on Pylons/TG2.
B.- Use third party, TG2-independent tools which handle predicate
checkers.
Does anything need further clarification?
Cheers.
--
Gustavo Narea <xri://=Gustavo>.
| Tech blog: =Gustavo/(+blog)/tech ~ About me: =Gustavo/about |
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups
"TurboGears" 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?hl=en
-~----------~----~----~----~------~----~------~--~---