Add LearnHouse Deployment Isolation Toolkit and debugging tools

- Introduced comprehensive documentation for diagnosing and fixing deployment isolation issues between DEV and LIVE instances.
- Implemented enhanced debug API endpoints for deployment verification, URL hardcoding detection, cookie isolation testing, and session configuration checks.
- Created scripts for visual demonstration of cookie isolation, enhanced debugging deployment, and verification of NextAuth cookie isolation.
- Developed a master isolation verification script to run all isolation checks in sequence and summarize results.
- Added detailed README and environment variable guidelines for proper deployment isolation.
This commit is contained in:
WhiteX 2025-06-13 00:12:15 +03:00 committed by rzmk
parent 2ace169ab1
commit d32389a8ef
14 changed files with 1571 additions and 39 deletions

View file

@ -143,9 +143,31 @@ Added comprehensive debug endpoints:
After implementing these changes: After implementing these changes:
1. Run `./verify-isolation.sh` to check deployment isolation 1. **Deploy the enhanced debug tools**:
2. Verify each deployment has its own database connection using `/api/v1/debug/deployment` endpoint ```bash
3. Check that no cross-domain references appear in the frontend ./deploy-enhanced-debug.sh
```
2. **Run the enhanced verification script** for comprehensive isolation checks:
```bash
./verify-enhanced-isolation.sh
```
3. **Verify cookie isolation** using the dedicated cookie debug endpoint:
```bash
curl -v https://adr-lms.whitex.cloud/api/v1/debug/cookies
curl -v https://edu.adradviser.ro/api/v1/debug/cookies
```
4. **Check session configuration** to ensure proper isolation:
```bash
curl https://adr-lms.whitex.cloud/api/v1/debug/session
curl https://edu.adradviser.ro/api/v1/debug/session
```
5. **Test with incognito browsers** to ensure sessions don't cross-contaminate between deployments
See `ENHANCED_DEBUG_TOOLS.md` for more detailed information on these debugging endpoints.
## Implementation Plan ## Implementation Plan

123
ENHANCED_DEBUG_TOOLS.md Normal file
View file

@ -0,0 +1,123 @@
# Enhanced Debug Endpoints for Isolation Troubleshooting
This document describes the enhanced debugging tools available to diagnose cross-deployment isolation issues between DEV and LIVE environments.
## Available Debug Endpoints
The following endpoints are available for diagnosing isolation issues:
### 1. Deployment Information
**Endpoint:** `/api/v1/debug/deployment`
This endpoint provides detailed information about the current deployment, including:
- Deployment name
- Hostname and container ID
- Database connection details (host, user, database name)
- Redis connection details
- Cookie domain configuration
- Environment variables related to deployment URLs
**Example Usage:**
```bash
curl https://adr-lms.whitex.cloud/api/v1/debug/deployment
```
### 2. URL Hardcoding Detection
**Endpoint:** `/api/v1/debug/urls`
This endpoint scans the NextJS bundle for hardcoded URLs that could lead to cross-contamination between deployments:
- Detects references to both DEV and LIVE domains
- Identifies NextAuth configuration issues
- Shows environment variables related to API URLs
**Example Usage:**
```bash
curl https://adr-lms.whitex.cloud/api/v1/debug/urls
```
### 3. Cookie Isolation Testing
**Endpoint:** `/api/v1/debug/cookies`
This endpoint tests cookie isolation by:
- Setting a test cookie with the current deployment name
- Detecting any test cookies from other deployments
- Reporting the cookie domain in use
- Showing headers from the request
**Example Usage:**
```bash
curl https://adr-lms.whitex.cloud/api/v1/debug/cookies -v
```
### 4. Session Configuration Check
**Endpoint:** `/api/v1/debug/session`
This endpoint examines session-related configuration:
- Shows request headers related to origins
- Reports NextAuth URL configuration
- Indicates where session requests would be sent
**Example Usage:**
```bash
curl https://adr-lms.whitex.cloud/api/v1/debug/session
```
## Using the Enhanced Verification Script
We've also created an enhanced isolation verification script that checks both DEV and LIVE deployments and provides a summary of their isolation status:
```bash
./verify-enhanced-isolation.sh
```
The script will:
1. Check deployment configuration on both environments
2. Test cookie isolation
3. Look for hardcoded URLs
4. Verify session configuration
5. Analyze database isolation
6. Provide a summary of isolation status
## Resolving Isolation Issues
If the verification indicates isolation issues, ensure the following settings are unique between deployments:
1. **Database Connections**: Each deployment should use its own database
```
# DEV environment
LEARNHOUSE_SQL_CONNECTION_STRING=postgresql://learnhouse_dev:YOUR_DEV_PASSWORD@db-dev:5432/learnhouse_dev
# LIVE environment
LEARNHOUSE_SQL_CONNECTION_STRING=postgresql://learnhouse:YOUR_LIVE_PASSWORD@db-live:5432/learnhouse
```
2. **Cookie Domains**: Each deployment should have its own cookie domain
```
# DEV environment
LEARNHOUSE_COOKIE_DOMAIN=adr-lms.whitex.cloud
# LIVE environment
LEARNHOUSE_COOKIE_DOMAIN=edu.adradviser.ro
```
3. **Top Domain Configuration**: Ensure each deployment has the correct top domain
```
# DEV environment
NEXT_PUBLIC_LEARNHOUSE_TOP_DOMAIN=whitex.cloud
# LIVE environment
NEXT_PUBLIC_LEARNHOUSE_TOP_DOMAIN=adradviser.ro
```
4. **Deployment Name**: Set a descriptive name to help with debugging
```
# DEV environment
DEPLOYMENT_NAME=DEV
# LIVE environment
DEPLOYMENT_NAME=LIVE
```

View file

@ -18,7 +18,12 @@ We've identified that both DEV and LIVE deployments are accessing the same datab
git pull origin dev git pull origin dev
``` ```
- [ ] Verify the debug endpoint files exist: - [ ] Deploy the enhanced debug tools first:
```bash
./deploy-enhanced-debug.sh
```
- [ ] Verify the enhanced debug endpoints are working:
```bash ```bash
ls -la apps/api/src/routers/debug.py ls -la apps/api/src/routers/debug.py
``` ```
@ -80,19 +85,37 @@ We've identified that both DEV and LIVE deployments are accessing the same datab
### Step 5: Verification ### Step 5: Verification
- [ ] Run verification scripts: - [ ] Run the comprehensive isolation verification script:
```bash ```bash
./verify-isolation.sh ./verify-all-isolation.sh
./verify-db-isolation.sh ```
This will:
- Check deployment configuration
- Verify database isolation
- Test cookie isolation
- Check for hardcoded URLs
- Create a visual cookie isolation demo
- [ ] For more detailed verification, run individual scripts:
```bash
./verify-enhanced-isolation.sh # Basic deployment checks
./verify-db-isolation.sh # Database-specific checks
./test-nextauth-cookie-isolation.sh # Cookie isolation tests
```
- [ ] Test the visual cookie isolation demo:
```bash
./create-cookie-demo.sh
# Open the resulting HTML file in a browser
``` ```
- [ ] Access debug endpoints directly: - [ ] Access debug endpoints directly:
- DEV: https://adr-lms.whitex.cloud/api/v1/debug/deployment - DEV: https://adr-lms.whitex.cloud/api/v1/debug/deployment
- LIVE: https://edu.adradviser.ro/api/v1/debug/deployment - LIVE: https://edu.adradviser.ro/api/v1/debug/deployment
- DEV: https://adr-lms.whitex.cloud/api/v1/debug/cookies
- [ ] Check URLs in frontend: - LIVE: https://edu.adradviser.ro/api/v1/debug/cookies
- DEV: https://adr-lms.whitex.cloud/api/v1/debug/urls - DEV: https://adr-lms.whitex.cloud/api/v1/debug/session
- LIVE: https://edu.adradviser.ro/api/v1/debug/urls - LIVE: https://edu.adradviser.ro/api/v1/debug/session
- [ ] Test in incognito browsers to verify session isolation - [ ] Test in incognito browsers to verify session isolation
@ -100,20 +123,30 @@ We've identified that both DEV and LIVE deployments are accessing the same datab
If isolation issues persist after implementation: If isolation issues persist after implementation:
1. **Verify Database Connections**: 1. **Use the Enhanced Debug Tools**:
- Confirm debug endpoints show different database hosts - Look at the detailed reports in `/tmp/learnhouse-isolation-report/`
- Run specific tests: `./test-nextauth-cookie-isolation.sh` for cookie issues
- Check session configuration: `/api/v1/debug/session` endpoint
2. **Verify Database Connections**:
- Confirm debug endpoints show different database hosts and names
- Check actual database servers to confirm connections come from different sources - Check actual database servers to confirm connections come from different sources
- Test with the database isolation script: `./verify-db-isolation.sh`
2. **Clear Browser Data**: 3. **Clear Browser Data**:
- Use incognito mode or clear all cookies/cache for proper testing - Use incognito mode or clear all cookies/cache for proper testing
- Try the cookie isolation demo to visually check cookie behavior
3. **Check Docker Network Isolation**: 4. **Check Docker Network Isolation**:
- Ensure each deployment uses its own Docker network - Ensure each deployment uses its own Docker network
- Verify hostnames resolve to different IP addresses within containers - Verify hostnames resolve to different IP addresses within containers
4. **Validate URL Patching**: 5. **Validate URL Patching**:
- Run URL debug endpoint to confirm no hardcoded references remain - Run URL debug endpoint to confirm no hardcoded references remain
- Check the enhanced URL report in `/api/v1/debug/urls` endpoint
For additional help, refer to the full documentation in: For additional help, refer to the full documentation in:
- `DATABASE_ISOLATION_FIX.md` - `ENHANCED_DEBUG_TOOLS.md` - Detailed guide to all debug endpoints
- `DEPLOYMENT_TROUBLESHOOTING.md` - `DATABASE_ISOLATION_FIX.md` - Database isolation specifics
- `DEPLOYMENT_TROUBLESHOOTING.md` - General deployment troubleshooting
- `ISOLATION_TOOLKIT_README.md` - Overview of all isolation tools

