macro like functionality in python django - python

I have some django view handler functions which are structured like this
def view1(request):
# Check for authorization
if not isAuthorized(request):
return HttpResponse('Foo error', status=401)
return HttpResponse('view1 data')
def view2(request):
# Check for authorization
if not isAuthorized(request):
return HttpResponse('Foo error', status=401)
return HttpResponse('view2 data')
def view3(request):
# Check for authorization
if not isAuthorized(request):
return HttpResponse('Foo error', status=401)
return HttpResponse('view3 data')
I want to make this part :
# Check for authorization
if not isAuthorized(request):
return HttpResponse('Foo error', status=401)
some sort of one-liner, so that I do not have to repeat it in each view
In C this would have been a macro, but I've no clue how to achieve something similar in python
The check authorization function part is an example, it can be any check which has nothing to do with user authorization in particular
[Edit]
https://stackoverflow.com/users/2337736/peter-deglopper mentions decorators ...
To elaborate
I have a web API that can take either POST or GET
# return either GET or POST dict whichever exists
def getParams(request):
if request.method == 'GET':
return request.GET
return request.POST
The views do this :
def someAPI(request):
dct = getParams(request)
if not isValid(dct):
return HttpResponse('Bad request', status=401)
How could I acheive this with a decorator? I have that getParams() function in between....

Usually you'd use view decorators for this:
https://docs.djangoproject.com/en/dev/topics/http/decorators/
There's a built in one for checking login status:
https://docs.djangoproject.com/en/1.7/topics/auth/default/#the-login-required-decorator
And one for arbitrary tests on the authenticated user:
https://docs.djangoproject.com/en/1.7/topics/auth/default/#django.contrib.auth.decorators.user_passes_test
A full example adapted from http://mrcoles.com/blog/3-decorator-examples-and-awesome-python/:
from functools import wraps
def validate_params(view_func):
def _decorator(request, *args, **kwargs):
dct = getattr(request, request.METHOD)
if not isValid(dct):
return HttpResponse('Bad request', status=401)
else:
response = view_func(request, *args, **kwargs)
return response
return wraps(view_func)(_decorator)
I'm using getattr rather than an if/then on request.METHOD - I think that's cleaner. If you like you could still use your getParams call instead.

As Peter says, decorators are an ideal solution for this. But an alternative is to use class-based views: you can define a base class that does the checking, and subclasses that implement the specific behaviour.

Related

django: customized token authentication

I am doing APIs, and trying to use token. I want to achieve:
1, client issues a request to server with token in the header;
2, server verify the token and do something (one time).
Looking into the DRF, If I would like to overwrite the authenticate function, I also need to return User object or our own customized User object. I would not like to return any User object, because the whole process does not involve any User, just token and permission to do something. How to do this?
Thanks
First of all, create modelToken in models.py. Furthermore, you need to create a token_required decorator. Whenever a user logs in, a token is created, and whenever she/he logs out, the token is destroyed.
login:
def login(request):
username=request.payload.get('username')
password=request.payload.get('password')
user,err=Auth.authenticate(username,password)
if err:
raise Exception()
token=Token.generate()
#you can return user
return {'token':token}
decorators:
def token_required(func):
def inner(request, *args, **kwargs):
try:
request.token=Token.objects.get(token=token)
return func(request, *args, **kwargs)
except Token.DoesNotExists:
pass
return inner
logout:
#token_required
def logout(request):
if request.method=='POST':
request.token.delete()
return {'status':'ok'}

How to use django custom decorator

I have a lot of methods that require authorization. So instead of writting the same code over and over I wanna simplify it. As I understood I can't use #login_required as it redirects to login page, which I don't have. (user logs in into system via dropdown menu included in all templates). I just wanna raise PermissionDenied without any redirecting.
get_profile(request):
if request.user.is_authenticated():
do things
else:
raise PermisionDenied
One solution is use a custom decorator:
def login_required_no_redirect(f):
def wrap(request, *args, **kwargs):
if request.user.is_authenticated():
return f(request, *args, **kwargs)
else:
raise PermissionDenied
wrap.__doc__ = f.__doc__
wrap.__name__ = f.__name__
return wrap
But it requires to pass a function inside. So I can't just place #login_required_no_redirect() above the method. It requires to pass some function as a parameter. Where do I get it? There're a lot of django decorators without parameters, how do I write the similar one?
Best regards,
Django already has a #permission_required decorator for this.
If the raise_exception parameter is given, the decorator will raise PermissionDenied, prompting the 403 (HTTP Forbidden) view instead of redirecting to the login page.

How to make a python decorator stop a user flow entering a function?

I currently have a authorization header:
def authorize(allowed_groups=None, debug=False):
# Do your auth here...
headers = cherrypy.request.headers
return
if 'consumer_key' in headers:
if check_consumer_key(headers['consumer_key']):
if 'access_token' in headers:
cherryPy.accept = check_access_token['access_token']
else:
cherrypy.accept = False
I'm using it here:
#cherrypy.tools.authorize()
def GET(self, id=None):
#return the order list for this account type
if cherrypy.accept:
print "Accepted"
user = User()
usermodel = user.get_all()
return json.dumps(usermodel, cls=AlchemyEncoder)
So, rather than doing a cherrpy.accept = True, how can I return an error page in the decorator itself when the authorization isn't right?
You could just make another decorator that will only call the original function if the authorization succeeds and otherwise just displays an error page instead. This way you can avoid changing the other decorator.
def rejectNotAuthorized (f):
def inner ():
if cherrypy.accept:
f()
else:
displayErrorPage()
return inner
#cherrypy.tools.authorize()
#rejectNotAuthorized
def GET(self, id=None):
pass
Of course you could also integrate that into the original decorator. You just need to make sure to return a function that, depending on the authentication result, either runs the real function or shows the error.

