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.
Related
I'm trying to create a two-tiered authentication system with basic auth, using Flask-HTTPAuth. My application has two routes, a base route at / accessible to any logged in user, and an admin route at /admin accessible only to users who are (as you might expect) logged in as admins.
So I decided to implement this by chaining decorators, with the relevant part of the code as follows (where dbops is just a namespace that handles talking to the database):
#auth.verify_password
def verify_pw(lastname, password):
ln = lastname.lower()
if ln in dbops.list_users():
hashed_pw = dbops.find_hashed_password(ln)
return bcrypt.checkpw(password.encode('utf8'), hashed_pw.encode('utf8'))
return False
def must_be_admin(f):
#wraps(f)
def wrapper(*args, **kwargs):
if dbops.is_admin(auth.username()):
return f(*args, **kwargs)
return "Not authorized."
return wrapper
#core.route("/")
#auth.login_required
def dataentry():
return render_template("dataentry.html")
#core.route("/admin")
#must_be_admin
#auth.login_required
def admin():
return render_template("admin.html")
This works fine so long as anyone trying to log in as an admin user first visits the / route: it prompts for a username and password, and then the admin user can go to /admin and carry out logged-in admin tasks.
However, if an admin user first visits /admin it doesn't give a login prompt. It just throws, and after poking around in the debugger I've determined that auth.username() is returning an empty string. So, my guess is that for some reason, the inner decorator isn't being applied, hence the lack of a login prompt.
Does anyone know what might be going on here?
My first hypothesis was that this was an easy bug, because the inner function on the admin decorator wasn't being called until after the is_admin check. So I tried to fix that my calling the function---and thus presumably making auth.username() available--- before the check, as follows:
def must_be_admin(f):
#wraps(f)
def wrapper(*args, **kwargs):
dummy_to_get_username = f(*args, **kwargs)
if dbops.is_admin(auth.username()):
return dummy_to_get_username
return "Not authorized."
return wrapper
But that just caused the same behavior.
I see from this prior SO that the recommended way to do this from the library author is to just create two separate Flask-HTTPAuth objects. Which I can do, no problem. But clearly my mental model about how decorators are working are failing, so I'd like to solve this problem independent of getting the functionality I want working...
The correct order in which you apply decorators is sometimes hard to figure out without knowing what the decorator does, but unfortunately the wrong order will make the application behave incorrectly.
For decorators that do something "before" the view function runs, as in this case, you typically have to put the decorators in the order in which you want them to execute. So I think your code will do what you expect when you use Flask-HTTPAuth's login_required before your must_be_admin:
#core.route("/admin")
#auth.login_required
#must_be_admin
def admin():
return render_template("admin.html")
In this way, the credentials will be checked first, and if missing or invalid login_required will return a 401 error to the browser, which will make the login prompt appear. Only after credentials are determined to be valid you want to evaluate the admin decorator.
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.
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.
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.
It's not the same to POST to an URL than to GET it, DELETE it or PUT it. These actions are fundamentally different. However, Django seems to ignore them in its dispatch mechanism. Basically, one is forced to either ignore HTTP verbs completely or do this on every view:
def my_view(request, arg1, arg2):
if request.method == 'GET':
return get_view(request, arg1, arg2)
if request.method == 'POST':
return post_view(request, arg1, arg2)
return http.HttpResponseNotAllowed(['GET', 'POST'])
The few solutions I have found for this in the web (this snippet for verb-based dispatch, or this decorator for verb requirement) are not very elegant as they are clearly just workarounds.
The situation with CherryPy seems to be the same. The only frameworks I know of that get this right are web.py and Google App Engine's.
I see this as a serious design flaw for a web framework. Does anyone agree? Or is it a deliberate decision based on reasons/requirements I ignore?
I can't speak for Django, but in CherryPy, you can have one function per HTTP verb with a single config entry:
request.dispatch = cherrypy.dispatch.MethodDispatcher()
However, I have seen some situations where that's not desirable.
One example would be a hard redirect regardless of verb.
Another case is when the majority of your handlers only handle GET. It's especially annoying in that case to have a thousand page handlers all named 'GET'. It's prettier to express that in a decorator than in a function name:
def allow(*methods):
methods = list(methods)
if not methods:
methods = ['GET', 'HEAD']
elif 'GET' in methods and 'HEAD' not in methods:
methods.append('HEAD')
def wrap(f):
def inner(*args, **kwargs):
cherrypy.response.headers['Allow'] = ', '.join(methods)
if cherrypy.request.method not in methods:
raise cherrypy.HTTPError(405)
return f(*args, **kwargs):
inner.exposed = True
return inner
return wrap
class Root:
#allow()
def index(self):
return "Hello"
cowboy_greeting = "Howdy"
#allow()
def cowboy(self):
return self.cowboy_greeting
#allow('PUT')
def cowboyup(self, new_greeting=None):
self.cowboy_greeting = new_greeting
Another common one I see is looking up data corresponding to the resource in a database, which should happen regardless of verb:
def default(self, id, **kwargs):
# 404 if no such beast
thing = Things.get(id=id)
if thing is None:
raise cherrypy.NotFound()
# ...and now switch on method
if cherrypy.request.method == 'GET': ...
CherryPy tries to not make the decision for you, yet makes it easy (a one-liner) if that's what you want.
Came across this from Google, and thought of updating.
Django
Just FYI, This is now supported in Django as class based views. You can extend the generic class View and add methods like get(), post(), put() etc. E.g. -
from django.http import HttpResponse
from django.views.generic import View
class MyView(View):
def get(self, request, *args, **kwargs):
return HttpResponse('Hello, World!')
The dispatch() part handles this-
dispatch(request, *args, **kwargs)
The view part of the view – the
method that accepts a request argument plus arguments, and returns a
HTTP response.
The default implementation will inspect the HTTP method and attempt to
delegate to a method that matches the HTTP method; a GET will be
delegated to get(), a POST to post(), and so on.
By default, a HEAD request will be delegated to get(). If you need to
handle HEAD requests in a different way than GET, you can override the
head() method. See Supporting other HTTP methods for an example.
The default implementation also sets request, args and kwargs as
instance variables, so any method on the view can know the full
details of the request that was made to invoke the view.
Then you can use it in urls.py -
from django.conf.urls import patterns, url
from myapp.views import MyView
urlpatterns = patterns('',
url(r'^mine/$', MyView.as_view(), name='my-view'),
)
More details.
CherryPy
CherryPy now also supports this. They have a full page on this.
I believe the decision for django was made because usually just GET and POST is enough, and that keeps the framework simpler for its requirements. It is very convenient to just "not care" about which verb was used.
However, there are plenty other frameworks that can do dispatch based on verb. I like werkzeug, it makes easy to define your own dispatch code, so you can dispatch based on whatever you want, to whatever you want.
Because this is not hard to DIY. Just have a dictionary of accepted verbs to functions in each class.
def dispatcher(someObject, request):
try:
return someObject.acceptedVerbs[request.method]()
except:
return http.HttpResponseNotAllowed(someObject.acceptedVerbs.keys())