Files
orion/scripts/test_auth_complete.py
Samir Boulahtit b8a46e1746 fix: protect critical re-export imports from linter removal
Problem:
- Ruff removed 'from app.core.database import Base' from models/database/base.py
- Import appeared "unused" (F401) but was actually a critical re-export
- Caused ImportError: cannot import name 'Base' at runtime
- Re-export pattern: import in one file to export from package

Solution:
1. Added F401 ignore for models/database/base.py in pyproject.toml
2. Created scripts/verify_critical_imports.py verification script
3. Integrated verification into make check and CI pipeline
4. Updated documentation with explanation

New Verification Script:
- Checks all critical re-export imports exist
- Detects import variations (parentheses, 'as' clauses)
- Handles SQLAlchemy declarative_base alternatives
- Runs as part of make check automatically

Protected Files:
- models/database/base.py - Re-exports Base for all models
- models/__init__.py - Exports Base for Alembic
- models/database/__init__.py - Exports Base from package
- All __init__.py files (already protected)

Makefile Changes:
- make verify-imports - Run import verification
- make check - Now includes verify-imports
- make ci - Includes verify-imports in pipeline

Documentation Updated:
- Code quality guide explains re-export protection
- Pre-commit workflow includes verification
- Examples of why re-exports matter

This prevents future issues where linters remove seemingly
"unused" imports that are actually critical for application structure.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-28 20:10:22 +01:00

