#!/bin/bash # baker: user administration for breadpunk.club # by breadw set -euo pipefail usage() { case "$1" in [0-9]*) ec="$1"; shift ;; *) ec=0 ;; esac (( $# > 0 )) && printf '!! baker: %s\n' "$*" sed -n '2s/^#* *//' "$0" >&2 echo >&2 cat <<-ENDUSAGE >&2 usage: baker COMMAND [OPTIONS] USER COMMANDs: add/hire add a new user rm/fire remove a user promote add to groups demote remove from groups help show help - use 'baker help COMMAND' for specifics ENDUSAGE exit "$ec" } die() { ec="$1"; shift echo "!! ${FUNCNAME[1]}: $*" >&2 exit "$ec" } log() { printf '%s...' "$*" >&2; } ok() { printf ' %s\n' "Done" >&2; } bakeradd() { # add a baker # baker add -u USER -n NAME -k KEYFILE [-G GROUPS] [-s SHELL] # a wrapper around adduser user= name= keyfile= groups=bakers shell=/bin/bash while getopts s:n:G:k:u: opt; do case "$opt" in s) shell="$OPTARG" ;; n) name="$OPTARG" ;; G) groups="$groups${groups:+,}$OPTARG" ;; k) keyfile="$OPTARG" ;; u) user="$OPTARG" ;; \?) die 1 ;; *) die 1 "Unknown option -$opt" ;; esac done [ -z "$user" ] && die "Need a user" [ -z "$name" ] && die "Need a name" [ -z "$keyfile" ] && die "Need a keyfile" log "Adding user $user" adduser \ --shell="$shell" \ --gecos="$name" \ --disabled-password "$user" && ok log "Adding user $user to groups $groups" usermod -a -G "$groups" "$user" && ok log "Setting up ~/.ssh for $user" { sudo --user="$user" mkdir "/home/$user/.ssh" cat "$keyfile" >> "/home/$user/.ssh/authorized_keys2" chown "$user:$user" "/home/$user/.ssh/authorized_keys2" chmod 700 "/home/$user/.ssh" chmod 644 "/home/$user/.ssh/authorized_keys2" } && ok log "Adding gemini userdir for $user" { mkdir "/var/gemini/bakers/$user" chown "$user:$user" "/var/gemini/bakers/$user" chmod 755 "/var/gemini/bakers/$user" } && ok echo "$user added." } bakerremove() { # remove a baker # baker rm USER user="$1" log "Locking $user's account" passwd -l "$user" && ok log "Killing $user's processes" pkill -KILL -u "$user" && ok log "Removing $user's crontab" crontab -r -u "$user" && ok log "Removing user $user" # TODO: look into deluser.conf deluser --remove-home "$user" --backup-to /bread/fired/ && ok echo "$user removed." } bakerpromote() { # add a baker to groups # baker promote USER GROUP... user="$1"; shift groups= for group; do groups="$groups${groups:+,}$group" done log "Adding $user to groups: $groups" usermod -a -G "$groups" "$user" && ok } bakerdemote() { # remove a baker from groups # baker demote USER GROUP... user="$1" _sed=( sed ) for group; do if (groups "$user" | grep "$group"); then log "Removing $user from $group" _sed+=( -e "/$group/d" ) && ok else echo "$user is not a member of $group" fi done groups="$(getent group|grep "$user"|cut -d: -f1|"${_sed[@]}"|tr '\n' ',')" log "Applying changes" usermod -G "${groups%,}" "$user" && ok } main() { # entry point (( $# == 0 )) && usage 1 cmd="$1"; shift || die "Not enough arguments for \"$cmd\"" case "$cmd" in help|h|usage|-h|--help) (( $# == 0 )) && usage 0 grep -q "baker$1" "$0" && { echo "baker $1" awk "BEGIN{FS=\"\\n\";RS=\"\";}/baker$1/{print}" "$0" | sed -n 's/# //p' exit } || die "No command \"$1\"" ;; add|hire) cmd=add ;; remove|rm|fire) cmd=remove ;; promote) cmd=promote ;; demote) cmd=demote ;; esac (( $(id -u) == 0 )) || die "Script must be run as root" "baker$cmd" "$@" } main "$@"