On Sun, Jun 14, 2009 at 6:59 PM, Gustavo Narea<[email protected]> wrote:
>
> Hi everyone.
>
> Jorge said:
>> On Sat, Jun 13, 2009 at 10:35 AM, Gustavo Narea<[email protected]> wrote:
>> > Hello,
>> >
>> > Jorge said:
>> >> My goal is to make the "booleanize" stuff work by default. Where teh
>> >> predicate will return true/false if it's called from allow_only or
>> >> @protect (sorry if that's not the name of the decorator) then
>> >>
>> >> result = predicate()
>> >> if not result:
>> >> raise NotAuthorized
>> >
>> > Where would that code be? In the user's controller or inside @require?
>> > Either way, that's already the way things work. Give it a try.
>>
>> in "repoze.what"
>>
>> >> which means the predicate is simply a function,
>> >
>> > Just to return a Python bool, instead of another class instance?
>>
>> yes,
>>
>> > 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.
>
>> > 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?
>
>> > 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.
> 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.
> 1.- You'll end up with a method that is not stateless, in spite of it being
> most likely shared by several threads.
> 2.- It just cannot receive a bool. It must receive an instance of some class
> -- an object which could verify that the condition is met. And that's what
> predicate checkers already do.
>
> Whoever experiments to work around this, will either:
> - Give up soon enough.
> - Come up with a horribly complex and unreliable solution.
>
> Because there's nothing sane we could do about it.
>
>
>> > 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?
>
>> 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/c2aa4cb5ed07
>>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?
> 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.
> 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
-~----------~----~----~----~------~----~------~--~---