Maybe a stupid question here:
Is Requests(A python HTTP lib) support Django 1.4 ?
I use Requests follow the Official Quick Start like below:
requests.get('http://127.0.0.1:8000/getAllTracks', auth=('myUser', 'myPass'))
but i never get authentication right.(Of course i've checked the url, username, password again and again.)
The above url 'http://127.0.0.1:8000/getAllTracks' matches an url pattern of the url.py of a Django project, and that url pattern's callback is the 'getAllTracks' view of a Django app.
If i comment out the authentication code of the 'getAllTracks' view, then the above code works OK, but if i add those authentication code back for the view, then the above code never get authenticated right.
The authentication code of the view is actually very simple, exactly like below (The second line):
def getAllTracks(request):
if request.user.is_authenticated():
tracks = Tracks.objects.all()
if tracks:
# Do sth. here
Which means if i delete the above second line(with some indents adjustments of course), then the requests.get() operation do the right thing for me, but if not(keep the second line), then it never get it right.
Any help would be appreciated.
In Django authentication works in following way:
There is a SessionMiddleware and AuthenticationMiddleware. The process_request() of both these classes is called before any view is called.
SessionMiddleware uses cookies at a lower level. It checks for a cookie named sessionid and try to associate this cookie with a user.
AuthenticationMiddleware checks if this cookie is associated with an user then sets request.user as that corresponding user. If the cookie sessionid is not found or can't be associated with any user, then request.user is set to an instance of AnonymousUser().
Since Http is a stateless protocol, django maintains session for a particular user using these two middlewares and using cookies at a lower level.
Coming to the code, so that requests can work with django.
You must first call the view where you authenticate and login the user. The response from this view will contain sessionid in cookies.
You should use this cookie and send it in the next request so that django can authenticate this particular user and so that your request.user.is_authenticated() passes.
from django.contrib.auth import authenticate, login
def login_user(request):
user = authenticate(username=request.POST.get('username'), password=request.POST.get('password'))
if user:
login(request, user)
return HttpResponse("Logged In")
return HttpResponse("Not Logged In")
def getAllTracks(request):
if request.user.is_authenticated():
return HttpResponse("Authenticated user")
return HttpResponse("Non Authenticated user")
Making the requests:
import requests
resp = requests.post('http://127.0.0.1:8000/login/', {'username': 'akshar', 'password': 'abc'})
print resp.status_code
200 #output
print resp.content
'Logged In' #output
cookies = dict(sessionid=resp.cookies.get('sessionid'))
print cookies
{'sessionid': '1fe38ea7b22b4d4f8d1b391e1ea816c0'} #output
response_two = requests.get('http://127.0.0.1:8000/getAllTracks/', cookies=cookies)
Notice that we pass cookies using cookies keyword argument
print response_two.status_code
200 #output
print response_two.content
'Authenticated user' #output
So, our request.user.is_authenticated() worked properly.
response_three = requests.get('http://127.0.0.1:8000/hogwarts/getAllTracks/')
Notice we do not pass the cookies here.
print response_three.content
'Non Authenticated user' #output
I guess, auth keyword for Requests enables HTTP Basic authentication which is not what is used in Django. You should make a POST request to login url of your project with username and password provided in POST data, after that your Requests instance will receive a session cookie with saved authentication data and will be able to do successful requests to auth-protected views.
Might be easier for you to just set a cookie on initial authentication, pass that back to the client, and then for future requests expect the client to send back that token in the headers, like so:
r = requests.post('http://127.0.0.1:8000', auth=(UN, PW))
self.token = r.cookies['token']
self.headers = {'token': token}
and then in further calls you could, assuming you're in the same class, just do:
r = requests.post('http://127.0.0.1:8000/getAllTracks', headers=self.headers)
Related
I've got a tiny function that just looks to get a response from my DRF API Endpoint.
My DRF settings look like this:
"DEFAULT_AUTHENTICATION_CLASSES": [
# Enabling this it will require Django Session (Including CSRF)
"rest_framework.authentication.SessionAuthentication"
],
"DEFAULT_PERMISSION_CLASSES": [
# Globally only allow IsAuthenticated users access to API Endpoints
"rest_framework.permissions.IsAuthenticated"
],
I'm using this to try and hit the endpoint:
def get_car_details(car_id):
headers = {"X-Requested-With": "XMLHttpRequest"}
api_app = "http://localhost:8000/"
api_model = "cars/"
response = requests.get(api_app + api_model + str(car_id), headers=headers)
json_response = response.json()
return json_response
I keep getting 'detail': 'Authentication credentials were not provided'
Do I need to generate a CSRF token and include it in a GET request? The only time this gets hit is when a user goes to a view that requires they are logged in. Is there a way to pass that logged-in user to the endpoint??
When you make your request from the get_car_details function you need to be sure that the request is authenticated. This does not look like a CSRF issue.
When using session based authentication a session cookie is passed back after logging in. So before you call get_car_details you would need to first make a request to login, keep that session, and use that session when calling the API.
Requests has a session class requests.Session() that you can use for this purpose. Details here: https://docs.python-requests.org/en/latest/user/advanced/
Many people find token based authentication easier partly a session cookie does not need to be maintained.
I have two separate Flask applications, one is an API with the domain "admin.company.com" and the second one is a dashboard under the domain "dashboard.company.com".
My main goal is to secure the api and enable authentication.
I set up authentication on the api and when I'm testing it via the swagger-ui it works good with no issues. I manage to authenticate myself and submit requests. On top of that, in the token_required() below I coded a section that expects to receive JWT and decode it:
def token_required(f):
#wraps(f)
def decorator(*args, **kwargs):
token = None
if 'jwt-token' in request.headers:
token = request.headers['jwt-token']
if not token:
return jsonify({'message': 'a valid token is missing'})
try:
current_user = False
# for authentication via the swagger-ui
if token == 'my_awesome_password':
current_user = 'admin'
else:
data = jwt.decode(token, app.secret_key)
current_user = 'admin' if data['public_id'] == 'admin' else False
if not current_user:
return jsonify({'message': 'token is invalid'})
except:
return jsonify({'message': 'token is invalid'})
return f(*args, **kwargs)
return decorator
The problem is with the dashboard application:
On the dashboard app, I configured the /login route to generate JWT (which needs to be sent to the api app in every HTTP request I made), then do a set_cookie() with httpOnly=True flag (for security purposes), but then how can the JavaScript access it when it has to make XHR requests? Having the httpOnly flag on makes it unreachable.
If using httpOnly=True is not the ideal way in this case, how can I tackle it and make sure the JWT will always be sent to the api app in the request headers?
I'm using PyJWT package.
Thanks for any help.
If you use JWTs for XHR requests, then you don't necessarily need to use cookies – the browser/consumer could store the token and attach it to every request. If you do want to use cookies (this is also a viable option) then you don't need to worry about attaching the JWT as the browser will do that automatically for the domains you specified in the set_cookie command.
I have a Django app that uses the builtin login system.
I'm trying to write a unit test that verifies that the login url redirects to home if the user is already authenticated. Meaning, if an already authenticated user tries to visit the login url, they get redirected to home.
This is a built-in feature of Django's LoginView, and I have verified that it is indeed working. However, I'm not sure how to write a unit test for this.
Question: How do I verify that this redirect occurs via unit tests? LoginView returns a TemplateResponse, which doesn't have a url parameter.
What I have so far is this:
from django.test import TestCase
from django.urls import reverse
home = reverse('home')
login = reverse('login')
logout = reverse('logout')
signup = reverse('signup')
USER_CREDS = { some users credentials }
class TestLoginView(TestCase):
def test_login_redirect(self):
self.client.post(signup, USER_CREDS)
self.client.get(logout)
self.client.post(login, USER_CREDS)
response = self.client.get(login)
self.assertRedirects(response, home)
EDIT:
SimpleTestCase.assertRedirects does not work for TemplateResponse objects.
TemplateResponse objects do not contain a url parameter, which is required for SimpleTestCase.assertRedirect.
The method raises the following error: AttributeError: 'TemplateResponse' object has no attribute 'url'
This is not the same as the linked question, because the response object from a LoginView is a TemplateResponse, not an HttpResponse.
You can check that the login process redirects to the desired named URL with assertRedirects:
SimpleTestCase.assertRedirects(response, expected_url, status_code=302, target_status_code=200, msg_prefix='', fetch_redirect_response=True)
Asserts that the response returned a status_code redirect status,
redirected to expected_url (including any GET data), and that the
final page was received with target_status_code.
Then in your test:
self.assertRedirects(response, home)
Edit
I've just had a similar problem, few notes:
Instead of performing the login process, you can use TestCase.force_login that skips the authentication and verification steps.
class TestLoginView(TestCase):
def test_login_redirect(self):
self.client.force_login(user)
# logged user retrieves the login page
response = self.client.get(login)
self.assertRedirects(response, home)
Response should be a 302 redirect to home: <HttpResponseRedirect status_code=302, "text/html; charset=utf-8", url="/">
If you are not getting a redirect when doing response = self.client.get(login) that means that it is loading the login page, which is what you don't want, so you have to fix that error.
So the problem is not that TemplateResponse objects do not contain a url parameter, it is that your login page isn't redirecting a logged in user to the home page.
I am writing some tests for a form on a Django site. I want to test that a logged in user is able to use the form assuming correct input. To that end, I am using a Django request factory. I can't seem to get my test user logged in though. That, or I'm not even making the request correctly. I'm not sure. Relevant code below:
def create_user():
username = 'User '+id_generator()
return User.objects.create(username = username, password = 'password')
def test_can_register_deck(self):
t = create_empty_tourney()
u = create_user()
d = create_deck(u)
rf = RequestFactory()
request = rf.post(reverse('tourney:tourney', args=(t.slug,)),{'deck' : d})
request.user = u
response = self.client.get(reverse('tourney:tourney', args=(t.slug,)), request)
self.assertEqual(response.status_code, 200)
The request has to think the user is logged in, or the rest of the test won't work. This throws no errors, but I'm not even sure my response is utilizing the request correctly. Am I correctly telling the test to "make a post request to the tourney page as this user, and try to submit a deck"?
I think that the RequestFactory and the client are two different ways of testing django views.
The request that is returned from rf.post is meant to be passed directly to a view function. If you were to do that, i think you will find that, the user will be detected as logged in.
I looked at the source, and the documentation, and I don't see where the test client accepts a request. You can log in a user with the test client using:
self.client.login(
username=u.username,
password='password')
from bottle import route, run, debug, error, request, template
#route('/home')
#route('/home/')
def login():
return template('templates/login')
#route('/home', method='POST')
#route('/home/', method='POST')
def welocme():
data = request.POST
if data:
password = data.get('password')
check_pass = 'password'
if password == check_pass:
return template('templates/welcome')
else:
return template('templates/login')
else:
return template('templates/login')
My requirement is:
I will get a login and welcome page on same url. Login page has only one password field.
My problem:
If I get login and go to welcome page again on refreshing, it send me to login page. But ideally it should be on the welcome page only.
#error(404)
def error404(error):
return 'http://google.com'
My second problem: I want to redirect on a particular url on 404.
If the user goes to the "/home" page, don't you want to check to see if they're logged in before showing them the login screen? It appears like you assume they're not already logged in if the HTTP method is not POST.
I don't know much about your framework, but I assume if the login is successful you should set a cookie, and then you should check the cookie for HTTP GETs to see if the user is authenticated.
Your second question is answered here.
You might also want to take a look at Beaker's cookie sessions and use that to keep your application's state between requests.
If I understand the question correctly, the only way to get to render the welcome template is via POST.
You could change this so that GET requests check whether someone is logged in. If that fails, then redirect them to the login page.