Welcome | Get started | Dive | Contribute | Topics | Reference | Changes | More

Refusing permission to an anonymous request

This document reproduces a unicode error which occurred when Lino tried to say “As Anonymous you have no permission to run this action.” in German (where the internationalized text contains non-ascii characters.

The error was:

UnicodeEncodeError at /api/trading/InvoicesByJournal
'ascii' codec can't encode character u'\xfc' in position 64: ordinal not in range(128)

This document uses lino_book.projects.cosi1:

>>> print(settings.SETTINGS_MODULE)
>>> print(settings.SITE.default_user)
>>> print(settings.SITE.user_model)
<class 'lino.modlib.users.models.User'>
>>> print(settings.SITE.remote_user_header)
>>> print(settings.SITE.get_auth_method())
>>> print('\n'.join(settings.MIDDLEWARE))
>>> 'django.contrib.sessions' in settings.INSTALLED_APPS

Some client logs in and gets some data:

>>> test_client.force_login(rt.login('rolf').user)

The user uses the main menu to open sales.InvoicesByJournal, which will do the following AJAX call to get its data:

>>> url = '/api/trading/InvoicesByJournal'
>>> url += "?start=0&limit=25&fmt=json&rp=ext-comp-1135"
>>> url += "&pv=1&pv=&pv=&pv=&pv=&mt=24&mk=1"
>>> res = test_client.get(url, HTTP_X_REQUESTED_WITH='XMLHttpRequest')
>>> res.status_code
>>> r = json.loads(res.content.decode())
>>> print(json.dumps(sorted(r.keys())))
["count", "html_text", "no_data_text", "param_values", "rows", "success", "title"]
>>> len(r['rows'])

Now imagine that the user gets a break and leaves her browser open, the server meanwhile did a dump and a reload. So the sessions have been removed:

>>> x = sessions.Session.objects.all().delete()

The user comes back and resizes her browser window, or some other action which will trigger a refresh. The same URL will now return a 400 (Bad request) response:

>>> import logging
>>> logger = logging.getLogger("django.request")
>>> logger.setLevel(logging.CRITICAL)
>>> res = test_client.get(url)  
>>> res.status_code
>>> soup = BeautifulSoup(res.content, 'lxml')
>>> print(soup.body.prettify())
  403 Forbidden

The above URL is usually issued as an AJAX call.

Lino recognizes AJAX calls by the extra HTTP header HTTP_X_REQUESTED_WITH having the value XMLHttpRequest. For this simulation we must say this explicitly to Django’s test client.

>>> res = test_client.get(url, HTTP_X_REQUESTED_WITH='XMLHttpRequest')

When an exception like the above occurs during an AJAX call, Lino’s reponse has different format which is defined by the lino.utils.ajax middleware. It sets the status code to 400 (Bad Request) which means “The request could not be understood by the server due to malformed syntax. The client SHOULD NOT repeat the request without modifications.” (w3.org)

>>> res.status_code
>>> print(res.content.decode())
PermissionDenied: As 000 (Anonym) you have no view permission for this action.