Expose usedns, date_pattern, and prefregex in jail config UI
- Add use_dns and prefregex fields to JailConfig model (backend + frontend types) - Add prefregex to JailConfigUpdate; validate as regex before writing - Fetch usedns and prefregex in get_jail_config via asyncio.gather - Write usedns and prefregex in update_jail_config - ConfigPage JailAccordionPanel: editable date_pattern input, dns_mode Select dropdown (yes/warn/no/raw), and prefregex input - 8 new service unit tests + 3 new router integration tests - 628 tests pass; 85% line coverage; ruff/mypy/tsc/eslint clean
This commit is contained in:
@@ -75,6 +75,8 @@ _DEFAULT_JAIL_RESPONSES: dict[str, Any] = {
|
||||
"get|sshd|datepattern": (0, None),
|
||||
"get|sshd|logencoding": (0, "UTF-8"),
|
||||
"get|sshd|backend": (0, "polling"),
|
||||
"get|sshd|usedns": (0, "warn"),
|
||||
"get|sshd|prefregex": (0, ""),
|
||||
"get|sshd|actions": (0, ["iptables"]),
|
||||
}
|
||||
|
||||
@@ -143,6 +145,41 @@ class TestGetJailConfig:
|
||||
|
||||
assert result.jail.date_pattern is None
|
||||
|
||||
async def test_use_dns_populated(self) -> None:
|
||||
"""get_jail_config returns use_dns from the socket response."""
|
||||
responses = {**_DEFAULT_JAIL_RESPONSES, "get|sshd|usedns": (0, "no")}
|
||||
with _patch_client(responses):
|
||||
result = await config_service.get_jail_config(_SOCKET, "sshd")
|
||||
|
||||
assert result.jail.use_dns == "no"
|
||||
|
||||
async def test_use_dns_default_when_missing(self) -> None:
|
||||
"""get_jail_config defaults use_dns to 'warn' when socket returns None."""
|
||||
responses = {**_DEFAULT_JAIL_RESPONSES, "get|sshd|usedns": (0, None)}
|
||||
with _patch_client(responses):
|
||||
result = await config_service.get_jail_config(_SOCKET, "sshd")
|
||||
|
||||
assert result.jail.use_dns == "warn"
|
||||
|
||||
async def test_prefregex_populated(self) -> None:
|
||||
"""get_jail_config returns prefregex from the socket response."""
|
||||
responses = {
|
||||
**_DEFAULT_JAIL_RESPONSES,
|
||||
"get|sshd|prefregex": (0, r"^%(__prefix_line)s"),
|
||||
}
|
||||
with _patch_client(responses):
|
||||
result = await config_service.get_jail_config(_SOCKET, "sshd")
|
||||
|
||||
assert result.jail.prefregex == r"^%(__prefix_line)s"
|
||||
|
||||
async def test_prefregex_empty_when_missing(self) -> None:
|
||||
"""get_jail_config returns empty string prefregex when socket returns None."""
|
||||
responses = {**_DEFAULT_JAIL_RESPONSES, "get|sshd|prefregex": (0, None)}
|
||||
with _patch_client(responses):
|
||||
result = await config_service.get_jail_config(_SOCKET, "sshd")
|
||||
|
||||
assert result.jail.prefregex == ""
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# list_jail_configs
|
||||
@@ -275,6 +312,88 @@ class TestUpdateJailConfig:
|
||||
assert add_cmd is not None
|
||||
assert add_cmd[3] == "new_pattern"
|
||||
|
||||
async def test_sets_dns_mode(self) -> None:
|
||||
"""update_jail_config sends 'set <jail> usedns' for dns_mode."""
|
||||
from app.models.config import JailConfigUpdate
|
||||
|
||||
sent_commands: list[list[Any]] = []
|
||||
|
||||
async def _send(command: list[Any]) -> Any:
|
||||
sent_commands.append(command)
|
||||
return (0, "OK")
|
||||
|
||||
class _FakeClient:
|
||||
def __init__(self, **_kw: Any) -> None:
|
||||
self.send = AsyncMock(side_effect=_send)
|
||||
|
||||
update = JailConfigUpdate(dns_mode="no")
|
||||
with patch("app.services.config_service.Fail2BanClient", _FakeClient):
|
||||
await config_service.update_jail_config(_SOCKET, "sshd", update)
|
||||
|
||||
usedns_cmd = next(
|
||||
(c for c in sent_commands if len(c) >= 4 and c[2] == "usedns"),
|
||||
None,
|
||||
)
|
||||
assert usedns_cmd is not None
|
||||
assert usedns_cmd[3] == "no"
|
||||
|
||||
async def test_sets_prefregex(self) -> None:
|
||||
"""update_jail_config sends 'set <jail> prefregex' for prefregex."""
|
||||
from app.models.config import JailConfigUpdate
|
||||
|
||||
sent_commands: list[list[Any]] = []
|
||||
|
||||
async def _send(command: list[Any]) -> Any:
|
||||
sent_commands.append(command)
|
||||
return (0, "OK")
|
||||
|
||||
class _FakeClient:
|
||||
def __init__(self, **_kw: Any) -> None:
|
||||
self.send = AsyncMock(side_effect=_send)
|
||||
|
||||
update = JailConfigUpdate(prefregex=r"^%(__prefix_line)s")
|
||||
with patch("app.services.config_service.Fail2BanClient", _FakeClient):
|
||||
await config_service.update_jail_config(_SOCKET, "sshd", update)
|
||||
|
||||
prefregex_cmd = next(
|
||||
(c for c in sent_commands if len(c) >= 4 and c[2] == "prefregex"),
|
||||
None,
|
||||
)
|
||||
assert prefregex_cmd is not None
|
||||
assert prefregex_cmd[3] == r"^%(__prefix_line)s"
|
||||
|
||||
async def test_skips_none_prefregex(self) -> None:
|
||||
"""update_jail_config does not send prefregex command when field is None."""
|
||||
from app.models.config import JailConfigUpdate
|
||||
|
||||
sent_commands: list[list[Any]] = []
|
||||
|
||||
async def _send(command: list[Any]) -> Any:
|
||||
sent_commands.append(command)
|
||||
return (0, "OK")
|
||||
|
||||
class _FakeClient:
|
||||
def __init__(self, **_kw: Any) -> None:
|
||||
self.send = AsyncMock(side_effect=_send)
|
||||
|
||||
update = JailConfigUpdate(prefregex=None)
|
||||
with patch("app.services.config_service.Fail2BanClient", _FakeClient):
|
||||
await config_service.update_jail_config(_SOCKET, "sshd", update)
|
||||
|
||||
prefregex_cmd = next(
|
||||
(c for c in sent_commands if len(c) >= 4 and c[2] == "prefregex"),
|
||||
None,
|
||||
)
|
||||
assert prefregex_cmd is None
|
||||
|
||||
async def test_raises_validation_error_on_invalid_prefregex(self) -> None:
|
||||
"""update_jail_config raises ConfigValidationError for an invalid prefregex."""
|
||||
from app.models.config import JailConfigUpdate
|
||||
|
||||
update = JailConfigUpdate(prefregex="[invalid")
|
||||
with pytest.raises(ConfigValidationError, match="prefregex"):
|
||||
await config_service.update_jail_config(_SOCKET, "sshd", update)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# get_global_config
|
||||
|
||||
Reference in New Issue
Block a user