#!/bin/bash

# Copyright: 2023 Virtuozzo International GmbH

# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at

# http://www.apache.org/licenses/LICENSE-2.0

# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.



inherit default config os envinfo;
inherit ${gw_boost_lib_inherit};
inherit extendperm;
include virtuozzo vzexec;

DESCRIPTION="Manipulate ssh keys, users";
VERSION="1"
DEFAULT_ACTION="Usage";
$PROGRAM 'useradd';
$PROGRAM 'groupadd';
$PROGRAM 'getent';
$PROGRAM 'awk';
$PROGRAM 'sed';
$PROGRAM 'grep';

declare _UID;
declare _GID;
declare _USER_NAME="jelastic";
declare _homedir;
declare _USER_EXISTS;
declare _GROUP_EXISTS;
declare _SSH_PRIVATE_KEY;
declare _REFORM_MESSAGE;
declare _SUPPLEMENTARY_GROUPS;
declare _response_GUID;
declare _REGENERATE_KEY="no";
declare _ACCESS_TYPE;
declare _ACCESS_ACTION;
_SSH_ACCESS_GROUP="ssh-access";
declare -r _JELASTIC_SHELL="/opt/jsh/jsh"

VERBOSE=1;
function onModLoadCallback() {
	[[ "$UID" != '0' ]] && {  writeJSONResponseErr "result=>4034" "message=>You must be root!"; die -q; }
    return 0
}

function doUsage() {
    showUsageMessage
}

function checkUserExists(){
	local user=${1};
	# response=$($GETENT passwd ${user});
	response=$($GREP -E "^${user}:" /etc/passwd );
	_USER_EXISTS=$?;
	log "Check user ${response}";
}

function checkGroupExists(){
	local group=${1};
	response=$($GETENT group ${group});
	_GROUP_EXISTS=$?;
	log "Check group ${response} $_GROUP_EXISTS";
}

function groupId(){
	local group=${1};
	unset _response_GUID;
	log "groupId: ${group}";
	[ -z "$group" ] && {  writeJSONResponseErr "result=>4027" "message=>Cannot get GUID!"; die -q; };
	checkGroupExists "${group}";
	[ "$_GROUP_EXISTS" -ne 0 ] && {
		log "Adding group ${group}";
		$GROUPADD "${group}" 2>&1 | $GRAB_OUTPUT ;
		result=$?;
		[ "$result" -ne 0 ] && { echo "ERROR adding group !!!!!!!!!!!!!!!!!!!!!!!!!"; }
	}
	_response_GUID=$($GETENT group ${group} | $AWK -F: '{ print $3 }');

}

