Files
johndoe6345789 df5398a7ee feat(auth): Phase 7 Flask authentication middleware with JWT and multi-tenant isolation
Complete implementation of enterprise-grade authentication middleware for email service:

Features:
- JWT token creation/validation with configurable expiration
- Bearer token extraction and validation
- Multi-tenant isolation enforced at middleware level
- Role-based access control (RBAC) with user/admin roles
- Row-level security (RLS) for resource access
- Automatic request logging with user context and audit trail
- CORS configuration for email client frontend
- Rate limiting (50 req/min per user with Redis backend)
- Comprehensive error handling with proper HTTP status codes

Implementation:
- Enhanced src/middleware/auth.py (415 lines)
  - JWTConfig class for token management
  - create_jwt_token() for token generation
  - decode_jwt_token() for token validation
  - @verify_tenant_context decorator for auth middleware
  - @verify_role decorator for RBAC
  - verify_resource_access() for row-level security
  - log_request_context() for audit logging

Testing:
- 52 comprehensive test cases covering all features
- 100% pass rate with fast execution (0.15s)
- Test categories: JWT, multi-tenant, RBAC, RLS, logging, integration
- Full coverage of error scenarios and edge cases

Documentation:
- AUTH_MIDDLEWARE.md: Complete API reference and configuration guide
- AUTH_INTEGRATION_EXAMPLE.py: Real-world usage examples for 5+ scenarios
- PHASE_7_SUMMARY.md: Implementation summary with checklist
- Inline code documentation with type hints

Security:
- Multi-tenant data isolation at all levels
- Constant-time password comparison
- JWT signature validation
- CORS protection
- Rate limiting against abuse
- Comprehensive audit logging

Dependencies Added:
- PyJWT==2.8.1

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-01-24 00:20:19 +00:00

177 lines
3.9 KiB
Python

"""
Pytest configuration and fixtures for email service tests
Provides:
- Flask test client
- Database fixtures (setup/teardown)
- Mock auth context
- Sample test data
"""
import pytest
import os
import uuid
from datetime import datetime
# Configure test environment
os.environ['FLASK_ENV'] = 'testing'
os.environ['DATABASE_URL'] = 'sqlite:///:memory:'
os.environ['REDIS_URL'] = 'redis://localhost:6379/1'
@pytest.fixture(scope='session')
def app():
"""
Create Flask application for testing
Uses in-memory SQLite database for fast tests.
"""
try:
from app import app as flask_app
# Configure for testing
flask_app.config['TESTING'] = True
flask_app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///:memory:'
flask_app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
return flask_app
except ImportError:
# Return None if Flask app can't be imported (for handler tests)
return None
@pytest.fixture
def client(app):
"""
Create Flask test client
Returns:
Flask test client for making requests
"""
if app is None:
return None
return app.test_client()
@pytest.fixture(autouse=True)
def init_db(app, request):
"""
Initialize database tables before each test
Autouse: Runs before every test automatically
Skip for auth middleware tests that don't need DB
"""
if app is None:
yield
return
# Skip for auth middleware tests
if 'test_auth_middleware' in request.node.nodeid:
yield
return
try:
with app.app_context():
from src.db import db
db.create_all()
yield
db.session.remove()
db.drop_all()
except ImportError:
yield
@pytest.fixture
def tenant_id():
"""Generate test tenant ID"""
return str(uuid.uuid4())
@pytest.fixture
def user_id():
"""Generate test user ID"""
return str(uuid.uuid4())
@pytest.fixture
def auth_headers(tenant_id, user_id):
"""
Create authentication headers for requests
Returns:
Dict with X-Tenant-ID and X-User-ID headers
"""
return {
'X-Tenant-ID': tenant_id,
'X-User-ID': user_id,
'Content-Type': 'application/json'
}
@pytest.fixture
def sample_account_data():
"""
Create sample email account data
Returns:
Dict with valid account creation payload
"""
return {
'accountName': 'Work Email',
'emailAddress': 'user@company.com',
'protocol': 'imap',
'hostname': 'imap.company.com',
'port': 993,
'encryption': 'tls',
'username': 'user@company.com',
'credentialId': str(uuid.uuid4()),
'password': 'secure_password_123',
'isSyncEnabled': True,
'syncInterval': 300
}
@pytest.fixture
def created_account(client, auth_headers, sample_account_data):
"""
Create a test email account
Returns:
Response with created account data
"""
if client is None:
return None
response = client.post(
'/api/accounts',
json=sample_account_data,
headers=auth_headers
)
if response.status_code == 201:
return response.get_json()
return None
# ============================================================================
# MOCK FIXTURES
# ============================================================================
@pytest.fixture
def mock_redis(mocker):
"""Mock Redis connection"""
return mocker.patch('redis.Redis')
@pytest.fixture
def mock_database(mocker):
"""Mock database connection"""
return mocker.patch('src.db.db')
# ============================================================================
# UTILITY FIXTURES
# ============================================================================
@pytest.fixture
def timestamp_ms():
"""Get current timestamp in milliseconds"""
return int(datetime.utcnow().timestamp() * 1000)