Skip to content

Commit

Permalink
Check password for pwned status on login; fixes #826
Browse files Browse the repository at this point in the history
  • Loading branch information
Carson-Tang authored and Xyene committed Apr 22, 2020
1 parent 2f64355 commit 85ca010
Show file tree
Hide file tree
Showing 4 changed files with 35 additions and 10 deletions.
8 changes: 1 addition & 7 deletions dmoj/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
from martor.views import markdown_search_user

from judge.feed import AtomBlogFeed, AtomCommentFeed, AtomProblemFeed, BlogFeed, CommentFeed, ProblemFeed
from judge.forms import CustomAuthenticationForm
from judge.sitemap import BlogPostSitemap, ContestSitemap, HomePageSitemap, OrganizationSitemap, ProblemSitemap, \
SolutionSitemap, UrlSitemap, UserSitemap
from judge.views import TitledTemplateView, api, blog, comment, contests, language, license, mailgun, organization, \
Expand Down Expand Up @@ -51,12 +50,7 @@
TitledTemplateView.as_view(template_name='registration/registration_closed.html',
title='Registration not allowed'),
name='registration_disallowed'),
url(r'^login/$', auth_views.LoginView.as_view(
template_name='registration/login.html',
extra_context={'title': _('Login')},
authentication_form=CustomAuthenticationForm,
redirect_authenticated_user=True,
), name='auth_login'),
url(r'^login/$', user.CustomLoginView.as_view(), name='auth_login'),
url(r'^logout/$', user.UserLogoutView.as_view(), name='auth_logout'),
url(r'^password/change/$', auth_views.PasswordChangeView.as_view(
template_name='registration/password_change_form.html',
Expand Down
10 changes: 9 additions & 1 deletion judge/middleware.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,19 @@ def __init__(self, get_response):
def __call__(self, request):
if request.user.is_authenticated:
profile = request.profile = request.user.profile
logout_path = reverse('auth_logout')
login_2fa_path = reverse('login_2fa')
change_password_path = reverse('password_change')
change_password_done_path = reverse('password_change_done')
if (profile.is_totp_enabled and not request.session.get('2fa_passed', False) and
request.path not in (login_2fa_path, reverse('auth_logout')) and
request.path not in (login_2fa_path, logout_path) and
not request.path.startswith(settings.STATIC_URL)):
return HttpResponseRedirect(login_2fa_path + '?next=' + urlquote(request.get_full_path()))
elif (request.session.get('password_pwned', False) and
request.path not in (change_password_path, change_password_done_path,
login_2fa_path, logout_path) and
not request.path.startswith(settings.STATIC_URL)):
return HttpResponseRedirect(change_password_path + '?next=' + urlquote(request.get_full_path()))
else:
request.profile = None
return self.get_response(request)
Expand Down
23 changes: 21 additions & 2 deletions judge/views/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@
from django.contrib.auth import logout as auth_logout
from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import Permission
from django.contrib.auth.views import redirect_to_login
from django.contrib.auth.views import LoginView, redirect_to_login
from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ValidationError
from django.db import transaction
from django.db.models import Count, Max, Min
from django.http import Http404, HttpResponseRedirect, JsonResponse
Expand All @@ -23,11 +24,12 @@
from django.views.generic import DetailView, ListView, TemplateView
from reversion import revisions

from judge.forms import ProfileForm, newsletter_id
from judge.forms import CustomAuthenticationForm, ProfileForm, newsletter_id
from judge.models import Profile, Rating, Submission
from judge.performance_points import get_pp_breakdown
from judge.ratings import rating_class, rating_progress
from judge.utils.problems import contest_completed_ids, user_completed_ids
from judge.utils.pwned import PwnedPasswordsValidator
from judge.utils.ranker import ranker
from judge.utils.subscription import Subscription
from judge.utils.unicode import utf8text
Expand Down Expand Up @@ -117,6 +119,23 @@ def get(self, request, *args, **kwargs):
return super(UserPage, self).get(request, *args, **kwargs)


class CustomLoginView(LoginView):
template_name = 'registration/login.html'
extra_context = {'title': _('Login')}
authentication_form = CustomAuthenticationForm
redirect_authenticated_user = True

def form_valid(self, form):
password = form.cleaned_data['password']
validator = PwnedPasswordsValidator()
try:
validator.validate(password)
self.request.session['password_pwned'] = False
except ValidationError:
self.request.session['password_pwned'] = True
return super().form_valid(form)


EPOCH = datetime(1970, 1, 1, tzinfo=timezone.utc)


Expand Down
4 changes: 4 additions & 0 deletions templates/registration/password_change_form.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

{% block body %}
<form action="" method="post" class="form-area">
{% if request.session.password_pwned %}
<h4>{{ _('We found your password in <a href="https://haveibeenpwned.com/Passwords">a database of compromised passwords</a>.
To protect your account, we are requiring you to change your password to a more secure password.') }}</h4>
{% endif %}
{% csrf_token %}
<table border="0" class="django-as-table">{{ form.as_table() }}</table>
<hr>
Expand Down

0 comments on commit 85ca010

Please sign in to comment.