fix: RDG event label and prefix type filter (0.3.2)
Use event 302/303 instead of Win302 in RDG titles. Match event types by prefix in events list (e.g. rdg.connection). Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -154,8 +154,10 @@ def list_events(
|
|||||||
stmt = stmt.where(Event.severity == severity)
|
stmt = stmt.where(Event.severity == severity)
|
||||||
count_stmt = count_stmt.where(Event.severity == severity)
|
count_stmt = count_stmt.where(Event.severity == severity)
|
||||||
if type:
|
if type:
|
||||||
stmt = stmt.where(Event.type == type)
|
normalized = type.strip()
|
||||||
count_stmt = count_stmt.where(Event.type == type)
|
if normalized:
|
||||||
|
stmt = stmt.where(Event.type.ilike(f"{normalized}%"))
|
||||||
|
count_stmt = count_stmt.where(Event.type.ilike(f"{normalized}%"))
|
||||||
if host_id is not None:
|
if host_id is not None:
|
||||||
stmt = stmt.where(Event.host_id == host_id)
|
stmt = stmt.where(Event.host_id == host_id)
|
||||||
count_stmt = count_stmt.where(Event.host_id == host_id)
|
count_stmt = count_stmt.where(Event.host_id == host_id)
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ def _windows_event_label(event: Event) -> str:
|
|||||||
if win_id is None:
|
if win_id is None:
|
||||||
return ""
|
return ""
|
||||||
text = str(win_id).strip()
|
text = str(win_id).strip()
|
||||||
return f"Win{text}" if text else ""
|
return f"event {text}" if text else ""
|
||||||
|
|
||||||
|
|
||||||
def _action_label(event: Event) -> str:
|
def _action_label(event: Event) -> str:
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
"""Единый источник версии SAC (API, health, логи, OpenAPI)."""
|
"""Единый источник версии SAC (API, health, логи, OpenAPI)."""
|
||||||
|
|
||||||
APP_NAME = "Security Alert Center"
|
APP_NAME = "Security Alert Center"
|
||||||
APP_VERSION = "0.3.1"
|
APP_VERSION = "0.3.2"
|
||||||
APP_VERSION_LABEL = f"{APP_NAME} v.{APP_VERSION}"
|
APP_VERSION_LABEL = f"{APP_NAME} v.{APP_VERSION}"
|
||||||
|
|||||||
@@ -0,0 +1,54 @@
|
|||||||
|
"""Tests for GET /api/v1/events filters."""
|
||||||
|
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
VALID_RDG = {
|
||||||
|
"schema_version": "1.0",
|
||||||
|
"occurred_at": "2026-05-27T10:00:00+03:00",
|
||||||
|
"source": {"product": "rdp-login-monitor", "product_version": "1.2.3-SAC"},
|
||||||
|
"host": {"hostname": "gw-filter-test", "os_family": "windows"},
|
||||||
|
"category": "auth",
|
||||||
|
"severity": "info",
|
||||||
|
"title": "RDG",
|
||||||
|
"summary": "pytest",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _ingest(client, auth_headers, *, event_type: str) -> str:
|
||||||
|
event_id = str(uuid.uuid4())
|
||||||
|
payload = {**VALID_RDG, "event_id": event_id, "type": event_type}
|
||||||
|
assert client.post("/api/v1/events", json=payload, headers=auth_headers).status_code == 201
|
||||||
|
return event_id
|
||||||
|
|
||||||
|
|
||||||
|
def test_events_type_filter_prefix(client, auth_headers, jwt_headers):
|
||||||
|
success_id = _ingest(client, auth_headers, event_type="rdg.connection.success")
|
||||||
|
disconnected_id = _ingest(client, auth_headers, event_type="rdg.connection.disconnected")
|
||||||
|
other_id = _ingest(client, auth_headers, event_type="rdp.login.success")
|
||||||
|
|
||||||
|
listed = client.get(
|
||||||
|
"/api/v1/events",
|
||||||
|
headers=jwt_headers,
|
||||||
|
params={"type": "rdg.connection", "hostname": "gw-filter-test", "page_size": 50},
|
||||||
|
)
|
||||||
|
assert listed.status_code == 200
|
||||||
|
body = listed.json()
|
||||||
|
ids = {row["event_id"] for row in body["items"]}
|
||||||
|
assert success_id in ids
|
||||||
|
assert disconnected_id in ids
|
||||||
|
assert other_id not in ids
|
||||||
|
|
||||||
|
|
||||||
|
def test_events_type_filter_exact_still_works(client, auth_headers, jwt_headers):
|
||||||
|
success_id = _ingest(client, auth_headers, event_type="rdg.connection.success")
|
||||||
|
disconnected_id = _ingest(client, auth_headers, event_type="rdg.connection.disconnected")
|
||||||
|
|
||||||
|
listed = client.get(
|
||||||
|
"/api/v1/events",
|
||||||
|
headers=jwt_headers,
|
||||||
|
params={"type": "rdg.connection.success", "hostname": "gw-filter-test", "page_size": 50},
|
||||||
|
)
|
||||||
|
assert listed.status_code == 200
|
||||||
|
ids = {row["event_id"] for row in listed.json()["items"]}
|
||||||
|
assert success_id in ids
|
||||||
|
assert disconnected_id not in ids
|
||||||
@@ -4,6 +4,6 @@ from app.version import APP_NAME, APP_VERSION, APP_VERSION_LABEL
|
|||||||
|
|
||||||
|
|
||||||
def test_version_constants():
|
def test_version_constants():
|
||||||
assert APP_VERSION == "0.3.1"
|
assert APP_VERSION == "0.3.2"
|
||||||
assert APP_NAME == "Security Alert Center"
|
assert APP_NAME == "Security Alert Center"
|
||||||
assert APP_VERSION_LABEL == "Security Alert Center v.0.3.1"
|
assert APP_VERSION_LABEL == "Security Alert Center v.0.3.2"
|
||||||
|
|||||||
@@ -85,6 +85,7 @@ def test_build_rdg_display_title_and_path(db_session, monkeypatch):
|
|||||||
assert display.qwinsta_enabled is True
|
assert display.qwinsta_enabled is True
|
||||||
assert "papatramp" in display.summary
|
assert "papatramp" in display.summary
|
||||||
assert "шлюз K6A-DC3" in display.summary
|
assert "шлюз K6A-DC3" in display.summary
|
||||||
|
assert "event 302" in display.title
|
||||||
|
|
||||||
|
|
||||||
def test_event_to_summary_enriches_rdg(db_session, monkeypatch):
|
def test_event_to_summary_enriches_rdg(db_session, monkeypatch):
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/** Fallback до загрузки /health; при релизе держите в sync с backend/app/version.py */
|
/** Fallback до загрузки /health; при релизе держите в sync с backend/app/version.py */
|
||||||
export const APP_NAME = "Security Alert Center";
|
export const APP_NAME = "Security Alert Center";
|
||||||
export const APP_VERSION = "0.3.1";
|
export const APP_VERSION = "0.3.2";
|
||||||
export const APP_VERSION_LABEL = `${APP_NAME} v.${APP_VERSION}`;
|
export const APP_VERSION_LABEL = `${APP_NAME} v.${APP_VERSION}`;
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
<option>high</option>
|
<option>high</option>
|
||||||
<option>critical</option>
|
<option>critical</option>
|
||||||
</select>
|
</select>
|
||||||
<input v-model="typeFilter" placeholder="type" @keyup.enter="applyFilters" />
|
<input v-model="typeFilter" placeholder="type (префикс)" @keyup.enter="applyFilters" />
|
||||||
<input v-model="hostnameFilter" placeholder="hostname / имя сервера" @keyup.enter="applyFilters" />
|
<input v-model="hostnameFilter" placeholder="hostname / имя сервера" @keyup.enter="applyFilters" />
|
||||||
<label class="events-date-field">
|
<label class="events-date-field">
|
||||||
<span class="events-date-label">С</span>
|
<span class="events-date-label">С</span>
|
||||||
|
|||||||
Reference in New Issue
Block a user