#!/usr/bin/env bash

#
# ## user\_create\_if\_missing()
#
# Create a system user if it does not exist on the system.
#
# ### Input Parameters
#
# The first parameter should be the user name, then optionally
#   [with] group {{groupname}}
#
# ### Stream Outputs
#
# What gets printed to STDOUT and STDERROR. 'None.' if nothing is output.
#
# ### Environmental effects
#
# What, if any, environmental side effects this function causes. 'None.' for none.
#
# ### Return Codes
#
# 0 for success
#
# ### Failure Scenarios
#
# Fails if no username is given.
#
# ### Usage Examples
#
#     user$ user_create_if_missing "nginx" with group "nginx"
#
# ### Code Walkthrough
user_create_if_missing()
{
  # Declare the token, group and user variables.
  local _token _group _user
  # parse arguments
  while (( $# > 0 ))
  do
    # Set the token to the first parameter
    _token="$1"
    # Remove the token from the parameters list
    shift

    # Switched based on the value of the token
    case "${_token}" in
      # if the token is 'group' then set the group variable to the next prameter
      # and remove the next parameter from the parameters list
      group)
        _group="${1}"
        shift
        ;;
      # if the token is 'with' then move on to processing the next token
      # as this is syntactic sugar.
      with)
        continue
        ;;
      # if the token is 'from' then set the source variable to the next parameter
      # and remove the next parameter from the parameters list
      from)
        _source="${1}"
        shift
        ;;
      # if the token is '--force' then set the force_flag to 1
      --force)
        _force_flag=1
        ;;

      # if the token does not match any of the above then
      *)
        # if the user variable has not yet been set then assign the token into
        # the user variable
        if variable_is_empty _user
        then
          _user="${_token}"

        # otherwise if the group variable has not yet been set then assign
        # the token into the group variable
        elif variable_is_empty _group
        then
          _group="${_token}"
        fi
        ;;
    esac
  done

  if variable_is_empty _group
  then
    _group="${_user}"
  fi

  # If the user is root (eg. id 0)
  if (( UID == 0 ))
  then
    # then we switch based on the uname command ouptput for os type
    # ##### TODO: Use the os_is_X functions here instead.
    case "$(uname)" in
      "OpenBSD")
        groupadd "$_group"
        useradd -g "$_user" "$_user" -s /bin/bash
        usermod -s /bin/bash "$_user"
        ;;

      "FreeBSD")
        pw groupadd -q "$_user"
        ;;

      "Linux")
        # groupadd nginx
        # useradd -G nginx -M nginx
        if [[ -f "/etc/SuSE-release" ]] ; then
          groupadd "$_group"
        else
          groupadd -f "$_group"
        fi

        useradd -g "${_user}" "${_user}"
        usermod -d "${data_path%\/*}" "${_user}"
        usermod -s /bin/bash "${_user}"
      ;;

      "Darwin")
        current_max_gid=$(dscl . -list /Groups gid | awk '{print $2}' | sort -g -r | head -1)
        next_gid=$((current_max_gid+1))

        current_max_uid=$(dscl . -list /Users UniqueID | awk '{print $2}' | sort -g -r | head -1)
        next_uid=$((current_max_uid+1))

        if ! dscl . -list /Users | grep -q "^${_user}$" ; then
          dscl . -create "/Groups/$_group"
          dscl . -create "/Groups/$_group" gid "$next_gid"
          dscl . -create "/Users/${_user}"
          dscl . -create "/Users/${_user}" PrimaryGroupID "$gid" # UserShell /bin/bash
          dscl . -create "/Users/${_user}" uid "$next_uid"
          dscl . -create "/Users/${_user}" realname "Postgresql User"
          dscl . -create "/Users/${_user}" home "${data_path%\/*}"
          dscl . -create "/Users/${_user}" shell "/bin/bash"
          dscl . -create "/Users/${_user}" gid "$gid"
          dscl . -create "/Users/${_user}" passwd "*"
        fi
      ;;

      "SunOS")
        groupadd "$_group"
        useradd -g "$_user" -s /bin/bash -d "${data_path}" "$_user"
        usermod -s /bin/bash "$_user"
        ;;
    esac
  else
    fail "Users and groups may be manipulated only by the super user (root) via su or sudo."
  fi
}

