On Mon, Jun 15, 2009 at 11:54 AM, Gustavo Narea<[email protected]> wrote:
>
> 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.
>

did you missed the part where I said that syntax will need to change?
we'll need something like

allow_only = autorize(MockObject())

>
>> >> > 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.
>
yes, thank you for that now I understand what your problem with this is.

>
>> >> 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.
>
that is because you haven't had to explain this +10 times on IRC to
different people.

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

I'm more than willing to complicate the allow_only and require
interfaces in order to simplify the "query api" as you are going to
use the "query" part many more times than then "require" api.

As I said a couple of emails ago you keep trying to stick with the
status quo. If that is your api AND you CALL the function on each
request, just like __before__ is used in "roll your own" pylons auth
systems.

> 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