Finally figured out what's going on.

My hypothesis:
An HTTP request with POST method sent to a Django view wrapped with
@csrf_exempt will still raise a 403 CSRF error if the wrapped view
raises an Http404 exception.

In this specific case, we raise 404 when there is no valid video_id
passed in the POST request:
264                 # Make sure there's at least one
265                 if len(distro_list) < 1:
266                         raise Http404

Raising a 403 was obviously not the result behavior we expected,
instead we would have expected the 404 to be raised without CSRF
interfering given the initial view was properly wrapped with
@csrf_exempt. Further contributing, this was rather difficult to
diagnose due to the opaque 403 error message even when DEBUG = True.

Is this expected Django behavior? If not, can we please address this
in a future release?

Is there a better way to provide debugging information for 403 errors
raised from the built-in CSRF methods?

All the best and thanks for a great framework.

Kieran

On Jul 27, 6:54 pm, Kieran Farr <kieran.f...@gmail.com> wrote:
> Further research shows that CSRF is enabled regardless of my
> settings.py if we use Django's built-in auth.
>
> Obviously, we need to still use Django's auth, so we can't just
> disable CSRF site-wide like this 
> hack:http://stackoverflow.com/questions/1650941/django-csrf-framework-cann...
> ...or this hack...http://djangosnippets.org/snippets/2069/
>
> Obviously also, we have no control over the third-party server sending
> the POST request, so we can't resort to this 
> hack:http://stackoverflow.com/questions/2405353/having-a-postable-api-and-...
>
> The decorator @csrf_exempt does not work as described in the docs as
> our view always returns a 403 when any content is POSTed.
>
> Very confusing!
>
> Kieran
>
> On Jul 27, 1:32 pm, Kieran Farr <kieran.f...@gmail.com> wrote:
>
>
>
> > Raj sorry I misread your question. This initial response is in re: my
> > listener.
>
> > The "sender" of this POST request is a third party server that we have
> > no control over.
>
> > However, even when debugging with wget we receive the 403.
>
> > Here is the wget command I've been testing with:
>
> > $ wgethttp://www.url.com/subdir/listener/--post-data"?test=test"; -U
> > "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_4; en-us) AppleWebKit/
> > 533.16 (KHTML, like Gecko) Version/5.0 Safari/533.16"
>
> > --2010-07-27 20:06:36--  http://www.url.com/subdir/listener/
> > Resolving [URL] ... [IP ADDRESS]
> > Connecting to [URL]|[IP ADDRESS]|:80... connected.
> > HTTP request sent, awaiting response... 403 Forbidden
> > 2010-07-27 20:06:37 ERROR 403: Forbidden.
>
> > However, if I send without the post-data, here's the response:
>
> > $ wgethttp://www.url.com/subdir/listener/
>
> > --2010-07-27 20:31:14--  http://www.url.com/subdir/listener/
> > Resolving [URL]... [IP ADDRESS]
> > Connecting to [URL|[IP ADDRESS]|:80... connected.
> > HTTP request sent, awaiting response... 200 OK
> > Length: unspecified [text/plain]
> > Saving to: `index.html.1'
>
> > [ <=>                                                                       
> >                                                             ]
> > 27          --.-K/s   in 0s
>
> > 2010-07-27 20:31:14 (1.84 MB/s) - `index.html.1' saved [27]
>
> > On Jul 27, 11:34 am, Kieran Farr <kieran.f...@gmail.com> wrote:
>
> > > This is intended not to be protected by auth, so this page is publicly
> > > accessible, no login required.
>
> > > Here's the function in its entirety:
>
> > > 255 @csrf_response_exempt
> > > 256 def DistroHeySpreadListener(request):
> > > 257         if request.method == 'POST':
> > > 258                 post = request.POST
> > > 259                 raw = request.raw_post_data
> > > 260                 remote_media_id = post.get("video_id", "")
> > > 261                 # Try to fetch the distros for this
> > > remote_media_id
> > > 262                 distro_list =
> > > Distro.objects.filter(remote_media_id__exact=remote_media_id)
> > > 263
> > > 264                 # Make sure there's at least one
> > > 265                 if len(distro_list) < 1:
> > > 266                         raise Http404
> > > 267
> > > 268                 # For each distro instance, check to see if
> > > there's a response in this api call
> > > 269                 for distro in distro_list:
> > > 270                         # Prepare logging
> > > 271                         log = distro.log
> > > 272                         log += "Raw data from POST" + str(raw)
> > > 273                         distro.log = log
> > > 274                         distro.save()
> > > 275
> > > 276                         # Get distro short name
> > > 277                         dest_short_name =
> > > distro.credential.destination.short_name
> > > 278
> > > 279                         # From short_name, return either error or
> > > link starting with http://
> > > 280                         response = post.get('link[%s]' %
> > > dest_short_name, False) or post.get('error[%s]' % dest_short_name,
> > > False)
> > > 281
> > > 282                         if response.startswith('http://'):
> > > 283                                 # If response starts with http
> > > then success, populate as link
> > > 284                                 distro.destination_url = response
> > > 285                                 distro.state = TRANSCODE_SUCCESS
> > > 286                                 log += '\nSUCCESS, url = %s' %
> > > response
> > > 287                         else:
> > > 288                                 # Else, change state to error and
> > > log error message
> > > 289                                 log += '\nERROR, error = %s' %
> > > response
> > > 290                                 distro.state = TRANSCODE_ERROR
> > > 291
> > > 292                         distro.log = log
> > > 293                         distro.save()
> > > 294
> > > 295
> > > 296                 h = HttpResponse("Thanks, HeySpread! We love
> > > you.", mimetype="text/plain", status=200)
> > > 297                 h.csrf_exempt = True
> > > 298                 return h
> > > 299 #               return HttpResponse("Thanks, HeySpread! We love
> > > you.", mimetype="text/plain", status=200)
> > > 300         else:
> > > 301                 return HttpResponse("Oops, something went wrong.",
> > > mimetype="text/plain", status=200)
>
> > > On Jul 27, 11:30 am, raj <rajeeshrn...@gmail.com> wrote:
>
> > > > Most probably it has something to do with permissions. Go thru the
> > > > exact code block which tries to post the data. Is the login successful
> > > > and does the logged-in user have got all necessary permissions
> > > > required? Post that part of the code if you can't find out.
>
> > > > Rajeesh.
>
> > > > On Jul 27, 11:20 pm, Kieran Farr <kieran.f...@gmail.com> wrote:
>
> > > > > Further, debugging 403s is nearly impossible. It'd be very helpful
> > > > > when in DEBUG mode to reveal who/what/why raised the 403.
>
> > > > > > What am I doing wrong/missing?

-- 
You received this message because you are subscribed to the Google Groups 
"Django users" group.
To post to this group, send email to django-us...@googlegroups.com.
To unsubscribe from this group, send email to 
django-users+unsubscr...@googlegroups.com.
For more options, visit this group at 
http://groups.google.com/group/django-users?hl=en.

Reply via email to