/home/wpollock1/public_html/ShScript/add-users

#!/bin/bash -
# Script to add many student accounts at once.  This is the
# Fedora version.  Run as root only.
# See also the "disable-user" script to disable accounts before
# permanently removing them with 'remove-user'.
# TODO: currently invalid inputs abort program.  This should be
# changed to a loop to try 'n' times (say 3) before aborting.
# TODO: Add option for setting account expire date (for ua users).
# TODO: change email subject to include the prefix (class name)
# TODO: Save passwd list in ~/tmp files.
# TODO: Add better defaults for all options.
# TODO: code review: POSIX has added many shell features recently.  The
# script should also serve as a model.
#
#  $Id: add-users,v 1.11 2016/05/04 06:16:52 wpollock Exp $
#
# Originally written 2002 by Wayne Pollock, Tampa FL USA.

PATH=/bin:/sbin:/usr/bin:/usr/sbin

PROG=${0##*/}
HOST=$(uname -n)
INSTRUCTOR=""
CREATE_ADDITIONAL_GRP="n"
ADD_INST_TO_ADDITIONAL_GRP="n"
ADD_INST_TO_STUTENT_GRPS="n"

md5crypt()
{
    printf '%s\n' "$1" |openssl passwd -1 -stdin
}

# A fancy separator line with optional centered title,
# of 68 characters ("-") wide.

drawLine()
{
    local num line title
    title="$1"
    let "num = ( 68 - ${#title} ) / 2"
    line=$(perl -e "print \"-\" x $num;")
    printf '\n%s' "$line"
    [ "$1" ] && printf '%s' " $1 "
    printf '%s\n\n' "$line"
    echo
}

getInst()
{
    local CURR_USER instructor

    # "id" doesn't always (?) report the real UID, so prefer "logname":
    if [ -x /usr/bin/logname ]
    then CURR_USER=`logname`
    else CURR_USER=`id -run`
    fi

    printf '%s' "What is your account name (default: $CURR_USER)? " >/dev/tty
    read instructor
    if [ "$instructor" = "" ]
    then instructor=$CURR_USER
    fi
    if ! grep -e "^$instructor:" /etc/passwd >/dev/null 2>&1
    then
        echo "No user account \"$instructor\" found!" >&2
        echo "Exiting..." >&2
        exit 2
    fi
    echo "$instructor"
}

if [ "`id -u`" != "0" ]
then echo "You must be root to run this script.  (Try \"sudo $PROG\".)"
    exit 1
fi

drawLine "Class Account Creation Wizard"

cat <<\EOF
Created accounts have the form <prefix><number>
(e.g. "rab01", "rab02", ...

Enter class prefix(es) (e.g.: ua ub uc)
EOF
printf "A good choice is the first few letters of the instructor's name: "
read CLASSES
set -- $CLASSES
if [ $# = 0 ]
then    echo "No classes entered, good-bye!"
        exit 1
fi

drawLine "Number of Accounts"

echo "Enter number of accounts to create per class (e.g.: 25)."
echo "(Note that one additional account (numbered \"00\") will be"
echo "created for the instructor to use, as a demo for example.)"
echo
printf '%s' "How many student accounts should be created? "
read NUM JUNK
if [ -z "$NUM" ]
then    echo "No value entered, good-bye!"
        exit 1
fi
if [ "$JUNK" ]
then    echo "Only enter a single value, good-bye!"
        exit 1
fi

shopt -s extglob  # Allow extended pattern matching in bash
case "$NUM" in
+([0-9])) if [ "$NUM" -eq 0 ]
          then echo "You must create at least one account, good-bye!"
               exit 1
          elif [ "$NUM" -gt 40 ]
          then printf '%s' "$NUM are a lot of accounts, are you sure? "
               read ANS
               if [ "$ANS" != 'y' ]
               then  echo "Good-bye!"
                     exit 1
               fi
          fi
          ;;
*)        echo "You must enter a positive number, good-bye!"
          exit 1
          ;;
esac

drawLine "Additional Group Setup"

echo "An additional group can be created for each class, named"
echo "for the class prefix.  Each student can be added as a member"
echo "of this group, so students will be members of two groups."
echo
printf '%s' "Do you wish to create an additional group per class (y/n)? "
read answer
if [ "$answer" = "y" ]
then
    CREATE_ADDITIONAL_GRP="y"
    echo
    printf '%s' "Do you want to add yourself to this group as well (y/n)? "
    read answer
    if [ "$answer" = "y" ]
    then
	INSTRUCTOR=$(getInst)
	ADD_INST_TO_ADDITIONAL_GRP="y"
    fi
fi

drawLine "Instructor Access"

echo "The system umask of \"027\" allows full access by a file's"
echo "owner, read access by group members, and no access for others."
echo "By adding the instructor as a group member, for each student's"
echo "group (there is one group per account), the instructor is"
echo "given read access to all student files, facilitating grading and"
echo "support."
echo
echo "Configure accounts so the instructor has read access"
printf '%s' " to all created account home directories (y/n)? "
read answer
if [ "$answer" = "y" ]
then ADD_INST_TO_STUTENT_GRPS="y"
     [ -z "$INSTRUCTOR" ] && INSTRUCTOR=$(getInst)
fi

drawLine "Account Locking"

TODAY=$(date +%D)
echo "For security accounts are created locked (disabled)."
printf '%s' "Enter the date the accounts should be unlocked (default: \"$TODAY\"): "
read UnlockDate JUNK
if [ -z "$UnlockDate" ]
then UnlockDate=$TODAY
fi
if [ -n "$JUNK" ]
then echo "You must enter a date such as \"mm/dd/yy\".  Good-bye!"
     exit 1
fi
CANONIZED_DATE=$(date --date="$UnlockDate" +%D) || exit 2

# Check to make sure the date entered is not in the past:
Today_stamp=`date --date="$TODAY" +%s`
Canon_stamp=`date --date="$CANONIZED_DATE" +%s`
if [ "$Canon_stamp" -lt "$Today_stamp" ]
then
    echo "You must enter today's date, or some date in the future!"
    exit 2
fi

if [ "$TODAY" = "$CANONIZED_DATE" ]
then pwPrefix=""
     printf '\n%s\n' '    *** Accounts will be created unlocked (enabled)! ***'
else pwPrefix="!"
     echo "Accounts will be unlocked at midnight on $CANONIZED_DATE."
fi

drawLine "Initial Passwords"

cat <<\EOF
For highest security, accounts can be created with default
passwords.  If you chose this option the account names and
their passwords will be emailed to you.  If not, accounts
will not have passwords (users are be forced to change the
passwords when they first login, regardless).
EOF
printf '\n%s' 'Create accounts with initial passwords? (y/n)'
read REPLY
case "$REPLY" in
  [yY]*) GenPW=yes
	 echo
         printf '%s' "Enter email address to send password report to: "
         read INST_EMAIL JUNK
         echo "Passwords will be sent to $INST_EMAIL"
         ;;
  *)     GenPW=no
         echo "No passwords will be generated!"
         ;;
