#!/bin/bash

cvmfs_test_name="Test CVMFS over HTTPS with VOMS authentication"
cvmfs_test_autofs_on_startup=false
cvmfs_test_suites="quick"


# TODO(rmeusel): make this multi-platform
TEST600_HTTPS_CONFIG=/etc/httpd/conf.d/test600.cvmfs.secure.conf
# Set later to the scratch dir
TEST600_HOSTCERT=
TEST600_HOSTKEY=
TEST600_VOMSDIR=
TEST600_TESTCA=
TEST600_TESTCRL=
TEST600_TESTSIGNING=
# These three variables will be updated later with the generated hash
TEST600_TESTCA_HASH=
TEST600_TESTCRL_HASH=
TEST600_TESTSIGNING_HASH=
# replacing /etc/grid-security
TEST600_GRID_SECURITY_DIR=
# Mountpoint and cache directory for grid.cern.ch private mount
TEST600_GRID_MOUNTPOINT=
TEST600_GRID_CACHE=
# For generate_certs
TEST600_CERTS_DIR=
# Bind mount to /etc/grid-security active
TEST600_GRID_SECURITY_TAINTED=
# local mount for testing
TEST600_PRIVATE_MOUNT=
TEST600_HOSTNAME=
TEST600_SELINUX_DISABLED=
TEST600_RESOLV_CONF_DISABLED=

disable_resolv_conf() {
  if [ -f /etc/resolv.conf ]; then
    TEST600_RESOLV_CONF_DISABLED="/etc/resolv.conf.disabled.cvmfstest600"
    sudo mv /etc/resolv.conf ${TEST600_RESOLV_CONF_DISABLED}
  fi
}

enable_resolv_conf() {
  if [ -f "$TEST600_RESOLV_CONF_DISABLED" ]; then
    sudo mv ${TEST600_RESOLV_CONF_DISABLED} /etc/resolv.conf
    TEST600_RESOLV_CONF_DISABLED=
  fi
}

cleanup() {
  echo "running cleanup()..."
  enable_resolv_conf
  sudo umount ${TEST600_GRID_MOUNTPOINT}
  [ "x$TEST600_GRID_SECURITY_TAINTED" = "xyes" ] && sudo umount /etc/grid-security
  [ -z "$TEST600_HTTPS_CONFIG"                 ] || sudo rm -f $TEST600_HTTPS_CONFIG
  [ -z "$TEST600_HOSTCERT"                     ] || sudo rm -f $TEST600_HOSTCERT
  [ -z "$TEST600_HOSTKEY"                      ] || sudo rm -f $TEST600_HOSTKEY
  [ -z "$TEST600_VOMSDIR"                      ] || sudo rm -fR $TEST600_VOMSDIR
  [ -z "$TEST600_TESTCA"                       ] || sudo rm -f $TEST600_TESTCA
  [ -z "$TEST600_TESTCRL"                      ] || sudo rm -f $TEST600_TESTCRL
  [ -z "$TEST600_TESTSIGNING"                  ] || sudo rm -f $TEST600_TESTSIGNING
  [ -z "$TEST600_TESTCA_HASH"                  ] || sudo rm -f $TEST600_TESTCA_HASH
  [ -z "$TEST600_TESTCRL_HASH"                 ] || sudo rm -f $TEST600_TESTCRL_HASH
  [ -z "$TEST600_TESTSIGNING_HASH"             ] || sudo rm -f $TEST600_TESTSIGNING_HASH
  [ -z "$TEST600_HOSTNAME"                     ] || sudo sed -i -e "/$TEST600_HOSTNAME/d" /etc/hosts
  if [ ! -z "$TEST600_PRIVATE_MOUNT" ]; then
    sudo umount $TEST600_PRIVATE_MOUNT
    sudo rmdir $TEST600_PRIVATE_MOUNT
  fi
  [ -z "$TEST600_SELINUX_DISABLED"             ] || sudo setenforce 1
}