74
ISOLATION_IMPROVEMENTS.md Normal file
View file

@ -0,0 +1,74 @@
# LearnHouse Isolation Fix: Improvements Summary
We've developed a comprehensive set of tools, scripts, and documentation to help diagnose and fix deployment isolation issues between DEV and LIVE instances. Here's a summary of the improvements:
## 1. Enhanced Debug Endpoints
We expanded the API debug capabilities significantly:
- **`/api/v1/debug/deployment`**: Enhanced with detailed database, Redis, container, and hostname information
- **`/api/v1/debug/urls`**: Improved to detect cross-contamination from both domains and categorize findings
- **`/api/v1/debug/cookies`**: New endpoint to test cookie isolation and detect cross-deployment cookies
- **`/api/v1/debug/session`**: New endpoint to check session configuration and origins
## 2. Verification Scripts
We created several verification scripts for different aspects of isolation:
- **`verify-enhanced-isolation.sh`**: Comprehensive isolation checks for all aspects of deployment
- **`test-nextauth-cookie-isolation.sh`**: Focused testing for NextAuth cookie isolation
- **`verify-all-isolation.sh`**: Master script that runs all verification checks and produces a report
- **`create-cookie-demo.sh`**: Visual tool to demonstrate and test cookie behavior in browsers
## 3. Documentation
- **`ENHANCED_DEBUG_TOOLS.md`**: Detailed guide to all debug endpoints and how to use them
- **`ISOLATION_TOOLKIT_README.md`**: Overview of all tools available for isolation testing
- **Updated `ISOLATION_IMPLEMENTATION_CHECKLIST.md`**: Comprehensive checklist with new tools
- **Updated `DATABASE_ISOLATION_FIX.md`**: Enhanced verification methods
## 4. Developer Experience
- Visual cookie isolation demo with browser-based testing
- HTML reports for easy sharing and analyzing of results
- Colored terminal output for easy interpretation of verification results
- Container and hostname information for infrastructure verification
## 5. Implementation Details
The specific code improvements include:
1. **Enhanced database information**:
- Now shows database username, hostname and database name
- Extracts Redis instance information
2. **Cookie isolation testing**:
- Sets test cookies with deployment name
- Checks if cookies from one deployment are visible to another
- Visual browser-based tool to demonstrate isolation
3. **Session configuration verification**:
- Analyzes headers and environment variables that affect session behavior
- Shows where sessions would be sent based on current configuration
4. **Comprehensive URL checking**:
- Categorizes URLs by domain to identify cross-contamination
- Reports specific instances of hardcoded URLs in the frontend
## Usage
To use these enhanced tools:
1. Deploy the enhanced debug module:
```bash
./deploy-enhanced-debug.sh
```
2. Run the comprehensive verification:
```bash
./verify-all-isolation.sh
```
3. Check the reports generated in `/tmp/learnhouse-isolation-report/`
These enhancements will make it much easier to diagnose and fix isolation issues between the DEV and LIVE LearnHouse deployments.

107
ISOLATION_TOOLKIT_README.md Normal file
View file

@ -0,0 +1,107 @@
# LearnHouse Deployment Isolation Toolkit
This toolkit provides comprehensive tools and documentation for diagnosing and fixing deployment isolation issues between DEV and LIVE LearnHouse instances.
## Background
We identified cross-deployment contamination issues between DEV (adr-lms.whitex.cloud) and LIVE (edu.adradviser.ro) deployments, including:
- Data contamination (both deployments accessing the same database)
- Session mixing (cookies being shared between deployments)
- Inconsistent user experience (hardcoded URLs pointing to the wrong environment)
## Isolation Toolkit Components
### Documentation
1. **[ISOLATION_IMPLEMENTATION_CHECKLIST.md](./ISOLATION_IMPLEMENTATION_CHECKLIST.md)**
Step-by-step checklist for implementing complete isolation between deployments.
2. **[DATABASE_ISOLATION_FIX.md](./DATABASE_ISOLATION_FIX.md)**
Detailed explanation of database isolation issues and how to fix them.
3. **[DEPLOYMENT_TROUBLESHOOTING.md](./DEPLOYMENT_TROUBLESHOOTING.md)**
Guide for troubleshooting deployment and configuration issues.
4. **[ENHANCED_DEBUG_TOOLS.md](./ENHANCED_DEBUG_TOOLS.md)**
Documentation for the enhanced debugging endpoints.
5. **[COOLIFY_ENV_VARS.md](./COOLIFY_ENV_VARS.md)**
Environment variable templates for Coolify deployments.
### Debugging Tools
1. **Debug API Endpoints**
- `/api/v1/debug/deployment` - Get detailed deployment configuration
- `/api/v1/debug/urls` - Scan for hardcoded URLs in the frontend bundle
- `/api/v1/debug/cookies` - Test cookie isolation between deployments
- `/api/v1/debug/session` - Verify session configuration
2. **Verification Scripts**
- `verify-isolation.sh` - Basic isolation verification
- `verify-enhanced-isolation.sh` - Comprehensive isolation checks
- `verify-db-isolation.sh` - Database-specific isolation verification
- `test-nextauth-cookie-isolation.sh` - NextAuth cookie isolation test
3. **Visual Demonstration Tools**
- `create-cookie-demo.sh` - Creates an HTML tool to visually demonstrate cookie isolation
### Deployment Scripts
1. **`deploy-enhanced-debug.sh`**
Deploys the enhanced debugging endpoints to diagnose isolation issues.
2. **`deploy-isolation-fix.sh`**
Applies the complete isolation fixes to both deployments.
## Getting Started
1. **Deploy debugging tools:**
```bash
./deploy-enhanced-debug.sh
```
2. **Run the enhanced isolation verification:**
```bash
./verify-enhanced-isolation.sh
```
3. **Test cookie isolation:**
```bash
./test-nextauth-cookie-isolation.sh
```
4. **Create a visual cookie isolation demo:**
```bash
./create-cookie-demo.sh
# Then open cookie-isolation-demo.html in your browser
```
5. **Follow the implementation checklist:**
See [ISOLATION_IMPLEMENTATION_CHECKLIST.md](./ISOLATION_IMPLEMENTATION_CHECKLIST.md)
## Key Environment Variables for Isolation
For proper isolation, ensure each deployment has unique values for these variables:
### DEV Environment
```
DEPLOYMENT_NAME=DEV
LEARNHOUSE_SQL_CONNECTION_STRING=postgresql://learnhouse_dev:PASSWORD@db-dev:5432/learnhouse_dev
LEARNHOUSE_COOKIE_DOMAIN=adr-lms.whitex.cloud
NEXT_PUBLIC_LEARNHOUSE_DOMAIN=adr-lms.whitex.cloud
NEXT_PUBLIC_LEARNHOUSE_TOP_DOMAIN=whitex.cloud
```
### LIVE Environment
```
DEPLOYMENT_NAME=LIVE
LEARNHOUSE_SQL_CONNECTION_STRING=postgresql://learnhouse:PASSWORD@db-live:5432/learnhouse
LEARNHOUSE_COOKIE_DOMAIN=edu.adradviser.ro
NEXT_PUBLIC_LEARNHOUSE_DOMAIN=edu.adradviser.ro
NEXT_PUBLIC_LEARNHOUSE_TOP_DOMAIN=adradviser.ro
```
## Troubleshooting
If you encounter issues during the isolation process, refer to [DEPLOYMENT_TROUBLESHOOTING.md](./DEPLOYMENT_TROUBLESHOOTING.md).

