257 lines
6.5 KiB
Bash
Executable File
257 lines
6.5 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
USERS_FILE="${USERS_FILE:-config/users}"
|
|
DEFAULT_COST="${BCRYPT_COST:-12}"
|
|
SKIP_RESTART="${SKIP_RESTART:-0}"
|
|
|
|
# --- Utilidades -------------------------------------------------------------
|
|
|
|
usage() {
|
|
cat <<'EOF'
|
|
Gestor de usuarios Radicale
|
|
|
|
Uso:
|
|
./radicale_users.sh list
|
|
./radicale_users.sh add <usuario> [--password <pwd> | --random]
|
|
./radicale_users.sh passwd <usuario> [--password <pwd> | --random]
|
|
./radicale_users.sh delete <usuario> [--force]
|
|
./radicale_users.sh --help
|
|
|
|
Variables:
|
|
USERS_FILE Ruta del archivo htpasswd (por defecto config/users)
|
|
SKIP_RESTART Si vale 1, no reinicia el contenedor automáticamente
|
|
BCRYPT_COST Coste para htpasswd -B (por defecto 12)
|
|
|
|
Sin argumentos se abre el menú interactivo original.
|
|
EOF
|
|
}
|
|
|
|
ensure_users_file() {
|
|
if [[ ! -f "$USERS_FILE" ]]; then
|
|
echo "⚠️ No existe $USERS_FILE. Creándolo..."
|
|
mkdir -p "$(dirname "$USERS_FILE")"
|
|
touch "$USERS_FILE"
|
|
fi
|
|
}
|
|
|
|
require_command() {
|
|
if ! command -v "$1" >/dev/null 2>&1; then
|
|
echo "❌ Necesitas '$1' instalado en el sistema." >&2
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
maybe_restart() {
|
|
if [[ "$SKIP_RESTART" == "1" ]]; then
|
|
echo "⚠️ SKIP_RESTART=1, no se reiniciará Radicale automáticamente."
|
|
return
|
|
fi
|
|
echo "🔄 Reiniciando contenedor Radicale..."
|
|
docker compose restart radicale >/dev/null 2>&1 || true
|
|
echo "✅ Radicale reiniciado."
|
|
}
|
|
|
|
generate_password() {
|
|
if command -v openssl >/dev/null 2>&1; then
|
|
openssl rand -base64 18
|
|
else
|
|
date +%s | sha256sum | head -c 16
|
|
fi
|
|
}
|
|
|
|
prompt_password() {
|
|
local pwd confirm
|
|
read -rs -p "Introduce contraseña: " pwd
|
|
echo ""
|
|
read -rs -p "Confirma contraseña: " confirm
|
|
echo ""
|
|
if [[ "$pwd" != "$confirm" ]]; then
|
|
echo "❌ Las contraseñas no coinciden." >&2
|
|
exit 1
|
|
fi
|
|
echo "$pwd"
|
|
}
|
|
|
|
htpasswd_update() {
|
|
local user="$1" password="$2"
|
|
htpasswd -B -C "$DEFAULT_COST" -b "$USERS_FILE" "$user" "$password" >/dev/null
|
|
normalize_bcrypt_prefix "$user"
|
|
}
|
|
|
|
normalize_bcrypt_prefix() {
|
|
local user="$1"
|
|
if ! command -v python3 >/dev/null 2>&1; then
|
|
echo "⚠️ python3 no disponible: no se pudo normalizar el hash bcrypt de '$user' (prefijo \$2y\$)." >&2
|
|
echo " Radicale solo acepta hashes \$2b\$, por lo que este usuario podría fallar." >&2
|
|
return
|
|
fi
|
|
python3 - "$USERS_FILE" "$user" <<'PY'
|
|
import pathlib, re, sys
|
|
path = pathlib.Path(sys.argv[1])
|
|
user = sys.argv[2]
|
|
data = path.read_text()
|
|
pattern = re.compile(rf'^({re.escape(user)}:\$)2y', re.M)
|
|
updated, count = pattern.subn(r'\g<1>2b', data)
|
|
if count:
|
|
path.write_text(updated)
|
|
PY
|
|
}
|
|
|
|
list_users() {
|
|
ensure_users_file
|
|
echo "👥 Usuarios actuales:"
|
|
if [[ -s "$USERS_FILE" ]]; then
|
|
cut -d':' -f1 "$USERS_FILE" | sort
|
|
else
|
|
echo "(ninguno)"
|
|
fi
|
|
}
|
|
|
|
cmd_add() {
|
|
ensure_users_file
|
|
local user="$1" password="" random=0
|
|
shift || true
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--password|-p) password="$2"; shift 2 ;;
|
|
--random|-r) random=1; shift ;;
|
|
--no-restart) SKIP_RESTART=1; shift ;;
|
|
*) echo "❗ Opción desconocida: $1" >&2; exit 1 ;;
|
|
esac
|
|
done
|
|
if [[ -z "$user" ]]; then
|
|
echo "❌ Debes indicar un usuario." >&2
|
|
exit 1
|
|
fi
|
|
if grep -q "^$user:" "$USERS_FILE"; then
|
|
echo "⚠️ El usuario '$user' ya existe." >&2
|
|
exit 1
|
|
fi
|
|
if [[ $random -eq 1 ]]; then
|
|
password="$(generate_password)"
|
|
echo "🔐 Contraseña generada para '$user': $password"
|
|
elif [[ -z "$password" ]]; then
|
|
password="$(prompt_password)"
|
|
fi
|
|
htpasswd_update "$user" "$password"
|
|
maybe_restart
|
|
}
|
|
|
|
cmd_passwd() {
|
|
ensure_users_file
|
|
local user="$1" password="" random=0
|
|
shift || true
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--password|-p) password="$2"; shift 2 ;;
|
|
--random|-r) random=1; shift ;;
|
|
--no-restart) SKIP_RESTART=1; shift ;;
|
|
*) echo "❗ Opción desconocida: $1" >&2; exit 1 ;;
|
|
esac
|
|
done
|
|
if [[ -z "$user" ]]; then
|
|
echo "❌ Debes indicar usuario." >&2
|
|
exit 1
|
|
fi
|
|
if ! grep -q "^$user:" "$USERS_FILE"; then
|
|
echo "⚠️ El usuario '$user' no existe." >&2
|
|
exit 1
|
|
fi
|
|
if [[ $random -eq 1 ]]; then
|
|
password="$(generate_password)"
|
|
echo "🔐 Nueva contraseña generada: $password"
|
|
elif [[ -z "$password" ]]; then
|
|
password="$(prompt_password)"
|
|
fi
|
|
htpasswd_update "$user" "$password"
|
|
maybe_restart
|
|
}
|
|
|
|
cmd_delete() {
|
|
ensure_users_file
|
|
local user="$1" force=0
|
|
shift || true
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--force|-f) force=1; shift ;;
|
|
--no-restart) SKIP_RESTART=1; shift ;;
|
|
*) echo "❗ Opción desconocida: $1" >&2; exit 1 ;;
|
|
esac
|
|
done
|
|
if [[ -z "$user" ]]; then
|
|
echo "❌ Debes indicar usuario." >&2
|
|
exit 1
|
|
fi
|
|
if ! grep -q "^$user:" "$USERS_FILE"; then
|
|
echo "⚠️ El usuario '$user' no existe." >&2
|
|
exit 1
|
|
fi
|
|
if [[ $force -eq 0 ]]; then
|
|
read -rp "¿Eliminar '$user'? (s/N): " confirm
|
|
[[ "$confirm" =~ ^[sS]$ ]] || { echo "🚫 Operación cancelada."; return; }
|
|
fi
|
|
sed -i.bak "/^$user:/d" "$USERS_FILE"
|
|
rm -f "${USERS_FILE}.bak"
|
|
echo "❌ Usuario '$user' eliminado."
|
|
maybe_restart
|
|
}
|
|
|
|
# --- Menú interactivo legado -----------------------------------------------
|
|
interactive_menu() {
|
|
while true; do
|
|
echo "========================================"
|
|
echo "🔐 GESTOR DE USUARIOS RADICALE"
|
|
echo "========================================"
|
|
echo "1️⃣ Listar usuarios"
|
|
echo "2️⃣ Crear nuevo usuario"
|
|
echo "3️⃣ Editar contraseña de usuario"
|
|
echo "4️⃣ Eliminar usuario"
|
|
echo "5️⃣ Salir"
|
|
echo "----------------------------------------"
|
|
read -rp "Selecciona una opción [1-5]: " opt
|
|
echo ""
|
|
case "$opt" in
|
|
1) list_users ;;
|
|
2) read -rp "Usuario nuevo: " user; cmd_add "$user" ;;
|
|
3) read -rp "Usuario a modificar: " user; cmd_passwd "$user" ;;
|
|
4) read -rp "Usuario a eliminar: " user; cmd_delete "$user" ;;
|
|
5) echo "👋 Saliendo..."; exit 0 ;;
|
|
*) echo "❗ Opción no válida." ;;
|
|
esac
|
|
echo ""
|
|
read -rp "Presiona ENTER para continuar..." _
|
|
clear
|
|
done
|
|
}
|
|
|
|
# --- Punto de entrada -------------------------------------------------------
|
|
main() {
|
|
require_command htpasswd
|
|
|
|
if [[ $# -eq 0 ]]; then
|
|
interactive_menu
|
|
exit 0
|
|
fi
|
|
|
|
case "$1" in
|
|
list) list_users ;;
|
|
add)
|
|
shift
|
|
cmd_add "${1:-}" "${@:2}"
|
|
;;
|
|
passwd)
|
|
shift
|
|
cmd_passwd "${1:-}" "${@:2}"
|
|
;;
|
|
delete|remove)
|
|
shift
|
|
cmd_delete "${1:-}" "${@:2}"
|
|
;;
|
|
--help|-h) usage ;;
|
|
*) echo "❗ Comando desconocido: $1"; usage; exit 1 ;;
|
|
esac
|
|
}
|
|
|
|
main "$@"
|