feat(auth): add opaque bearer sessions
This commit is contained in:
@@ -1,13 +1,17 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from sqlalchemy import create_engine
|
||||
from __future__ import annotations
|
||||
|
||||
import pytest
|
||||
from sqlalchemy import create_engine
|
||||
from sqlalchemy.orm import Session, sessionmaker
|
||||
from sqlalchemy.pool import StaticPool
|
||||
|
||||
from app.db.base import Base
|
||||
from app.db.base import Base
|
||||
from app.models.auth_session import AuthSession
|
||||
from app.models.user_session_metric import UserSessionMetric
|
||||
from app.schemas.auth import LoginRequest
|
||||
from app.schemas.settings import SettingsWrite
|
||||
from app.services.auth import AuthService, AuthenticatedUser
|
||||
from app.services.auth import AuthenticatedUser, AuthService
|
||||
from app.services.auth_sessions import AuthSessionService
|
||||
from app.services.employee import EmployeeService
|
||||
from app.services.settings import SettingsService
|
||||
|
||||
@@ -37,6 +41,11 @@ def test_employee_can_login_with_seed_default_password() -> None:
|
||||
assert result.user.grade == employee.grade
|
||||
assert result.user.roleCodes
|
||||
assert result.user.isAdmin is False
|
||||
assert result.accessToken
|
||||
assert result.tokenType == "Bearer"
|
||||
stored_session = db.query(AuthSession).one()
|
||||
assert stored_session.token_hash == AuthSessionService.hash_token(result.accessToken)
|
||||
assert stored_session.token_hash != result.accessToken
|
||||
|
||||
|
||||
def test_current_user_snapshot_refreshes_employee_position() -> None:
|
||||
@@ -113,8 +122,15 @@ def test_employee_login_skips_directory_bootstrap_when_employee_exists(monkeypat
|
||||
calls.append("ensure_directory_ready")
|
||||
raise AssertionError("existing employee login should not run directory bootstrap")
|
||||
|
||||
monkeypatch.setattr(AuthService, "_find_employee_by_email", lambda self, _: ExistingEmployee())
|
||||
monkeypatch.setattr("app.services.auth.verify_password", lambda password, password_hash: True)
|
||||
monkeypatch.setattr(
|
||||
AuthService,
|
||||
"_find_employee_by_email",
|
||||
lambda self, _: ExistingEmployee(),
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
"app.services.auth.verify_password",
|
||||
lambda password, password_hash: True,
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
AuthService,
|
||||
"_build_employee_user",
|
||||
@@ -143,3 +159,35 @@ def test_employee_login_skips_directory_bootstrap_when_employee_exists(monkeypat
|
||||
assert user is not None
|
||||
assert user.username == "demo@example.com"
|
||||
assert calls == []
|
||||
|
||||
|
||||
def test_login_session_write_rolls_back_metric_when_token_issue_fails(monkeypatch) -> None:
|
||||
with build_session() as db:
|
||||
user = AuthenticatedUser(
|
||||
username="rollback@example.com",
|
||||
name="Rollback User",
|
||||
role="使用者",
|
||||
department="",
|
||||
position="",
|
||||
grade="",
|
||||
employee_no="",
|
||||
manager_name="",
|
||||
location="",
|
||||
cost_center="",
|
||||
finance_owner_name="",
|
||||
risk_profile={},
|
||||
role_codes=["user"],
|
||||
email="rollback@example.com",
|
||||
avatar="R",
|
||||
)
|
||||
|
||||
def fail_issue(*args, **kwargs):
|
||||
raise RuntimeError("token issue failed")
|
||||
|
||||
monkeypatch.setattr(AuthSessionService, "issue", fail_issue)
|
||||
|
||||
with pytest.raises(RuntimeError, match="token issue failed"):
|
||||
AuthService(db)._build_login_response(user)
|
||||
|
||||
assert db.query(AuthSession).count() == 0
|
||||
assert db.query(UserSessionMetric).count() == 0
|
||||
|
||||
Reference in New Issue
Block a user