# This file may be sourced from a test script written in Bash or BATS.  It
# contains a number of utility functions, most of which have to do with:
#
#  1. implementing common tasks, often setup or configuration wrt iRODS; or
#  2. more primitive functions e.g. string manipulations or comparisons, etc.

SCRIPTDIR=${BASH_SOURCE[0]}
up_from_script_dir() {
  local x incr=""
  for ((x=0;x<${1:-0};x++)); do incr+="/.."; done
  realpath "$(dirname "$SCRIPTDIR")""$incr"
}

# Sample usages:
#   By user irods: set_up_ssl "" "-q"
#   By sudo enabled user: set_up_ssl "sudo" "-q"
set_up_ssl() {
  local SUDO=${1:-""}
  local OPTS=${2:-""}
  $SUDO su - irods  -c "python3 $(up_from_script_dir 1)/setup_ssl.py $OPTS"
}

# Clears out environment and resets to rodsadmin 'rods'.
# Meant mostly to allow initial steps by a rodsadmin for setting up tests.

iinit_as_rods() {
  rm -fr ~/.irods
  iinit <<<$(hostname)$'\n1247\nrods\ntempZone\nrods'
}

dot_to_space() {
  sed 's/\./ /g'<<<"$1"
}

CLEANUP=$':\n'

GT() { (return 1); echo $?; }
LT() { (return -1); echo $?; }
EQ() { (return 0); echo $?; }