response redirect to a different page when 405 raise

I have a view function which I want to use only request method is POST. I got to know about require_POST decorator here, but what I want is that if method is not POST then redirect user to different view using HttpResponseRedirect. How can I do this ?
from django.views.decorators.http import require_POST
#require_POST
def my_view(request):
# I can assume now that only POST requests make it this far
# ...
I know I can do it by adding two lines of code, without using require_POST redirect in my method itself like
if request.method != 'POST':
return HttpResponseRedirect(view_url)
but I am more interested in using decorator itself and when this decorator raises 405, then redirect to another view.
A custom decorator is the way to go.
I myself use one similar to the one you need and I'll post the code.
Do upvote #aychedee s answer since he was first. :)
def require_post_decorator(function=None, redirect_url='/'):
def _decorator(view_function):
def _view(request, *args, **kwargs):
if request.method == 'POST':
#do some before the view is reached stuffs here.
return view_function(request, *args, **kwargs)
else:
return HttpResponseRedirect(redirect_url)
_view.__name__ = view_function.__name__
_view.__dict__ = view_function.__dict__
_view.__doc__ = view_function.__doc__
return _view
if function:
return _decorator(function)
return _decorator
You would have to use a custom decorator. You can see the code for the Django decorator here. It's already returned a response before your code is reached at all, so absolutely nothing you do in your view function would be run anyway.
There is nothing wrong with manually returning a redirect if the request is not a POST. If you use this pattern in a few different places in your code I would then refactor it into a decorator later. But if this is the first place you are using it then it's overkill.

How to make a python decorator function in Flask with arguments (for authorization)

I used a flask snippet for my flask-login that checks that a user is logged in:
from functools import wraps
def logged_in(f):
#wraps(f)
def decorated_function(*args, **kwargs):
if session.get('logged_in') is not None:
return f(*args, **kwargs)
else:
flash('Please log in first.', 'error')
return redirect(url_for('login'))
return decorated_function
And I decorate views like so:
#app.route('/secrets', methods=['GET', 'POST'])
#logged_in
def secrets():
error = None
I'd like to do something similar for authorization, too. Right now, I have many views to check that a user owns a resource, let's say the hotdogs resource.
If the logged_in user is the owner of that particular hotdog, he can edit and manage his hotdogs. If he isn't, I kick him out to the unauthorized screen.
#app.route('/<hotdog>/addmustard/',methods=["GET"])
#logged_in
def addmustard(hotdog):
if not (authorizeowner(hotdog)):
return redirect(url_for('unauthorized'))
do_stuff()
authorizeowner() takes a hotdog as input and checks that the recorded hotdog owner matches the owner name listed in the session variable.
I tried making a owns_hotdog wrapper/decorator function similar to my logged in one, but it complained that it didn't accept arguments. How can I achieve something similar? Something like...
def owns_hotdog(f):
#wraps(f)
def decorated_function(*args, **kwargs):
if not authorizeowner(hotdog):
return f(*args, **kwargs)
else:
flash('Please log in first.', 'error')
return redirect(url_for('login'))
return decorated_function
From the error message, decorator seems not to be receiving the hotdog argument that Flask views have access to from the variable in the route. My hope is for something like...
#app.route('/<hotdog>/addmustard/',methods=["GET"])
#logged_in
#owns_hotdog(hotdog)
def addmustard(hotdog):
do_stuff()
Everything works with my current authorizeowner(hotdog) function, but it just seems cleaner to have this in place as a wrapper on top of my route, rather than as the first line inside the route.
Some other notes:
I know that Flask-Security and Flask-Principal can manage
authorization for me. Unfortunately, I'm using an unsupported
database back-end and am unable to use these extensions. So, I'm
forced to do authentication without them.
If you see any glaring holes in doing authorization this way, please let me know!
Here's how to do it:
from functools import update_wrapper
def owns_hotdog(hotdog):
def decorator(fn):
def wrapped_function(*args, **kwargs):
# First check if user is authenticated.
if not logged_in():
return redirect(url_for('login'))
# For authorization error it is better to return status code 403
# and handle it in errorhandler separately, because the user could
# be already authenticated, but lack the privileges.
if not authorizeowner(hotdog):
abort(403)
return fn(*args, **kwargs)
return update_wrapper(wrapped_function, fn)
return decorator
#app.errorhandler(403)
def forbidden_403(exception):
return 'No hotdogs for you!', 403
When decorator takes arguments, it's not really a decorator, but a factory function which returns the real decorator.
But if I were you, I would use Flask-Login for authentication and augment it with custom decorators and functions as yours to handle authorization.
I looked into Flask-Principal, but found it overly complicated for my tastes. Haven't checked Flask-Security, but I believe it uses Flask-Principal for authorization. Overall I think that Flask-Login with some custom code is enough most of the time.

Categories