mcp-security-framework 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. mcp_security_framework/__init__.py +96 -0
  2. mcp_security_framework/cli/__init__.py +18 -0
  3. mcp_security_framework/cli/cert_cli.py +511 -0
  4. mcp_security_framework/cli/security_cli.py +791 -0
  5. mcp_security_framework/constants.py +209 -0
  6. mcp_security_framework/core/__init__.py +61 -0
  7. mcp_security_framework/core/auth_manager.py +1011 -0
  8. mcp_security_framework/core/cert_manager.py +1663 -0
  9. mcp_security_framework/core/permission_manager.py +735 -0
  10. mcp_security_framework/core/rate_limiter.py +602 -0
  11. mcp_security_framework/core/security_manager.py +943 -0
  12. mcp_security_framework/core/ssl_manager.py +735 -0
  13. mcp_security_framework/examples/__init__.py +75 -0
  14. mcp_security_framework/examples/django_example.py +615 -0
  15. mcp_security_framework/examples/fastapi_example.py +472 -0
  16. mcp_security_framework/examples/flask_example.py +506 -0
  17. mcp_security_framework/examples/gateway_example.py +803 -0
  18. mcp_security_framework/examples/microservice_example.py +690 -0
  19. mcp_security_framework/examples/standalone_example.py +576 -0
  20. mcp_security_framework/middleware/__init__.py +250 -0
  21. mcp_security_framework/middleware/auth_middleware.py +292 -0
  22. mcp_security_framework/middleware/fastapi_auth_middleware.py +447 -0
  23. mcp_security_framework/middleware/fastapi_middleware.py +757 -0
  24. mcp_security_framework/middleware/flask_auth_middleware.py +465 -0
  25. mcp_security_framework/middleware/flask_middleware.py +591 -0
  26. mcp_security_framework/middleware/mtls_middleware.py +439 -0
  27. mcp_security_framework/middleware/rate_limit_middleware.py +403 -0
  28. mcp_security_framework/middleware/security_middleware.py +507 -0
  29. mcp_security_framework/schemas/__init__.py +109 -0
  30. mcp_security_framework/schemas/config.py +694 -0
  31. mcp_security_framework/schemas/models.py +709 -0
  32. mcp_security_framework/schemas/responses.py +686 -0
  33. mcp_security_framework/tests/__init__.py +0 -0
  34. mcp_security_framework/utils/__init__.py +121 -0
  35. mcp_security_framework/utils/cert_utils.py +525 -0
  36. mcp_security_framework/utils/crypto_utils.py +475 -0
  37. mcp_security_framework/utils/validation_utils.py +571 -0
  38. mcp_security_framework-0.1.0.dist-info/METADATA +411 -0
  39. mcp_security_framework-0.1.0.dist-info/RECORD +76 -0
  40. mcp_security_framework-0.1.0.dist-info/WHEEL +5 -0
  41. mcp_security_framework-0.1.0.dist-info/entry_points.txt +3 -0
  42. mcp_security_framework-0.1.0.dist-info/top_level.txt +2 -0
  43. tests/__init__.py +0 -0
  44. tests/test_cli/__init__.py +0 -0
  45. tests/test_cli/test_cert_cli.py +379 -0
  46. tests/test_cli/test_security_cli.py +657 -0
  47. tests/test_core/__init__.py +0 -0
  48. tests/test_core/test_auth_manager.py +582 -0
  49. tests/test_core/test_cert_manager.py +795 -0
  50. tests/test_core/test_permission_manager.py +395 -0
  51. tests/test_core/test_rate_limiter.py +626 -0
  52. tests/test_core/test_security_manager.py +841 -0
  53. tests/test_core/test_ssl_manager.py +532 -0
  54. tests/test_examples/__init__.py +8 -0
  55. tests/test_examples/test_fastapi_example.py +264 -0
  56. tests/test_examples/test_flask_example.py +238 -0
  57. tests/test_examples/test_standalone_example.py +292 -0
  58. tests/test_integration/__init__.py +0 -0
  59. tests/test_integration/test_auth_flow.py +502 -0
  60. tests/test_integration/test_certificate_flow.py +527 -0
  61. tests/test_integration/test_fastapi_integration.py +341 -0
  62. tests/test_integration/test_flask_integration.py +398 -0
  63. tests/test_integration/test_standalone_integration.py +493 -0
  64. tests/test_middleware/__init__.py +0 -0
  65. tests/test_middleware/test_fastapi_middleware.py +523 -0
  66. tests/test_middleware/test_flask_middleware.py +582 -0
  67. tests/test_middleware/test_security_middleware.py +493 -0
  68. tests/test_schemas/__init__.py +0 -0
  69. tests/test_schemas/test_config.py +811 -0
  70. tests/test_schemas/test_models.py +879 -0
  71. tests/test_schemas/test_responses.py +1054 -0
  72. tests/test_schemas/test_serialization.py +493 -0
  73. tests/test_utils/__init__.py +0 -0
  74. tests/test_utils/test_cert_utils.py +510 -0
  75. tests/test_utils/test_crypto_utils.py +603 -0
  76. tests/test_utils/test_validation_utils.py +477 -0
