Redirection + 403 error - django

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

Related

Attach headers to redirect in 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!

Misconfigured custom HTTP error templates in Django project (404,500)

I am in Django HTTP error codes hell. Would be great if an expert can help me out of my misconfiguration.
My Django project runs with nginx as a reverse proxy coupled to a gunicorn application server.
Requirement:
I want a custom Page not found template to render (i.e. 404) when a url pattern is entered that doesn't exist in my urls.py. Sounds simple enough, and is well documented.
I have already gone ahead and implemented this.
The Problem:
Assume example.com is my live project.
1) If I try to access https://example.com/asdfasdf (i.e. unmatched, random gibberish) on my production server, it displays the 500 template instead of 404.
2) Next, if I try to curl the said url pattern via curl -I https://example.com/asdfasdf/, I see 200 OK instead of 404 or 500. Wth?
3) Moreover, if I try the same behavior with Debug = True on localhost, 404 is returned correctly (both template and HTTP error code are in consonance).
These 3 behaviors are quite perplexing.
My configuration:
I created error_views.py and inserted it in the folder where I keep my regular views.py. This error file contains:
from django.shortcuts import render
def server_error(request):
return render(request, '500.html')
def not_found(request):
return render(request, '404.html')
def permission_denied(request):
return render(request, '404.html')
def bad_request(request):
return render(request, '404.html')
In my urls.py (kept in the same folder as settings.py), I added the following after all url patterns:
handler404 = 'my_app.error_views.not_found'
handler500 = 'my_app.error_views.server_error'
handler403 = 'my_app.error_views.permission_denied'
handler400 = 'my_app.error_views.bad_request'
I created 404.html and 500.html, and inserted them in the default /templates/ directory.
In settings.py, I have ALLOWED_HOSTS = ['*']
Lastly, my nginx conf dealing with this is as follows (placed within the server block in the virtual host file):
# Error pages
error_page 500 502 503 504 /500.html;
location = /500.html {
root /home/ubuntu/this_proj/project_dir/templates/;
}
location = /too_bad.svg {
root /home/ubuntu/this_proj/project_dir/static/img/;
}
All of this is fairly regular stuff and I'm missing what I've misconfigured here. Can an expert guide me out of this mess?
Thanks in advance, and please ask for more information in case warranted.
Note: I tried solutions provided in similar questions on SO here and here. Needless to say, those misconfigurations were very different, displaying none of the symptoms I'm seeing.
If you use a custom handler, you have to explicitly set the proper http status for the response object. If you don't set the status, the default is 200 OK.
def not_found(request):
return render(request, '404.html', status=404)

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;
});

Django test always returning 301

I know there are few same posts with this problem, but they doesn't helped for me. I'm always got a 301 status in tests:
self.client.get('/')
and this:
self.client.get('/admin/')
return:
AssertionError: 301 != 200
All urls will returning 301 status... Only way that help is: self.client.get('/', follow=True)
Anybody knows where is problem?
301 is status for redirection, whitch means your get request first have response that is the 301.
Http headers contains the url to redirect to...
If you want your request to follow, you have pass in follow=True, which indicates the method to automatically trigger another request to the redirect url.
There can be many redirections.
It's a common error in assertion tests.
Open your browser to see if the tailing backslash has caused this issue.
I had the same error as you described.
My django code is:
response = self.client.get('**/admin**')<br>
self.assertEqual(response.status_code, 200)<br>
AssertionError: 301 != 200
This is my solution:
Option 1
self.client.get('**/admin/**')<br>
Option 2
self.client.get('**/admin**', follow=True)
Is the root URL protected by login? That's certainly the case for the admin URL, so it will redirect to the login page unless you have already logged in. If you have protected the root view with #login_required that would explain what you see.
This is how I solved it:
def test_index_status_code(self):
response = self.client.get('/backstage')
self.assertRedirects(response, '/backstage/', status_code=301, target_status_code=200)

Django-HttpRedirect fails in Internet explorer

I have a Facebook application using Django. In one of my views I use following piece of code to make user logged-in.
In IE, return HttpResponseRedirect line fails with error message "This content cannot be displayed in a frame...", although other browsers are working fine.
Do you have an idea, why IE fails for HttpResponseRedirect?
(This is problem is produced on IE9 on Windows 7, server is using django-1.3)
def auto_login(request):
username = request.GET['username']
password = request.GET['password']
user = authenticate(username=username, password=password)
if user is not None:
if user.is_active:
login(request, user)
theURL='http://apps.facebook.com/myapp/'
return HttpResponseRedirect(theURL)
else:
return HttpResponse("disabled account")
else:
return HttpResponse("Invalid login")
This can be two things, both related to the browser security model.
Option 1 is the redirect to another domain.
Clients may decide to follow the redirect, or to refuse. In particular a HTTP 307 redirect (which allows forwarding of POST data) is not always accepted by clients.
Option 2 is related to the redirect of a resource with HTTP method POST url to another resource with method GET.
If the HTTP method of the current view and the redirect are different (i.e. HTTP POST against the /login url vs. HTTP GET of the facebook/myapp), at least IE8 will refuse to redirect. I'm not sure of this has been changed in IE9.
There's a few things you could try.
You could try another HTTP response code. Assuming there is no need to forward the HTTP parameters from the original request to the redirected request, a response code 303 would be better than a 307.
If your situation involves a redirect of an HTTP POST resource to the external HTTP GET resource at facebook, another attempt is put an extra redirect in the middle:
POST resource on yoursite.com --> redirect to GET resource on yoursite.com --> external redirect to facebook domain.
The "extra redirect" option could fix one browser but break another (browsers have limits on redirects, which may vary per browser type and version). If you would get into this situation you may need to detect the user-agent and switch between IE and other browsers.
A few good links:
Django/IE8 Admin Interface Weirdness
http://en.wikipedia.org/wiki/List_of_HTTP_status_codes