#!/usr/bin/env python3 """ CLI de gestion du coffre-fort de credentials (vault). Usage : # Ajouter un login python -m core.auth.manage_vault --vault /path/to/vault.enc --action add \ --app "DPI_Crossway" --type login \ --username "robot_lea" --password "xxx" # Ajouter un seed TOTP python -m core.auth.manage_vault --vault /path/to/vault.enc --action add \ --app "DPI_Crossway" --type totp_seed \ --secret "JBSWY3DPEHPK3PXP" # Lister les applications configurées python -m core.auth.manage_vault --vault /path/to/vault.enc --action list # Générer un code TOTP python -m core.auth.manage_vault --vault /path/to/vault.enc --action generate-totp \ --app "DPI_Crossway" # Supprimer un credential python -m core.auth.manage_vault --vault /path/to/vault.enc --action remove \ --app "DPI_Crossway" --type login Le mot de passe maître est demandé interactivement via getpass. """ import argparse import getpass import sys from .credential_vault import CredentialVault from .totp_generator import TOTPGenerator def main(): parser = argparse.ArgumentParser( description="Gestionnaire de coffre-fort de credentials pour Léa.", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=__doc__, ) parser.add_argument( "--vault", required=True, help="Chemin du fichier vault chiffré", ) parser.add_argument( "--action", required=True, choices=["add", "list", "remove", "generate-totp", "show"], help="Action à effectuer", ) parser.add_argument("--app", help="Nom de l'application") parser.add_argument( "--type", dest="cred_type", choices=["login", "totp_seed", "session_token", "certificate"], help="Type de credential", ) # Champs pour le type "login" parser.add_argument("--username", help="Nom d'utilisateur (type login)") parser.add_argument("--password", help="Mot de passe (type login)") parser.add_argument("--domain", help="Domaine Windows (type login, optionnel)") # Champs pour le type "totp_seed" parser.add_argument("--secret", help="Secret base32 (type totp_seed)") parser.add_argument( "--digits", type=int, default=6, help="Nombre de chiffres TOTP (défaut: 6)" ) parser.add_argument( "--interval", type=int, default=30, help="Intervalle TOTP en secondes (défaut: 30)" ) parser.add_argument( "--algorithm", default="SHA1", help="Algorithme HMAC (défaut: SHA1)" ) args = parser.parse_args() # Demander le mot de passe maître master_password = getpass.getpass("Mot de passe maître : ") if not master_password: print("Erreur : mot de passe maître requis.", file=sys.stderr) sys.exit(1) try: vault = CredentialVault(args.vault, master_password) except ValueError as e: print(f"Erreur d'ouverture du vault : {e}", file=sys.stderr) sys.exit(1) # ---- Actions ---- if args.action == "list": apps = vault.list_apps() if not apps: print("Vault vide — aucune application configurée.") else: print(f"Applications configurées ({len(apps)}) :") for app in apps: types = vault.list_credential_types(app) print(f" {app} : {', '.join(types)}") elif args.action == "add": if not args.app: print("Erreur : --app requis pour l'action 'add'.", file=sys.stderr) sys.exit(1) if not args.cred_type: print("Erreur : --type requis pour l'action 'add'.", file=sys.stderr) sys.exit(1) if args.cred_type == "login": if not args.username: args.username = input("Username : ") if not args.password: args.password = getpass.getpass("Password : ") data = {"username": args.username, "password": args.password} if args.domain: data["domain"] = args.domain elif args.cred_type == "totp_seed": if not args.secret: args.secret = input("Secret base32 : ") data = { "secret": args.secret, "digits": args.digits, "interval": args.interval, "algorithm": args.algorithm, } elif args.cred_type == "session_token": token = input("Token de session : ") data = {"token": token} elif args.cred_type == "certificate": cert_path = input("Chemin du certificat : ") key_path = input("Chemin de la clé privée : ") data = {"cert_path": cert_path, "key_path": key_path} else: print(f"Type non géré : {args.cred_type}", file=sys.stderr) sys.exit(1) vault.add_credential(args.app, args.cred_type, data) vault.save() print(f"Credential ajouté : {args.app} / {args.cred_type}") elif args.action == "remove": if not args.app or not args.cred_type: print( "Erreur : --app et --type requis pour l'action 'remove'.", file=sys.stderr, ) sys.exit(1) removed = vault.remove_credential(args.app, args.cred_type) if removed: vault.save() print(f"Credential supprimé : {args.app} / {args.cred_type}") else: print(f"Credential non trouvé : {args.app} / {args.cred_type}") elif args.action == "generate-totp": if not args.app: print( "Erreur : --app requis pour l'action 'generate-totp'.", file=sys.stderr, ) sys.exit(1) totp_creds = vault.get_credential(args.app, "totp_seed") if not totp_creds: print( f"Pas de seed TOTP configuré pour '{args.app}'.", file=sys.stderr, ) sys.exit(1) totp = TOTPGenerator( secret=totp_creds["secret"], digits=totp_creds.get("digits", 6), interval=totp_creds.get("interval", 30), algorithm=totp_creds.get("algorithm", "SHA1"), ) code = totp.generate() remaining = totp.time_remaining() print(f"Code TOTP : {code}") print(f"Expire dans : {remaining}s") elif args.action == "show": if not args.app: print( "Erreur : --app requis pour l'action 'show'.", file=sys.stderr, ) sys.exit(1) types = vault.list_credential_types(args.app) if not types: print(f"Aucun credential pour '{args.app}'.") else: print(f"Credentials pour '{args.app}' :") for cred_type in types: cred = vault.get_credential(args.app, cred_type) # Masquer les mots de passe et secrets display = {} for k, v in (cred or {}).items(): if k in ("password", "secret", "token"): display[k] = v[:3] + "***" if len(str(v)) > 3 else "***" else: display[k] = v print(f" {cred_type} : {display}") if __name__ == "__main__": main()