You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

39 lines
1.3 KiB

"""Secure API-key storage backed by the operating system keyring."""
from __future__ import annotations
import keyring
TARGET_NAME = "OPENAI_API_KEY"
class CredentialError(Exception):
"""A credential operation failed without exposing backend details."""
class CredentialService:
"""Read and write the application's API key through a keyring backend."""
def __init__(self, backend=keyring) -> None:
self._backend = backend
def get_api_key(self) -> str | None:
try:
credential = self._backend.get_credential(TARGET_NAME, None)
return credential.password if credential is not None else None
except Exception:
raise CredentialError("Unable to access the saved API key.") from None
def has_api_key(self) -> bool:
return bool(self.get_api_key())
def set_api_key(self, value: str) -> None:
if not isinstance(value, str) or not value.strip():
raise CredentialError("An API key is required.")
try:
credential = self._backend.get_credential(TARGET_NAME, None)
username = credential.username if credential is not None else TARGET_NAME
self._backend.set_password(TARGET_NAME, username, value)
except Exception:
raise CredentialError("Unable to save the API key.") from None