#!/bin/bash
# vim: cindent:shiftwidth=4:tabstop=4:smarttab:textwidth=100

set -o posix
set -o errexit
set -o pipefail
set -o nounset
#set -o xtrace

# update system's access authorization tokens
# based on Bcfg2 administered source files

#CHANGELOG
#0.50	#initial
#0.51	#user account update moved to update-admin_users
#0.6.0	restyle according to https://kb.clearcable.ca/KB/ProgrammingStyleStandards
#0.7.0	accept Ed25519 user keys

declare -r SRC='/etc/ssh/authorized_keys'
declare -a UPDATED_FILE=()

if [[ ! -s "$SRC" ]]; then
	echo "ERROR: nothing to do; missing $SRC" 1>&2
	exit
fi

USERNAME=''
while read -r TYPE KEY EMAIL DATE; do
	if [[ -z "$TYPE" ]]; then
		# skip blank line and reset section
		USERNAME=''
	elif [[ "$TYPE" = 'ssh-dss' ]]; then
		echo 'ERROR: DSA keys not supported' 1>&2
	elif [[ "$TYPE" = 'ecdsa-sha2-nistp256' ]]; then
		echo 'ERROR: ECDSA keys not supported' 1>&2
	elif [[ "$TYPE" = "ssh-rsa" || "$TYPE" = 'ssh-ed25519' ]]; then
		if [[ -z "$USERNAME" ]]; then
			echo "ERROR: $TYPE key found without matching USERNAME" 1>&2
		else
			DST="$HOME_DIR/.ssh/authorized_keys"
			if [[ ! -d $HOME_DIR/.ssh ]]; then
				su --command "mkdir --parents $HOME_DIR/.ssh" $USERNAME
			fi

			echo "$TYPE $KEY $EMAIL" >> "$DST.new"
			chown "$USERNAME:$GROUPNAME" "$DST.new"
			UPDATED_FILE+=("$DST")
			echo "$DST $USERNAME:$GROUPNAME $TYPE $EMAIL"
		fi
	elif [[ -z "$KEY" ]]; then
		if id "$TYPE" >/dev/null 2>&1; then
			# username header
			USERNAME=$TYPE
			GROUPNAME=$(id --group --name "$USERNAME")
			HOME_DIR=$(getent passwd "$USERNAME" | awk -F: '{print $6}')
		else
			echo "ERROR: invalid user $TYPE" 1>&2
			USERNAME=''
		fi
	else
		echo "ERROR: unknown line type for: $TYPE $KEY $EMAIL $DATE" 1>&2
	fi
done < $SRC

# activate all the new files
for DST in "${UPDATED_FILE[@]}"; do
	if [[ -s "$DST.new" ]]; then
		if cmp --silent "$DST.new" "$DST"; then
			# identical - no change
			rm --force "$DST.new"
		else
			# update needed
			chmod "ugo=r" "$DST.new"
			mv --force "$DST.new" "$DST"
			echo "$DST"
		fi
	fi
done
