#!/bin/bash

#
# name:		motp-manager
# version:	0.3
# author:	zooz <jablonskis@gmail.com>
# license:	GPLv3
# description:	Moble OTP pam module (http://motp.sourceforge.net)
#		users manipulation program. Make sure you specify
#		where your motp.conf and cache directory is below.
#

# settings
mconf="/etc/security/motp.conf"
cachedir="/var/cache/motp"

# disable colors with egrep
egrep="/bin/egrep --color=never"


# turn on shell options
shopt -s extglob

# set func to default
all_list=0
single_list=0
user_del=0
clear_cache=0
user_add=0

# capture a total number of positional parameters
total_parms="$#"

# check if executed by root
if [ $UID -ne 0 ]
 then
	printf "%s\n" "You must be root."
	exit 1
fi

# check if motp.conf and chachedir exists
if [[ ! -w $mconf ]]
 then
	echo "Error: motp.conf is missing."
	exit 1
 elif [[ ! -d $cachedir ]]
  then
	echo "Error: cache directory is missing."
	exit 1
fi

# usage function
usage() {
 printf "\n%s\n" "Usage: $(basename $0) <options> <username> | [ -s|-p|-o ]"
 printf " %s\n" "-h show this message"
 printf " %s\n" "-l show all users"
 printf " %s\n" "-k <username> - show single user"
 printf " %s\n" "-a <username> - add a new user, requires -s <secret> -p <pin> -o <GMT offset>"
 printf " %s\n" "-m <username> - modify user, requires -s or -p or -o"
 printf " %s\n" "-r <username> - remove user"
 printf " %s\n" "-c <username> - clear user's cache"
 printf " %s\n" "-s <secret> - secret (option used together with -a|-m)"
 printf " %s\n" "-p <pin> - PIN"
 printf " %s\n" "-o <offset> - GMT offset"
 printf "\n"
}

# output header
header() {
 printf "%-15s%-35s%-10s%s\n" "Username" "Secret" "Pin" "GMT Offset"
 printf "%-15s%-35s%-10s%s\n" "--------" "------" "---" "----------"
}

# all users list function
all_list() {
 header
 while read -ra list_array
  do
	printf "%-15s%-35s%-10s%s\n" "${list_array[0]}" "${list_array[1]}" "${list_array[2]}" "${list_array[3]}"
 done < <($egrep -v '(^#|^$)' $mconf)
}

# show single user function
single_list() {
 userm="$($egrep -o "^$username[[:space:]]" $mconf | awk '{print $1}')"
 user_line="$($egrep "^$username[[:space:]]" $mconf)"
 if [[ $userm ]] && [[ $username == $userm ]]
  then
	header
	read -ra n <<< "$user_line"
	printf "%-15s%-35s%-10s%s\n" "${n[0]}" "${n[1]}" "${n[2]}" "${n[3]}"
  else
	echo "User '$username' does not exist."
	exit 1
 fi
}

# cache clear function
clear_cache() {
 userm=$($egrep "^$username[[:space:]]" $mconf | awk '{print $1}')
 if [ "$username" == "$userm" ]
  then
	rm -rf "$cachedir/$username" 2>&1 > /dev/null
  else
	echo "User '$username' does not exist."
	exit 1
 fi
}

# delete user
user_del() {
 userm=$($egrep "^$username[[:space:]]" $mconf | awk '{print $1}')
 if [ "$username" == "$userm" ]
  then
	# clear cache
	clear_cache
	sed -r -i -e "/^$username[[:space:]].*/d" $mconf
	echo "User '$username' successfully deleted."
  else
	echo "User '$username' does not exist."
	exit 1
 fi
}

