#!/bin/bash

PATH=/lib/rc/bin:$PATH

TEXTDOMAIN=cl_access_add
CL_ACCESS_VERSION=0.1.0_alpha1
DESCRIPTION=$"Access preparing tools"
DEBUG_LOG=/var/log/calculate/cl-access-add.log

USERNAME=access
ACCESSDIR=/var/lib/calculate/calculate-access/
STORAGE=$ACCESSDIR/storage


# прервать скрипт в случае ошибки любой из команд
# break the script execution in case of a command error
set -e

: >$DEBUG_LOG

# вывод короткой справки
# show the short help message
usage() {
    echo "Usage: $0 --id ID [--sshkey PKEY] [--header HEADER] --key KEYFILE --device DEVICE --mountpoint MOUNTPOINT

Version: $CL_ACCESS_VERSION

${DESCRIPTION}

    -h, --help  display all options and exit"
}

# вывод полной справки
# show the long help message
long_usage() {
    echo "Usage: $0 --id ID [--sshkey PKEY] [--header HEADER] --key KEYFILE --device DEVICE --mountpoint MOUNTPOINT

Version: $CL_ACCESS_VERSION

${DESCRIPTION}

  --id ID                    set access id
  --sshkey PKEY              public authorized key 
  --header HEADER            separated LUKS header
  --key KEYFILE              LUKS key file
  --device DEVICE            LUKS device (/dev or PARTUUID)
  --mountpoint MOUNTPOINT    mount point
"
}

# подготовить параметры командной строки
# prepare the commmand line parameters
rearrange_params() {
    set +e
    TEMP=$(unset POSIXLY_CORRECT; getopt \
        -o "h" \
        --long help \
        --long id: \
        --long sshkey: \
        --long header: \
        --long key: \
        --long device: \
        --long mountpoint: \
        -- "$@" 2>&1)
    if (( $? != 0 )); then
        echo "$TEMP" | sed 's/getopt: /cl-access-add: /;$d'
        exit 1
    fi
    set -e
}

# выполнить параметры командной строки
# apply the command line parameters
do_args() {
    while :; do
        case $1 in
        -h|--help)
            long_usage
            exit 0
            ;;
        --id)
            ID="$2"
            shift
            ;;
        --sshkey)
            SSHKEY="$2"
            shift
            ;;
        --header)
            HEADER="$2"
	    shift
            ;;
        --key)
            KEY="$2"
	    shift
            ;;
        --device)
            DEVICE="$2"
	    shift
	    ;;
	--partuuid)
            PARTUUID="$2"
	    shift
            ;;
        --mountpoint)
            MP="$2"
	    shift
            ;;
	--) shift; break;;
        *) usage;
           eerror $"Unknown option: $1"
           ;;
       esac
       shift
    done
    if [[ -n $1 ]]
    then
        usage;
        eerror $"Unknown argument: $1"
    fi
}

######################
# Обработать параметры
# Process the options
######################
rearrange_params "$@"
eval set -- "$TEMP"
do_args "$@"

check_setup() {
	[[ -d $ACCESSDIR ]]
}

id_not_exists() {
	local id=$1
	[[ ! -d $STORAGE/$id ]]
}

create_id() {
	local id=$1
	local dn=$STORAGE/$id
	mkdir -p $dn
	chown $USERNAME. -R $dn
}

update_sshkey() {
	local id=$1
	local sshkey=$2
	local publickey=$STORAGE/$id/id_rsa.pub
	local sshdir=$ACCESSDIR/.ssh
	local authkeys=$sshdir/authorized_keys
	cp $sshkey $publickey
	chown $USERNAME. $publickey
	if [[ -f $authkeys ]]
	then
		sed -i "/access-shell $id/d" $authkeys
	else
		if [[ ! -d $sshdir ]]
		then
			mkdir -p $sshdir
			chown $USERNAME. $sshdir
		fi
		touch $authkeys
	fi
	cat >>$authkeys <<EOF
command="~/bin/access-shell $id",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty $(cat $publickey)
EOF
	chown $USERNAME. $authkeys
}

ask_rewrite_sshkey() {
	while true
	do
		read -p $"Do you want replace authorzied key? " yn
		case $yn in
			[Yy]* ) return 0;;
			[Nn]* ) return 1;;
			* ) ewarn $"Please answer yes or no." ;;
		esac
	done
}

check_sshkey() {
	local sshkey=$1
	if ! [[ -f $sshkey ]]
	then
		eerror $"Public authorized key not found"
		return 1
	fi
	if grep -q "PRIVATE KEY" $sshkey
	then
		eerror $"You should use public key instead private"
		return 1
	fi
	if ! ssh-keygen -l -f $sshkey &>/dev/null
	then
		eerror $"$sshkey is not public key"
		return 1
	fi

	return 0
}

check_luks_header() {
	local header=$1
	if ! [[ -f $header ]]
	then
		eerror $"LUKS header not found"
		return 1
	fi
	if ! cryptsetup isLuks $header &>/dev/null
	then
		eerror $"LUKS header is wrong"
		return 1
	fi
	return 0
}

check_luks_key() {
	local key=$1
	local header=$2

	if ! [[ -f $key ]]
	then
		eerror $"LUKS key not found"
		return 1
	fi
	if [[ -z $header ]]
	then
		ewarn $"Could not check key without header"
		return 0
	else
		cryptsetup -d $key -q  luksDump --dump-master-key $header &>/dev/null || eerror "Wrong LUKS key/header pair"
	fi
}

check_device_name() {
	local device=$1
	is_uuid $device || is_dev $device || eerror $"Device must be PARTUUID or /dev"
}

is_uuid() {
	[[ "$1" =~ ^(PARTUUID=)?(([0-9a-f]+-)+[0-9a-f]+)$ ]]
}

is_dev() {
	[[ "$1" =~ ^/dev/ ]]
}

create_record() {
	local id=$1
	local key=$2
	local device=$3
	local mp=$4
	local header=$5
	for rec in {0..99}
	do
		recdn=$STORAGE/$id/$rec
		if [[ ! -d $recdn ]]
		then
			mkdir -p $recdn
			[[ -n $header ]] && cp $header $recdn/header
			cp $key $recdn/key
			if is_uuid $device
			then
				echo ${BASH_REMATCH[2]} >$recdn/partuuid
			else
				echo $device >$recdn/dev
			fi
			echo  $mp >$recdn/mountpoint

			break
		fi
	done
}

check_setup || eerror $"Calculate access is not setup"

[[ -z $ID ]] && usage && eerror $"Please specify ID"
[[ -z $SSHKEY ]] && id_not_exists $ID && eerror $"You should specify ssh key for register new ID"
[[ -z $KEY ]] && usage && eerror $"Please specify LUKS key"
[[ -z $DEVICE ]] && usage && eerror $"Please specify LUKS device"
[[ -z $MP ]] && usage && eerror $"Please specify mount point"


[[ -n $HEADER ]] && check_luks_header $HEADER
[[ -n $KEY ]] && check_luks_key $KEY $HEADER
[[ -n $SSHKEY ]] && check_sshkey $SSHKEY
#check_mount_point_name $MOUNTPOINT
check_device_name $DEVICE

id_not_exists $ID || ask_rewrite_sshkey


id_not_exists $ID && create_id $ID

create_record "$ID" "$KEY" "$DEVICE" "$MP" "$HEADER"

[[ -n $SSHKEY ]] && update_sshkey $ID $SSHKEY

einfo "All OK!"
