#!/usr/bin/env bash # learner-remove.sh - Remove a learner account # Usage: learner-remove.sh [--archive] # # Removes: # - Unix user account # - Home directory (or archives if --archive flag) # - Maubot plugin symlinks set -euo pipefail MAUBOT_PLUGINS_DIR="/var/lib/maubot/plugins" ARCHIVE_DIR="/var/backups/learners" # Colors for output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' NC='\033[0m' # No Color log_info() { echo -e "${GREEN}[INFO]${NC} $1"; } log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; } log_error() { echo -e "${RED}[ERROR]${NC} $1" >&2; } usage() { echo "Usage: $0 [--archive]" echo "" echo "Arguments:" echo " username - Learner's username to remove" echo " --archive - Archive home directory instead of deleting" echo "" echo "Example:" echo " $0 alice # Delete user and home directory" echo " $0 alice --archive # Archive home directory before deleting" exit 1 } validate_username() { local username="$1" # Check if user exists if ! id "$username" &>/dev/null; then log_error "User '$username' does not exist" exit 1 fi # Safety check - don't delete system users local uid uid=$(id -u "$username") if [[ $uid -lt 1000 ]]; then log_error "Refusing to delete system user '$username' (UID $uid < 1000)" exit 1 fi # Safety check - don't delete important users case "$username" in root|dan|admin|maubot|postgres|nginx) log_error "Refusing to delete protected user '$username'" exit 1 ;; esac } remove_maubot_symlinks() { local username="$1" log_info "Removing maubot plugin symlinks for '$username'..." # Remove any symlinks that point to user's home directory local count=0 for symlink in "$MAUBOT_PLUGINS_DIR"/*; do if [[ -L "$symlink" ]]; then local target target=$(readlink "$symlink") if [[ "$target" == "/home/$username/"* ]]; then rm "$symlink" log_info "Removed symlink: $symlink" ((count++)) || true fi fi done # Also remove any plugins named after user for symlink in "$MAUBOT_PLUGINS_DIR/${username}-"*; do if [[ -e "$symlink" || -L "$symlink" ]]; then rm -f "$symlink" log_info "Removed: $symlink" ((count++)) || true fi done if [[ $count -eq 0 ]]; then log_info "No maubot symlinks found for '$username'" fi } archive_home() { local username="$1" local home_dir="/home/$username" if [[ ! -d "$home_dir" ]]; then log_warn "Home directory does not exist: $home_dir" return fi log_info "Archiving home directory..." mkdir -p "$ARCHIVE_DIR" local archive_name archive_name="${username}_$(date +%Y%m%d_%H%M%S).tar.gz" local archive_path="$ARCHIVE_DIR/$archive_name" tar -czf "$archive_path" -C /home "$username" chmod 600 "$archive_path" log_info "Archived to: $archive_path" } remove_user() { local username="$1" log_info "Removing user '$username'..." # Kill any running processes pkill -u "$username" 2>/dev/null || true sleep 1 # Remove user and home directory userdel -r "$username" 2>/dev/null || { # If userdel -r fails, try without -r and manually remove home userdel "$username" 2>/dev/null || true rm -rf "/home/${username:?}" } log_info "User removed" } main() { if [[ $# -lt 1 ]]; then usage fi local username="$1" local archive=false # Parse flags shift while [[ $# -gt 0 ]]; do case "$1" in --archive) archive=true shift ;; *) log_error "Unknown option: $1" usage ;; esac done # Must run as root if [[ $EUID -ne 0 ]]; then log_error "This script must be run as root" exit 1 fi validate_username "$username" # Confirm deletion echo "" log_warn "This will remove user '$username' and all their data!" if [[ "$archive" == true ]]; then echo "Home directory will be archived to $ARCHIVE_DIR" else echo "Home directory will be PERMANENTLY DELETED" fi echo "" read -rp "Are you sure? (yes/no): " confirm if [[ "$confirm" != "yes" ]]; then log_info "Aborted" exit 0 fi remove_maubot_symlinks "$username" if [[ "$archive" == true ]]; then archive_home "$username" fi remove_user "$username" log_info "Learner '$username' removed successfully" } main "$@"