555 lines
18 KiB
Python
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""
Complete Authentication System Test Script
This script tests all three authentication contexts:
1. Admin authentication and cookie isolation
2. Vendor authentication and cookie isolation
3. Customer authentication and cookie isolation
4. Cross-context access prevention
5. Logging middleware
Usage:
python test_auth_complete.py
Requirements:
- Server running on http://localhost:8000
- Test users configured:
* Admin: username=admin, password=admin123
* Vendor: username=vendor, password=vendor123
* Customer: username=customer, password=customer123, vendor_id=1
"""
import requests
BASE_URL = "http://localhost:8000"
class Color:
"""Terminal colors for pretty output"""
GREEN = "\033[92m"
RED = "\033[91m"
YELLOW = "\033[93m"
BLUE = "\033[94m"
MAGENTA = "\033[95m"
CYAN = "\033[96m"
BOLD = "\033[1m"
END = "\033[0m"
def print_section(name: str):
"""Print section header"""
print(f"\n{Color.BOLD}{Color.CYAN}{'' * 60}")
print(f" {name}")
print(f"{'' * 60}{Color.END}")
def print_test(name: str):
"""Print test name"""
print(f"\n{Color.BOLD}{Color.BLUE}🧪 Test: {name}{Color.END}")
def print_success(message: str):
"""Print success message"""
print(f"{Color.GREEN}{message}{Color.END}")
def print_error(message: str):
"""Print error message"""
print(f"{Color.RED}{message}{Color.END}")
def print_info(message: str):
"""Print info message"""
print(f"{Color.YELLOW} {message}{Color.END}")
def print_warning(message: str):
"""Print warning message"""
print(f"{Color.MAGENTA}⚠️ {message}{Color.END}")
# ============================================================================
# ADMIN AUTHENTICATION TESTS
# ============================================================================
def test_admin_login() -> dict | None:
"""Test admin login and cookie configuration"""
print_test("Admin Login")
try:
response = requests.post(
f"{BASE_URL}/api/v1/admin/auth/login",
json={"username": "admin", "password": "admin123"},
)
if response.status_code == 200:
data = response.json()
cookies = response.cookies
if "access_token" in data:
print_success("Admin login successful")
print_success(f"Received access token: {data['access_token'][:20]}...")
else:
print_error("No access token in response")
return None
if "admin_token" in cookies:
print_success("admin_token cookie set")
print_info("Cookie path should be /admin (verify in browser)")
else:
print_error("admin_token cookie NOT set")
return {"token": data["access_token"], "user": data.get("user", {})}
print_error(f"Login failed: {response.status_code}")
print_error(f"Response: {response.text}")
return None
except Exception as e:
print_error(f"Exception during admin login: {str(e)}")
return None
def test_admin_cannot_access_vendor_api(admin_token: str):
"""Test that admin token cannot access vendor API"""
print_test("Admin Token on Vendor API (Should Block)")
try:
response = requests.get(
f"{BASE_URL}/api/v1/vendor/TESTVENDOR/products",
headers={"Authorization": f"Bearer {admin_token}"},
)
if response.status_code in [401, 403]:
data = response.json()
print_success("Admin correctly blocked from vendor API")
print_success(f"Error code: {data.get('error_code', 'N/A')}")
return True
if response.status_code == 200:
print_error("SECURITY ISSUE: Admin can access vendor API!")
return False
print_warning(f"Unexpected status code: {response.status_code}")
return False
except Exception as e:
print_error(f"Exception: {str(e)}")
return False
def test_admin_cannot_access_customer_api(admin_token: str):
"""Test that admin token cannot access customer account pages"""
print_test("Admin Token on Customer API (Should Block)")
try:
response = requests.get(
f"{BASE_URL}/shop/account/dashboard",
headers={"Authorization": f"Bearer {admin_token}"},
)
# Customer pages may return HTML or JSON error
if response.status_code in [401, 403]:
print_success("Admin correctly blocked from customer pages")
return True
if response.status_code == 200:
print_error("SECURITY ISSUE: Admin can access customer pages!")
return False
print_warning(f"Unexpected status code: {response.status_code}")
return False
except Exception as e:
print_error(f"Exception: {str(e)}")
return False
# ============================================================================
# VENDOR AUTHENTICATION TESTS
# ============================================================================
def test_vendor_login() -> dict | None:
"""Test vendor login and cookie configuration"""
print_test("Vendor Login")
try:
response = requests.post(
f"{BASE_URL}/api/v1/vendor/auth/login",
json={"username": "vendor", "password": "vendor123"},
)
if response.status_code == 200:
data = response.json()
cookies = response.cookies
if "access_token" in data:
print_success("Vendor login successful")
print_success(f"Received access token: {data['access_token'][:20]}...")
else:
print_error("No access token in response")
return None
if "vendor_token" in cookies:
print_success("vendor_token cookie set")
print_info("Cookie path should be /vendor (verify in browser)")
else:
print_error("vendor_token cookie NOT set")
if "vendor" in data:
print_success(f"Vendor: {data['vendor'].get('vendor_code', 'N/A')}")
return {
"token": data["access_token"],
"user": data.get("user", {}),
"vendor": data.get("vendor", {}),
}
print_error(f"Login failed: {response.status_code}")
print_error(f"Response: {response.text}")
return None
except Exception as e:
print_error(f"Exception during vendor login: {str(e)}")
return None
def test_vendor_cannot_access_admin_api(vendor_token: str):
"""Test that vendor token cannot access admin API"""
print_test("Vendor Token on Admin API (Should Block)")
try:
response = requests.get(
f"{BASE_URL}/api/v1/admin/vendors",
headers={"Authorization": f"Bearer {vendor_token}"},
)
if response.status_code in [401, 403]:
data = response.json()
print_success("Vendor correctly blocked from admin API")
print_success(f"Error code: {data.get('error_code', 'N/A')}")
return True
if response.status_code == 200:
print_error("SECURITY ISSUE: Vendor can access admin API!")
return False
print_warning(f"Unexpected status code: {response.status_code}")
return False
except Exception as e:
print_error(f"Exception: {str(e)}")
return False
def test_vendor_cannot_access_customer_api(vendor_token: str):
"""Test that vendor token cannot access customer account pages"""
print_test("Vendor Token on Customer API (Should Block)")
try:
response = requests.get(
f"{BASE_URL}/shop/account/dashboard",
headers={"Authorization": f"Bearer {vendor_token}"},
)
if response.status_code in [401, 403]:
print_success("Vendor correctly blocked from customer pages")
return True
if response.status_code == 200:
print_error("SECURITY ISSUE: Vendor can access customer pages!")
return False
print_warning(f"Unexpected status code: {response.status_code}")
return False
except Exception as e:
print_error(f"Exception: {str(e)}")
return False
# ============================================================================
# CUSTOMER AUTHENTICATION TESTS
# ============================================================================
def test_customer_login() -> dict | None:
"""Test customer login and cookie configuration"""
print_test("Customer Login")
try:
response = requests.post(
f"{BASE_URL}/api/v1/public/vendors/1/customers/login",
json={"username": "customer", "password": "customer123"},
)
if response.status_code == 200:
data = response.json()
cookies = response.cookies
if "access_token" in data:
print_success("Customer login successful")
print_success(f"Received access token: {data['access_token'][:20]}...")
else:
print_error("No access token in response")
return None
if "customer_token" in cookies:
print_success("customer_token cookie set")
print_info("Cookie path should be /shop (verify in browser)")
else:
print_error("customer_token cookie NOT set")
return {"token": data["access_token"], "user": data.get("user", {})}
print_error(f"Login failed: {response.status_code}")
print_error(f"Response: {response.text}")
return None
except Exception as e:
print_error(f"Exception during customer login: {str(e)}")
return None
def test_customer_cannot_access_admin_api(customer_token: str):
"""Test that customer token cannot access admin API"""
print_test("Customer Token on Admin API (Should Block)")
try:
response = requests.get(
f"{BASE_URL}/api/v1/admin/vendors",
headers={"Authorization": f"Bearer {customer_token}"},
)
if response.status_code in [401, 403]:
data = response.json()
print_success("Customer correctly blocked from admin API")
print_success(f"Error code: {data.get('error_code', 'N/A')}")
return True
if response.status_code == 200:
print_error("SECURITY ISSUE: Customer can access admin API!")
return False
print_warning(f"Unexpected status code: {response.status_code}")
return False
except Exception as e:
print_error(f"Exception: {str(e)}")
return False
def test_customer_cannot_access_vendor_api(customer_token: str):
"""Test that customer token cannot access vendor API"""
print_test("Customer Token on Vendor API (Should Block)")
try:
response = requests.get(
f"{BASE_URL}/api/v1/vendor/TESTVENDOR/products",
headers={"Authorization": f"Bearer {customer_token}"},
)
if response.status_code in [401, 403]:
data = response.json()
print_success("Customer correctly blocked from vendor API")
print_success(f"Error code: {data.get('error_code', 'N/A')}")
return True
if response.status_code == 200:
print_error("SECURITY ISSUE: Customer can access vendor API!")
return False
print_warning(f"Unexpected status code: {response.status_code}")
return False
except Exception as e:
print_error(f"Exception: {str(e)}")
return False
def test_public_shop_access():
"""Test that public shop pages are accessible without authentication"""
print_test("Public Shop Access (No Auth Required)")
try:
response = requests.get(f"{BASE_URL}/shop/products")
if response.status_code == 200:
print_success("Public shop pages accessible without auth")
return True
print_error(f"Failed to access public shop: {response.status_code}")
return False
except Exception as e:
print_error(f"Exception: {str(e)}")
return False
def test_health_check():
"""Test health check endpoint"""
print_test("Health Check")
try:
response = requests.get(f"{BASE_URL}/health")
if response.status_code == 200:
data = response.json()
print_success("Health check passed")
print_info(f"Status: {data.get('status', 'N/A')}")
return True
print_error(f"Health check failed: {response.status_code}")
return False
except Exception as e:
print_error(f"Exception: {str(e)}")
return False
# ============================================================================
# MAIN TEST RUNNER
# ============================================================================
def main():
"""Run all tests"""
print(f"\n{Color.BOLD}{Color.CYAN}{'' * 60}")
print(" 🔒 COMPLETE AUTHENTICATION SYSTEM TEST SUITE")
print(f"{'' * 60}{Color.END}")
print(f"Testing server at: {BASE_URL}")
results = {"passed": 0, "failed": 0, "total": 0}
# Health check first
print_section("🏥 System Health")
results["total"] += 1
if test_health_check():
results["passed"] += 1
else:
results["failed"] += 1
print_error("Server not responding. Is it running?")
return
# ========================================================================
# ADMIN TESTS
# ========================================================================
print_section("👤 Admin Authentication Tests")
# Admin login
results["total"] += 1
admin_auth = test_admin_login()
if admin_auth:
results["passed"] += 1
else:
results["failed"] += 1
admin_auth = None
# Admin cross-context tests
if admin_auth:
results["total"] += 1
if test_admin_cannot_access_vendor_api(admin_auth["token"]):
results["passed"] += 1
else:
results["failed"] += 1
results["total"] += 1
if test_admin_cannot_access_customer_api(admin_auth["token"]):
results["passed"] += 1
else:
results["failed"] += 1
# ========================================================================
# VENDOR TESTS
# ========================================================================
print_section("🏪 Vendor Authentication Tests")
# Vendor login
results["total"] += 1
vendor_auth = test_vendor_login()
if vendor_auth:
results["passed"] += 1
else:
results["failed"] += 1
vendor_auth = None
# Vendor cross-context tests
if vendor_auth:
results["total"] += 1
if test_vendor_cannot_access_admin_api(vendor_auth["token"]):
results["passed"] += 1
else:
results["failed"] += 1
results["total"] += 1
if test_vendor_cannot_access_customer_api(vendor_auth["token"]):
results["passed"] += 1
else:
results["failed"] += 1
# ========================================================================
# CUSTOMER TESTS
# ========================================================================
print_section("🛒 Customer Authentication Tests")
# Public shop access
results["total"] += 1
if test_public_shop_access():
results["passed"] += 1
else:
results["failed"] += 1
# Customer login
results["total"] += 1
customer_auth = test_customer_login()
if customer_auth:
results["passed"] += 1
else:
results["failed"] += 1
customer_auth = None
# Customer cross-context tests
if customer_auth:
results["total"] += 1
if test_customer_cannot_access_admin_api(customer_auth["token"]):
results["passed"] += 1
else:
results["failed"] += 1
results["total"] += 1
if test_customer_cannot_access_vendor_api(customer_auth["token"]):
results["passed"] += 1
else:
results["failed"] += 1
# ========================================================================
# RESULTS
# ========================================================================
print_section("📊 Test Results")
print(f"\n{Color.BOLD}Total Tests: {results['total']}{Color.END}")
print_success(f"Passed: {results['passed']}")
if results["failed"] > 0:
print_error(f"Failed: {results['failed']}")
if results["failed"] == 0:
print(f"\n{Color.GREEN}{Color.BOLD}🎉 ALL TESTS PASSED!{Color.END}")
print(
f"{Color.GREEN}Your authentication system is properly isolated!{Color.END}"
)
else:
print(f"\n{Color.RED}{Color.BOLD}⚠️ SOME TESTS FAILED{Color.END}")
print(f"{Color.RED}Please review the output above.{Color.END}")
# Browser tests reminder
print_section("🌐 Manual Browser Tests")
print("Please also verify in browser:")
print("\n1. Open DevTools → Application → Cookies")
print("\n2. Log in as admin:")
print(" - Cookie name: admin_token")
print(" - Path: /admin")
print(" - HttpOnly: ✓")
print("\n3. Log in as vendor:")
print(" - Cookie name: vendor_token")
print(" - Path: /vendor")
print(" - HttpOnly: ✓")
print("\n4. Log in as customer:")
print(" - Cookie name: customer_token")
print(" - Path: /shop")
print(" - HttpOnly: ✓")
print("\n5. Verify cross-context isolation:")
print(" - Admin cookie NOT sent to /vendor/* or /shop/*")
print(" - Vendor cookie NOT sent to /admin/* or /shop/*")
print(" - Customer cookie NOT sent to /admin/* or /vendor/*")
print(f"\n{Color.CYAN}{'' * 60}{Color.END}\n")
if __name__ == "__main__":
main()