feat: add secure secret provider

This commit is contained in:
2026-07-30 17:47:36 +02:00
parent a28e2d8bc9
commit 570e7fa153
7 changed files with 471 additions and 7 deletions
+48
View File
@@ -3,6 +3,7 @@
from __future__ import annotations
import argparse
import getpass
import sys
from collections.abc import Callable, Sequence
@@ -16,6 +17,7 @@ from javis.memory.sqlite_store import (
)
from javis.providers.base import ProviderError
from javis.providers.ollama import OllamaProvider
from javis.security.secrets import SecretProvider, SecretStoreError
InputFunction = Callable[[str], str]
OutputFunction = Callable[[str], None]
@@ -34,9 +36,49 @@ def build_parser() -> argparse.ArgumentParser:
subparsers = parser.add_subparsers(dest="command", required=True)
chat_parser = subparsers.add_parser("chat", help="lokalen Textchat starten")
chat_parser.add_argument("--session", help="vorhandene Sitzungs-ID laden")
secrets_parser = subparsers.add_parser("secrets", help="lokale Geheimwerte sicher verwalten")
secret_subparsers = secrets_parser.add_subparsers(dest="secret_action", required=True)
set_parser = secret_subparsers.add_parser("set", help="Geheimwert verdeckt setzen")
set_parser.add_argument("secret_name", choices=["gemini"])
secret_subparsers.add_parser("status", help="nur Verfügbarkeit anzeigen")
delete_parser = secret_subparsers.add_parser("delete", help="Geheimwert löschen")
delete_parser.add_argument("secret_name", choices=["gemini"])
return parser
def run_secrets(
action: str,
*,
provider: SecretProvider | None = None,
password_fn: Callable[[str], str] = getpass.getpass,
output: OutputFunction = print,
) -> int:
secret_provider = provider or SecretProvider()
try:
if action == "status":
state = "verfügbar" if secret_provider.gemini_key_available() else "nicht verfügbar"
output(f"Gemini-Schlüssel: {state}")
return 0
if action == "set":
value = password_fn("Gemini API-Key: ")
secret_provider.set_gemini_key(value)
output("Gemini-Schlüssel wurde sicher gespeichert.")
return 0
if action == "delete":
deleted = secret_provider.delete_gemini_key()
output(
"Gemini-Schlüssel wurde gelöscht."
if deleted
else "Gemini-Schlüssel war nicht verfügbar."
)
return 0
except SecretStoreError as exc:
output(f"Fehler: {exc}")
return 2
output("Unbekannte Secret-Aktion.")
return 2
def _show_history(service: ChatService, session_id: str, output: OutputFunction) -> None:
loaded = service.load_session(session_id)
if not loaded.messages:
@@ -155,6 +197,12 @@ def main(
output: OutputFunction = print,
) -> int:
args = build_parser().parse_args(argv)
if args.command == "secrets":
return run_secrets(
args.secret_action,
password_fn=input_fn if input_fn is not input else getpass.getpass,
output=output,
)
if args.command != "chat":
return 2