backend/cuentas.py: restablece en bloque (todos menos leo y claude) y da de alta a varios desde backend/cuentas/2026-10-23_alta-sitios.json (jesush, miguel, joel, antonio, danielg); la contraseña la pide en la terminal. Miembros con sitio, correo y correo_alt; marca contrasena_inicial con aviso en el sitio hasta que la persona la cambie. Los ids de responsable se recalculan con el equipo de hoy. Lote 2026-10-23_usuarios-campo. Pruebas: tests-cuentas.sh (47). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016X4CAujMuib2tDqt9JRg6X
98 lines
8.6 KiB
Bash
98 lines
8.6 KiB
Bash
#!/usr/bin/env bash
|
|
# Pruebas de cuentas.py y de la marca «contraseña inicial» (23-oct). Todo en carpetas temporales: no toca nada real.
|
|
# Uso: bash tests-cuentas.sh <ruta-a-hbox-docs> [puerto]
|
|
set -u
|
|
REPO=${1:?ruta a hbox-docs}; PORT=${2:-8094}
|
|
B=$(cd "$REPO/backend" && pwd)
|
|
T=$(mktemp -d)
|
|
export ACTAS_DIR=$T/actas HTPASSWD_FILE=$T/htpasswd HTPASSWD_LOCK=$T/.lock ACTAS_ADMINS=leo PORT
|
|
mkdir -p "$ACTAS_DIR" "$T/equipo"
|
|
ok=0; fail=0
|
|
check() { if [[ "$3" == *"$2"* ]]; then ok=$((ok+1)); echo " ✓ $1"; else fail=$((fail+1)); echo " ✕ $1 — esperaba «$2», obtuve: ${3:0:240}"; fi; }
|
|
py() { python3 -c "import sys; sys.path.insert(0, '$B'); import app as A, json, os; $1"; }
|
|
|
|
# punto de partida: 7 cuentas con contraseñas conocidas; luz sin miembro; adriana ya tiene correo
|
|
py "
|
|
open('$HTPASSWD_FILE', 'w').write(''.join(u + ':' + A.apr1('vieja-' + u, 'salt' + u[:4]) + '\n' for u in ('leo','adriana','alan','jesus','daniel','claude','luz')))
|
|
for u, n, a, c in (('leo','Leopoldo Rodríguez','direccion',''), ('adriana','Adriana','operaciones','adriana@otro.mx'),
|
|
('alan','Alán Basurto','campo',''), ('jesus','Jesús','pruebas',''), ('daniel','Daniel Martínez','campo',''),
|
|
('claude','Claude','asistente','')):
|
|
json.dump({'id': u, 'nombre': n, 'area': a, 'rol': '', 'correo': c, 'telefono': '', 'activo': True}, open('$T/equipo/' + u + '.json', 'w'), ensure_ascii=False)
|
|
"
|
|
ANTES=$(sha256sum "$HTPASSWD_FILE" | cut -c1-16)
|
|
LEO_ANTES=$(grep '^leo:' "$HTPASSWD_FILE")
|
|
ALTA=$B/cuentas/2026-10-23_alta-sitios.json
|
|
verifica() { py "
|
|
l = dict(x.split(':', 1) for x in open('$HTPASSWD_FILE').read().split())
|
|
r = []
|
|
for u in '$1'.split():
|
|
h = l.get(u, ''); p = h.split('\$')
|
|
r.append(u + ('=ok' if h and A.apr1('$2', p[2]) == h else '=NO'))
|
|
print(' '.join(r))"; }
|
|
|
|
echo "## plan y «no»"
|
|
OUT=$(echo no | python3 "$B/cuentas.py" todo "$ALTA" 2>&1)
|
|
check "muestra las 5 altas" "Altas nuevas (5)" "$OUT"
|
|
check "restablece a 4 (leo y claude excepto; luz sin miembro)" "Contraseña restablecida (4): adriana, alan, daniel, jesus" "$OUT"
|
|
check "avisa de luz" "sin miembro activo" "$OUT"
|
|
check "jesus pasa a Jesús Romero" "nombre → Jesús Romero" "$OUT"
|
|
check "con «no» no escribe" "No se cambió nada" "$OUT"
|
|
check "el archivo sigue igual" "$ANTES" "$(sha256sum "$HTPASSWD_FILE" | cut -c1-16)"
|
|
|
|
echo "## todo, de verdad"
|
|
OUT=$(printf 'Helgen2026!\n' | python3 "$B/cuentas.py" todo "$ALTA" --si --password-stdin 2>&1)
|
|
check "termina" "Listo." "$OUT"
|
|
check "creados" "creados: jesush, miguel, joel, antonio, danielg" "$OUT"
|
|
check "restablecidos y nuevos entran con la nueva" "adriana=ok alan=ok jesus=ok daniel=ok jesush=ok miguel=ok joel=ok antonio=ok danielg=ok" "$(verifica 'adriana alan jesus daniel jesush miguel joel antonio danielg' 'Helgen2026!')"
|
|
check "leo no cambia" "$LEO_ANTES" "$(grep '^leo:' "$HTPASSWD_FILE")"
|
|
check "claude y luz siguen con la vieja" "claude=ok luz=ok" "$(verifica 'claude' 'vieja-claude') $(verifica 'luz' 'vieja-luz')"
|
|
check "compatible con htpasswd/nginx (openssl)" "ok" "$(H=$(grep '^miguel:' "$HTPASSWD_FILE" | cut -d: -f2); S=$(echo "$H" | cut -d'$' -f3); [ "$(openssl passwd -apr1 -salt "$S" 'Helgen2026!')" = "$H" ] && echo ok || echo NO)"
|
|
check "sin líneas repetidas" "12" "$(cut -d: -f1 "$HTPASSWD_FILE" | sort -u | wc -l | tr -d ' ')"
|
|
check "respaldo con modo 600" "600" "$(stat -c %a "$T"/equipo/_respaldos/htpasswd-* | head -1)"
|
|
M=$(py "print(json.dumps({u: json.load(open('$T/equipo/' + u + '.json')) for u in ('jesush','miguel','jesus','adriana','leo','alan')}, ensure_ascii=False))")
|
|
check "jesush: TIMSA, campo, correo" '"sitio": "timsa"' "$(echo "$M" | python3 -c 'import sys,json; print(json.dumps(json.load(sys.stdin)["jesush"], ensure_ascii=False))')"
|
|
check "nuevos con contraseña inicial" "True True" "$(echo "$M" | python3 -c 'import sys,json; m=json.load(sys.stdin); print(m["jesush"]["contrasena_inicial"], m["miguel"]["contrasena_inicial"])')"
|
|
check "jesus: nombre y correo" "Jesús Romero jesusr@helgen.tech True" "$(echo "$M" | python3 -c 'import sys,json; m=json.load(sys.stdin)["jesus"]; print(m["nombre"], m["correo"], m["contrasena_inicial"])')"
|
|
check "adriana conserva su correo" "adriana@otro.mx" "$(echo "$M" | python3 -c 'import sys,json; print(json.load(sys.stdin)["adriana"]["correo"])')"
|
|
check "leo: correos, sin marca" "leo@helgen.tech ceo@helgen.tech None" "$(echo "$M" | python3 -c 'import sys,json; m=json.load(sys.stdin)["leo"]; print(m["correo"], m["correo_alt"], m.get("contrasena_inicial"))')"
|
|
check "alan: sitio ICAVE" "icave" "$(echo "$M" | python3 -c 'import sys,json; print(json.load(sys.stdin)["alan"]["sitio"])')"
|
|
check "bitácora de accesos" "contraseña restablecida,adriana" "$(cat "$T/equipo/_accesos.csv")"
|
|
check "lista sin contraseñas" "inicial" "$(python3 "$B/cuentas.py" lista 2>&1)"
|
|
check "lista no muestra hashes" "0" "$(python3 "$B/cuentas.py" lista 2>&1 | grep -c 'apr1')"
|
|
|
|
echo "## otra vez (no duplica) y errores"
|
|
OUT=$(printf 'Helgen2026!\n' | python3 "$B/cuentas.py" todo "$ALTA" --si --password-stdin 2>&1)
|
|
check "segunda corrida: ya no hay altas" "Contraseña restablecida (9)" "$OUT"
|
|
check "sigue sin repetidos (12 cuentas, 0 dobles)" "12/0" "$(cut -d: -f1 "$HTPASSWD_FILE" | wc -l | tr -d ' ')/$(cut -d: -f1 "$HTPASSWD_FILE" | sort | uniq -d | wc -l | tr -d ' ')"
|
|
check "contraseña corta → no cambia nada" "al menos 8" "$(printf 'corta\n' | python3 "$B/cuentas.py" restablecer --solo alan --si --password-stdin 2>&1)"
|
|
check "--solo" "Contraseña restablecida (1): alan" "$(echo no | python3 "$B/cuentas.py" restablecer --solo alan 2>&1)"
|
|
|
|
echo "## el servicio: whoami, Mi cuenta, Equipo"
|
|
python3 "$B/app.py" > "$T/app.log" 2>&1 &
|
|
APP=$!; trap "kill $APP 2>/dev/null" EXIT; sleep 1.5
|
|
U="http://127.0.0.1:$PORT"
|
|
como() { local u=$1; shift; curl -s -H "X-Remote-User: $u" -H "Content-Type: application/json" "$@"; }
|
|
check "whoami de adriana: inicial" '"contrasena_inicial":true' "$(como adriana $U/api/whoami)"
|
|
check "whoami de leo: no" '"contrasena_inicial":false' "$(como leo $U/api/whoami)"
|
|
check "adriana la cambia" '"reset":false' "$(como adriana -X POST $U/api/password -d '{"actual":"Helgen2026!","nueva":"una-frase-larga","confirma":"una-frase-larga"}')"
|
|
check "ya no es la inicial" '"contrasena_inicial":false' "$(como adriana $U/api/whoami)"
|
|
check "historial dice que la cambió" "cambi" "$(py "print(json.load(open('$T/equipo/adriana.json'))['historial'][-1]['cambio'])")"
|
|
check "leo se la restablece" '"reset":true' "$(como leo -X POST $U/api/password -d '{"usuario":"adriana","nueva":"Helgen2026!","confirma":"Helgen2026!"}')"
|
|
check "vuelve a ser la inicial" '"contrasena_inicial":true' "$(como adriana $U/api/whoami)"
|
|
check "Equipo (no admin) no muestra la marca" "0" "$(como joel $U/api/equipo | grep -c contrasena_inicial)"
|
|
check "Equipo (admin) sí" "contrasena_inicial" "$(como leo $U/api/equipo)"
|
|
check "sitio por alias (Ensenada → eit)" '"sitio":"eit"' "$(como leo -X PATCH "$U/api/equipo/miembro?id=joel" -d '{"sitio":"Ensenada","correo":"Joel@Helgen.tech"}')"
|
|
check "correo en minúsculas" '"correo":"joel@helgen.tech"' "$(como leo $U/api/equipo)"
|
|
check "correo inválido → 400" "Correo inv" "$(como leo -X PATCH "$U/api/equipo/miembro?id=joel" -d '{"correo":"joel"}')"
|
|
check "sitio inválido → 400" "Sitio inv" "$(como leo -X PATCH "$U/api/equipo/miembro?id=joel" -d '{"sitio":"marte"}')"
|
|
check "la marca no se cambia por PATCH" "True" "$(como leo -X PATCH "$U/api/equipo/miembro?id=miguel" -d '{"contrasena_inicial":false,"rol":"Responsable principal de instalación en EIT"}' >/dev/null; py "print(json.load(open('$T/equipo/miguel.json'))['contrasena_inicial'])")"
|
|
check "alta desde Equipo: marca inicial y sitio" '"contrasena_inicial":true' "$(como leo -X POST $U/api/usuarios -d '{"usuario":"prueba","password_inicial":"Helgen2026!","nombre":"Prueba","area":"campo","sitio":"lct","correo":"prueba@helgen.tech"}')"
|
|
check "índice CSV con las columnas nuevas" "sitio" "$(head -1 "$T/equipo/_indice.csv")"
|
|
echo "## alguien que estaba como texto libre cuenta en cuanto se da de alta"
|
|
check "pendiente con «Pepe» (todavía no es miembro)" "sin registrar" "$(como leo -X POST $U/api/tareas -d '{"id":"t-pepe","titulo":"Prueba Pepe","pista":"campo","responsable":"adriana, Pepe"}')"
|
|
check "alta de Pepe" '"usuario":"pepe"' "$(como leo -X POST $U/api/usuarios -d '{"usuario":"pepe","password_inicial":"Helgen2026!","nombre":"Pepe","area":"campo"}')"
|
|
check "el tablero ya se lo cuenta a Pepe" "t-pepe" "$(como leo "$U/api/tareas?responsable=pepe")"
|
|
check "Equipo: Pepe con 1 abierto" "1" "$(como leo $U/api/equipo | python3 -c 'import sys,json; print([m["pendientes"]["abiertos"] for m in json.load(sys.stdin)["miembros"] if m["id"]=="pepe"][0])')"
|
|
echo
|
|
echo "RESULTADO: $ok correctas · $fail fallidas · datos en $T"
|