@@ -0,0 +1,803 @@
1
+ """
2
+ API Gateway Example Implementation
3
+
4
+ This module provides a complete example of how to implement the MCP Security Framework
5
+ in an API Gateway, including all abstract method implementations for real server usage.
6
+
7
+ The example demonstrates:
8
+ - API Gateway with security framework
9
+ - Request routing and load balancing
10
+ - Rate limiting at gateway level
11
+ - Certificate-based authentication
12
+ - Production-ready security features
13
+ - Comprehensive error handling
14
+
15
+ Author: MCP Security Team
16
+ Version: 1.0.0
17
+ License: MIT
18
+ """
19
+
20
+ import os
21
+ import json
22
+ import logging
23
+ import asyncio
24
+ import aiohttp
25
+ from typing import Dict, List, Any, Optional
26
+ from datetime import datetime, timedelta
27
+
28
+ from mcp_security_framework.core.security_manager import SecurityManager
29
+ from mcp_security_framework.core.auth_manager import AuthManager
30
+ from mcp_security_framework.core.ssl_manager import SSLManager
31
+ from mcp_security_framework.core.permission_manager import PermissionManager
32
+ from mcp_security_framework.core.rate_limiter import RateLimiter
33
+ from mcp_security_framework.schemas.config import SecurityConfig, AuthConfig, SSLConfig
34
+ from mcp_security_framework.schemas.models import AuthResult, AuthStatus, AuthMethod
35
+ from mcp_security_framework.constants import (
36
+ DEFAULT_CLIENT_IP, DEFAULT_SECURITY_HEADERS, AUTH_METHODS,
37
+ ErrorCodes, HTTP_UNAUTHORIZED, HTTP_FORBIDDEN, HTTP_TOO_MANY_REQUESTS
38
+ )
39
+
40
+
41
+ class APIGatewayExample:
42
+ """
43
+ Complete API Gateway Example with Security Framework Implementation
44
+
45
+ This class demonstrates a production-ready API Gateway
46
+ with comprehensive security features including:
47
+ - Request routing and load balancing
48
+ - Gateway-level authentication and authorization
49
+ - Rate limiting at gateway level
50
+ - Certificate-based authentication
51
+ - Request/response transformation
52
+ - Comprehensive logging and monitoring
53
+ """
54
+
55
+ def __init__(self, config_path: Optional[str] = None):
56
+ """
57
+ Initialize API Gateway example with security configuration.
58
+
59
+ Args:
60
+ config_path: Path to security configuration file
61
+ """
62
+ self.config = self._load_config(config_path)
63
+ self.security_manager = SecurityManager(self.config)
64
+ self.logger = logging.getLogger(__name__)
65
+ self._setup_logging()
66
+ self._setup_routing_rules()
67
+ self._setup_load_balancer()
68
+
69
+ def _load_config(self, config_path: Optional[str]) -> SecurityConfig:
70
+ """
71
+ Load security configuration from file or create default.
72
+
73
+ Args:
74
+ config_path: Path to configuration file
75
+
76
+ Returns:
77
+ SecurityConfig: Loaded configuration
78
+ """
79
+ if config_path and os.path.exists(config_path):
80
+ with open(config_path, 'r') as f:
81
+ config_data = json.load(f)
82
+ return SecurityConfig(**config_data)
83
+
84
+ # Create production-ready API Gateway configuration
85
+ return SecurityConfig(
86
+ auth=AuthConfig(
87
+ enabled=True,
88
+ methods=[AUTH_METHODS["API_KEY"], AUTH_METHODS["JWT"], AUTH_METHODS["CERTIFICATE"]],
89
+ api_keys={
90
+ "gateway_key_123": {"username": "gateway-admin", "roles": ["gateway", "admin"]},
91
+ "client_key_456": {"username": "client-app", "roles": ["client", "user"]},
92
+ "service_key_789": {"username": "internal-service", "roles": ["service", "internal"]}
93
+ },
94
+ jwt_secret="your-super-secret-jwt-key-change-in-production",
95
+ jwt_algorithm="HS256",
96
+ jwt_expiry_hours=24,
97
+ public_paths=["/health", "/metrics", "/status"],
98
+ security_headers=DEFAULT_SECURITY_HEADERS
99
+ ),
100
+ ssl=SSLConfig(
101
+ enabled=True,
102
+ cert_file="certs/gateway.crt",
103
+ key_file="certs/gateway.key",
104
+ ca_cert_file="certs/ca.crt",
105
+ verify_mode="CERT_REQUIRED",
106
+ min_version="TLSv1.2"
107
+ ),
108
+ rate_limit={
109
+ "enabled": True,
110
+ "default_requests_per_minute": 500, # Gateway-level limits
111
+ "default_requests_per_hour": 5000,
112
+ "burst_limit": 10,
113
+ "window_size_seconds": 60,
114
+ "storage_backend": "redis",
115
+ "redis_config": {
116
+ "host": "redis-cluster",
117
+ "port": 6379,
118
+ "db": 0,
119
+ "password": None
120
+ },
121
+ "exempt_paths": ["/health", "/metrics", "/status"],
122
+ "exempt_roles": ["gateway", "admin"]
123
+ },
124
+ permissions={
125
+ "enabled": True,
126
+ "roles_file": "config/roles.json",
127
+ "default_role": "user",
128
+ "hierarchy_enabled": True
129
+ },
130
+ logging={
131
+ "enabled": True,
132
+ "level": "INFO",
133
+ "format": "%(asctime)s - %(name)s - %(levelname)s - %(message)s",
134
+ "file_path": "logs/gateway.log",
135
+ "max_file_size": 10,
136
+ "backup_count": 5,
137
+ "console_output": True,
138
+ "json_format": True
139
+ }
140
+ )
141
+
142
+ def _setup_logging(self):
143
+ """Setup logging configuration."""
144
+ if self.config.logging.enabled:
145
+ logging.basicConfig(
146
+ level=getattr(logging, self.config.logging.level),
147
+ format=self.config.logging.format,
148
+ handlers=[
149
+ logging.FileHandler(self.config.logging.file_path) if self.config.logging.file_path else logging.NullHandler(),
150
+ logging.StreamHandler() if self.config.logging.console_output else logging.NullHandler()
151
+ ]
152
+ )
153
+
154
+ def _setup_routing_rules(self):
155
+ """Setup routing rules for different services."""
156
+ self.routing_rules = {
157
+ "/api/v1/users": {
158
+ "service": "user-service",
159
+ "endpoints": ["https://user-service-1:8080", "https://user-service-2:8080"],
160
+ "timeout": 30,
161
+ "retries": 3,
162
+ "required_permissions": ["read", "user"]
163
+ },
164
+ "/api/v1/orders": {
165
+ "service": "order-service",
166
+ "endpoints": ["https://order-service-1:8081", "https://order-service-2:8081"],
167
+ "timeout": 60,
168
+ "retries": 3,
169
+ "required_permissions": ["read", "write", "order"]
170
+ },
171
+ "/api/v1/payments": {
172
+ "service": "payment-service",
173
+ "endpoints": ["https://payment-service-1:8082", "https://payment-service-2:8082"],
174
+ "timeout": 45,
175
+ "retries": 2,
176
+ "required_permissions": ["write", "payment"]
177
+ },
178
+ "/api/v1/admin": {
179
+ "service": "admin-service",
180
+ "endpoints": ["https://admin-service:8083"],
181
+ "timeout": 30,
182
+ "retries": 1,
183
+ "required_permissions": ["admin"]
184
+ }
185
+ }
186
+
187
+ def _setup_load_balancer(self):
188
+ """Setup load balancer for service endpoints."""
189
+ self.load_balancer = {
190
+ "algorithm": "round_robin", # round_robin, least_connections, weighted
191
+ "health_check_interval": 30,
192
+ "health_check_timeout": 5,
193
+ "max_failures": 3
194
+ }
195
+ self.current_endpoint_index = {} # Track current endpoint for round-robin
196
+
197
+ async def route_request(self, request_data: Dict[str, Any]) -> Dict[str, Any]:
198
+ """
199
+ Route request through API Gateway with security validation.
200
+
201
+ Args:
202
+ request_data: Request data including path, method, headers, body
203
+
204
+ Returns:
205
+ Dict[str, Any]: Response data
206
+ """
207
+ try:
208
+ # Extract request components
209
+ path = request_data.get("path", "")
210
+ method = request_data.get("method", "GET")
211
+ headers = request_data.get("headers", {})
212
+ body = request_data.get("body", {})
213
+ client_ip = request_data.get("client_ip", DEFAULT_CLIENT_IP)
214
+ request_id = request_data.get("request_id", self._generate_request_id())
215
+
216
+ # Step 1: Rate limiting check
217
+ if not self.check_rate_limit(client_ip):
218
+ return self._create_error_response(
219
+ "Rate limit exceeded",
220
+ ErrorCodes.RATE_LIMIT_EXCEEDED_ERROR,
221
+ HTTP_TOO_MANY_REQUESTS,
222
+ request_id
223
+ )
224
+
225
+ # Step 2: Authentication
226
+ auth_result = self.authenticate_request(headers)
227
+ if not auth_result.is_valid:
228
+ return self._create_error_response(
229
+ "Authentication failed",
230
+ auth_result.error_code,
231
+ HTTP_UNAUTHORIZED,
232
+ request_id,
233
+ auth_result.error_message
234
+ )
235
+
236
+ # Step 3: Find routing rule
237
+ routing_rule = self._find_routing_rule(path)
238
+ if not routing_rule:
239
+ return self._create_error_response(
240
+ "Service not found",
241
+ ErrorCodes.GENERAL_ERROR,
242
+ 404,
243
+ request_id
244
+ )
245
+
246
+ # Step 4: Authorization check
247
+ if not self.check_permissions(auth_result.roles, routing_rule["required_permissions"]):
248
+ return self._create_error_response(
249
+ "Insufficient permissions",
250
+ ErrorCodes.PERMISSION_DENIED_ERROR,
251
+ HTTP_FORBIDDEN,
252
+ request_id
253
+ )
254
+
255
+ # Step 5: Route request to backend service
256
+ response = await self._forward_request(
257
+ routing_rule, method, path, headers, body, request_id
258
+ )
259
+
260
+ # Step 6: Log security event
261
+ self._log_security_event("request_routed", {
262
+ "username": auth_result.username,
263
+ "path": path,
264
+ "method": method,
265
+ "service": routing_rule["service"],
266
+ "success": True,
267
+ "request_id": request_id,
268
+ "timestamp": datetime.utcnow().isoformat()
269
+ })
270
+
271
+ return response
272
+
273
+ except Exception as e:
274
+ self.logger.error(f"Request routing failed: {str(e)}")
275
+ return self._create_error_response(
276
+ "Internal server error",
277
+ ErrorCodes.GENERAL_ERROR,
278
+ 500,
279
+ request_id if 'request_id' in locals() else self._generate_request_id()
280
+ )
281
+
282
+ def authenticate_request(self, headers: Dict[str, str]) -> AuthResult:
283
+ """
284
+ Authenticate request using headers.
285
+
286
+ Args:
287
+ headers: Request headers
288
+
289
+ Returns:
290
+ AuthResult: Authentication result
291
+ """
292
+ try:
293
+ # Try API key authentication
294
+ api_key = headers.get("X-API-Key") or headers.get("Authorization", "").replace("Bearer ", "")
295
+ if api_key:
296
+ return self.security_manager.auth_manager.authenticate_api_key(api_key)
297
+
298
+ # Try JWT authentication
299
+ auth_header = headers.get("Authorization", "")
300
+ if auth_header.startswith("Bearer "):
301
+ token = auth_header[7:]
302
+ return self.security_manager.auth_manager.authenticate_jwt_token(token)
303
+
304
+ # Try certificate authentication (would be handled at TLS level)
305
+ client_cert = headers.get("X-Client-Cert")
306
+ if client_cert:
307
+ return self.security_manager.auth_manager.authenticate_certificate(client_cert)
308
+
309
+ return AuthResult(
310
+ is_valid=False,
311
+ status=AuthStatus.FAILED,
312
+ username=None,
313
+ roles=[],
314
+ auth_method=None,
315
+ error_code=ErrorCodes.AUTHENTICATION_ERROR,
316
+ error_message="No valid authentication credentials found"
317
+ )
318
+
319
+ except Exception as e:
320
+ self.logger.error(f"Authentication failed: {str(e)}")
321
+ return AuthResult(
322
+ is_valid=False,
323
+ status=AuthStatus.FAILED,
324
+ username=None,
325
+ roles=[],
326
+ auth_method=None,
327
+ error_code=ErrorCodes.AUTHENTICATION_ERROR,
328
+ error_message=str(e)
329
+ )
330
+
331
+ def check_permissions(self, user_roles: List[str], required_permissions: List[str]) -> bool:
332
+ """
333
+ Check if user has required permissions.
334
+
335
+ Args:
336
+ user_roles: User roles
337
+ required_permissions: Required permissions
338
+
339
+ Returns:
340
+ bool: True if user has required permissions
341
+ """
342
+ try:
343
+ return self.security_manager.permission_manager.validate_access(
344
+ user_roles, required_permissions
345
+ )
346
+ except Exception as e:
347
+ self.logger.error(f"Permission check failed: {str(e)}")
348
+ return False
349
+
350
+ def check_rate_limit(self, identifier: str) -> bool:
351
+ """
352
+ Check if request is within rate limits.
353
+
354
+ Args:
355
+ identifier: Request identifier (IP, user ID, etc.)
356
+
357
+ Returns:
358
+ bool: True if request is within rate limits
359
+ """
360
+ try:
361
+ return self.security_manager.rate_limiter.check_rate_limit(identifier)
362
+ except Exception as e:
363
+ self.logger.error(f"Rate limit check failed: {str(e)}")
364
+ return True # Allow request if rate limiting fails
365
+
366
+ def _find_routing_rule(self, path: str) -> Optional[Dict[str, Any]]:
367
+ """
368
+ Find routing rule for the given path.
369
+
370
+ Args:
371
+ path: Request path
372
+
373
+ Returns:
374
+ Optional[Dict[str, Any]]: Routing rule or None
375
+ """
376
+ for route_path, rule in self.routing_rules.items():
377
+ if path.startswith(route_path):
378
+ return rule
379
+ return None
380
+
381
+ async def _forward_request(self, routing_rule: Dict[str, Any], method: str, path: str,
382
+ headers: Dict[str, str], body: Dict[str, Any], request_id: str) -> Dict[str, Any]:
383
+ """
384
+ Forward request to backend service.
385
+
386
+ Args:
387
+ routing_rule: Routing rule for the service
388
+ method: HTTP method
389
+ path: Request path
390
+ headers: Request headers
391
+ body: Request body
392
+ request_id: Request ID
393
+
394
+ Returns:
395
+ Dict[str, Any]: Response from backend service
396
+ """
397
+ try:
398
+ # Select endpoint using load balancer
399
+ endpoint = self._select_endpoint(routing_rule["service"], routing_rule["endpoints"])
400
+
401
+ # Prepare headers for backend service
402
+ backend_headers = {
403
+ **headers,
404
+ "X-Request-ID": request_id,
405
+ "X-Gateway": "true",
406
+ "X-Forwarded-For": headers.get("X-Forwarded-For", DEFAULT_CLIENT_IP),
407
+ "X-Original-Path": path
408
+ }
409
+
410
+ # Create SSL context if needed
411
+ ssl_context = None
412
+ if self.config.ssl.enabled:
413
+ ssl_context = self.security_manager.ssl_manager.create_client_context()
414
+
415
+ # Make request to backend service
416
+ timeout = aiohttp.ClientTimeout(total=routing_rule["timeout"])
417
+
418
+ async with aiohttp.ClientSession(timeout=timeout) as session:
419
+ url = f"{endpoint}{path}"
420
+
421
+ if method.upper() == "GET":
422
+ async with session.get(url, headers=backend_headers, ssl=ssl_context) as response:
423
+ return await self._process_response(response, request_id)
424
+ elif method.upper() == "POST":
425
+ async with session.post(url, headers=backend_headers, json=body, ssl=ssl_context) as response:
426
+ return await self._process_response(response, request_id)
427
+ elif method.upper() == "PUT":
428
+ async with session.put(url, headers=backend_headers, json=body, ssl=ssl_context) as response:
429
+ return await self._process_response(response, request_id)
430
+ elif method.upper() == "DELETE":
431
+ async with session.delete(url, headers=backend_headers, ssl=ssl_context) as response:
432
+ return await self._process_response(response, request_id)
433
+ else:
434
+ raise ValueError(f"Unsupported HTTP method: {method}")
435
+
436
+ except Exception as e:
437
+ self.logger.error(f"Request forwarding failed: {str(e)}")
438
+ return self._create_error_response(
439
+ "Backend service unavailable",
440
+ ErrorCodes.GENERAL_ERROR,
441
+ 503,
442
+ request_id
443
+ )
444
+
445
+ def _select_endpoint(self, service: str, endpoints: List[str]) -> str:
446
+ """
447
+ Select endpoint using load balancer algorithm.
448
+
449
+ Args:
450
+ service: Service name
451
+ endpoints: List of available endpoints
452
+
453
+ Returns:
454
+ str: Selected endpoint
455
+ """
456
+ if not endpoints:
457
+ raise ValueError(f"No endpoints available for service: {service}")
458
+
459
+ if self.load_balancer["algorithm"] == "round_robin":
460
+ # Simple round-robin implementation
461
+ if service not in self.current_endpoint_index:
462
+ self.current_endpoint_index[service] = 0
463
+
464
+ endpoint = endpoints[self.current_endpoint_index[service]]
465
+ self.current_endpoint_index[service] = (self.current_endpoint_index[service] + 1) % len(endpoints)
466
+ return endpoint
467
+ else:
468
+ # Default to first endpoint
469
+ return endpoints[0]
470
+
471
+ async def _process_response(self, response: aiohttp.ClientResponse, request_id: str) -> Dict[str, Any]:
472
+ """
473
+ Process response from backend service.
474
+
475
+ Args:
476
+ response: Response from backend service
477
+ request_id: Request ID
478
+
479
+ Returns:
480
+ Dict[str, Any]: Processed response
481
+ """
482
+ try:
483
+ response_data = await response.json()
484
+
485
+ # Add gateway headers
486
+ response_headers = {
487
+ "X-Gateway": "true",
488
+ "X-Request-ID": request_id,
489
+ "X-Response-Time": str(response.headers.get("X-Response-Time", "")),
490
+ **DEFAULT_SECURITY_HEADERS
491
+ }
492
+
493
+ return {
494
+ "success": True,
495
+ "status_code": response.status,
496
+ "headers": response_headers,
497
+ "data": response_data,
498
+ "request_id": request_id,
499
+ "timestamp": datetime.utcnow().isoformat()
500
+ }
501
+
502
+ except Exception as e:
503
+ self.logger.error(f"Response processing failed: {str(e)}")
504
+ return self._create_error_response(
505
+ "Response processing failed",
506
+ ErrorCodes.GENERAL_ERROR,
507
+ 500,
508
+ request_id
509
+ )
510
+
511
+ def _create_error_response(self, message: str, error_code: int, status_code: int,
512
+ request_id: str, details: Optional[str] = None) -> Dict[str, Any]:
513
+ """
514
+ Create error response.
515
+
516
+ Args:
517
+ message: Error message
518
+ error_code: Error code
519
+ status_code: HTTP status code
520
+ request_id: Request ID
521
+ details: Additional error details
522
+
523
+ Returns:
524
+ Dict[str, Any]: Error response
525
+ """
526
+ return {
527
+ "success": False,
528
+ "error": message,
529
+ "error_code": error_code,
530
+ "status_code": status_code,
531
+ "request_id": request_id,
532
+ "details": details,
533
+ "timestamp": datetime.utcnow().isoformat()
534
+ }
535
+
536
+ def _generate_request_id(self) -> str:
537
+ """Generate unique request ID for tracing."""
538
+ import uuid
539
+ return str(uuid.uuid4())
540
+
541
+ def _log_security_event(self, event_type: str, details: Dict[str, Any]):
542
+ """
543
+ Log security event.
544
+
545
+ Args:
546
+ event_type: Type of security event
547
+ details: Event details
548
+ """
549
+ try:
550
+ self.logger.info(
551
+ f"Security event: {event_type}",
552
+ extra={
553
+ "event_type": event_type,
554
+ "gateway": "api-gateway",
555
+ "timestamp": details.get("timestamp"),
556
+ "username": details.get("username"),
557
+ "path": details.get("path"),
558
+ "method": details.get("method"),
559
+ "service": details.get("service"),
560
+ "success": details.get("success"),
561
+ "request_id": details.get("request_id"),
562
+ **details
563
+ }
564
+ )
565
+ except Exception as e:
566
+ self.logger.error(f"Failed to log security event: {str(e)}")
567
+
568
+ async def health_check(self) -> Dict[str, Any]:
569
+ """
570
+ Perform health check for the API Gateway.
571
+
572
+ Returns:
573
+ Dict[str, Any]: Health check result
574
+ """
575
+ try:
576
+ # Check security manager health
577
+ security_healthy = self.security_manager is not None
578
+
579
+ # Check rate limiter health
580
+ rate_limit_healthy = self.security_manager.rate_limiter is not None
581
+
582
+ # Check routing rules
583
+ routing_healthy = len(self.routing_rules) > 0
584
+
585
+ # Check SSL configuration
586
+ ssl_healthy = self.config.ssl.enabled and os.path.exists(self.config.ssl.cert_file)
587
+
588
+ overall_healthy = all([
589
+ security_healthy,
590
+ rate_limit_healthy,
591
+ routing_healthy,
592
+ ssl_healthy
593
+ ])
594
+
595
+ return {
596
+ "status": "healthy" if overall_healthy else "unhealthy",
597
+ "gateway": "api-gateway",
598
+ "timestamp": datetime.utcnow().isoformat(),
599
+ "checks": {
600
+ "security_manager": security_healthy,
601
+ "rate_limiter": rate_limit_healthy,
602
+ "routing_rules": routing_healthy,
603
+ "ssl_configuration": ssl_healthy
604
+ }
605
+ }
606
+ except Exception as e:
607
+ self.logger.error(f"Health check failed: {str(e)}")
608
+ return {
609
+ "status": "unhealthy",
610
+ "gateway": "api-gateway",
611
+ "error": str(e),
612
+ "timestamp": datetime.utcnow().isoformat()
613
+ }
614
+
615
+ async def get_metrics(self) -> Dict[str, Any]:
616
+ """
617
+ Get API Gateway metrics.
618
+
619
+ Returns:
620
+ Dict[str, Any]: Metrics data
621
+ """
622
+ try:
623
+ # Get rate limiter metrics
624
+ rate_limit_stats = self.security_manager.rate_limiter.get_statistics()
625
+
626
+ # Get routing statistics
627
+ routing_stats = {
628
+ "total_routes": len(self.routing_rules),
629
+ "services": list(set(rule["service"] for rule in self.routing_rules.values())),
630
+ "load_balancer_algorithm": self.load_balancer["algorithm"]
631
+ }
632
+
633
+ return {
634
+ "gateway": "api-gateway",
635
+ "timestamp": datetime.utcnow().isoformat(),
636
+ "rate_limiting": rate_limit_stats,
637
+ "routing": routing_stats,
638
+ "security": self.get_security_status()
639
+ }
640
+ except Exception as e:
641
+ self.logger.error(f"Failed to get metrics: {str(e)}")
642
+ return {
643
+ "gateway": "api-gateway",
644
+ "error": str(e),
645
+ "timestamp": datetime.utcnow().isoformat()
646
+ }
647
+
648
+ def get_security_status(self) -> Dict[str, Any]:
649
+ """
650
+ Get security framework status.
651
+
652
+ Returns:
653
+ Dict[str, Any]: Security status information
654
+ """
655
+ return {
656
+ "gateway": "api-gateway",
657
+ "ssl_enabled": self.config.ssl.enabled,
658
+ "auth_enabled": self.config.auth.enabled,
659
+ "rate_limiting_enabled": self.config.rate_limit.enabled,
660
+ "permissions_enabled": self.config.permissions.enabled,
661
+ "logging_enabled": self.config.logging.enabled,
662
+ "auth_methods": self.config.auth.methods,
663
+ "timestamp": datetime.utcnow().isoformat()
664
+ }
665
+
666
+
667
+ # Example usage and testing
668
+ class APIGatewayExampleTest:
669
+ """Test class for API Gateway example functionality."""
670
+
671
+ @staticmethod
672
+ async def test_authentication():
673
+ """Test authentication functionality."""
674
+ gateway = APIGatewayExample()
675
+
676
+ # Test API key authentication
677
+ headers = {"X-API-Key": "gateway_key_123"}
678
+ auth_result = gateway.authenticate_request(headers)
679
+ assert auth_result.is_valid
680
+ assert auth_result.username == "gateway-admin"
681
+ assert "gateway" in auth_result.roles
682
+
683
+ print("✅ API Key authentication test passed")
684
+
685
+ @staticmethod
686
+ async def test_permissions():
687
+ """Test permission checking."""
688
+ gateway = APIGatewayExample()
689
+
690
+ # Test gateway permissions
691
+ gateway_roles = ["gateway"]
692
+ client_roles = ["client"]
693
+ admin_roles = ["admin"]
694
+
695
+ # Gateway should have gateway permissions
696
+ assert gateway.check_permissions(gateway_roles, ["read", "write"])
697
+
698
+ # Client should have client permissions
699
+ assert gateway.check_permissions(client_roles, ["read"])
700
+
701
+ # Admin should have all permissions
702
+ assert gateway.check_permissions(admin_roles, ["read", "write", "delete"])
703
+
704
+ print("✅ Permission checking test passed")
705
+
706
+ @staticmethod
707
+ async def test_rate_limiting():
708
+ """Test rate limiting functionality."""
709
+ gateway = APIGatewayExample()
710
+
711
+ # Test rate limiting
712
+ identifier = "test_client"
713
+ for i in range(5):
714
+ is_allowed = gateway.check_rate_limit(identifier)
715
+ print(f"Request {i+1}: {'Allowed' if is_allowed else 'Blocked'}")
716
+
717
+ print("✅ Rate limiting test completed")
718
+
719
+ @staticmethod
720
+ async def test_routing():
721
+ """Test routing functionality."""
722
+ gateway = APIGatewayExample()
723
+
724
+ # Test routing rule finding
725
+ rule = gateway._find_routing_rule("/api/v1/users")
726
+ assert rule is not None
727
+ assert rule["service"] == "user-service"
728
+
729
+ rule = gateway._find_routing_rule("/api/v1/orders")
730
+ assert rule is not None
731
+ assert rule["service"] == "order-service"
732
+
733
+ rule = gateway._find_routing_rule("/unknown/path")
734
+ assert rule is None
735
+
736
+ print("✅ Routing test passed")
737
+
738
+ @staticmethod
739
+ async def test_health_check():
740
+ """Test health check functionality."""
741
+ gateway = APIGatewayExample()
742
+
743
+ health = await gateway.health_check()
744
+ assert "status" in health
745
+ assert "gateway" in health
746
+ assert health["gateway"] == "api-gateway"
747
+
748
+ print("✅ Health check test passed")
749
+
750
+ @staticmethod
751
+ async def test_metrics():
752
+ """Test metrics functionality."""
753
+ gateway = APIGatewayExample()
754
+
755
+ metrics = await gateway.get_metrics()
756
+ assert "gateway" in metrics
757
+ assert "timestamp" in metrics
758
+ assert metrics["gateway"] == "api-gateway"
759
+
760
+ print("✅ Metrics test passed")
761
+
762
+
763
+ async def main():
764
+ """Main function for testing and example usage."""
765
+ # Run tests
766
+ print("Running API Gateway Example Tests...")
767
+ await APIGatewayExampleTest.test_authentication()
768
+ await APIGatewayExampleTest.test_permissions()
769
+ await APIGatewayExampleTest.test_rate_limiting()
770
+ await APIGatewayExampleTest.test_routing()
771
+ await APIGatewayExampleTest.test_health_check()
772
+ await APIGatewayExampleTest.test_metrics()
773
+
774
+ # Example usage
775
+ print("\nExample Usage:")
776
+ gateway = APIGatewayExample()
777
+
778
+ # Route a request
779
+ request_data = {
780
+ "path": "/api/v1/users/123",
781
+ "method": "GET",
782
+ "headers": {
783
+ "X-API-Key": "client_key_456",
784
+ "X-Forwarded-For": "192.168.1.100"
785
+ },
786
+ "body": {},
787
+ "client_ip": "192.168.1.100"
788
+ }
789
+
790
+ result = await gateway.route_request(request_data)
791
+ print(f"Request result: {result}")
792
+
793
+ # Get health status
794
+ health = await gateway.health_check()
795
+ print(f"Health status: {health}")
796
+
797
+ # Get metrics
798
+ metrics = await gateway.get_metrics()
799
+ print(f"Metrics: {metrics}")
800
+
801
+
802
+ if __name__ == "__main__":
803
+ asyncio.run(main())