How to add an attribute to Django request object - django

I try to add the business attribute to the request object by using my own middleware, it nicely works with rest_framework.authentication.SessionAuthentication and I can use request.business in my views. But when I try to authenticate with JWT method (rest_framework_simplejwt.authentication.JWTAuthentication) when my middleware code is run request.user is set to AnonymouseUser so can't fetch business associated with user? Why did this happen?
# middleware.py
class MyMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
request.business = None
if request.user.is_authenticated and hasattr(request.user, 'business'):
request.business = request.user.business
response = self.get_response(request)
return response
Middlewares:
MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware',
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'my_app.middleware.MyMiddleware',
]
rest_framework settings:
REST_FRAMEWORK = {
...
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework_simplejwt.authentication.JWTAuthentication',
'rest_framework.authentication.SessionAuthentication',
],
}

Unfortunately, DEFAULT_AUTHENTICATION_CLASSES are not processed in middleware process, but in the view itself. What is giving you the proper user when using session is the Django's AuthenticationMiddleware and REST Framework is just using this value when the session authentication is enabled.
To solve that, you can do one of the following:
Move adding request.business to the views (for example by adding some class that all your views will inherit from)
Move JWT authentication into Django middlewares (this has a side effect of DRF enforcing CSRF checks by default when user is logged in)

Related

Django csrf token not refreshed after first login

I have hosted a website at http://cognezic-dev.ap-south-1.elasticbeanstalk.com/login/. On closer inspection, the form has the same csrf token across page refreshes and browsers, which I suspect to be the cause of the issue,this works fine on the local django server.Dont know where this is being cached. I have added CSRF Middleware in the settings.py too.
You can use the test credentials as username bismeet and password bis12345 for testing.
I have also tried creating a custom middleware as follows:
from django.middleware.csrf import rotate_token, CsrfViewMiddleware
from django.utils.deprecation import MiddlewareMixin
class CSRFRefresh(CsrfViewMiddleware,MiddlewareMixin):
def process_response(self, request, response):
print("Custom MiddleWare called")
rotate_token(request)
return response
But it still fails.
My settings.py with custom middleware contains:
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'solvesto.middleware.csrf_refresh.CSRFRefresh'
]
If not using the custom middleware,I use:
'django.middleware.csrf.CsrfViewMiddleware'
instead of
'solvesto.middleware.csrf_refresh.CSRFRefresh'
The only last resort I see to make this work is to remove csrf altogether,which is of course,bad for security.
I removed the csrf security,no other solution,works now.

Middleware not executed by Django

I am trying to include a middleware in a Django project, but it seems the middleware is not being executed by Django. The idea is to impersonate another user account when having app-administrator privileges.
The MIDDLEWARE section of my settings.py file looks like this:
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'mird_project.monitor.middleware.impersonateMiddleware.ImpersonateMiddleware',
]
The middleware class looks like this:
from .models import Usuario
class ImpersonateMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
response = self.get_response(request)
return response
def process_request(self, request):
us_obj = Usuario.objects.all().filter(id_usuario=request.user.username).first()
tipo = us_obj.tipo.id_tipo
if tipo == "AD" and "__impersonate" in request.GET:
request.session['impersonate_id'] = request.GET["__impersonate"]
elif "__unimpersonate" in request.GET:
del request.session['impersonate_id']
if tipo == "AD" and 'impersonate_id' in request.session:
request.user = Usuario.objects.get(id_usuario=request.session['impersonate_id'])
return None
I inserted an assert False, request inside the process_request method so that it would abort execution with an exception and show me what request contained. It never even got executed, so I assume the middleware never gets executed. It doesn't throw any kind of error and the impersonation mechanism just displays the same administrator user in the site.
Any ideas why the middleware isn't being called?
It looks like you are mixing old and new style middleware APIs. The process_request() method is pre-Django 1.10 and won't get called automatically unless your middleware class uses MiddlewareMixin.
You'll need to call process_request() yourself from the __call__() method:
def __call__(self, request):
self.process_request(request) # Call process_request()
response = self.get_response(request)
return response
Or alternatively you could inherit from MiddlewareMixin so that process_request() is called by Django. However it would only make sense to do that if you need to make the middleware backwards compatible.

Django Rest Framework IsAuthenticated permission Error Anonymous user