View file

@ -2,7 +2,9 @@ import os
from fastapi import APIRouter, Depends from fastapi import APIRouter, Depends
from src.routers import health from src.routers import health
from src.routers import usergroups from src.routers import usergroups
from src.routers import dev, trail, users, auth, orgs, roles, search, debug from src.routers import dev, trail, users, auth, orgs, roles, search
# Use enhanced debug module with improved isolation diagnostics
from src.routers import debug_enhanced as debug
from src.routers.ai import ai from src.routers.ai import ai
from src.routers.courses import chapters, collections, courses, assignments, certifications from src.routers.courses import chapters, collections, courses, assignments, certifications
from src.routers.courses.activities import activities, blocks from src.routers.courses.activities import activities, blocks

View file

@ -1,6 +1,8 @@
import os import os
import json
import socket
import subprocess import subprocess
from fastapi import APIRouter, Request from fastapi import APIRouter, Request, Response
from config.config import get_learnhouse_config from config.config import get_learnhouse_config
router = APIRouter() router = APIRouter()
@ -12,18 +14,32 @@ async def debug_deployment():
# Parse database host safely # Parse database host safely
db_host = "unknown" db_host = "unknown"
db_user = "unknown"
db_name = "unknown"
if '@' in learnhouse_config.database_config.sql_connection_string: if '@' in learnhouse_config.database_config.sql_connection_string:
try: try:
parts = learnhouse_config.database_config.sql_connection_string.split('@') # Split out username and host parts
if len(parts) > 1: auth_host_parts = learnhouse_config.database_config.sql_connection_string.split('@')
host_parts = parts[1].split('/') if len(auth_host_parts) > 1:
# Extract username
auth_parts = auth_host_parts[0].split('//')
if len(auth_parts) > 1:
user_parts = auth_parts[1].split(':')
if len(user_parts) > 0:
db_user = user_parts[0]
# Extract host and database name
host_parts = auth_host_parts[1].split('/')
if len(host_parts) > 0: if len(host_parts) > 0:
db_host = host_parts[0] db_host = host_parts[0]
except Exception: if len(host_parts) > 1:
db_name = host_parts[1]
except Exception as e:
pass pass
# Parse redis host safely # Parse redis host safely
redis_host = "unknown" redis_host = "unknown"
redis_db = "unknown"
if '@' in learnhouse_config.redis_config.redis_connection_string: if '@' in learnhouse_config.redis_config.redis_connection_string:
try: try:
parts = learnhouse_config.redis_config.redis_connection_string.split('@') parts = learnhouse_config.redis_config.redis_connection_string.split('@')
@ -31,19 +47,51 @@ async def debug_deployment():
host_parts = parts[1].split(':') host_parts = parts[1].split(':')
if len(host_parts) > 0: if len(host_parts) > 0:
redis_host = host_parts[0] redis_host = host_parts[0]
if len(host_parts) > 1 and '/' in host_parts[1]:
redis_db = host_parts[1].split('/')[1]
except Exception: except Exception:
pass pass
# Get hostname information
import socket
hostname = "unknown"
try:
hostname = socket.gethostname()
except:
pass
# Get process and container info
container_id = "unknown"
try:
# Try to get container ID from cgroup
with open('/proc/self/cgroup', 'r') as f:
for line in f:
if 'docker' in line:
container_id = line.strip().split('/')[-1]
break
except:
pass
return { return {
"deployment_name": os.environ.get('DEPLOYMENT_NAME', 'NOT_SET'), "deployment_name": os.environ.get('DEPLOYMENT_NAME', 'NOT_SET'),
"hostname": hostname,
"container_id": container_id,
"cookie_domain": learnhouse_config.hosting_config.cookie_config.domain, "cookie_domain": learnhouse_config.hosting_config.cookie_config.domain,
"api_domain": learnhouse_config.hosting_config.domain, "api_domain": learnhouse_config.hosting_config.domain,
"database_host": db_host, "database": {
"redis_host": redis_host, "host": db_host,
"database_name": learnhouse_config.database_config.sql_connection_string.split('/')[-1] if '/' in learnhouse_config.database_config.sql_connection_string else "unknown", "user": db_user,
"name": db_name,
},
"redis": {
"host": redis_host,
"db": redis_db
},
"env_vars": { "env_vars": {
"NEXT_PUBLIC_LEARNHOUSE_DOMAIN": os.environ.get('NEXT_PUBLIC_LEARNHOUSE_DOMAIN', 'NOT_SET'), "NEXT_PUBLIC_LEARNHOUSE_DOMAIN": os.environ.get('NEXT_PUBLIC_LEARNHOUSE_DOMAIN', 'NOT_SET'),
"NEXT_PUBLIC_LEARNHOUSE_API_URL": os.environ.get('NEXT_PUBLIC_LEARNHOUSE_API_URL', 'NOT_SET') "NEXT_PUBLIC_LEARNHOUSE_API_URL": os.environ.get('NEXT_PUBLIC_LEARNHOUSE_API_URL', 'NOT_SET'),
"NEXT_PUBLIC_LEARNHOUSE_TOP_DOMAIN": os.environ.get('NEXT_PUBLIC_LEARNHOUSE_TOP_DOMAIN', 'NOT_SET'),
"LEARNHOUSE_COOKIE_DOMAIN": os.environ.get('LEARNHOUSE_COOKIE_DOMAIN', 'NOT_SET')
} }
} }
@ -51,21 +99,106 @@ async def debug_deployment():
async def debug_urls(request: Request): async def debug_urls(request: Request):
"""Debug endpoint to detect hardcoded URLs in NextJS bundle""" """Debug endpoint to detect hardcoded URLs in NextJS bundle"""
try: try:
# This only works if Next.js files are accessible from the API container # Define domains to check for hardcoding
result = subprocess.run( known_domains = [
["find", "/app/web/.next", "-type", "f", "-name", "*.js", "-exec", "grep", "-o", "http://edu.adradviser.ro[^\"']*", "{}", ";"], "edu.adradviser.ro", # LIVE domain
capture_output=True, text=True, timeout=5 "adr-lms.whitex.cloud" # DEV domain
) ]
hardcoded_urls = list(set(result.stdout.strip().split('\n'))) if result.stdout.strip() else []
# Get the current domain for comparison # Add any additional domains from environment variables
current_domain = os.environ.get('NEXT_PUBLIC_LEARNHOUSE_DOMAIN', 'unknown') current_domain = os.environ.get('NEXT_PUBLIC_LEARNHOUSE_DOMAIN', '')
if current_domain and current_domain not in known_domains:
known_domains.append(current_domain)
# Build patterns for all domains to detect cross-contamination
patterns = []
for domain in known_domains:
patterns.extend([
f"http://{domain}[^\"']*",
f"https://{domain}[^\"']*",
f"//{domain}[^\"']*",
f"'{domain}'",
f"\"{domain}\"",
f"fetch\\(\"https://{domain}",
f"fetch\\(\"http://{domain}",
])
# Add general patterns for NextAuth configuration
patterns.extend([
"\"/api/auth/session\"",
"\"auth/session\"",
"fetch\\(\"/api/auth",
"domain:\"[^\"]*\"",
"baseUrl:\"[^\"]*\"",
"basePath:\"[^\"]*\"",
"NEXTAUTH_URL=\"[^\"]*\"",
"NEXTAUTH_URL='[^']*'"
])
all_urls = []
domain_matches = {domain: [] for domain in known_domains}
# Search for URLs in JS files
for pattern in patterns:
result = subprocess.run(
["find", "/app/web/.next", "-type", "f", "-name", "*.js", "-exec", "grep", "-o", pattern, "{}", ";"],
capture_output=True, text=True, timeout=10
)
if result.stdout.strip():
found_urls = list(set(result.stdout.strip().split('\n')))
all_urls.extend(found_urls)
# Categorize URLs by domain
for url in found_urls:
for domain in known_domains:
if domain in url:
domain_matches[domain].append(url)
# Look for NextAuth configuration
auth_configs = []
try:
auth_result = subprocess.run(
["find", "/app/web/.next", "-type", "f", "-name", "*.js", "-exec", "grep", "-o", "NEXTAUTH_URL[^,}]*", "{}", ";"],
capture_output=True, text=True, timeout=5
)
if auth_result.stdout.strip():
auth_configs = list(set(auth_result.stdout.strip().split('\n')))
except Exception:
pass
# Gather environment variable information
env_vars = {
"NEXT_PUBLIC_LEARNHOUSE_DOMAIN": os.environ.get('NEXT_PUBLIC_LEARNHOUSE_DOMAIN', 'NOT_SET'),
"NEXT_PUBLIC_LEARNHOUSE_API_URL": os.environ.get('NEXT_PUBLIC_LEARNHOUSE_API_URL', 'NOT_SET'),
"NEXT_PUBLIC_API_URL": os.environ.get('NEXT_PUBLIC_API_URL', 'NOT_SET'),
"NEXTAUTH_URL": os.environ.get('NEXTAUTH_URL', 'NOT_SET'),
"NEXT_PUBLIC_LEARNHOUSE_TOP_DOMAIN": os.environ.get('NEXT_PUBLIC_LEARNHOUSE_TOP_DOMAIN', 'NOT_SET'),
"LEARNHOUSE_COOKIE_DOMAIN": os.environ.get('LEARNHOUSE_COOKIE_DOMAIN', 'NOT_SET')
}
# Get the top domain from an environment variable or extract from current domain
top_domain = os.environ.get('NEXT_PUBLIC_LEARNHOUSE_TOP_DOMAIN', '')
if not top_domain and current_domain:
parts = current_domain.split('.')
if len(parts) >= 2:
top_domain = '.'.join(parts[-2:])
return { return {
"detected_hardcoded_urls": hardcoded_urls, "detected_hardcoded_urls": all_urls,
"domain_specific_matches": domain_matches,
"nextauth_configs": auth_configs,
"current_domain": current_domain, "current_domain": current_domain,
"top_domain": top_domain,
"env_vars": env_vars,
"client_host": request.client.host, "client_host": request.client.host,
"headers": dict(request.headers), "headers": dict(request.headers),
"deployment_name": os.environ.get('DEPLOYMENT_NAME', 'NOT_SET'),
"request_url": str(request.url)
}
except Exception as e:
return {
"error": str(e),
"message": "Could not scan for hardcoded URLs",
"deployment_name": os.environ.get('DEPLOYMENT_NAME', 'NOT_SET') "deployment_name": os.environ.get('DEPLOYMENT_NAME', 'NOT_SET')
} }
except Exception as e: except Exception as e:

View file

@ -0,0 +1,269 @@
import os
import json
import socket
import subprocess
from fastapi import APIRouter, Request, Response
from config.config import get_learnhouse_config
router = APIRouter()
@router.get("/deployment")
async def debug_deployment():
"""Debug endpoint for deployment verification and isolation testing"""
learnhouse_config = get_learnhouse_config()
# Parse database host safely
db_host = "unknown"
db_user = "unknown"
db_name = "unknown"
if '@' in learnhouse_config.database_config.sql_connection_string:
try:
# Split out username and host parts
auth_host_parts = learnhouse_config.database_config.sql_connection_string.split('@')
if len(auth_host_parts) > 1:
# Extract username
auth_parts = auth_host_parts[0].split('//')
if len(auth_parts) > 1:
user_parts = auth_parts[1].split(':')
if len(user_parts) > 0:
db_user = user_parts[0]
# Extract host and database name
host_parts = auth_host_parts[1].split('/')
if len(host_parts) > 0:
db_host = host_parts[0]
if len(host_parts) > 1:
db_name = host_parts[1]
except Exception as e:
pass
# Parse redis host safely
redis_host = "unknown"
redis_db = "unknown"
if '@' in learnhouse_config.redis_config.redis_connection_string:
try:
parts = learnhouse_config.redis_config.redis_connection_string.split('@')
if len(parts) > 1:
host_parts = parts[1].split(':')
if len(host_parts) > 0:
redis_host = host_parts[0]
if len(host_parts) > 1 and '/' in host_parts[1]:
redis_db = host_parts[1].split('/')[1]
except Exception:
pass
# Get hostname information
hostname = "unknown"
try:
hostname = socket.gethostname()
except:
pass
# Get process and container info
container_id = "unknown"
try:
# Try to get container ID from cgroup
with open('/proc/self/cgroup', 'r') as f:
for line in f:
if 'docker' in line:
container_id = line.strip().split('/')[-1]
break
except:
pass
return {
"deployment_name": os.environ.get('DEPLOYMENT_NAME', 'NOT_SET'),
"hostname": hostname,
"container_id": container_id,
"cookie_domain": learnhouse_config.hosting_config.cookie_config.domain,
"api_domain": learnhouse_config.hosting_config.domain,
"database": {
"host": db_host,
"user": db_user,
"name": db_name,
},
"redis": {
"host": redis_host,
"db": redis_db
},
"env_vars": {
"NEXT_PUBLIC_LEARNHOUSE_DOMAIN": os.environ.get('NEXT_PUBLIC_LEARNHOUSE_DOMAIN', 'NOT_SET'),
"NEXT_PUBLIC_LEARNHOUSE_API_URL": os.environ.get('NEXT_PUBLIC_LEARNHOUSE_API_URL', 'NOT_SET'),
"NEXT_PUBLIC_LEARNHOUSE_TOP_DOMAIN": os.environ.get('NEXT_PUBLIC_LEARNHOUSE_TOP_DOMAIN', 'NOT_SET'),
"LEARNHOUSE_COOKIE_DOMAIN": os.environ.get('LEARNHOUSE_COOKIE_DOMAIN', 'NOT_SET')
}
}
@router.get("/urls")
async def debug_urls(request: Request):
"""Debug endpoint to detect hardcoded URLs in NextJS bundle"""
try:
# Define domains to check for hardcoding
known_domains = [
"edu.adradviser.ro", # LIVE domain
"adr-lms.whitex.cloud" # DEV domain
]
# Add any additional domains from environment variables
current_domain = os.environ.get('NEXT_PUBLIC_LEARNHOUSE_DOMAIN', '')
if current_domain and current_domain not in known_domains:
known_domains.append(current_domain)
# Build patterns for all domains to detect cross-contamination
patterns = []
for domain in known_domains:
patterns.extend([
f"http://{domain}[^\"']*",
f"https://{domain}[^\"']*",
f"//{domain}[^\"']*",
f"'{domain}'",
f"\"{domain}\"",
f"fetch\\(\"https://{domain}",
f"fetch\\(\"http://{domain}",
])
# Add general patterns for NextAuth configuration
patterns.extend([
"\"/api/auth/session\"",
"\"auth/session\"",
"fetch\\(\"/api/auth",
"domain:\"[^\"]*\"",
"baseUrl:\"[^\"]*\"",
"basePath:\"[^\"]*\"",
"NEXTAUTH_URL=\"[^\"]*\"",
"NEXTAUTH_URL='[^']*'"
])
all_urls = []
domain_matches = {domain: [] for domain in known_domains}
# Search for URLs in JS files
for pattern in patterns:
result = subprocess.run(
["find", "/app/web/.next", "-type", "f", "-name", "*.js", "-exec", "grep", "-o", pattern, "{}", ";"],
capture_output=True, text=True, timeout=10
)
if result.stdout.strip():
found_urls = list(set(result.stdout.strip().split('\n')))
all_urls.extend(found_urls)
# Categorize URLs by domain
for url in found_urls:
for domain in known_domains:
if domain in url:
domain_matches[domain].append(url)
# Look for NextAuth configuration
auth_configs = []
try:
auth_result = subprocess.run(
["find", "/app/web/.next", "-type", "f", "-name", "*.js", "-exec", "grep", "-o", "NEXTAUTH_URL[^,}]*", "{}", ";"],
capture_output=True, text=True, timeout=5
)
if auth_result.stdout.strip():
auth_configs = list(set(auth_result.stdout.strip().split('\n')))
except Exception:
pass
# Gather environment variable information
env_vars = {
"NEXT_PUBLIC_LEARNHOUSE_DOMAIN": os.environ.get('NEXT_PUBLIC_LEARNHOUSE_DOMAIN', 'NOT_SET'),
"NEXT_PUBLIC_LEARNHOUSE_API_URL": os.environ.get('NEXT_PUBLIC_LEARNHOUSE_API_URL', 'NOT_SET'),
"NEXT_PUBLIC_API_URL": os.environ.get('NEXT_PUBLIC_API_URL', 'NOT_SET'),
"NEXTAUTH_URL": os.environ.get('NEXTAUTH_URL', 'NOT_SET'),
"NEXT_PUBLIC_LEARNHOUSE_TOP_DOMAIN": os.environ.get('NEXT_PUBLIC_LEARNHOUSE_TOP_DOMAIN', 'NOT_SET'),
"LEARNHOUSE_COOKIE_DOMAIN": os.environ.get('LEARNHOUSE_COOKIE_DOMAIN', 'NOT_SET')
}
# Get the top domain from an environment variable or extract from current domain
top_domain = os.environ.get('NEXT_PUBLIC_LEARNHOUSE_TOP_DOMAIN', '')
if not top_domain and current_domain:
parts = current_domain.split('.')
if len(parts) >= 2:
top_domain = '.'.join(parts[-2:])
return {
"detected_hardcoded_urls": all_urls,
"domain_specific_matches": domain_matches,
"nextauth_configs": auth_configs,
"current_domain": current_domain,
"top_domain": top_domain,
"env_vars": env_vars,
"client_host": request.client.host,
"headers": dict(request.headers),
"deployment_name": os.environ.get('DEPLOYMENT_NAME', 'NOT_SET'),
"request_url": str(request.url)
}
except Exception as e:
return {
"error": str(e),
"message": "Could not scan for hardcoded URLs",
"deployment_name": os.environ.get('DEPLOYMENT_NAME', 'NOT_SET')
}
@router.get("/cookies")
async def debug_cookies(request: Request, response: Response):
"""Debug endpoint to test cookie isolation and behavior"""
# Get current configuration
learnhouse_config = get_learnhouse_config()
cookie_domain = learnhouse_config.hosting_config.cookie_config.domain
deployment_name = os.environ.get('DEPLOYMENT_NAME', 'unknown')
# Set a test cookie with the current configuration
response.set_cookie(
key=f"isolation-test-{deployment_name}",
value=deployment_name,
domain=cookie_domain,
httponly=True,
samesite="lax",
path="/"
)
# Try to read any existing isolation test cookies
cookies = request.cookies
isolation_cookies = {}
for key, value in cookies.items():
if key.startswith("isolation-test-"):
isolation_cookies[key] = value
return {
"deployment_name": deployment_name,
"cookie_domain": cookie_domain,
"request_host": request.headers.get("host", "unknown"),
"detected_isolation_cookies": isolation_cookies,
"all_cookies": {k: "****" if not k.startswith("isolation-test") else v for k, v in cookies.items()},
"top_domain": os.environ.get('NEXT_PUBLIC_LEARNHOUSE_TOP_DOMAIN', 'NOT_SET'),
"message": f"Set test cookie 'isolation-test-{deployment_name}={deployment_name}' with domain={cookie_domain}"
}
@router.get("/session")
async def debug_session(request: Request):
"""Debug endpoint to check session-related headers and environment variables"""
# Extract host information
host = request.headers.get("host", "unknown")
origin = request.headers.get("origin", "unknown")
referer = request.headers.get("referer", "unknown")
# Extract NextAuth related information
nextauth_url = os.environ.get('NEXTAUTH_URL', 'NOT_SET')
nextauth_url_internal = os.environ.get('NEXTAUTH_URL_INTERNAL', 'NOT_SET')
# Check if session requests would go to the correct place
session_destination = nextauth_url or f"https://{host}"
return {
"deployment_name": os.environ.get('DEPLOYMENT_NAME', 'unknown'),
"request_headers": {
"host": host,
"origin": origin,
"referer": referer,
},
"session_config": {
"NEXTAUTH_URL": nextauth_url,
"NEXTAUTH_URL_INTERNAL": nextauth_url_internal,
"detected_session_destination": session_destination
},
"cookie_domain": get_learnhouse_config().hosting_config.cookie_config.domain,
"message": "This endpoint helps diagnose where NextAuth session data would be sent"
}

