feat: Add API key authentication for backup tool
- Add get_api_key() and require_api_key() to auth.py - Add generic key-value settings storage to SettingsStore - Add test_api_key_auth to verify the implementation - Uses secrets.compare_digest for timing-safe comparison - Auto-generates API key on first use and stores in settings DB
This commit is contained in:
@@ -1205,6 +1205,69 @@ class SettingsStore:
|
||||
)
|
||||
return int(cur.rowcount or 0)
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Generic key-value settings
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
def get_settings(self) -> dict[str, Any]:
|
||||
"""Return all generic settings as a dict."""
|
||||
self.init_schema()
|
||||
with self.connect() as conn:
|
||||
conn.execute(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS app_settings (
|
||||
key TEXT PRIMARY KEY,
|
||||
value TEXT NOT NULL,
|
||||
updated_at INTEGER NOT NULL
|
||||
)
|
||||
"""
|
||||
)
|
||||
rows = conn.execute("SELECT key, value FROM app_settings").fetchall()
|
||||
return {row["key"]: row["value"] for row in rows}
|
||||
|
||||
def get_setting(self, key: str, default: Any = None) -> Any:
|
||||
"""Return a single setting value or default."""
|
||||
self.init_schema()
|
||||
with self.connect() as conn:
|
||||
conn.execute(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS app_settings (
|
||||
key TEXT PRIMARY KEY,
|
||||
value TEXT NOT NULL,
|
||||
updated_at INTEGER NOT NULL
|
||||
)
|
||||
"""
|
||||
)
|
||||
row = conn.execute(
|
||||
"SELECT value FROM app_settings WHERE key = ?", (key,)
|
||||
).fetchone()
|
||||
return row["value"] if row else default
|
||||
|
||||
def update_setting(self, key: str, value: str) -> None:
|
||||
"""Set a generic key-value setting."""
|
||||
self.init_schema()
|
||||
now = int(time.time())
|
||||
with self.connect() as conn:
|
||||
conn.execute(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS app_settings (
|
||||
key TEXT PRIMARY KEY,
|
||||
value TEXT NOT NULL,
|
||||
updated_at INTEGER NOT NULL
|
||||
)
|
||||
"""
|
||||
)
|
||||
conn.execute(
|
||||
"""
|
||||
INSERT INTO app_settings (key, value, updated_at)
|
||||
VALUES (?, ?, ?)
|
||||
ON CONFLICT(key) DO UPDATE SET
|
||||
value = excluded.value,
|
||||
updated_at = excluded.updated_at
|
||||
""",
|
||||
(key, value, now),
|
||||
)
|
||||
|
||||
|
||||
_store: SettingsStore | None = None
|
||||
|
||||
|
||||
Reference in New Issue
Block a user