I'm writing api using django rest framework using Token Authentication method written as below
#api_view(['GET'])
#permission_classes((IsAuthenticated, ))
def ah(request, format=None):
result = request.user.is_authenticated()
content = {"hello":result}
return Response(content)
my settings are
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.IsAdminUser',
'rest_framework.permissions.IsAuthenticated',
],
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.TokenAuthentication',
#'rest_framework.authentication.BasicAuthentication',
#'rest_framework.authentication.SessionAuthentication'
)
}
MIDDLEWARE_CLASSES = [
'django.contrib.sessions.middleware.SessionMiddleware',
#'middleware.FirstTokenAuth.AuthenticationMiddlewareJWT',
#'middleware.TokenAuthTest.JWTAuthenticationMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.middleware.security.SecurityMiddleware',
#'corsheaders.middleware.CorsMiddleware',
'django.middleware.common.CommonMiddleware',
#'django.middleware.csrf.CsrfViewMiddleware',
#'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
When I call this API using IsAdminUserpermission class The django restframework returns:
403 response "detail": "Authentication credentials were not provided." if the token wasn't provided in the header
401 response "detail": "You do not have permission to perform this action."
if the token was not for admin user
but the main problem is here when I set
#permission_classes((IsAuthenticated, ))
The API is called normally without returning 403 or 401 even if i didn't add a token to the header and the user returned is anonymous user.
How can I prevent anonymous user from calling API and return 403 response for him.
Any help Please !!
The #permission_classes is for identifying if the api needs authentication. If you want to use token, try to add #authentication_classes with TokenAuthentication inside. This will check the token in your header and create the user object inside request.
Use this:
permission_classes = [permissions.IsAuthenticated,]
It worked for me.

middleware does an infinite redirect

I've already read this but the answer doesnt fit my needs.
I've made this very simple middleware:
class RedirectIfUserIsNotActiveMiddleware(object):
"""
Middleware to redirect if account is disabled
"""
#staticmethod
def process_request(request):
if not request.user.is_active:
try:
# test to see if not already on the right url:
a = resolve(request.path)
if a.url_name != settings.URL_REDIRECT_USER_NOT_ACTIVE:
return HttpResponseRedirect(
reverse_lazy(settings.URL_REDIRECT_USER_NOT_ACTIVE))
# request.path do a 404 if URL unknown -> do nothing :
except Resolver404:
pass
And I've added this here:
MIDDLEWARE_CLASSES = (
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.locale.LocaleMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'django.middleware.security.SecurityMiddleware',
'third_party.hqf_middleware.RedirectIfUserIsNotActiveMiddleware',
)
But when the account is disabled I get an infinite loop with the message on the webbrowser: "mywebsite.com redirected you too many times.".
In my settings I have this:
URL_REDIRECT_USER_NOT_ACTIVE = 'my_home_profile_account_desactivated'
And the URL I'm trying to get is always (even each time step by step) /en/my-home/profile/edit
What am I missing?
It looks as if the my_home_profile_account_desactivated view is redirecting to your login page, and then the middleware redirects back to my_home_profile_account_desactivated, creating a redirect loop.
You should be able to break the authentication loop by changing the middleware so that you only redirect users that are authenticated and inactive.
#staticmethod
def process_request(request):
if request.user.is_authenticated() and not request.user.is_active:
...

etag and last modified headers for django app not being set

I am trying to set the etag header using the condition decorator with Django 1.3.
I am using the following:
#condition(etag_func=profile_etag, last_modified_func=profile_last_modified)
#require_person_viewed_is_verified
def profile(request, id):
"""
Return profile for person id.
If profile is not verified only staff and self can view.
"""
user = request.user
...
with a middleware classes of
MIDDLEWARE_CLASSES = (
'django.middleware.cache.UpdateCacheMiddleware',
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'johnny.middleware.LocalStoreClearMiddleware',
'johnny.middleware.QueryCacheMiddleware',
# 'django.middleware.cache.CacheMiddleware',
'django.middleware.http.ConditionalGetMiddleware',
'pybb.middleware.PybbMiddleware',
)
USE_ETAGS = True
I am not getting the etag or last modified headers being set in the http response. Any ideas please?
I was using CharlesProxy (which is a great tool btw) and I had no caching enabled! School boy error I know!