# -*- coding: utf-8 -*- import logging import secrets from django.apps import apps from django.core.exceptions import ValidationError from django.contrib import messages from django.contrib.auth import views as auth_views, get_user_model from django.contrib.auth.password_validation import validate_password from django.http import HttpResponseRedirect from django.shortcuts import resolve_url from django.template.loader import render_to_string from django.urls import reverse_lazy, reverse from django.utils.safestring import mark_safe from django.utils.translation import gettext as _ from django.views import generic from . import emails from . import forms app_config = apps.get_containing_app_config(__package__) logger = logging.getLogger(__name__) class LoginView(auth_views.LoginView): form_class = forms.LoginForm template_name = 'dav_auth/forms/login.html' weak_password_warning_template_name = 'dav_auth/includes/weak_password_warning.html' def get_redirect_url(self): url = super().get_redirect_url() if not url and app_config.settings.login_redirect_url: url = resolve_url(app_config.settings.login_redirect_url) return url def form_valid(self, form): r = super().form_valid(form) messages.success(self.request, _('Benutzer angemeldet: %(username)s') % {'username': form.get_user()}) try: validate_password(form.cleaned_data['password']) except ValidationError as e: logger.warning('Detected weak password for user id %d: %s', self.request.user.pk, e) message = render_to_string(self.weak_password_warning_template_name) messages.warning(self.request, mark_safe(message)) return r class LogoutView(auth_views.LogoutView): @property def next_page(self): url = None if app_config.settings.logout_redirect_url: url = resolve_url(app_config.settings.logout_redirect_url) return url def dispatch(self, request, *args, **kwargs): r = super().dispatch(request, *args, **kwargs) messages.success(self.request, _('Benutzer abgemeldet.')) return r class SetPasswordView(auth_views.PasswordChangeView): form_class = forms.SetPasswordForm template_name = 'dav_auth/forms/set_password.html' def get_success_url(self): return resolve_url(app_config.settings.login_redirect_url) def form_valid(self, form): r = super().form_valid(form) messages.success(self.request, _('Passwort gespeichert.')) logger.info('Changed password for user \'%s\'', self.request.user) if form.cleaned_data.get('send_password_mail', False): email = emails.PasswordSetEmail(self.request.user, form.cleaned_data['new_password']) email.send() return r class CreateAndSendPasswordView(generic.FormView): form_class = forms.CreateAndSendPasswordForm template_name = 'dav_auth/forms/recreate_password.html' success_url = reverse_lazy('dav_auth:login') password_length = app_config.settings.auto_password_length password_chars = app_config.settings.auto_password_characters def _create_new_password(self, length=None, characters=None): if length is None: length = self.password_length if characters is None: characters = self.password_chars return ''.join(secrets.choice(characters) for i in range(length)) def form_valid(self, form): username = form.cleaned_data.get('username') user_model = get_user_model() # Generate a new password (even if the user does not exist, to avoid revealing that fact). random_password = self._create_new_password() try: user = user_model.objects.get(username=username) user.set_password(random_password) user.save() email = emails.PasswordSetEmail(user, random_password) email.send() logger.info('Recreated password for user \'%s\'', username) except user_model.DoesNotExist: logger.warning('Recreated password for unknown user \'%s\'', username) # Show message, that we sent an email, even we did not, so we do not reveal that the user doesn't exist. messages.success(self.request, _('Neues Passwort versendet.')) return super().form_valid(form) def get(self, request, *args, **kwargs): if request.user.is_authenticated: return HttpResponseRedirect(reverse('dav_auth:set_password')) return super().get(request, *args, **kwargs)