Alexander Tyapkov Alexander Tyapkov - 15 days ago 5
Python Question

Django middleware with login and password which hides all the website pages

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:


  1. When I place the code inside process_request I can catch any call
    to any page on the website, but csrf is not rendered. As result csrf error raised when user submits form.

  2. When I place
    the code inside process_view everything is working for the page in
    case it exists. If it doesn't exist user is redirected according to
    404 and other exception urls. And I don't want this redirect to happen.



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

Answer

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.