Files
appstore/apps/tinyauth/5.2.0/scripts/init.sh

85 lines
3.8 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd -P)"
ENV_FILE="${ENV_FILE:-$ROOT_DIR/.env}"
DOCKER_BIN="${DOCKER_BIN:-docker}"
IMAGE='ghcr.io/tinyauthapp/tinyauth:v5.1.3@sha256:a1bde189951900f84a60f133c26a1ec7a3264073ec7875b548f4fbadb7f5f735'
fail() {
printf '%s\n' "$1" >&2
exit 1
}
read_env_value() {
local key="$1"
local value
value="$(sed -n "s/^${key}=//p" "$ENV_FILE" | tail -n 1)"
case "$value" in
\"*\") value="${value#\"}"; value="${value%\"}" ;;
\'*\') value="${value#\'}"; value="${value%\'}" ;;
esac
printf '%s\n' "$value"
}
[[ -f "$ENV_FILE" && ! -L "$ENV_FILE" ]] || fail "$ENV_FILE must be a regular file"
command -v "$DOCKER_BIN" >/dev/null 2>&1 || fail 'Docker is required to generate the Tinyauth password hash'
username="${TINYAUTH_ADMIN_USERNAME:-$(read_env_value TINYAUTH_ADMIN_USERNAME)}"
password="${TINYAUTH_ADMIN_PASSWORD:-$(read_env_value TINYAUTH_ADMIN_PASSWORD)}"
app_url="${TINYAUTH_APP_URL:-$(read_env_value TINYAUTH_APP_URL)}"
secure_cookie="${TINYAUTH_SECURE_COOKIE:-$(read_env_value TINYAUTH_SECURE_COOKIE)}"
data_dir="$(realpath -m -- "$ROOT_DIR/data")"
case "$data_dir" in
"$ROOT_DIR"/*) ;;
*) fail 'Tinyauth data directory must remain inside the application version directory' ;;
esac
for path in "$data_dir" "$data_dir/resources" "$data_dir/oidc"; do
[[ ! -L "$path" ]] || fail "$path must not be a symbolic link"
[[ ! -e "$path" || -d "$path" ]] || fail "$path must be a directory"
done
users_file="$data_dir/users"
[[ ! -L "$users_file" ]] || fail 'Tinyauth users file must not be a symbolic link'
[[ ! -e "$users_file" || -f "$users_file" ]] || fail 'Tinyauth users path must be a regular file'
[[ "$username" =~ ^[A-Za-z0-9._-]{1,64}$ ]] || fail 'TINYAUTH_ADMIN_USERNAME must contain only letters, digits, dot, underscore, or hyphen'
[[ ${#password} -ge 12 && ${#password} -le 256 ]] || fail 'TINYAUTH_ADMIN_PASSWORD must contain 12 to 256 characters'
[[ "$password" != *$'\n'* && "$password" != *$'\r'* ]] || fail 'TINYAUTH_ADMIN_PASSWORD must not contain line breaks'
[[ "$app_url" =~ ^https?://[^[:space:]]+$ ]] || fail 'TINYAUTH_APP_URL must be an absolute HTTP or HTTPS URL without whitespace'
app_host="${app_url#*://}"
app_host="${app_host%%/*}"
app_host="${app_host%%:*}"
[[ "$app_host" == *.* && "$app_host" =~ ^[A-Za-z0-9][A-Za-z0-9.-]*[A-Za-z0-9]$ ]] || fail 'TINYAUTH_APP_URL must use a domain name with at least two labels'
[[ ! "$app_host" =~ ^[0-9.]+$ ]] || fail 'TINYAUTH_APP_URL must not use an IP address'
[[ "$secure_cookie" == false || "$secure_cookie" == true ]] || fail 'TINYAUTH_SECURE_COOKIE must be true or false'
install -d -m 0750 -o 1000 -g 1000 -- "$data_dir" "$data_dir/resources" "$data_dir/oidc"
# Variables in this command are expanded by the shell inside the generator container.
# shellcheck disable=SC2016
generator_command='IFS= read -r password; exec tinyauth user create --username "$1" --password "$password"'
generator_output="$(
printf '%s\n' "$password" |
"$DOCKER_BIN" run --rm -i --network none --read-only --user 1000:1000 \
--cap-drop ALL --security-opt no-new-privileges --env NO_COLOR=1 \
--entrypoint /bin/sh "$IMAGE" -ec \
"$generator_command" \
sh "$username"
)"
user_entry="$(printf '%s\n' "$generator_output" | sed -n 's/^TINYAUTH_AUTH_USERS=//p' | head -n 1)"
[[ "$user_entry" =~ ^[A-Za-z0-9._-]{1,64}:\$2[aby]\$[0-9]{2}\$[./A-Za-z0-9]{53}$ ]] || fail 'Failed to generate a valid bcrypt user entry'
umask 077
temporary="$(mktemp "$data_dir/.users.tmp.XXXXXX")"
trap 'rm -f -- "${temporary:-}"' EXIT
printf '%s\n' "$user_entry" >"$temporary"
chmod 0600 "$temporary"
chown 1000:1000 "$temporary"
mv -f -- "$temporary" "$users_file"
trap - EXIT
chown 1000:1000 "$data_dir" "$data_dir/resources" "$data_dir/oidc" "$users_file"
chmod 0750 "$data_dir" "$data_dir/resources" "$data_dir/oidc"
chmod 0600 "$users_file"