#
# ## user\_delete()
#
# Deletes the named system user.
#
# ### Input Parameters
#
# The first parameter is the system user to delete.
#
# ### Stream Outputs
#
# None.
#
# ### Environmental effects
#
# The given user will no longer exist on the system.
#
# ### Return Codes
#
# 0 for success
#
# ### Failure Scenarios
#
# Fails if the user name was not specified.
#
# ### Usage Examples
#
#     user$ user_delete "nginx"
#
# ### Code Walkthrough
user_delete()
{
  # #### TODO: Finish documenting this function
  # Declare local variables for token and group
  local _token _user

  # While there are parameters, loop over them
  while (( $# > 0 ))
  do
    # Set the first parameter into the token variable and remove the token from
    # the parameters list now that it is in a variable.
    _token="$1"
    shift

    # Switch on the token variable's contents
    case "${_token}" in
      # if the token is 'user' then assign the next parameter to the user
      # variable and remove the next parameter from the parameters list
      user)
        _user="${1}"
        shift
        ;;
      # if the token is 'with' then go on to processing the next token as this
      # is syntactic sugar.
      with)
        continue
        ;;
      # if the token is 'from' then assign the next parameter to the source
      # variable and remove the next parameter from the parameters list
      from)
        _source="${1}"
        shift
        ;;
      # if the token is '--force' then then set the force_flag to 1
      # (non-default value)
      --force)
        _force_flag=1
        ;;
      # if the token does not match any of the above then
      *)
        # if the user variable is empty then assign the current token into the
        # user variable and stop processing input parameters
        if variable_is_empty _user
        then
          _user="${_token}"
          break
        fi
        ;;
    esac
  done

  # If the user is root (eg. id 0)
  if (( UID == 0 ))
  then
    (
    # Then we switch based on the os type
    # TODO: Switch these to use the os_is_X system module dsl
    case "$(uname)" in
      "Linux")
        userdel "${package_user}"
      ;;
      "OpenBSD")
        groupdel "$package_user"
        userdel -g "$package_user" "$package_user"
        ;;

      "FreeBSD")
        pw groupdel -q "$package_user"
        ;;

      "Darwin")
        gid="501" #only gids > 500 show up in user preferences

        #Find an open gid
        while true; do
          name=$(dscl . search /groups PrimaryGroupID $gid | cut -f1 -s)
          if [ -z "$name" ] ; then
            break
          fi
          gid=$[$gid +1]
        done

        #Create the group
        dscl . -delete "/Users/${package_user}" PrimaryGroupID "$gid" # UserShell /bin/bash
        dscl . -delete "/Groups/$package_user"
        dscl . -delete "/Groups/$package_user" gid "$gid"
      ;;

      "SunOS")
        groupdel "$package_user"
        userdel "$package_user"
        ;;
    esac
    ) || true # Ignore failures with this for now...
  else
    fail "Users and groups may be manipulated only by the super user (root) via su or sudo."
  fi
}

#
# ## user\_is\_root()
#
# Tests if the user the current process is running as is root.
#
# ### Input Parameters
#
# None.
#
# ### Stream Outputs
#
# What gets printed to STDOUT and STDERROR. 'None.' if nothing is output.
#
# ### Environmental effects
#
# None.
#
# ### Return Codes
#
# 0 if user is root (UID 0).
# 1 if user is not root (UID 0).
#
# ### Failure Scenarios
#
# No failure scenarios currently.
#
# ### Usage Examples
#
#     user$ user_is_root
#     user$ echo $?
#     1
#
#     root# user_is_root
#     root# echo $?
#     0
#
# ### Code Walkthrough
user_is_root()
{
  # If the user is root (id is 0) then return true, 0. otherwise return false, 1
  if (( UID == 0 ))
  then
    return 0
  else
    return 1
  fi
}

#
# ## user\_is\_not\_root()
#
# Tests if the user the current process is running as is not root.
#
# ### Input Parameters
#
# None.
#
# ### Stream Outputs
#
# None.
#
# ### Environmental effects
#
# None.
#
# ### Return Codes
#
# 0 if user is not root (eg UID is nonzero)
# 1 if user is root (UID 0)
#
# ### Failure Scenarios
#
# No failure scenarios currently
#
# ### Usage Examples
#
#     user$ user_is_not_root
#     user$ echo $?
#     0
#
#     root# user_is_not_root
#     root# echo $?
#     1
#
# ### Code Walkthrough
user_is_not_root()
{
  # If the user is not root (id > 0), return true, 0 otherwise return false, 1
  if (( UID != 0 ))
  then
    return 0
  else
    return 1
  fi
}

