From feaff42010d74e7ae2a5bdad7e0cc5cdcc45af3c Mon Sep 17 00:00:00 2001 From: Jonathan Date: Fri, 10 Jul 2026 20:07:49 +0200 Subject: [PATCH] feat: add worker config reader with secret decryption --- worker/lyra_worker/config.py | 14 ++++++++++++++ worker/tests/conftest.py | 2 ++ worker/tests/test_config.py | 35 +++++++++++++++++++++++++++++++++++ 3 files changed, 51 insertions(+) create mode 100644 worker/lyra_worker/config.py create mode 100644 worker/tests/test_config.py diff --git a/worker/lyra_worker/config.py b/worker/lyra_worker/config.py new file mode 100644 index 0000000..7fc1dbf --- /dev/null +++ b/worker/lyra_worker/config.py @@ -0,0 +1,14 @@ +import psycopg + +from lyra_worker.crypto import decrypt_secret + + +def get_config(conn: psycopg.Connection) -> dict[str, str]: + """Return all config as {key: value}, decrypting rows marked secret.""" + with conn.cursor() as cur: + cur.execute('SELECT key, value, secret FROM "Config"') + rows = cur.fetchall() + out: dict[str, str] = {} + for key, value, secret in rows: + out[key] = decrypt_secret(value) if secret else value + return out diff --git a/worker/tests/conftest.py b/worker/tests/conftest.py index 42d9594..4ff5caf 100644 --- a/worker/tests/conftest.py +++ b/worker/tests/conftest.py @@ -12,12 +12,14 @@ def conn(): with connection.cursor() as cur: cur.execute('DELETE FROM "Job"') cur.execute('DELETE FROM "Request"') + cur.execute('DELETE FROM "Config"') connection.commit() yield connection # Clean up rows created during the test (Job cascades from Request). with connection.cursor() as cur: cur.execute('DELETE FROM "Job"') cur.execute('DELETE FROM "Request"') + cur.execute('DELETE FROM "Config"') connection.commit() connection.close() diff --git a/worker/tests/test_config.py b/worker/tests/test_config.py new file mode 100644 index 0000000..111996c --- /dev/null +++ b/worker/tests/test_config.py @@ -0,0 +1,35 @@ +import pytest + +from lyra_worker.config import get_config +from lyra_worker.crypto import encrypt_secret + +TEST_KEY = "MDEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3ODlhYmNkZWY=" + + +@pytest.fixture(autouse=True) +def _key(monkeypatch): + monkeypatch.setenv("LYRA_SECRET_KEY", TEST_KEY) + + +def _put(conn, key, value, secret): + with conn.cursor() as cur: + cur.execute( + 'INSERT INTO "Config" (key, value, secret, "updatedAt") VALUES (%s, %s, %s, now()) ' + 'ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value, secret = EXCLUDED.secret', + (key, value, secret), + ) + conn.commit() + + +def test_reads_plain_and_decrypts_secret(conn): + _put(conn, "slskd.url", "http://slskd:5030", False) + _put(conn, "slskd.api_key", encrypt_secret("topsecret"), True) + + cfg = get_config(conn) + assert cfg["slskd.url"] == "http://slskd:5030" + assert cfg["slskd.api_key"] == "topsecret" # decrypted + + +def test_missing_keys_absent(conn): + cfg = get_config(conn) + assert "qobuz.password" not in cfg