Files
Linux-Server-Management-Too…/lib/user-manager.sh
T
cschantz a51d968185 Initial commit: Server Management Toolkit v2.0
- Complete security menu restructure (3-mode: Analysis/Actions/Live)
- Intelligent cPHulk enablement with CSF whitelist import
- Live network security monitoring dashboard
- Multi-source threat detection and classification
- 50+ organized security tools across 4-level menu hierarchy
- System health diagnostics with cPanel/WHM integration
- Reference database for cross-module intelligence sharing
2025-11-03 18:21:40 -05:00

647 lines
20 KiB
Bash
Executable File

#!/bin/bash
#############################################################################
# User Manager Library
# Dynamic user listing and management for cPanel/Plesk/InterWorx
#############################################################################
# Source dependencies
if [ -z "$TOOLKIT_BASE_DIR" ]; then
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/common-functions.sh"
source "$SCRIPT_DIR/system-detect.sh"
fi
#############################################################################
# USER LISTING (Control Panel Specific)
#############################################################################
list_all_users() {
case "$SYS_CONTROL_PANEL" in
cpanel)
list_cpanel_users
;;
plesk)
list_plesk_users
;;
interworx)
list_interworx_users
;;
*)
list_system_users
;;
esac
}
# cPanel user listing
list_cpanel_users() {
if [ -d "/var/cpanel/users" ]; then
ls /var/cpanel/users/ 2>/dev/null || true
else
# Fallback: parse /etc/trueuserdomains
awk -F: '{print $2}' /etc/trueuserdomains 2>/dev/null | sort -u || true
fi
}
# Plesk user listing
list_plesk_users() {
if command_exists mysql && [ -f /etc/psa/.psa.shadow ]; then
mysql -Ns psa -e "SELECT login FROM sys_users WHERE type='user'" 2>/dev/null
else
# Fallback: list directories
find /var/www/vhosts -maxdepth 1 -type d -printf "%f\n" 2>/dev/null | grep -v "^system$\|^default$\|^chroot$"
fi
}
# InterWorx user listing
list_interworx_users() {
if [ -x "/usr/local/interworx/bin/listaccounts.pex" ]; then
/usr/local/interworx/bin/listaccounts.pex --output user 2>/dev/null
else
# Fallback: parse InterWorx config
find /home -maxdepth 1 -type d -name "*.conf" 2>/dev/null | xargs -I {} basename {} .conf
fi
}
# System users (no control panel)
list_system_users() {
# List users with home directories in /home and valid shells
awk -F: '$6 ~ /^\/home\// && $7 !~ /nologin|false/ {print $1}' /etc/passwd
}
#############################################################################
# USER INFORMATION
#############################################################################
get_user_info() {
local username="$1"
local info_file="${TEMP_SESSION_DIR}/user_${username}_info.tmp"
case "$SYS_CONTROL_PANEL" in
cpanel)
get_cpanel_user_info "$username" > "$info_file"
;;
plesk)
get_plesk_user_info "$username" > "$info_file"
;;
interworx)
get_interworx_user_info "$username" > "$info_file"
;;
*)
get_system_user_info "$username" > "$info_file"
;;
esac
cat "$info_file"
}
# cPanel user info
get_cpanel_user_info() {
local username="$1"
local user_file="/var/cpanel/users/${username}"
if [ ! -f "$user_file" ]; then
echo "USER_EXISTS=no"
return 1
fi
# Parse cPanel user file
local primary_domain=$(grep "^DNS=" "$user_file" | cut -d= -f2)
local email=$(grep "^CONTACTEMAIL=" "$user_file" | cut -d= -f2)
# cPanel doesn't store HOMEDIR in user file - it's always /home/username
local home_dir="/home/${username}"
# Get addon/parked domains
local all_domains=$(grep "^DNS" "$user_file" | cut -d= -f2 | tr '\n' ' ')
# Get disk usage
local disk_used=$(du -sh "$home_dir" 2>/dev/null | awk '{print $1}')
echo "USER_EXISTS=yes"
echo "USERNAME=$username"
echo "PRIMARY_DOMAIN=$primary_domain"
echo "ALL_DOMAINS=$all_domains"
echo "EMAIL=$email"
echo "HOME_DIR=$home_dir"
echo "DISK_USED=$disk_used"
}
# Plesk user info
get_plesk_user_info() {
local username="$1"
if ! command_exists mysql || [ ! -f /etc/psa/.psa.shadow ]; then
echo "USER_EXISTS=no"
return 1
fi
local home_dir="/var/www/vhosts/${username}"
local primary_domain=$(mysql -Ns psa -e "SELECT name FROM domains WHERE id IN (SELECT domain_id FROM sys_users WHERE login='$username')" 2>/dev/null | head -1)
local disk_used=$(du -sh "$home_dir" 2>/dev/null | awk '{print $1}')
echo "USER_EXISTS=yes"
echo "USERNAME=$username"
echo "PRIMARY_DOMAIN=$primary_domain"
echo "HOME_DIR=$home_dir"
echo "DISK_USED=$disk_used"
}
# InterWorx user info
get_interworx_user_info() {
local username="$1"
local home_dir="/home/${username}"
if [ ! -d "$home_dir" ]; then
echo "USER_EXISTS=no"
return 1
fi
local disk_used=$(du -sh "$home_dir" 2>/dev/null | awk '{print $1}')
echo "USER_EXISTS=yes"
echo "USERNAME=$username"
echo "HOME_DIR=$home_dir"
echo "DISK_USED=$disk_used"
}
# System user info (no control panel)
get_system_user_info() {
local username="$1"
local user_entry=$(getent passwd "$username")
if [ -z "$user_entry" ]; then
echo "USER_EXISTS=no"
return 1
fi
local home_dir=$(echo "$user_entry" | cut -d: -f6)
local disk_used=$(du -sh "$home_dir" 2>/dev/null | awk '{print $1}')
echo "USER_EXISTS=yes"
echo "USERNAME=$username"
echo "HOME_DIR=$home_dir"
echo "DISK_USED=$disk_used"
}
#############################################################################
# USER DOMAINS
#############################################################################
get_user_domains() {
local username="$1"
case "$SYS_CONTROL_PANEL" in
cpanel)
get_cpanel_user_domains "$username"
;;
plesk)
get_plesk_user_domains "$username"
;;
interworx)
get_interworx_user_domains "$username"
;;
*)
echo ""
;;
esac
}
get_cpanel_user_domains() {
local username="$1"
# Primary domain (format: domain: user)
grep ": ${username}$" /etc/trueuserdomains 2>/dev/null | cut -d: -f1 || true
# Addon domains
if [ -f "/etc/userdatadomains" ]; then
grep "==${username}$" /etc/userdatadomains 2>/dev/null | cut -d: -f1 || true
fi
}
get_plesk_user_domains() {
local username="$1"
if command_exists mysql && [ -f /etc/psa/.psa.shadow ]; then
mysql -Ns psa -e "SELECT d.name FROM domains d JOIN sys_users u ON d.id=u.domain_id WHERE u.login='$username'" 2>/dev/null
fi
}
get_interworx_user_domains() {
local username="$1"
if [ -x "/usr/local/interworx/bin/listaccounts.pex" ]; then
/usr/local/interworx/bin/listaccounts.pex --user "$username" --output domain 2>/dev/null
fi
}
#############################################################################
# USER DATABASES
#############################################################################
get_user_databases() {
local username="$1"
case "$SYS_CONTROL_PANEL" in
cpanel)
get_cpanel_user_databases "$username"
;;
plesk)
get_plesk_user_databases "$username"
;;
interworx)
get_interworx_user_databases "$username"
;;
*)
# Try to find databases matching username pattern
mysql -e "SHOW DATABASES" 2>/dev/null | grep "^${username}_"
;;
esac
}
get_cpanel_user_databases() {
local username="$1"
# cPanel databases typically follow pattern: username_dbname
mysql -e "SHOW DATABASES" 2>/dev/null | grep "^${username}_" || true
}
get_plesk_user_databases() {
local username="$1"
if command_exists mysql && [ -f /etc/psa/.psa.shadow ]; then
mysql -Ns psa -e "SELECT db.name FROM data_bases db JOIN domains d ON db.dom_id=d.id JOIN sys_users u ON d.id=u.domain_id WHERE u.login='$username'" 2>/dev/null
fi
}
get_interworx_user_databases() {
local username="$1"
# InterWorx databases typically follow pattern: username_dbname
mysql -e "SHOW DATABASES" 2>/dev/null | grep "^${username}_" || true
}
#############################################################################
# USER LOG FILES
#############################################################################
get_user_log_files() {
local username="$1"
local domains=$(get_user_domains "$username")
case "$SYS_CONTROL_PANEL" in
cpanel)
for domain in $domains; do
echo "${SYS_LOG_DIR}/${domain}"
echo "${SYS_LOG_DIR}/${domain}-ssl_log"
done
;;
plesk)
echo "/var/www/vhosts/${username}/statistics/logs/access_log"
echo "/var/www/vhosts/${username}/statistics/logs/error_log"
for domain in $domains; do
echo "/var/www/vhosts/${domain}/statistics/logs/access_log"
echo "/var/www/vhosts/${domain}/statistics/logs/error_log"
done
;;
interworx)
for domain in $domains; do
echo "/home/${username}/var/${domain}/logs/access_log"
echo "/home/${username}/var/${domain}/logs/error_log"
done
;;
esac | grep -v "^$" | sort -u
}
#############################################################################
# USER SELECTION MENU
#############################################################################
select_user_interactive() {
local prompt="${1:-Select a user}"
local users=($(list_all_users))
local total_users=${#users[@]}
if [ $total_users -eq 0 ]; then
print_error "No users found" >&2
return 1
fi
# Build user info cache to avoid repeated get_user_domains calls
declare -A user_primary_domain
declare -A user_domain_count
for user in "${users[@]}"; do
local domains=$(get_user_domains "$user" 2>/dev/null | grep -v "^$")
if [ -n "$domains" ]; then
user_domain_count["$user"]=$(echo "$domains" | wc -l)
user_primary_domain["$user"]=$(echo "$domains" | head -1)
else
user_domain_count["$user"]=0
user_primary_domain["$user"]="(no domains)"
fi
done
# Send all display output to stderr so it shows on screen (not captured by $(...))
{
echo ""
print_section "$prompt"
echo ""
echo "Found $total_users user(s) on this server"
echo "-------------------------------------------------------------------------------"
# Auto-show list if 10 or fewer users
if [ $total_users -le 10 ]; then
echo ""
for user in "${users[@]}"; do
echo -e " ${GREEN}$user${NC} - ${user_primary_domain[$user]} (${user_domain_count[$user]} domains)"
done
fi
echo ""
echo "-------------------------------------------------------------------------------"
echo ""
echo "Options:"
if [ $total_users -gt 10 ]; then
echo " L - List all $total_users users"
fi
echo " S [text] - Search/filter users (e.g., 's pick' or 's example.com')"
echo " [username] - Enter exact username"
echo " A - All users (system-wide scan)"
echo " 0 - Cancel/Go back"
echo ""
} >&2
read -p "Enter choice> " choice
case "$choice" in
[Aa]|ALL|all)
echo "ALL"
return 0
;;
[Ss]\ *|[Ss])
# Search mode
local search_term="${choice#[Ss] }" # Remove 'S ' prefix
search_term="${search_term#[Ss]}" # Remove 'S' if no space
if [ -z "$search_term" ]; then
# No search term provided, ask for it
read -p "Enter search term (username or domain)> " search_term >&2
fi
if [ -z "$search_term" ]; then
print_error "No search term provided" >&2
return 1
fi
# Search and build match array
local -a matched_users
local -a menu_items
for user in "${users[@]}"; do
# Case-insensitive partial match in username or domain
if [[ "${user,,}" == *"${search_term,,}"* ]] || [[ "${user_primary_domain[$user],,}" == *"${search_term,,}"* ]]; then
matched_users+=("$user")
menu_items+=("$user - ${user_primary_domain[$user]} (${user_domain_count[$user]} domains)")
fi
done
if [ ${#matched_users[@]} -eq 0 ]; then
print_error "No users found matching '$search_term'" >&2
return 1
elif [ ${#matched_users[@]} -eq 1 ]; then
# Single match - ask for confirmation
{
echo ""
echo "Found 1 match: ${matched_users[0]} - ${user_primary_domain[${matched_users[0]}]} (${user_domain_count[${matched_users[0]}]} domains)"
echo ""
} >&2
read -p "Use this user? (Y/n)> " confirm >&2
if [[ ! $confirm =~ ^[Nn]$ ]]; then
echo "${matched_users[0]}"
return 0
else
return 1
fi
else
# Multiple matches - show interactive arrow-key menu
{
echo ""
echo "Found ${#matched_users[@]} matches for '$search_term'"
echo "Use arrow keys (↑/↓) to navigate, Enter to select:"
echo ""
} >&2
PS3="Select user> "
select option in "${menu_items[@]}" "Cancel"; do
if [ "$option" = "Cancel" ]; then
return 1
elif [ -n "$option" ]; then
# Extract username from selected menu item (before the ' - ')
local selected_user="${matched_users[$((REPLY-1))]}"
echo "$selected_user"
return 0
fi
done >&2
fi
;;
[Ll]|LIST|list)
# Show full list
{
echo ""
echo "Complete user list ($total_users users):"
echo "-------------------------------------------------------------------------------"
for user in "${users[@]}"; do
echo -e " ${GREEN}$user${NC} - ${user_primary_domain[$user]} (${user_domain_count[$user]} domains)"
done
echo "-------------------------------------------------------------------------------"
echo ""
} >&2
# Ask again after showing list
read -p "Enter username (or A for all, 0 to cancel)> " choice
# Re-evaluate choice
if [[ " ${users[@]} " =~ " ${choice} " ]]; then
echo "$choice"
return 0
elif [ "$choice" = "A" ] || [ "$choice" = "a" ]; then
echo "ALL"
return 0
elif [ "$choice" = "0" ]; then
return 1
else
print_error "User '$choice' not found" >&2
return 1
fi
;;
0|cancel|back)
return 1
;;
"")
print_error "No input provided" >&2
return 1
;;
*)
# Check if it's an exact username match
if [[ " ${users[@]} " =~ " ${choice} " ]]; then
echo "$choice"
return 0
fi
# Not exact match
print_error "User '$choice' not found" >&2
if [ $total_users -gt 10 ]; then
echo " Tip: Type 'L' to list all users" >&2
fi
return 1
;;
esac
}
#############################################################################
# USER PROCESSES
#############################################################################
get_user_processes() {
local username="$1"
ps aux | grep "^${username}" | grep -v grep
}
get_user_top_processes() {
local username="$1"
local limit="${2:-10}"
ps aux | grep "^${username}" | grep -v grep | sort -k3 -rn | head -n "$limit"
}
#############################################################################
# DATABASE HELPER FUNCTIONS
#############################################################################
get_database_owner() {
local db_name="$1"
# Try to determine owner from database name prefix (common cPanel convention)
# Database names are typically: username_dbname
local prefix=$(echo "$db_name" | cut -d_ -f1)
# Check if this prefix matches a user
local users=$(list_all_users)
for user in $users; do
if [ "$user" = "$prefix" ]; then
echo "$user"
return 0
fi
done
# If no match, return unknown
echo "unknown"
}
get_database_domain() {
local db_name="$1"
local owner=$(get_database_owner "$db_name")
# Try to get primary domain for the owner
if [ "$owner" != "unknown" ]; then
get_user_domains "$owner" 2>/dev/null | head -1
else
echo ""
fi
}
#############################################################################
# WORDPRESS DETECTION
#############################################################################
find_user_wordpress_sites() {
local username="$1"
local home_dir=$(get_user_info "$username" | grep "^HOME_DIR=" | cut -d= -f2)
if [ -z "$home_dir" ] || [ ! -d "$home_dir" ]; then
return 1
fi
# Find wp-config.php files
find "$home_dir" -name "wp-config.php" -type f 2>/dev/null | while read wp_config; do
local wp_dir=$(dirname "$wp_config")
local domain=$(basename "$(dirname "$wp_dir")" 2>/dev/null)
# Try to get actual domain from wp-config
local site_url=$(grep "WP_SITEURL\|WP_HOME" "$wp_config" | head -1 | grep -oP "https?://\K[^/'\"]+")
if [ -n "$site_url" ]; then
echo "${site_url}|${wp_dir}"
else
echo "${domain}|${wp_dir}"
fi
done
}
#############################################################################
# DISPLAY FUNCTIONS
#############################################################################
show_user_summary() {
local username="$1"
print_section "User Summary: $username"
# Get user info
local user_info=$(get_user_info "$username")
if echo "$user_info" | grep -q "USER_EXISTS=no"; then
print_error "User does not exist"
return 1
fi
# Parse info
local primary_domain=$(echo "$user_info" | grep "^PRIMARY_DOMAIN=" | cut -d= -f2)
local home_dir=$(echo "$user_info" | grep "^HOME_DIR=" | cut -d= -f2)
local disk_used=$(echo "$user_info" | grep "^DISK_USED=" | cut -d= -f2)
# Display
echo " Username: $username"
[ -n "$primary_domain" ] && echo " Primary Domain: $primary_domain"
echo " Home Directory: $home_dir"
echo " Disk Usage: $disk_used"
echo ""
# Domains
local domains=$(get_user_domains "$username")
local domain_count=$(echo "$domains" | grep -v "^$" | wc -l)
echo " Domains ($domain_count):"
echo "$domains" | sed 's/^/ - /'
echo ""
# Databases
local databases=$(get_user_databases "$username")
local db_count=$(echo "$databases" | grep -v "^$" | wc -l)
echo " Databases ($db_count):"
echo "$databases" | sed 's/^/ - /'
echo ""
}
show_all_users_summary() {
print_section "All Users Summary"
local users=($(list_all_users))
local total_users=${#users[@]}
echo " Total Users: $total_users"
echo ""
printf " ${BOLD}%-20s %-30s %10s %10s${NC}\n" "Username" "Primary Domain" "Domains" "Databases"
echo " ────────────────────────────────────────────────────────────────────"
for user in "${users[@]}"; do
local primary=$(get_user_domains "$user" | head -1)
local domain_count=$(get_user_domains "$user" | grep -v "^$" | wc -l)
local db_count=$(get_user_databases "$user" | grep -v "^$" | wc -l)
printf " %-20s %-30s %10s %10s\n" "$user" "$primary" "$domain_count" "$db_count"
done
echo ""
}