Hello,

Jorge said:
> >> > Anyway, that function would be anything but a predicate. It won't be
> >> > reusable, it won't be stateless.
> >>
> >> why it has to be stateless? why not reusable? you are assuming we'll
> >> keep doing allow_only = not_autorized() that of course is dumb.
> >> something like this could do the job.
> >> http://wiki.python.org/moin/PythonDecoratorLibrary#Countingfunctioncalls
> >
> > It must be stateless just because it must be reusable across requests and
> > threads. Isn't that reason enough?
> >
> > If it's a function that returns True or False, then its result is
> > obviously not stateless.
>
> in which way a function is not stateless? a function by definition IS
> stateless. there is no "local storage" of a function.

Did you really read what I said? "If it's a function that returns True or 
False, then its result is obviously not stateless."

In other words, *the* *result* of the function (*not* the function itself) is 
not stateless. That result will be either True or False, which is a 
*constant*, and the evaluation of an object cannot depend on a constant!


> >> > As a consequence, its result could not be used in
> >> > @require or .allow_only -- it will be True or False forever, whatever
> >> > the requests, because it would be evaluated when the module is loaded
> >> > and will remain constant from there on.
> >>
> >> not really. If you make a decorator that will evaluate on each call it
> >> will serve the same purpose. Although I agree the syntax will need to
> >> be changed.
> >
> > What are you going to evaluate, if what is left is a bool? (see below)
>
> why do you assume the call is going to be made on module load?

Maybe because it's one of the basic things you first learn in Python? Try this 
simple experiment if you don't believe me:

Create a module which has the contents below:
"""
class MockObject(object):
    def __init__(self):
        print "Initialized"

class CoolController(object):
    allow_only = MockObject()

    def my_action(self,):
        pass
    my_action.predicate = MockObject()
"""

Now try to import it from another module (just import it, don't do anything 
else) and tell me what you got. Next, guess why you got that. Hint: Anything 
that is outside a function/method is evaluated when the module is loaded, even 
decorators and their arguments.


> >> > The other similar thing would be to wrap the predicates around
> >> > functions which return True/False. But it wouldn't be a good idea
> >> > either: 1.- You'd need one of those functions per predicate.
> >>
> >> why? first of all the predicate will be become the functions instead
> >> of the class. Second with just one decorator you can do this. Remember
> >> a decorator can keep state
> >>
> >> >  2.- It could be dangerous. They should only be used *inside* the
> >> > controller action (i.e., inside the method, never outside) or in the
> >> > templates. This is, they must not be used in .allow_only or @require,
> >> > for example.
> >>
> >> see below
> >>
> >> >  3.- You'd have to instantiate them on each function call, which would
> >> > be inefficient:
> >> > """
> >> > def has_permission(permission):
> >> >    predicate = repoze.what.predicates.has_permission(permission)
> >> >    return predicate.is_met(request.environ)
> >> > """
> >>
> >> if it's a function it doesn't needs to be instantiated, seems like you
> >> missed the point a predicate will be a function not a class and you
> >> will have 3-4 decorators that will provide the "is_met", "allow_only"
> >> and "require" functionality.
> >
> > What you propose is simply unrealistic:
> >
> > If you replace predicates with functions that return True or False, what
> > the decorator will receive will be a True or False. The decorator won't
> > be able to evaluate anything.
> >
> > The closest thing you could do, is to use it as callable in the @require
> > and call it in the controllers:
> > """
> > @require(predicate1)
>
> @require(predicate1,param1,param2)
>
> > def my_action(self,):
> >    if predicate2():
> >        pass
> > """
>
> I'm willing to put on that sacrifice for a cleaner query API. after
> all I believe you do more queries on auth than calls to
> require/allow_only
>
> > And that would be wrong too, because then predicates won't be able to
> > receive arguments:
> > """
> > @require(has_permission("foo"))  # <-- @require received True or False
> > when #     the module was loaded and this value #     won't ever change
>
> why do you keep thinking the function will be evaluated on module
> load. the function will be called! by require on each call to the
> function.

You're totally mistaken.


> > def my_action(self, ):
> >    if predicate_foo(): # here it will work
> >        pass
> > """
> >
> > Now let's imagine that somehow it's possible what you say, that the
> > predicate could keep track of the status:
>
> Am I talking Chinese? the predicate doesn't need to keep track of
> anything when I said that? it will simply read for the env and return
> true or false.

