#!/bin/sh
######################################################################
#
# $Id: webjob-create-account,v 1.2 2012/01/07 08:01:17 mavrik Exp $
#
######################################################################
#
# Copyright 2011-2012 The WebJob Project, All Rights Reserved.
#
######################################################################
#
# Purpose: Create a WebJob user account.
#
######################################################################

IFS=' 	
'

PROGRAM=`basename ${0}`

umask 027

######################################################################
#
# VerifyPassword
#
######################################################################

VerifyPassword()
{
  MY_ARG_USERNAME=${1}
  MY_ARG_PASSWORD=${2}
  MY_ARG_HTUSERS_ENTRY=${3}

  MY_TARGET_PASSWORD_HASH=`echo "${MY_ARG_HTUSERS_ENTRY}" | sed "s/^${MY_ARG_USERNAME}://; s/^LOCKED//;"`

  case "${MY_TARGET_PASSWORD_HASH}" in
  \$apr1\$*)
    MY_ACTUAL_PASSWORD_HASH=`perl -e 'use Crypt::PasswdMD5; print apache_md5_crypt(q('${MY_ARG_PASSWORD}'), q('${MY_TARGET_PASSWORD_HASH}')), "\n";'`
    ;;
  [0-9A-Za-z./][0-9A-Za-z./]*)
    MY_ACTUAL_PASSWORD_HASH=`perl -e 'print crypt(q('${MY_ARG_PASSWORD}'), q('${MY_TARGET_PASSWORD_HASH}')), "\n";'`
    ;;
  *)
    return 1
    ;;
  esac

  if [ X"${MY_ACTUAL_PASSWORD_HASH}" = X"${MY_TARGET_PASSWORD_HASH}" ] ; then
    return 0
  fi

  return 1
}

######################################################################
#
# VerifyPrograms
#
######################################################################

VerifyPrograms()
{
  MY_PROGRAMS="
htpasswd
perl
webjob-cfg-get-kvps
webjob-dsvtool
"
  MY_DIRS=`echo "${PATH}" | sed 's/:/ /g;'`

  for MY_PROGRAM in ${MY_PROGRAMS} ; do
    MY_PROGRAM_FOUND="0"
    for MY_DIR in ${MY_DIRS} ; do
      MY_TEST_PATH="${MY_DIR}/${MY_PROGRAM}"
      if [ -x ${MY_TEST_PATH} ] ; then
        MY_PROGRAM_FOUND="1"
        break;
      fi
    done
    if [ ${MY_PROGRAM_FOUND} -ne 1 ] ; then
      echo "${PROGRAM}: Error='Unable to locate an executable instance of ${MY_PROGRAM} in the current PATH (${PATH}).'" 1>&2
      exit 2
    fi
  done
}

######################################################################
#
# Usage
#
######################################################################

Usage()
{
  echo 1>&2
  echo "${PROGRAM} [-H webjob-home] [-o option[,option[,...]]] [-p password] [-S webjob-server-home] -u username" 1>&2
  echo 1>&2
  exit 1
}

######################################################################
#
# Main
#
######################################################################

OPTIONS=""

PASSWORD=""

PASSWORD_GENERATED="0"

PASSWORD_SPECIFIED="0"

WEBJOB_SERVER_HOME="/var/webjob"

while getopts "H:o:p:S:u:" OPTION ; do
  case "${OPTION}" in
  H)
    WEBJOB_HOME="${OPTARG}"
    ;;
  o)
    OPTIONS="${OPTARG}"
    ;;
  p)
    PASSWORD="${OPTARG}"
    PASSWORD_SPECIFIED="1"
    ;;
  S)
    WEBJOB_SERVER_HOME="${OPTARG}"
    ;;
  u)
    USERNAME="${OPTARG}"
    ;;
  *)
    Usage
    ;;
  esac
done

shift `expr ${OPTIND} - 1`

if [ -z "${USERNAME}" ] ; then
  Usage
fi

PATH=${WEBJOB_HOME=/usr/local/webjob}/bin:${PATH} ; export PATH

VerifyPrograms

if [ ! -d "${WEBJOB_SERVER_HOME}" ] ; then
  echo "${PROGRAM}: Error='Server home directory (${WEBJOB_SERVER_HOME}) does not exist.'" 1>&2
  exit 2
fi

