支持开发环境局域网登录测试

This commit is contained in:
Cheng Zhou
2026-05-29 09:36:07 +08:00
parent c9b677e96a
commit 63457aab11
10 changed files with 253 additions and 19 deletions
+40 -5
View File
@@ -22,6 +22,11 @@ class LoginRequest(BaseModel):
ciphertext: str = Field(min_length=1)
class DevLoginRequest(BaseModel):
email: str = Field(min_length=1)
password: str = Field(min_length=1)
class LoginKeyResponse(BaseModel):
key_id: str
public_key: str
@@ -84,6 +89,29 @@ async def authenticate_encrypted_password(payload: LoginRequest, db: AsyncSessio
return db_user
async def authenticate_plain_password(payload: DevLoginRequest, db: AsyncSession):
db_user = await user_crud.get_by_email(db, payload.email)
if not db_user:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="账号不存在",
)
if not verify_password(payload.password, db_user.password_hash):
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="密码错误",
)
return db_user
def ensure_user_active(db_user) -> None:
if db_user.status != UserStatus.ACTIVE:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="账号未审核或不可用",
)
@router.post("/register", status_code=status.HTTP_201_CREATED)
async def register(
payload: UserRegisterRequest,
@@ -112,15 +140,22 @@ async def login_for_access_token(
payload: LoginRequest, db: AsyncSession = Depends(get_db_session)
) -> Token:
db_user = await authenticate_encrypted_password(payload, db)
if db_user.status != UserStatus.ACTIVE:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="账号未审核或不可用",
)
ensure_user_active(db_user)
return issue_user_token(db_user)
@router.post("/dev-login", response_model=Token)
async def dev_login_for_access_token(
payload: DevLoginRequest, db: AsyncSession = Depends(get_db_session)
) -> Token:
if settings.ENV != "development":
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Not found")
db_user = await authenticate_plain_password(payload, db)
ensure_user_active(db_user)
return issue_user_token(db_user)
@router.get("/me", response_model=UserRead)
async def read_me(current_user=Depends(get_current_user)) -> UserRead:
return current_user
+53
View File
@@ -12,6 +12,7 @@ import json
import os
from app.main import create_app
from app.core.config import settings
from app.core.deps import get_db_session
from app.core.security import hash_password
from app.crud import user as user_crud
@@ -200,6 +201,58 @@ async def test_plaintext_login_is_rejected(client_and_db):
assert resp.status_code == 422
@pytest.mark.asyncio
async def test_dev_login_allows_plaintext_only_in_development(client_and_db):
client, _ = client_and_db
original_env = settings.ENV
settings.ENV = "development"
try:
resp = await client.post("/api/v1/auth/dev-login", json={"email": "admin@test.com", "password": "admin123"})
finally:
settings.ENV = original_env
assert resp.status_code == 200
assert resp.json()["token_type"] == "bearer"
assert resp.json()["access_token"]
@pytest.mark.asyncio
async def test_dev_login_is_disabled_outside_development(client_and_db):
client, _ = client_and_db
original_env = settings.ENV
settings.ENV = "production"
try:
resp = await client.post("/api/v1/auth/dev-login", json={"email": "admin@test.com", "password": "admin123"})
finally:
settings.ENV = original_env
assert resp.status_code == 404
@pytest.mark.asyncio
async def test_dev_login_rejects_inactive_users(client_and_db):
client, SessionLocal = client_and_db
original_env = settings.ENV
settings.ENV = "development"
payload = {
"email": "pending-dev-login@test.com",
"password": "Password123",
"full_name": "Pending Dev Login",
"clinical_department": "Clinical",
}
try:
await client.post("/api/v1/auth/register", json=payload)
resp = await client.post(
"/api/v1/auth/dev-login",
json={"email": payload["email"], "password": payload["password"]},
)
finally:
settings.ENV = original_env
assert resp.status_code == 401
assert "账号未审核" in resp.json().get("detail", "")
@pytest.mark.asyncio
async def test_login_challenge_cannot_be_reused(client_and_db):
client, _ = client_and_db