How to transfer url parameters to repoze custom predicate checkers - python

I would like to create a repoze custom predicate checker that is capable to access url parameters and validate something. But I would like to use allow_only to set this permission checker in all the controller's scope. Something like:
class MyController(BaseController):
allow_only = All(not_anonymous(msg=l_(u'You must be logged on')),
my_custom_predicate(msg=l_(u'something wrong')))
def index(self, **kw):
return dict()
then, my_custom_predicate should check the url paramters for every request in every MyController method, and do whatever it do.
The problem is just that: how to allow my_custom_predicate to check the url parameters, using it in that way I wrote above.

May be you need to use ControllerProtector
from repoze.what.plugins.pylonshq import ControllerProtector
allow_only = All(not_anonymous(msg=l_(u'You must be logged on')),
my_custom_predicate(msg=l_(u'something wrong')))
#ControllerProtector(allow_only)
class MyController(BaseController):
def index(self, **kw):
return dict()
See docs at http://code.gustavonarea.net/repoze.what-pylons/API.html

Related

Python3 "Class factory" - ex: API(token).MyClass()

I'm writing a python REST client for an API.
The API needs authentication and I would like to have many API client objects running on the same script.
My current code for the API is something like this:
class RestAPI:
def __init__(self, id):
self.id = id
self.fetch()
def fetch(self):
requests.get(self.url+self.id, auth=self.apikey)
class Purchase(RestAPI):
url = 'http://example.com/purchases/'
class Invoice(RestAPI):
url = 'http://example.com/invoices/'
...
And I would like to use the API like that:
api_admin = Api('adminmytoken')
api_user = Api('usertoken')
…
amount = api_admin.Purchase(2).amount
api_user.Purchase(2).amount # raises because api_user is not authorized for this purchase
The problem is that each object needs to know it's apikey depending on the client I want to use.
That pattern looks like to me to a "class factory": all the classes of RestAPI need to know of the provided token.
How is it possible to cleanly do that without giving manually the token to each model ?
I think the issue here is that your design is a little backwards. Inheritance might not be the key here. What I might do is take the api token as an argument on the User class, then that gets passed to an instance-level binding on the Rest interface:
class APIUser:
def __init__(self, id, api_key, **kwargs):
self._rest = Interface(id, api_key, **kwargs)
def purchase(self, some_arg):
# the interface itself does the actual legwork,
# and you are simply using APIUser to call functions with the interface
return self._rest.fetch('PURCHASE', some_arg)
class Interface:
methods = {
# call you want (class url)
'PURCHASE': (Purchase, 'https://myexample.com/purchases'),
'INVOICE': (Invoice, 'https://myexample.com/invoices'),
# add more methods here
}
def __init__(self, id, key):
self.id = id
self.key = key
self.session = requests.Session()
def _fetch(self, method, *args, **kwargs):
# do some methods to go get data
try:
# use the interface to look up your class objects
# which you may or may not need
_class, url = self.methods[method]
except KeyError as e:
raise ValueError(f"Got unsupported method, expected "
f"{'\n'.join(self.methods)}") from e
headers = kwargs.pop('headers', {})
# I'm not sure the actual interface here, maybe you call the
# url to get metadata to populate the class with first...
req = requests.Request(_class.http_method, url+self.id, auth=self.key, headers=headers).prepare()
resp = self.session.send(req)
# this will raise the 401 ahead of time
resp.raise_for_status()
# maybe your object uses metadata from the response
params = resp.json()
# return the business object only if the user should see it
return _class(*args, **kwargs, **params)
class Purchase:
http_method = 'GET'
def __init__(self, *args, **kwargs):
# do some setup here with your params passed by the json
# from the api
user = APIUser("token", "key") # this is my user session
some_purchase = user.purchase(2) # will raise a 401 Unauthorized error from the requests session
admin = APIUser("admintoken", "adminkey") # admin session
some_purchase = admin.purchase(2)
# returns a purchase object
some_purchase.amount
There are a few reasons why you might want to go this way:
You don't get the object back if you aren't allowed to see it
Now the rest interface is in control of who sees what, and that's implicitly tied to the user object itself, without every other class needing to be aware of what's going on
You can change your url's in one place (if you need to)
Your business objects are just business objects, they don't need to do anything else
By separating out what your objects actually are, you still only need to pass the api keys and tokens once, to the User class. The Interface is bound on the instance, still giving you the flexibility of multiple users within the same script.
You also get the models you call on explicitly. If you try to take a model, you have to call it, and that's when the Interface can enforce your authentication. You no longer need your authentication to be enforced by your business objects

Check view method parameter name in Django class based views