WEBJOB_SERVER_CONFIG_FILE="${WEBJOB_SERVER_HOME}/config/server.cfg"
if [ ! -f "${WEBJOB_SERVER_CONFIG_FILE}" ] ; then
  echo "${PROGRAM}: Error='Server config file (${WEBJOB_SERVER_CONFIG_FILE}) does not exist.'" 1>&2
  exit 2
fi

WWW_OWNER=`webjob-cfg-get-kvps -t webjob.server -f ${WEBJOB_SERVER_CONFIG_FILE} -o BeQuiet,ValuesOnly ApacheOwner`
if [ -z "${WWW_OWNER}" ] ; then
  WWW_OWNER="www"
fi
WWW_OWNER_TEST=`awk -F: '{print $1}' /etc/passwd | egrep "${WWW_OWNER}"`
if [ -z "${WWW_OWNER_TEST}" ] ; then
  echo "${PROGRAM}: Error='Unable to verify that \"${WWW_OWNER}\" is a valid owner. Make sure that ApacheOwner is set properly in ${WEBJOB_SERVER_CONFIG_FILE}.'" 1>&2
  exit 2
fi

WWW_GROUP=`webjob-cfg-get-kvps -t webjob.server -f ${WEBJOB_SERVER_CONFIG_FILE} -o BeQuiet,ValuesOnly ApacheGroup`
if [ -z "${WWW_GROUP}" ] ; then
  WWW_GROUP="www"
fi
WWW_GROUP_TEST=`awk -F: '{print $1}' /etc/group | egrep "${WWW_GROUP}"`
if [ -z "${WWW_GROUP_TEST}" ] ; then
  echo "${PROGRAM}: Error='Unable to verify that \"${WWW_GROUP}\" is a valid group. Make sure that ApacheGroup is set properly in ${WEBJOB_SERVER_CONFIG_FILE}.'" 1>&2
  exit 2
fi

######################################################################
#
# Ensure that all prerequisites exist. Any error is considered fatal
# since it implies that the server's configuration is not complete.
#
######################################################################

HTUSERS_FILE="${WEBJOB_SERVER_HOME}/config/apache/ht-public"
if [ ! -f "${HTUSERS_FILE}" ] ; then
  echo "${PROGRAM}: Error='Password file (${HTUSERS_FILE}) does not exist.'" 1>&2
  exit 2
fi

WEBJOB_USERS_DIR="${WEBJOB_SERVER_HOME}/users"
if [ ! -d "${WEBJOB_USERS_DIR}" ] ; then
  echo "${PROGRAM}: Error='Users directory (${WEBJOB_USERS_DIR}) does not exist.'" 1>&2
  exit 2
fi

  ####################################################################
  #
  # Parse the options list.
  #
  ####################################################################

  FORCE="0"

  FORCE_PASSWORD="0"

  LOCK_ACCOUNT="0"

  for OPTION in `echo "${OPTIONS}" | sed 's/,/ /g;' | tr '[A-Z]' '[a-z]'` ; do
    case "${OPTION}" in
    force)
      FORCE="1"
      ;;
    forcepassword)
      FORCE_PASSWORD="1"
      ;;
    lockaccount)
      LOCK_ACCOUNT="1"
      ;;
    esac
  done

######################################################################
#
# Do some work.
#
######################################################################

