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'}
Related
I am relatively new to RESTful API, so it is certainly possible I am not designing this correctly.
I want to return a different subsets of a JSON user object from /api/users/[user_id] based on who is authenticating. So if the user "alice" is trying to access /api/users/alice, she would get much more of her info (such as private settings, etc) than user "bob" who would simply get her public profile.
I am currently using flask_restful with httpbasicauth. Right now I have the following:
class UserAPI(flask_restful.Resource):
#g.auth.login_required
def get(self, username):
# here is where I want to get the HTTPBasicAuth username
# to determine how much data to return
user = User.objects(username=username).exclude('password').first()
if user is not None:
return user.to_json()
else:
flask_restful.abort(404, message='User not found: ' + username)
The issue is that I cannot seem to figure out a CLEAN way to get the HTTP basic auth data. I know I could parse the request and decode the base-64 data, but I feel I shouldn't have to do that. Or, even better, find a way to pass the user_id from /api/users/[user_id] into the login_required annotation.
I feel this is would be a very common use case so I can't figure out why I can't find anything in this area. Am I designing this completely wrong?
Thanks very much!
I suggest not using flask.ext.httpauth. I didn't find it very useful. I use a decorator that takes the Authorization header and checks it with the db. You can access the username entered in request.authorization.username and the password is in request.authorization.password.
from flask import request
from flask.ext.restful import abort, Resource
from functools import wraps
def requires_auth(f):
#wraps(f)
def decorated(*args, **kwargs):
auth = request.authorization
if not auth:
abort(401)
user = User.objects(username=auth.username).first()
auth_ok = False
if user != None:
auth_ok = verify_password(auth.password) == user.password
if not auth_ok:
return abort(401)
return f(*args, **kwargs)
return decorated
class UserAPI(Resource):
#requires_auth
def get(self):
user = User.objects(username=request.authorization.username).\
exclude('password').first()
if user is not None:
return user.to_json()
else:
abort(404, message='User not found: ' + username)
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.
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 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.
Say you want to redirect a user if they are not logged in. Currently I have this:
def my_account():
if "user" not in session or not session["user"]:
return redirect('/')
...
def my_favourites():
if "user" not in session or not session["user"]:
return redirect('/')
...
But now I would like to reuse the code that checks if a user is logged in. But this requires the session object.
I'm not sure if I could or should pass the request or session object explicitly in a helper function. Or if there is yet another more preferred way?
If you want to write re-usable code to verify if a user is logged in, one recommended way is to use decorators on the view functions. For example,
#login_required
def do_something():
* pass
Then, you can define this decorator below. You can then use your request and session object. Also, this will redirect to the login url if user is not logged in already and then forward to the next page once logged in.
def login_required(f):
* #wraps(f)
* def decorated_function(*args, **kwargs):
* if "user" not in session or not session["user"]:
* return redirect(url_for('login', next=request.url))
* return f(*args, **kwargs)
* return decorated_function
Have you tried http://packages.python.org/Flask-Login/ ? I believe this is a package that will provide what you are looking for easier.