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.
37 lines
1.1 KiB
37 lines
1.1 KiB
"""Secure API-key storage backed by the operating system keyring.""" |
|
|
|
from __future__ import annotations |
|
|
|
import keyring |
|
|
|
|
|
SERVICE_NAME = "voice-transcriptor" |
|
ACCOUNT_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: |
|
return self._backend.get_password(SERVICE_NAME, ACCOUNT_NAME) |
|
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: |
|
self._backend.set_password(SERVICE_NAME, ACCOUNT_NAME, value) |
|
except Exception: |
|
raise CredentialError("Unable to save the API key.") from None
|
|
|