fix: config read API error handling, consistent 403 messages, deduplicate key lists
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+15
-17
@@ -104,14 +104,8 @@ def _can_own_host(user, host) -> bool:
|
|||||||
|
|
||||||
def _mask_config_for_api(config) -> dict:
|
def _mask_config_for_api(config) -> dict:
|
||||||
"""Return a JSON-serializable config dict with secrets masked."""
|
"""Return a JSON-serializable config dict with secrets masked."""
|
||||||
_SERVER_KEYS = [
|
|
||||||
"hbd_port", "hbd_host", "ws_port", "wss_port", "hb_port",
|
|
||||||
"interval", "grace", "base_url", "threshold_renotify_interval",
|
|
||||||
"logfile", "pidfile", "pickfile", "journal_enabled", "journal_dir",
|
|
||||||
"journal_max_size", "journal_max_backups", "default_owner",
|
|
||||||
]
|
|
||||||
result = {}
|
result = {}
|
||||||
result["server"] = {k: config.get(k) for k in _SERVER_KEYS}
|
result["server"] = {k: config.get(k) for k in configio_mod._SERVER_KEYS}
|
||||||
|
|
||||||
users = {}
|
users = {}
|
||||||
for username, attrs in (config.get("users") or {}).items():
|
for username, attrs in (config.get("users") or {}).items():
|
||||||
@@ -1031,38 +1025,42 @@ async def start(
|
|||||||
if err:
|
if err:
|
||||||
return err
|
return err
|
||||||
if user and not user.admin:
|
if user and not user.admin:
|
||||||
return web.json_response({"error": "Admin only"}, status=403)
|
return web.json_response({"error": "Forbidden"}, status=403)
|
||||||
return web.json_response(_mask_config_for_api(config))
|
return web.json_response(_mask_config_for_api(config))
|
||||||
|
|
||||||
|
_YAML_EXTRACTORS = {
|
||||||
|
"notification_channels": lambda d: d.get("notification_channels") or {},
|
||||||
|
"thresholds": lambda d: d.get("threshold_configs") or {},
|
||||||
|
"hosts": lambda d: d.get("hosts") or {},
|
||||||
|
"dns": lambda d: {k: d[k] for k in configio_mod._DNS_KEYS if k in d},
|
||||||
|
}
|
||||||
|
|
||||||
async def api_config_section_get(request):
|
async def api_config_section_get(request):
|
||||||
"""GET /api/0/config/section/{name} — raw YAML text for a YAML-editor section."""
|
"""GET /api/0/config/section/{name} — raw YAML text for a YAML-editor section."""
|
||||||
user, err = _require_auth(request)
|
user, err = _require_auth(request)
|
||||||
if err:
|
if err:
|
||||||
return err
|
return err
|
||||||
if user and not user.admin:
|
if user and not user.admin:
|
||||||
return web.json_response({"error": "Admin only"}, status=403)
|
return web.json_response({"error": "Forbidden"}, status=403)
|
||||||
if not _config_path:
|
if not _config_path:
|
||||||
return web.json_response({"error": "Config path not available"}, status=503)
|
return web.json_response({"error": "Config path not available"}, status=503)
|
||||||
|
|
||||||
name = request.match_info["name"]
|
name = request.match_info["name"]
|
||||||
_DNS_KEYS = ["nsupdate_bin", "dyndomains", "dyndnshosts", "drophosts"]
|
|
||||||
_YAML_EXTRACTORS = {
|
|
||||||
"notification_channels": lambda d: d.get("notification_channels") or {},
|
|
||||||
"thresholds": lambda d: d.get("threshold_configs") or {},
|
|
||||||
"hosts": lambda d: d.get("hosts") or {},
|
|
||||||
"dns": lambda d: {k: d[k] for k in _DNS_KEYS if k in d},
|
|
||||||
}
|
|
||||||
if name not in _YAML_EXTRACTORS:
|
if name not in _YAML_EXTRACTORS:
|
||||||
return web.json_response({"error": "Unknown section"}, status=404)
|
return web.json_response({"error": "Unknown section"}, status=404)
|
||||||
|
|
||||||
import io as _io
|
import io as _io
|
||||||
from ruamel.yaml import YAML as _YAML
|
from ruamel.yaml import YAML as _YAML
|
||||||
|
try:
|
||||||
data = configio_mod.read_roundtrip(_config_path)
|
data = configio_mod.read_roundtrip(_config_path)
|
||||||
section_data = _YAML_EXTRACTORS[name](data)
|
section_data = _YAML_EXTRACTORS[name](data)
|
||||||
_sy = _YAML()
|
_sy = _YAML()
|
||||||
_sy.preserve_quotes = True
|
_sy.preserve_quotes = True
|
||||||
buf = _io.StringIO()
|
buf = _io.StringIO()
|
||||||
_sy.dump(section_data, buf)
|
_sy.dump(section_data, buf)
|
||||||
|
except Exception as exc:
|
||||||
|
logger.error("Config section read failed: %s", exc)
|
||||||
|
return web.json_response({"error": str(exc)}, status=500)
|
||||||
return web.json_response({"yaml": buf.getvalue()})
|
return web.json_response({"yaml": buf.getvalue()})
|
||||||
|
|
||||||
async def api_config_backups_get(request):
|
async def api_config_backups_get(request):
|
||||||
@@ -1071,7 +1069,7 @@ async def start(
|
|||||||
if err:
|
if err:
|
||||||
return err
|
return err
|
||||||
if user and not user.admin:
|
if user and not user.admin:
|
||||||
return web.json_response({"error": "Admin only"}, status=403)
|
return web.json_response({"error": "Forbidden"}, status=403)
|
||||||
if not _config_path:
|
if not _config_path:
|
||||||
return web.json_response({"backups": []})
|
return web.json_response({"backups": []})
|
||||||
backups = configio_mod.list_backups(_config_path)
|
backups = configio_mod.list_backups(_config_path)
|
||||||
|
|||||||
Reference in New Issue
Block a user