function __injected() {
    local CTID=$1 KEY
    [[ -z "${CTID}" ]] && {
	writeJSONResponseErr "result=>4105" "message=>Container not found"
	die -q
    }
    local pubkey
    result=0

    $PROGRAM 'ssh-keygen'
    $PROGRAM 'vzctl'
    $PROGRAM 'hostname'
    # invalidate package db
    $VZCTL exec $CTID '[[ -f /etc/redhat-release ]] && rpm --rebuilddb &>/dev/null'

    #_DEFAULT_COMMENT comes from user-common.lib via include of $gw_boost_lib_inherit variable from metainf
    if [ -z "$_DEFAULT_COMMENT" ] ; then
        _DEFAULT_COMMENT="ssh-gate@jelastic.com"
    fi
    tmpfile=$(mktemp --dry-run)
    response=$($SSH_KEYGEN -C $_DEFAULT_COMMENT -P "" -q -f $tmpfile 2>&1)
    log "ssh-keygen response $response"

    $VZCTL exec $CTID 'if ! cd /root/.ssh 2>/dev/null >/dev/null; then mkdir -p /root/.ssh 2>/dev/null >/dev/null || rm /root && mkdir -p /root/.ssh; chmod 0700 /root/.ssh; fi'
    if [ -f "$tmpfile" ] ; then
        pubkey=$(cat "$tmpfile.pub")
        privkey=$(cat "$tmpfile")
    else
        result=4045
    fi

    #fix permissions for existing key
    $VZCTL exec $CTID "[ -f /root/.ssh/id_rsa ] && chmod 400 /root/.ssh/id_rsa"
    if $VZCTL exec $CTID "[[ -f '/root/.ssh/id_rsa' ]]" && \
        $VZCTL exec $CTID "ssh-keygen -l -f /root/.ssh/id_rsa" &>/dev/null && \
        $VZCTL exec $CTID "ssh-keygen -y -P \"\" -C $_DEFAULT_COMMENT -f /root/.ssh/id_rsa &>/dev/null" && \
        [[ "$_REGENERATE_KEY" != "yes" ]]; then
            $VZCTL exec $CTID "[[ -f /root/.ssh/authorized_keys ]]" && $VZCTL exec $CTID 'grep -q "$(ssh-keygen -y -f /root/.ssh/id_rsa)" /root/.ssh/authorized_keys' || \
            $VZCTL exec $CTID "echo \"\$(ssh-keygen -y -f /root/.ssh/id_rsa) $_DEFAULT_COMMENT\" >> /root/.ssh/authorized_keys"
    else
        $VZCTL exec $CTID "[[ -f '/root/.ssh/authorized_keys' ]] && { $SED -i -re \"/${_DEFAULT_COMMENT}/d;/^s*$/d\" /root/.ssh/authorized_keys; chmod 0400 /root/.ssh/authorized_keys 2>>"$JEM_CALLS_LOG"; }"
        $VZCTL exec $CTID "echo -e \"\n${pubkey}\n\" >> /root/.ssh/authorized_keys"
        $VZCTL exec $CTID "echo -e \"${privkey}\" > /root/.ssh/id_rsa"
        $VZCTL exec $CTID "chmod 400  /root/.ssh/id_rsa";
    fi
    $VZCTL exec $CTID "$SED -i -re 's/#?[Ss][Tt][Rr][Ii][Cc][Tt][Mm][Oo][Dd][Ee][Ss]\s+[Yy][Ee][Ss]/StrictModes no/g' /etc/ssh/sshd_config >/dev/null 2>&1"
    $VZCTL exec $CTID "service sshd reload >/dev/null 2>&1 || service ssh reload >/dev/null 2>&1"
    KEY="$($VZCTL exec $CTID 'cat /root/.ssh/id_rsa')"
    echo "{\"result\":\"${result}\",\"message\":\"$KEY\"}"
    rm "$tmpfile"
    rm "$tmpfile.pub"
    return 0;
}

function __processVDS(){
    CTID=$1
    inject doAdd 3i "return 0;"
    inject getAppUserInfo 3i "return 0;"
    inject doAdd 3i "__injected $CTID;"
    return 0
}

