#!/bin/bash
#
# Script to edit secrets for a host.
#
# This script is used by an administrator on his/hers local machine. The
# general principle is for this script to ssh to the target host, decrypt
# the secrets and allow changes to be made, and then fetch the encrypted
# secrets from the host and add it to the Cosmos repository on the
# administrators machine.
#
# Funnily enough, this script will execute itself (with the argument
# '--on-host') on the target host in order to do the decryption etc. Don't
# allow this to confuse you and everything will be fine.
#

set -e
umask 077

LAST_OUTPUT_FILENAME="/root/.last_edit-secrets_output"

test -d /dev/shm && export TMPDIR='/dev/shm'

TMPFILE=$(mktemp edit-secrets.XXXXXXXXXX)
TMPFILE2=$(mktemp edit-secrets.XXXXXXXXXX)

if [ ! -f $TMPFILE ]; then
    echo "$TMPFILE"
    echo "$0: Failed creating temporary file"
    exit 1
fi
if [ ! -f $TMPFILE2 ]; then
    echo "$TMPFILE2"
    echo "$0: Failed creating temporary file 2"
    exit 1
fi

trap "rm -f $TMPFILE $TMPFILE2" EXIT


if [[ ! $1 ]]; then
    # deliberately don't mention the --on-host argument
    echo "Syntax: $0 fqdn"
    exit 1
fi


function edit_copy_and_commit()
{
    #
    # This code runs on the administrators local machine
    #
    local host=$1

    if [[ ${EDITOR} ]]; then
	declare -r REMOTE_EDITOR="${EDITOR}"
    else
	declare -r REMOTE_EDITOR='/usr/bin/vim.tiny'
    fi

    # Execute this script, on a remote host
    ssh -t root@"${host}" EDITOR="${REMOTE_EDITOR}" /var/cache/cosmos/repo/edit-secrets --on-host
    scp -q root@"${host}:${LAST_OUTPUT_FILENAME}" ${TMPFILE}

    local save_to
    if grep ^"STATUS=UPDATED" $TMPFILE > /dev/null; then
        save_to="${host}/overlay/etc/hiera/data/secrets.yaml.asc"

	# extract the GPG output
	perl -e '$a = 0; while (<>) { $a = 1 if ($_ =~ /-+BEGIN PGP MESSAGE-+/);
                 print $_ if $a; $a = 0 if ($_ =~ /-+END PGP MESSAGE-+/); }' < $TMPFILE > $TMPFILE2

	if ! grep "END PGP MESSAGE" $TMPFILE2 > /dev/null; then
	    echo "$0: Failed extracting PGP output from file $TMPFILE into $TMPFILE2"
	    exit 1
	fi
    elif grep ^"STATUS=EYAML_UPDATED" $TMPFILE > /dev/null; then
        save_to="${host}/overlay/etc/hiera/data/local.eyaml"

	# remove the STATUS= line
	grep -v '^STATUS=EYAML_UPDATED' $TMPFILE > $TMPFILE2

	# check syntax
	if [ -x $(dirname $0)/scripts/jsonyaml-no-output.py ]; then
	    if ! $(dirname $0)/scripts/jsonyaml-no-output.py yaml $TMPFILE2; then
		echo "$0: Error: $TMPFILE2 doesn't look like a YAML file"
		exit 1
	    fi
	else
	    echo "$0: Warning: Unable to check syntax of $TMPFILE2"
	fi
    else
	echo ""
	echo "Not updated"
	echo ""

	exit 0
    fi

    # use cat to preserve permissions etc.
    mkdir -p "`dirname ${save_to}`"
    cat $TMPFILE2 > "${save_to}"
    git add "${save_to}"

    if grep ^"STATUS=EYAML_UPDATED" $TMPFILE > /dev/null; then
	git diff --cached "${save_to}"
    fi

    echo ""
    echo "$save_to updated"
    echo ""

    exit 0
}

