Attach headers to redirect in Django - django

In my view, I want to redirect to a URL (which points to a hosted image) but also add the User-Agent header to that GET request (to avoid 403 errors). I've explored two options:
The redirect(url) Django function. Is there a way to somehow add on headers?
Using the requests library:
r = requests.get(picture.url, headers={'User-Agent': user_agent,})
But then what should I return from my view? return r, return r.content or return json() didn't work for me.
== EDIT AFTER DUPLICATE QUESTION SOLUTION ==
As suggested, I tried the solution as shown here:
def my_view(request):
response = redirect("www.somewebsite.com/image.png")
response['User-Agent'] = "Mozilla..."
return response
But that didn't help with the 403 error when fetching image.png. I want to make sure that headers are added to the GET request fetching the image, not to the response returned by the view.
Thanks!

Related

Django CSRF Token present but still getting 403 Forbidden Error

I am trying to set up a Django API that receives POST requests with some JSON data and basically sends emails to a list of recipients. The logic is rather simple:
First I have the view for when I create a blog post. In the template, I include the csrf_token as specified on the Django Documentation. When I hit the submit button, behind the scene the create-post view, in addition to creating the post, makes a request (I am using the requests module) to the API which is charged with sending the emails. This is the piece of logic the sends the request to the API:
data = {
"title": new_post.title,
"summary": new_post.summary,
"link": var["BASE_URL"] + f"blog/post/{new_post.slug}"
}
csrf_token = get_token(request)
# print(csrf_token)
headers = {"X-CSRFToken": csrf_token}
requests.post(var["BASE_URL"] + "_api/send-notification/", json=data, headers=headers)
As you can see I am adding the X-CSRFToken to the headers which I generate through the get_token() method, as per the Django docs. However, the response in the API is a 403 Forbidden status CSRF Token not set.
I have checked the headers in the request and the token is indeed present. In addition, I have been providing other routes to the API and I have been using it for AJAX calls which again is very simple just follow the Django docs and they work perfectly well.
The problem seems to arise when I make the call from within the view, AJAX calls are handle by Javascript static files, and as I said they work fine.
I have thought that Django didn't allow the use of 2 CSRF tokens on the same page (one for the submit form and the other in the view by get_token()), but that's not the problem.
This is typically the error I get:
>>> Forbidden (CSRF cookie not set.): /_api/send-notification/
>>> "POST /_api/send-notification/ HTTP/1.1" 403 2864
I have read several similar questions on SO but they mostly involved using the csrf_exempt decorator, which in my opinion is not really a solution. It just gets rid of the CRSF token usefulness altogether.
Does anyone have any idea what might be causing this problem?
Thanks
Error tries to tell you that you need to add token into cookie storage like that:
cookies = {'csrftoken': csrf_token}
requests.post(var["BASE_URL"] + "_api/send-notification/", json=data, headers=headers, cookies=cookies)

Why does this request return an empty string?

I have the following function which makes a get request to a url.
def fetch_data(session = None):
s = session or requests.Session()
url = 'http://www.moldelectrica.md/utils/load4.php'
response = s.get(url)
print response.status_code
data = response.text
return data
I expect to get a string back in the form.
169,26,0,19,36,151,9,647,26,15,0,0,0,0,0,150,7,27,-11,-27,-101,-19,-32,-78,-58,0,962,866,96,0,50.02
But instead I get an empty unicode string. The status code returned is 200.
I've looked at the request headers but nothing in them suggests that any headers will require being set manually. Cookies are used but I think the session object should handle that.
Figured it out. As I said this url provides data for a display so it wouldn't normally be visited directly. Usually it would be requested by the display page and that page would provide a cookie.
So the solution is to make a request to the display url then reuse the session and make another request to the data url.

Angular can't get CSRF cookie from Django