function __injected_not_vds_add() {
    CTID=$1
    if [ "x$_DISABLE_PASS_AUTH" == "xtrue" ] ; then
        local DISABLE_PASS_FLAG="-d";
    fi
    vzexecSetCTID ${CTID}
    # invalidate package db
    vzexecRun '[[ -f /etc/redhat-release ]] && rpm --rebuilddb &>/dev/null'
    #vzctl exec ${_CTID} "jem user add"
    vzexecRun jem user add ${DISABLE_PASS_FLAG}
    if [ "$(vzexecGetLastErrCode)" -eq 0 ]; then
        _homedir=$(vzreadlink --vzroot="${prefix}" $(vzexecGetLastStdOut | $SED -rn 's/.*["]homedir["]:[[:blank:]]*["]([^"]+)["].*/\1/p'))
        DATA_OWNER="$(vzexecGetLastStdOut | $SED -rn 's/.*["]data_owner["]:[[:blank:]]*["]([^"]+)["].*/\1/p')"
        _UID=${DATA_OWNER/:*}
        _GUID=${DATA_OWNER/*:}
        [ -z "$_UID" -o -z "$_GUID" ] && {  writeJSONResponseErr "result=>4026" "message=>Cannot get UID!"; die -q; };
        if [ "x${COMPUTE_TYPE}" == "xgate" ] ;then
            return 0;
        fi
        bashRestricts 2>>"$JEM_CALLS_LOG" && vzexecGetLastStdOut && return 0 || { writeJSONResponseErr "result=>4119" "message=>Cannot fix user rights"; return 4119; }
    else
        writeJSONResponseErr "result=>4045" "message=>${vzexecGetLastStdErr}"
    fi
}

function __processNotVDS(){
    CTID=$1
    inject doAdd 3i "return 0;"
    inject getAppUserInfo 3i "return 0;"
    inject doAdd 3i "__injected_not_vds_add $CTID;"
    return 0
}

function preAddCallback(){
    log "$@";

    while [ $# -gt 0 ]
    do
            case $1 in
            # for options with required arguments, an additional shift is required
            -d) _DISABLE_PASS_AUTH="true";;
            (--) shift; break;;
            -n|--new) _REGENERATE_KEY="yes";;
            (--) shift; break;;
            -e|--eid)
                _EID="$2";
                shift;shift;;
            esac
            shift
    done

    log "Compute Type not gate."
    _USERADD_OPT=" -M ";
    _SUPPLEMENTARY_GROUPS="$_SSH_ACCESS_GROUP";

    if [ ! -z "${_EID}" ]; then
        _CTID=$($SED -nr '/^<ns1:veid>([[:digit:]]+)<[/]ns1:veid>$/{s//\1/p; q}' "/var/opt/pva/agent/etc/configs/${_EID}" 2>/dev/null || :)
        [[ -z "${_CTID}" ]] && {
            writeJSONResponseErr "result=>4105" "message=>Container not found"
            die -q
        }

        # Call user add only for running container
        local waitCT=30 running
        isContainerRunning ${_CTID} ; running=$?
        while [ $running -ne 0 ]
        do
            waitCT=$((waitCT-1))
            sleep 0.5
            if [[ $waitCT == 0 ]]; then
                writeJSONResponseErr "result=>4110" "message=>Cannot execute command. Container is not running" ;
                die -q;
            fi
            isContainerRunning ${_CTID} ; running=$?
        done
        prefix=$($VZLIST $_CTID -Ho root)
        [[ -z "${prefix}" ]] && {
            writeJSONResponseErr "result=>4105" "message=>Container root dir not found"
            die -q
        }
        if [[ -f ${prefix}/etc/jelastic/metainf.conf ]]; then
            # JE-45748: evalueate compute type from metainf, empty value if there is no compute type in file
            eval "CT_$(grep "COMPUTE_TYPE=" ${prefix}/etc/jelastic/metainf.conf 2>/dev/null || echo "COMPUTE_TYPE=")"
            if [ ! -z $CT_COMPUTE_TYPE ] && [ "x$CT_COMPUTE_TYPE" != "xvds" ] && [ "x$CT_COMPUTE_TYPE" != "xubuntuvds" ] && [ "x$CT_COMPUTE_TYPE" != "xdebianvds" ] && [ "x$CT_COMPUTE_TYPE" != "xvzlinuxvps" ] && [ "x$CT_COMPUTE_TYPE" != "xalmalinuxvps" ] ; then
                # JE-45748 : container with metainf.conf and not empty compute type
                __processNotVDS ${_CTID}
                _UID=700
                _GUID=700
            else
                # JE-45748 : container with metainf.conf and empty compute type
                __processVDS ${_CTID}
                _UID=0
                _GUID=0
            fi
        else
            # JE-45748 : container without metainf.conf
            __processVDS ${_CTID}
            _UID=0
            _GUID=0
        fi
    fi

    isFunction "getAppUserInfo" && { log "Call getAppUserInfo"; "getAppUserInfo" "${_USER_NAME}"; } || {  writeJSONResponseErr "result=>4048" "message=>Cannot get user info!"; die -q; };
    [ -z "$_UID" -o -z "$_GUID" ] && {  writeJSONResponseErr "result=>4026" "message=>Cannot get UID!"; die -q; };
}

function doAdd() {
	log "detected user id $_UID";
	log "detected group id $_GUID";
	#check jelastic-ssh group and add if not exists

	[ -z "${_UID}" ]  && { log "Cannot get UID"; writeJSONResponseErr "result=>4026" "message=>Cannot get UID!"; die -q; };
	[ -z "${_GUID}" ]  && { log "Cannot get GUID";  writeJSONResponseErr "result=>4027" "message=>Cannot get GUID!"; die -q; };
	[ -z "${_homedir}" ] && { log "Cannot get homedir";  writeJSONResponseErr "result=>4025" "message=>Cannot get homedir!"; die -q; };
	[ -z "${_USER_NAME}" ] && { log "Cannot get user name";  writeJSONResponseErr "result=>4024" "message=>Cannot get user name!"; die -q; };

	checkUserExists "${_USER_NAME}";
	[ ${_USER_EXISTS} -ne 0 ] && {
		[ ! -z "${_SUPPLEMENTARY_GROUPS}" ] && {
			local tmpgroup=$($SED 's/\,/ /g' <<< $_SUPPLEMENTARY_GROUPS);
			for group in $tmpgroup
			do
				log "$group";
				checkGroupExists "${group}";
				[ "$_GROUP_EXISTS" -ne 0 ] && {
					log "Adding group ${group}";
					$GROUPADD "${group}" 2>&1 | $GRAB_OUTPUT ;
					result=$?;
					[ "$result" -ne 0 ] && { writeJSONResponseErr "result=>4044" "message=>ERROR adding group!"; die -q; };
				}
			done
			_SUPPLEMENTARY_GROUPS=" -G "${_SUPPLEMENTARY_GROUPS};
		}
		$USERADD -ou ${_UID} ${_USERADD_OPT} -g ${_GUID} -d ${_homedir} ${_SUPPLEMENTARY_GROUPS} ${_USER_NAME}  2>&1 | $GRAB_OUTPUT;
		checkUserExists ${_USER_NAME};
		[ ${_USER_EXISTS} -ne 0 ] && { writeJSONResponseErr "result=>4045" "message=>ERROR adding user!"; die -q; };
	}
	ExtendPerm ;
	if isFunction "postAddReform"; then
		"postAddReform" "${_USER_NAME}";
		result_code=$?;
		if [ "$result_code" -eq 0 ]; then
			#writeJSONResponseOut "result=>0" "message=>${_SSH_PRIVATE_KEY}"
			echo "{\"result\":\"0\",\"message\":\"${_SSH_PRIVATE_KEY}\", \"homedir\":\"${_homedir}\", \"data_owner\":\"${_UID}:${_GUID}\"}"
		else
			writeJSONResponseErr "result=>4106" "message=>${_REFORM_MESSAGE}"
			return 1
		fi
	else
		forceLog "No postAddReform" ;
		writeJSONResponseOut "result=>0" "message=>OK";
	fi
}

function doAccess(){
        while [ $# -gt 0 ]
        do
                case $1 in
                        -t|--type)
                            _ACCESS_TYPE=$2; shift ;;
                        -a|--action)
                            _ACCESS_ACTION=$2; shift ;;
                        (--) shift; break;;
                esac
                shift
        done
        if [ "x${_ACCESS_TYPE}" == "xpwd" -a "x${_ACCESS_ACTION}" == "xdisable" ]
        then
            sshfile="/etc/ssh/sshd_config";
            if $GREP -qE "^PasswordAuthentication.*$" "${sshfile}"; then
                    $SED -i 's/^PasswordAuthentication.*$/PasswordAuthentication no/g' "${sshfile}"
            else
                if $GREP -qE "^\#PasswordAuthentication.*$" "${sshfile}"; then
                    $SED -i 's/^\#PasswordAuthentication.*$/PasswordAuthentication no/g' "${sshfile}"
                else
                    echo "PasswordAuthentication no" >> ${sshfile};
                fi
            fi
            command -v systemctl >/dev/null 2>&1 && {
                systemctl --quiet is-enabled systemd-logind.service 2>/dev/null;
                isEnabled=$?;
                if [ $isEnabled -eq 0 ] ; then
                        systemctl  --quiet try-restart systemd-logind.service 2>/dev/null ;
                fi
             }

            reloadServiceSilent sshd
            writeJSONResponseOut "result=>0" "message=>Ok";
        else
            writeJSONResponseOut "result=>0" "message=>Not implemented";
        fi
}

function describeAdd() {
	echo "Add Jelastic user for GW" ;
}

function describeAccess() {
	echo "Enable/disabe ssh access" ;
}

function describeAddParameters() {
    echo "-e|--eid <environment id> -n|--new -u|--uid <user id>";
}

function describeAddOptions() {
	echo "-d: disables access by password";
	echo "-e|--eid: environment id";
	echo "-n|--new: regenerate keys";
	echo "-u|--uid: user id";
}

function describeAccessParameters() {
    echo "-t|--type <pwd> -a|--action <disable>";
}

function describeAccessOptions() {
	echo "-t|--type: access type";
	echo "-a|--action: action (enable|disable)";
}
