Pipeline replay visuel : - VLM-first : l'agent appelle Ollama directement pour trouver les éléments - Template matching en fallback (seuil strict 0.90) - Stop immédiat si élément non trouvé (pas de clic blind) - Replay depuis session brute (/replay-session) sans attendre le VLM - Vérification post-action (screenshot hash avant/après) - Gestion des popups (Enter/Escape/Tab+Enter) Worker VLM séparé : - run_worker.py : process distinct du serveur HTTP - Communication par fichiers (_worker_queue.txt + _replay_active.lock) - Le serveur HTTP ne fait plus jamais de VLM → toujours réactif - Service systemd rpa-worker.service Capture clavier : - raw_keys (vk + press/release) pour replay exact indépendant du layout - Fix AZERTY : ToUnicodeEx + AltGr detection - Enter capturé comme \n, Tab comme \t - Filtrage modificateurs seuls (Ctrl/Alt/Shift parasites) - Fusion text_input consécutifs, dédup key_combo Sécurité & Internet : - HTTPS Let's Encrypt (lea.labs + vwb.labs.laurinebazin.design) - Token API fixe dans .env.local - HTTP Basic Auth sur VWB - Security headers (HSTS, CSP, nosniff) - CORS domaines publics, plus de wildcard Infrastructure : - DPI awareness (SetProcessDpiAwareness) Python + Rust - Métadonnées système (dpi_scale, window_bounds, monitors, os_theme) - Template matching multi-scale [0.5, 2.0] - Résolution dynamique (plus de hardcode 1920x1080) - VLM prefill fix (47x speedup, 3.5s au lieu de 180s) Modules : - core/auth/ : credential vault (Fernet AES), TOTP (RFC 6238), auth handler - core/federation/ : LearningPack export/import anonymisé, FAISS global - deploy/ : package Léa (config.txt, Lea.bat, install.bat, LISEZMOI.txt) UX : - Filtrage OS (VWB + Chat montrent que les workflows de l'OS courant) - Bibliothèque persistante (cache local + SQLite) - Clustering hybride (titre fenêtre + DBSCAN) - EdgeConstraints + PostConditions peuplés - GraphBuilder compound actions (toutes les frappes) Agent Rust : - Token Bearer auth (network.rs) - sysinfo.rs (DPI, résolution, window bounds via Win32 API) - config.txt lu automatiquement - Support Chrome/Brave/Firefox (pas que Edge) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
214 lines
7.2 KiB
Python
214 lines
7.2 KiB
Python
#!/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()
|