#!/bin/bash
set -u

#------------------------------------------------------------------------------
# Version ID Block. Relies on +k filetype modifier.
# VersionID='$Id: //p4-sdp/dev_c2s/Unsupported/Samples/triggers/SSO_default.sh#2 $ $Change: 31472 $'

#==============================================================================
# This Helix Core trigger script makes SSO the default.

# This is done by adding new users to the SSO group (as defined in the Helix
# Authentication Extension), and setting an unusable P4PASSWD.  This one
# trigger script is referenced twice in the Triggers table, once as a form-save
# trigger and once as a form-commit trigger.  The p4d server fires a form-save
# trigger after the form (in this case a user spec/form) has been validated
# as acceptable by the server, but before the form has been committed to the
# database. The form-commit trigger fires after a form has been committed
# to the database.

# Triggers table entries are of this form (both entries required):
#
# SSO_default form-save user "/p4/common/site/bin/triggers/SSO_default.sh %formfile% {ssogroupname|none}"
# SSO_default form-commit user "/p4/common/site/bin/triggers/SSO_default.sh %formfile% {ssogroupname|none}"
#
# Replace '{ssogroupname|none}' with the name of the group that identifers
# users as HAS users, e.g. 'SSO'. This applies if the HAS extensions is
# configured for an "opt in" model, where users must be in the SSO group to use
# HAS. Otherwise, if HAS is the default and the extension is configured with a
# group of users who do not use HAS, use the value 'none' instead.
#
# Sample Triggers table entries (both entries required) for "Opt in to SSO" model:
# SSO_default form-save user "/p4/common/site/bin/triggers/SSO_default.sh %formfile% SSO"
# SSO_default form-commit user "/p4/common/site/bin/triggers/SSO_default.sh %formfile% SSO"
#
# Sample Triggers table entries (both entries required) with a NonSSO group instead:
# SSO_default form-save user "/p4/common/site/bin/triggers/SSO_default.sh %formfile% none"
# SSO_default form-commit user "/p4/common/site/bin/triggers/SSO_default.sh %formfile% none"

# Workflow:
#
# The form-save trigger adds new users to the SSO group, and uses the 'p4 key'
# command to indicate they should have an unusable P4PASSWD set.  The
# form-commit trigger sets the unusable P4PASSWD.
#
# If "none" is specified as the second argument for the SSO group name, no group
# addition is done. This is to accommodate sites that default to HAS as opposed to 
# explicitly opting users in via SSO group membership.
#
# The form-save trigger fires when user spec form is about to be updated on
# the server.  If a spec form is saved for a new P4USER not yet known to p4d,
# add them to the SSO group, and then set a key named:
#
# SetUnusableP4PASSWD-<User>.
#
# It is possible to add a user name to a group even before the user account is
# created, so that is handled in the form-save call.
#
# The form-commit trigger fires after the form is committed to the p4d
# server. If the SetUnusableP4PASSWD-<User> key is set for the user (it having
# been set in the form-save trigger), run 'p4 passwd' to set an unusable UUID
# password.  In the form-commit trigger, the account exists in p4d so we can
# run the 'p4 passwd' command (which isn't possible in the form-save trigger
# as the user doesn't yet exist in p4d at that point).

#==============================================================================
# Declarations and Environment

