Server IP : 85.214.239.14 / Your IP : 3.133.155.48 Web Server : Apache/2.4.62 (Debian) System : Linux h2886529.stratoserver.net 4.9.0 #1 SMP Tue Jan 9 19:45:01 MSK 2024 x86_64 User : www-data ( 33) PHP Version : 7.4.18 Disable Function : pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,pcntl_unshare, MySQL : OFF | cURL : OFF | WGET : ON | Perl : ON | Python : ON | Sudo : ON | Pkexec : OFF Directory : /proc/3/cwd/proc/3/cwd/srv/modoboa/env/lib64/python3.5/site-packages/modoboa/core/views/ |
Upload File : |
"""Core authentication views.""" import logging import oath from django.http import HttpResponse, HttpResponseRedirect, Http404 from django.template.loader import render_to_string from django.urls import reverse from django.utils import translation from django.utils.encoding import force_bytes from django.utils.http import urlsafe_base64_encode from django.utils.translation import ugettext as _ from django.views import generic from django.views.decorators.cache import never_cache from django.contrib.auth import ( authenticate, login, logout, views as auth_views ) from django.contrib.auth.tokens import default_token_generator from braces.views import JSONResponseMixin from modoboa.core import forms from modoboa.core.password_hashers import get_password_hasher from modoboa.lib import cryptutils from modoboa.parameters import tools as param_tools from .. import models from .. import sms_backends from .. import signals from .base import find_nextlocation logger = logging.getLogger("modoboa.auth") def dologin(request): """Try to authenticate.""" error = None if request.method == "POST": form = forms.LoginForm(request.POST) if form.is_valid(): logger = logging.getLogger("modoboa.auth") user = authenticate(username=form.cleaned_data["username"], password=form.cleaned_data["password"]) if user and user.is_active: if param_tools.get_global_parameter("update_scheme", raise_exception=False): # check if password scheme is correct scheme = param_tools.get_global_parameter( "password_scheme", raise_exception=False) # use SHA512CRYPT as default fallback if scheme is None: pwhash = get_password_hasher('sha512crypt')() else: pwhash = get_password_hasher(scheme)() if not user.password.startswith(pwhash.scheme): logging.info( _("Password scheme mismatch. Updating %s password"), user.username ) user.set_password(form.cleaned_data["password"]) user.save() if pwhash.needs_rehash(user.password): logging.info( _("Password hash parameter missmatch. " "Updating %s password"), user.username ) user.set_password(form.cleaned_data["password"]) user.save() login(request, user) if not form.cleaned_data["rememberme"]: request.session.set_expiry(0) translation.activate(request.user.language) request.session[translation.LANGUAGE_SESSION_KEY] = ( request.user.language) logger.info( _("User '%s' successfully logged in") % user.username ) signals.user_login.send( sender="dologin", username=form.cleaned_data["username"], password=form.cleaned_data["password"]) return HttpResponseRedirect(find_nextlocation(request, user)) error = _( "Your username and password didn't match. Please try again.") logger.warning( "Failed connection attempt from '%(addr)s' as user '%(user)s'" % {"addr": request.META["REMOTE_ADDR"], "user": form.cleaned_data["username"]} ) nextlocation = request.POST.get("next", "") httpcode = 401 else: form = forms.LoginForm() nextlocation = request.GET.get("next", "") httpcode = 200 announcements = signals.get_announcements.send( sender="login", location="loginpage") announcements = [announcement[1] for announcement in announcements] return HttpResponse( render_to_string( "registration/login.html", { "form": form, "error": error, "next": nextlocation, "annoucements": announcements}, request), status=httpcode) dologin = never_cache(dologin) def dologout(request): """Logout current user.""" if not request.user.is_anonymous: signals.user_logout.send(sender="dologout", request=request) logger = logging.getLogger("modoboa.auth") logger.info( _("User '{}' successfully logged out").format( request.user.username)) logout(request) return HttpResponseRedirect(reverse("core:login")) class PasswordResetView(auth_views.PasswordResetView): """Custom view to override form.""" form_class = forms.PasswordResetForm def setup(self, request, *args, **kwargs): super().setup(request, *args, **kwargs) self.from_email = request.localconfig.parameters.get_value( "sender_address" ) def get_context_data(self, **kwargs): """Include help text.""" context = super().get_context_data(**kwargs) context["announcement"] = ( self.request.localconfig.parameters .get_value("password_recovery_msg") ) return context def form_valid(self, form): """Redirect to code verification page if needed.""" sms_password_recovery = ( self.request.localconfig.parameters .get_value("sms_password_recovery") ) if not sms_password_recovery: return super().form_valid(form) user = models.User._default_manager.filter( email=form.cleaned_data["email"], phone_number__isnull=False ).first() if not user: # Fallback to email return super().form_valid(form) backend = sms_backends.get_active_backend( self.request.localconfig.parameters) secret = cryptutils.random_hex_key(20) code = oath.totp(secret) text = _( "Please use the following code to recover your Modoboa password: {}" .format(code) ) if not backend.send(text, [str(user.phone_number)]): return super().form_valid(form) self.request.session["user_pk"] = user.pk self.request.session["totp_secret"] = secret return HttpResponseRedirect(reverse("password_reset_confirm_code")) class VerifySMSCodeView(generic.FormView): """View to verify a code received by SMS.""" form_class = forms.VerifySMSCodeForm template_name = "registration/password_reset_confirm_code.html" def get_form_kwargs(self): """Include totp secret in kwargs.""" kwargs = super().get_form_kwargs() try: kwargs.update({"totp_secret": self.request.session["totp_secret"]}) except KeyError: raise Http404 return kwargs def form_valid(self, form): """Redirect to reset password form.""" user = models.User.objects.get(pk=self.request.session.pop("user_pk")) self.request.session.pop("totp_secret") token = default_token_generator.make_token(user) uid = urlsafe_base64_encode(force_bytes(user.pk)) url = reverse("password_reset_confirm", args=[uid, token]) return HttpResponseRedirect(url) class ResendSMSCodeView(JSONResponseMixin, generic.View): """A view to resend validation code.""" def get(self, request, *args, **kwargs): sms_password_recovery = ( self.request.localconfig.parameters .get_value("sms_password_recovery") ) if not sms_password_recovery: raise Http404 try: user = models.User._default_manager.get( pk=self.request.session["user_pk"]) except KeyError: raise Http404 backend = sms_backends.get_active_backend( self.request.localconfig.parameters) secret = cryptutils.random_hex_key(20) code = oath.totp(secret) text = _( "Please use the following code to recover your Modoboa password: {}" .format(code) ) if not backend.send(text, [user.phone_number]): raise Http404 self.request.session["totp_secret"] = secret return self.render_json_response({"status": "ok"})