mount_cvmfs_grid() {
  local mountpoint=$1
  local cache_dir=$2

  mkdir -p $mountpoint $cache_dir

  echo "writing config in ${cache_dir}/client.conf"
  cat << EOF > ${cache_dir}/client.conf
CVMFS_CACHE_BASE=$cache_dir
CVMFS_RELOAD_SOCKETS=$cache_dir
CVMFS_CLAIM_OWNERSHIP=yes
CVMFS_SERVER_URL=http://cvmfs-stratum-one.cern.ch/cvmfs/grid.cern.ch
CVMFS_HTTP_PROXY="${CVMFS_TEST_PROXY}"
CVMFS_KEYS_DIR=/etc/cvmfs/keys/cern.ch
EOF
  cat ${cache_dir}/client.conf

  cvmfs2 -o config=${cache_dir}/client.conf grid.cern.ch $mountpoint
  return $?
}

generate_certs() {
  local script_location=$1
  mkdir -p $TEST600_CERTS_DIR $TEST600_GRID_CACHE $TEST600_GRID_MOUNTPOINT
  local voms_path=

  if is_el5; then
    voms_path="${TEST600_GRID_MOUNTPOINT}/emi-ui-2.10.4-1_sl5v1"
  elif is_el6; then
    voms_path="${TEST600_GRID_MOUNTPOINT}/emi-ui-2.10.4-1_sl5v1"
    # TODO(rmeusel): use that once the world stopped burning...
    #                removes a couple of compat* depedencies
    # voms_path="${TEST600_GRID_MOUNTPOINT}/emi-ui-3.17.1-1_sl6v1"
  else
    echo "didn't find voms path for this platform"
    return 10
  fi

  echo "using VOMS path: '$voms_path'"
  cp $script_location/ca-generate-certs $TEST600_CERTS_DIR/
  cp $script_location/openssl-usercert-extensions.conf $TEST600_CERTS_DIR/
  cp $script_location/openssl-cert-extensions-template.conf $TEST600_CERTS_DIR/
  cp $script_location/openssl.config $TEST600_CERTS_DIR/

  echo "generating vomsproxy.pem"
  pushdir $TEST600_CERTS_DIR
  ./ca-generate-certs ${TEST600_HOSTNAME}
  result=$?
  LD_LIBRARY_PATH=${voms_path}/usr/lib64 \
    PATH=$PATH:${voms_path}/usr/bin/ \
     voms-proxy-fake -certdir ${TEST600_GRID_SECURITY_DIR}/certificates \
      -cert usercert.pem -key userkey.pem -out vomsproxy.pem -rfc \
      -hostcert hostcert.pem -hostkey hostkey.pem -fqan /cvmfs/Role=NULL \
      -voms cvmfs -uri ${TEST600_HOSTNAME}:15000 || return 1

  LD_LIBRARY_PATH=${voms_path}/usr/lib64 \
    PATH=$PATH:${voms_path}/usr/bin/ \
     voms-proxy-fake -certdir ${TEST600_GRID_SECURITY_DIR}/certificates \
      -cert usercert.pem -key userkey.pem -out vomsproxy2.pem -rfc \
      -hostcert hostcert.pem -hostkey hostkey.pem -fqan /cvmfs/Role=NULL \
      -voms cvmfs -uri ${TEST600_HOSTNAME}:15000 || return 1

  popdir

  mkdir -p ${TEST600_GRID_SECURITY_DIR}/vomsdir/cvmfs

  set -x
  echo "/DC=org/DC=Open Science Grid/O=OSG Test/OU=Services/CN=${TEST600_HOSTNAME}" > ${TEST600_GRID_SECURITY_DIR}/vomsdir/cvmfs/${TEST600_HOSTNAME}.lsc
  echo "/DC=org/DC=Open Science Grid/O=OSG Test/CN=OSG Test CA" >> ${TEST600_GRID_SECURITY_DIR}/vomsdir/cvmfs/${TEST600_HOSTNAME}.lsc

  LD_LIBRARY_PATH=${voms_path}/usr/lib64 X509_CERT_DIR=${TEST600_GRID_SECURITY_DIR}/certificates \
    PATH=$PATH:${voms_path}/usr/bin/ \
    voms-proxy-info -file $TEST600_CERTS_DIR/vomsproxy.pem -all

  sudo cp $TEST600_CERTS_DIR/hostcert.pem $TEST600_HOSTCERT || return 1
  sudo cp $TEST600_CERTS_DIR/hostkey.pem $TEST600_HOSTKEY || return 1
  sudo cp $TEST600_CERTS_DIR/OSG-Test-CA.pem $TEST600_TESTCA || return 1
  sudo cp $TEST600_CERTS_DIR/OSG-Test-CA.r0 $TEST600_TESTCRL || return 1
  sudo cp $TEST600_CERTS_DIR/OSG-Test-CA.signing_policy $TEST600_TESTSIGNING || return 1
  local hash=`openssl x509 -in $TEST600_TESTCA -noout -hash`
  TEST600_TESTCA_HASH=${TEST600_GRID_SECURITY_DIR}/certificates/${hash}.0
  TEST600_TESTCRL_HASH=${TEST600_GRID_SECURITY_DIR}/certificates/${hash}.r0
  TEST600_TESTSIGNING_HASH=${TEST600_GRID_SECURITY_DIR}/certificates/${hash}.signing_policy
  sudo ln -sf $TEST600_TESTCA $TEST600_TESTCA_HASH || return 1
  sudo ln -sf $TEST600_TESTCRL $TEST600_TESTCRL_HASH || return 1
  sudo ln -sf $TEST600_TESTSIGNING $TEST600_TESTSIGNING_HASH || return 1
  set +x

  return $result;
}

