#!/bin/bash
#------------------------------------------------------------------------------
set -u

# Usage:
# Set your Perforce context, login as a super user.
# cd /dir/where/this/is
# vi users_to_add.csv
# ./add_users.sh 2>&1 | tee add_users.$(date +'%Y%m%d-%H%M').log

# This script adds a bunch of users from a users_to_add.csv file of the form:
# <user>,<email>,<full_name>[,<group1 group2 group3 ...]

# This first line of the uses_to_add.csv file is assumed to be a header and
# is always ignored.

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

# Version ID Block. Relies on +k filetype modifier.
#------------------------------------------------------------------------------
# shellcheck disable=SC2016
declare VersionID='$Id: //p4-sdp/main/Unsupported/Maintenance/add_users.sh#1 $ $Change: 33433 $'
declare VersionStream=${VersionID#*//}; VersionStream=${VersionStream#*/}; VersionStream=${VersionStream%%/*};
declare VersionCL=${VersionID##*: }; VersionCL=${VersionCL%% *}
declare Version=${VersionStream}.${VersionCL}
[[ "$VersionStream" == r* ]] || Version="${Version^^}"

#==============================================================================
# Local Functions
#------------------------------------------------------------------------------
function msg () { echo -e "$*"; }
function warnmsg () { msg "\nWarning: ${1:-Unknown Warning}\n" >&2; }
function errmsg () { msg "\nError: ${1:-Unknown Error}\n" >&2; }
function bail () { errmsg "${1:-Unknown Error}"; exit ${2:-1}; }

#------------------------------------------------------------------------------
# add_user_to_group ($user, $group)
#
# Add one user to one group.
#------------------------------------------------------------------------------
function add_user_to_group () {
   declare user=${1:-Unset}
   declare group=${2:-Unset}
   [[ $user == Unset || $group == Unset ]] && return

   # If both Users and Groups fields are empty, the group does not
   # exist.
   if  [[ -z "$(p4 -ztag -F %Users0% group -o $group)" ]]; then
      if [[ -z "$(p4 -ztag -F %Owners0% group -o $group)" ]]; then
         warnmsg "Skipping add of user [$user] to unknown group [$group]."
      fi
   fi

   msg "Adding user [$user] to group [$group]."

   p4 group -o $group > $TmpFile2
   echo -e "\t$user" >> $TmpFile2
   p4 -s group -i < $TmpFile2
}

declare UserDataFile=users_to_add.csv
declare FullName=
declare Email=
declare User=
declare Group=
declare AuthMethod=
declare AccessGroups=

declare -i FirstLine=1
declare -i InternalUser
declare -i ExistingUser
declare -i SkippedUserCount=0
declare -i NewUserCount=0
declare -i UserCount=0
declare -i UserLimit=0
declare -i LicensesAvailable=0
declare -i LicensesNeeded=0
declare PasswordFile=$(mktemp)
declare TmpFile=$(mktemp)
declare TmpFile2=$(mktemp)
H1="\n=============================================================================="
H2="\n------------------------------------------------------------------------------"

msg "Started ${0##*/} version $Version at $(date)."

touch $PasswordFile
chmod 600 $PasswordFile
echo -e "Welcome1\nWelcome1" > $PasswordFile

[[ -r $UserDataFile ]] || bail "Missing or unreadable user data file: $UserDataFile."

msg "${H1}\nPreflight check."

UserCount=$(p4 -ztag -F %userCount% license -u)
UserLimit=$(p4 -ztag -F %userLimit% license -u)
[[ -z "$UserCount" || -z "$UserLimit" ]] && bail "Could not determine license info."

LicensesAvailable=$((UserLimit - UserCount))

while read userData; do
   # Skip the first line (assumed to be a header).
   if [[ $FirstLine -eq 1 ]]; then
      FirstLine=0
      continue
   fi

   # Skip blank lines and comments.
   [[ -z "$(echo $userData)" ]] && continue
   [[ "$userData" == "#"* ]] && continue

   User=$(echo $userData|cut -d ',' -f 1)
   Email=$(echo $userData|cut -d ',' -f 2)
   FullName=$(echo $userData|cut -d ',' -f 3)
   AccessGroups=$(echo $userData|cut -d ',' -f 4)

   if [[ -n "$(p4 -ztag -F %Access% user -o $User)" ]]; then
      ExistingUser=1
   else
      ExistingUser=0
   fi

   if [[ $ExistingUser -eq 1 ]]; then
      msg "Skipping creation of existing user [$User]."
      SkippedUserCount=$((SkippedUserCount+1))
   else
      NewUserCount=$((NewUserCount+1))
   fi
done <  $UserDataFile

msg "\nPreflight Summary:
\tNew Users to Create:   $NewUserCount
\tExisting uses to skip: $SkippedUserCount
\tCurrent User Count:    $UserCount
\tLicensed User Limit:   $UserLimit
\tLicenses Available:    $LicensesAvailable\n\n"

if [[ "$NewUserCount" -le "$LicensesAvailable" ]]; then
   msg "Verified: Enough seats are available to create new users."
else
   LicensesNeeded=$((NewUserCount - LicensesAvailable))
   bail "There are not enough licenses available! Contact sales@perforce.com and order at least $LicensesNeeded more licenses, or else remove $LicensesNeeded or more inactive users.\n"
fi

FirstLine=1
SkippedUserCount=0
NewUserCount=0

msg "${H1}\nAdding $NewUserCount users."

while read userData; do
   # Skip the first line (assumed to be a header).
   if [[ $FirstLine -eq 1 ]]; then
      FirstLine=0
      continue
   fi

   # Skip blank lines and comments.
   [[ -z "$(echo $userData)" ]] && continue
   [[ "$userData" == "#"* ]] && continue

   User=$(echo $userData|cut -d ',' -f 1)
   Email=$(echo $userData|cut -d ',' -f 2)
   FullName=$(echo $userData|cut -d ',' -f 3)
   AccessGroups=$(echo $userData|cut -d ',' -f 4)

   if [[ -n "$(p4 -ztag -F %Access% user -o $User)" ]]; then
      ExistingUser=1
   else
      ExistingUser=0
   fi

   if [[ $ExistingUser -eq 1 ]]; then
      msg "Skipping creation of existing user [$User]."
      SkippedUserCount=$((SkippedUserCount+1))
   else
      NewUserCount=$((NewUserCount+1))
      echo -e "User: $User\n\nEmail: $Email\n\nFullName: $FullName\n\n" > $TmpFile
      msg "Creating user $User"
      p4 -s user -f -i < $TmpFile ||\
         bail "Could not create user $User with this spec:\n$(cat $TmpFile)\n"

      AuthMethod=$(p4 -ztag -F %AuthMethod% user -o $User)
      if [[ "$AuthMethod" == perforce ]]; then
         msg "Setting Password for user $User."
         p4 -s passwd $User < $PasswordFile ||\
            bail "Failed to set password for user $User."

         msg "Doing 'p4 admin resetpassword -u $User' to require password reset."
         p4 admin resetpassword -u $User
      elif [[ -z "$AuthMethod" ]]; then
         bail "Could not determine AuthMethod for user $User."
      else
         msg "AuthMethod is $AuthMethod for user $User; not setting password."
      fi

      if [[ -n "$AccessGroups" ]]; then
         for Group in $AccessGroups; do
            add_user_to_group "$User" "$Group"
         done
      fi
   fi
done <  $UserDataFile

rm -f $PasswordFile

msg "\nAccount Creation Summary:\n\tCreated $NewUserCount new users.\n\tSkipped $SkippedUserCount existing users.\n"
