diff --git a/accountsplus/admin.py b/accountsplus/admin.py index ed0bb1f..df8f7db 100644 --- a/accountsplus/admin.py +++ b/accountsplus/admin.py @@ -1,5 +1,3 @@ -from __future__ import unicode_literals - import django.core.exceptions import django.shortcuts import django.http @@ -21,8 +19,8 @@ from django.conf import settings from django.apps import apps -import signals -import models +from accountsplus import signals +from accountsplus import models sensitive_post_parameters_m = django.utils.decorators.method_decorator( @@ -185,10 +183,11 @@ def reset_passwords(self, request, queryset): reset_passwords.short_description = 'Send password reset emails to selected Users' def get_timezone(self, obj): - return unicode(obj.timezone) + return str(obj.timezone) def masquerade(self, obj): - return 'sign in'.format(django.urls.reverse('masquerade', kwargs={'user_id': obj.id})) + return django.utils.html.mark_safe( + 'sign in'.format(django.urls.reverse('masquerade', kwargs={'user_id': obj.id}))) masquerade.short_description = 'Sign in' masquerade.allow_tags = True diff --git a/accountsplus/apps.py b/accountsplus/apps.py index f8f6117..e073873 100644 --- a/accountsplus/apps.py +++ b/accountsplus/apps.py @@ -1,5 +1,3 @@ -from __future__ import unicode_literals - from django.apps import AppConfig diff --git a/accountsplus/context_processors.py b/accountsplus/context_processors.py index 6b8b519..033c3e8 100644 --- a/accountsplus/context_processors.py +++ b/accountsplus/context_processors.py @@ -1,8 +1,5 @@ -from __future__ import unicode_literals - - def masquerade_info(request): - if request.user.is_authenticated(): + if request.user.is_authenticated: return { 'is_masquerading': request.session.get('is_masquerading', False), } diff --git a/accountsplus/forms.py b/accountsplus/forms.py index 6c9fbbc..5007950 100644 --- a/accountsplus/forms.py +++ b/accountsplus/forms.py @@ -1,5 +1,3 @@ -from __future__ import unicode_literals - import django.forms from django.conf import settings from django.apps import apps diff --git a/accountsplus/middleware.py b/accountsplus/middleware.py index 3caa62b..ef7ee60 100644 --- a/accountsplus/middleware.py +++ b/accountsplus/middleware.py @@ -1,11 +1,10 @@ -from __future__ import unicode_literals import django.utils.timezone from django.utils.deprecation import MiddlewareMixin class TimezoneMiddleware(MiddlewareMixin): def process_request(self, request): - if request.user.is_authenticated() and request.user.timezone: + if request.user.is_authenticated and request.user.timezone: django.utils.timezone.activate(request.user.timezone) else: django.utils.timezone.deactivate() diff --git a/accountsplus/models.py b/accountsplus/models.py index 2f6d487..97bdfbc 100644 --- a/accountsplus/models.py +++ b/accountsplus/models.py @@ -1,5 +1,3 @@ -from __future__ import unicode_literals - import logging from django.utils.translation import ugettext_lazy as _ @@ -176,4 +174,4 @@ def delete(self, using=None, keep_parents=False): @property def is_masquerading(self): - return self.masquerading_user_id > 0 + return self.masquerading_user_id is not None and self.masquerading_user_id > 0 diff --git a/accountsplus/signals.py b/accountsplus/signals.py index b7bf566..5c7db9e 100644 --- a/accountsplus/signals.py +++ b/accountsplus/signals.py @@ -1,5 +1,3 @@ -from __future__ import unicode_literals - import django.contrib.auth.signals from django.dispatch import receiver, Signal from django.conf import settings diff --git a/accountsplus/templatetags/__init__.py b/accountsplus/templatetags/__init__.py index baffc48..e69de29 100644 --- a/accountsplus/templatetags/__init__.py +++ b/accountsplus/templatetags/__init__.py @@ -1 +0,0 @@ -from __future__ import unicode_literals diff --git a/accountsplus/tests/test_admin.py b/accountsplus/tests/test_admin.py index edc1a87..4920b43 100644 --- a/accountsplus/tests/test_admin.py +++ b/accountsplus/tests/test_admin.py @@ -1,5 +1,3 @@ -from __future__ import unicode_literals - import django.test import django.contrib.admin diff --git a/accountsplus/tests/test_models.py b/accountsplus/tests/test_models.py index d9382c3..af1c58e 100644 --- a/accountsplus/tests/test_models.py +++ b/accountsplus/tests/test_models.py @@ -1,5 +1,3 @@ -from __future__ import unicode_literals - import django.test import django.core.mail import django.db.models diff --git a/accountsplus/tests/test_signals.py b/accountsplus/tests/test_signals.py index e23eaf4..5e038fc 100644 --- a/accountsplus/tests/test_signals.py +++ b/accountsplus/tests/test_signals.py @@ -1,5 +1,3 @@ -from __future__ import unicode_literals - import django.test import django.test.utils diff --git a/accountsplus/tests/test_urls.py b/accountsplus/tests/test_urls.py index cc649bd..693824c 100644 --- a/accountsplus/tests/test_urls.py +++ b/accountsplus/tests/test_urls.py @@ -1,5 +1,3 @@ -from __future__ import unicode_literals - import django.test import django.shortcuts import django.conf.urls diff --git a/accountsplus/tests/test_views.py b/accountsplus/tests/test_views.py index b12d2c6..8aa1e2a 100644 --- a/accountsplus/tests/test_views.py +++ b/accountsplus/tests/test_views.py @@ -1,5 +1,3 @@ -from __future__ import unicode_literals - import django.test import django.test.utils import django.test.client diff --git a/accountsplus/urls.py b/accountsplus/urls.py index 56edc40..6cf66f1 100644 --- a/accountsplus/urls.py +++ b/accountsplus/urls.py @@ -1,30 +1,29 @@ -from __future__ import unicode_literals - import django.conf.urls -from django.contrib.auth.urls import url +import django.contrib.auth.urls import django.contrib.auth.views -import views -import forms + +from accountsplus import views +from accountsplus import forms urlpatterns = [ - url(r'^logout/$', views.logout_then_login, name='logout'), - url(r'^password_change/$', views.password_change, name='password_change'), - url(r'^password_reset/$', views.password_reset, name='password_reset'), - url(r'^reset/(?P[0-9A-Za-z_\-]+)/(?P[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})/$', - django.contrib.auth.views.password_reset_confirm, name='password_reset_confirm'), + django.conf.urls.url(r'^logout/$', views.logout_then_login, name='logout'), + django.conf.urls.url(r'^password_change/$', views.password_change, name='password_change'), + django.conf.urls.url(r'^password_reset/$', django.contrib.auth.views.PasswordResetView.as_view(), name='password_reset'), + django.conf.urls.url(r'^reset/(?P[0-9A-Za-z_\-]+)/(?P[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})/$', + django.contrib.auth.views.PasswordResetConfirmView.as_view(), name='password_reset_confirm'), # override the admin password reset flow to use the normal site password # reset flow - url(r'^password_reset/$', views.password_reset, name='admin_password_reset'), + django.conf.urls.url(r'^password_reset/$', views.password_reset, name='admin_password_reset'), - url(r'^login/$', - django.contrib.auth.views.login, + django.conf.urls.url(r'^login/$', + django.contrib.auth.views.LoginView.as_view(), kwargs={'authentication_form': forms.EmailBasedAuthenticationForm, 'redirect_authenticated_user': True}, name='login'), - url(r'^', django.conf.urls.include(django.contrib.auth.urls)), + django.conf.urls.url(r'^', django.conf.urls.include(django.contrib.auth.urls)), # masquerade views - url(r'^admin/masquerade/end/$', views.end_masquerade, name='end_masquerade'), - url(r'^admin/masquerade/(?P\d+)/$', views.masquerade, name='masquerade'), + django.conf.urls.url(r'^admin/masquerade/end/$', views.EndMasqueradeUserView.as_view(), name='end_masquerade'), + django.conf.urls.url(r'^admin/masquerade/(?P\d+)/$', views.MasqueradeUserView.as_view(), name='masquerade'), ] diff --git a/accountsplus/views.py b/accountsplus/views.py index 015afae..4941214 100644 --- a/accountsplus/views.py +++ b/accountsplus/views.py @@ -1,10 +1,10 @@ -from __future__ import unicode_literals import logging from django.utils.translation import ugettext as _ import django.views.decorators.cache import django.views.decorators.csrf import django.views.decorators.debug +import django.views.generic import django.contrib.auth.decorators import django.contrib.auth.views import django.contrib.auth.forms @@ -14,20 +14,24 @@ import django.http import django.template.response import django.utils.module_loading -import django.core.urlresolvers -from django.conf import settings as app_settings +import django.urls +from django.conf import settings +from django.apps import apps from axes import utils -import signals -import forms -import settings +from accountsplus import signals +from accountsplus import forms +from accountsplus import settings as accounts_settings logger = logging.getLogger(__name__) +SESSION_IS_MASQUERADING = 'is_masquerading' +SESSION_MASQUERADE_USER_ID = 'masquerade_user_id' -def logout_then_login(request, login_url=None, extra_context=None): + +def logout_then_login(request, login_url=None): """ Logs out the user if they are logged in. Then redirects to the log-in page. """ @@ -35,89 +39,43 @@ def logout_then_login(request, login_url=None, extra_context=None): if request.session.get('is_masquerading'): return django.shortcuts.redirect('end_masquerade') else: - return django.contrib.auth.views.logout_then_login(request, login_url, extra_context) + return django.contrib.auth.views.logout_then_login(request, login_url) -@django.views.decorators.cache.never_cache -@django.contrib.auth.decorators.login_required -def masquerade(request, user_id=None): - User = django.contrib.auth.get_user_model() +class MasqueradeUserView(django.views.generic.RedirectView): - return_page = request.META.get('HTTP_REFERER') or 'admin:index' - if not user_id: - django.contrib.messages.error(request, 'Masquerade failed: no user specified') - return django.shortcuts.redirect(return_page) - if not request.user.has_perm(User.PERMISSION_MASQUERADE): - django.contrib.messages.error(request, 'Masquerade failed: insufficient privileges') - return django.shortcuts.redirect(return_page) - if not (request.user.is_superuser or request.user.is_staff): - django.contrib.messages.error(request, 'Masquerade failed: must be staff or superuser') - return django.shortcuts.redirect(return_page) - - try: - user = User.objects.get(pk=user_id) - except User.DoesNotExist: - logger.error('User {} ({}) masquerading failed for user {}'.format(request.user.email, request.user.id, user_id)) - django.contrib.messages.error(request, 'Masquerade failed: unknown user {}'.format(user_id)) - return django.shortcuts.redirect(return_page) - - if user.is_superuser: - logger.warning( - 'User {} ({}) cannot masquerade as superuser {} ({})'.format(request.user.email, request.user.id, user.email, user.id)) - django.contrib.messages.warning(request, 'Cannot masquerade as a superuser') - return django.shortcuts.redirect(return_page) - - admin_user = request.user - user.backend = request.session[django.contrib.auth.BACKEND_SESSION_KEY] - # log the new user in - signals.masquerade_start.send(sender=masquerade, request=request, user=admin_user, masquerade_as=user) - # this is needed to track whether this login is for a masquerade - setattr(user, 'is_masquerading', True) - setattr(user, 'masquerading_user', admin_user) - django.contrib.auth.login(request, user) - - request.session['is_masquerading'] = True - request.session['masquerade_user_id'] = admin_user.id - request.session['return_page'] = return_page - request.session['masquerade_is_superuser'] = admin_user.is_superuser - - logger.info( - 'User {} ({}) masquerading as {} ({})'.format(admin_user.email, admin_user.id, request.user.email, request.user.id)) - django.contrib.messages.success(request, 'Masquerading as user {0}'.format(user.email)) - - return django.http.HttpResponseRedirect(app_settings.LOGIN_REDIRECT_URL) - - -@django.views.decorators.cache.never_cache -@django.contrib.auth.decorators.login_required -def end_masquerade(request): - User = django.contrib.auth.get_user_model() - if 'is_masquerading' not in request.session: - return django.shortcuts.redirect('admin:index') + def get(self, request, *args, **kwargs): + admin_user = request.user - if 'masquerade_user_id' in request.session: - try: - masqueraded_user = request.user - user = User.objects.get( - pk=request.session['masquerade_user_id']) - user.backend = request.session[ - django.contrib.auth.BACKEND_SESSION_KEY] - # this is needed to track whether this login is for a masquerade - django.contrib.auth.logout(request) - signals.masquerade_end.send( - sender=end_masquerade, request=request, user=user, - masquerade_as=masqueraded_user) - django.contrib.auth.login(request, user) - logging.info('End masquerade user: {} ({}) by: {} ({})'.format( - masqueraded_user.email, masqueraded_user.id, - user.email, user.id)) - django.contrib.messages.success(request, 'Masquerade ended') - except User.DoesNotExist as e: - logging.critical( - 'Masquerading user {} does not exist'.format( - request.session['masquerade_user_id'])) - - return django.shortcuts.redirect('admin:index') + if not request.user.is_superuser: + django.contrib.messages.error(request, 'Masquerade failed: superuser only can masquerade') + return super(MasqueradeUserView, self).get(request, *args, **kwargs) + + User = apps.get_model(settings.AUTH_USER_MODEL) + user = User.objects.get(pk=kwargs['user_id']) + user.backend = request.session[django.contrib.auth.BACKEND_SESSION_KEY] + django.contrib.auth.login(request, user, backend='django.contrib.auth.backends.ModelBackend') + + request.session[SESSION_IS_MASQUERADING] = True + request.session[SESSION_MASQUERADE_USER_ID] = admin_user.id + return super(MasqueradeUserView, self).get(request, *args, **kwargs) + + def get_redirect_url(self, *args, **kwargs): + return settings.LOGIN_REDIRECT_URL + + +class EndMasqueradeUserView(django.views.generic.RedirectView): + + def get_redirect_url(self, *args, **kwargs): + return django.urls.reverse_lazy('admin:index') + + def get(self, request, *args, **kwargs): + User = apps.get_model(settings.AUTH_USER_MODEL) + user = User.objects.get(pk=request.session[SESSION_MASQUERADE_USER_ID]) + user.backend = request.session[django.contrib.auth.BACKEND_SESSION_KEY] + django.contrib.auth.logout(request) + django.contrib.auth.login(request, user, backend='django.contrib.auth.backends.ModelBackend') + return super(EndMasqueradeUserView, self).get(request, *args, **kwargs) @django.views.decorators.debug.sensitive_post_parameters() @@ -130,7 +88,7 @@ def password_change(request, PasswordChangeForm, current_app=None, extra_context=None): if post_change_redirect is None: - post_change_redirect = django.core.urlresolvers.reverse( + post_change_redirect = django.urls.reverse( 'password_change_done') else: post_change_redirect = django.shortcuts.resolve_url( @@ -173,7 +131,7 @@ def password_reset(request, extra_email_context=None): User = django.contrib.auth.get_user_model() - response = django.contrib.auth.views.password_reset( + response = django.contrib.auth.views.PasswordResetView( request, template_name, email_template_name, subject_template_name, password_reset_form, token_generator, post_reset_redirect, from_email, extra_context, @@ -190,7 +148,7 @@ def password_reset(request, class GenericLockedView(django.views.generic.FormView): - template_name = settings.LOCKOUT_TEMPLATE + template_name = accounts_settings.LOCKOUT_TEMPLATE form_class = forms.CaptchaForm urlPattern = '' diff --git a/requirements.txt b/requirements.txt index 2d1a8fc..0d75a9c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ setuptools -Django==1.10.1 +Django==2.2.1 flake8 pep8 pyflakes diff --git a/settings_test.py b/settings_test.py index 23e747a..d1721e0 100644 --- a/settings_test.py +++ b/settings_test.py @@ -1,4 +1,3 @@ -from __future__ import unicode_literals import os diff --git a/urls.py b/urls.py index b030bcb..22f88cc 100644 --- a/urls.py +++ b/urls.py @@ -1,5 +1,3 @@ -from __future__ import unicode_literals - import django.conf.urls import django.contrib.admin