Ah, but was your handler404 view csrf_exempt (the default django one
django.views.defaults.page_not_found isn't...) ;-)
http://code.djangoproject.com/browser/django/trunk/django/views/defaults.py#L4
(really, you'd probably better off _returning_ a
django.http.HttpResponseNotFound response than raising a Http404
exception from your exempted view in this case I guess).
IMO there's no reason to leak info even to the extent of "this object
doesn't exist" to scurrilous CSRFers by default.
> Is there a better way to provide debugging information for 403 errors
> raised from the built-in CSRF methods?
>
Note also that django 1.2.1 allows specification of
settings.CSRF_FAILURE_VIEW.
http://docs.djangoproject.com/en/dev/ref/settings/?from=olddocs#csrf-failure-view
You could even make that view immediately throw an exception to be
caught by something else ...I think.
There are quirks, or at least things to be aware of, with the order
things happen in django.core.handlers.base.BaseHandler.get_response()
[1]. Note an exception middleware /doesn't catch/ all exceptions,
some "escape" if they happen early, so e.g. it's best to still specify a
custom handler404/handler500 when using an exception middleware, because
they might still get called.
Even then the only way I've found to completely customize the behaviour
when settings.DEBUG is true (because I wanted ajax technical_response
debug pages to be specially formatted compared to standard whole-page
technical_response) is to subclass the Handler (WSGIHandler) to meddle
with get_response.
I hesitate to call the situation a "bug", because it's all stuff you
probably shouldn't be messing with much, making it too easy
to render your server insecure is probably a django non-goal...
[1]
http://code.djangoproject.com/browser/django/trunk/django/core/handlers/base.py#L66
<snip>
I'm not entirely sure but i think i've hit the same problem.
I tried to access a view with urllib & urllib that requires a logged
in user with the appropriate rights.
First i need to login in and post the username and password in order to use
a second call to access the protected function.
This is the test code i used to automatically login:
import urllib2,urllib
o = urllib2.build_opener( urllib2.HTTPCookieProcessor() )
urllib2.install_opener(o)
id='Admin'
pw='xyz'
p=urllib.urlencode({"username" : id, "password" : pw})
f=o.open("http://calltracking:8000/accounts/login/", p)
data=f.read()
f.close()
The login view has the decorator @csrf_exempt and login works fine.
Next, i try the actual view that also has @csrf_exempt specified.
I get this back:
>>> f=o.open("http://calltracking:8000/management/statistics/top/user/yearly/", p)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "c:\python26\lib\urllib2.py", line 397, in open
response = meth(req, response)
File "c:\python26\lib\urllib2.py", line 510, in http_response
'http', request, response, code, msg, hdrs)
File "c:\python26\lib\urllib2.py", line 435, in error
return self._call_chain(*args)
File "c:\python26\lib\urllib2.py", line 369, in _call_chain
result = func(*args)
File "c:\python26\lib\urllib2.py", line 518, in http_error_default
raise HTTPError(req.get_full_url(), code, msg, hdrs, fp)
urllib2.HTTPError: HTTP Error 403: FORBIDDEN
I'm not sure how to proceed as it seems that csrf is getting in the way.
Regards,
Benedict
Yes,
here it is.
Depending on wether the user logged in is staff (ICT) or not, i use
a different base toe extend my template from (different menu).
@csrf_exempt
def stats_top_callers_per_year(request):
if ( request.user.is_staff ):
extends_from = "management/base.html"
else: extends_from = "management/public_base.html"
stat_type = "top_callers_per_year"
stats_all = {}
message=_(u"Top 30 callers per year")
class Stats(object):
pass
current_year = datetime.datetime.now().year
archive_year = current_year - 4
years = xrange(archive_year, current_year+1)
for year in years:
stats_year = []
counts = {}
# Initialize count dict
# If we don't do this, we get a KeyError
for user in User.objects.all():
counts[user]=0
# Count the times an initiator has been calling
for i in Call.objects.filter(date_created__year=year):
for _init in i.initiator.all():
counts[_init] += 1
# Sort the dictionary
count_sorted = sorted(counts.items(), lambda x, y: cmp(x[1], y[1]), reverse=True)
# Make the stats ready for the template
for user_stat in count_sorted[0:30]:
if ( user_stat[1] > 0 ):
st = Stats()
st.calls = user_stat[1]
st.user = user_stat[0]
stats_year.append(st)
stats_all[year]=stats_year
stats_sorted = sorted(stats_all.items(), lambda x, y: cmp(x[0], y[0]), reverse=True)
return render_to_response('management/stats.html', {'extends_from': extends_from, 'stats_year': stats_sorted, 'message': message,
'stat': stat_type, 'tab': "top_callers_per_year"}, context_instance=RequestContext(request))
My goal is to be able to automatically login (seems to work) and then call this function so
the page is generated before any user hits the page.
Regards,
Benedict