MY_ERROR_FLAG="0"

  ####################################################################
  #
  # Do a sanity check on the username.
  #
  ####################################################################

  MY_USERNAME_OK=`echo ${USERNAME} | perl -n -e 'if ($_ =~ /^[0-9A-Za-z_]{1,32}$/) { print "pass\n"; } else { print "fail\n"; }'`

  if [ x"${MY_USERNAME_OK}" != x"pass" ] ; then
    echo "${PROGRAM}: Error='Username (${USERNAME}) does not pass muster. The account will not be created.'" 1>&2
    exit 2
  fi

  ####################################################################
  #
  # Conditionally generate a password, and update the htusers file.
  # If the htusers file already has an entry for this account and no
  # password was specified and the force option is disabled, abort --
  # blindly updating the htusers file with a new password could lock
  # out existing accounts. If a password was specified, then assume
  # the user wants the htusers file to be updated.
  #
  ####################################################################

  if [ ${PASSWORD_SPECIFIED} -eq 0 -a -n "${WEBJOB_PASSWORD}" ] ; then
    PASSWORD="${WEBJOB_PASSWORD}"
    PASSWORD_SPECIFIED="1"
  fi

  if [ ${PASSWORD_SPECIFIED} -eq 1 ] ; then
    if [ -n "${PASSWORD}" ] ; then
      MY_PASSWORD=${PASSWORD}
    else
      : # Empty passwords are not allowed. This error will be caught below.
    fi
  else
    MY_PASSWORD=`webjob-dsvtool -p`
    PASSWORD_GENERATED="1"
  fi

  echo "${MY_PASSWORD}" | egrep "^[0-9A-Za-z/+]{8,}$" > /dev/null 2>&1
  if [ $? -ne 0 ] ; then
    echo "${PROGRAM}: Error='The password for ${USERNAME} does not pass muster. The account will not be created.'" 1>&2
    exit 2
  fi

  MY_HTUSERS_ENTRY=`egrep "^${USERNAME}:" ${HTUSERS_FILE} 2> /dev/null`
  if [ -n "${MY_HTUSERS_ENTRY}" ] ; then
    if [ ${FORCE_PASSWORD} -eq 0 ] ; then
      if [ ${PASSWORD_SPECIFIED} -eq 1 ] ; then
        VerifyPassword "${USERNAME}" "${MY_PASSWORD}" "${MY_HTUSERS_ENTRY}"
        if [ $? -ne 0 ] ; then
          echo "${PROGRAM}: Error='Account (${USERNAME}) already has an htusers entry, and the supplied password is not correct.'" 1>&2
          exit 4 # XER_PasswordRequired
        fi
      else
        echo "${PROGRAM}: Error='Account (${USERNAME}) already has an htusers entry. To continue, either supply the current password with the \"-p\" option or use the \"ForcePassword\" option to force a password update.'" 1>&2
        exit 4 # XER_PasswordRequired
      fi
    fi
  fi

  if [ ${PASSWORD_GENERATED} -eq 1 ] ; then
    echo "Updating ${HTUSERS_FILE} (Username=\"${USERNAME}\"; Password=\"${MY_PASSWORD}\";)"
  else
    echo "Updating ${HTUSERS_FILE}"
  fi

  if [ ${LOCK_ACCOUNT} -eq 1 ] ; then
    MY_NEW_HTUSERS_ENTRY=`htpasswd -b -m -n ${USERNAME} ${MY_PASSWORD} | sed "s/^${USERNAME}:/${USERNAME}:LOCKED/;"`
  else
    MY_NEW_HTUSERS_ENTRY=`htpasswd -b -m -n ${USERNAME} ${MY_PASSWORD}`
  fi
  if [ -n "${MY_NEW_HTUSERS_ENTRY}" ] ; then
    webjob-cfg-set-kvps -d : -f ${HTUSERS_FILE} "${MY_NEW_HTUSERS_ENTRY}"
    if [ $? -ne 0 ] ; then
      echo "${PROGRAM}: Error='Unable to set password for ${USERNAME}.'" 1>&2
      MY_ERROR_FLAG="1"
    fi
  fi

  ####################################################################
  #
  # Create the user's tree.
  #
  ####################################################################

  MY_USER_DIR="${WEBJOB_USERS_DIR}/${USERNAME}"

  MY_UPLOADS_DIR="${MY_USER_DIR}/uploads"

  MY_DSV_DIR="${MY_USER_DIR}/dsv"

  MY_DIRS="
${MY_USER_DIR}
${MY_DSV_DIR}
${MY_UPLOADS_DIR}
"

  for MY_DIR in ${MY_DIRS} ; do
    if [ ! -d "${MY_DIR}" ] ; then
      echo "Creating ${MY_DIR}"
      mkdir -p ${MY_DIR}
      if [ $? -ne 0 ] ; then
        echo "${PROGRAM}: Error='Encountered a problem while creating ${MY_DIR}.'" 1>&2
        MY_ERROR_FLAG="1"
      fi
    else
      echo "Skipping ${MY_DIR} (already exists)"
    fi
  done

  ####################################################################
  #
  # Change the group so the WWW user can access the account's files.
  #
  ####################################################################

  chgrp -f -R ${WWW_GROUP} ${MY_USER_DIR}
  if [ $? -ne 0 ] ; then
    echo "${PROGRAM}: Error='Encountered a problem while setting group ownerships for ${USERNAME}.'" 1>&2
    MY_ERROR_FLAG="1"
  fi

######################################################################
#
# Shutdown and go home.
#
######################################################################

if [ ${MY_ERROR_FLAG} -eq 1 ] ; then
  echo "${PROGRAM}: Error='Encountered one or more errors. The account for ${USERNAME} is incomplete.'" 1>&2
  exit 3 # XER_PartialSuccess
else
  exit 0 # XER_OK
fi