function edit_file_on_host() {
    #
    # Local execution on a host
    #

    local SECRETFILE=/etc/hiera/data/secrets.yaml.asc
    local EYAMLFILE=/etc/hiera/data/local.eyaml

    if [ -f "${EYAMLFILE}" ]; then
	edit_eyaml_file ${EYAMLFILE}
    elif [ -f "${SECRETFILE}" ]; then
	edit_gpg_file ${SECRETFILE}
    elif [ -f /etc/hiera/eyaml/public_certkey.pkcs7.pem ]; then
	# default to eyaml if the key exists and none of the secrets-file above exist
	touch ${EYAMLFILE}
	edit_eyaml_file ${EYAMLFILE}
    fi
}

function edit_gpg_file()
{
    local SECRETFILE=$1

    GNUPGHOME=/etc/hiera/gpg/
    export GNUPGHOME

    local GPG=`which gpg2 || true`
    if [ ! -x "$GPG" ]; then
	GPG=`which gpg || true`
	if [ ! -x "$GPG" ]; then
	    echo "$0: gpg2 or gpg not found"
	    exit 1
	fi
    fi

    if ! $GPG --list-secret-keys | grep -q ^"sec\s"; then
	echo "$0: Secret key does not exist (in $GNUPGHOME)."
	echo ""
	echo "Generate it with /var/cache/cosmos/model/pre-tasks.d/040hiera-gpg"
	echo ""
	exit 1
    fi

    if [ -s $SECRETFILE ]; then
	$GPG -d $SECRETFILE > $TMPFILE
    fi

    cp $TMPFILE $TMPFILE2
    sensible-editor $TMPFILE
    rm -f ${TMPFILE}~ ${TMPFILE2}~

    echo ""
    echo ""

    local status=0
    cmp -s $TMPFILE $TMPFILE2 || status=1
    if [ $status -eq 0 ]; then
	(
	    echo "STATUS=NOT_CHANGED"
	)  > $LAST_OUTPUT_FILENAME
	echo ""
	echo "$0: No changes detected"
    else
	# figure out this hosts gpg key id
	if lsb_release -r | grep -q 18.04; then
	    recipient=$($GPG --list-secret-keys | grep -A1 '^sec' | tail -1 | awk '{print $1}')
	else
	    recipient=$($GPG --list-secret-key | grep ^sec | head -1 | awk '{print $2}' | cut -d / -f 2)
	fi

	save_to="`hostname --fqdn`/overlay${SECRETFILE}"
	echo ""
	(
	    echo "STATUS=UPDATED"
	    echo ""
	) > $LAST_OUTPUT_FILENAME
	$GPG --output - --armor --recipient $recipient --sign --encrypt $TMPFILE >> $LAST_OUTPUT_FILENAME
	echo ""
	echo "GPG output saved in $LAST_OUTPUT_FILENAME - save it in Cosmos as"
	echo ""
	echo "  $save_to"
	echo ""
    fi
}

function edit_eyaml_file()
{
    local EYAMLFILE=$1

    local FQDN=$(hostname --fqdn)
    local privkey='/etc/hiera/eyaml/private_key.pkcs7.pem'
    local pubkey='/etc/hiera/eyaml/public_certkey.pkcs7.pem'
    for f in $privkey $pubkey; do
	test -f "${f}" || { echo "$0: eyaml key file ${f} not found"; exit 1; }
    done

    # save source file for comparision afterwards
    cp "${EYAMLFILE}" "${TMPFILE}"
    eyaml edit --pkcs7-private-key "${privkey}" --pkcs7-public-key "${pubkey}" "${EYAMLFILE}"

    local status=0
    cmp -s "${EYAMLFILE}" $TMPFILE || status=1
    if [ $status -eq 0 ]; then
	(
	    echo "STATUS=NOT_CHANGED"
	)  > $LAST_OUTPUT_FILENAME
	echo ""
	echo "$0: No changes detected"
    else
	echo ""
	(
	    echo "STATUS=EYAML_UPDATED"
	    echo ""
	) > $LAST_OUTPUT_FILENAME
	cat "${EYAMLFILE}" >> $LAST_OUTPUT_FILENAME
    fi
}


if [[ $1 == '--on-host' ]]; then
    edit_file_on_host
else
    host=$(echo $1 | sed -e 's!/*$!!')    # remove trailing slashes

    if [ ! -d $host ]; then
	echo "$0: No host-directory for '$host' found - execute in top-level cosmos dir"
	exit 1
    fi

    edit_copy_and_commit $host
fi

exit 0
