"""凭证管理模块。 使用 keyring 库安全存储 SAP 密码,优雅降级到 config.ini。 服务名格式: sap-cli:{host}:{client} """ from __future__ import annotations import argparse import getpass import logging # 从独立模块导入,避免与 config 循环 import from sapcli.password import ( # noqa: F401 – re-export _KEYRING_AVAILABLE, _keyring, _keyring_mod, _service_name, get_password, resolve_password, ) logger = logging.getLogger("sapcli.auth") def set_password(host: str, client: str, user: str, password: str) -> bool: """存储密码到 keyring。 Returns: 是否成功存储 """ if not _KEYRING_AVAILABLE: print(" ✗ keyring 库不可用,无法安全存储密码") print(" 安装方法: pip install keyring") return False try: _keyring.set_password(_service_name(host, client), user, password) return True except Exception as e: print(f" ✗ 存储密码失败: {e}") return False def delete_password(host: str, client: str, user: str) -> bool: """从 keyring 删除密码。 Returns: 是否成功删除 """ if not _KEYRING_AVAILABLE: print(" ✗ keyring 库不可用") return False try: _keyring.delete_password(_service_name(host, client), user) return True except _keyring_mod.errors.PasswordDeleteError: print(" ℹ keyring 中未找到该密码") return False except Exception as e: print(f" ✗ 删除密码失败: {e}") return False def cmd_auth_login(args: argparse.Namespace, client=None) -> None: """auth login: 交互式保存密码到 keyring。""" import os from sapcli.config import load_config try: cfg, _ = load_config(getattr(args, "config", None)) except Exception as e: print(f" ✗ 无法加载配置: {e}") return print("=" * 60) print(" sap-cli 密钥登录") print("=" * 60) print(f" 主机: {cfg.host}") print(f" Client: {cfg.client}") print(f" 用户: {cfg.user}") password = getpass.getpass(" 请输入密码: ") if not password: print(" ✗ 密码不能为空") return if set_password(cfg.host, cfg.client, cfg.user, password): print(f" ✓ 密码已安全存储到 keyring") print(f" 服务名: {_service_name(cfg.host, cfg.client)}") else: print(" ✗ 密码存储失败") def cmd_auth_logout(args: argparse.Namespace, client=None) -> None: """auth logout: 从 keyring 删除密码。""" from sapcli.config import load_config try: cfg, _ = load_config(getattr(args, "config", None)) except Exception as e: print(f" ✗ 无法加载配置: {e}") return if delete_password(cfg.host, cfg.client, cfg.user): print(" ✓ 密码已从 keyring 删除") else: print(" ✗ 删除失败") def cmd_auth_status(args: argparse.Namespace, client=None) -> None: """auth status: 显示 keyring 状态。""" print("=" * 60) print(" sap-cli 密钥状态") print("=" * 60) if _KEYRING_AVAILABLE: print(f" keyring: ✓ 可用") try: backend = _keyring.get_keyring().__class__.__name__ print(f" 后端: {backend}") except Exception: logger.debug("获取 keyring 后端名称失败", exc_info=True) else: print(f" keyring: ✗ 不可用") print(f" 安装方法: pip install keyring")