cvmfs_run_test() {
  local logfile=$1
  local script_location=$2
  local scratch_dir=$(pwd)

  if [ ! -z $CVMFS_TEST_S3_CONFIG ]; then
    echo "This is not yet implemented for S3"
    return 1
  fi

  local TEST600_GRID_SECURITY_DIR=${scratch_dir}/grid-security
  mkdir -p ${TEST600_GRID_SECURITY_DIR}/certificates ${scratch_dir}/vomsdir
  TEST600_HOSTCERT=${TEST600_GRID_SECURITY_DIR}/hostcert-cvmfs.pem
  TEST600_HOSTKEY=${TEST600_GRID_SECURITY_DIR}/hostkey-cvmfs.pem
  TEST600_VOMSDIR=${TEST600_GRID_SECURITY_DIR}/vomsdir/cvmfs
  TEST600_TESTCA=${TEST600_GRID_SECURITY_DIR}/certificates/OSG-Test-CA.pem
  TEST600_TESTCRL=${TEST600_GRID_SECURITY_DIR}/certificates/OSG-Test-CA.r0
  TEST600_TESTSIGNING=${TEST600_GRID_SECURITY_DIR}/certificates/OSG-Test-CA.signing_policy
  # These three variables will be updated later with the generated hash
  TEST600_TESTCA_HASH=${TEST600_GRID_SECURITY_DIR}/certificates/OSG-Test-CA.pem
  TEST600_TESTCRL_HASH=${TEST600_GRID_SECURITY_DIR}/certificates/OSG-Test-CA.r0
  TEST600_TESTSIGNING_HASH=${TEST600_GRID_SECURITY_DIR}/certificates/OSG-Test-CA.signing_policy

  TEST600_CERTS_DIR=$(pwd)/certs
  TEST600_GRID_MOUNTPOINT=$(pwd)/cvmfs-grid/mountpoint
  TEST600_GRID_CACHE=$(pwd)/cvmfs-grid/cache

  # Generate repo first - check will fail after apache change below until we
  # change the default URL.
  if [ -f "$TEST600_HTTPS_CONFIG" ]; then
    sudo rm -f $TEST600_HTTPS_CONFIG
    echo "restarting apache"
    apache_switch off
    apache_switch on
  fi

  echo "set a trap for system directory cleanup"
  trap cleanup EXIT HUP INT TERM

  echo "setting a dummy host name"
  TEST600_HOSTNAME='test-600-dummyhost'
  echo "127.0.0.1 $TEST600_HOSTNAME" | sudo tee --append /etc/hosts
  echo "==== new /etc/hosts: ===="
  cat /etc/hosts

  if has_selinux; then
    echo "make sure that SELinux does not interfere"
    TEST600_SELINUX_DISABLED=1
    sudo setenforce 0 || return 1
  fi

  # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

  echo "create VOMS certificate for $CVMFS_TEST_USER"

  echo "mount grid.cern.ch"
  mount_cvmfs_grid $TEST600_GRID_MOUNTPOINT $TEST600_GRID_CACHE
  ls $TEST600_GRID_MOUNTPOINT

  echo "Generating certificates"
  local vomsproxy=$(pwd)/certs/vomsproxy.pem
  generate_certs $script_location || return 4
  cat $vomsproxy                  || return 5

  echo "use vomsproxy certificate"
  export X509_USER_PROXY=$vomsproxy

  echo "make '$TEST600_GRID_SECURITY_DIR' available as /etc/grid-security"
  sudo mkdir -p /etc/grid-security                                || return 20
  sudo mount --bind $TEST600_GRID_SECURITY_DIR /etc/grid-security || return 21
  TEST600_GRID_SECURITY_TAINTED="yes"

  # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

  echo "create a fresh repository named $CVMFS_TEST_REPO with user $CVMFS_TEST_USER"
  create_empty_repo $CVMFS_TEST_REPO $CVMFS_TEST_USER /tmp/cvmfs.secure.log -V "x509%cvmfs:/cvmfs" || return $?
  echo "Make sure we don't lock out ourselves when publishing"
  echo "CVMFS_AUTHZ_HELPER=/usr/libexec/cvmfs/authz/cvmfs_allow_helper" | \
    sudo tee -a /etc/cvmfs/repositories.d/$CVMFS_TEST_REPO/client.conf
  cvmfs_suid_helper rw_umount $CVMFS_TEST_REPO
  cvmfs_suid_helper rdonly_umount $CVMFS_TEST_REPO
  cvmfs_suid_helper rdonly_mount $CVMFS_TEST_REPO
  cvmfs_suid_helper rw_mount $CVMFS_TEST_REPO

  echo "gather information about the just created repo"
  load_repo_config $CVMFS_TEST_REPO
  local spool_dir="$CVMFS_SPOOL_DIR"
  local cache_base="$CVMFS_CACHE_BASE"

  echo "put some stuff into $CVMFS_TEST_REPO"
  start_transaction $CVMFS_TEST_REPO                           || return $?
  echo "Hello World" > /cvmfs/$CVMFS_TEST_REPO/hello_world     || return 3
  echo "Hello World v2" > /cvmfs/$CVMFS_TEST_REPO/hello_world2 || return 3
  publish_repo $CVMFS_TEST_REPO -v                             || return $?

  # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

  # requires mod_gridsite from the gridsite package
  echo "Setting up apache configuration"
  sudo cp $script_location/test600.cvmfs.secure.conf $TEST600_HTTPS_CONFIG             || return 60
  sudo sed -i "s/@REPONAME@/$CVMFS_TEST_REPO/" $TEST600_HTTPS_CONFIG                   || return 61
  sudo sed -i "s,@GRIDSECURITYDIR@,$TEST600_GRID_SECURITY_DIR," $TEST600_HTTPS_CONFIG  || return 62
  cp $script_location/gacl $(get_local_repo_storage $CVMFS_TEST_REPO)/data/.gacl       || return 63
  cp $script_location/gacl-39 $(get_local_repo_storage $CVMFS_TEST_REPO)/data/39/.gacl || return 64

  echo "restarting apache"
  apache_switch off
  apache_switch on

  # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

  local mntpnt="${scratch_dir}/secure_mnt"
  TEST600_PRIVATE_MOUNT="$mntpnt"
  # VMware Fusion's DNS server times out on ${TEST600_HOSTNAME}
  disable_resolv_conf
  do_local_mount_as_root "$mntpnt"          \
                         "$CVMFS_TEST_REPO" \
                         "https://${TEST600_HOSTNAME}:8443/cvmfs/$CVMFS_TEST_REPO" || return 19
  enable_resolv_conf

  echo "copy vomsproxy for 'nobody'"
  local nobody_vomsproxy="$(pwd)/certs/vomsproxy.pem.nobody"
  sudo cp $(pwd)/certs/vomsproxy.pem $nobody_vomsproxy || return 20
  sudo chown nobody:            $nobody_vomsproxy || return 21

  echo "This should work"
  sudo -u nobody /bin/sh -c "export X509_USER_PROXY=$nobody_vomsproxy; cat ${mntpnt}/hello_world" > /dev/null || return 30

  echo "This should work because the above and below are in the same session id."
  sudo -u nobody /bin/sh -c "cat ${mntpnt}/hello_world" || return 31

  echo "This should not work"
  sudo -u nobody setsid /bin/sh -c "env && cat ${mntpnt}/hello_world" && return 32

  # Put in incorrect key into proxy file.
  echo "make invalid vomsproxy for 'nobody'"
  nobody_vomsproxy="$(pwd)/certs/vomsproxy2.pem.nobody"
  openssl x509 -in "$(pwd)/certs/vomsproxy2.pem" > $nobody_vomsproxy
  openssl rsa -in "$(pwd)/certs/vomsproxy.pem" >> $nobody_vomsproxy
  cat "$(pwd)/certs/usercert.pem" >> $nobody_vomsproxy
  chmod 600 $nobody_vomsproxy
  cat $nobody_vomsproxy
  sudo chown nobody:             $nobody_vomsproxy || return 21
  echo "This should not work"
  sudo -u nobody setsid /bin/sh -c "export X509_USER_PROXY=$nobody_vomsproxy; cat ${mntpnt}/hello_world" && return 33

  # Take the second proxy and remove the private key - should be invalid.
  echo "make second invalid vomsproxy for 'nobody'"
  nobody_vomsproxy="$(pwd)/certs/vomsproxy3.pem.nobody"
  openssl x509 -in "$(pwd)/certs/vomsproxy.pem" > $nobody_vomsproxy
  cat "$(pwd)/certs/usercert.pem" >> $nobody_vomsproxy
  chmod 600 $nobody_vomsproxy
  sudo chown nobody:             $nobody_vomsproxy || return 21
  cat $nobody_vomsproxy
  echo "This should not work"
  sudo -u nobody setsid /bin/sh -c "export X509_USER_PROXY=$nobody_vomsproxy; cat ${mntpnt}/hello_world" && return 33

  # Make sure our second VOMS proxy was valid.
  echo "This should work"
  nobody_vomsproxy="$(pwd)/certs/vomsproxy3.pem.nobody"
  sudo cp $(pwd)/certs/vomsproxy2.pem $nobody_vomsproxy || return 20
  sudo chown nobody:             $nobody_vomsproxy || return 21
  sudo -u nobody /bin/sh -c "export X509_USER_PROXY=$nobody_vomsproxy; cat ${mntpnt}/hello_world" > /dev/null || return 34

  # Try to access a file from the server using a PKCS8-formatted private key
  # Note this is a "raw certificate" - no VOMS extension.
  echo "This should work"
  nobody_vomsproxy="$(pwd)/certs/proxy.pem.nobody"
  cat "$(pwd)/certs/usercert.pem" > $nobody_vomsproxy
  openssl pkcs8 -topk8 -inform PEM -in /tmp/cvmfs-test/workdir/600-securecvmfs/certs/userkey.pem -nocrypt >> $nobody_vomsproxy
  chmod 600 $nobody_vomsproxy
  cat $nobody_vomsproxy
  sudo chown nobody:                $nobody_vomsproxy || return 21
  sudo -u nobody /bin/sh -c "export X509_USER_PROXY=$nobody_vomsproxy; cat ${mntpnt}/hello_world2" > /dev/null || return 35

  echo "Check authz attr"
  local authz_attr=
  authz_attr="$(attr -qg authz $mntpnt)" || return 41
  [ x"$authz_attr" = x"x509%cvmfs:/cvmfs" ]   || return 42

  echo "*** Empty disk cache"
  sudo cvmfs_talk -p "${mntpnt}c/${CVMFS_TEST_REPO}/cvmfs_io.${CVMFS_TEST_REPO}" \
    cleanup 0 || return 51

  echo "*** Enforce fail-over for following request"
  sudo cvmfs_talk -p "${mntpnt}c/${CVMFS_TEST_REPO}/cvmfs_io.${CVMFS_TEST_REPO}" \
    host set "https://${TEST600_HOSTNAME}:8443/notavail;https://${TEST600_HOSTNAME}:8443/cvmfs/$CVMFS_TEST_REPO"  || return 52
  sudo cvmfs_talk -p "${mntpnt}c/${CVMFS_TEST_REPO}/cvmfs_io.${CVMFS_TEST_REPO}" host info
  sudo -u nobody /bin/sh -c \
    "export X509_USER_PROXY=$nobody_vomsproxy; cat ${mntpnt}/hello_world2" || return 53
  sudo cvmfs_talk -p "${mntpnt}c/${CVMFS_TEST_REPO}/cvmfs_io.${CVMFS_TEST_REPO}" host info

  return 0
}
