Major structural reorganization and feature additions: ## Folder Reorganization - Move 17 luci-app-* packages to package/secubox/ (except luci-app-secubox core hub) - Update all tooling to support new structure: - secubox-tools/quick-deploy.sh: search both locations - secubox-tools/validate-modules.sh: validate both directories - secubox-tools/fix-permissions.sh: fix permissions in both locations - .github/workflows/test-validate.yml: build from both paths - Update README.md links to new package/secubox/ paths ## AppStore Migration (Complete) - Add catalog entries for all remaining luci-app packages: - network-tweaks.json: Network optimization tools - secubox-bonus.json: Documentation & demos hub - Total: 24 apps in AppStore catalog (22 existing + 2 new) - New category: 'documentation' for docs/demos/tutorials ## VHost Manager v2.0 Enhancements - Add profile activation system for Internal Services and Redirects - Implement createVHost() API wrapper for template-based deployment - Fix Virtual Hosts view rendering with proper LuCI patterns - Fix RPCD backend shell script errors (remove invalid local declarations) - Extend backend validation for nginx return directives (redirect support) - Add section_id parameter for named VHost profiles - Add Remove button to Redirects page for feature parity - Update README to v2.0 with comprehensive feature documentation ## Network Tweaks Dashboard - Close button added to component details modal Files changed: 340+ (336 renames with preserved git history) Packages affected: 19 luci-app, 2 secubox-app, 1 theme, 4 tools 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
518 lines
13 KiB
Bash
Executable File
518 lines
13 KiB
Bash
Executable File
#!/bin/sh
|
|
# Auth Guardian RPCD Backend
|
|
# OAuth2 authentication and voucher system
|
|
|
|
. /lib/functions.sh
|
|
. /usr/share/libubox/jshn.sh
|
|
|
|
# Configuration paths
|
|
CONFIG_FILE="/etc/config/auth_guardian"
|
|
SESSIONS_FILE="/tmp/auth_guardian_sessions.json"
|
|
LOGS_FILE="/tmp/auth_guardian_logs.json"
|
|
|
|
# Initialize storage
|
|
init_storage() {
|
|
mkdir -p /tmp/auth_guardian
|
|
[ ! -f "$SESSIONS_FILE" ] && echo '[]' > "$SESSIONS_FILE"
|
|
[ ! -f "$LOGS_FILE" ] && echo '[]' > "$LOGS_FILE"
|
|
touch "$CONFIG_FILE"
|
|
}
|
|
|
|
# Generate unique ID
|
|
generate_id() {
|
|
head -c 8 /dev/urandom | hexdump -e '"%016x"'
|
|
}
|
|
|
|
# Generate voucher code
|
|
generate_voucher_code() {
|
|
local part1=$(head -c 3 /dev/urandom | hexdump -e '"%06x"' | tr '[:lower:]' '[:upper:]')
|
|
local part2=$(head -c 3 /dev/urandom | hexdump -e '"%06x"' | tr '[:lower:]' '[:upper:]')
|
|
local part3=$(head -c 3 /dev/urandom | hexdump -e '"%06x"' | tr '[:lower:]' '[:upper:]')
|
|
echo "${part1}-${part2}-${part3}"
|
|
}
|
|
|
|
# Add log entry
|
|
add_log() {
|
|
local event="$1"
|
|
local details="$2"
|
|
local mac="$3"
|
|
|
|
init_storage
|
|
|
|
local timestamp=$(date -Iseconds)
|
|
local entry="{\"timestamp\":\"$timestamp\",\"event\":\"$event\",\"details\":\"$details\",\"mac\":\"$mac\"}"
|
|
|
|
if [ -f "$LOGS_FILE" ]; then
|
|
jq ". += [$entry] | .[-500:]" "$LOGS_FILE" > "${LOGS_FILE}.tmp" 2>/dev/null && mv "${LOGS_FILE}.tmp" "$LOGS_FILE"
|
|
fi
|
|
}
|
|
|
|
# Get system status
|
|
status() {
|
|
init_storage
|
|
|
|
json_init
|
|
|
|
config_load auth_guardian
|
|
local enabled auth_method
|
|
config_get enabled global enabled "0"
|
|
config_get auth_method global auth_method "splash"
|
|
|
|
json_add_boolean "enabled" "$enabled"
|
|
json_add_string "auth_method" "$auth_method"
|
|
|
|
# Count active sessions
|
|
local sessions=0
|
|
if [ -f "$SESSIONS_FILE" ]; then
|
|
sessions=$(jq '[.[] | select(.active == true)] | length' "$SESSIONS_FILE" 2>/dev/null || echo 0)
|
|
fi
|
|
json_add_int "active_sessions" "$sessions"
|
|
|
|
# Count OAuth providers
|
|
local provider_count=0
|
|
config_foreach count_section oauth_provider && provider_count=$?
|
|
json_add_int "oauth_providers" "$provider_count"
|
|
|
|
# Count vouchers
|
|
local voucher_count=0
|
|
local active_vouchers=0
|
|
config_foreach count_vouchers voucher
|
|
json_add_int "total_vouchers" "$voucher_count"
|
|
json_add_int "active_vouchers" "$active_vouchers"
|
|
|
|
# Check nodogsplash
|
|
local captive_portal=0
|
|
pgrep -x nodogsplash >/dev/null 2>&1 && captive_portal=1
|
|
json_add_boolean "captive_portal_active" "$captive_portal"
|
|
|
|
json_dump
|
|
}
|
|
|
|
count_section() {
|
|
return $(( $? + 1 ))
|
|
}
|
|
|
|
count_vouchers() {
|
|
voucher_count=$((voucher_count + 1))
|
|
local used
|
|
config_get used "$1" used "0"
|
|
[ "$used" = "0" ] && active_vouchers=$((active_vouchers + 1))
|
|
}
|
|
|
|
# List OAuth providers
|
|
list_providers() {
|
|
config_load auth_guardian
|
|
json_init
|
|
json_add_array "providers"
|
|
|
|
_add_provider() {
|
|
local name client_id client_secret redirect_uri enabled
|
|
config_get name "$1" name ""
|
|
config_get client_id "$1" client_id ""
|
|
config_get client_secret "$1" client_secret ""
|
|
config_get redirect_uri "$1" redirect_uri ""
|
|
config_get enabled "$1" enabled "0"
|
|
|
|
json_add_object ""
|
|
json_add_string "id" "$1"
|
|
json_add_string "name" "$name"
|
|
json_add_string "client_id" "$client_id"
|
|
json_add_string "client_secret" "${client_secret:0:10}..." # Masked
|
|
json_add_string "redirect_uri" "$redirect_uri"
|
|
json_add_boolean "enabled" "$enabled"
|
|
json_close_object
|
|
}
|
|
|
|
config_foreach _add_provider oauth_provider
|
|
|
|
json_close_array
|
|
json_dump
|
|
}
|
|
|
|
# Set OAuth provider
|
|
set_provider() {
|
|
read -r input
|
|
json_load "$input"
|
|
|
|
local provider name client_id client_secret redirect_uri
|
|
json_get_var provider provider
|
|
json_get_var name name
|
|
json_get_var client_id client_id
|
|
json_get_var client_secret client_secret
|
|
json_get_var redirect_uri redirect_uri
|
|
|
|
if [ -z "$provider" ] || [ -z "$client_id" ]; then
|
|
json_init
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "Provider type and client_id required"
|
|
json_dump
|
|
return
|
|
fi
|
|
|
|
# Create or update provider
|
|
local section_id="oauth_$(echo $provider | tr '[:upper:]' '[:lower:]')"
|
|
|
|
uci -q delete "auth_guardian.${section_id}" 2>/dev/null
|
|
uci set "auth_guardian.${section_id}=oauth_provider"
|
|
uci set "auth_guardian.${section_id}.name=$name"
|
|
uci set "auth_guardian.${section_id}.provider=$provider"
|
|
uci set "auth_guardian.${section_id}.client_id=$client_id"
|
|
uci set "auth_guardian.${section_id}.client_secret=$client_secret"
|
|
uci set "auth_guardian.${section_id}.redirect_uri=$redirect_uri"
|
|
uci set "auth_guardian.${section_id}.enabled=1"
|
|
uci commit auth_guardian
|
|
|
|
add_log "provider_configured" "OAuth provider $name configured" ""
|
|
|
|
json_init
|
|
json_add_boolean "success" 1
|
|
json_add_string "message" "OAuth provider configured"
|
|
json_add_string "provider_id" "$section_id"
|
|
json_dump
|
|
}
|
|
|
|
# Delete OAuth provider
|
|
delete_provider() {
|
|
read -r input
|
|
json_load "$input"
|
|
|
|
local provider_id
|
|
json_get_var provider_id provider_id
|
|
|
|
if [ -z "$provider_id" ]; then
|
|
json_init
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "Provider ID required"
|
|
json_dump
|
|
return
|
|
fi
|
|
|
|
uci -q delete "auth_guardian.${provider_id}" 2>/dev/null
|
|
uci commit auth_guardian
|
|
|
|
add_log "provider_deleted" "OAuth provider $provider_id deleted" ""
|
|
|
|
json_init
|
|
json_add_boolean "success" 1
|
|
json_add_string "message" "Provider deleted"
|
|
json_dump
|
|
}
|
|
|
|
# List all vouchers
|
|
list_vouchers() {
|
|
config_load auth_guardian
|
|
json_init
|
|
json_add_array "vouchers"
|
|
|
|
_add_voucher() {
|
|
local code duration_hours data_limit_mb created used note
|
|
config_get code "$1" code ""
|
|
config_get duration_hours "$1" duration_hours "24"
|
|
config_get data_limit_mb "$1" data_limit_mb "0"
|
|
config_get created "$1" created ""
|
|
config_get used "$1" used "0"
|
|
config_get note "$1" note ""
|
|
config_get used_by "$1" used_by ""
|
|
config_get used_at "$1" used_at ""
|
|
|
|
json_add_object ""
|
|
json_add_string "id" "$1"
|
|
json_add_string "code" "$code"
|
|
json_add_int "duration_hours" "$duration_hours"
|
|
json_add_int "data_limit_mb" "$data_limit_mb"
|
|
json_add_string "created" "$created"
|
|
json_add_boolean "used" "$used"
|
|
json_add_string "note" "$note"
|
|
json_add_string "used_by" "$used_by"
|
|
json_add_string "used_at" "$used_at"
|
|
json_close_object
|
|
}
|
|
|
|
config_foreach _add_voucher voucher
|
|
|
|
json_close_array
|
|
json_dump
|
|
}
|
|
|
|
# Create voucher
|
|
create_voucher() {
|
|
read -r input
|
|
json_load "$input"
|
|
|
|
local duration_hours data_limit_mb note
|
|
json_get_var duration_hours duration_hours
|
|
json_get_var data_limit_mb data_limit_mb
|
|
json_get_var note note
|
|
|
|
# Defaults
|
|
duration_hours=${duration_hours:-24}
|
|
data_limit_mb=${data_limit_mb:-0}
|
|
note=${note:-""}
|
|
|
|
# Generate voucher code
|
|
local code=$(generate_voucher_code)
|
|
local voucher_id="v_$(generate_id)"
|
|
local timestamp=$(date -Iseconds)
|
|
|
|
# Save to UCI
|
|
uci set "auth_guardian.${voucher_id}=voucher"
|
|
uci set "auth_guardian.${voucher_id}.code=$code"
|
|
uci set "auth_guardian.${voucher_id}.duration_hours=$duration_hours"
|
|
uci set "auth_guardian.${voucher_id}.data_limit_mb=$data_limit_mb"
|
|
uci set "auth_guardian.${voucher_id}.created=$timestamp"
|
|
uci set "auth_guardian.${voucher_id}.used=0"
|
|
uci set "auth_guardian.${voucher_id}.note=$note"
|
|
uci commit auth_guardian
|
|
|
|
add_log "voucher_created" "Voucher $code created" ""
|
|
|
|
json_init
|
|
json_add_boolean "success" 1
|
|
json_add_string "message" "Voucher created"
|
|
json_add_string "voucher_id" "$voucher_id"
|
|
json_add_string "code" "$code"
|
|
json_add_int "duration_hours" "$duration_hours"
|
|
json_add_int "data_limit_mb" "$data_limit_mb"
|
|
json_dump
|
|
}
|
|
|
|
# Delete voucher
|
|
delete_voucher() {
|
|
read -r input
|
|
json_load "$input"
|
|
|
|
local voucher_id
|
|
json_get_var voucher_id voucher_id
|
|
|
|
if [ -z "$voucher_id" ]; then
|
|
json_init
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "Voucher ID required"
|
|
json_dump
|
|
return
|
|
fi
|
|
|
|
config_load auth_guardian
|
|
local code
|
|
config_get code "$voucher_id" code ""
|
|
|
|
uci -q delete "auth_guardian.${voucher_id}" 2>/dev/null
|
|
uci commit auth_guardian
|
|
|
|
add_log "voucher_deleted" "Voucher $code deleted" ""
|
|
|
|
json_init
|
|
json_add_boolean "success" 1
|
|
json_add_string "message" "Voucher deleted"
|
|
json_dump
|
|
}
|
|
|
|
# Validate voucher code
|
|
validate_voucher() {
|
|
read -r input
|
|
json_load "$input"
|
|
|
|
local code mac
|
|
json_get_var code code
|
|
json_get_var mac mac
|
|
|
|
if [ -z "$code" ]; then
|
|
json_init
|
|
json_add_boolean "valid" 0
|
|
json_add_string "error" "Voucher code required"
|
|
json_dump
|
|
return
|
|
fi
|
|
|
|
# Search for voucher
|
|
config_load auth_guardian
|
|
local found=0
|
|
local voucher_id=""
|
|
local duration=24
|
|
local data_limit=0
|
|
local used=0
|
|
|
|
_check_voucher() {
|
|
local v_code v_used
|
|
config_get v_code "$1" code ""
|
|
config_get v_used "$1" used "0"
|
|
|
|
if [ "$v_code" = "$code" ]; then
|
|
found=1
|
|
voucher_id="$1"
|
|
config_get duration "$1" duration_hours "24"
|
|
config_get data_limit "$1" data_limit_mb "0"
|
|
used="$v_used"
|
|
fi
|
|
}
|
|
|
|
config_foreach _check_voucher voucher
|
|
|
|
if [ "$found" = "1" ] && [ "$used" = "0" ]; then
|
|
# Mark as used
|
|
local timestamp=$(date -Iseconds)
|
|
uci set "auth_guardian.${voucher_id}.used=1"
|
|
uci set "auth_guardian.${voucher_id}.used_by=$mac"
|
|
uci set "auth_guardian.${voucher_id}.used_at=$timestamp"
|
|
uci commit auth_guardian
|
|
|
|
# Authorize MAC via nodogsplash if available
|
|
if command -v ndsctl >/dev/null 2>&1 && [ -n "$mac" ]; then
|
|
ndsctl auth "$mac" >/dev/null 2>&1
|
|
fi
|
|
|
|
add_log "voucher_used" "Voucher $code validated" "$mac"
|
|
|
|
json_init
|
|
json_add_boolean "valid" 1
|
|
json_add_string "message" "Voucher valid and activated"
|
|
json_add_int "duration_hours" "$duration"
|
|
json_add_int "data_limit_mb" "$data_limit"
|
|
json_dump
|
|
else
|
|
json_init
|
|
json_add_boolean "valid" 0
|
|
json_add_string "error" "Invalid or already used voucher"
|
|
json_dump
|
|
fi
|
|
}
|
|
|
|
# List active sessions
|
|
list_sessions() {
|
|
init_storage
|
|
|
|
json_init
|
|
json_add_array "sessions"
|
|
|
|
# Get sessions from nodogsplash if available
|
|
if command -v ndsctl >/dev/null 2>&1; then
|
|
ndsctl json 2>/dev/null | jq -c '.clients[]?' 2>/dev/null | while IFS= read -r client; do
|
|
local mac=$(echo "$client" | jq -r '.mac // ""')
|
|
local ip=$(echo "$client" | jq -r '.ip // ""')
|
|
local uploaded=$(echo "$client" | jq -r '.uploaded // 0')
|
|
local downloaded=$(echo "$client" | jq -r '.downloaded // 0')
|
|
local duration=$(echo "$client" | jq -r '.duration // 0')
|
|
|
|
json_add_object ""
|
|
json_add_string "mac" "$mac"
|
|
json_add_string "ip" "$ip"
|
|
json_add_int "uploaded_bytes" "$uploaded"
|
|
json_add_int "downloaded_bytes" "$downloaded"
|
|
json_add_int "duration_seconds" "$duration"
|
|
json_add_boolean "active" 1
|
|
json_add_string "auth_method" "voucher"
|
|
json_close_object
|
|
done
|
|
fi
|
|
|
|
# Also add from sessions file
|
|
if [ -f "$SESSIONS_FILE" ]; then
|
|
jq -c '.[]?' "$SESSIONS_FILE" 2>/dev/null | while IFS= read -r session; do
|
|
echo "$session"
|
|
done
|
|
fi
|
|
|
|
json_close_array
|
|
json_dump
|
|
}
|
|
|
|
# Revoke session
|
|
revoke_session() {
|
|
read -r input
|
|
json_load "$input"
|
|
|
|
local mac
|
|
json_get_var mac mac
|
|
|
|
if [ -z "$mac" ]; then
|
|
json_init
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "MAC address required"
|
|
json_dump
|
|
return
|
|
fi
|
|
|
|
# Deauth via nodogsplash
|
|
if command -v ndsctl >/dev/null 2>&1; then
|
|
ndsctl deauth "$mac" >/dev/null 2>&1
|
|
fi
|
|
|
|
# Remove from sessions file
|
|
if [ -f "$SESSIONS_FILE" ]; then
|
|
jq "map(select(.mac != \"$mac\"))" "$SESSIONS_FILE" > "${SESSIONS_FILE}.tmp" 2>/dev/null && mv "${SESSIONS_FILE}.tmp" "$SESSIONS_FILE"
|
|
fi
|
|
|
|
add_log "session_revoked" "Session revoked for $mac" "$mac"
|
|
|
|
json_init
|
|
json_add_boolean "success" 1
|
|
json_add_string "message" "Session revoked"
|
|
json_dump
|
|
}
|
|
|
|
# Get authentication logs
|
|
get_logs() {
|
|
read -r input
|
|
json_load "$input"
|
|
|
|
local limit
|
|
json_get_var limit limit
|
|
limit=${limit:-100}
|
|
|
|
init_storage
|
|
|
|
json_init
|
|
json_add_array "logs"
|
|
|
|
if [ -f "$LOGS_FILE" ]; then
|
|
jq -c ".[-${limit}:][]" "$LOGS_FILE" 2>/dev/null | while IFS= read -r entry; do
|
|
echo "$entry"
|
|
done
|
|
fi
|
|
|
|
json_close_array
|
|
json_dump
|
|
}
|
|
|
|
# Main dispatcher
|
|
case "$1" in
|
|
list)
|
|
cat << 'EOF'
|
|
{
|
|
"status": {},
|
|
"list_providers": {},
|
|
"set_provider": { "provider": "string", "name": "string", "client_id": "string", "client_secret": "string", "redirect_uri": "string" },
|
|
"delete_provider": { "provider_id": "string" },
|
|
"list_vouchers": {},
|
|
"create_voucher": { "duration_hours": 24, "data_limit_mb": 0, "note": "string" },
|
|
"delete_voucher": { "voucher_id": "string" },
|
|
"validate_voucher": { "code": "string", "mac": "string" },
|
|
"list_sessions": {},
|
|
"revoke_session": { "mac": "string" },
|
|
"get_logs": { "limit": 100 }
|
|
}
|
|
EOF
|
|
;;
|
|
call)
|
|
case "$2" in
|
|
status) status ;;
|
|
list_providers) list_providers ;;
|
|
set_provider) set_provider ;;
|
|
delete_provider) delete_provider ;;
|
|
list_vouchers) list_vouchers ;;
|
|
create_voucher) create_voucher ;;
|
|
delete_voucher) delete_voucher ;;
|
|
validate_voucher) validate_voucher ;;
|
|
list_sessions) list_sessions ;;
|
|
revoke_session) revoke_session ;;
|
|
get_logs) get_logs ;;
|
|
*)
|
|
json_init
|
|
json_add_int "error" -32601
|
|
json_add_string "message" "Method not found: $2"
|
|
json_dump
|
|
;;
|
|
esac
|
|
;;
|
|
esac
|