436 lines
14 KiB
Bash
436 lines
14 KiB
Bash
|
|
#!/bin/bash
|
||
|
|
|
||
|
|
# =================================================================
|
||
|
|
# COMPREHENSIVE ADMIN ROLE DEBUG SCRIPT
|
||
|
|
# =================================================================
|
||
|
|
# This script will trace the entire flow of admin role assignment
|
||
|
|
# from registration through JWT token creation to API calls
|
||
|
|
|
||
|
|
# Configuration
|
||
|
|
API_BASE="http://localhost:8000"
|
||
|
|
AUTH_BASE="http://localhost:8001"
|
||
|
|
TEST_EMAIL="debug.admin.test.$(date +%s)@bakery.com"
|
||
|
|
TEST_PASSWORD="DebugPassword123!"
|
||
|
|
TEST_NAME="Debug Admin User"
|
||
|
|
|
||
|
|
# Colors for output
|
||
|
|
RED='\033[0;31m'
|
||
|
|
GREEN='\033[0;32m'
|
||
|
|
YELLOW='\033[1;33m'
|
||
|
|
BLUE='\033[0;34m'
|
||
|
|
PURPLE='\033[0;35m'
|
||
|
|
CYAN='\033[0;36m'
|
||
|
|
NC='\033[0m' # No Color
|
||
|
|
|
||
|
|
echo -e "${CYAN}🔍 COMPREHENSIVE ADMIN ROLE DEBUG${NC}"
|
||
|
|
echo -e "${CYAN}=================================${NC}"
|
||
|
|
echo "Test Email: $TEST_EMAIL"
|
||
|
|
echo "API Base: $API_BASE"
|
||
|
|
echo "Auth Base: $AUTH_BASE"
|
||
|
|
echo ""
|
||
|
|
|
||
|
|
# Utility functions
|
||
|
|
log_step() {
|
||
|
|
echo -e "${BLUE}📋 $1${NC}"
|
||
|
|
}
|
||
|
|
|
||
|
|
log_success() {
|
||
|
|
echo -e "${GREEN}✅ $1${NC}"
|
||
|
|
}
|
||
|
|
|
||
|
|
log_error() {
|
||
|
|
echo -e "${RED}❌ $1${NC}"
|
||
|
|
}
|
||
|
|
|
||
|
|
log_warning() {
|
||
|
|
echo -e "${YELLOW}⚠️ $1${NC}"
|
||
|
|
}
|
||
|
|
|
||
|
|
log_debug() {
|
||
|
|
echo -e "${PURPLE}🐛 DEBUG: $1${NC}"
|
||
|
|
}
|
||
|
|
|
||
|
|
# Function to decode JWT token (requires jq and base64)
|
||
|
|
decode_jwt() {
|
||
|
|
local token="$1"
|
||
|
|
local part="$2" # header=0, payload=1, signature=2
|
||
|
|
|
||
|
|
if [ -z "$token" ]; then
|
||
|
|
echo "No token provided"
|
||
|
|
return 1
|
||
|
|
fi
|
||
|
|
|
||
|
|
# Split token by dots
|
||
|
|
IFS='.' read -ra PARTS <<< "$token"
|
||
|
|
|
||
|
|
if [ ${#PARTS[@]} -ne 3 ]; then
|
||
|
|
echo "Invalid JWT format"
|
||
|
|
return 1
|
||
|
|
fi
|
||
|
|
|
||
|
|
# Decode the specified part (default to payload)
|
||
|
|
local part_index=${part:-1}
|
||
|
|
local encoded_part="${PARTS[$part_index]}"
|
||
|
|
|
||
|
|
# Add padding if needed
|
||
|
|
local padding=$(( 4 - ${#encoded_part} % 4 ))
|
||
|
|
if [ $padding -ne 4 ]; then
|
||
|
|
encoded_part="${encoded_part}$(printf '%*s' $padding | tr ' ' '=')"
|
||
|
|
fi
|
||
|
|
|
||
|
|
# Decode and format as JSON
|
||
|
|
echo "$encoded_part" | base64 -d 2>/dev/null | jq '.' 2>/dev/null || echo "Failed to decode JWT part"
|
||
|
|
}
|
||
|
|
|
||
|
|
# Function to extract JSON field
|
||
|
|
extract_json_field() {
|
||
|
|
local json="$1"
|
||
|
|
local field="$2"
|
||
|
|
echo "$json" | python3 -c "
|
||
|
|
import json, sys
|
||
|
|
try:
|
||
|
|
data = json.load(sys.stdin)
|
||
|
|
print(data.get('$field', ''))
|
||
|
|
except:
|
||
|
|
print('')
|
||
|
|
" 2>/dev/null
|
||
|
|
}
|
||
|
|
|
||
|
|
# =================================================================
|
||
|
|
# STEP 1: VERIFY SERVICES ARE RUNNING
|
||
|
|
# =================================================================
|
||
|
|
|
||
|
|
log_step "Step 1: Verifying services are running"
|
||
|
|
|
||
|
|
# Check API Gateway
|
||
|
|
if ! curl -s "$API_BASE/health" > /dev/null; then
|
||
|
|
log_error "API Gateway is not responding at $API_BASE"
|
||
|
|
echo "Please ensure services are running: docker-compose up -d"
|
||
|
|
exit 1
|
||
|
|
fi
|
||
|
|
log_success "API Gateway is responding"
|
||
|
|
|
||
|
|
# Check Auth Service directly
|
||
|
|
if ! curl -s "$AUTH_BASE/health" > /dev/null; then
|
||
|
|
log_error "Auth Service is not responding at $AUTH_BASE"
|
||
|
|
exit 1
|
||
|
|
fi
|
||
|
|
log_success "Auth Service is responding"
|
||
|
|
|
||
|
|
echo ""
|
||
|
|
|
||
|
|
# =================================================================
|
||
|
|
# STEP 2: USER REGISTRATION WITH DETAILED DEBUGGING
|
||
|
|
# =================================================================
|
||
|
|
|
||
|
|
log_step "Step 2: User Registration with Admin Role"
|
||
|
|
echo "Email: $TEST_EMAIL"
|
||
|
|
echo "Role: admin (explicitly set)"
|
||
|
|
echo ""
|
||
|
|
|
||
|
|
log_debug "Sending registration request..."
|
||
|
|
|
||
|
|
# Registration request with explicit role
|
||
|
|
REGISTER_RESPONSE=$(curl -s -w "\nHTTP_CODE:%{http_code}" -X POST "$API_BASE/api/v1/auth/register" \
|
||
|
|
-H "Content-Type: application/json" \
|
||
|
|
-d "{
|
||
|
|
\"email\": \"$TEST_EMAIL\",
|
||
|
|
\"password\": \"$TEST_PASSWORD\",
|
||
|
|
\"full_name\": \"$TEST_NAME\",
|
||
|
|
\"role\": \"admin\"
|
||
|
|
}")
|
||
|
|
|
||
|
|
# Extract HTTP code and response
|
||
|
|
HTTP_CODE=$(echo "$REGISTER_RESPONSE" | grep "HTTP_CODE:" | cut -d: -f2)
|
||
|
|
REGISTER_RESPONSE=$(echo "$REGISTER_RESPONSE" | sed '/HTTP_CODE:/d')
|
||
|
|
|
||
|
|
echo "Registration HTTP Status: $HTTP_CODE"
|
||
|
|
echo "Registration Response:"
|
||
|
|
echo "$REGISTER_RESPONSE" | python3 -m json.tool 2>/dev/null || echo "$REGISTER_RESPONSE"
|
||
|
|
|
||
|
|
if [ "$HTTP_CODE" != "200" ] && [ "$HTTP_CODE" != "201" ]; then
|
||
|
|
log_error "Registration failed with HTTP $HTTP_CODE"
|
||
|
|
exit 1
|
||
|
|
fi
|
||
|
|
|
||
|
|
# Extract user information from registration response
|
||
|
|
USER_ID=$(extract_json_field "$REGISTER_RESPONSE" "user_id")
|
||
|
|
if [ -z "$USER_ID" ]; then
|
||
|
|
# Try alternative field names
|
||
|
|
USER_ID=$(echo "$REGISTER_RESPONSE" | python3 -c "
|
||
|
|
import json, sys
|
||
|
|
try:
|
||
|
|
data = json.load(sys.stdin)
|
||
|
|
user = data.get('user', {})
|
||
|
|
print(user.get('id', data.get('id', '')))
|
||
|
|
except:
|
||
|
|
print('')
|
||
|
|
" 2>/dev/null)
|
||
|
|
fi
|
||
|
|
|
||
|
|
ACCESS_TOKEN=$(extract_json_field "$REGISTER_RESPONSE" "access_token")
|
||
|
|
REFRESH_TOKEN=$(extract_json_field "$REGISTER_RESPONSE" "refresh_token")
|
||
|
|
|
||
|
|
log_debug "Extracted from registration:"
|
||
|
|
echo " User ID: $USER_ID"
|
||
|
|
echo " Access Token: ${ACCESS_TOKEN:0:50}..."
|
||
|
|
echo " Refresh Token: ${REFRESH_TOKEN:0:50}..."
|
||
|
|
|
||
|
|
echo ""
|
||
|
|
|
||
|
|
# =================================================================
|
||
|
|
# STEP 3: DECODE AND ANALYZE JWT TOKEN
|
||
|
|
# =================================================================
|
||
|
|
|
||
|
|
log_step "Step 3: JWT Token Analysis"
|
||
|
|
|
||
|
|
if [ -n "$ACCESS_TOKEN" ]; then
|
||
|
|
log_debug "Decoding JWT Header:"
|
||
|
|
JWT_HEADER=$(decode_jwt "$ACCESS_TOKEN" 0)
|
||
|
|
echo "$JWT_HEADER"
|
||
|
|
|
||
|
|
echo ""
|
||
|
|
log_debug "Decoding JWT Payload:"
|
||
|
|
JWT_PAYLOAD=$(decode_jwt "$ACCESS_TOKEN" 1)
|
||
|
|
echo "$JWT_PAYLOAD"
|
||
|
|
|
||
|
|
# Extract role from JWT payload
|
||
|
|
JWT_ROLE=$(echo "$JWT_PAYLOAD" | jq -r '.role // "NOT_FOUND"' 2>/dev/null)
|
||
|
|
JWT_USER_ID=$(echo "$JWT_PAYLOAD" | jq -r '.user_id // "NOT_FOUND"' 2>/dev/null)
|
||
|
|
JWT_EMAIL=$(echo "$JWT_PAYLOAD" | jq -r '.email // "NOT_FOUND"' 2>/dev/null)
|
||
|
|
|
||
|
|
echo ""
|
||
|
|
log_debug "JWT Payload Analysis:"
|
||
|
|
echo " Role in JWT: $JWT_ROLE"
|
||
|
|
echo " User ID in JWT: $JWT_USER_ID"
|
||
|
|
echo " Email in JWT: $JWT_EMAIL"
|
||
|
|
|
||
|
|
if [ "$JWT_ROLE" = "admin" ]; then
|
||
|
|
log_success "JWT contains admin role correctly"
|
||
|
|
else
|
||
|
|
log_error "JWT role is '$JWT_ROLE', expected 'admin'"
|
||
|
|
fi
|
||
|
|
else
|
||
|
|
log_error "No access token received in registration response"
|
||
|
|
echo "Cannot analyze JWT"
|
||
|
|
fi
|
||
|
|
|
||
|
|
echo ""
|
||
|
|
|
||
|
|
# =================================================================
|
||
|
|
# STEP 4: VERIFY USER PROFILE ENDPOINT
|
||
|
|
# =================================================================
|
||
|
|
|
||
|
|
log_step "Step 4: User Profile Verification"
|
||
|
|
|
||
|
|
if [ -n "$ACCESS_TOKEN" ]; then
|
||
|
|
log_debug "Calling /users/me endpoint..."
|
||
|
|
|
||
|
|
USER_PROFILE_RESPONSE=$(curl -s -w "\nHTTP_CODE:%{http_code}" -X GET "$API_BASE/api/v1/users/me" \
|
||
|
|
-H "Authorization: Bearer $ACCESS_TOKEN")
|
||
|
|
|
||
|
|
# Extract HTTP code and response
|
||
|
|
HTTP_CODE=$(echo "$USER_PROFILE_RESPONSE" | grep "HTTP_CODE:" | cut -d: -f2)
|
||
|
|
USER_PROFILE_RESPONSE=$(echo "$USER_PROFILE_RESPONSE" | sed '/HTTP_CODE:/d')
|
||
|
|
|
||
|
|
echo "User Profile HTTP Status: $HTTP_CODE"
|
||
|
|
echo "User Profile Response:"
|
||
|
|
echo "$USER_PROFILE_RESPONSE" | python3 -m json.tool 2>/dev/null || echo "$USER_PROFILE_RESPONSE"
|
||
|
|
|
||
|
|
if [ "$HTTP_CODE" = "200" ]; then
|
||
|
|
PROFILE_ROLE=$(extract_json_field "$USER_PROFILE_RESPONSE" "role")
|
||
|
|
PROFILE_EMAIL=$(extract_json_field "$USER_PROFILE_RESPONSE" "email")
|
||
|
|
PROFILE_USER_ID=$(extract_json_field "$USER_PROFILE_RESPONSE" "id")
|
||
|
|
|
||
|
|
echo ""
|
||
|
|
log_debug "Profile endpoint analysis:"
|
||
|
|
echo " Role from profile: $PROFILE_ROLE"
|
||
|
|
echo " Email from profile: $PROFILE_EMAIL"
|
||
|
|
echo " User ID from profile: $PROFILE_USER_ID"
|
||
|
|
|
||
|
|
if [ "$PROFILE_ROLE" = "admin" ]; then
|
||
|
|
log_success "Profile endpoint shows admin role correctly"
|
||
|
|
else
|
||
|
|
log_error "Profile endpoint shows role '$PROFILE_ROLE', expected 'admin'"
|
||
|
|
fi
|
||
|
|
else
|
||
|
|
log_error "Failed to get user profile (HTTP $HTTP_CODE)"
|
||
|
|
fi
|
||
|
|
else
|
||
|
|
log_error "No access token available for profile verification"
|
||
|
|
fi
|
||
|
|
|
||
|
|
echo ""
|
||
|
|
|
||
|
|
# =================================================================
|
||
|
|
# STEP 5: DIRECT DATABASE VERIFICATION (if possible)
|
||
|
|
# =================================================================
|
||
|
|
|
||
|
|
log_step "Step 5: Database Verification"
|
||
|
|
|
||
|
|
log_debug "Attempting to verify user role in database..."
|
||
|
|
|
||
|
|
# Try to connect to the auth database directly
|
||
|
|
DB_QUERY_RESULT=$(docker exec bakery-auth-db psql -U auth_user -d auth_db -t -c "SELECT id, email, role, created_at FROM users WHERE email='$TEST_EMAIL';" 2>/dev/null || echo "DB_ACCESS_FAILED")
|
||
|
|
|
||
|
|
if [ "$DB_QUERY_RESULT" != "DB_ACCESS_FAILED" ]; then
|
||
|
|
echo "Database Query Result:"
|
||
|
|
echo "$DB_QUERY_RESULT"
|
||
|
|
|
||
|
|
# Parse the database result
|
||
|
|
DB_ROLE=$(echo "$DB_QUERY_RESULT" | awk -F'|' '{print $3}' | tr -d ' ')
|
||
|
|
|
||
|
|
log_debug "Role in database: '$DB_ROLE'"
|
||
|
|
|
||
|
|
if [ "$DB_ROLE" = "admin" ]; then
|
||
|
|
log_success "Database shows admin role correctly"
|
||
|
|
else
|
||
|
|
log_error "Database shows role '$DB_ROLE', expected 'admin'"
|
||
|
|
fi
|
||
|
|
else
|
||
|
|
log_warning "Cannot access database directly (this is normal in some setups)"
|
||
|
|
echo "You can manually check with:"
|
||
|
|
echo " docker exec bakery-auth-db psql -U auth_user -d auth_db -c \"SELECT id, email, role FROM users WHERE email='$TEST_EMAIL';\""
|
||
|
|
fi
|
||
|
|
|
||
|
|
echo ""
|
||
|
|
|
||
|
|
# =================================================================
|
||
|
|
# STEP 6: TEST ADMIN ENDPOINT ACCESS
|
||
|
|
# =================================================================
|
||
|
|
|
||
|
|
log_step "Step 6: Testing Admin Endpoint Access"
|
||
|
|
|
||
|
|
if [ -n "$ACCESS_TOKEN" ] && [ -n "$USER_ID" ]; then
|
||
|
|
log_debug "Attempting to call admin deletion preview endpoint..."
|
||
|
|
|
||
|
|
ADMIN_TEST_RESPONSE=$(curl -s -w "\nHTTP_CODE:%{http_code}" -X GET "$API_BASE/api/v1/users/delete/$USER_ID/deletion-preview" \
|
||
|
|
-H "Authorization: Bearer $ACCESS_TOKEN")
|
||
|
|
|
||
|
|
# Extract HTTP code and response
|
||
|
|
HTTP_CODE=$(echo "$ADMIN_TEST_RESPONSE" | grep "HTTP_CODE:" | cut -d: -f2)
|
||
|
|
ADMIN_TEST_RESPONSE=$(echo "$ADMIN_TEST_RESPONSE" | sed '/HTTP_CODE:/d')
|
||
|
|
|
||
|
|
echo "Admin Endpoint HTTP Status: $HTTP_CODE"
|
||
|
|
echo "Admin Endpoint Response:"
|
||
|
|
echo "$ADMIN_TEST_RESPONSE" | python3 -m json.tool 2>/dev/null || echo "$ADMIN_TEST_RESPONSE"
|
||
|
|
|
||
|
|
case "$HTTP_CODE" in
|
||
|
|
"200")
|
||
|
|
log_success "Admin endpoint access successful!"
|
||
|
|
;;
|
||
|
|
"403")
|
||
|
|
log_error "Access denied - user does not have admin privileges"
|
||
|
|
echo "This confirms the role issue!"
|
||
|
|
;;
|
||
|
|
"401")
|
||
|
|
log_error "Authentication failed - token may be invalid"
|
||
|
|
;;
|
||
|
|
*)
|
||
|
|
log_warning "Unexpected response from admin endpoint (HTTP $HTTP_CODE)"
|
||
|
|
;;
|
||
|
|
esac
|
||
|
|
else
|
||
|
|
log_error "Cannot test admin endpoint - missing access token or user ID"
|
||
|
|
fi
|
||
|
|
|
||
|
|
echo ""
|
||
|
|
|
||
|
|
# =================================================================
|
||
|
|
# STEP 7: AUTH SERVICE LOGS ANALYSIS
|
||
|
|
# =================================================================
|
||
|
|
|
||
|
|
log_step "Step 7: Auth Service Logs Analysis"
|
||
|
|
|
||
|
|
log_debug "Checking recent auth service logs..."
|
||
|
|
|
||
|
|
AUTH_LOGS=$(docker logs --tail 50 bakery-auth-service 2>/dev/null | grep -E "(register|role|admin|$TEST_EMAIL)" || echo "NO_LOGS_FOUND")
|
||
|
|
|
||
|
|
if [ "$AUTH_LOGS" != "NO_LOGS_FOUND" ]; then
|
||
|
|
echo "Recent Auth Service Logs (filtered):"
|
||
|
|
echo "$AUTH_LOGS"
|
||
|
|
else
|
||
|
|
log_warning "Cannot access auth service logs"
|
||
|
|
echo "You can check manually with:"
|
||
|
|
echo " docker logs bakery-auth-service | grep -E \"(register|role|admin)\""
|
||
|
|
fi
|
||
|
|
|
||
|
|
echo ""
|
||
|
|
|
||
|
|
# =================================================================
|
||
|
|
# STEP 8: SUMMARY AND RECOMMENDATIONS
|
||
|
|
# =================================================================
|
||
|
|
|
||
|
|
log_step "Step 8: Debug Summary and Recommendations"
|
||
|
|
|
||
|
|
echo -e "${CYAN}🔍 DEBUG SUMMARY${NC}"
|
||
|
|
echo -e "${CYAN}===============${NC}"
|
||
|
|
|
||
|
|
echo ""
|
||
|
|
echo "Test User Details:"
|
||
|
|
echo " Email: $TEST_EMAIL"
|
||
|
|
echo " Expected Role: admin"
|
||
|
|
echo " User ID: ${USER_ID:-'NOT_EXTRACTED'}"
|
||
|
|
|
||
|
|
echo ""
|
||
|
|
echo "Token Analysis:"
|
||
|
|
echo " Access Token Received: $([ -n "$ACCESS_TOKEN" ] && echo 'YES' || echo 'NO')"
|
||
|
|
echo " Role in JWT: ${JWT_ROLE:-'NOT_FOUND'}"
|
||
|
|
echo " JWT User ID: ${JWT_USER_ID:-'NOT_FOUND'}"
|
||
|
|
|
||
|
|
echo ""
|
||
|
|
echo "API Responses:"
|
||
|
|
echo " Registration HTTP: ${HTTP_CODE:-'UNKNOWN'}"
|
||
|
|
echo " Profile Role: ${PROFILE_ROLE:-'NOT_CHECKED'}"
|
||
|
|
echo " Admin Endpoint Access: $([ -n "$ADMIN_TEST_RESPONSE" ] && echo "TESTED" || echo "NOT_TESTED")"
|
||
|
|
|
||
|
|
echo ""
|
||
|
|
echo "Database Verification:"
|
||
|
|
echo " DB Role: ${DB_ROLE:-'NOT_CHECKED'}"
|
||
|
|
|
||
|
|
echo ""
|
||
|
|
log_debug "Potential Issues:"
|
||
|
|
|
||
|
|
# Issue 1: Role not in JWT
|
||
|
|
if [ "$JWT_ROLE" != "admin" ]; then
|
||
|
|
echo " ❌ Role not correctly encoded in JWT token"
|
||
|
|
echo " → Check auth service JWT creation logic"
|
||
|
|
echo " → Verify SecurityManager.create_access_token includes role"
|
||
|
|
fi
|
||
|
|
|
||
|
|
# Issue 2: Registration not setting role
|
||
|
|
if [ "$PROFILE_ROLE" != "admin" ]; then
|
||
|
|
echo " ❌ User profile doesn't show admin role"
|
||
|
|
echo " → Check user registration saves role correctly"
|
||
|
|
echo " → Verify database schema allows role field"
|
||
|
|
fi
|
||
|
|
|
||
|
|
# Issue 3: Admin endpoint still denies access
|
||
|
|
if [ "$HTTP_CODE" = "403" ]; then
|
||
|
|
echo " ❌ Admin endpoint denies access despite admin role"
|
||
|
|
echo " → Check require_admin_role_dep implementation"
|
||
|
|
echo " → Verify role extraction from token in auth decorators"
|
||
|
|
fi
|
||
|
|
|
||
|
|
echo ""
|
||
|
|
echo -e "${YELLOW}🔧 RECOMMENDED NEXT STEPS:${NC}"
|
||
|
|
echo "1. Check the auth service logs during registration"
|
||
|
|
echo "2. Verify the database schema has a 'role' column"
|
||
|
|
echo "3. Check if the JWT creation includes the role field"
|
||
|
|
echo "4. Verify the role extraction in get_current_user_dep"
|
||
|
|
echo "5. Test with a direct database role update if needed"
|
||
|
|
|
||
|
|
echo ""
|
||
|
|
echo -e "${CYAN}🧪 Manual Verification Commands:${NC}"
|
||
|
|
echo "# Check database directly:"
|
||
|
|
echo "docker exec bakery-auth-db psql -U auth_user -d auth_db -c \"SELECT id, email, role FROM users WHERE email='$TEST_EMAIL';\""
|
||
|
|
echo ""
|
||
|
|
echo "# Check auth service logs:"
|
||
|
|
echo "docker logs bakery-auth-service | grep -A5 -B5 '$TEST_EMAIL'"
|
||
|
|
echo ""
|
||
|
|
echo "# Decode JWT manually:"
|
||
|
|
echo "echo '$ACCESS_TOKEN' | cut -d. -f2 | base64 -d | jq ."
|
||
|
|
|
||
|
|
echo ""
|
||
|
|
log_success "Debug script completed!"
|
||
|
|
echo -e "${YELLOW}Review the analysis above to identify the root cause.${NC}"
|