In our project, we have used django SessionMiddleware to handle users sessions and it's working fine. The only problem here is when PermissionDenied exceptions happens, an error and its traceback will be printed out in the console! However as expected, by raising that exception, the 403 page will show to the user, but I think it doesn't seem rational, because the middleware here is handling the exception! Just like not found exception, I expect no error in the console. Is there anything wrong?!
here is the middleware settings:
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django_otp.middleware.OTPMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'axes.middleware.AxesMiddleware',
]
And here's the printed error:
Forbidden (Permission denied): /the/not_allowed/page
Traceback (most recent call last):
File "/venv/lib/python3.8/site-packages/django/core/handlers/exception.py", line 34, in inner
response = get_response(request)
File "/venv/lib/python3.8/site-packages/django/core/handlers/base.py", line 115, in _get_response
response = self.process_exception_by_middleware(e, request)
File "/venv/lib/python3.8/site-packages/django/core/handlers/base.py", line 113, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/usr/lib/python3.8/contextlib.py", line 75, in inner
return func(*args, **kwds)
File "/our_project/base/decorators.py", line 88, in wrapper
return view_func(request, *args, **kwargs)
File "/venv/lib/python3.8/site-packages/django/contrib/auth/decorators.py", line 21, in _wrapped_view
return view_func(request, *args, **kwargs)
File "/venv/lib/python3.8/site-packages/django/contrib/auth/decorators.py", line 20, in _wrapped_view
if test_func(request.user):
File "/venv/lib/python3.8/site-packages/django/contrib/auth/decorators.py", line 70, in check_perms
raise PermissionDenied
django.core.exceptions.PermissionDenied
From this comment
In this example the exception is raised from permission_required
decorator in django.contrib.auth.decorators. I passed
raise_exception=True to this decorator to make it raise exception instead of redirecting to login page
So, it is clear that you have set raise_exception=True in your decorator.
and from the doc
If the raise_exception parameter is given, the decorator will raise PermissionDenied, prompting the 403 (HTTP Forbidden) view instead of redirecting to the login page.
So, technically, when the conditions are not met, Django will raise an exception. But, depending on the value of 403.html, Django will show you either the plain 403 page or custom HTML response.
I expect no error in the console. Is there anything wrong?
There is nothing wrong here (or I couldn't see anything).
So, if you want to omit the traceback from the console, you may need to write a error handling middleware to handle this exception.
# error handling middleware
from django.core.exceptions import PermissionDenied
from django.shortcuts import render
class PermissionDeniedErrorHandler:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
response = self.get_response(request)
return response
def process_exception(self, request, exception):
# This is the method that responsible for the safe-exception handling
if isinstance(exception, PermissionDenied):
return render(
request=request,
template_name="your_custom_403.html",
status=403
)
return None
Note: Do not forgot to bind this middleware in your MIDDLEWARE settings.
and thus, you will not get any error tracebacks in the console.
Cheers!!!
Related
I'm using django-revproxy and Django REST Framework in my project. And I'm exposing an API where users get analytics about their chatbots, and it works as the following:
The user send requests the analytics from the Django project
Django project, checks if the user is authenticated and owns that chatbot
if True it contacts another external service.
My urls.py:
# urls.py
urlpatterns = (
url(r'^analytics/(?P<path>.*)$', api.AnalyticsFunctionsProxyView.as_view()),
)
And in my view:
# views.py
from rest_framework.authentication import TokenAuthentication
from rest_framework_jwt.authentication import JSONWebTokenAuthentication
from rest_framework.permissions import IsAuthenticated
from revproxy.views import ProxyView
from .permissions import HasChatBotPermission
...
class AnalyticsFunctionsProxyView(ProxyView):
upstream = settings.ANALYTICS_FAAS_URL
def parse_body(self, request):
if isinstance(request, rest_framework.request.Request):
return request.data
return super(AnalyticsFunctionsProxyView, self).parse_body(request)
#classmethod
def as_view(cls, *args, **kwargs):
view = super(AnalyticsFunctionsProxyView, cls).as_view(*args, **kwargs)
view = permission_classes((IsAuthenticated, HasChatBotPermission,))(view)
view = authentication_classes([TokenAuthentication, JSONWebTokenAuthentication])(view)
view = api_view(['GET', 'POST'])(view)
return view
And my HasChatBotPermission permissions
#permissions.py
class HasChatBotPermission(permissions.BasePermission):
def has_permission(self, request, view):
try:
bot_name = request.data.get('name')
user = request.user
self.message = 'Permission denied for {}'.format(name)
return ChatBot.objects.filter(user=user, project_name=project_id).exists()
except Exception:
self.message = 'Permission denied, no project_id was defined!'
return False
When the view is called it raises this exception:
Traceback (most recent call last):
File "/home/fcmam5/dela3a/env/lib/python3.6/site-packages/rest_framework/request.py", line 379, in __getattribute__
return super(Request, self).__getattribute__(attr)
AttributeError: 'Request' object has no attribute 'body'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/home/fcmam5/dela3a/env/lib/python3.6/site-packages/django/core/handlers/exception.py", line 42, in inner
response = get_response(request)
File "/home/fcmam5/dela3a/env/lib/python3.6/site-packages/django/core/handlers/base.py", line 187, in _get_response
response = self.process_exception_by_middleware(e, request)
File "/home/fcmam5/dela3a/env/lib/python3.6/site-packages/django/core/handlers/base.py", line 185, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/home/fcmam5/anaconda3/lib/python3.6/contextlib.py", line 52, in inner
return func(*args, **kwds)
File "/home/fcmam5/dela3a/env/lib/python3.6/site-packages/django/views/decorators/csrf.py", line 58, in wrapped_view
return view_func(*args, **kwargs)
File "/home/fcmam5/dela3a/env/lib/python3.6/site-packages/django/views/generic/base.py", line 68, in view
return self.dispatch(request, *args, **kwargs)
File "/home/fcmam5/dela3a/env/lib/python3.6/site-packages/rest_framework/views.py", line 477, in dispatch
response = self.handle_exception(exc)
File "/home/fcmam5/dela3a/env/lib/python3.6/site-packages/rest_framework/views.py", line 437, in handle_exception
self.raise_uncaught_exception(exc)
File "/home/fcmam5/dela3a/env/lib/python3.6/site-packages/rest_framework/views.py", line 474, in dispatch
response = handler(request, *args, **kwargs)
File "/home/fcmam5/dela3a/env/lib/python3.6/site-packages/rest_framework/decorators.py", line 52, in handler
return func(*args, **kwargs)
File "/home/fcmam5/dela3a/env/lib/python3.6/site-packages/django/views/generic/base.py", line 68, in view
return self.dispatch(request, *args, **kwargs)
File "/home/fcmam5/dela3a/env/lib/python3.6/site-packages/revproxy/views.py", line 204, in dispatch
proxy_response = self._created_proxy_response(request, path)
File "/home/fcmam5/dela3a/env/lib/python3.6/site-packages/revproxy/views.py", line 139, in _created_proxy_response
request_payload = request.body
File "/home/fcmam5/dela3a/env/lib/python3.6/site-packages/rest_framework/request.py", line 383, in __getattribute__
return getattr(self._request, attr)
File "/home/fcmam5/dela3a/env/lib/python3.6/site-packages/django/http/request.py", line 264, in body
raise RawPostDataException("You cannot access body after reading from request's data stream")
django.http.request.RawPostDataException: You cannot access body after reading from request's data stream
The issue is caused by this line bot_name = request.data.get('name') in my permissions.py, when I pass a string directly it passes without any problems.
My question is:
How can I access the request body without having this error? Why I'm having this error?
Is there a better solution for checking the user permission with Django revproxy.
This is my first question in Stackoverflow, sorry if my question is not clea, and for my poor English :)
You're having this error because django-revproxy attempts to read the raw request body so it can create a proxy request to the upstream server.
However, with Django's (and WSGI's, and buffering) semantics, this isn't possible once you've accessed the request body as anything but a raw stream, which you do when you request.data.get('name'). This parses the request body as JSON, HTTP multipart, whatever, depending on DRF's request negotiation configuration, and consumes the stream.
There are two ways you could get around this, as far as I see:
pass bot_name somewhere else than the body; a query string parameter, an HTTP header, part of the URL, for instance, so you don't need to access the body, or
make the backend request yourself using requests instead of reverse-proxying (which is basically the same thing, but with added magic to try and copy the request through as-is).
I am trying to create google map API 3 markers using users' locations in Geodjango.
I encounter Internal Server Error 500 in rendering the google map and markers. On the CMD console , I get TypeError: 'GeoQuerySet' object is not callable.
Can you please suggest a solution and explain the cause for the exception?
The views.py
class MyView(View):
def get(self, request):
resp = UserList.objects.all().values('nickname', 'last_location')
print (resp)
return JsonResponse(request, resp, safe=False)
The urls.py
from django.conf.urls import include, url
from django.contrib import admin
from mymap_app import views as map_views
admin.autodiscover()
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^my_endpoint/$',map_views.MyView.as_view(),name='user_list'),
url(r'^$',map_views.IndexView.as_view(), name='index'),
]
Traceback:
Traceback (most recent call last):
File "C:\Python35-32\lib\site-packages\django\core\handlers\exception.py", lin
e 41, in inner
response = get_response(request)
File "C:\Python35-32\lib\site-packages\django\core\handlers\base.py", line 187
, in _get_response
response = self.process_exception_by_middleware(e, request)
File "C:\Python35-32\lib\site-packages\django\core\handlers\base.py", line 185
, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "C:\Python35-32\lib\site-packages\django\views\generic\base.py", line 68,
in view
return self.dispatch(request, *args, **kwargs)
File "C:\Python35-32\lib\site-packages\django\views\generic\base.py", line 88,
in dispatch
return handler(request, *args, **kwargs)
File "C:\Users\Kaleab\Desktop\WebMap\AAmap_Project\mymap_app\views.py", line 2
1, in get
return JsonResponse(request, resp, safe=False)
File "C:\Python35-32\lib\site-packages\django\http\response.py", line 530, in
__init__
data = json.dumps(data, cls=encoder, **json_dumps_params)
File "C:\Python35-32\lib\json\__init__.py", line 237, in dumps
**kw).encode(obj)
TypeError: 'GeoQuerySet' object is not callable
[05/Sep/2017 22:36:45] "GET /my_endpoint/ HTTP/1.1" 500 18865
Look at this: data = json.dumps(data, cls=encoder, **json_dumps_params).
JsonResponse is trying to serialize the parameters you passed it, but a GeoQuerySet is not serializable by the function call and therefore throws a TypeError. See the documentation below:
https://docs.python.org/2/library/json.html
If skipkeys is true (default: False), then dictionary keys that are not of a
basic type (str, unicode, int, long, float, bool, None) will be
skipped instead of raising a TypeError.
In order to get around this you could instead do:
return HttpResponse(
json.dumps(response, skipkeys=True), content_type="application/json"
)
It would also be possible to format the GeoQuerySet as a python dictionary and then pass it to JsonResponse.
The following change to the views.py resolved the Geoqueryset object not callable error.
class MyView(View):
def get(self, request):
response = UserList.objects.all().values('nickname','last_location')
print(response)
return JsonResponse({'response':list(response)})
I have made a custom middleware and if it throws exception, it is automatically handled by BaseHandler function get_response (django.core.handlers.base). My exception ObjectDoesNotExist, by default, is not handled and therefore a function handle_uncaught_exception takes care of it. But it considers all error as 500 Internal Server (even if it is a 400 Bad request) and returns default django error html template with 500 code. Here is my code:
custom_middleware.py:
class CustomMiddleware(object):
"""
custom middleware
"""
def process_request(self, request):
try:
# if agent doesn't exists then return json response
agent = Agent.objects.get(agent_token=agent_token)
except ObjectDoesNotExist as e:
raise Exception(exception_code=400, detail=e, response_msg="You are not an authorized agent", request= request_data)
This automatically is handled by BaseHandler.get_response()
So to solve the problem what I did was inherited the BaseHandler and override get_response function adding exception handler for my error and which gives a json response rather than HTML template.
base.py
from django.core.handlers.base import BaseHandler
class ExceptionHandler(BaseHandler):
def get_response(self, request):
my code
But this doesn't help as it calls the base class method even after overriding.
Here is the traceback:
File "/webapp/apps/middleware/custom_middleware.py" in process_request
49. agent = Agent.objects.get(agent_token=agent_token)
File "lib/python3.4/site-packages/django/db/models/manager.py" in manager_method
122. return getattr(self.get_queryset(), name)(*args, **kwargs)
File "lib/python3.4/site-packages/django/db/models/query.py" in get
387. self.model._meta.object_name
During handling of the above exception (Agent matching query does not exist.), another exception occurred:
File "lib/python3.4/site-packages/django/core/handlers/base.py" in get_response
123. response = middleware_method(request)
File "webapp/apps/middleware/request_log.py" in process_request
54. raise Exception(exception_code=400, detail=e, response_msg="You are not an authorized agent", request= request_data)
Any help
I have a very basic Django class-based view as follows. It doesn't really even do anything:
from django.views.generic import View, TemplateView
class index(TemplateView):
template_name = 'myApp/index.html'
def dispatch(self, request, *args, **kwargs):
return super(index, self).dispatch(request, *args, **kwargs)
However, when I load this view through the browser, I get the following error:
Exception Type: TypeError
Exception Value:
__init__() takes exactly 1 argument (2 given)
Exception Location: /Library/Python/2.7/site-packages/Django-1.6.5-py2.7.egg/django/core/handlers/base.py in get_response, line 112
Python Executable: /usr/bin/python
Python Version: 2.7.5
Python Path:
Why am I getting this error? And how to fix it please?
Below is more context around the location of the error:
/Library/Python/2.7/site-packages/Django-1.6.5-py2.7.egg/django/core/handlers/base.py in get_response
106. response = middleware_method(request, callback, callback_args, callback_kwargs)
107. if response:
108. break
109. if response is None:
110. wrapped_callback = self.make_view_atomic(callback)
111. try:
112. response = wrapped_callback(request, *callback_args, **callback_kwargs) ...
113. except Exception as e:
114. # If the view raised an exception, run it through exception
115. # middleware, and if the exception middleware returns a
116. # response, use that. Otherwise, reraise the exception.
117. for middleware_method in self._exception_middleware:
118. response = middleware_method(request, e)
I'm working on a simple webservice in django. This is my first web app in django/python so I wouldn't be surprised if I'm missing something obvious here, but...
I'm currently trying to test my logic for filtering a url.
# Works as expected
response = self.client.post("/mysite/goodurl/")
self.assertEqual(response.status_code, 200)
# Has an exception rather than a 404
response = self.client.post("/mysite/badurl/")
self.assertEqual(response.status_code, 404)
So, the badurl case isn't simply being not found and throwing a 404, instead I'm getting this error:
Traceback (most recent call last):
File "/home/user/me/mysite/tests.py", line 55, in test_add_tracker
response = self.client.post("/mysite/badurl/")
File "/home/path/to/some/bin/dir/freeware/Python/lib/python2.7/site-packages/django/test/client.py", line 449, in post
response = super(Client, self).post(path, data=data, content_type=content_type, **extra)
File "/home/path/to/some/bin/dir/freeware/Python/lib/python2.7/site-packages/django/test/client.py", line 262, in post
return self.request(**r)
File "/home/path/to/some/bin/dir/freeware/Python/lib/python2.7/site-packages/django/core/handlers/base.py", line 111, in get_response
response = callback(request, *callback_args, **callback_kwargs)
File "/home/path/to/some/bin/dir/freeware/Python/lib/python2.7/site-packages/django/views/decorators/csrf.py", line 77, in wrapped_view
return view_func(*args, **kwargs)
TypeError: EtimeFetcher() got an unexpected keyword argument 'alias'
I've tried googling for the EtimeFetcher message, but no luck. Any ideas?
Uou may probably have some view to catch all Http404 errors. It seems that Django however had found a view to execute for the /badurl. Search in your code for statements containing the alias named parameter, like: "alias=yyyy". Or probably some url pattern, contaning 'alias' as extra parameter in urls.py?