View file

@ -67,8 +67,8 @@ export const nextAuthOptions = {
httpOnly: true, httpOnly: true,
sameSite: 'lax', sameSite: 'lax',
path: '/', path: '/',
// When working on localhost, the cookie domain must be omitted entirely (https://stackoverflow.com/a/1188145) // When working on localhost or with different domains, use the current domain instead of a shared top domain
domain: `.${LEARNHOUSE_TOP_DOMAIN}`, domain: process.env.LEARNHOUSE_COOKIE_DOMAIN || (LEARNHOUSE_TOP_DOMAIN === 'localhost' ? undefined : `.${LEARNHOUSE_TOP_DOMAIN}`),
secure: !isDevEnv, secure: !isDevEnv,
}, },
}, },

264
create-cookie-demo.sh Executable file
View file

@ -0,0 +1,264 @@
#!/bin/bash
# Create a demonstration HTML file to visualize cookie isolation problems
# This script generates an HTML file that shows which cookies are visible across deployments
echo "Creating cookie isolation visualization tool..."
# Define HTML content
cat > /home/whitex/dev/github/learnhouse/cookie-isolation-demo.html << 'EOL'
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>LearnHouse Cookie Isolation Test</title>
<style>
body {
font-family: Arial, sans-serif;
line-height: 1.6;
max-width: 1000px;
margin: 0 auto;
padding: 20px;
color: #333;
}
h1 {
color: #0066cc;
border-bottom: 2px solid #eee;
padding-bottom: 10px;
}
h2 {
color: #0066cc;
margin-top: 30px;
}
.test-panel {
border: 1px solid #ddd;
padding: 20px;
margin: 20px 0;
border-radius: 5px;
background-color: #f9f9f9;
}
button {
background-color: #0066cc;
color: white;
border: none;
padding: 10px 15px;
border-radius: 5px;
cursor: pointer;
margin: 5px 0;
font-size: 14px;
}
button:hover {
background-color: #0055aa;
}
#results {
border: 1px solid #ddd;
padding: 15px;
border-radius: 5px;
margin-top: 20px;
min-height: 200px;
background-color: #fff;
}
.success {
color: green;
font-weight: bold;
}
.failure {
color: red;
font-weight: bold;
}
.deployment {
padding: 10px;
margin: 10px 0;
border-radius: 5px;
}
.dev {
background-color: #e6f7ff;
border-left: 5px solid #0099ff;
}
.live {
background-color: #fff0e6;
border-left: 5px solid #ff9966;
}
table {
width: 100%;
border-collapse: collapse;
margin: 15px 0;
}
table, th, td {
border: 1px solid #ddd;
}
th, td {
padding: 10px;
text-align: left;
}
th {
background-color: #f2f2f2;
}
.info {
background-color: #f0f0f0;
padding: 15px;
border-radius: 5px;
margin: 20px 0;
border-left: 5px solid #999;
}
</style>
</head>
<body>
<h1>LearnHouse Cookie Isolation Test</h1>
<div class="info">
<p>This tool helps visualize cookie isolation between DEV and LIVE LearnHouse deployments.
It will help you identify if cookies from one deployment are visible to the other, which
could lead to session contamination.</p>
</div>
<div class="test-panel">
<h2>1. Set Test Cookies</h2>
<p>First, set test cookies on both deployments:</p>
<button onclick="setDevCookie()">Set DEV Cookie</button>
<button onclick="setLiveCookie()">Set LIVE Cookie</button>
<div id="setCookieResult"></div>
</div>
<div class="test-panel">
<h2>2. Test Cookie Isolation</h2>
<p>Now check if cookies are properly isolated between deployments:</p>
<button onclick="testCookieIsolation()">Test Cookie Isolation</button>
</div>
<h2>Results</h2>
<div id="results">
<p>Results will appear here after running tests...</p>
</div>
<script>
const DEV_URL = 'http://adr-lms.whitex.cloud';
const LIVE_URL = 'http://edu.adradviser.ro';
// Function to fetch with CORS handling
async function fetchWithCors(url) {
try {
const response = await fetch(url, {
method: 'GET',
mode: 'cors',
credentials: 'include', // Important: include cookies
headers: {
'Accept': 'application/json',
}
});
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
return await response.json();
} catch (error) {
console.error('Fetch error:', error);
return { error: error.message };
}
}
// Set cookie on DEV deployment
async function setDevCookie() {
document.getElementById('setCookieResult').innerHTML = 'Setting DEV cookie...';
const result = await fetchWithCors(`${DEV_URL}/api/v1/debug/cookies`);
document.getElementById('setCookieResult').innerHTML =
`<div class="deployment dev">Set DEV cookie: ${JSON.stringify(result.message || 'Failed')}</div>`;
}
// Set cookie on LIVE deployment
async function setLiveCookie() {
document.getElementById('setCookieResult').innerHTML = 'Setting LIVE cookie...';
const result = await fetchWithCors(`${LIVE_URL}/api/v1/debug/cookies`);
document.getElementById('setCookieResult').innerHTML =
`<div class="deployment live">Set LIVE cookie: ${JSON.stringify(result.message || 'Failed')}</div>`;
}
// Test if cookies are isolated between deployments
async function testCookieIsolation() {
document.getElementById('results').innerHTML = 'Testing cookie isolation...';
// Test DEV cookies
const devResult = await fetchWithCors(`${DEV_URL}/api/v1/debug/cookies`);
// Test LIVE cookies
const liveResult = await fetchWithCors(`${LIVE_URL}/api/v1/debug/cookies`);
// Analyze results
let html = '<h3>Cookie Isolation Test Results</h3>';
html += '<div class="deployment dev">';
html += '<h4>DEV Deployment Cookies</h4>';
html += '<table>';
html += '<tr><th>Cookie</th><th>Value</th></tr>';
const devCookies = devResult.detected_isolation_cookies || {};
if (Object.keys(devCookies).length === 0) {
html += '<tr><td colspan="2">No isolation test cookies found</td></tr>';
} else {
for (const [cookie, value] of Object.entries(devCookies)) {
html += `<tr><td>${cookie}</td><td>${value}</td></tr>`;
}
}
html += '</table></div>';
html += '<div class="deployment live">';
html += '<h4>LIVE Deployment Cookies</h4>';
html += '<table>';
html += '<tr><th>Cookie</th><th>Value</th></tr>';
const liveCookies = liveResult.detected_isolation_cookies || {};
if (Object.keys(liveCookies).length === 0) {
html += '<tr><td colspan="2">No isolation test cookies found</td></tr>';
} else {
for (const [cookie, value] of Object.entries(liveCookies)) {
html += `<tr><td>${cookie}</td><td>${value}</td></tr>`;
}
}
html += '</table></div>';
// Analysis
html += '<h4>Analysis</h4>';
const devHasLiveCookies = Object.keys(devCookies).some(c => c.includes('LIVE'));
const liveHasDevCookies = Object.keys(liveCookies).some(c => c.includes('DEV'));
if (!devHasLiveCookies && !liveHasDevCookies) {
html += '<div class="success">SUCCESS: Cookie isolation is working correctly!</div>';
html += '<p>The DEV cookies are not visible to the LIVE deployment, and vice versa.</p>';
html += '<p>This means that sessions should be properly isolated between deployments.</p>';
} else {
html += '<div class="failure">FAILURE: Cookie isolation is NOT working!</div>';
if (devHasLiveCookies) {
html += '<p>- DEV deployment can see LIVE cookies</p>';
}
if (liveHasDevCookies) {
html += '<p>- LIVE deployment can see DEV cookies</p>';
}
html += '<p>This means session contamination is occurring between deployments.</p>';
html += '<p>Please ensure each deployment has a unique cookie domain set with:</p>';
html += '<pre>LEARNHOUSE_COOKIE_DOMAIN=adr-lms.whitex.cloud (for DEV)\nLEARNHOUSE_COOKIE_DOMAIN=edu.adradviser.ro (for LIVE)</pre>';
}
document.getElementById('results').innerHTML = html;
}
</script>
</body>
</html>
EOL
echo "Cookie isolation demonstration tool has been created at:"
echo "/home/whitex/dev/github/learnhouse/cookie-isolation-demo.html"
echo
echo "To use this tool:"
echo "1. Open the HTML file in a browser"
echo "2. Click 'Set DEV Cookie' and 'Set LIVE Cookie' buttons"
echo "3. Click 'Test Cookie Isolation' to see if cookies are properly isolated"
echo
echo "This tool demonstrates visually whether the cookie domains are properly isolated between deployments."