I did a lot of research on this topic, but it's still not working for me.
I set my csrftoken cookie in Django,and it does in the response object.
But in any browser, it says no cookies in this site
Backend:
#ensure_csrf_cookie
def home(request):
csrf_token = get_token(request)
response = HttpResponse()
response = render(request, 'index.html')
response.set_cookie(key='csrftoken', value=csrf_token)
return response
Angular:
myapp.config(function($httpProvider){
//I use this when in angular1.0.x
//$http.defaults.headers.post['X-CSRFToken'] = $cookies['csrftoken'];
//now in angular1.2.x I use code below. but none of them works
$httpProvider.defaults.xsrfCookieName = 'csrftoken';
$httpProvider.defaults.xsrfHeaderName = 'X-CSRFToken';
});
When I do a POST I get message
Failed to load resource: the server responded with a status of 403 (FORBIDDEN)
Also if I print out header info in the $http error function:
console.log(header('Set-Cookie'));
console.log(header('Access-Control-Allow-Headers'));
console.log(header('Access-Control-Allow-Methods'));
all of these three are null.
I can't figure it why! Especially, it works fine in localhost, either Firefox or Chrome, but in an Apache server, always no cookie in this site.
Is there any setting should I do? Can anyone help my with this issue?
I'm not sure this will help, but your view is terribly written. You're trying to force the csrf in about five different ways, and you also have some redundant lines that don't do anything (you do response = HttpResponse() and then override it completely, making that line completely void). so there's a good chance one of them is screwing things over.
The point is - when you use render you don't need to do anything else to enforce the csrf (you know, except for making sure it's enabled). That's the point of using it over render_to_response. Try this much simpler version and see how much it helps:
def home(request):
return render(request, 'index.html')
Please check the domain of the cookie set by Django.
Be aware of cross-domain requests.
$http docs : Angular provides a mechanism to counter XSRF, When performing XHR requests but will not be set for cross-domain requests.
Here is a small lib that might help you https://github.com/pasupulaphani/angular-csrf-cross-domain/blob/master/dist/angular-csrf-cross-domain.js
Try including the ngCookies module in your application.
myApp.run(function ($http, $cookies) {
$http.defaults.headers.common['X-CSRFToken'] = $cookies.csrftoken;
});

Setting HTTP_REFERER header in Django test

I'm working on a Django web application which (amongst other things) needs to handle transaction status info sent using a POST request.
In addition to the HTTP security supported by the payment gateway, my view checks request.META['HTTP_REFERER'] against an entry in settings.py to try to prevent funny business:
if request.META.get('HTTP_REFERER', '') != settings.PAYMENT_URL and not settings.DEBUG:
return HttpResponseForbidden('Incorrect source URL for updating payment status')
Now I'd like to work out how to test this behaviour.
I can generate a failure easily enough; HTTP_REFERER is (predictably) None with a normal page load:
def test_transaction_status_succeeds(self):
response = self.client.post(reverse('transaction_status'), { ... })
self.assertEqual(response.status_code, 403)
How, though, can I fake a successful submission? I've tried setting HTTP_REFERER in extra, e.g. self.client.post(..., extra={'HTTP_REFERER': 'http://foo/bar'}), but this isn't working; the view is apparently still seeing a blank header.
Does the test client even support custom headers? Is there a work-around if not? I'm using Django 1.1, and would prefer not to upgrade just yet if at all possible.
Almost right. It's actually:
def transaction_status_suceeds(self):
response = self.client.post(reverse('transaction_status'), {}, HTTP_REFERER='http://foo/bar')
I'd missed a ** (scatter operator / keyword argument unpacking operator / whatever) when reading the source of test/client.py; extra ends up being a dictionary of extra keyword arguments to the function itself.
You can pass HTTP headers to the constructor of Client:
from django.test import Client
from django.urls import reverse
client = Client(
HTTP_USER_AGENT='Mozilla/5.0',
HTTP_REFERER='http://www.google.com',
)
response1 = client.get(reverse('foo'))
response2 = client.get(reverse('bar'))
This way you don't need to pass headers every time you make a request.

Redirection + 403 error

I am searching for a way to have something like that :
return HttpResponseForbiddenRedirect(reverse("view_name"))
an HttpResponse which redirect to a view (with its name) but still throw a 403 error
I tried to do something like that :
class HttpResponseForbiddenRedirect(HttpResponse):
def __init__(self, redirect_to):
super(HttpResponseForbiddenRedirect, self).__init__()
self['Location'] = iri_to_uri(redirect_to)
self.status_code = 403
But it didn't work. For some reason I don't understand, I don't get any content
It doesn't work because you can't have a 403 response that is also acted upon as if it is a 302 response.
The HTTP spec tells browsers how to handle certain status codes, and so a browser getting a 403 won't bother to look to see if there's a Location header in the way that it would do with a 302.
If you want to redirect someone from X to Y because they're not allowed to see X, then just issue a standard 302, and do something like setting a message (using django.contrib.messages) to inform the user of why they've been redirected, or redirect them to a page that explains what is going on.
You're missing the idea behind HTTP status codes. Redirect is being made with HTTP code 301/302. So you cannot make redirect and return 403 at the same time. It is simply not a redirect, if there is no 301/302 code returned.
I don't get it why you need this, but you can always make a view like:
def my403view(request): # e.g. /403.html
return HttpResponseForbidden()
and to do your redirect with:
return HttpResponseRedirect(reverse("403.html"))
This will redirect(with code 302) to "my403view", and it will return 403.
I found a solution to this :
from app_name.views import my_view
....
retour = my_views(request)
return HttpResponseForbidden(retour)
It is in fact quite simple
And so I get the 403 error + the page I wanna load