#!/bin/sh
# vim: set ts=4 sw=4 ft=sh:
#---help---
# Usage: apk-deploy-addkey [options] <name>
#
# Authorize new SSH key to connect and install APK packages.
#
# Arguments:
#   <name>    Name to identify the key.
#
# Options:
#   -n --apk-key-name  Name of the APK key.
#   -A --no-apk-key    Don't prompt for APK key.
#   -V --version       Print the program version and exit.
#   -h --help          Show this message and exit.
#
# Environment:
#   APK_DEPLOY_USER    The user to authorize the SSH key for.
#
# Homepage: <https://github.com/jirutka/apk-deploy-tool>
#---help---
set -eu -o pipefail

readonly PROGNAME='apk-deploy-addkey'
readonly VERSION='0.5.3'

readonly APK_KEYS_DIR='/etc/apk/keys'
readonly DEPLOY_USER=${APK_DEPLOY_USER:-"deploy"}
readonly DEPLOY_GROUP=$(id -gn "$DEPLOY_USER")
readonly DEPLOY_HOME=$(getent passwd "$DEPLOY_USER" | cut -d: -f6)
readonly AUTHORIZED_KEYS="$DEPLOY_HOME/.ssh/authorized_keys"


die() {
	printf 'ERROR: %s\n' "$2" >&2
	exit "$1"
}

help() {
	sed -n '/^#---help---/,/^#---help---/p' "$0" | sed 's/^# \?//; 1d;$d;'
}

check_name() {
	local name="$1"

	printf '%s\n' "$name" | grep -q '^[A-Za-z0-9@_.-]\+$' \
		|| die 2 'invalid name!'

	cut -d' ' -f4 "$AUTHORIZED_KEYS" | grep -qFx "$name" \
		&& die 3 'key with the given name already exists!' || true
}

check_ssh_pubkey() {
	local pubkey="$1"

	test -n "$pubkey" \
		|| die 2 'no key provided!'

	printf '%s\n' "$pubkey" | ssh-keygen -lf /dev/stdin >/dev/null 2>&1 \
		|| die 4 'not a valid SSH public key!'

	cut -d' ' -f1-2 "$AUTHORIZED_KEYS" | grep -qFx "$pubkey" \
		&& die 3 'SSH key already exists!' || true
}

authorized_key_line() {
	local name="$1"
	local pubkey="$(echo "$2" | cut -d' ' -f1-2)"

	printf 'no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty %s %s\n' "$pubkey" "$name"
}

read_multiline() {
	local line; while read line; do
		printf '%s\n' "$line"
		test -n "$line" || break
	done
}

ssh_pubkey_to_pkcs8() {
	printf '%s\n' "$1" | ssh-keygen -e -m PKCS8 -f /dev/stdin
}


apk_pubkey_name=''
apk_pubkey_skip=no

opts=$(getopt -n $PROGNAME -o An:h -l no-apk-key,apk-key-name:,version,help -- "$@") || exit 1
eval set -- "$opts"

while [ $# -gt 0 ]; do
	n=2
	case "$1" in
		-n | --apk-key-name) apk_pubkey_name="$2";;
		-A | --no-apk-key) apk_pubkey_skip=yes; n=1;;
		-V | --version) echo "$PROGNAME $VERSION"; exit 0;;
		-h | --help) help; exit 0;;
		--) shift; break;;
	esac
	shift $n
done
[ $# -eq 1 ] || die 2 'invalid number of arguments!'

if ! [ -f "$AUTHORIZED_KEYS" ]; then
	install -d -m 700 -o "$DEPLOY_USER" -g "$DEPLOY_GROUP" "${AUTHORIZED_KEYS%/*}"
	install -m 600 -o "$DEPLOY_USER" -g "$DEPLOY_GROUP" /dev/null "$AUTHORIZED_KEYS"
fi

name="$1"
check_name "$name"

: ${apk_pubkey_name:="$name"}
apk_pubkey_file="$APK_KEYS_DIR/${apk_pubkey_name%rsa.pub}.rsa.pub"

printf 'SSH public key: '
read ssh_pubkey
ssh_pubkey=$(printf '%s\n' "$ssh_pubkey" | cut -d' ' -f1-2)
check_ssh_pubkey "$ssh_pubkey"
authorized_key_line "$name" "$ssh_pubkey" >> "$AUTHORIZED_KEYS"

if [ "$apk_pubkey_skip" = no ] && ! [ -f "$apk_pubkey_file" ]; then
	printf 'APK public RSA key (leave empty if same as SSH key): '
	apk_pubkey=$(read_multiline)
	if [ -z "$apk_pubkey" ]; then
		[ "${ssh_pubkey#ssh-rsa }" != "$ssh_pubkey" ] \
			|| die 4 'apk supports only RSA keys!'
		apk_pubkey=$(ssh_pubkey_to_pkcs8 "$ssh_pubkey")
	fi
	printf '%s\n' "$apk_pubkey" > "$apk_pubkey_file"
fi

echo 'Done'
