diff --git a/AGENTS.md b/AGENTS.md index b73474f..a51caa1 120000 --- a/AGENTS.md +++ b/AGENTS.md @@ -1 +1 @@ -/srv/helpers/_Assistant_Lead_Tech/CLAUDE.md \ No newline at end of file +/Users/maks/AI_RULES/_Assistant_Lead_Tech/CLAUDE.md \ No newline at end of file diff --git a/scripts/aliases.sh b/scripts/aliases.sh index 39e7115..6515b98 100755 --- a/scripts/aliases.sh +++ b/scripts/aliases.sh @@ -29,11 +29,11 @@ alias bmad-init="\"\$LEADTECH/scripts/bmad-init-project.sh\"" alias projects='cd /Volumes/TeraSSD/Projets_Dev 2>/dev/null || cd /srv/projects' -# Load global secrets (KeePass → env) +# Load global secrets (Bitwarden → env) alias loadg="source \"\$LEADTECH/scripts/load-global-secrets.sh\"" -# Sync service secrets (KeePass → service.env) +# Sync service secrets (Bitwarden → service.env) alias sync-service="source \"\$LEADTECH/scripts/sync-service-secrets.sh\"" -# Sync project secrets (KeePass → .env du projet courant) +# Sync project secrets (Bitwarden → .env du projet courant) alias sync-project="source \"\$LEADTECH/scripts/sync-project-secrets.sh\"" diff --git a/scripts/env_paths.sh b/scripts/env_paths.sh index 27d6e08..43d83c1 100755 --- a/scripts/env_paths.sh +++ b/scripts/env_paths.sh @@ -1,16 +1,4 @@ #!/usr/bin/env bash -case "$(uname -s)" in - Darwin) - export SECRETS_KDBX="/Volumes/TeraSSD/Max_Perso/Pièces Importantes/MDPs/env_and_co.kdbx" - export AUTO_SCRIPTS_DIR="$HOME/AI_RULES/Auto_scripts" - ;; - Linux) - export SECRETS_KDBX="$HOME/.config/auto-secrets/env_and_co.kdbx" - export AUTO_SCRIPTS_DIR="$HOME/.config/auto-secrets" - ;; - *) - echo "OS non supporté" >&2 - return 1 - ;; -esac +export BW_SERVER_URL="https://vaultwarden.wyvern-snapper.ts.net" +export AUTO_SCRIPTS_DIR="$HOME/.config/auto-secrets" diff --git a/scripts/load-global-secrets.sh b/scripts/load-global-secrets.sh index d8b7cfb..c98ea92 100755 --- a/scripts/load-global-secrets.sh +++ b/scripts/load-global-secrets.sh @@ -3,48 +3,56 @@ _load_global_secrets() { source "$LEADTECH/scripts/env_paths.sh" || { echo "env_paths.sh introuvable" >&2; return 1; } - if [ ! -f "$SECRETS_KDBX" ]; then - echo "Coffre introuvable : $SECRETS_KDBX" >&2 + if ! command -v bw >/dev/null 2>&1; then + echo "bw (Bitwarden CLI) introuvable" >&2 return 1 fi - if ! command -v keepassxc-cli >/dev/null 2>&1; then - echo "keepassxc-cli introuvable" >&2 - return 1 - fi + # Configurer le serveur si pas déjà fait + bw config server "$BW_SERVER_URL" >/dev/null 2>&1 - if [ -z "${KDBX_PASSWORD:-}" ]; then - printf "Mot de passe KeePassXC : " >&2 + # Demander le master password si pas en variable + if [ -z "${BW_MASTER_PASSWORD:-}" ]; then + printf "Master password Bitwarden : " >&2 stty -echo - IFS= read -r KDBX_PASSWORD + IFS= read -r BW_MASTER_PASSWORD stty echo printf '\n' >&2 fi + # Unlock et récupérer le session token + local session + session=$(BW_MASTER_PASSWORD="$BW_MASTER_PASSWORD" bw unlock --passwordenv BW_MASTER_PASSWORD --raw 2>/dev/null) + if [ -z "$session" ]; then + echo "Échec du déverrouillage Bitwarden." >&2 + return 1 + fi + export BW_SESSION="$session" + echo "Chargement des secrets globaux..." >&2 - local csv - csv=$(printf '%s\n' "$KDBX_PASSWORD" | keepassxc-cli export --format csv "$SECRETS_KDBX" 2>/dev/null) || { - echo "Impossible d'exporter le coffre." >&2 + # Récupérer l'id du dossier "global" + local folder_id + folder_id=$(bw list folders --session "$BW_SESSION" 2>/dev/null \ + | python3 -c "import sys,json; folders=json.load(sys.stdin); print(next((f['id'] for f in folders if f['name']=='global'), ''))") + + if [ -z "$folder_id" ]; then + echo "Dossier 'global' introuvable dans Bitwarden." >&2 return 1 - } + fi + # Lister les items du dossier et extraire TITRE=password local pairs - pairs=$(printf '%s' "$csv" | python3 -c " -import sys, csv, re, io + pairs=$(bw list items --folderid "$folder_id" --session "$BW_SESSION" 2>/dev/null \ + | python3 -c " +import sys, json, re -raw = sys.stdin.read() -start = raw.find('\"Group\"') -if start == -1: - sys.exit(0) -reader = csv.DictReader(io.StringIO(raw[start:])) -for row in reader: - group = row.get('Group', '') - title = row.get('Title', '') - password = row.get('Password', '') - if group != 'Racine/global' and not group.startswith('Racine/global/'): - continue - if not re.match(r'^[A-Z_][A-Z0-9_]*\$', title): +items = json.load(sys.stdin) +for item in items: + title = item.get('name', '') + password = (item.get('login') or {}).get('password') or '' + password = password.strip() + if not re.match(r'^[A-Z_][A-Z0-9_]*$', title): continue if not password: continue diff --git a/scripts/sync-project-secrets.sh b/scripts/sync-project-secrets.sh index 989127c..4eefe45 100755 --- a/scripts/sync-project-secrets.sh +++ b/scripts/sync-project-secrets.sh @@ -3,64 +3,67 @@ _sync_project_secrets() { source "$LEADTECH/scripts/env_paths.sh" || { echo "env_paths.sh introuvable" >&2; return 1; } - if [ ! -f "$SECRETS_KDBX" ]; then - echo "Coffre introuvable : $SECRETS_KDBX" >&2 + if ! command -v bw >/dev/null 2>&1; then + echo "bw (Bitwarden CLI) introuvable" >&2 return 1 fi - if ! command -v keepassxc-cli >/dev/null 2>&1; then - echo "keepassxc-cli introuvable" >&2 - return 1 - fi + bw config server "$BW_SERVER_URL" >/dev/null 2>&1 - if ! command -v expect >/dev/null 2>&1; then - echo "expect introuvable" >&2 - return 1 - fi - - # Nom du projet = nom du dossier courant local project_name project_name="$(basename "$PWD")" - local entry_path="projects/$project_name" local target_file="$PWD/.env" echo "Projet détecté : $project_name" >&2 - echo "Entrée KeePass : $entry_path" >&2 - if [ -z "${KDBX_PASSWORD:-}" ]; then - printf "Mot de passe KeePassXC : " >&2 - stty -echo - IFS= read -r KDBX_PASSWORD - stty echo - printf '\n' >&2 + # Unlock si pas de session active + if [ -z "${BW_SESSION:-}" ]; then + if [ -z "${BW_MASTER_PASSWORD:-}" ]; then + printf "Master password Bitwarden : " >&2 + stty -echo + IFS= read -r BW_MASTER_PASSWORD + stty echo + printf '\n' >&2 + fi + BW_SESSION=$(BW_MASTER_PASSWORD="$BW_MASTER_PASSWORD" bw unlock --passwordenv BW_MASTER_PASSWORD --raw 2>/dev/null) + if [ -z "$BW_SESSION" ]; then + echo "Échec du déverrouillage Bitwarden." >&2 + return 1 + fi + export BW_SESSION fi echo "Récupération des secrets projet..." >&2 - # Lire le champ Notes de l'entrée — une seule ouverture du coffre - local notes - notes=$(KDBX_PASSWORD="$KDBX_PASSWORD" SECRETS_KDBX="$SECRETS_KDBX" ENTRY_PATH="$entry_path" expect <<'EOF' - log_user 0 - set timeout 15 - spawn keepassxc-cli show -a notes $env(SECRETS_KDBX) $env(ENTRY_PATH) - expect "Saisir le mot de passe pour déverrouiller*" - send -- "$env(KDBX_PASSWORD)\r" - expect eof - catch wait result - puts -nonewline $expect_out(buffer) - exit [lindex $result 3] -EOF - ) || { - echo "Impossible de lire l'entrée '$entry_path'." >&2 - return 1 - } + # Récupérer l'id du dossier "projects/" + local folder_name="projects/$project_name" + local folder_id + folder_id=$(bw list folders --session "$BW_SESSION" 2>/dev/null \ + | python3 -c "import sys,json; folders=json.load(sys.stdin); print(next((f['id'] for f in folders if f['name']=='$folder_name'), ''))") - if [ -z "$notes" ]; then - echo "Le champ Notes est vide pour '$project_name'." >&2 + if [ -z "$folder_id" ]; then + echo "Dossier '$folder_name' introuvable dans Bitwarden." >&2 + return 1 + fi + + # Chercher l'item dont le nom == project_name dans ce dossier + local notes + notes=$(bw list items --folderid "$folder_id" --session "$BW_SESSION" 2>/dev/null \ + | python3 -c " +import sys, json + +items = json.load(sys.stdin) +for item in items: + if item.get('name') == '$project_name': + print((item.get('notes') or '').strip()) + break +") + + if [ -z "$notes" ]; then + echo "Aucune note trouvée pour '$project_name' dans Bitwarden." >&2 return 1 fi - # Écrire le .env printf '%s\n' "$notes" > "$target_file" chmod 600 "$target_file" diff --git a/scripts/sync-service-secrets.sh b/scripts/sync-service-secrets.sh index 8243ad6..6e9e25a 100755 --- a/scripts/sync-service-secrets.sh +++ b/scripts/sync-service-secrets.sh @@ -3,53 +3,59 @@ _sync_service_secrets() { source "$LEADTECH/scripts/env_paths.sh" || { echo "env_paths.sh introuvable" >&2; return 1; } - if [ ! -f "$SECRETS_KDBX" ]; then - echo "Coffre introuvable : $SECRETS_KDBX" >&2 + if ! command -v bw >/dev/null 2>&1; then + echo "bw (Bitwarden CLI) introuvable" >&2 return 1 fi - if ! command -v keepassxc-cli >/dev/null 2>&1; then - echo "keepassxc-cli introuvable" >&2 - return 1 - fi + bw config server "$BW_SERVER_URL" >/dev/null 2>&1 - local target_file="$HOME/.config/auto-secrets/service.env" + local target_file="$AUTO_SCRIPTS_DIR/service.env" mkdir -p "$(dirname "$target_file")" touch "$target_file" chmod 600 "$target_file" - if [ -z "${KDBX_PASSWORD:-}" ]; then - printf "Mot de passe KeePassXC : " >&2 - stty -echo - IFS= read -r KDBX_PASSWORD - stty echo - printf '\n' >&2 + # Unlock si pas de session active + if [ -z "${BW_SESSION:-}" ]; then + if [ -z "${BW_MASTER_PASSWORD:-}" ]; then + printf "Master password Bitwarden : " >&2 + stty -echo + IFS= read -r BW_MASTER_PASSWORD + stty echo + printf '\n' >&2 + fi + BW_SESSION=$(BW_MASTER_PASSWORD="$BW_MASTER_PASSWORD" bw unlock --passwordenv BW_MASTER_PASSWORD --raw 2>/dev/null) + if [ -z "$BW_SESSION" ]; then + echo "Échec du déverrouillage Bitwarden." >&2 + return 1 + fi + export BW_SESSION fi echo "Sync des secrets de service..." >&2 - local csv - csv=$(printf '%s\n' "$KDBX_PASSWORD" | keepassxc-cli export --format csv "$SECRETS_KDBX" 2>/dev/null) || { - echo "Impossible d'exporter le coffre." >&2 + # Récupérer l'id du dossier "services" + local folder_id + folder_id=$(bw list folders --session "$BW_SESSION" 2>/dev/null \ + | python3 -c "import sys,json; folders=json.load(sys.stdin); print(next((f['id'] for f in folders if f['name']=='services'), ''))") + + if [ -z "$folder_id" ]; then + echo "Dossier 'services' introuvable dans Bitwarden." >&2 return 1 - } + fi + # Extraire TITRE=password pour chaque item dont le nom est une var d'env valide local rendered_lines - rendered_lines=$(printf '%s' "$csv" | python3 -c " -import sys, csv, re, io + rendered_lines=$(bw list items --folderid "$folder_id" --session "$BW_SESSION" 2>/dev/null \ + | python3 -c " +import sys, json, re -raw = sys.stdin.read() -start = raw.find('\"Group\"') -if start == -1: - sys.exit(0) -reader = csv.DictReader(io.StringIO(raw[start:])) -for row in reader: - group = row.get('Group', '') - title = row.get('Title', '') - password = row.get('Password', '') - if group != 'Racine/services' and not group.startswith('Racine/services/'): - continue - if not re.match(r'^[A-Z_][A-Z0-9_]*\$', title): +items = json.load(sys.stdin) +for item in items: + title = item.get('name', '') + password = (item.get('login') or {}).get('password') or '' + password = password.strip() + if not re.match(r'^[A-Z_][A-Z0-9_]*$', title): continue if not password: continue