# Version ID Block. Relies on +k filetype modifier.
#------------------------------------------------------------------------------
# shellcheck disable=SC2016
declare VersionID='$Id: //p4-sdp/dev_c2s/Unsupported/Samples/triggers/SSO_default.sh#2 $ $Change: 31472 $'
declare VersionStream=${VersionID#*//}; VersionStream=${VersionStream#*/}; VersionStream=${VersionStream%%/*};
declare VersionCL=${VersionID##*: }; VersionCL=${VersionCL%% *}
declare Version=${VersionStream}.${VersionCL}
[[ "$VersionStream" == r* ]] || Version="${Version^^}"

declare ThisScript=${0##*/}
declare ThisUser=
declare FormFile=${1:-UnsetFormFile}
declare Log="${LOGS:-/tmp}/${ThisScript%.sh}.log"
declare SSOGroup=${2:-UnsetGroupName}
declare Password=
declare PasswordFile=
declare GroupSpecFile=
declare User=
declare UserSetPasswordKey=
declare -i Debug=0
declare -i ErrorCount=0

#==============================================================================
# Local Functions
function msg () { echo -e "$*"; }
function errmsg () { msg "\\nError: ${1:-Unknown  Error}\\n"; ErrorCount+=1; }
function bail () { errmsg "${1:-Unknown Error}"; exit "${ErrorCount}"; }
function dbg () { [[ "$Debug" -eq 0 ]] || msg "DEBUG: $*"; }

#==============================================================================
# Main Program

# Capture all output to a log; display nothing, not even errors.
touch "$Log" || bail "Could not init log [$Log] for $ThisScript."
exec >>"$Log"
exec 2>&1

ThisUser=$(id -n -u)
msg "Started $ThisScript version $Version as $ThisUser@${HOSTNAME%%.*} on $(date)."

# Set umask so temp files are 600 perms (read/writable only by owner).
umask 177

[[ "$FormFile" == "UnsetFormFile" ]] && \
   bail "Bad Usage: Parameter 1 [FormFile] not passed in."
[[ -r "$FormFile" ]] ||\
   bail "Form file passed in does not exist."

[[ "$SSOGroup" == "UnsetGroupName" ]] && \
   bail "Bad Usage: Parameter 2 [SSOGroup] not passed in."

# Check that a User field exists, indicating the form file is likely valid.
if grep -q ^User: "$FormFile"; then
   User=$(grep ^User: "$FormFile"|awk '{print $2}')
   UserSetPasswordKey="SetUnusableP4PASSWD-$User"

   if p4 user --exists -o "$User" > /dev/null; then
      msg "User [$User] already exists; not adding to SSO."
      if [[ "$(p4 key "$UserSetPasswordKey")" == "YES" ]]; then
         msg "Key detected: $UserSetPasswordKey"
         PasswordFile=$(mktemp)
         Password=$(uuidgen)
         if echo -e "$Password\\n$Password" > "$PasswordFile"; then
            if p4 passwd "$User" < "$PasswordFile"; then
               msg "SSO user [$User] now has unusable P4PASSWD."
               if p4 key -d "$UserSetPasswordKey"; then
                  msg "Key cleared: $UserSetPasswordKey"
               else
                  errmsg "Failed to clear key: $UserSetPasswordKey"
               fi
            else
               errmsg "Failed to set UUID P4PASSWD for user [$User]."
            fi
         else
            errmsg "Failed to create temp password file for user [$User]."
         fi
         rm -f "$PasswordFile"
      else
         msg "UserSetPasswordKey not detected. Ignoring user [$User]."
      fi
   else
      if [[ "$SSOGroup" == "none" ]]; then
         if p4 key "$UserSetPasswordKey" YES; then
            msg "Key set so form-commit trigger sets unusable P4PASSWD for new SSO user [$User]."
         else
            errmsg "Failed to set key $UserSetPasswordKey."
         fi
      else
         GroupSpecFile=$(mktemp)
         if p4 group -o "$SSOGroup" | grep -v ^# | sed -e :a -e '/^\n*$/{$d;N;};/\n$/ba' > "$GroupSpecFile"; then
            if [[ -s "$GroupSpecFile" ]]; then
               if echo -e "\\t$User" >> "$GroupSpecFile"; then
                  if p4 -s group -i < "$GroupSpecFile"; then
                     msg "User [$User] added to SSO group [$SSOGroup]."
                     if p4 key "$UserSetPasswordKey" YES; then
                        msg "Key set so form-commit trigger sets unusable P4PASSWD for new SSO user [$User]."
                     else
                        errmsg "Failed to set key $UserSetPasswordKey."
                     fi
                  else
                     errmsg "Failed to load this spec file for group [$SSOGroup]:$(grep -v '^#' "$GroupSpecFile")"
                  fi
               else
                  errmsg "Could not add user [$User] to SSO Group [$SSOGroup]."
               fi
            else
               errmsg "Failed to generate a valid group spec file for group [$SSOGroup]."
            fi
         else
            errmsg "Could not generate group spec file for SSO group [$SSOGroup]."
         fi
         rm -f "$GroupSpecFile"
      fi
   fi
else
   msg "Form file [$FormFile] has no User field. Ignoring it."
fi

dbg "Normal exit."

exit 0
