Check if user is authenticated with django TokenAuthentication - python

I'm trying to develop a REST API with DRF that uses TokenAuthentication. This will be used in an android app.
I was able to authenticate a user and retrieve it's token. The problem I'm having now is with the following view:
#csrf_exempt
def foo(request):
if request.method == 'GET':
if request.user.is_authenticated():
...
do stuff
...
return HttpResponse(data, "application/json")
else:
return HttpResponse(status=401)
Basically the user should be authenticated in order to receive the data, otherwise, he will receive a 401 response.
I'm making a GET request to the proper URL with the following parameters in the Header:
content-type : application/json
authorization : Token <user token>
Which is basically what I'm doing for other Viewsets (this is not a Viewset) I have - and it works.
In this case, it's always sending the HTTP response with 401 code (user isn't authenticated).
I can't figure out if the problem is with the Header values I'm passing or if this is not the proper way to check if the user is authenticated.
Edit: if I do: "print request.user" i get AnonymousUser
Thanks!
Solved
As suggested by "ABDUL NIYAS P M" I used the APIView
Basically, I just added the #api_view(['GET']) decorator to the View.
#csrf_exempt
#api_view(['GET'])
#permission_classes((IsAuthenticated, ))
def foo(request):
if request.method == 'GET':
...

An easier way to do this is by checking if the user session is existing or not.
When DRF creates a token, it also creates the session cookie.
return HttpResponse(json.dumps({"is_authenticated": True if request.session.get('_auth_user_id', 0) else False}),
content_type='application/json')

Related

How to store data into a model associated with the current user?

In my frontend i'm logging into another app's api in the browser, I'm then redirected back to my app, that hits a View in my backend which gets a code from the other app's api, sends code back in a post request then receives an access token and stores it in a model associated with the current user.
My problem is that after the user gives permission to other app in the browser it redirects back to my backend view without the users token in the header so if i have permissions_classes set it wont allow user to access that view... but if i take the permissions_classes off, the view won't know who the current user is.
View #1 that prepares the other app's API url:
class getAPIAuthURL(APIView):
authentication_class = [authentication.TokenAuthentication]
permission_class = [permissions.IsAuthenticated]
def get(self, request):
scopes = 'scopes'
url = Request('GET', 'https://accounts.api.com/authorize',
params={
'scope': scopes,
'response_type': 'code',
'redirect_uri': REDIRECT_URL,
'client_id': CLIENT_ID
}
).prepare().url
return Response(url, status=status.HTTP_200_OK)
View #2 that gets data and stores it in model (this is the REDIRECT_URL from previous view):
class APICallback(APIView):
authentication_class = [authentication.TokenAuthentication]
permission_class = [permissions.IsAuthenticated]
def api_callback(request, format=None):
code = request.GET.get('code')
if not code:
return Response({'Error': 'Code not found in request'}, status=status.HTTP_400_BAD_REQUEST)
response = post('https://accounts.api.com/api/token', data={
'code': code,
}).json()
print(response)
user = request.user
access_token = response.get('access_token')
token = APITokenModel(user=user, access_token=access_token)
token.save()
return redirect('frontend')
I have other Views that make requests and it has been able to get the token to know who the user is, but when this View is called I get a 401 Unauthorized error.
How do I let Django know the token I'm receiving from the other app's api belongs to the current user?
also... when I take off permissions and authentication class from the View it returns the user as Anonymous User
First, what authentication class are you using? You should know that your TokenAuthentication class uses the Authorization header in your request to authenticate you. If that's not been passed then you should fix that.
It would be worth knowing that you don't send auth tokens as GET and should not be sent as those. Unless of course you want to write an Authentication class of your own.
EDIT
In lieu of our discuss in the comments, try this redirect...
# import the class
from django.http import HttpResponseRedirect
# now redirect
return HttpResponseRedirect(redirect_to="url", headers=dict)

Django: decorator causes 'function' object has no attribute 'get' when wrapping view in urls

I am using Django defender, watch_login() decorator to add brute-force prevention to my custom login view:
def user_login(request):
if request.user.is_authenticated:
if request.user.is_active:
return redirect('home:home')
if request.method == 'POST':
username = request.POST.get('username')
password = request.POST.get('password')
user = authenticate(username=username, password=password)
if user is not None:
if user.is_active:
login(request, user)
return redirect('home:home')
else:
return HttpResponse('Account is disabled')
else:
messages.error(request,'Sorry, the username or password you entered is not correct')
return redirect('main:user_login')
return render(request, 'main/user_login.html')
However, when I wrap the function with the decorator in my urls.py file I get the error:
'function' object has no attribute 'get'.
I have tried using the decorator in views.py with no arguments by simply doing #watch_login(). However, that does not seem to be working. How can I add this wrapper to my function in the urls.py file?
I have also tried this:Wrapping/decorating a function in urls.py vs in views.py
And I still get the same error ('function' object has no attribute 'get')
EDIT
I have also tried HttpResponseRedirect using the following method yet the user does not get blocked after reaching maximum login limits:
When user submit wrong credentials I do:
url = reverse('main:user_login')
return HttpResponseRedirect(url, status=302)
And as my decorator I have:
#watch_login(status_code=302)
Which still seems not to be working. And when I do cleanup_django_defender I can see there are no Attempts stored in my database.
EDIT
I had to render the template again on incorrect login rather than redirecting since it will be redirected with a new HttpStatus and watch_login() will never return true.
So now on a failed login attempt I simply have:
return render(request, 'main/user_login.html')
However, the issue with this approach is that the POST request parameters will not reset and as per Django documentation it is best practice to redirect after for submission
From #watch_login() docstring:
To make it work on normal functions just pass the status code that
should indicate a failure and/or a string that will be checked within
the response body.
Which means your user_login() function should return a specific status code (and/or a message) on error.
Example from tests 1:
#watch_login(status_code=401)
def fake_api_401_login_view_without_msg(request):
""" Fake the api login with 401 """
return HttpResponse(status=401)
Example from tests 2:
#watch_login(status_code=401, msg="Invalid credentials")
def fake_api_401_login_view_without_msg(request):
""" Fake the api login with 401 """
return HttpResponse("Sorry, Invalid credentials", status=401)
EDIT
If you redirect on failed login this condition from #watch_login() will never evaluate to true (because response.status_code == 302).
if status_code == 302: # standard Django login view
login_unsuccessful = (
response
and not response.has_header("location")
and response.status_code != status_code
)