67
deploy-enhanced-debug.sh Executable file
View file

@ -0,0 +1,67 @@
#!/bin/bash
# Script to deploy enhanced isolation debugging tools
# This script will update the debug endpoints on both DEV and LIVE deployments
echo "===================================================================="
echo "LearnHouse Enhanced Debug Tools Deployment"
echo "===================================================================="
# ANSI color codes
GREEN='\033[0;32m'
RED='\033[0;31m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Define git branches
DEV_BRANCH="isolation-debug"
MAIN_BRANCH="main"
# Ensure we have the latest code
echo -e "${BLUE}Fetching latest code...${NC}"
git fetch origin
current_branch=$(git rev-parse --abbrev-ref HEAD)
# Create a new branch for our changes
echo -e "${BLUE}Creating branch for debug tools...${NC}"
git checkout -b $DEV_BRANCH
# Check if the debug files exist
if [ ! -f "apps/api/src/routers/debug_enhanced.py" ]; then
echo -e "${RED}Error: Enhanced debug module not found! Run the setup script first.${NC}"
git checkout $current_branch
exit 1
fi
# Stage and commit our changes
echo -e "${BLUE}Committing changes...${NC}"
git add apps/api/src/routers/debug_enhanced.py
git add apps/api/src/router.py
git add verify-enhanced-isolation.sh
git add ENHANCED_DEBUG_TOOLS.md
git commit -m "Add enhanced debug endpoints for deployment isolation troubleshooting"
# Push to origin
echo -e "${BLUE}Pushing changes to origin...${NC}"
if git push -u origin $DEV_BRANCH; then
echo -e "${GREEN}Successfully pushed changes to origin/$DEV_BRANCH${NC}"
else
echo -e "${RED}Failed to push changes. Please check your git configuration.${NC}"
git checkout $current_branch
exit 1
fi
echo ""
echo "===================================================================="
echo -e "${GREEN}Deployment preparation completed!${NC}"
echo ""
echo -e "To deploy these changes:"
echo -e "1. Create a pull request from ${BLUE}$DEV_BRANCH${NC} to ${BLUE}$MAIN_BRANCH${NC}"
echo -e "2. After review and merge, deploy to both environments"
echo -e "3. Use ${BLUE}./verify-enhanced-isolation.sh${NC} to check isolation"
echo ""
echo -e "Documentation: See ${BLUE}ENHANCED_DEBUG_TOOLS.md${NC} for endpoint usage"
echo "===================================================================="
# Return to original branch
git checkout $current_branch

View file

@ -0,0 +1,94 @@
#!/bin/bash
# NextAuth Cookie Isolation Test Script
# Tests whether the NextAuth session cookies are properly isolated between deployments
echo "=============================================================="
echo "NextAuth Cookie Isolation Test"
echo "=============================================================="
# Define deployment URLs
DEV_URL="http://adr-lms.whitex.cloud"
LIVE_URL="http://edu.adradviser.ro"
# Colors for output
GREEN='\033[0;32m'
RED='\033[0;31m'
YELLOW='\033[0;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Function to check if curl is installed
if ! command -v curl &> /dev/null; then
echo -e "${RED}Error: curl is not installed. Please install curl first.${NC}"
exit 1
fi
# Function to check if jq is installed (for prettier output)
if ! command -v jq &> /dev/null; then
echo -e "${YELLOW}Warning: jq is not installed. JSON output will not be formatted.${NC}"
JQ_CMD="cat"
else
JQ_CMD="jq"
fi
echo -e "${BLUE}Step 1: Checking NextAuth configuration in DEV environment...${NC}"
curl -s "${DEV_URL}/api/v1/debug/session" | $JQ_CMD
echo
echo -e "${BLUE}Step 2: Checking NextAuth configuration in LIVE environment...${NC}"
curl -s "${LIVE_URL}/api/v1/debug/session" | $JQ_CMD
echo
echo -e "${BLUE}Step 3: Testing cookie isolation with test cookies...${NC}"
echo "Setting test cookies on DEV deployment..."
curl -s -c /tmp/dev_cookies.txt "${DEV_URL}/api/v1/debug/cookies" > /dev/null
echo "Setting test cookies on LIVE deployment..."
curl -s -c /tmp/live_cookies.txt "${LIVE_URL}/api/v1/debug/cookies" > /dev/null
echo -e "${BLUE}Step 4: Checking for cookie isolation...${NC}"
echo "Sending DEV cookies to LIVE deployment..."
DEV_COOKIES_ON_LIVE=$(curl -s -b /tmp/dev_cookies.txt "${LIVE_URL}/api/v1/debug/cookies" | grep -o "isolation-test-DEV")
echo "Sending LIVE cookies to DEV deployment..."
LIVE_COOKIES_ON_DEV=$(curl -s -b /tmp/live_cookies.txt "${DEV_URL}/api/v1/debug/cookies" | grep -o "isolation-test-LIVE")
echo
if [[ -z "$DEV_COOKIES_ON_LIVE" && -z "$LIVE_COOKIES_ON_DEV" ]]; then
echo -e "${GREEN}SUCCESS: Cookie isolation is working correctly!${NC}"
echo "The DEV cookies are not visible to the LIVE deployment, and vice versa."
echo "This means that sessions should be properly isolated."
else
echo -e "${RED}FAILURE: Cookie isolation is NOT working!${NC}"
if [[ ! -z "$DEV_COOKIES_ON_LIVE" ]]; then
echo "- DEV cookies are visible to the LIVE deployment"
fi
if [[ ! -z "$LIVE_COOKIES_ON_DEV" ]]; then
echo "- LIVE cookies are visible to the DEV deployment"
fi
echo
echo "This means session contamination will occur between deployments."
echo "Please ensure each deployment has a unique cookie domain set with:"
echo " LEARNHOUSE_COOKIE_DOMAIN=adr-lms.whitex.cloud (for DEV)"
echo " LEARNHOUSE_COOKIE_DOMAIN=edu.adradviser.ro (for LIVE)"
fi
echo
echo -e "${BLUE}Step 5: Checking domain and cookie settings...${NC}"
echo "DEV settings:"
curl -s "${DEV_URL}/api/v1/debug/deployment" | grep -E "cookie_domain|api_domain" | $JQ_CMD
echo
echo "LIVE settings:"
curl -s "${LIVE_URL}/api/v1/debug/deployment" | grep -E "cookie_domain|api_domain" | $JQ_CMD
echo
echo -e "${BLUE}Cleaning up temporary files...${NC}"
rm -f /tmp/dev_cookies.txt /tmp/live_cookies.txt
echo
echo "=============================================================="
echo "Test complete!"
echo "=============================================================="

