import base64 import json import os from django.http import JsonResponse, HttpResponseBadRequest from django.shortcuts import render, redirect from django.views.decorators.http import require_http_methods from django.views.decorators.csrf import csrf_protect, ensure_csrf_cookie from django.conf import settings from .es_client import get_user_by_username from .crypto import get_public_key_spki_b64, rsa_oaep_decrypt_b64, aes_gcm_decrypt_b64, verify_password @require_http_methods(["GET"]) @ensure_csrf_cookie def login_page(request): return render(request, "accounts/login.html") @require_http_methods(["GET"]) @ensure_csrf_cookie def pubkey(request): pk_b64 = get_public_key_spki_b64() return JsonResponse({"public_key_spki": pk_b64}) @require_http_methods(["POST"]) @csrf_protect def set_session_key(request): try: payload = json.loads(request.body.decode("utf-8")) except json.JSONDecodeError: return HttpResponseBadRequest("Invalid JSON") enc_key_b64 = payload.get("encrypted_key", "") if not enc_key_b64: return HttpResponseBadRequest("Missing fields") try: key_bytes = rsa_oaep_decrypt_b64(enc_key_b64) except Exception: return HttpResponseBadRequest("Decrypt error") request.session["session_enc_key_b64"] = base64.b64encode(key_bytes).decode("ascii") return JsonResponse({"ok": True}) @require_http_methods(["POST"]) @csrf_protect def secure_login_submit(request): try: payload = json.loads(request.body.decode("utf-8")) except json.JSONDecodeError: return HttpResponseBadRequest("Invalid JSON") iv_b64 = payload.get("iv", "") ct_b64 = payload.get("ciphertext", "") if not iv_b64 or not ct_b64: return HttpResponseBadRequest("Missing fields") key_b64 = request.session.get("session_enc_key_b64") if not key_b64: return HttpResponseBadRequest("Session key missing") try: key_bytes = base64.b64decode(key_b64) pt = aes_gcm_decrypt_b64(key_bytes, iv_b64, ct_b64) obj = json.loads(pt.decode("utf-8")) except Exception: return HttpResponseBadRequest("Decrypt error") username = (obj.get("username") or "").strip() password = (obj.get("password") or "") if not username or not password: return HttpResponseBadRequest("Missing credentials") user = get_user_by_username(username) if not user: return JsonResponse({"ok": False, "message": "User not found"}, status=401) if not verify_password(password, user.get("password_salt") or "", user.get("password_hash") or ""): return JsonResponse({"ok": False, "message": "Invalid credentials"}, status=401) try: request.session.cycle_key() except Exception: pass request.session["user_id"] = user["user_id"] request.session["username"] = user["username"] try: request.session["permission"] = int(user["permission"]) if user.get("permission") is not None else 1 except Exception: request.session["permission"] = 1 if "session_enc_key_b64" in request.session: del request.session["session_enc_key_b64"] return JsonResponse({"ok": True, "redirect_url": f"/main/home/?user_id={user['user_id']}"}) @require_http_methods(["GET"]) def home(request): # Minimal placeholder page per requirement # Ensure user_id is passed via query and session contains id user_id = request.GET.get("user_id") session_user_id = request.session.get("user_id") context = { "user_id": user_id or session_user_id, } return render(request, "accounts/home.html", context) @require_http_methods(["POST"]) @csrf_protect def logout(request): # Flush the session to clear all data and rotate the key try: request.session.flush() except Exception: pass # Return a response that also deletes cookies client-side resp = JsonResponse({"ok": True, "redirect_url": "/accounts/login/"}) try: # Delete session cookie resp.delete_cookie( settings.SESSION_COOKIE_NAME, path='/', samesite=settings.SESSION_COOKIE_SAMESITE, secure=settings.SESSION_COOKIE_SECURE, ) # Optionally delete CSRF cookie to satisfy "清除cookie" 的要求 resp.delete_cookie( settings.CSRF_COOKIE_NAME, path='/', samesite=settings.CSRF_COOKIE_SAMESITE, secure=settings.CSRF_COOKIE_SECURE, ) except Exception: pass return resp