I have created a decorator in my Django project to inject parameter values to the decorated method's parameters.
I do this by using inspect.getargspec to check which parameters are present in the method and place them in kwargs. Otherwise I get an error due to the incorrect number of parameters in the method.
While this works properly in individual view methods, it fails when it comes to Django's class based views.
I believe this might be because the decorators are applied using #method_decorator at the class level to the dispatch method instead of the individual get and post methods.
I'm a python newbie and might be overlooking something obvious here.
Is there a better way to do what I'm doing? Is it possible to get the method parameter names in a class based view?
I'm using Python 2.7 and Django 1.11
The Decorator
def need_jwt_verification(decorated_function):
#wraps(decorated_function)
def decorator(*args, **kwargs):
request = args[0]
if not isinstance(request, HttpRequest):
raise RuntimeError(
"This decorator can only work with django view methods accepting a HTTPRequest as the first parameter")
if AUTHORIZATION_HEADER_NAME not in request.META:
return HttpResponse("Missing authentication header", status=401)
jwt_token = request.META[AUTHORIZATION_HEADER_NAME].replace(BEARER_METHOD_TEXT, "")
try:
decoded_payload = jwt_service.verify_token(jwt_token)
parameter_names = inspect.getargspec(decorated_function).args
if "phone_number" in parameter_names or "phone_number" in parameter_names:
kwargs["phone_number"] = decoded_payload["phone"]
if "user_id" in parameter_names:
kwargs["user_id"] = decoded_payload["user_id"]
if "email" in parameter_names:
kwargs["email"] = decoded_payload["email"]
return decorated_function(*args, **kwargs)
except JWTError as e:
return HttpResponse("Incorrect or expired authentication header", status=401)
return decorator
A class based view
#method_decorator([csrf_exempt, need_jwt_verification], name="dispatch")
class EMController(View):
def get(self, request, phone_number, event_id):
data = get_data()
return JsonResponse(data, safe=False)
def post(self, request, phone_number, event_id):
return JsonResponse("Operation successful", safe=False)
EDIT:
The obvious solution of applying the decorator at the method level, doesn't work with Django's class based views. You need apply the decorator at the url configuration or apply the decorator to the dispatch method.
EDIT:
I've posted code that was related to a workaround I was exploring, passing the parameter names as an argument into the decorator.
I found this post: Function decorators with parameters on a class based view in Django
which may provide the answer to your problem:
If you want to pass a decorator with parameters, you only need to:
Evaluate the parameters in the decorator-creator function.
Pass the evaluated value to #method_decorator.
The above mentioned and the code provided in the linked answer taken under consideration, you should:
injectables=[inject_1, inject_2, ..., inject_n]
decorators = [csrf_exempt, need_jwt_verification(injectables)]
#method_decorator(decorators, name="dispatch")
class EMController(View):
...
Leaving my previous mistaken answer here for legacy reasons, don't try this at home (or anywhere, in django, for that matter!!)
If we observe the "decorating a class" docs, we can see the following:
Or, more succinctly, you can decorate the class instead and pass the name of the method to be decorated as the keyword argument name:
so you have to change the name argument of your #method_decorator to match the method that will apply to:
decorators = [csrf_exempt, need_jwt_verification(injectables=[])]
#method_decorator(decorators, name='get')
#method_decorator(decorators, name='post')
class EMController(View):
Personally I prefer to place my decorators on top of the specific method they will apply to:
class EMController(View):
#method_decorator(decorators)
def get(self, request, phone_number, event_id):
...
#method_decorator(decorators)
def post(self, request, phone_number, event_id):
...
What I want seemed impossible in the current state of the libraries. So here's what I finally went with.
parameter_names = inspect.getargspec(decorated_function).args
if "phone_number" in parameter_names or "phone_number" in injectables:
kwargs["phone_number"] = decoded_payload["phone"]
if "user_id" in parameter_names:
kwargs["user_id"] = decoded_payload["user_id"]
if "email" in parameter_names:
kwargs["email"] = decoded_payload["email"]
request.__setattr__("JWT", {})
request.JWT["phone_number"] = decoded_payload["phone"]
request.JWT["user_id"] = decoded_payload["user_id"]
request.JWT["email"] = decoded_payload["email"]
This decorator will automatically populate parameters in method based views as intended.
But it will also inject an JWT attribute to the request object for the class based views to use. Like request.GET and request.POST.

Is it possible аdd http-header x-accel-expires in add_view, add_static_view from Pyramid?

I'm need opportunity to add http-header(X-Accel-Expires) for each add_view.
And for add_static_view.
Ideally would be pass parameter, something like add_view(..., x_accel_expires=100), add_static_view(..., x_accel_expires=100), but the pyramid can't this.
I can do Base View, where add http-header X-Accel-Expires.
I will only need to add an attribute in each view, something like: add_headers = (('X-Accel-Expires', '100'),).
But how can add this header for add_static_view?
For the case of add_view you can use the decorator argument as documented by view configuration parameters:
A dotted Python name to a function (or the function itself) which will be used to decorate the registered view callable. The decorator function will be called with the view callable as a single argument. The view callable it is passed will accept (context, request). The decorator must return a replacement view callable which also accepts (context, request). The decorator may also be an iterable of decorators, in which case they will be applied one after the other to the view, in reverse order.
This is the example given in the documentation:
#view_config(..., decorator=(decorator2, decorator1))
def myview(request):
pass
Is similar to doing:
#view_config(...)
#decorator2
#decorator1
def myview(request):
pass
This would allow you to write the following for example:
def accel_headers_factory(expires=100):
def add_accel_headers(view):
def wrapped_view(context, request):
resp = view(context, request)
resp.headers.append(('X-Accel-Expires', expires))
return wrapped_view
return add_accel_headers
Then use:
#view_config(..., decorator=(accel_headers_factory(500),))
def myview(request):
return {}
This would then always add the X-Accel-Expires header to the response as returned from the view.
Unfortunately it doesn't look like add_static_view allows you to pass it a decorator argument.

