Files
k3nnyandClaude Opus 5.5 1e63e4fe80 Bibliothèque vidéo partagée par URL signées
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>
2026-09-27 10:06:08 +02:00

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