Sorry, I meant that the @require would keep track of the status, as you said 
above: s/predicate/require.


> >> >  4.- You can already do "if my_predicate(): do_something()", thanks to
> >> > the booleanize_predicates() monkey-patch enabled by default in TG2.
> >>
> >> which you totally trash as a "hack" in the documentation and point out
> >> it's dangerous because you are injecting (monkey-patch) the function
> >> instead of making it the default, which is only dangerous because of
> >> the monkey-patch.
> >
> > No. Even if __non_zero__ was implemented by default (which is absolutely
> > impossible!), it'd be the *exact* *same* thing.
>
> could you please present some code that explains this claim. I see no
> reason why __non_zero__ is unsafe, insecure, etc. as you point out in
> the docs, which are inconveniently down :(  There is nothing on the
> python documentation that implies this. What is the big problem with
> it?

OK, I've just made my best attempt to explain it again in a new thread.


> >> Last please keep in mind this is an experiment and I'm not entirely
> >> sure I can accomplish the same API with predicates as functions. But
> >> in the case I do go back to classes I think we should rethink the API
> >> (which is my original goal) because as I pointed out in here
> >> http://groups.google.com/group/turbogears/browse_thread/thread/c2aa4cb5e
> >>d07 f52d, the current API is completely horrible
> >
> > "Completely horrible" sounds quite excessive, IMHO.
>
> no it is not excessive everyone, that has seen this says WTF?

Well, until proved otherwise (i.e., I hear it from someone else), "everyone" 
means just Jorge to me.


> > I do believe the API *partially* sucks, though, but for other reason
> > which has nothing to do with predicate evaluation: It's highly influenced
> > by TG1 Identity and AuthKit, two authz frameworks defective by design
> > because they reinvented the wheel in the authorization model used,
> > instead of implementing and possibly extending *proven* authorization
> > mechanisms (such as _real_ ACLs).
> >
> > There was no need to reinvent the wheel in the authorization field, what
> > we lack is working implementations. repoze.what 2 will be an attempt to
> > amend this mistake.
>
> I really don't see any communication happening here. You keep looking
> at this from the way things are instead of how they should be. Please
> explain to me in the least number of lines of code why something like
> this will never work.
>
> def get_credentials():
>     credentials = environ.get('repoze.what.credentials', {})
>     if not credentials:
>         unmet('No Credentials where found')
>
> def check_authorization(result):
>     "This method ensures your predicate behaves returning something.
> Should it enforce boolean?"
>     if not result:
>         raise NotAuthorizedError
>     return result
>
> def is_user(user_name):
>     message = u'The current user must be "%(user_name)s"'
>
>     credentials = get_credentials()
>     if user_name == credentials.get('repoze.what.userid'):
>         return True
>
> def in_group(group_name):
>     message = u'The current user must belong to the group "%(group_name)s"'
>
>     credentials = get_credentials()
>     if group_name in credentials.get('groups'):
>         return True
>
> the environ is trashed on each request which means each time you call
> the function it will evaluate to something different (stateless) if
> you are anal about thread-locals from pylons simply pass in the
> environ to each predicate.

Man, that's just horrible. The explanation goes beyond the way repoze.what 
works, it's all about the very basics of Python: Here you cannot use functions 
that return True or False. Period. 

I repeat: They'd be evaluated when the module is loaded and thus .allow_only 
and @require will just receive a True or a False, which is obviously not going 
to change on a request-basis.

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