esac

echo
if [ "$NUM" -lt 10 ]
then DISP_NUM="0$NUM"
else DISP_NUM="$NUM"
fi

drawLine "Disk Quotas"

QUOTA_SOFT=6000  # 6 MiB
QUOTA_HARD=12000 # 12 MiB
# note file quotas, and all quotas for /var and /tmp are not
# configurable in this script!

echo Disk quotas are set for /home, /var, and /tmp for all users.
echo The defaults should be fine for most users but some may require
echo more space for their home directories.
echo
printf 'Use larger quotas for /home? (y/N) '
read answer
if [ "$answer" = "y" ]
then
  QUOTA_SOFT=25000000 # 25 GiB
  QUOTA_HARD=35000000 # 35 GiB
fi

drawLine "Configuration Summary"

# Show summary information for confirmation:

if [ $# -gt 1 ]
then
    printf '%s' "Creating $# classes of $((NUM + 1)) accounts each "
    echo "(\"${1}00\" - \"${1}$DISP_NUM\", ...)."
else
    printf '%s' "Creating 1 class of $((NUM + 1)) accounts "
    echo "(\"${1}00\" - \"${1}$DISP_NUM\")."
fi

echo "With quotas of"
printf '%-12s %-12s %-12s %-12s %-12s\n' \
 'Filesystem' 'Blocks-soft' 'Blocks-hard' 'Files-soft' 'Files-hard'
printf '%-12s %-12s %-12s %-12s %-12s\n' \
 '/home' "$QUOTA_SOFT" "$QUOTA_HARD" 2000 3000
printf '%-12s %-12s %-12s %-12s %-12s\n' \
 '/var' 1000 1500 100 200
printf '%-12s %-12s %-12s %-12s %-12s\n' \
 '/tmp' 1000 1500 100 200

if [ "$pwPrefix" = "" ]
then echo "Accounts will be created unlocked."
else echo "Accounts will be unlocked at midnight on $CANONIZED_DATE."
fi

if [ "$GenPW" = "yes" ]
then echo "Password list will be emailed to \"$INST_EMAIL\"."
else echo "Accounts will NOT have initial passwords."
fi

if [ "$CREATE_ADDITIONAL_GRP" = "y" ]
then echo "Creating an additional group per class."
     if [ "$ADD_INST_TO_ADDITIONAL_GRP" = "y" ]
     then echo "    $INSTRUCTOR will be included in this group."
     fi
fi

if [ "$ADD_INST_TO_STUTENT_GRPS" = "y" ]
then echo "$INSTRUCTOR will have read access to all account files."
fi

echo
printf "OK to proceed? (y/N)? "
read answer
if [ "$answer" != "y" ]
then
    echo "You ARE the weakest link...good-bye!"
    exit 1
fi

# Create pipe to email message (password list):
[ "$GenPW" = "yes" ] && \
    exec 3> >(/bin/mail -s "New $HOST Account Passwords" "$INST_EMAIL")

for CLASS in $CLASSES
do
    if grep "^$CLASS[0-9]" /etc/passwd >/dev/null 2>&1
    then echo "Class \"$CLASS\" already exists, skipping..."
         logger -p warn -t "add-users" \
             "Skipping account creation for $CLASS, already exists."
         continue
    fi

    logger -t "add-users" "Creating new user accounts with prefix $CLASS."

    if [ "$CREATE_ADDITIONAL_GRP" = "y" ]
    then echo "Creating new group \"$CLASS\"..."
         groupadd "$CLASS"
	 if [ "$ADD_INST_TO_ADDITIONAL_GRP" = "y" ]
	 then
	     gpasswd -a "$INSTRUCTOR" "$CLASS"
	fi
    fi

    for i in $(seq -f "%02.0f" 0 $NUM)
    do
        User="$CLASS$i"
        printf '%s' "Adding user $User."

        # Create group account for user:
        groupadd "$User"
        printf '.'

        if [ "$ADD_INST_TO_STUTENT_GRPS" = "y" ]
        then
            gpasswd -a "$INSTRUCTOR" "$User" |tr '\n' '.'
        fi

        # Set the password: "" = no pw, unlocked, "!" = no pw, locked,
        # "Xy1Abc" = pw and unlocked, and "!Xy1Abc" = pw, locked.
        PASSWORD="$pwPrefix"
        if [ "$GenPW" = "yes" ]
        then
            # pronouncable with at least one digit and capital leter:
            pass=$(pwgen -cn1)
            crypt=$(md5crypt "$pass")
            PASSWORD="$pwPrefix$crypt"
            printf "$User\t$pass\n" >&3  # Append name, password to email.
        fi

        # Create user: -m means create home dir and copy /etc/skel,
        #              -p '!' means create NULL (no) password for Linux
        #                 but initially lock (disable) the account,
        #              -g $User means put the user in his/her own group

        useradd -m -p "$PASSWORD" -g "$User" -K UMASK=027 "$User"
        printf '.'

        # Add the user to group "tty".  This is needed on some systems
        # (such as Fedora 21) before users can use write, wall, talk:
        gpasswd -a "$User" tty
        printf '.'

        # This forces the password to be change every 150 days, and
        # sets the date of last change to be 1/1/1970.  Thus a user
        # is forced to set a password the first time they log in, and
        # that password should be valid for one semester (plus a few
        # extra weeks).
        chage -M 150 -d 1 "$User"  #Linux cmd, Solaris uses passwd
        printf '.'

        # Set the quotas for the user:
        setquota -u "$User" $QUOTA_SOFT $QUOTA_HARD 2000 3000 /home
        setquota -u "$User" 1000 1500 100 200 /var
        setquota -u "$User" 1000 1500 100 200 /tmp
        printf '.'

	# Add user to additional group:
	if [ "$CREATE_ADDITIONAL_GRP" = "y" ]
	then
	    gpasswd -a "$User" "$CLASS" >/dev/null
            printf '.'
	fi

        echo "done!"
    done
done

# Close the pipe to mail (which will send the email):
[ "$GenPW" = "yes" ] && exec 3>&-

# Create an "at" job to unlock accounts on the specified date
# if necessary:
if [ "$pwPrefix" = '!' ]
then
at midnight $CANONIZED_DATE >/dev/null 2>&1 <<-EOF
	for c in $CLASSES
	do
	    for i in `seq -s ' ' -f "%02.0f" 0 $NUM`
	    do
	        usermod -U "\$c\$i"
	    done
	done >/dev/null 2>&1
	logger -t "add-users" "Enabling $CLASSES student accounts."
	EOF
fi