feat: 平台认证(scrypt上移/内存token 8h/登录限流/改密/角色依赖/409语义)

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
浅念
2026-10-13 08:18:15 +08:00
parent bfcaba5c87
commit f49e8e92b1
8 changed files with 626 additions and 71 deletions
+11 -11
View File
@@ -70,8 +70,8 @@ async def test_bootstrap_creates_superuser_once(tmp_path):
assert len(users) == 1 and users[0].username == "admin"
assert users[0].tenant_id is None # NULL = 超管
assert users[0].must_change_password is True # 引导密码必须首登改掉
assert verify_password("s3cret!", users[0])
assert not verify_password("other", users[0])
assert verify_password("s3cret!", users[0].password_hash, users[0].salt)
assert not verify_password("other", users[0].password_hash, users[0].salt)
finally:
await engine.dispose()
@@ -85,7 +85,7 @@ async def test_bootstrap_noop_when_any_user_exists(tmp_path):
await bootstrap_superuser(engine, username="root", password="second")
users = await list_admin_users(engine)
assert len(users) == 1 and users[0].username == "admin"
assert verify_password("first", users[0])
assert verify_password("first", users[0].password_hash, users[0].salt)
finally:
await engine.dispose()
@@ -128,23 +128,23 @@ def test_hash_password_versioned_format_and_fresh_salt():
def test_verify_password_bad_stored_hash_returns_false():
"""存储异常按验败处理不炸(M-4):坏 hex/坏段数/坏参数 → False;
无前缀过渡行兼容:salt 列 + 默认参数仍可验真(M-1 解析兼容)。"""
无前缀过渡行兼容:salt 列 + 默认参数仍可验真(M-1 解析兼容)。
Task 2 起 verify 纯参式 (password, password_hash, salt)——不再需要
ORM 实体即可直测。"""
bad_stored = [
"not-hex!", # 无前缀,坏 hex
"scrypt$16384$8$1$deadbeef", # 段数不足
"scrypt$abc$8$1$00$00", # 参数非整数
]
for stored in bad_stored:
user = AdminUser(username="u", password_hash=stored, salt="zz")
assert verify_password("whatever", user) is False
assert verify_password("whatever", stored, "zz") is False
salt = bytes(16)
digest = hashlib.scrypt( # 裸摘要 + salt 列的过渡行形态
b"legacy", salt=salt, n=2 ** 14, r=8, p=1)
legacy = AdminUser(
username="u", password_hash=digest.hex(), salt=salt.hex())
assert verify_password("legacy", legacy) is True
assert verify_password("other", legacy) is False
legacy_hash, legacy_salt = digest.hex(), salt.hex()
assert verify_password("legacy", legacy_hash, legacy_salt) is True
assert verify_password("other", legacy_hash, legacy_salt) is False
# ---- 模型约束 -------------------------------------------------------------
@@ -323,5 +323,5 @@ def test_lifespan_platform_engine_seam_and_bootstrap(tmp_path, monkeypatch):
users = asyncio.run(_probe())
assert [u.username for u in users] == ["bootadmin"]
assert verify_password("boot-pw", users[0])
assert verify_password("boot-pw", users[0].password_hash, users[0].salt)
assert users[0].must_change_password is True