Successful logins, user creation and deletion, password changes and the enabled and admin toggles are logged with actor, target and IP. So are failed logins, a wrong current password and denied access. Values are JSON encoded, so client input cannot break the line or add fields. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
66 lines
2.0 KiB
Python
66 lines
2.0 KiB
Python
from flask import flash, session, url_for
|
|
|
|
from max.audit import audit
|
|
from max.csrf import no_csrf
|
|
from max.permissions import AllowAnyAccess
|
|
from max.translations import t
|
|
from max.views import BaseTemplateGetView, BaseView
|
|
|
|
from .login import login, logout
|
|
from .middlewares import no_authentication_required
|
|
|
|
# Messages for the login page (text, category), selected by ?msg=<key>.
|
|
LOGIN_MESSAGES = {
|
|
"login_required": ("Please log in to visit the requested URL", "error"),
|
|
"logout": ("Logout was successful!", "success"),
|
|
}
|
|
|
|
|
|
def init_routes(app):
|
|
app.add_url_rule(
|
|
"/login", "login", no_csrf(no_authentication_required(Login.as_view()))
|
|
)
|
|
app.add_url_rule("/logout", "logout", Logout.as_view())
|
|
|
|
|
|
class Login(AllowAnyAccess, BaseTemplateGetView):
|
|
template_name = "auth/login.html"
|
|
|
|
def get(self):
|
|
if session.get("user_id") is not None:
|
|
return self.redirect("/")
|
|
return super().get()
|
|
|
|
def get_context(self):
|
|
context = super().get_context()
|
|
if "message" not in context:
|
|
message = LOGIN_MESSAGES.get(self.request.args.get("msg"))
|
|
if message is not None:
|
|
context["message"] = t(message[0])
|
|
context["message_category"] = message[1]
|
|
return context
|
|
|
|
def get_webpagetitle(self):
|
|
return t("Login")
|
|
|
|
def post(self):
|
|
email = self.request.form.get("email")
|
|
password = self.request.form.get("password")
|
|
|
|
error, user = login(email, password)
|
|
if error:
|
|
audit("login.failed", attempted_email=email)
|
|
self.context["message"] = error
|
|
self.context["message_category"] = "error"
|
|
self.context["email"] = email
|
|
else:
|
|
audit("login", actor=user)
|
|
flash(t("Login was successful!"), category="success")
|
|
return self.redirect("/")
|
|
|
|
|
|
class Logout(AllowAnyAccess, BaseView):
|
|
def post(self):
|
|
logout()
|
|
return self.redirect(url_for("login", msg="logout"))
|