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
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
|
|
|