Custom Django login form middleware - django

I'm looking at a solution of implementing a modal pop-up login form to be used site wide in the navigation bar. I'm working with Django 1.11. Now, I'm defining a LoginFormMiddleware class as follows:
middleware.py
from django.http import HttpResponseRedirect
from django.contrib.auth import login
from django.contrib.auth.forms import AuthenticationForm
class LoginFormMiddleware(object):
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
return self.get_response(request)
def process_request(self, request):
from django.contrib.auth.forms import AuthenticationForm
if request.method == 'POST' and request.POST.has_key('base-account') and request.POST['base-account'] == 'Login':
form = AuthenticationForm(data=request.POST, prefix="login")
if form.is_valid():
from django.contrib.auth import login
login(request, form.get_user())
request.method = 'GET'
else:
form = AuthenticationForm(request, prefix="login")
request.login_form = form
This is all included in the standard way in settings.py in MIDDLEWARE = []. I also have 'django.template.context_processors.request' defined in TEMPLATES = [ { ... 'OPTIONS': { 'context_processors': [ *** ] } ] as I should.
Using {{ request.login_form }} in my templates as follows:
<div class="container">
{{ request.login_form }}
</div>
Yet nothing is being rendered - where it should? From browser inspector tools:
Can anyone advise me on what I am missing please?
Full middleware list:
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',
'wagtail.wagtailcore.middleware.SiteMiddleware',
'wagtail.wagtailredirects.middleware.RedirectMiddleware',
'thinkingplace.middleware.LoginFormMiddleware',
]

process_request is from the old-style, pre-Django 1.10 middleware. You are using new-style middleware so Django will not call that method.
You could move that code to the __call__ method before you call get_response.
def __call__(self, request):
# code that sets request.login_form
return self.get_response(request)
However, I’m not sure that using a middleware is the best approach here. I would consider creating a context processor that adds the login form to the template context, then submit login requests to a single login view.

Related

How to add an attribute to Django request object

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)

Django Success URL Gets Appended To Middleware Redirect URL Instead of Redirecting To Success URL

I have a form with a username field on it located at /accounts/choose-username/.
views.py
from django.views.generic import FormView
from .forms import ChooseUsernameForm
from django.contrib.auth import get_user_model
User = get_user_model()
class ChooseUsernameView(FormView):
template_name = 'registration/choose-username.html'
form_class = ChooseUsernameForm
success_url = 'accounts/profile/' # <-------------
def form_valid(self, form):
"""If the form is valid, redirect to the supplied URL."""
print(form.cleaned_data['username'])
print(self.request.user)
user = User.objects.get(email=self.request.user)
user.username = form.cleaned_data['username']
user.save()
return super().form_valid(form)
middleware.py
from django.shortcuts import redirect, reverse
class SimpleMiddleware:
def __init__(self, get_response):
self.get_response = get_response
# One-time configuration and initialization.
def __call__(self, request):
# Code to be executed for each request before
# the view (and later middleware) are called.
response = self.get_response(request)
if request.user.is_authenticated:
if request.user.username is None:
print(request.path)
if not request.path == reverse('choose_username'):
return redirect(reverse('choose_username'))
return response
When I submit the form, my url is accounts/choose-username/accounts/profile/
I want it to be accounts/profile/
MIDDLEWARE settings.py looks like this:
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'accounts.middleware.SimpleMiddleware', # <---
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
My Question
Why does my success URL get appended to my accounts/choose-username/ URL? How can I fix it?
As this—accounts/profile/—doesn't start with a slash (/), so it is a relative path. Browsers automatically append the relative path to the current page's url.
You need to use absolute path (add slash in the beginning).
success_url = '/accounts/profile/'

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.

Issue with Django 2.0 : 'WSGIRequest' object has no attribute 'session'