182
verify-all-isolation.sh Executable file
View file

@ -0,0 +1,182 @@
#!/bin/bash
# Master Isolation Verification Script
# This script runs all isolation verification checks in sequence
echo "===================================================================="
echo "LearnHouse Deployment Isolation - Complete Verification Suite"
echo "===================================================================="
# Define colors for output
GREEN='\033[0;32m'
RED='\033[0;31m'
YELLOW='\033[0;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Define the deployments
DEV_URL="http://adr-lms.whitex.cloud"
LIVE_URL="http://edu.adradviser.ro"
# Function to check if a command exists
command_exists() {
command -v "$1" >/dev/null 2>&1
}
# Check requirements
echo -e "${BLUE}Checking requirements...${NC}"
MISSING_TOOLS=0
if ! command_exists curl; then
echo -e "${RED}Missing required tool: curl${NC}"
MISSING_TOOLS=1
fi
if ! command_exists jq; then
echo -e "${YELLOW}Warning: jq is not installed. JSON output will not be formatted.${NC}"
fi
if [ $MISSING_TOOLS -eq 1 ]; then
echo -e "${RED}Please install the missing tools and try again.${NC}"
exit 1
fi
echo -e "${GREEN}All required tools are available.${NC}"
echo
# Function to run a verification script and report result
run_verification() {
script="$1"
description="$2"
echo -e "${BLUE}Running: ${description}${NC}"
echo "--------------------------------------------------------------------"
if [ -x "$script" ]; then
if "$script"; then
result=$?
if [ $result -eq 0 ]; then
echo -e "${GREEN}✓ PASSED: ${description}${NC}"
else
echo -e "${RED}✗ FAILED: ${description} (Exit code: $result)${NC}"
fi
else
echo -e "${RED}✗ ERROR: Failed to execute ${description}${NC}"
fi
else
echo -e "${RED}✗ ERROR: Script not found or not executable: ${script}${NC}"
fi
echo "--------------------------------------------------------------------"
echo
}
# Create output directory for reports
REPORT_DIR="/tmp/learnhouse-isolation-report"
mkdir -p "$REPORT_DIR"
echo -e "${BLUE}Reports will be saved in: ${REPORT_DIR}${NC}"
echo
# Step 1: Run the enhanced deployment verification
echo -e "${BLUE}STEP 1: Testing basic deployment configuration${NC}"
curl -s "${DEV_URL}/api/v1/debug/deployment" > "${REPORT_DIR}/dev-deployment.json"
curl -s "${LIVE_URL}/api/v1/debug/deployment" > "${REPORT_DIR}/live-deployment.json"
run_verification "./verify-enhanced-isolation.sh" "Enhanced Deployment Verification"
# Step 2: Test database isolation specifically
echo -e "${BLUE}STEP 2: Testing database isolation${NC}"
run_verification "./verify-db-isolation.sh" "Database Isolation Check"
# Step 3: Test NextAuth cookie isolation
echo -e "${BLUE}STEP 3: Testing NextAuth cookies${NC}"
run_verification "./test-nextauth-cookie-isolation.sh" "NextAuth Cookie Isolation Test"
# Step 4: Check for hardcoded URLs in the frontend
echo -e "${BLUE}STEP 4: Checking for hardcoded URLs${NC}"
echo "Checking DEV deployment for LIVE URLs..."
curl -s "${DEV_URL}/api/v1/debug/urls" > "${REPORT_DIR}/dev-urls.json"
DEV_HARDCODED_COUNT=$(grep -o "edu.adradviser.ro" "${REPORT_DIR}/dev-urls.json" | wc -l)
echo "Checking LIVE deployment for DEV URLs..."
curl -s "${LIVE_URL}/api/v1/debug/urls" > "${REPORT_DIR}/live-urls.json"
LIVE_HARDCODED_COUNT=$(grep -o "adr-lms.whitex.cloud" "${REPORT_DIR}/live-urls.json" | wc -l)
if [ $DEV_HARDCODED_COUNT -eq 0 ] && [ $LIVE_HARDCODED_COUNT -eq 0 ]; then
echo -e "${GREEN}✓ PASSED: No cross-deployment hardcoded URLs found${NC}"
else
echo -e "${RED}✗ FAILED: Found hardcoded URLs:${NC}"
if [ $DEV_HARDCODED_COUNT -gt 0 ]; then
echo " - DEV deployment contains ${DEV_HARDCODED_COUNT} references to LIVE domain"
fi
if [ $LIVE_HARDCODED_COUNT -gt 0 ]; then
echo " - LIVE deployment contains ${LIVE_HARDCODED_COUNT} references to DEV domain"
fi
fi
echo "--------------------------------------------------------------------"
echo
# Step 5: Create the cookie isolation demo
echo -e "${BLUE}STEP 5: Creating cookie isolation demonstration tool${NC}"
run_verification "./create-cookie-demo.sh" "Cookie Isolation Demo Creation"
# Summary of all tests
echo "===================================================================="
echo -e "${BLUE}SUMMARY OF ISOLATION VERIFICATION${NC}"
echo "===================================================================="
# Check deployment names
DEV_NAME=$(grep -o '"deployment_name":"[^"]*"' "${REPORT_DIR}/dev-deployment.json" | cut -d'"' -f4)
LIVE_NAME=$(grep -o '"deployment_name":"[^"]*"' "${REPORT_DIR}/live-deployment.json" | cut -d'"' -f4)
# Check database isolation
DEV_DB=$(grep -o '"name":"[^"]*"' "${REPORT_DIR}/dev-deployment.json" | head -1 | cut -d'"' -f4)
LIVE_DB=$(grep -o '"name":"[^"]*"' "${REPORT_DIR}/live-deployment.json" | head -1 | cut -d'"' -f4)
# Check cookie domain isolation
DEV_COOKIE=$(grep -o '"cookie_domain":"[^"]*"' "${REPORT_DIR}/dev-deployment.json" | cut -d'"' -f4)
LIVE_COOKIE=$(grep -o '"cookie_domain":"[^"]*"' "${REPORT_DIR}/live-deployment.json" | cut -d'"' -f4)
echo -e "Deployment Names:"
if [[ "$DEV_NAME" == "DEV" && "$LIVE_NAME" == "LIVE" ]]; then
echo -e " ${GREEN}✓ Correct: DEV='$DEV_NAME', LIVE='$LIVE_NAME'${NC}"
else
echo -e " ${RED}✗ Incorrect: DEV='$DEV_NAME', LIVE='$LIVE_NAME'${NC}"
fi
echo -e "Database Isolation:"
if [[ "$DEV_DB" != "$LIVE_DB" && "$DEV_DB" != "unknown" && "$LIVE_DB" != "unknown" ]]; then
echo -e " ${GREEN}✓ Isolated: DEV='$DEV_DB', LIVE='$LIVE_DB'${NC}"
else
echo -e " ${RED}✗ Not isolated: DEV='$DEV_DB', LIVE='$LIVE_DB'${NC}"
fi
echo -e "Cookie Domain Isolation:"
if [[ "$DEV_COOKIE" != "$LIVE_COOKIE" ]]; then
echo -e " ${GREEN}✓ Isolated: DEV='$DEV_COOKIE', LIVE='$LIVE_COOKIE'${NC}"
else
echo -e " ${RED}✗ Not isolated: DEV='$DEV_COOKIE', LIVE='$LIVE_COOKIE'${NC}"
fi
echo -e "URL Hardcoding:"
if [ $DEV_HARDCODED_COUNT -eq 0 ] && [ $LIVE_HARDCODED_COUNT -eq 0 ]; then
echo -e " ${GREEN}✓ No cross-deployment hardcoded URLs${NC}"
else
echo -e " ${RED}✗ Found hardcoded URLs: DEV=$DEV_HARDCODED_COUNT, LIVE=$LIVE_HARDCODED_COUNT${NC}"
fi
echo
echo "Report files saved to: ${REPORT_DIR}"
echo "===================================================================="
# Final assessment
if [[ "$DEV_NAME" == "DEV" && "$LIVE_NAME" == "LIVE" &&
"$DEV_DB" != "$LIVE_DB" && "$DEV_DB" != "unknown" && "$LIVE_DB" != "unknown" &&
"$DEV_COOKIE" != "$LIVE_COOKIE" &&
$DEV_HARDCODED_COUNT -eq 0 && $LIVE_HARDCODED_COUNT -eq 0 ]]; then
echo -e "${GREEN}OVERALL RESULT: PASSED - Deployments appear to be properly isolated!${NC}"
exit 0
else
echo -e "${RED}OVERALL RESULT: FAILED - Deployment isolation issues detected!${NC}"
echo -e "Please refer to the ${BLUE}ISOLATION_IMPLEMENTATION_CHECKLIST.md${NC} to resolve these issues."
exit 1
fi

