Refactor router dependencies to use explicit fail2ban socket and HTTP session injection

This commit is contained in:
2026-04-06 16:38:17 +02:00
parent 42c030c706
commit 3b58179845
2 changed files with 34 additions and 64 deletions

View File

@@ -21,9 +21,15 @@ from __future__ import annotations
from typing import Annotated
from fastapi import APIRouter, Body, HTTPException, Path, Request, status
from fastapi import APIRouter, Body, HTTPException, Path, status
from app.dependencies import AuthDep, DbDep
from app.dependencies import (
AuthDep,
DbDep,
Fail2BanSocketDep,
HttpSessionDep,
)
from app.exceptions import JailNotFoundError, JailOperationError
from app.models.ban import JailBannedIpsResponse
from app.models.jail import (
IgnoreIpRequest,
@@ -32,7 +38,6 @@ from app.models.jail import (
JailListResponse,
)
from app.services import geo_service, jail_service
from app.exceptions import JailNotFoundError, JailOperationError
from app.utils.fail2ban_client import Fail2BanConnectionError
router: APIRouter = APIRouter(prefix="/api/jails", tags=["Jails"])
@@ -100,8 +105,8 @@ def _conflict(message: str) -> HTTPException:
summary="List all active fail2ban jails",
)
async def get_jails(
request: Request,
_auth: AuthDep,
socket_path: Fail2BanSocketDep,
) -> JailListResponse:
"""Return a summary of every active fail2ban jail.
@@ -110,13 +115,11 @@ async def get_jails(
for each jail.
Args:
request: Incoming request (used to access ``app.state``).
_auth: Validated session — enforces authentication.
Returns:
:class:`~app.models.jail.JailListResponse` with all active jails.
"""
socket_path: str = request.app.state.settings.fail2ban_socket
try:
return await jail_service.list_jails(socket_path)
except Fail2BanConnectionError as exc:
@@ -129,9 +132,9 @@ async def get_jails(
summary="Return full detail for a single jail",
)
async def get_jail(
request: Request,
_auth: AuthDep,
name: _NamePath,
socket_path: Fail2BanSocketDep,
) -> JailDetailResponse:
"""Return the complete configuration and runtime state for one jail.
@@ -140,7 +143,6 @@ async def get_jail(
counters.
Args:
request: Incoming request (used to access ``app.state``).
_auth: Validated session — enforces authentication.
name: Jail name.
@@ -151,7 +153,6 @@ async def get_jail(
HTTPException: 404 when the jail does not exist.
HTTPException: 502 when fail2ban is unreachable.
"""
socket_path: str = request.app.state.settings.fail2ban_socket
try:
return await jail_service.get_jail(socket_path, name)
except JailNotFoundError:
@@ -171,8 +172,8 @@ async def get_jail(
summary="Reload all fail2ban jails",
)
async def reload_all_jails(
request: Request,
_auth: AuthDep,
socket_path: Fail2BanSocketDep,
) -> JailCommandResponse:
"""Reload every fail2ban jail to apply configuration changes.
@@ -180,7 +181,6 @@ async def reload_all_jails(
jails simultaneously.
Args:
request: Incoming request (used to access ``app.state``).
_auth: Validated session — enforces authentication.
Returns:
@@ -190,7 +190,6 @@ async def reload_all_jails(
HTTPException: 502 when fail2ban is unreachable.
HTTPException: 409 when fail2ban reports the operation failed.
"""
socket_path: str = request.app.state.settings.fail2ban_socket
try:
await jail_service.reload_all(socket_path)
return JailCommandResponse(message="All jails reloaded successfully.", jail="*")
@@ -206,14 +205,13 @@ async def reload_all_jails(
summary="Start a stopped jail",
)
async def start_jail(
request: Request,
_auth: AuthDep,
name: _NamePath,
socket_path: Fail2BanSocketDep,
) -> JailCommandResponse:
"""Start a fail2ban jail that is currently stopped.
Args:
request: Incoming request (used to access ``app.state``).
_auth: Validated session — enforces authentication.
name: Jail name.
@@ -225,7 +223,6 @@ async def start_jail(
HTTPException: 409 when fail2ban reports the operation failed.
HTTPException: 502 when fail2ban is unreachable.
"""
socket_path: str = request.app.state.settings.fail2ban_socket
try:
await jail_service.start_jail(socket_path, name)
return JailCommandResponse(message=f"Jail {name!r} started.", jail=name)
@@ -243,9 +240,9 @@ async def start_jail(
summary="Stop a running jail",
)
async def stop_jail(
request: Request,
_auth: AuthDep,
name: _NamePath,
socket_path: Fail2BanSocketDep,
) -> JailCommandResponse:
"""Stop a running fail2ban jail.
@@ -254,7 +251,6 @@ async def stop_jail(
jail is already stopped the request succeeds silently (idempotent).
Args:
request: Incoming request (used to access ``app.state``).
_auth: Validated session — enforces authentication.
name: Jail name.
@@ -265,7 +261,6 @@ async def stop_jail(
HTTPException: 409 when fail2ban reports the operation failed.
HTTPException: 502 when fail2ban is unreachable.
"""
socket_path: str = request.app.state.settings.fail2ban_socket
try:
await jail_service.stop_jail(socket_path, name)
return JailCommandResponse(message=f"Jail {name!r} stopped.", jail=name)
@@ -281,9 +276,9 @@ async def stop_jail(
summary="Toggle idle mode for a jail",
)
async def toggle_idle(
request: Request,
_auth: AuthDep,
name: _NamePath,
socket_path: Fail2BanSocketDep,
on: bool = Body(..., description="``true`` to enable idle, ``false`` to disable."),
) -> JailCommandResponse:
"""Enable or disable idle mode for a fail2ban jail.
@@ -292,7 +287,6 @@ async def toggle_idle(
preserving all existing bans.
Args:
request: Incoming request (used to access ``app.state``).
_auth: Validated session — enforces authentication.
name: Jail name.
on: ``true`` to enable idle, ``false`` to disable.
@@ -305,7 +299,6 @@ async def toggle_idle(
HTTPException: 409 when fail2ban reports the operation failed.
HTTPException: 502 when fail2ban is unreachable.
"""
socket_path: str = request.app.state.settings.fail2ban_socket
state_str = "on" if on else "off"
try:
await jail_service.set_idle(socket_path, name, on=on)
@@ -327,14 +320,13 @@ async def toggle_idle(
summary="Reload a single jail",
)
async def reload_jail(
request: Request,
_auth: AuthDep,
name: _NamePath,
socket_path: Fail2BanSocketDep,
) -> JailCommandResponse:
"""Reload a single fail2ban jail to pick up configuration changes.
Args:
request: Incoming request (used to access ``app.state``).
_auth: Validated session — enforces authentication.
name: Jail name.
@@ -346,7 +338,6 @@ async def reload_jail(
HTTPException: 409 when fail2ban reports the operation failed.
HTTPException: 502 when fail2ban is unreachable.
"""
socket_path: str = request.app.state.settings.fail2ban_socket
try:
await jail_service.reload_jail(socket_path, name)
return JailCommandResponse(message=f"Jail {name!r} reloaded.", jail=name)
@@ -377,14 +368,13 @@ class _IgnoreSelfRequest(IgnoreIpRequest):
summary="List the ignore IPs for a jail",
)
async def get_ignore_list(
request: Request,
_auth: AuthDep,
name: _NamePath,
socket_path: Fail2BanSocketDep,
) -> list[str]:
"""Return the current ignore list (IP whitelist) for a fail2ban jail.
Args:
request: Incoming request (used to access ``app.state``).
_auth: Validated session — enforces authentication.
name: Jail name.
@@ -395,7 +385,6 @@ async def get_ignore_list(
HTTPException: 404 when the jail does not exist.
HTTPException: 502 when fail2ban is unreachable.
"""
socket_path: str = request.app.state.settings.fail2ban_socket
try:
return await jail_service.get_ignore_list(socket_path, name)
except JailNotFoundError:
@@ -411,10 +400,10 @@ async def get_ignore_list(
summary="Add an IP or network to the ignore list",
)
async def add_ignore_ip(
request: Request,
_auth: AuthDep,
name: _NamePath,
body: IgnoreIpRequest,
socket_path: Fail2BanSocketDep,
) -> JailCommandResponse:
"""Add an IP address or CIDR network to a jail's ignore list.
@@ -422,7 +411,6 @@ async def add_ignore_ip(
trigger the configured fail regex.
Args:
request: Incoming request (used to access ``app.state``).
_auth: Validated session — enforces authentication.
name: Jail name.
body: Payload containing the IP or CIDR to add.
@@ -436,7 +424,6 @@ async def add_ignore_ip(
HTTPException: 409 when fail2ban reports the operation failed.
HTTPException: 502 when fail2ban is unreachable.
"""
socket_path: str = request.app.state.settings.fail2ban_socket
try:
await jail_service.add_ignore_ip(socket_path, name, body.ip)
return JailCommandResponse(
@@ -462,15 +449,14 @@ async def add_ignore_ip(
summary="Remove an IP or network from the ignore list",
)
async def del_ignore_ip(
request: Request,
_auth: AuthDep,
name: _NamePath,
body: IgnoreIpRequest,
socket_path: Fail2BanSocketDep,
) -> JailCommandResponse:
"""Remove an IP address or CIDR network from a jail's ignore list.
Args:
request: Incoming request (used to access ``app.state``).
_auth: Validated session — enforces authentication.
name: Jail name.
body: Payload containing the IP or CIDR to remove.
@@ -483,7 +469,6 @@ async def del_ignore_ip(
HTTPException: 409 when fail2ban reports the operation failed.
HTTPException: 502 when fail2ban is unreachable.
"""
socket_path: str = request.app.state.settings.fail2ban_socket
try:
await jail_service.del_ignore_ip(socket_path, name, body.ip)
return JailCommandResponse(
@@ -504,9 +489,9 @@ async def del_ignore_ip(
summary="Toggle the ignoreself option for a jail",
)
async def toggle_ignore_self(
request: Request,
_auth: AuthDep,
name: _NamePath,
socket_path: Fail2BanSocketDep,
on: bool = Body(..., description="``true`` to enable ignoreself, ``false`` to disable."),
) -> JailCommandResponse:
"""Toggle the ``ignoreself`` flag for a fail2ban jail.
@@ -515,7 +500,6 @@ async def toggle_ignore_self(
own IP addresses to the ignore list so the host can never ban itself.
Args:
request: Incoming request (used to access ``app.state``).
_auth: Validated session — enforces authentication.
name: Jail name.
on: ``true`` to enable, ``false`` to disable.
@@ -528,7 +512,6 @@ async def toggle_ignore_self(
HTTPException: 409 when fail2ban reports the operation failed.
HTTPException: 502 when fail2ban is unreachable.
"""
socket_path: str = request.app.state.settings.fail2ban_socket
state_str = "enabled" if on else "disabled"
try:
await jail_service.set_ignore_self(socket_path, name, on=on)
@@ -555,10 +538,11 @@ async def toggle_ignore_self(
summary="Return paginated currently-banned IPs for a single jail",
)
async def get_jail_banned_ips(
request: Request,
_auth: AuthDep,
db: DbDep,
name: _NamePath,
socket_path: Fail2BanSocketDep,
http_session: HttpSessionDep,
page: int = 1,
page_size: int = 25,
search: str | None = None,
@@ -570,7 +554,6 @@ async def get_jail_banned_ips(
geo-enriched exclusively for that page slice.
Args:
request: Incoming request (used to access ``app.state``).
_auth: Validated session — enforces authentication.
name: Jail name.
page: 1-based page number (default 1, min 1).
@@ -596,9 +579,6 @@ async def get_jail_banned_ips(
detail="page_size must be between 1 and 100.",
)
socket_path: str = request.app.state.settings.fail2ban_socket
http_session = getattr(request.app.state, "http_session", None)
try:
return await jail_service.get_jail_banned_ips(
socket_path=socket_path,