Release v2.1: GDPR, passkeys, session management, admin redesign

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-07 02:43:57 +03:00
parent d4f0eaa7d9
commit 0a51001791
32 changed files with 1529 additions and 193 deletions
+83
View File
@@ -0,0 +1,83 @@
import json
from flask import Blueprint, flash, jsonify, redirect, request, url_for
from flask_login import current_user, login_required, login_user
from app import db
from app.passkey_service import (
authentication_options,
delete_passkey,
registration_options,
verify_authentication,
verify_registration,
)
from webauthn.helpers.options_to_json import options_to_json
bp = Blueprint("passkey", __name__, url_prefix="/auth/passkey")
@bp.route("/register/options", methods=["POST"])
@login_required
def register_options():
options = registration_options(current_user)
return jsonify(json.loads(options_to_json(options)))
@bp.route("/register/verify", methods=["POST"])
@login_required
def register_verify():
data = request.get_json(silent=True) or {}
name = data.get("name", "Passkey")
credential = data.get("credential")
if not credential:
return jsonify({"error": "Нет данных passkey"}), 400
try:
passkey = verify_registration(current_user, credential, name)
return jsonify({"ok": True, "id": passkey.id, "name": passkey.name})
except Exception as exc:
return jsonify({"error": str(exc)}), 400
@bp.route("/login/options", methods=["POST"])
def login_options():
from app.models import User
username = (request.get_json(silent=True) or {}).get("username", "").strip()
if not username:
return jsonify({"error": "Укажите логин или email"}), 400
user = User.query.filter(
(User.username == username) | (User.email == username.lower())
).first()
if not user or not user.is_active:
return jsonify({"error": "Passkey не найден для этого аккаунта"}), 404
try:
options = authentication_options(user)
return jsonify(json.loads(options_to_json(options)))
except ValueError as exc:
return jsonify({"error": str(exc)}), 400
@bp.route("/login/verify", methods=["POST"])
def login_verify():
from app.folder_utils import process_pending_invites
from app.session_service import create_user_session
data = request.get_json(silent=True) or {}
credential = data.get("credential")
remember = bool(data.get("remember"))
if not credential:
return jsonify({"error": "Нет данных passkey"}), 400
try:
user = verify_authentication(credential)
if not user.is_active:
return jsonify({"error": "Аккаунт заблокирован"}), 403
login_user(user, remember=remember)
create_user_session(user, remember=remember)
process_pending_invites(user)
redirect_url = url_for("admin.dashboard") if user.is_admin else url_for("cabinet.index")
return jsonify({"ok": True, "redirect": redirect_url})
except Exception as exc:
return jsonify({"error": str(exc)}), 400