#!/bin/bash
# performs actions requested through the web interface for unmanaged clients

SPATH="$(dirname "$(readlink -f ${BASH_SOURCE[0]})")"    # dir of the script
SNAME="$(basename ${BASH_SOURCE[0]})"                    # name of the script

ASCEDSCONF='local site certbot'
ASCEDSLIBS='utils'

if [ ! -r "${SPATH}/asceds-website-head" ] ; then
   echo "No header ${SPATH}/asceds-website-head. Exiting"
   exit 9
fi

. ${SPATH}/asceds-website-head

asceds-web-unmanaged-usage()
{
  cat << ENDUSAGE

Performs actions requested through the web interface for unmanaged clients 
asceds-web-unmanaged [-h] [-q] [-n] [-r] [-c <client_name>]
-h --> display usage and exit
-q --> no question asked; sends output to the logs in ${ASCEDSLOGDIR}/
-n --> new certificate actions only
-r --> revoke actions only
-k <encryption> --> type of encryption (rsa/ecc, default: rsa)
-c <client_name> --> act only for <client_name>
If both types of requests (new certificate and revoke) are found,
revoke requests are performed first.

Revoke: requests in ${ASCEDSWEBDIR}/cert_queue/*.rev:
   read .rev file;
   log: date Web user <username> revoked certificate for <hostname>;
   asceds-certbot-revoke -q -e -c <hostname>;
   remove .rev file;

Newcert: requests in ${ASCEDSWEBDIR}/cert_queue/*.cert:
   read .cert file;
   create/adjust ~asceds/<hostname>_cert.conf;
        populate populate ASCEDS_CERT_ID if empty;
   log: date Web user <username> requested certificate for <hostname>
        with SANs <sans>, client type changed to: <ack>;
   asceds-certbot-gencert -e -q -c <hostname> -s <sans>;
   remove .cert file.

ENDUSAGE
exit 0
}

# output to terminal by default
QUIETRUN=""

# perform newcert actions (default = no)
NEWCERTACT=''

# perform revoke actions (default = no)
REVOKEACT=''

# client to use with the -c option
CLIENT_NAME=''

# encryption type (rsa/ecc, default rsa)
CLIENT_ENC='rsa'

# general validation and parse of the command line
for ARG in ${ARGS} ; do
   #echo "ARG=${ARG}"
   if [ "${ARG}" != "${ARG//[^-_a-zA-Z0-9.]/}" ] ; then
      asceds-error "Malformed argument ${ARG}"
   elif [ "${ARG}" == "-q" ] ; then
      shift
      asceds-exec-noninteractive "$*"
      # bring other scripts in non-interactive mode
      QUIETRUN="y"
   elif [ "${ARG}" == "-n" ] ; then
      shift
      NEWCERTACT='yes'
   elif [ "${ARG}" == "-r" ] ; then
      shift
      REVOKEACT='yes'
   elif [ "${ARG}" == "-h" ] ; then
      shift
      asceds-web-unmanaged-usage
   elif [ "${ARG}" == "-k" ] ; then
      shift
      CL_ENC=$1
      if [ "${CL_ENC}" == 'ecc' ] || [ "${CL_ENC}" == 'ecdsa' ] ; then
         CLIENT_ENC='ecc'
      fi
   elif [ "${ARG}" == "-c" ] ; then
      shift
      CLIENT_NAME=$( cat <<< $1 | tr '[:upper:]' '[:lower:]' )
      if [ -z "${CLIENT_NAME}" ] || [[ "${CLIENT_NAME}" = "-"* ]] ; then
         asceds-error "No client name passwed as argument"
      fi 
      asceds-expand-hostname "${CLIENT_NAME}" && \
         CLIENT_NAME="${ASCEDS_FQDN}"
      asceds-validate-fqdn "${CLIENT_NAME}" || \
         asceds-error "Invalid client FQDN ${CLIENT_NAME}"
   else
      shift
   fi
done

REVOKELIST=''
NEWCERTLIST=''
if [ -n "${CLIENT_NAME}" ] ; then
   if [ -n "${REVOKEACT}" ] ; then
      REVOKELIST="${ASCEDSWEBDIR}/cert_queue/${CLIENT_NAME}.rev"
      if [ ! -r "${REVOKELIST}" ] ; then
         asceds-error "No revoke request file ${REVOKELIST}"
      fi
   elif [ -n "${NEWCERTACT}" ] ; then
      NEWCERTLIST="${ASCEDSWEBDIR}/cert_queue/${CLIENT_NAME}.cert"
      if [ ! -r "${NEWCERTLIST}" ] ; then
         asceds-error "No new certificate request file ${NEWCERTLIST}"
      fi
   fi
else
   if [ -n "${REVOKEACT}" ] ; then
      if [ "$( echo ${ASCEDSWEBDIR}/cert_queue/*.rev )" != "${ASCEDSWEBDIR}/cert_queue/*.rev" ] ; then
         REVOKELIST=$( echo ${ASCEDSWEBDIR}/cert_queue/*.rev )
      fi
   fi
   if [ -n "${NEWCERTACT}" ] ; then
      if [ "$( echo ${ASCEDSWEBDIR}/cert_queue/*.cert )" != "${ASCEDSWEBDIR}/cert_queue/*.cert" ] ; then
         NEWCERTLIST=$( echo ${ASCEDSWEBDIR}/cert_queue/*.cert )
      fi
   fi
fi


if [ -n "${REVOKELIST}" ] ; then
   # asceds-echo "Searching for revoke requests"
   for REQ in ${REVOKELIST} ; do
      CLIENT=$( basename ${REQ} )
      CLIENT=${CLIENT%.*}

      # parse ${REQ}
      # . ${REQ} 
      asceds-parse-revokereq "${REQ}"

      if [ "${CLIENT_DOMAIN_NAME}" != "${CLIENT}" ] ; then
         asceds-error "Name of revoke request file ${CLIENT} doesn't match content ${CLIENT_DOMAIN_NAME}."
      fi
      
      # revoke --> would backup and remove cert.conf
      # and remove the cert files including dirs 
      asceds-certbot-revoke -q -e -c ${CLIENT_DOMAIN_NAME} > /dev/null 2>&1

      # log
      echo "--> ${REQUEST_DATE} Web user ${REQUESTED_BY} revoked certificate for ${CLIENT_DOMAIN_NAME}"
      echo "--> ${REQUEST_DATE} Web user ${REQUESTED_BY}" >> ${ASCEDSWEBHISTFILE}
      echo "    revoked certificate for ${CLIENT_DOMAIN_NAME}" >> ${ASCEDSWEBHISTFILE} 

      # notify user
      if [ -z "${CLIENT_NAME}" ] ; then
         # send email to user
         SUBJ="Revoked certificate for ${CLIENT_DOMAIN_NAME}"
         BODY="${REQUEST_DATE} Web user ${REQUESTED_BY} 
            revoked certificate for ${CLIENT_DOMAIN_NAME}"
         if [ -z "${ALARMSEND}" ] ; then
            asceds-error "No mailer installed: no email will be sent"
         else
            cat <<< ${BODY} | ${ALARMSEND} -s "${SUBJ}" ${REQUESTED_BY}
         fi
      fi
      
      # clean up
      ${RM} -f ${REQ} > /dev/null 2>&1
   done
fi 

if [ -n "${NEWCERTLIST}" ] ; then
   # asceds-echo "Searching for new certificate requests"
   for REQ in ${NEWCERTLIST} ; do
      CLIENT=$( basename ${REQ} )
      CLIENT=${CLIENT%.*}
   
      # parse ${REQ}
      # . ${REQ} 
      asceds-parse-newcertreq "${REQ}"
      if [ "${CLIENT_DOMAIN_NAME}" != "${CLIENT}" ] ; then
         asceds-error "Name of new certificate request file ${CLIENT} doesn't match content ${CLIENT_DOMAIN_NAME}."
      fi
      
      # build cert.conf
      CERTCONFFILE="${ASCEDSHOMEDIR}/${CLIENT_DOMAIN_NAME}_cert.conf"
      if [ -r "${CERTCONFFILE}" ] ; then
         # if cert.conf file already exists
         # backup cert.conf
         ${CP} -f ${CERTCONFFILE} ${CERTCONFFILE}.bak > /dev/null 2>&1

         # populate ASCEDS_CERT_ID only if empty: managed -> unmanaged
         asceds-extract "ASCEDS_CERT_ID" "${CERTCONFFILE}"
         if [ -z "${VARVAL}" ] ; then
            ASCEDS_CERT_ID=$( cat /dev/urandom | tr -dc 'a-zA-Z0-9' | \
                                 fold -w 32 | head -n 1 )
            asceds-refresh-var "${CERTCONFFILE}" "ASCEDS_CERT_ID" \
                            "${ASCEDS_CERT_ID}" > /dev/null 2>&1
         fi
         # adjust previous values
         asceds-refresh-var "${CERTCONFFILE}" "CLIENT_SANS_LIST" \
                            "${CLIENT_SANS_LIST}" > /dev/null 2>&1
         asceds-refresh-var "${CERTCONFFILE}" \
                            "NO_RETURN" "yes" > /dev/null 2>&1
         asceds-refresh-var "${CERTCONFFILE}" "NO_ASCEDS" \
                            "${REQUESTED_BY}" > /dev/null 2>&1
         asceds-refresh-var "${CERTCONFFILE}" "CLIENT_ENC" \
                            "${CLIENT_ENC}" > /dev/null 2>&1
      else
         # build fresh cert.conf
         : > ${CERTCONFFILE} || \
             asceds-error "Cannot create cert.conf file ${CERTCONFFILE}"
         ASCEDS_CERT_ID=$( cat /dev/urandom | tr -dc 'a-zA-Z0-9' | \
                                 fold -w 32 | head -n 1 )
         cat <<< "# client hostname" >> ${CERTCONFFILE}
         cat <<< "CLIENT_DOMAIN_NAME='${CLIENT_DOMAIN_NAME}'" >> ${CERTCONFFILE}
         cat <<< "" >> ${CERTCONFFILE}
         cat <<< "# client SANS list" >> ${CERTCONFFILE}
         cat <<< "CLIENT_SANS_LIST='${CLIENT_SANS_LIST}'" >> ${CERTCONFFILE}
         cat <<< "" >> ${CERTCONFFILE}
         cat <<< "# don't return certificate to client?" >> ${CERTCONFFILE}
         cat <<< "NO_RETURN='yes'" >> ${CERTCONFFILE}
         cat <<< "" >> ${CERTCONFFILE}
         cat <<< "# if not handled by asceds: for notifications" >> ${CERTCONFFILE}
         cat <<< "NO_ASCEDS='${REQUESTED_BY}'" >> ${CERTCONFFILE}
         cat <<< "" >> ${CERTCONFFILE}
         cat <<< "# certificate id: for unmanaged clients" >> ${CERTCONFFILE}
         cat <<< "ASCEDS_CERT_ID='${ASCEDS_CERT_ID}'" >> ${CERTCONFFILE}
         cat <<< "" >> ${CERTCONFFILE}
         cat <<< "# client platform" >> ${CERTCONFFILE}
         cat <<< "CLIENT_PLATFORM='unknown'" >> ${CERTCONFFILE}
         cat <<< "" >> ${CERTCONFFILE}
         cat <<< "# client certificate encoding" >> ${CERTCONFFILE}
         cat <<< "CLIENT_ENC=${CLIENT_ENC}" >> ${CERTCONFFILE}
      fi
      chown asceds:asceds ${CERTCONFFILE}*

      
      # log
      echo "--> ${REQUEST_DATE} Web user ${REQUESTED_BY} requested certificate for ${CLIENT_DOMAIN_NAME}"
      echo "with SANS list: ${CLIENT_SANS_LIST}"
      echo "client type change: ${ACK_CHANGE_TYPE}"
      echo "--> ${REQUEST_DATE} Web user ${REQUESTED_BY}" >> ${ASCEDSWEBHISTFILE}
      echo "    requested certificate for ${CLIENT_DOMAIN_NAME}" >> ${ASCEDSWEBHISTFILE}
      echo "    with SANS list: ${CLIENT_SANS_LIST}" >> ${ASCEDSWEBHISTFILE}
      echo "    client type change: ${ACK_CHANGE_TYPE}" >> ${ASCEDSWEBHISTFILE}

      # gencert
      GENCERTARGS="-q -e -c ${CLIENT_DOMAIN_NAME}"
      if [ -n "${CLIENT_SANS_LIST}" ] ; then
         GENCERTARGS="${GENCERTARGS} -s ${CLIENT_SANS_LIST}"
      fi
      asceds-certbot-gencert ${GENCERTARGS} > /dev/null 2>&1 
      RETVAL=$?
      if [ "${RETVAL}" != "0" ] ; then     
         ${RM} -f ${REQ} > /dev/null 2>&1
         asceds-error "asceds-certbot-gencert could not generate a certificate."
      fi
      
      # notify user
      if [ -z "${CLIENT_NAME}" ] ; then
         # send email to user
         SUBJ="Generated certificate for ${CLIENT_DOMAIN_NAME}"
         BODY="${REQUEST_DATE} Web user ${REQUESTED_BY} 
            generated certificate for ${CLIENT_DOMAIN_NAME}"
         if [ -z "${ALARMSEND}" ] ; then
            asceds-error "No mailer installed: no email will be sent"
         else
            cat <<< ${BODY} | ${ALARMSEND} -s "${SUBJ}" ${REQUESTED_BY}
         fi
      fi

      # clean
      ${RM} -f ${REQ} > /dev/null 2>&1
   done
fi

