Extrait de li.k3nny.fr et rendu indépendant du serveur : toute la configuration propre à l'hôte passe par li.env (URL publique, chemins, expiration, répertoires « HTML seulement », commande nginx). Écarts avec la version en production : - li-user del retire aussi la page .html, pas seulement le .xspf ; - li-gen ignore les répertoires cachés, pas seulement les fichiers ; - textes de la page HTML accentués. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
133 lines
4.3 KiB
Bash
Executable File
133 lines
4.3 KiB
Bash
Executable File
#!/bin/sh
|
|
# Gestion des ayants droit de la bibliotheque.
|
|
#
|
|
# users.json est la SEULE source de verite ; la map nginx en est deduite et
|
|
# se reecrit entierement a chaque geste. Retirer quelqu un de ce fichier et
|
|
# recharger, c est tuer tous ses liens dans la seconde : c est la revocation.
|
|
# L expiration inscrite dans les URL n est qu un filet.
|
|
#
|
|
# Configuration : /etc/li/li.env (ou le fichier nomme par LI_CONF), partage
|
|
# avec li-gen. Voir li.env.example.
|
|
set -eu
|
|
|
|
LI_CONF=${LI_CONF:-/etc/li/li.env}
|
|
# shellcheck disable=SC1090
|
|
[ -f "$LI_CONF" ] && . "$LI_CONF"
|
|
|
|
: "${LI_BASE_URL:?LI_BASE_URL n est pas defini (voir li.env.example)}"
|
|
LI_DATA=${LI_DATA:-/srv/appdata/li}
|
|
LI_EXPIRES=${LI_EXPIRES:-2145916800} # 2038 — doit valoir celui de li-gen
|
|
LI_NGINX=${LI_NGINX:-docker exec li nginx}
|
|
# li-gen lit les memes variables : on les lui transmet telles que chargees.
|
|
export LI_CONF LI_BASE_URL LI_DATA LI_EXPIRES
|
|
|
|
BASE=${LI_BASE_URL%/}
|
|
AYANTS=$LI_DATA/users.json
|
|
MAP=$LI_DATA/conf/users.conf
|
|
LI_GEN=${LI_GEN:-$(dirname "$0")/li-gen}
|
|
|
|
usage() {
|
|
echo "usage: li-user add|del|lien|list [nom]" >&2
|
|
exit 2
|
|
}
|
|
|
|
verifie_nom() {
|
|
# Le nom voyage dans l URL et devient un nom de fichier : on le tient
|
|
# court et sans surprise plutot que d avoir a l echapper partout.
|
|
case "$1" in
|
|
*[!A-Za-z0-9_-]*|"") echo "Nom invalide : lettres, chiffres, - et _ seulement." >&2; exit 1 ;;
|
|
esac
|
|
}
|
|
|
|
rendre_map() {
|
|
mkdir -p "$(dirname "$MAP")"
|
|
umask 077
|
|
{
|
|
echo "# Engendre par li-user — ne pas editer a la main."
|
|
echo "# Un identifiant absent d ici obtient un secret VIDE, et le vhost"
|
|
echo "# refuse avant meme de calculer la moindre signature."
|
|
echo 'map $arg_u $li_secret {'
|
|
echo ' default "";'
|
|
jq -r 'to_entries | sort_by(.key)[] | " \"\(.key)\" \"\(.value)\";"' "$AYANTS"
|
|
echo '}'
|
|
} > "$MAP.tmp"
|
|
mv "$MAP.tmp" "$MAP"
|
|
chmod 644 "$MAP" # lu par le conteneur, qui le monte en lecture seule
|
|
|
|
# shellcheck disable=SC2086 # LI_NGINX est une commande, mots compris
|
|
$LI_NGINX -t >/dev/null 2>&1 || {
|
|
echo "La configuration nginx ne passe plus le controle — rien recharge." >&2
|
|
$LI_NGINX -t
|
|
exit 1
|
|
}
|
|
# shellcheck disable=SC2086
|
|
$LI_NGINX -s reload
|
|
}
|
|
|
|
signe_uri() { # $1 = uri, $2 = secret
|
|
printf '%s' "$LI_EXPIRES$1$2" \
|
|
| openssl md5 -binary | openssl base64 | tr '+/' '-_' | tr -d '='
|
|
}
|
|
|
|
lien() {
|
|
secret=$(jq -er --arg n "$1" '.[$n]' "$AYANTS") || {
|
|
echo "$1 : inconnu." >&2; exit 1
|
|
}
|
|
# Les deux documents portent les MEMES liens de telechargement : la
|
|
# signature ne depend que du chemin et du secret, jamais du document
|
|
# qui la transporte.
|
|
for ext in html xspf; do
|
|
uri="/playlists/$1.$ext"
|
|
printf ' %-5s %s\n' "$ext" "$BASE$uri?u=$1&e=$LI_EXPIRES&s=$(signe_uri "$uri" "$secret")"
|
|
done
|
|
}
|
|
|
|
[ -f "$AYANTS" ] || { mkdir -p "$LI_DATA"; umask 077; echo '{}' > "$AYANTS"; }
|
|
|
|
case "${1:-}" in
|
|
add)
|
|
[ $# -eq 2 ] || usage
|
|
verifie_nom "$2"
|
|
if jq -e --arg n "$2" 'has($n)' "$AYANTS" >/dev/null; then
|
|
echo "$2 existe deja — « li-user lien $2 » pour retrouver son URL." >&2
|
|
exit 1
|
|
fi
|
|
secret=$(LC_ALL=C tr -dc 'A-Za-z0-9' < /dev/urandom | head -c 24)
|
|
tmp=$(mktemp)
|
|
jq --arg n "$2" --arg s "$secret" '.[$n] = $s' "$AYANTS" > "$tmp"
|
|
mv "$tmp" "$AYANTS"
|
|
chmod 600 "$AYANTS"
|
|
rendre_map
|
|
"$LI_GEN"
|
|
echo
|
|
echo "Liens a transmettre a $2 :"
|
|
lien "$2"
|
|
;;
|
|
del)
|
|
[ $# -eq 2 ] || usage
|
|
jq -e --arg n "$2" 'has($n)' "$AYANTS" >/dev/null || {
|
|
echo "$2 : inconnu." >&2; exit 1
|
|
}
|
|
tmp=$(mktemp)
|
|
jq --arg n "$2" 'del(.[$n])' "$AYANTS" > "$tmp"
|
|
mv "$tmp" "$AYANTS"
|
|
chmod 600 "$AYANTS"
|
|
# Les documents partent AVANT le rechargement : un worker en cours de
|
|
# vidange a encore l ancienne map, et servirait un 404 la ou la
|
|
# reponse juste est 403.
|
|
rm -f "$LI_DATA/playlists/$2.xspf" "$LI_DATA/playlists/$2.html"
|
|
rendre_map
|
|
echo "$2 revoque — tous ses liens sont morts."
|
|
;;
|
|
lien)
|
|
[ $# -eq 2 ] || usage
|
|
lien "$2"
|
|
;;
|
|
list)
|
|
jq -r 'to_entries | sort_by(.key)[] | .key' "$AYANTS"
|
|
;;
|
|
*)
|
|
usage
|
|
;;
|
|
esac
|