Archived
Prepare KarlDNS production deployment
This commit is contained in:
1 parent
5a90bd9d87
commit
ce5b4e0ff2
101 files changed
+3364
-2018
No files matched your search
+53
-27
@@ -2,8 +2,8 @@ from pathlib import Path
|
||||
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
from router_dyndns.ddns import DdnsSettings, DdnsStore, make_app
|
||||
from router_dyndns.ddns_security import admin_csrf_token
|
||||
from karldns import DdnsSettings, DdnsStore, make_app
|
||||
from karldns.security import admin_csrf_token
|
||||
|
||||
|
||||
def test_admin_page_shows_operator_views_without_create_form(tmp_path: Path) -> None:
|
||||
@@ -48,12 +48,12 @@ def test_custom_admin_path_moves_operator_routes(tmp_path: Path) -> None:
|
||||
response = client.get("/ops", auth=("admin", "admin"))
|
||||
|
||||
assert response.status_code == 200
|
||||
assert 'href="/ops/records"' in response.text
|
||||
assert 'href="/ops/events"' in response.text
|
||||
assert 'href="/ops/records"' not in response.text
|
||||
assert 'href="/ops/events"' not in response.text
|
||||
assert 'action="/ops/cleanup"' in response.text
|
||||
assert 'action="/ops/accounts/rotate"' in response.text
|
||||
assert 'action="/ops/hostnames/rotate"' in response.text
|
||||
assert 'action="/admin/cleanup"' not in response.text
|
||||
assert client.get("/ops/records", auth=("admin", "admin")).status_code == 200
|
||||
assert client.get("/ops/records", auth=("admin", "admin")).status_code == 404
|
||||
assert client.get("/records", auth=("admin", "admin")).status_code == 404
|
||||
assert client.get("/admin", auth=("admin", "admin")).status_code == 404
|
||||
|
||||
@@ -70,8 +70,8 @@ def test_admin_records_and_events_are_paginated(tmp_path: Path) -> None:
|
||||
app = make_app(DdnsSettings(database_path=database_path, admin_password="admin"))
|
||||
client = TestClient(app)
|
||||
|
||||
records = client.get("/records?limit=1&offset=1", auth=("admin", "admin")).json()
|
||||
events = client.get("/events?limit=1&offset=1", auth=("admin", "admin")).json()
|
||||
records = client.get("/api/v1/admin/records?limit=1&offset=1", auth=("admin", "admin")).json()
|
||||
events = client.get("/api/v1/admin/events?limit=1&offset=1", auth=("admin", "admin")).json()
|
||||
|
||||
assert records["limit"] == 1
|
||||
assert records["offset"] == 1
|
||||
@@ -84,12 +84,48 @@ def test_admin_records_and_events_are_paginated(tmp_path: Path) -> None:
|
||||
assert len(events["items"]) == 1
|
||||
|
||||
|
||||
def test_admin_api_exposes_dashboard_data_and_history(tmp_path: Path) -> None:
|
||||
database_path = tmp_path / "ddns.sqlite3"
|
||||
store = DdnsStore(database_path)
|
||||
account = store.create_account("home.example.net", "router")
|
||||
store.upsert(account["hostname"], "203.0.113.10", None)
|
||||
store.log_update_event(account["hostname"], "203.0.113.10", None, "updated", "ok", "198.51.100.1")
|
||||
store.increment_metric("update_requests_total")
|
||||
settings = DdnsSettings(database_path=database_path, admin_password="admin")
|
||||
app = make_app(settings)
|
||||
client = TestClient(app)
|
||||
|
||||
assert client.get("/api/v1/admin/dashboard").status_code == 401
|
||||
|
||||
dashboard = client.get("/api/v1/admin/dashboard?limit=10", auth=("admin", "admin"))
|
||||
hostnames = client.get("/api/v1/admin/hostnames?limit=1&offset=0", auth=("admin", "admin"))
|
||||
records = client.get("/api/v1/admin/records?limit=1&offset=0", auth=("admin", "admin"))
|
||||
events = client.get("/api/v1/admin/events?limit=1&offset=0", auth=("admin", "admin"))
|
||||
|
||||
assert dashboard.status_code == 200
|
||||
assert dashboard.headers["cache-control"] == "no-store"
|
||||
dashboard_json = dashboard.json()
|
||||
assert dashboard_json["totals"]["hostnames"] == 1
|
||||
assert dashboard_json["totals"]["records"] == 1
|
||||
assert dashboard_json["totals"]["update_events"] == 1
|
||||
assert dashboard_json["metrics"]["values"]["update_requests_total"] == 1
|
||||
assert dashboard_json["hostnames"][0]["hostname"] == "home.example.net"
|
||||
assert dashboard_json["hostnames"][0]["status"] == "active"
|
||||
assert dashboard_json["hostnames"][0]["ipv4"] == "203.0.113.10"
|
||||
assert dashboard_json["update_events"][0]["status"] == "updated"
|
||||
assert dashboard_json["scheduled_tasks"][0]["name"] == "cleanup"
|
||||
|
||||
assert hostnames.json()["items"][0]["hostname"] == "home.example.net"
|
||||
assert records.json()["items"][0]["ipv4"] == "203.0.113.10"
|
||||
assert events.json()["items"][0]["hostname"] == "home.example.net"
|
||||
|
||||
|
||||
def test_admin_records_and_events_reject_excessive_offsets(tmp_path: Path) -> None:
|
||||
app = make_app(DdnsSettings(database_path=tmp_path / "ddns.sqlite3", admin_password="admin"))
|
||||
client = TestClient(app)
|
||||
|
||||
records = client.get("/records?offset=10001", auth=("admin", "admin"))
|
||||
events = client.get("/events?offset=10001", auth=("admin", "admin"))
|
||||
records = client.get("/api/v1/admin/records?offset=10001", auth=("admin", "admin"))
|
||||
events = client.get("/api/v1/admin/events?offset=10001", auth=("admin", "admin"))
|
||||
|
||||
assert records.status_code == 422
|
||||
assert events.status_code == 422
|
||||
@@ -125,8 +161,8 @@ def test_update_events_support_keyset_pagination(tmp_path: Path) -> None:
|
||||
app = make_app(DdnsSettings(database_path=database_path, admin_password="admin"))
|
||||
client = TestClient(app)
|
||||
|
||||
first = client.get("/events?limit=1", auth=("admin", "admin")).json()
|
||||
second = client.get(f"/events?limit=1&before={first['next_before']}", auth=("admin", "admin")).json()
|
||||
first = client.get("/api/v1/admin/events?limit=1", auth=("admin", "admin")).json()
|
||||
second = client.get(f"/api/v1/admin/events?limit=1&before={first['next_before']}", auth=("admin", "admin")).json()
|
||||
|
||||
assert first["items"][0]["hostname"] == "raw-new.example.net"
|
||||
assert second["items"][0]["hostname"] == "raw-old.example.net"
|
||||
@@ -184,16 +220,16 @@ def test_admin_can_rotate_links_and_password(tmp_path: Path) -> None:
|
||||
csrf = admin_csrf_token(settings)
|
||||
|
||||
update = client.post(
|
||||
"/admin/accounts/rotate",
|
||||
"/admin/hostnames/rotate",
|
||||
auth=("admin", "admin"),
|
||||
data={"csrf": csrf, "hostname": "home.example.net", "action": "update"},
|
||||
)
|
||||
assert update.status_code == 200
|
||||
assert "Update URL changed" in update.text
|
||||
assert client.get(f"/u/{account['update_slug']}", params={"myip": "203.0.113.9"}).status_code == 404
|
||||
assert client.get(f"/api/v1/router/update/{account['update_slug']}", params={"myip": "203.0.113.9"}).status_code == 404
|
||||
|
||||
management = client.post(
|
||||
"/admin/accounts/rotate",
|
||||
"/admin/hostnames/rotate",
|
||||
auth=("admin", "admin"),
|
||||
data={"csrf": csrf, "hostname": "home.example.net", "action": "management"},
|
||||
)
|
||||
@@ -201,20 +237,10 @@ def test_admin_can_rotate_links_and_password(tmp_path: Path) -> None:
|
||||
assert "Dashboard link changed" in management.text
|
||||
assert "/m/" in management.text
|
||||
|
||||
password = client.post(
|
||||
"/admin/accounts/rotate",
|
||||
auth=("admin", "admin"),
|
||||
data={"csrf": csrf, "hostname": "home.example.net", "action": "password"},
|
||||
)
|
||||
assert password.status_code == 200
|
||||
assert "Router password changed" in password.text
|
||||
assert "Full update URL:" in password.text
|
||||
assert "/nic/update?" in password.text
|
||||
assert "Kennwort (optional):" in password.text
|
||||
statuses = {event["status"] for event in store.list_update_events_for_hostname("home.example.net", 10)}
|
||||
assert "admin_update_url_rotated" in statuses
|
||||
assert "admin_management_url_rotated" in statuses
|
||||
assert "admin_router_credentials_rotated" in statuses
|
||||
assert "admin_router_credentials_rotated" not in statuses
|
||||
|
||||
|
||||
def test_admin_delete_normalizes_hostname_and_logs_audit_event(tmp_path: Path) -> None:
|
||||
@@ -225,7 +251,7 @@ def test_admin_delete_normalizes_hostname_and_logs_audit_event(tmp_path: Path) -
|
||||
client = TestClient(app)
|
||||
|
||||
response = client.post(
|
||||
"/admin/accounts/delete",
|
||||
"/admin/hostnames/delete",
|
||||
auth=("admin", "admin"),
|
||||
data={"csrf": admin_csrf_token(settings), "hostname": " HOME.EXAMPLE.NET. "},
|
||||
follow_redirects=False,
|
||||
|
||||
Reference in new issue
Block a user