71 lines
2.8 KiB
Python
71 lines
2.8 KiB
Python
# retoor <retoor@molodetz.nl>
|
|
from fastapi import APIRouter, Request
|
|
|
|
from molodetz.database import create_session, end_session, get_int_setting, get_user_by_email, get_user_by_username
|
|
from molodetz.dependencies import json_or_form
|
|
from molodetz.models import LoginForm
|
|
from molodetz.responses import action_result, json_error, respond, wants_json
|
|
from molodetz.schemas.account import LoginPageOut
|
|
from molodetz.seo import base_seo_context
|
|
from molodetz.templating import templates
|
|
from molodetz.utils.audit import record
|
|
from molodetz.utils.auth import get_current_user, invalidate_user_cache, is_account_active, verify_password_async
|
|
from molodetz.utils.request_meta import is_secure_request, safe_next
|
|
|
|
router = APIRouter()
|
|
|
|
|
|
def _seo(request):
|
|
return base_seo_context(request, "Log in", robots="noindex,nofollow")
|
|
|
|
|
|
@router.get("/login")
|
|
async def login_page(request: Request):
|
|
next_url = safe_next(request.query_params.get("next"), "/admin")
|
|
return respond(request, "auth/login.html", {"errors": [], "form": {}, "next": next_url, "seo": _seo(request)}, model=LoginPageOut)
|
|
|
|
|
|
@router.post("/login")
|
|
async def login_submit(request: Request):
|
|
data = await json_or_form(request, LoginForm)
|
|
user = get_user_by_username(data.username) or get_user_by_email(data.username.lower())
|
|
valid = user is not None and is_account_active(user) and await verify_password_async(data.password, user.get("password_hash"))
|
|
if not valid:
|
|
record(request, "auth.login.failed", result="denied", payload={"username": data.username[:64]})
|
|
ctx = {
|
|
"errors": ["Unknown combination of name and password."],
|
|
"form": {"username": data.username},
|
|
"next": data.next,
|
|
"seo": _seo(request),
|
|
}
|
|
if wants_json(request):
|
|
return json_error(401, "Invalid credentials")
|
|
return templates.TemplateResponse(request, "auth/login.html", ctx, status_code=400)
|
|
days = get_int_setting("session_remember_days", 30) if data.remember else get_int_setting("session_max_age_days", 7)
|
|
max_age = max(1, days) * 86400
|
|
token = create_session(user["uid"], max_age)
|
|
record(request, "auth.login", targets=[("user", user["uid"])], user=user)
|
|
response = action_result(request, safe_next(data.next, "/admin"))
|
|
response.set_cookie(
|
|
key="session",
|
|
value=token,
|
|
max_age=max_age,
|
|
httponly=True,
|
|
samesite="lax",
|
|
secure=is_secure_request(request),
|
|
)
|
|
return response
|
|
|
|
|
|
@router.post("/logout")
|
|
async def logout(request: Request):
|
|
user = get_current_user(request)
|
|
token = request.cookies.get("session")
|
|
if token:
|
|
end_session(token, user["uid"] if user else "guest")
|
|
invalidate_user_cache(propagate=True)
|
|
record(request, "auth.logout", user=user)
|
|
response = action_result(request, "/")
|
|
response.delete_cookie("session")
|
|
return response
|