I upgraded my Django version from 1.11.5 to 2.0 and I'm trying to solve different deprecated element.
However, even if my CSS/bootstrap style sheet doesn't work, I don't overcome to log into my Django software. I have this issue :
'WSGIRequest' object has no attribute 'session'
This is the entire Traceback :
Environment:
Request Method: POST
Request URL: http://127.0.0.1:8000/Authentification/Login/
Django Version: 2.0
Python Version: 3.6.2
Installed Applications:
['Institution',
'django.conf.urls',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'bootstrapform',
'django_countries',
'chartit',
'Configurations',
'Home',
'Authentication',
'Identity',
'rest_framework',
'Fiscal',
'bootstrap4']
Installed Middleware:
[]
Traceback:
File "/Users/valentinjungbluth/Desktop/DatasystemsCORE3.6/lib/python3.6/site-packages/django/core/handlers/exception.py" in inner
35. response = get_response(request)
File "/Users/valentinjungbluth/Desktop/DatasystemsCORE3.6/lib/python3.6/site-packages/django/core/handlers/base.py" in _get_response
128. response = self.process_exception_by_middleware(e, request)
File "/Users/valentinjungbluth/Desktop/DatasystemsCORE3.6/lib/python3.6/site-packages/django/core/handlers/base.py" in _get_response
126. response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/Users/valentinjungbluth/Desktop/DatasystemsCORE3.6/lib/python3.6/site-packages/django/views/decorators/csrf.py" in wrapped_view
54. return view_func(*args, **kwargs)
File "/Users/valentinjungbluth/Desktop/Django/DatasystemsCORE/DatasystemsCore/DatasystemsCORE/Authentication/views.py" in Login
26. login(request, user)
File "/Users/valentinjungbluth/Desktop/DatasystemsCORE3.6/lib/python3.6/site-packages/django/contrib/auth/__init__.py" in login
130. if SESSION_KEY in request.session:
Exception Type: AttributeError at /Authentification/Login/
Exception Value: 'WSGIRequest' object has no attribute 'session'
In my Authentification app :
# views.py file
#-*- coding: utf-8 -*-
from django.contrib.auth import authenticate, login, logout
from .forms import ConnexionForm
from django.shortcuts import render, reverse, get_object_or_404, redirect
from django.http import HttpResponseRedirect, HttpResponse
from .models import LoggedUsers
from API_GED import Logger
import datetime
from django.views.decorators.csrf import csrf_exempt,csrf_protect
#csrf_exempt
def Login(request):
error = False
if request.method == "POST":
form = ConnexionForm(request.POST)
if form.is_valid():
username = form.cleaned_data["username"]
password = form.cleaned_data["password"]
user = authenticate(username=username, password=password) # Nous vérifions si les données sont correctes
if user: # Si l'objet renvoyé n'est pas None
login(request, user)
response = redirect('Homepage')
return response
else: # sinon une erreur sera affichée
error = True
else:
form = ConnexionForm()
return render(request, 'Authentication_Homepage.html', locals())
def Logout(request):
logout(request)
return redirect(reverse('Choice'))
def ConnectedUsers(request) :
logged_users = LoggedUsers.objects.all()
print (logged_users)
logged_users_number = LoggedUsers.objects.all().count()
context = {
"logged_users":logged_users,
"logged_users_number":logged_users_number,
}
return render(request, "Authentication_LoggedUsers.html", context)
An my models.py file :
from django.db import models
from django.contrib.auth.models import User
from django.contrib.auth.signals import user_logged_in, user_logged_out
# class UserProfile(models.Model):
# user = models.OneToOneField(User)
# avatar = models.ImageField(upload_to='/images/')
class LoggedUsers(models.Model):
user = models.ForeignKey(User, primary_key=True, on_delete=models.CASCADE)
def __str__(self):
return '{}{}{}'.format(self.user.first_name + " ", self.user.last_name + " (", self.user.username + ")")
def login_user(sender, request, user, **kwargs):
LoggedUsers(user=user).save()
def logout_user(sender, request, user, **kwargs):
try:
u = LoggedUsers.objects.get(user=user)
u.delete()
except LoggedUsers.DoesNotExist:
pass
user_logged_in.connect(login_user)
user_logged_out.connect(logout_user)
In my settings.py file, I have MIDDLEWARE_CLASSES :
MIDDLEWARE_CLASSES = [
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.locale.LocaleMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.middleware.gzip.GZipMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'django.middleware.security.SecurityMiddleware',
'DatasystemsCORE.middleware.OnlineNowMiddleware',
]
Any idea ?
EDIT :
I wrote MIDDLEWARE 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',
]
It seems to work !
So the MIDDLEWARE_CLASSES changed to MIDDLEWARE https://docs.djangoproject.com/en/2.0/topics/http/middleware/#activating-middleware
MIDDLEWARE_CLASSES has been deprecated since Django 1.10 and was removed completely in version 2.0. That means you have no middleware at all.
You should use the MIDDLEWARE setting instead; note, that uses new-style middleware, you might need to upgrade your custom middleware class.

Django exception middleware: TypeError: object() takes no parameters

I'm using Django 1.10 and trying to catch all exceptions with exception middleware.
The code below causes an internal server error:
mw_instance = middleware(handler)
TypeError: object() takes no parameters
views.py
from django.http import HttpResponse
def my_view(request):
x = 1/0 # cause an exception
return HttpResponse("ok")
settings.py
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',
'myproject.middleware.ExceptionMiddleware',
]
middleware.py
from django.http import HttpResponse
class ExceptionMiddleware(object):
def process_exception(self, request, exception):
return HttpResponse("in exception")
I have seen these object() takes no parameters in django 1.10 and other questions talking about middleware versus middleware_classes, but I'm not sure how that applies to this case, or what I'd actually need to change to fix the issue.
Since you are using the new MIDDLEWARE settings, your Middleware class must accept a get_response argument: https://docs.djangoproject.com/en/1.10/topics/http/middleware/#writing-your-own-middleware
You could write your class like this:
from django.http import HttpResponse
class ExceptionMiddleware(object):
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
return self.get_response(request)
def process_exception(self, request, exception):
return HttpResponse("in exception")
You could also use the MiddlewareMixin to make your Middleware compatible with pre-1.10 and post-1.10 Django versions: https://docs.djangoproject.com/en/1.10/topics/http/middleware/#upgrading-pre-django-1-10-style-middleware
from django.http import HttpResponse
from django.utils.deprecation import MiddlewareMixin
class ExceptionMiddleware(MiddlewareMixin):
def process_exception(self, request, exception):
return HttpResponse("in exception")
in the newer version of Django, the middleware should be written like this
import datetime
from django.core.cache import cache
from django.conf import settings
from django.utils.deprecation import MiddlewareMixin
class ActiveUserMiddleware(MiddlewareMixin):
def process_request(self, request):
current_user = request.user
if request.user.is_authenticated:
now = datetime.datetime.now()
cache.set('seen_%s' % (current_user.username), now,
settings.USER_LASTSEEN_TIMEOUT)