#!/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}"