I have written following middleware which renders form and asks user for login and password. The middleware should be applied to the whole website:
class InviteLoginForWebsiteMiddleware(object):
def process_request(self, request):
if request.session.get('has_invite') == True:
return None
form = WebsiteLoginForm()
extra_context = dict()
extra_context['form'] = form
template_name = 'websiteLogin.html'
if request.method == "POST":
form = WebsiteLoginForm(request.POST)
if form.is_valid():
login = form.cleaned_data['login']
password = form.cleaned_data['password']
if login == "mylogin" and password == "mypassword":
request.session['has_inv'] = True
return None
return ExtraContextTemplateView.as_view(template_name=template_name, extra_context=extra_context)(request)
The problem with this solution is that when I am creating the form inside process_request csrf token is missing on the rendered page. I have looked for the answer and found that developers recommend to generate form and process it inside process_view
After moving all the code into process_view like:
def process_view(self, request, view_func, view_args, view_kwargs):
if request.session.get('has_inv') == True:
return None
form = WebsiteLoginForm()
extra_context = dict()
extra_context['form'] = form
template_name = 'websiteLogin.html'
if request.method == "POST":
form = WebsiteLoginForm(request.POST)
if form.is_valid():
login = form.cleaned_data['login']
password = form.cleaned_data['password']
if login == "mylogin" and password == "mypassword":
request.session['has_inv'] = True
return None
return ExtraContextTemplateView.as_view(template_name=template_name, extra_context=extra_context)(request)
the code started to work, csrf token was generated and I was able to submit form with login and password.
The problem appears when user enters not valid page like www.mysite.com/notworkingurl/. In this case the process_view is not working, 404 error raised and user is redirected to page which shows partly the interface of the web app. And, of course, I expect that middleware should hide all the pages of the app.
Once again:
Can someone suggest any good way to solve this issue?
Solution:
Thanks to suggestion of @knbk one of possible solutions is to use csrf_protect decorator. To make this idea work I have modified my view class as follows:
class ExtraContextTemplateViewCsrfProtect(TemplateView):
extra_context = None
@method_decorator(csrf_protect)
def dispatch(self, request, *args, **kwargs):
return super(ExtraContextTemplateViewCsrfProtect, self).dispatch(request, *args, **kwargs)
def get_context_data(self, *args, **kwargs):
context = super(ExtraContextTemplateViewCsrfProtect, self).get_context_data(*args, **kwargs)
if self.extra_context:
context.update(self.extra_context)
return context
post = TemplateView.get
Your middleware is not just missing a csrf token, it's vulnerable to CSRF attacks.
If you want to use this pattern, rather than redirecting to a single login view, you should move all the form logic into the actual view. You can then use csrf_protect
to protect the view against CSRF attacks, which also enables you to use the token in the template:
class InviteLoginForWebsiteMiddleware(object):
def process_request(self, request):
if request.session.get('has_invite') == True:
return None
return csrf_protect(CustomLoginView.as_view())(request)
However, I would recommend the method suggested by matox.