#
# ## user\_must\_be\_root()
#
# Halts the calling program with a failure message if the user is not root.
#
# ### Input Parameters
#
# None.
#
# ### Stream Outputs
#
# A failure message is output to STDERR if the failure condition is met.
#
# ### Environmental effects
#
# What, if any, environmental side effects this function causes. 'None.' for none.
#
# ### Return Codes
#
# 0 if the user is root
#
# ### Failure Scenarios
#
# Fails if the program user running is not root.
#
# ### Usage Examples
#
#     user$ user_must_be_root
#     ERROR: bdsm test test must be run as root and NOT as a user(user)
#
user_must_be_root()
{
  # if the user is root then return true, 0. otherwise send an error message
  # informing the user that the script must be run as a root and not as user
  if user_is_root
  then
    return 0
  else
    error "bdsm $extension $action must be run as a root and NOT as a user ($USER)."
  fi
}

#
# ## user\_must\_not\_be\_root()
#
# Halts the calling program with a failure message if the user is not root.
#
# ### Input Parameters
#
# None.
#
# ### Stream Outputs
#
# A failure message is output to STDERR if the failure condition is met.
#
# ### Environmental effects
#
# What, if any, environmental side effects this function causes. 'None.' for none.
#
# ### Return Codes
#
# 0 if the user is root
#
# ### Failure Scenarios
#
# Fails if the program user running is root.
#
# ### Usage Examples
#
#     user$ user_must_not_be_root
#     ERROR: bdsm test test must be run as a (project) user and NOT as a root.
#
user_must_not_be_root()
{
  # if the user is not root then return true, 0. otherwise send an error message
  # informing the user that the script must be run as a root and not as user.
  if user_is_not_root
  then
    return 0
  else
    fail "bdsm $extension $action must be run as a (project) user and NOT as root."
  fi
}

#
# ## user\_run\_as()
#
# Runs a command as a given user.
#
# ### Input Parameters
#
# None.
#
# ### Stream Outputs
#
# Any stream outputs of the command being run.
#
# ### Environmental effects
#
# Any environmental effects of the command being run.
#
# ### Return Codes
#
# 0 for success
#
# ### Failure Scenarios
#
# Fails if no user was specified.
#
# ### Usage Examples
#
#     root# user_run_as "user" pwd
#     /home/user
#
# ### Code Walkthrough
user_run_as()
{
  # Assign the first parameter into the user variable
  local _user="$1"

  # If the user variable is nonempty
  if variable_is_nonempty _user
  then
    # Remove the user from the parameters list.
    # Then run the given command as the user, with a nonlogin shell.
    # If the command completed successfully then return true, 0. otherwise
    # the command did not complete successfully so return false, 1
    shift
    if su "${_user}" -c "$*"
    then
      return 0
    else
      return 1
    fi
  else
    # otherwise the user was not passed in. This is a programming error so we
    # send a fail message which includes a backtrace allowing the developer
    # to quickly pinpoinnt and fix their error.
    fail "Cannot run as user as no user or commands were given."
  fi
}

#
# ## user\_run\_as\_login()
#
# Runs a command as a given user, in that users login shell.
#
# ### Input Parameters
#
# None.
#
# ### Stream Outputs
#
# Any stream outputs of the command being run.
#
# ### Environmental effects
#
# Any environmental effects of the command being run.
#
# ### Return Codes
#
# 0 for success
#
# ### Failure Scenarios
#
# Fails if no user was specified.
#
# ### Usage Examples
#
#     root# user_run_as_login "deploy" bdsm unicorn restart
#     << unicorn restart output >>
#
user_run_as_login()
{
  # Assign the first parameter into the user variable
  local _user="$1"

  # If the user variable is nonempty
  if variable_is_nonempty _user
  then
    # Then run the given command as the user, with a login shell.
    # If the command completed successfully so return true, 0. Otherwise the
    # command did not complete successfully so return false, 1
    if su - "${_user}" -c "$*"
    then
      return 0
    else
      return 1
    fi
  else
    # otherwise the user was not passed in. This is a programming error so we
    # send a fail message which includes a backtrace allowing the developer
    # to quickly pinpoinnt and fix their error.
    fail "Cannot run as user as no user or commands were given."
  fi
}
