Release v2.1: GDPR, passkeys, session management, admin redesign
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -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
|
||||
Reference in New Issue
Block a user