# user add function
user_add() {
 # offset defaults to 0 if not specified
 offset=${offset:-0}
 userm=$($egrep ^$username[[:space:]] $mconf | awk '{print $1}')
 if [ "$username" == "$userm" ]
  then
	echo "User '$username' already exists."
	exit 1
  else
	# validate input (user parameters)
	if [[ -z $secret || -z $pin ]]
	 then
		echo "Please specify secret, pin and offset."
		echo "Check '$(basename $0) -h' for more details."
		exit 1
	fi

	# secret validation
	if [[ ${#secret} -gt 32 || ${#secret} -lt 16 ]]
	 then
		echo "Please specify secret between 16 and 32 characters."
	 elif [[ $secret != +([0-9abcdffABCDEF]) ]]
	  then
		echo "Specified secret is invalid HEX."
	
	 # read and validate pin
	 elif [ "${#pin}" -ne 4 ]
	  then
		echo "PIN must be 4 digits."
	 elif [[ $pin != +([0-9]) ]]
	  then
		echo "PIN must contain only digits"
				
	 # read and validate offset (default is 0)
	 elif [[ $offset != *(*([-+])[0-9]*([:])) ]]
	  then
		echo "Invalid GMT Offset."
	 else
		printf "%-15s%-35s%-10s%s\n" "$username" "$secret" "$pin" "$offset" >> $mconf
		echo -e "'$username' has been successfully added:\n"
		header
		printf "%-15s%-35s%-10s%s\n" "$username" "$secret" "$pin" "$offset"
	fi
 fi
}

# user modification function
user_mod() {
 userm=$($egrep ^$username[[:space:]] $mconf | awk '{print $1}')
 if [ "$username" == "$userm" ]
  then

	# validate options
	if [[ $total_parms -le 2 ]]
	 then
		echo "Please speficy what needs to be modified."
		echo "Check '$(basename $0) -h' for more details."
		exit 1
	fi

	# secret validation
	if [[ -n $secret ]]
	 then
		if [[ ${#secret} -gt 32 || ${#secret} -lt 16 ]]
		 then
			echo "Please specify secret between 16 and 32 characters."
			exit 1
		 elif [[ $secret != +([0-9abcdffABCDEF]) ]]
		  then
			echo "Specified secret is invalid HEX."
			exit 1
		fi
	fi

	 # read and validate pin
	if [[ $pin ]]
	 then
		if [ "${#pin}" -ne 4 ]
		  then
			echo "PIN must be 4 digits."
			exit 1
		 elif [[ $pin != +([0-9]) ]]
		  then
			echo "PIN must contain only digits"
			exit 1
		fi
	fi
			
	 # read and validate offset
	if [[ $offset ]]
	 then
		 if [[ -z "$offset" ]]
		  then
			echo "Error: GMT Offset is missing"
			exit 1
		 elif [[ $offset != *(*([-+])[0-9]*([:])) ]]
		  then
			echo "Invalid GMT Offset."
			exit 1
		fi
	fi

	if [[ -n $secret ]]
	 then
		oldsecret="$($egrep "^$username[[:space:]]" $mconf | awk '{print $2}')"
		sed -i -e "/^$username[[:space:]]/s/$oldsecret/$secret/" $mconf
	fi

	if [[ -n $pin ]]
	 then
		oldpin="$($egrep "^$username[[:space:]]" $mconf | awk '{print $3}')"
		sed -i -e "/^$username[[:space:]]/s/$oldpin/$pin/" $mconf
	fi

	if [[ -n $offset ]]
	 then
		oldoffset="$($egrep "^$username[[:space:]]" $mconf | awk '{print $4}')"
		sed -i -e "/^$username[[:space:]]/s/$oldoffset/$offset/" $mconf
	fi

	# clear user's cache
	clear_cache

	# display user's configuration
	echo "Results after the change:"
	single_list

  else
	echo "User '$username' does not exist."
	exit 1
 fi
}

# show usage if no option
if [ $# -lt 1 ]
 then
	usage
 else 
	# define valid options
	optstring=hlk:m:r:c:s:p:o:a:

	OPTIND=1
	while getopts $optstring opt
	 do
		case $opt in
			l) all_list=1 ;;
			k) username=$OPTARG
				single_list=1 ;;
			m) username=$OPTARG
				user_mod=1 ;;
			r) username=$OPTARG
				user_del=1 ;;
			c) username=$OPTARG
				clear_cache=1 ;;
			s) secret=$OPTARG ;;
			p) pin=$OPTARG ;;
			o) offset=$OPTARG ;;
			a) username=$OPTARG
				user_add=1 ;;
			h|*) usage
				exit 0 ;;
		esac
	done
	shift "$((OPTIND - 1 ))"
fi


# function call based on shell options

[[ $all_list -eq 1 ]] && all_list
[[ $single_list -eq 1 ]] && single_list
[[ $user_del -eq 1 ]] && user_del
[[ $clear_cache -eq 1 ]] && clear_cache
[[ $user_add -eq 1 ]] && user_add
[[  $user_mod -eq 1 ]] && user_mod

