a51d968185
- 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
647 lines
20 KiB
Bash
Executable File
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 ""
|
|
}
|