Flask post delete, URL restrictions

Sample code:
#app.route('/delete-update/<int:post_id>')
#login_required
def delete_update(post_id):
try:
p = Post.query.filter(Post.id == post_id).delete()
db.session.commit()
flash('Post successfully deleted')
except:
db.session.rollback()
return redirect(url_for('index'))
I can delete a post using this route by a button click, But how to prevent deleting a post by requesting the URL
localhost:5000/delete-update/1
You should use the methods attribute after your route definition.
#app.route('/delete-update', methods=['POST'])
if request.method == 'POST':
#do your stuff
You can also use any default http method like PUT, DELETE, PATCH.
Be aware that with that change, if you do a request with method GET to:
http://localhost:5000/delete-update/1
you will receive a 405response with message method not allowed.
That's because our route does not handle GET anymore
Also doing that you should change the button handle to send a request to that route with method POST.

Differing Reponse Codes from Django Application

For both GET and POST requests I simply want to print the associated QueryDict in my test view:
if request.method == 'GET':
print request.GET
if request.method == 'POST':
print request.POST
When I make requests using both methods I get different response codes. I could use some help.
The post request has triggered Django's CSRF protection so you are getting a 403 Forbidden response.
Usually, it means that you need to include the CSRF template in your template.
In this case, because you are making requests from the shell, it might be appropriate to use the csrf_exempt decorator on this view to disable the CSRF protection.

Django POST request to my view from Pyres worker - CSRF token

I'm using Pyres workers to do some processing of data users enter in a form. Their processing is done by a view on my form, which I make a POST request to, with data including the data to process and a CSRF middleware token for the user. My issue is that this is apparently not enough, as Django still rejects my request with a 403 forbidden.
Relevant code:
Form handler:
def handler(request):
if(request.method == "POST"):
if(request.POST.__contains__("taskdata")):
#valid post of the form
taskdata = escape(request.POST.get("taskdata",""))
t = TaskData(data=taskdata, time_added=timezone.now(), token=request.POST.get("csrfmiddlewaretoken",""))
t.save()
r = ResQ(server="127.0.0.1:6379")
r.enqueue(TaskData, t.id)
return HttpResponse(t.id)
else:
#invalid post of the form
raise Http404
else:
raise Http404
Pyres worker job:
#staticmethod
def perform(taskData_id):
#Get the taskData from this id, test it for tasky stuff
task_data = TaskData.objects.get(pk=taskData_id)
post_data = [('id',task_data.id),('data',task_data.data), ('csrfmiddlewaretoken',task_data.token)] # a sequence of two element tuples
result = urllib2.urlopen('http://127.0.0.1:8000/tasks/nlp/process/', urllib.urlencode(post_data))
content = result.read()
return
View being posted to by that job:
def process(request):
if(request.method == "POST"):
return HttpResponse("HEY, it works!")
if(request.POST.__contains__("data") and request.POST.__contains__("id")):
#valid post to the form by the model
#taskdata = escape(request.POST.get("taskdata",""))
#data = get_times(taskdata)
return HttpResponse("Hey from process!")
#return HttpResponse(json.dumps(data))
else:
#invalid post of the form
raise Http404
else:
raise Http404
What I'm basically trying to do is save some raw data at form submission, along with the CSRF token for it. The workers then send that data + token to a processing view.
Unfortunately, posting the token doesn't seem to be enough.
Does anybody know what the csrf protection actually looks for, and how I can make my Pyres workers compliant?
(Suggested tag: pyres)
I think I see the problem.
The way Django's CSRF protection works is by generating a nonce, then setting a cookie to the value of the nonce, and ensuring the csrfmiddlewaretoken POST value matches the value of the cookie. The rationale is that it makes it a stateless system, which works without any persistent session data.
The problem is that the request you make in the Pyres worker job...
result = urllib2.urlopen('http://127.0.0.1:8000/tasks/nlp/process/',
urllib.urlencode(post_data))
...is coming from the server, not the client, so it won't have the cookie set.
Assuming the /tasks/nlp/process/ URL is protected such that it can only be accessed by the server, then it's probably simplest to make the process() view exempt from CSRF checking with...
#csrf_exempt
def process(request):
...
...otherwise you'll have to manually grab the cookie value in the handler() view, and pass it on to the Pyres worker job.
Update
To ensure the process() method can only be called by the server, one simple way would be to check the request object with something like...
#csrf_exempt
def process(request):
if request.META['REMOTE_ADDR'] != '127.0.0.1':
# Return some error response here.
# 403 is traditional for access denied, but I prefer sending 404
# so 'hackers' can't infer the existence of any 'hidden' URLs
# from the response code
raise Http404
# Now do the thing
....
...although there may be some built-in decorator or somesuch to do this for you.

Categories