162
verify-enhanced-isolation.sh Executable file
View file

@ -0,0 +1,162 @@
#!/bin/bash
# Enhanced isolation verification script
# This script helps verify that the DEV and LIVE deployments are properly isolated
echo "==================================================================================="
echo "LearnHouse Deployment Isolation Verification Tool"
echo "==================================================================================="
# Define the URLs of both deployments
DEV_URL="http://adr-lms.whitex.cloud"
LIVE_URL="http://edu.adradviser.ro"
# Colors for output
GREEN='\033[0;32m'
RED='\033[0;31m'
YELLOW='\033[0;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Function to check if a command exists
command_exists() {
command -v "$1" >/dev/null 2>&1
}
# Check if required tools are installed
if ! command_exists curl; then
echo -e "${RED}Error: curl is not installed. Please install curl and try again.${NC}"
exit 1
fi
if ! command_exists jq; then
echo -e "${YELLOW}Warning: jq is not installed. JSON responses will not be formatted nicely.${NC}"
JSON_PROCESSOR="cat"
else
JSON_PROCESSOR="jq"
fi
echo -e "${BLUE}Checking deployment configurations...${NC}"
echo ""
# Function to fetch deployment info
fetch_deployment_info() {
local url="$1"
local name="$2"
echo -e "${BLUE}Checking ${name} deployment (${url})...${NC}"
echo "---------------------------------------------------------------------------------"
# Make API call
response=$(curl -s "${url}/api/v1/debug/deployment" -H "Accept: application/json")
if [ $? -ne 0 ]; then
echo -e "${RED}Failed to connect to ${name} deployment.${NC}"
return 1
fi
# Display deployment info
echo -e "${GREEN}Deployment information:${NC}"
echo "$response" | $JSON_PROCESSOR
# Extract key information
deployment_name=$(echo "$response" | grep -o '"deployment_name":"[^"]*"' | cut -d'"' -f4)
cookie_domain=$(echo "$response" | grep -o '"cookie_domain":"[^"]*"' | cut -d'"' -f4)
database_host=$(echo "$response" | grep -o '"host":"[^"]*"' | head -1 | cut -d'"' -f4)
database_name=$(echo "$response" | grep -o '"name":"[^"]*"' | head -1 | cut -d'"' -f4)
echo ""
echo -e "${BLUE}Summary:${NC}"
echo "Deployment name: ${deployment_name:-unknown}"
echo "Cookie domain: ${cookie_domain:-unknown}"
echo "Database host: ${database_host:-unknown}"
echo "Database name: ${database_name:-unknown}"
echo ""
# Check for cookie isolation
echo -e "${BLUE}Testing cookie isolation...${NC}"
cookie_response=$(curl -s "${url}/api/v1/debug/cookies" -H "Accept: application/json")
echo "$cookie_response" | $JSON_PROCESSOR
echo ""
# Check for hardcoded URLs
echo -e "${BLUE}Checking for hardcoded URLs...${NC}"
url_response=$(curl -s "${url}/api/v1/debug/urls" -H "Accept: application/json")
# Count hardcoded references to the other environment
other_url=""
if [[ "$url" == "$DEV_URL" ]]; then
other_url="edu.adradviser.ro"
else
other_url="adr-lms.whitex.cloud"
fi
hardcoded_count=$(echo "$url_response" | grep -o "$other_url" | wc -l)
if [[ $hardcoded_count -gt 0 ]]; then
echo -e "${RED}Warning: Found $hardcoded_count hardcoded references to $other_url${NC}"
echo "This could cause isolation issues!"
else
echo -e "${GREEN}No hardcoded references to the other environment found.${NC}"
fi
# Check for session configuration
echo -e "${BLUE}Testing session configuration...${NC}"
session_response=$(curl -s "${url}/api/v1/debug/session" -H "Accept: application/json")
echo "$session_response" | $JSON_PROCESSOR
echo ""
echo "---------------------------------------------------------------------------------"
}
# Check both deployments
fetch_deployment_info "$DEV_URL" "DEV"
echo ""
fetch_deployment_info "$LIVE_URL" "LIVE"
echo ""
echo -e "${BLUE}Analyzing isolation status...${NC}"
echo "---------------------------------------------------------------------------------"
# Simple test: check if both deployments respond with the correct deployment name
dev_name=$(curl -s "${DEV_URL}/api/v1/debug/deployment" | grep -o '"deployment_name":"[^"]*"' | cut -d'"' -f4)
live_name=$(curl -s "${LIVE_URL}/api/v1/debug/deployment" | grep -o '"deployment_name":"[^"]*"' | cut -d'"' -f4)
# Check database isolation
dev_db=$(curl -s "${DEV_URL}/api/v1/debug/deployment" | grep -o '"name":"[^"]*"' | head -1 | cut -d'"' -f4)
live_db=$(curl -s "${LIVE_URL}/api/v1/debug/deployment" | grep -o '"name":"[^"]*"' | head -1 | cut -d'"' -f4)
# Check cookie domain isolation
dev_cookie=$(curl -s "${DEV_URL}/api/v1/debug/deployment" | grep -o '"cookie_domain":"[^"]*"' | cut -d'"' -f4)
live_cookie=$(curl -s "${LIVE_URL}/api/v1/debug/deployment" | grep -o '"cookie_domain":"[^"]*"' | cut -d'"' -f4)
echo "SUMMARY OF ISOLATION STATUS:"
echo ""
if [[ "$dev_name" == "DEV" && "$live_name" == "LIVE" ]]; then
echo -e "${GREEN}✓ Both deployments report the correct deployment name.${NC}"
else
echo -e "${RED}✗ Deployment name mismatch! DEV reports '$dev_name', LIVE reports '$live_name'${NC}"
fi
if [[ "$dev_db" != "$live_db" ]]; then
echo -e "${GREEN}✓ Database isolation: Different database names ($dev_db vs $live_db)${NC}"
else
echo -e "${RED}✗ Database isolation failure! Both environments use the same database: $dev_db${NC}"
fi
if [[ "$dev_cookie" != "$live_cookie" ]]; then
echo -e "${GREEN}✓ Cookie domain isolation: Different cookie domains ($dev_cookie vs $live_cookie)${NC}"
else
echo -e "${RED}✗ Cookie domain isolation failure! Both environments use the same cookie domain: $dev_cookie${NC}"
fi
echo ""
echo "==================================================================================="
if [[ "$dev_db" == "$live_db" || "$dev_cookie" == "$live_cookie" ]]; then
echo -e "${RED}Isolation verification FAILED! The deployments are not properly isolated.${NC}"
exit 1
else
echo -e "${GREEN}Isolation verification PASSED! The deployments appear to be properly isolated.${NC}"
exit 0
fi