Files
nscertkeycreate/legacy/certctl/secretstore.py
deamonkai fc94008530 initial
2026-01-23 12:11:21 -06:00

81 lines
2.2 KiB
Python

"""Small secret storage abstraction.
For now:
- macOS: uses the built-in Keychain via the `security` CLI.
- other OSes: prompts only (no persistent storage) unless CERTCTL_* env vars used.
This keeps dependencies minimal and avoids coupling to a specific enterprise vault.
"""
from __future__ import annotations
import os
import platform
import subprocess
from typing import Optional
def _is_macos() -> bool:
return platform.system().lower() == "darwin"
def get_from_keychain(service: str, account: str) -> Optional[str]:
if not _is_macos():
return None
try:
# -w prints password only
out = subprocess.check_output(
["security", "find-generic-password", "-s", service, "-a", account, "-w"],
stderr=subprocess.DEVNULL,
)
return out.decode().strip()
except subprocess.CalledProcessError:
return None
def set_in_keychain(service: str, account: str, secret: str) -> None:
if not _is_macos():
return
# Add or update
subprocess.check_call(
[
"security",
"add-generic-password",
"-U",
"-s",
service,
"-a",
account,
"-w",
secret,
],
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
)
def env_or_keychain(env_var: str, service: str, account: str) -> Optional[str]:
v = os.environ.get(env_var)
if v:
return v
return get_from_keychain(service, account)
# ---------------------------------------------------------------------------
# Backwards-compatible aliases
# ---------------------------------------------------------------------------
def is_macos_keychain_available() -> bool:
"""Return True if this host is macOS (Keychain supported via `security`)."""
return _is_macos()
def get_secret(service: str, account: str) -> Optional[str]:
"""Alias for get_from_keychain(service, account)."""
return get_from_keychain(service, account)
def set_secret(service: str, account: str, secret: str) -> None:
"""Alias for set_in_keychain(service, account, secret)."""
set_in_keychain(service, account, secret)