Introduce service/repository dependency protocols and tests

This commit is contained in:
2026-04-10 19:51:19 +02:00
parent 3b6e39ddad
commit 3371ff8324
8 changed files with 419 additions and 11 deletions

View File

@@ -12,9 +12,15 @@ from __future__ import annotations
import structlog
from fastapi import APIRouter, HTTPException, Request, Response, status
from app.dependencies import DbDep, SessionCacheDep, SettingsDep
from app.dependencies import (
AuthServiceDep,
DbDep,
SessionCacheDep,
SettingsDep,
SessionRepoDep,
)
from app.models.auth import LoginRequest, LoginResponse, LogoutResponse
from app.services import auth_service
from app.services.auth_service import sign_session_token
log: structlog.stdlib.BoundLogger = structlog.get_logger()
@@ -33,6 +39,8 @@ async def login(
response: Response,
db: DbDep,
settings: SettingsDep,
auth_service: AuthServiceDep,
session_repo: SessionRepoDep,
) -> LoginResponse:
"""Verify the master password and return a session token.
@@ -56,6 +64,7 @@ async def login(
db,
password=body.password,
session_duration_minutes=settings.session_duration_minutes,
session_repo=session_repo,
)
except ValueError as exc:
raise HTTPException(
@@ -63,7 +72,7 @@ async def login(
detail=str(exc),
) from exc
signed_token = auth_service.sign_session_token(
signed_token = sign_session_token(
session.token,
settings.session_secret,
)
@@ -89,6 +98,8 @@ async def logout(
db: DbDep,
settings: SettingsDep,
session_cache: SessionCacheDep,
auth_service: AuthServiceDep,
session_repo: SessionRepoDep,
) -> LogoutResponse:
"""Invalidate the active session.
@@ -107,7 +118,12 @@ async def logout(
"""
token = _extract_token(request)
if token:
raw_token = await auth_service.logout(db, token, settings.session_secret)
raw_token = await auth_service.logout(
db,
token,
settings.session_secret,
session_repo=session_repo,
)
if raw_token:
session_cache.invalidate(raw_token)
session_cache.invalidate(token)

View File

@@ -28,6 +28,7 @@ from app.dependencies import (
DbDep,
Fail2BanSocketDep,
HttpSessionDep,
JailServiceDep,
)
from app.exceptions import JailNotFoundError, JailOperationError
from app.models.ban import JailBannedIpsResponse
@@ -37,7 +38,7 @@ from app.models.jail import (
JailDetailResponse,
JailListResponse,
)
from app.services import geo_service, jail_service
from app.services import geo_service
from app.utils.fail2ban_client import Fail2BanConnectionError
router: APIRouter = APIRouter(prefix="/api/jails", tags=["Jails"])
@@ -107,6 +108,7 @@ def _conflict(message: str) -> HTTPException:
async def get_jails(
_auth: AuthDep,
socket_path: Fail2BanSocketDep,
jail_service: JailServiceDep,
) -> JailListResponse:
"""Return a summary of every active fail2ban jail.
@@ -135,6 +137,7 @@ async def get_jail(
_auth: AuthDep,
name: _NamePath,
socket_path: Fail2BanSocketDep,
jail_service: JailServiceDep,
) -> JailDetailResponse:
"""Return the complete configuration and runtime state for one jail.
@@ -174,6 +177,7 @@ async def get_jail(
async def reload_all_jails(
_auth: AuthDep,
socket_path: Fail2BanSocketDep,
jail_service: JailServiceDep,
) -> JailCommandResponse:
"""Reload every fail2ban jail to apply configuration changes.
@@ -208,6 +212,7 @@ async def start_jail(
_auth: AuthDep,
name: _NamePath,
socket_path: Fail2BanSocketDep,
jail_service: JailServiceDep,
) -> JailCommandResponse:
"""Start a fail2ban jail that is currently stopped.
@@ -243,6 +248,7 @@ async def stop_jail(
_auth: AuthDep,
name: _NamePath,
socket_path: Fail2BanSocketDep,
jail_service: JailServiceDep,
) -> JailCommandResponse:
"""Stop a running fail2ban jail.
@@ -279,6 +285,7 @@ async def toggle_idle(
_auth: AuthDep,
name: _NamePath,
socket_path: Fail2BanSocketDep,
jail_service: JailServiceDep,
on: bool = Body(..., description="``true`` to enable idle, ``false`` to disable."),
) -> JailCommandResponse:
"""Enable or disable idle mode for a fail2ban jail.
@@ -323,6 +330,7 @@ async def reload_jail(
_auth: AuthDep,
name: _NamePath,
socket_path: Fail2BanSocketDep,
jail_service: JailServiceDep,
) -> JailCommandResponse:
"""Reload a single fail2ban jail to pick up configuration changes.
@@ -371,6 +379,7 @@ async def get_ignore_list(
_auth: AuthDep,
name: _NamePath,
socket_path: Fail2BanSocketDep,
jail_service: JailServiceDep,
) -> list[str]:
"""Return the current ignore list (IP whitelist) for a fail2ban jail.
@@ -404,6 +413,7 @@ async def add_ignore_ip(
name: _NamePath,
body: IgnoreIpRequest,
socket_path: Fail2BanSocketDep,
jail_service: JailServiceDep,
) -> JailCommandResponse:
"""Add an IP address or CIDR network to a jail's ignore list.
@@ -453,6 +463,7 @@ async def del_ignore_ip(
name: _NamePath,
body: IgnoreIpRequest,
socket_path: Fail2BanSocketDep,
jail_service: JailServiceDep,
) -> JailCommandResponse:
"""Remove an IP address or CIDR network from a jail's ignore list.
@@ -492,6 +503,7 @@ async def toggle_ignore_self(
_auth: AuthDep,
name: _NamePath,
socket_path: Fail2BanSocketDep,
jail_service: JailServiceDep,
on: bool = Body(..., description="``true`` to enable ignoreself, ``false`` to disable."),
) -> JailCommandResponse:
"""Toggle the ``ignoreself`` flag for a fail2ban jail.
@@ -543,6 +555,7 @@ async def get_jail_banned_ips(
name: _NamePath,
socket_path: Fail2BanSocketDep,
http_session: HttpSessionDep,
jail_service: JailServiceDep,
page: int = 1,
page_size: int = 25,
search: str | None = None,