# Copyright © 2022 Aravinth Manivannan # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as # published by the Free Software Foundation, either version 3 of the # License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . from django.shortcuts import render, redirect, get_object_or_404 from django.contrib.auth.password_validation import validate_password from django.core.exceptions import ValidationError from django.utils.http import urlencode from django.contrib.auth import authenticate, login, logout from django.contrib.auth import get_user_model from django.contrib.auth.decorators import login_required from django.http import HttpResponse from django.views.decorators.csrf import csrf_protect from django.urls import reverse from .models import AccountConfirmChallenge from .utils import send_verification_email, ConfirmAccess from .decorators import redirect_if_authenticated @redirect_if_authenticated @csrf_protect def login_view(request): def default_login_ctx(): return { "title": "Login", } if request.method == "GET": ctx = default_login_ctx() if "next" in request.GET: ctx["next"] = request.GET["next"] return render(request, "accounts/auth/login.html", ctx) login_cred = request.POST["login"] user = None if "@" in login_cred: user = get_user_model().objects.get(email=login_cred) if user is not None: user = authenticate( username=user.username, password=request.POST["password"], ) else: user = authenticate( username=login_cred, password=request.POST["password"], ) if user is not None: login(request, user) if "next" in request.POST: next_url = request.POST["next"] if all([next_url, len(next_url) > 0]): return redirect(next_url) return redirect(reverse("accounts.home")) ctx = default_login_ctx() ctx["error"] = { "title": "Login Failed", "reason": "Username or passwrod is incorrect, please try again.", } return render(request, "accounts/auth/login.html", status=401, context=ctx) @login_required def protected_view(request): return redirect(reverse("dash.home")) @redirect_if_authenticated def default_login_url(request): if "next" in request.GET: ctx = {"next": request.GET["next"]} return redirect(f"{reverse('accounts.login')}?{urlencode(ctx)}") return redirect(reverse("accounts.login")) @login_required def logout_view(request): logout(request) return redirect(reverse("accounts.login")) @redirect_if_authenticated @csrf_protect def register_view(request): def default_register_ctx(username=None, email=None): return { "title": "Register", "username": username, "email": username, } if request.method == "GET": ctx = default_register_ctx() if "next" in request.GET: ctx["next"] = request.GET["next"] return render(request, "accounts/auth/register.html", ctx) confirm_password = request.POST["confirm_password"] password = request.POST["password"] username = request.POST["username"] email = request.POST["email"] if password != confirm_password: ctx = default_register_ctx(username=username, email=email) ctx["error"] = { "title": "Registration Failed", "reason": "Passwords don't match, please verify input", } return render(request, "accounts/auth/register.html", status=400, context=ctx) User = get_user_model() if User.objects.filter(email=email).exists(): ctx = default_register_ctx(username=username, email=email) ctx["error"] = { "title": "Registration Failed", "reason": "Email is already registered", } return render(request, "accounts/auth/register.html", status=400, context=ctx) if User.objects.filter(username=username).exists(): ctx = default_register_ctx(username=username, email=email) ctx["error"] = { "title": "Registration Failed", "reason": "Username is already registered", } return render(request, "accounts/auth/register.html", status=400, context=ctx) user = get_user_model()( username=username, email=email, is_active=False, ) # TODO: get email from settings.py user.set_password(password) try: user.full_clean() validate_password(password, user=user) except ValidationError as err: ctx = default_register_ctx(username=username, email=email) reason = "" for r in err: reason += r + " " ctx["error"] = {"title": "Registration Failed", "reason": reason} return render(request, "accounts/auth/register.html", status=400, context=ctx) user.is_active = False user.save() challenge = None if not AccountConfirmChallenge.objects.filter(owned_by=user).exists(): challenge = AccountConfirmChallenge(owned_by=user) challenge.save() send_verification_email(request, challenge=challenge) else: challenge = AccountConfirmChallenge.objects.get(owned_by=user) return redirect(challenge.pending_url()) @redirect_if_authenticated def verification_pending_view(request, public_ref): challenge = get_object_or_404(AccountConfirmChallenge, public_ref=public_ref) ctx = { "email": challenge.owned_by.email, "public_ref": challenge.public_ref, } return render(request, "accounts/auth/verification-pending.html", context=ctx) @redirect_if_authenticated def resend_verification_email_view(request, public_ref): challenge = get_object_or_404(AccountConfirmChallenge, public_ref=public_ref) send_verification_email(request, challenge=challenge) return redirect(challenge.pending_url()) def verify_account(request, challenge): challenge = get_object_or_404(AccountConfirmChallenge, challenge_text=challenge) if request.method == "GET": ctx = { "challenge": challenge, } return render(request, "accounts/auth/verify.html", context=ctx) challenge.owned_by.is_active = True challenge.owned_by.save() challenge.delete() return redirect("accounts.login") @login_required @csrf_protect def sudo(request): def default_login_ctx(): return { "title": "Confirm Access", } if request.method == "GET": ctx = default_login_ctx() ctx["next"] = request.GET["next"] return render(request, "accounts/auth/sudo.html", ctx) password = request.POST["password"] user = request.user user = authenticate( username=user.username, password=request.POST["password"], ) if user is None: ctx = default_login_ctx() ctx["next"] = request.POST["next"] ctx["error"] = { "title": "Wrong Password", "reason": "Password is incorrect, please try again.", } return render(request, "accounts/auth/sudo.html", status=401, context=ctx) ConfirmAccess.set(request=request) return redirect(request.POST["next"])