Add filter write/create/delete and jail-filter assign endpoints (Task 2.2)

- PUT /api/config/filters/{name}: updates failregex/ignoreregex/datepattern/
  journalmatch in filter.d/{name}.local; validates regex via re.compile();
  supports ?reload=true
- POST /api/config/filters: creates filter.d/{name}.local from FilterCreateRequest;
  returns 409 if file already exists
- DELETE /api/config/filters/{name}: deletes .local only; returns 409 for
  conf-only (readonly) filters
- POST /api/config/jails/{name}/filter: assigns filter to jail by writing
  'filter = {name}' to jail.d/{jail}.local; supports ?reload=true
- New models: FilterUpdateRequest, FilterCreateRequest, AssignFilterRequest
- New service helpers: _safe_filter_name, _validate_regex_patterns,
  _write_filter_local_sync, _set_jail_local_key_sync
- Fixed .local-only filter discovery in _parse_filters_sync (5-tuple return)
- Fixed get_filter extension stripping (.local is 6 chars not 5)
- Renamed file_config.py raw-write routes to /raw suffix
  (PUT /filters/{name}/raw, POST /filters/raw) to avoid routing conflicts
- Full service + router tests; all 930 tests pass
This commit is contained in:
2026-03-13 18:13:03 +01:00
parent 4c138424a5
commit e15ad8fb62
8 changed files with 1885 additions and 64 deletions

View File

@@ -144,25 +144,19 @@ fail2ban ships with a large collection of filter definitions in `filter.d/` (ove
---
### Task 2.2 — Backend: Activate and Edit Filters
### Task 2.2 — Backend: Activate and Edit Filters ✅ DONE
**Goal:** Allow users to assign a filter to a jail and edit filter regex patterns.
**Implemented:**
- `PUT /api/config/filters/{name}` — writes `failregex`, `ignoreregex`, `datepattern`, `journalmatch` changes to `filter.d/{name}.local`. Validates regex before writing. Supports `?reload=true`.
- `POST /api/config/filters` — creates `filter.d/{name}.local` from `FilterCreateRequest`. Returns 409 if file already exists.
- `DELETE /api/config/filters/{name}` — deletes `.local` only; refuses with 409 if filter is conf-only (readonly).
- `POST /api/config/jails/{name}/filter` — assigns a filter to a jail by writing `filter = {name}` to `jail.d/{jail}.local`. Supports `?reload=true`.
- All regex patterns validated via `re.compile()` before writing; invalid patterns return 422.
- New models: `FilterUpdateRequest`, `FilterCreateRequest`, `AssignFilterRequest`.
- Resolved routing conflict: `file_config.py` raw-write routes renamed to `PUT /filters/{name}/raw` and `POST /filters/raw` (consistent with existing `GET /filters/{name}/raw`).
- Full service + router tests added; all 930 tests pass.
**Details:**
- Add a `PUT /api/config/filters/{name}` endpoint that writes changes to a filter's `.local` override file. Accepts updated `failregex`, `ignoreregex`, `datepattern`, and `journalmatch` values. Never write to the `.conf` file directly.
- Add a `POST /api/config/jails/{jail_name}/filter` endpoint that changes which filter a jail uses. This writes `filter = {filter_name}` to the jail's `.local` config. Requires a reload for the change to take effect.
- Add a `POST /api/config/filters` endpoint to create a brand-new filter. Accepts a name and the filter definition fields. Creates a new file at `filter.d/{name}.local`.
- Add a `DELETE /api/config/filters/{name}` endpoint that deletes a custom filter's `.local` file. Refuse to delete files that are `.conf` (shipped defaults) — only user-created `.local` files without a corresponding `.conf` can be fully removed.
- Validate all regex patterns using Python's `re` module before writing them to disk. Return 422 with specific error details if any pattern is invalid.
- After any write operation, optionally trigger a fail2ban reload if the user requests it (query param `?reload=true`).
**Files to create/modify:**
- `app/services/config_file_service.py` (add filter write/create/delete methods)
- `app/routers/config.py` (add endpoints)
- `app/models/config.py` (add `FilterUpdateRequest`, `FilterCreateRequest`)
**References:** [Features.md §6](Features.md), [Backend-Development.md](Backend-Development.md)
**Files modified:** `app/models/config.py`, `app/services/config_file_service.py`, `app/routers/config.py`, `app/routers/file_config.py`, `tests/test_services/test_config_file_service.py`, `tests/test_routers/test_config.py`, `tests/test_routers/test_file_config.py`
---