Decorating pyramid views implemented as class methods

I have code like this in my pyramid project:
class SomeViews(object):
#view_config(...)
def view_a(request):
return {...}
#view_config(...)
def view_b(request):
return {...}
I would like to decorate the view methods to modify the returned dictionary. It's possible to apply an decorator to a view, if it's the first one before view_config. Otherwise Pyramid is still using the original function, due to the nature of Venusian.
Because I would apply the same decorator to all methods in a class, I would prefer to use a class decorator. So instead of doing
#view_config(...)
#my_decorator("some_meta_info")
def view_b(request):
return {...}
for each method, I would like to do
#my_decorator("some_meta_info")
class SomeViews(object):
...
But because the class decorator is executed after the view_config calls, again it does not work with Venusian. I had a look at the view_defaults implementation, to get a hint how to solve my problem, but I did not figured out how it works.
Any hint how to do that? What I want to do, is just to modify the result dictionary of a set of view methods. I also thought about using the BeforeRender event, but I found no way to inject the required meta data in a way that I can access it in the event handler. Using decorators would anyway be the more natural and pythonic way in my opinion.
import functools
def my_decorator(value):
def _dec(f):
#functools.wraps(f)
def wrapper(context, request):
print 'hey look!', value
return f(context, request)
return wrapper
return _dec
#view_defaults(decorator=my_decorator('some meta info'))
class SomeViews(object):
def __init__(self, request):
self.request = request
#view_config(..., renderer='string')
def view_a(self):
return 'foo'
Think of view_defaults as default options passed to every view_config on the class. If you add a decorator to the view_config though, the defaults are overridden and your default decorator would be dropped.

Python Bottle.py decorator confusion

i have a decorator named "auth"
def auth(check_func=validate_login):
def decorator(view):
def wrapper(*args, **kwargs):
auth = check_func()
if auth:
return view(*args, **kwargs)
return bottle.redirect('/login.html')
return wrapper
return decorator
the auth decorator is used like this
#get('/')
#view("someview")
#auth()
def handler():
#myhandlercode
so the auth decorator calls the view function which renders my template in bottle.py.
But now i want to return json instead of rendering a view. So what changes do i have to make to the auth decorator code to make this happen? I am confused as to how to call the handler instead of the view from the auth code.
EDIT 1 : Bottle allows you to return dict, it directly converts it to json. And i dont want to use the view at all, i just want to return json to the user from my handlers. So should i just remove the #view decorator? and what should i call instead in the auth decorator?
Bottle route decorators are able to apply decorators to you without destroy auto json feature.
#get('/', apply=[auth])
def handler():
...
If you don't want a view, just remove your #view decorator, Bottle handle dicts gracefully, converting it to a JSON answer.
I dont believe its right to have auth shoehorning in json dumping.
Here's an example with plain ol python of using decorators
def validate():
return True
def auth(valid=validate):
def _auth(f):
def _auth_wrap():
if not valid():
raise Exception('redirect')
return f()
return _auth_wrap
return _auth
def view(tmpl):
def _view(f):
def _view_wrap():
return tmpl.format(f())
return _view_wrap
return _view
#view('Hello, {0}')
#auth()
def handler():
return 'World'
handler
# outputs: __main__._view_wrap
handler()
# outputs: 'Hello, World'
and also the line
return tmpl.format(f())
The f func is __main__._auth_wrap which is calling validate and returning the executed handler.
so then you would handle dumping json by doing something else besides tmpl.format in the above, such as calling a seperate method and passing needed info, or shoehorning into view decorator, which either way will be more appropriate.
So to answer the end question a little better, if you want to dynamically decide whether or not to dump json based on the request and bottle.py's view func doesn't support this, then you could make a view wrapper similar to the above that does the checks on the request object or whatever you want to use to determine json output, and then either call bottle.py's view or json.dumps on f() within _view_wrap
If you want to make a func always dump json, then delete the view decorator and create a json decorator similar to the view decorator above that will return json.dumps(f())
The main point here is to keep auth doing what it's name implies.
You are returning Json (or rather a python dictionary) from your view, right? In that case you don't have to change anything in the decorator. That which the view returns is not immediately forwarded to the user's browser, it is first processed by bottle then returned to the user. When you return a dictionary from the view it is treated as Json, when returning a template string it is treated as Html.

Categories

Resources