feat: harden auth and study workflows

- replace plaintext login and unlock requests with RSA-OAEP/AES-GCM encrypted payloads

- add login challenge replay protection, production RSA key validation, and auth tests

- wire compose to environment-driven dev/prod settings without committing local secrets

- update setup-config smoke scripts and Postman docs for encrypted login

- add visit schedule migrations/tests and update study/subject setup workflows
This commit is contained in:
Cheng Zhou
2026-05-08 22:13:12 +08:00
parent a7bbcaa5dc
commit 74feca4467
47 changed files with 2423 additions and 534 deletions
+39
View File
@@ -0,0 +1,39 @@
import pytest
from app.core import login_crypto
from app.core.config import settings
from app.main import create_app
def reset_login_crypto_state():
login_crypto._private_key = None
login_crypto._public_key_pem = None
login_crypto._challenges.clear()
def test_production_requires_configured_login_private_key(monkeypatch):
reset_login_crypto_state()
monkeypatch.setattr(settings, "ENV", "production")
monkeypatch.setattr(settings, "LOGIN_RSA_PRIVATE_KEY", None)
with pytest.raises(ValueError, match="生产环境必须配置 LOGIN_RSA_PRIVATE_KEY"):
create_app()
reset_login_crypto_state()
def test_login_challenge_cache_has_max_active_limit(monkeypatch):
reset_login_crypto_state()
monkeypatch.setattr(settings, "ENV", "test")
monkeypatch.setattr(settings, "LOGIN_CHALLENGE_MAX_ACTIVE", 2)
first = login_crypto.create_login_challenge()
second = login_crypto.create_login_challenge()
third = login_crypto.create_login_challenge()
assert len(login_crypto._challenges) == 2
assert not login_crypto.consume_login_challenge(first.value)
assert login_crypto.consume_login_challenge(second.value)
assert login_crypto.consume_login_challenge(third.value)
reset_login_crypto_state()