compare_int_tuple() {
    local x=($1) y=($2)
    local lx=${#x[@]} ly=${#y[@]}
    local i maxlen=$((lx > ly ? lx : ly))
    for ((i=0;i<maxlen;i++)) {
        if [ $i -ge $lx ]; then return `LT`; fi
        if [ $i -ge $ly ]; then return `GT`; fi
        if [ ${x[$i]} -lt ${y[$i]} ]; then return `LT`; fi
        if [ ${x[$i]} -gt ${y[$i]} ]; then return `GT`; fi
    }
    return `EQ`
}

irods_version()
{
  local X=''
  [[ `ihelp -h|tail -1` =~ [0-9]+(\.[0-9]+)+ ]] && X=${BASH_REMATCH[0]}
  echo "$X"
}

pam_auth_string()
{
    compare_int_tuple "$(dot_to_space `irods_version`)" "$(dot_to_space 4.3)"
    if [ $? = `GT` ]; then
        echo "pam_password"
    else
        echo "pam"
    fi
}

get_auth_param () { iadmin get_grid_configuration authentication $1; }

with_change_auth_params_for_test()
{
  local restore_cmd=""

  # Use saved environment (must be rodsadmin).
  if [ -e ~/.irods.$$ ]; then
      export IRODS_ENVIRONMENT_FILE=~/.irods.$$/irods_environment.json
      export IRODS_AUTHENTICATION_FILE=~/.irods.$$/.irodsA
  fi

  while [ $# -ge 2 ]; do
    local reset_value=$(iadmin get_grid_configuration authentication $1)
    restore_cmd+=$'\n'"iadmin set_grid_configuration authentication $1 $reset_value"
    [ -n "$2" ] && iadmin set_grid_configuration authentication $1 $2
    shift 2
  done

  unset IRODS_ENVIRONMENT_FILE IRODS_AUTHENTICATION_FILE

  if [[ $SET_CLEANUP = [yY]* ]]; then
    CLEANUP+="$restore_cmd"
  fi
}

_begin_pam_environment_and_password() {
    # If the environment variable SKIP_IINIT_FOR_PASSWORD is set to a non-null length string, then
    # the step of generating the .irodsA containing the PAM authentication secret will be skipped.

    local username=${2:-alice}
    local ENV='{
    "irods_host": "localhost",
    "irods_zone_name": "tempZone",
    "irods_port": 1247,
    "irods_user_name": "'"$username"'",
    "irods_authentication_scheme": "'$(pam_auth_string)'",
    "irods_client_server_negotiation": "request_server_negotiation",
    "irods_client_server_policy": "CS_NEG_REQUIRE",
    "irods_ssl_ca_certificate_file": "/etc/irods/ssl/irods.crt",
    "irods_ssl_verify_server": "cert",
    "irods_encryption_key_size": 16,
    "irods_encryption_salt_size": 8,
    "irods_encryption_num_hash_rounds": 16,
    "irods_encryption_algorithm": "AES-256-CBC"
    }'
    rm -fr ~/.irods.$$
    mv ~/.irods ~/.irods.$$
    mkdir ~/.irods
    echo "$ENV" > ~/.irods/irods_environment.json

    if [ -n "$1" -a -z "$SKIP_IINIT_FOR_PASSWORD" ]; then
        iinit <<<"$1" 2>/tmp/iinit_as_alice.log
    fi
}

_end_pam_environment_and_password() {
    rm -fr ~/.irods
    mv ~/.irods.$$ ~/.irods
}

setup_pam_login_for_user() {
    local user=${2:-alice}
    sudo useradd $user --create-home
    local PASSWD=${1:-test123}
    sudo chpasswd <<<"$user:$PASSWD"
    iadmin mkuser $user rodsuser
    _begin_pam_environment_and_password "$PASSWD" $user
}

setup_pam_login_for_alice() {
    setup_pam_login_for_user "$1" alice
}

finalize_pam_login_for_user() {
    local USER=${1}
    _end_pam_environment_and_password
    iadmin rmuser "$USER"
    sudo userdel "$USER" --remove
}

finalize_pam_login_for_alice() {
    finalize_pam_login_for_user alice
}

test_specific_cleanup() {
  eval "$CLEANUP"
}

# PostgreSQL only
age_out_pam_password() {
  # sets create_ts and modify_ts (timestamps) to older values, decreasing them by an amount of (offset + 1) where offset
  # is the number of seconds for expiry_ts stored in the ICAT for the given user and password.  In this way, we can
  # artificially age out an existing pam password.
  # Parameters:
  #  $1 - The username
  #  $2 - (optional) override the amount used for offsetting the create & modify timestamps.
  local id=$(iquest %s "select USER_ID where USER_NAME = '$1'")
  local offset=$(sudo su - postgres -c "psql -t ICAT -c 'select pass_expiry_ts from r_user_password where user_id = $id'")
  local mtime=$(sudo su - postgres -c "psql -t ICAT -c 'select modify_ts from r_user_password where user_id = $id'")
  mtime=$(sed 's/^\s*0//' <<<"$mtime")
  [ -n "$2" ] && offset="$2"
  ((offset+=1))
  local new_time=$((mtime - offset))
  sudo su - postgres -c "psql ICAT -c 'update r_user_password set create_ts=$new_time, modify_ts=$new_time where user_id=$id'"
}

call_irodsctl() {
    local arg=${1:-restart}
    sudo su - irods -c "./irodsctl $arg"
}

add_irods_to_system_pam_configuration() {
    local tempfile=/tmp/irods-pam-config.$$
    cat <<-EOF >$tempfile
	auth        required      pam_env.so
	auth        sufficient    pam_unix.so
	auth        requisite     pam_succeed_if.so uid >= 500 quiet
	auth        required      pam_deny.so
	EOF
    sudo chown root.root $tempfile
    sudo mv $tempfile /etc/pam.d/irods
}

setup_preconnect_preference() {
    sudo su irods -c "sed -i.orig 's/\(^\s*acPreConnect.*CS_NEG\)\([A-Z_]*\)/\1_$1/' /etc/irods/core.re"
}

setup_pyN() {
  if [ ! -d /pyN ]; then
    mkdir /pyN ; chown testuser /pyN
    su - testuser -c "/root/python/bin/python3 -m virtualenv /pyN"
    cp -r /prc{,.rw}
    chown -R testuser /prc.rw
  fi
}

# Call to both initialize and activate a virtualenv with the requested Python3 interpreter
# installed.  Requires image to descend from 'bats-and-system-python'.
activate_virtual_env_with_prc_installed()
{
    local py_venv=${1:-pyN}
    # install python client.  We use a recursive copy of /prc so bdist doesn't try to build from a readonly mount
    [ "$py_venv" = pyN ] && sudo bash -c "$(declare -f setup_pyN); setup_pyN" && \
    sudo su - -c "source /${py_venv}/bin/activate && cp -rp /prc /prc-copy && \
                  pip install '/prc-copy[tests]' && sudo rm -fr /prc-copy" && \
    source /${py_venv}/bin/activate && \
    echo "---> Python virtual environment activated.  Interpreter Version is: $(python -V)" && \
    python -c 'import irods' || { echo >&2 "ERROR: python-irodsclient install failed."; false; }
}

# Produces an output of a precise timestamp with the file content appended,
# as an indicator of whether a file has been modified.
mtime_and_content()
{
    stat -c%y "$1"
    cat "$1"
}

irods_server_version() {
  python -c "import irods.helpers as h
import operator,sys
if len(sys.argv) == 1:
    (comparison,relto)=('','')
elif len(sys.argv) == 3:
    (comparison,relto)=sys.argv[1:3]
fm_tuple = lambda tup: '.'.join(str(_) for  _ in tup)
to_tuple = lambda vstr: tuple(int(_) for _ in vstr.split('.'))
svt = h.make_session().server_version_without_auth()
if relto:
  exit(0 if vars(operator)[comparison](svt,to_tuple(relto)) else 1)
print(fm_tuple(svt))
" $1 $2
}
