repositories / bugabinga.net
bugabinga.net
personal infrastructure for bugabinga!
owned by admin
scripts/local/auth-user.sh
Raw#!/usr/bin/env bash
set -euo pipefail
umask 077
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
file="${repo_root}/auth.auto.tfvars.json"
lock="${repo_root}/.auth.auto.tfvars.lock"
usage() {
echo "usage: auth-user.sh zot|basic USER [--password-stdin]" >&2
exit 2
}
kind="${1:-}"
username="${2:-}"
[[ "$#" -ge 2 && "$#" -le 3 ]] || usage
[[ "${username}" =~ ^[A-Za-z0-9._-]+$ ]] || {
echo "username may only contain letters, digits, '.', '_' and '-'" >&2
exit 2
}
password_stdin=false
if [[ "$#" -eq 3 ]]; then
[[ "$3" == "--password-stdin" ]] || usage
password_stdin=true
fi
case "${kind}" in
zot)
variable="zot_htpasswd_content"
separator=:
cost=12
;;
basic)
variable="hallucygenie_basic_auth_users"
separator=' '
cost=14
;;
*)
usage
;;
esac
if ${password_stdin}; then
IFS= read -r password
[[ -n "${password}" ]] || {
echo "empty password from stdin" >&2
exit 1
}
else
IFS= read -r -s -p "password: " password
printf '\n'
IFS= read -r -s -p "confirm password: " confirmation
printf '\n'
[[ "${password}" == "${confirmation}" ]] || {
echo "passwords do not match" >&2
exit 1
}
[[ -n "${password}" ]] || {
echo "empty password" >&2
exit 1
}
unset confirmation
fi
hash="$(printf '%s\n' "${password}" | caddy hash-password --algorithm bcrypt --bcrypt-cost "${cost}")"
unset password
line="${username}${separator}${hash}"
prefix="${username}${separator}"
exec 9>"${lock}"
flock -x 9
input="${file}"
temporary_input=""
if [[ ! -e "${file}" ]]; then
temporary_input="$(mktemp "${repo_root}/.auth-input.XXXXXX")"
printf '{}\n' >"${temporary_input}"
input="${temporary_input}"
fi
temporary_output="$(mktemp "${repo_root}/.auth-output.XXXXXX")"
trap 'rm -f -- "${temporary_input}" "${temporary_output}"' EXIT
if jq -e --arg variable "${variable}" --arg prefix "${prefix}" '
(.[$variable] // "") | split("\n") | any(startswith($prefix))
' "${input}" >/dev/null; then
action=updated
else
action=added
fi
jq --arg variable "${variable}" --arg prefix "${prefix}" --arg line "${line}" '
.[$variable] = (
((.[$variable] // "") | split("\n") | map(select(length > 0 and (startswith($prefix) | not))))
+ [$line]
| join("\n") + "\n"
)
' "${input}" >"${temporary_output}"
chmod 600 "${temporary_output}"
mv "${temporary_output}" "${file}"
printf "%s %s user '%s' in %s\n" "${action}" "${kind}" "${username}" "${file}"