mcp-security-framework 0.1.0__py3-none-any.whl → 1.1.1__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 (58) hide show
  1. mcp_security_framework/__init__.py +26 -15
  2. mcp_security_framework/cli/__init__.py +1 -1
  3. mcp_security_framework/cli/cert_cli.py +233 -197
  4. mcp_security_framework/cli/security_cli.py +324 -234
  5. mcp_security_framework/constants.py +21 -27
  6. mcp_security_framework/core/auth_manager.py +49 -20
  7. mcp_security_framework/core/cert_manager.py +398 -104
  8. mcp_security_framework/core/permission_manager.py +13 -9
  9. mcp_security_framework/core/rate_limiter.py +10 -0
  10. mcp_security_framework/core/security_manager.py +286 -229
  11. mcp_security_framework/examples/__init__.py +6 -0
  12. mcp_security_framework/examples/comprehensive_example.py +954 -0
  13. mcp_security_framework/examples/django_example.py +276 -202
  14. mcp_security_framework/examples/fastapi_example.py +897 -393
  15. mcp_security_framework/examples/flask_example.py +311 -200
  16. mcp_security_framework/examples/gateway_example.py +373 -214
  17. mcp_security_framework/examples/microservice_example.py +337 -172
  18. mcp_security_framework/examples/standalone_example.py +719 -478
  19. mcp_security_framework/examples/test_all_examples.py +572 -0
  20. mcp_security_framework/middleware/__init__.py +46 -55
  21. mcp_security_framework/middleware/auth_middleware.py +62 -63
  22. mcp_security_framework/middleware/fastapi_auth_middleware.py +179 -110
  23. mcp_security_framework/middleware/fastapi_middleware.py +156 -148
  24. mcp_security_framework/middleware/flask_auth_middleware.py +267 -107
  25. mcp_security_framework/middleware/flask_middleware.py +183 -157
  26. mcp_security_framework/middleware/mtls_middleware.py +106 -117
  27. mcp_security_framework/middleware/rate_limit_middleware.py +105 -101
  28. mcp_security_framework/middleware/security_middleware.py +109 -124
  29. mcp_security_framework/schemas/config.py +2 -1
  30. mcp_security_framework/schemas/models.py +19 -6
  31. mcp_security_framework/utils/cert_utils.py +14 -8
  32. mcp_security_framework/utils/datetime_compat.py +116 -0
  33. {mcp_security_framework-0.1.0.dist-info → mcp_security_framework-1.1.1.dist-info}/METADATA +2 -1
  34. mcp_security_framework-1.1.1.dist-info/RECORD +84 -0
  35. tests/conftest.py +303 -0
  36. tests/test_cli/test_cert_cli.py +194 -174
  37. tests/test_cli/test_security_cli.py +274 -247
  38. tests/test_core/test_cert_manager.py +33 -19
  39. tests/test_core/test_security_manager.py +2 -2
  40. tests/test_examples/test_comprehensive_example.py +613 -0
  41. tests/test_examples/test_fastapi_example.py +290 -169
  42. tests/test_examples/test_flask_example.py +304 -162
  43. tests/test_examples/test_standalone_example.py +106 -168
  44. tests/test_integration/test_auth_flow.py +214 -198
  45. tests/test_integration/test_certificate_flow.py +181 -150
  46. tests/test_integration/test_fastapi_integration.py +140 -149
  47. tests/test_integration/test_flask_integration.py +144 -141
  48. tests/test_integration/test_standalone_integration.py +331 -300
  49. tests/test_middleware/test_fastapi_auth_middleware.py +745 -0
  50. tests/test_middleware/test_fastapi_middleware.py +147 -132
  51. tests/test_middleware/test_flask_auth_middleware.py +696 -0
  52. tests/test_middleware/test_flask_middleware.py +201 -179
  53. tests/test_middleware/test_security_middleware.py +151 -130
  54. tests/test_utils/test_datetime_compat.py +147 -0
  55. mcp_security_framework-0.1.0.dist-info/RECORD +0 -76
  56. {mcp_security_framework-0.1.0.dist-info → mcp_security_framework-1.1.1.dist-info}/WHEEL +0 -0
  57. {mcp_security_framework-0.1.0.dist-info → mcp_security_framework-1.1.1.dist-info}/entry_points.txt +0 -0
  58. {mcp_security_framework-0.1.0.dist-info → mcp_security_framework-1.1.1.dist-info}/top_level.txt +0 -0
@@ -17,31 +17,37 @@ Version: 1.0.0
17
17
  License: MIT
18
18
  """
19
19
 
20
- import os
20
+ import asyncio
21
21
  import json
22
22
  import logging
23
- import asyncio
23
+ import os
24
+ from datetime import datetime, timedelta, timezone
25
+ from typing import Any, Dict, List, Optional
26
+
24
27
  import aiohttp
25
- from typing import Dict, List, Any, Optional
26
- from datetime import datetime, timedelta
27
28
 
28
- from mcp_security_framework.core.security_manager import SecurityManager
29
+ from mcp_security_framework.constants import (
30
+ AUTH_METHODS,
31
+ DEFAULT_CLIENT_IP,
32
+ DEFAULT_SECURITY_HEADERS,
33
+ HTTP_FORBIDDEN,
34
+ HTTP_TOO_MANY_REQUESTS,
35
+ HTTP_UNAUTHORIZED,
36
+ ErrorCodes,
37
+ )
29
38
  from mcp_security_framework.core.auth_manager import AuthManager
30
- from mcp_security_framework.core.ssl_manager import SSLManager
31
39
  from mcp_security_framework.core.permission_manager import PermissionManager
32
40
  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
- )
41
+ from mcp_security_framework.core.security_manager import SecurityManager
42
+ from mcp_security_framework.core.ssl_manager import SSLManager
43
+ from mcp_security_framework.schemas.config import AuthConfig, SecurityConfig, SSLConfig
44
+ from mcp_security_framework.schemas.models import AuthMethod, AuthResult, AuthStatus
39
45
 
40
46
 
41
47
  class MicroserviceExample:
42
48
  """
43
49
  Complete Microservice Example with Security Framework Implementation
44
-
50
+
45
51
  This class demonstrates a production-ready microservice
46
52
  with comprehensive security features including:
47
53
  - Service-to-service authentication
@@ -51,11 +57,11 @@ class MicroserviceExample:
51
57
  - Comprehensive logging and monitoring
52
58
  - Health checks and metrics
53
59
  """
54
-
60
+
55
61
  def __init__(self, service_name: str, config_path: Optional[str] = None):
56
62
  """
57
63
  Initialize microservice example with security configuration.
58
-
64
+
59
65
  Args:
60
66
  service_name: Name of the microservice
61
67
  config_path: Path to security configuration file
@@ -66,45 +72,58 @@ class MicroserviceExample:
66
72
  self.logger = logging.getLogger(__name__)
67
73
  self._setup_logging()
68
74
  self._setup_service_registry()
69
-
75
+
70
76
  def _load_config(self, config_path: Optional[str]) -> SecurityConfig:
71
77
  """
72
78
  Load security configuration from file or create default.
73
-
79
+
74
80
  Args:
75
81
  config_path: Path to configuration file
76
-
82
+
77
83
  Returns:
78
84
  SecurityConfig: Loaded configuration
79
85
  """
80
86
  if config_path and os.path.exists(config_path):
81
- with open(config_path, 'r') as f:
87
+ with open(config_path, "r") as f:
82
88
  config_data = json.load(f)
83
89
  return SecurityConfig(**config_data)
84
-
90
+
85
91
  # Create production-ready microservice configuration
86
92
  return SecurityConfig(
87
93
  auth=AuthConfig(
88
94
  enabled=True,
89
- methods=[AUTH_METHODS["API_KEY"], AUTH_METHODS["JWT"], AUTH_METHODS["CERTIFICATE"]],
95
+ methods=[
96
+ AUTH_METHODS["API_KEY"],
97
+ AUTH_METHODS["JWT"],
98
+ AUTH_METHODS["CERTIFICATE"],
99
+ ],
90
100
  api_keys={
91
- "service_key_123": {"username": "user-service", "roles": ["service", "user"]},
92
- "service_key_456": {"username": "order-service", "roles": ["service", "order"]},
93
- "service_key_789": {"username": "payment-service", "roles": ["service", "payment"]}
101
+ "service_key_123": {
102
+ "username": "user-service",
103
+ "roles": ["service", "user"],
104
+ },
105
+ "service_key_456": {
106
+ "username": "order-service",
107
+ "roles": ["service", "order"],
108
+ },
109
+ "service_key_789": {
110
+ "username": "payment-service",
111
+ "roles": ["service", "payment"],
112
+ },
94
113
  },
95
114
  jwt_secret="your-super-secret-jwt-key-change-in-production",
96
115
  jwt_algorithm="HS256",
97
116
  jwt_expiry_hours=24,
98
117
  public_paths=["/health", "/metrics", "/ready"],
99
- security_headers=DEFAULT_SECURITY_HEADERS
118
+ security_headers=DEFAULT_SECURITY_HEADERS,
100
119
  ),
101
120
  ssl=SSLConfig(
102
- enabled=True,
103
- cert_file=f"certs/{self.service_name}.crt",
104
- key_file=f"certs/{self.service_name}.key",
105
- ca_cert_file="certs/ca.crt",
121
+ enabled=False, # Disable SSL for example
122
+ cert_file=None,
123
+ key_file=None,
124
+ ca_cert_file=None,
106
125
  verify_mode="CERT_REQUIRED",
107
- min_version="TLSv1.2"
126
+ min_version="TLSv1.2",
108
127
  ),
109
128
  rate_limit={
110
129
  "enabled": True,
@@ -117,16 +136,16 @@ class MicroserviceExample:
117
136
  "host": "redis-cluster",
118
137
  "port": 6379,
119
138
  "db": 0,
120
- "password": None
139
+ "password": None,
121
140
  },
122
141
  "exempt_paths": ["/health", "/metrics", "/ready"],
123
- "exempt_roles": ["service"]
142
+ "exempt_roles": ["service"],
124
143
  },
125
144
  permissions={
126
145
  "enabled": True,
127
146
  "roles_file": "config/roles.json",
128
147
  "default_role": "service",
129
- "hierarchy_enabled": True
148
+ "hierarchy_enabled": True,
130
149
  },
131
150
  logging={
132
151
  "enabled": True,
@@ -136,10 +155,10 @@ class MicroserviceExample:
136
155
  "max_file_size": 10,
137
156
  "backup_count": 5,
138
157
  "console_output": True,
139
- "json_format": True # JSON format for microservices
140
- }
158
+ "json_format": True, # JSON format for microservices
159
+ },
141
160
  )
142
-
161
+
143
162
  def _setup_logging(self):
144
163
  """Setup logging configuration."""
145
164
  if self.config.logging.enabled:
@@ -147,104 +166,127 @@ class MicroserviceExample:
147
166
  level=getattr(logging, self.config.logging.level),
148
167
  format=self.config.logging.format,
149
168
  handlers=[
150
- logging.FileHandler(self.config.logging.file_path) if self.config.logging.file_path else logging.NullHandler(),
151
- logging.StreamHandler() if self.config.logging.console_output else logging.NullHandler()
152
- ]
169
+ (
170
+ logging.FileHandler(self.config.logging.file_path)
171
+ if self.config.logging.file_path
172
+ else logging.NullHandler()
173
+ ),
174
+ (
175
+ logging.StreamHandler()
176
+ if self.config.logging.console_output
177
+ else logging.NullHandler()
178
+ ),
179
+ ],
153
180
  )
154
-
181
+
155
182
  def _setup_service_registry(self):
156
183
  """Setup service registry for microservice discovery."""
157
184
  self.service_registry = {
158
185
  "user-service": {
159
186
  "url": "https://user-service:8080",
160
187
  "health_check": "/health",
161
- "api_key": "service_key_123"
188
+ "api_key": "service_key_123",
162
189
  },
163
190
  "order-service": {
164
191
  "url": "https://order-service:8081",
165
192
  "health_check": "/health",
166
- "api_key": "service_key_456"
193
+ "api_key": "service_key_456",
167
194
  },
168
195
  "payment-service": {
169
196
  "url": "https://payment-service:8082",
170
197
  "health_check": "/health",
171
- "api_key": "service_key_789"
172
- }
198
+ "api_key": "service_key_789",
199
+ },
173
200
  }
174
-
175
- async def call_service(self, service_name: str, endpoint: str, method: str = "GET", data: Optional[Dict] = None) -> Dict[str, Any]:
201
+
202
+ async def call_service(
203
+ self,
204
+ service_name: str,
205
+ endpoint: str,
206
+ method: str = "GET",
207
+ data: Optional[Dict] = None,
208
+ ) -> Dict[str, Any]:
176
209
  """
177
210
  Call another microservice with security.
178
-
211
+
179
212
  Args:
180
213
  service_name: Name of the service to call
181
214
  endpoint: Service endpoint
182
215
  method: HTTP method
183
216
  data: Request data
184
-
217
+
185
218
  Returns:
186
219
  Dict[str, Any]: Service response
187
220
  """
188
221
  try:
189
222
  if service_name not in self.service_registry:
190
223
  raise ValueError(f"Service {service_name} not found in registry")
191
-
224
+
192
225
  service_info = self.service_registry[service_name]
193
226
  url = f"{service_info['url']}{endpoint}"
194
-
227
+
195
228
  # Get service API key
196
- api_key = service_info['api_key']
197
-
229
+ api_key = service_info["api_key"]
230
+
198
231
  # Prepare headers with authentication
199
232
  headers = {
200
233
  "X-API-Key": api_key,
201
234
  "X-Service-Name": self.service_name,
202
235
  "X-Request-ID": self._generate_request_id(),
203
- "Content-Type": "application/json"
236
+ "Content-Type": "application/json",
204
237
  }
205
-
238
+
206
239
  # Make request with SSL context
207
240
  ssl_context = None
208
241
  if self.config.ssl.enabled:
209
242
  ssl_context = self.security_manager.ssl_manager.create_client_context()
210
-
243
+
211
244
  async with aiohttp.ClientSession() as session:
212
245
  if method.upper() == "GET":
213
- async with session.get(url, headers=headers, ssl=ssl_context) as response:
246
+ async with session.get(
247
+ url, headers=headers, ssl=ssl_context
248
+ ) as response:
214
249
  return await response.json()
215
250
  elif method.upper() == "POST":
216
- async with session.post(url, headers=headers, json=data, ssl=ssl_context) as response:
251
+ async with session.post(
252
+ url, headers=headers, json=data, ssl=ssl_context
253
+ ) as response:
217
254
  return await response.json()
218
255
  elif method.upper() == "PUT":
219
- async with session.put(url, headers=headers, json=data, ssl=ssl_context) as response:
256
+ async with session.put(
257
+ url, headers=headers, json=data, ssl=ssl_context
258
+ ) as response:
220
259
  return await response.json()
221
260
  elif method.upper() == "DELETE":
222
- async with session.delete(url, headers=headers, ssl=ssl_context) as response:
261
+ async with session.delete(
262
+ url, headers=headers, ssl=ssl_context
263
+ ) as response:
223
264
  return await response.json()
224
265
  else:
225
266
  raise ValueError(f"Unsupported HTTP method: {method}")
226
-
267
+
227
268
  except Exception as e:
228
269
  self.logger.error(f"Service call failed: {str(e)}")
229
270
  return {
230
271
  "error": "Service call failed",
231
272
  "message": str(e),
232
273
  "service": service_name,
233
- "endpoint": endpoint
274
+ "endpoint": endpoint,
234
275
  }
235
-
276
+
236
277
  def _generate_request_id(self) -> str:
237
278
  """Generate unique request ID for tracing."""
238
279
  import uuid
280
+
239
281
  return str(uuid.uuid4())
240
-
282
+
241
283
  async def process_request(self, request_data: Dict[str, Any]) -> Dict[str, Any]:
242
284
  """
243
285
  Process a microservice request with full security validation.
244
-
286
+
245
287
  Args:
246
288
  request_data: Request data including credentials and action
247
-
289
+
248
290
  Returns:
249
291
  Dict[str, Any]: Response data
250
292
  """
@@ -255,7 +297,7 @@ class MicroserviceExample:
255
297
  resource = request_data.get("resource", "")
256
298
  identifier = request_data.get("identifier", DEFAULT_CLIENT_IP)
257
299
  request_id = request_data.get("request_id", self._generate_request_id())
258
-
300
+
259
301
  # Step 1: Rate limiting check
260
302
  if not self.check_rate_limit(identifier):
261
303
  return {
@@ -263,9 +305,9 @@ class MicroserviceExample:
263
305
  "error": "Rate limit exceeded",
264
306
  "error_code": ErrorCodes.RATE_LIMIT_EXCEEDED_ERROR,
265
307
  "request_id": request_id,
266
- "timestamp": datetime.utcnow().isoformat()
308
+ "timestamp": datetime.now(timezone.utc).isoformat(),
267
309
  }
268
-
310
+
269
311
  # Step 2: Authentication
270
312
  auth_result = self.authenticate_user(credentials)
271
313
  if not auth_result.is_valid:
@@ -275,9 +317,9 @@ class MicroserviceExample:
275
317
  "error_code": auth_result.error_code,
276
318
  "error_message": auth_result.error_message,
277
319
  "request_id": request_id,
278
- "timestamp": datetime.utcnow().isoformat()
320
+ "timestamp": datetime.now(timezone.utc).isoformat(),
279
321
  }
280
-
322
+
281
323
  # Step 3: Authorization
282
324
  required_permissions = self._get_required_permissions(action, resource)
283
325
  if not self.check_permissions(auth_result.roles, required_permissions):
@@ -286,61 +328,76 @@ class MicroserviceExample:
286
328
  "error": "Insufficient permissions",
287
329
  "error_code": ErrorCodes.PERMISSION_DENIED_ERROR,
288
330
  "request_id": request_id,
289
- "timestamp": datetime.utcnow().isoformat()
331
+ "timestamp": datetime.now(timezone.utc).isoformat(),
290
332
  }
291
-
333
+
292
334
  # Step 4: Process the action
293
- result = await self._execute_action(action, resource, request_data.get("data", {}))
294
-
335
+ result = await self._execute_action(
336
+ action, resource, request_data.get("data", {})
337
+ )
338
+
295
339
  # Step 5: Log security event
296
- self._log_security_event("request_processed", {
297
- "username": auth_result.username,
298
- "action": action,
299
- "resource": resource,
300
- "success": True,
301
- "request_id": request_id,
302
- "timestamp": datetime.utcnow().isoformat()
303
- })
304
-
340
+ self._log_security_event(
341
+ "request_processed",
342
+ {
343
+ "username": auth_result.username,
344
+ "action": action,
345
+ "resource": resource,
346
+ "success": True,
347
+ "request_id": request_id,
348
+ "timestamp": datetime.now(timezone.utc).isoformat(),
349
+ },
350
+ )
351
+
305
352
  return {
306
353
  "success": True,
307
354
  "data": result,
308
355
  "user": {
309
356
  "username": auth_result.username,
310
357
  "roles": auth_result.roles,
311
- "auth_method": auth_result.auth_method
358
+ "auth_method": auth_result.auth_method,
312
359
  },
313
360
  "request_id": request_id,
314
- "timestamp": datetime.utcnow().isoformat()
361
+ "timestamp": datetime.now(timezone.utc).isoformat(),
315
362
  }
316
-
363
+
317
364
  except Exception as e:
318
365
  self.logger.error(f"Request processing failed: {str(e)}")
319
366
  return {
320
367
  "success": False,
321
368
  "error": "Internal server error",
322
369
  "error_code": ErrorCodes.GENERAL_ERROR,
323
- "request_id": request_id if 'request_id' in locals() else self._generate_request_id(),
324
- "timestamp": datetime.utcnow().isoformat()
370
+ "request_id": (
371
+ request_id
372
+ if "request_id" in locals()
373
+ else self._generate_request_id()
374
+ ),
375
+ "timestamp": datetime.now(timezone.utc).isoformat(),
325
376
  }
326
-
377
+
327
378
  def authenticate_user(self, credentials: Dict[str, Any]) -> AuthResult:
328
379
  """
329
380
  Authenticate user with provided credentials.
330
-
381
+
331
382
  Args:
332
383
  credentials: User credentials (api_key, jwt_token, or certificate)
333
-
384
+
334
385
  Returns:
335
386
  AuthResult: Authentication result
336
387
  """
337
388
  try:
338
389
  if "api_key" in credentials:
339
- return self.security_manager.auth_manager.authenticate_api_key(credentials["api_key"])
390
+ return self.security_manager.auth_manager.authenticate_api_key(
391
+ credentials["api_key"]
392
+ )
340
393
  elif "jwt_token" in credentials:
341
- return self.security_manager.auth_manager.authenticate_jwt_token(credentials["jwt_token"])
394
+ return self.security_manager.auth_manager.authenticate_jwt_token(
395
+ credentials["jwt_token"]
396
+ )
342
397
  elif "certificate" in credentials:
343
- return self.security_manager.auth_manager.authenticate_certificate(credentials["certificate"])
398
+ return self.security_manager.auth_manager.authenticate_certificate(
399
+ credentials["certificate"]
400
+ )
344
401
  else:
345
402
  return AuthResult(
346
403
  is_valid=False,
@@ -349,7 +406,7 @@ class MicroserviceExample:
349
406
  roles=[],
350
407
  auth_method=None,
351
408
  error_code=ErrorCodes.AUTHENTICATION_ERROR,
352
- error_message="No valid credentials provided"
409
+ error_message="No valid credentials provided",
353
410
  )
354
411
  except Exception as e:
355
412
  self.logger.error(f"Authentication failed: {str(e)}")
@@ -360,17 +417,19 @@ class MicroserviceExample:
360
417
  roles=[],
361
418
  auth_method=None,
362
419
  error_code=ErrorCodes.AUTHENTICATION_ERROR,
363
- error_message=str(e)
420
+ error_message=str(e),
364
421
  )
365
-
366
- def check_permissions(self, user_roles: List[str], required_permissions: List[str]) -> bool:
422
+
423
+ def check_permissions(
424
+ self, user_roles: List[str], required_permissions: List[str]
425
+ ) -> bool:
367
426
  """
368
427
  Check if user has required permissions.
369
-
428
+
370
429
  Args:
371
430
  user_roles: User roles
372
431
  required_permissions: Required permissions
373
-
432
+
374
433
  Returns:
375
434
  bool: True if user has required permissions
376
435
  """
@@ -381,14 +440,14 @@ class MicroserviceExample:
381
440
  except Exception as e:
382
441
  self.logger.error(f"Permission check failed: {str(e)}")
383
442
  return False
384
-
443
+
385
444
  def check_rate_limit(self, identifier: str) -> bool:
386
445
  """
387
446
  Check if request is within rate limits.
388
-
447
+
389
448
  Args:
390
449
  identifier: Request identifier (IP, user ID, etc.)
391
-
450
+
392
451
  Returns:
393
452
  bool: True if request is within rate limits
394
453
  """
@@ -397,44 +456,52 @@ class MicroserviceExample:
397
456
  except Exception as e:
398
457
  self.logger.error(f"Rate limit check failed: {str(e)}")
399
458
  return True # Allow request if rate limiting fails
400
-
401
- async def _execute_action(self, action: str, resource: str, data: Dict[str, Any]) -> Dict[str, Any]:
459
+
460
+ async def _execute_action(
461
+ self, action: str, resource: str, data: Dict[str, Any]
462
+ ) -> Dict[str, Any]:
402
463
  """
403
464
  Execute the requested action.
404
-
465
+
405
466
  Args:
406
467
  action: Action to perform
407
468
  resource: Resource to access
408
469
  data: Action data
409
-
470
+
410
471
  Returns:
411
472
  Dict[str, Any]: Action result
412
473
  """
413
474
  # Simulate different microservice actions
414
475
  if action == "get_user":
415
476
  # Call user service
416
- return await self.call_service("user-service", f"/api/v1/users/{data.get('user_id')}")
477
+ return await self.call_service(
478
+ "user-service", f"/api/v1/users/{data.get('user_id')}"
479
+ )
417
480
  elif action == "create_order":
418
481
  # Call order service
419
- return await self.call_service("order-service", "/api/v1/orders", "POST", data)
482
+ return await self.call_service(
483
+ "order-service", "/api/v1/orders", "POST", data
484
+ )
420
485
  elif action == "process_payment":
421
486
  # Call payment service
422
- return await self.call_service("payment-service", "/api/v1/payments", "POST", data)
487
+ return await self.call_service(
488
+ "payment-service", "/api/v1/payments", "POST", data
489
+ )
423
490
  elif action == "read":
424
491
  return {"resource": resource, "data": {"example": "data"}}
425
492
  elif action == "write":
426
493
  return {"resource": resource, "data": data, "status": "written"}
427
494
  else:
428
495
  return {"error": f"Unknown action: {action}"}
429
-
496
+
430
497
  def _get_required_permissions(self, action: str, resource: str) -> List[str]:
431
498
  """
432
499
  Get required permissions for action and resource.
433
-
500
+
434
501
  Args:
435
502
  action: Action to perform
436
503
  resource: Resource to access
437
-
504
+
438
505
  Returns:
439
506
  List[str]: Required permissions
440
507
  """
@@ -445,15 +512,15 @@ class MicroserviceExample:
445
512
  "process_payment": ["write", "payment"],
446
513
  "read": ["read"],
447
514
  "write": ["read", "write"],
448
- "delete": ["read", "write", "delete"]
515
+ "delete": ["read", "write", "delete"],
449
516
  }
450
-
517
+
451
518
  return permission_mappings.get(action, ["read"])
452
-
519
+
453
520
  def _log_security_event(self, event_type: str, details: Dict[str, Any]):
454
521
  """
455
522
  Log security event.
456
-
523
+
457
524
  Args:
458
525
  event_type: Type of security event
459
526
  details: Event details
@@ -470,49 +537,48 @@ class MicroserviceExample:
470
537
  "resource": details.get("resource"),
471
538
  "success": details.get("success"),
472
539
  "request_id": details.get("request_id"),
473
- **details
474
- }
540
+ **details,
541
+ },
475
542
  )
476
543
  except Exception as e:
477
544
  self.logger.error(f"Failed to log security event: {str(e)}")
478
-
545
+
479
546
  async def health_check(self) -> Dict[str, Any]:
480
547
  """
481
548
  Perform health check for the microservice.
482
-
549
+
483
550
  Returns:
484
551
  Dict[str, Any]: Health check result
485
552
  """
486
553
  try:
487
554
  # Check security manager health
488
555
  security_healthy = self.security_manager is not None
489
-
556
+
490
557
  # Check rate limiter health
491
558
  rate_limit_healthy = self.security_manager.rate_limiter is not None
492
-
559
+
493
560
  # Check service registry health
494
561
  registry_healthy = len(self.service_registry) > 0
495
-
562
+
496
563
  # Check SSL configuration
497
- ssl_healthy = self.config.ssl.enabled and os.path.exists(self.config.ssl.cert_file)
498
-
499
- overall_healthy = all([
500
- security_healthy,
501
- rate_limit_healthy,
502
- registry_healthy,
503
- ssl_healthy
504
- ])
505
-
564
+ ssl_healthy = self.config.ssl.enabled and os.path.exists(
565
+ self.config.ssl.cert_file
566
+ )
567
+
568
+ overall_healthy = all(
569
+ [security_healthy, rate_limit_healthy, registry_healthy, ssl_healthy]
570
+ )
571
+
506
572
  return {
507
573
  "status": "healthy" if overall_healthy else "unhealthy",
508
574
  "service": self.service_name,
509
- "timestamp": datetime.utcnow().isoformat(),
575
+ "timestamp": datetime.now(timezone.utc).isoformat(),
510
576
  "checks": {
511
577
  "security_manager": security_healthy,
512
578
  "rate_limiter": rate_limit_healthy,
513
579
  "service_registry": registry_healthy,
514
- "ssl_configuration": ssl_healthy
515
- }
580
+ "ssl_configuration": ssl_healthy,
581
+ },
516
582
  }
517
583
  except Exception as e:
518
584
  self.logger.error(f"Health check failed: {str(e)}")
@@ -520,45 +586,45 @@ class MicroserviceExample:
520
586
  "status": "unhealthy",
521
587
  "service": self.service_name,
522
588
  "error": str(e),
523
- "timestamp": datetime.utcnow().isoformat()
589
+ "timestamp": datetime.now(timezone.utc).isoformat(),
524
590
  }
525
-
591
+
526
592
  async def get_metrics(self) -> Dict[str, Any]:
527
593
  """
528
594
  Get microservice metrics.
529
-
595
+
530
596
  Returns:
531
597
  Dict[str, Any]: Metrics data
532
598
  """
533
599
  try:
534
600
  # Get rate limiter metrics
535
601
  rate_limit_stats = self.security_manager.rate_limiter.get_statistics()
536
-
602
+
537
603
  # Get security manager status
538
604
  security_status = self.get_security_status()
539
-
605
+
540
606
  return {
541
607
  "service": self.service_name,
542
- "timestamp": datetime.utcnow().isoformat(),
608
+ "timestamp": datetime.now(timezone.utc).isoformat(),
543
609
  "rate_limiting": rate_limit_stats,
544
610
  "security": security_status,
545
611
  "service_registry": {
546
612
  "registered_services": len(self.service_registry),
547
- "services": list(self.service_registry.keys())
548
- }
613
+ "services": list(self.service_registry.keys()),
614
+ },
549
615
  }
550
616
  except Exception as e:
551
617
  self.logger.error(f"Failed to get metrics: {str(e)}")
552
618
  return {
553
619
  "service": self.service_name,
554
620
  "error": str(e),
555
- "timestamp": datetime.utcnow().isoformat()
621
+ "timestamp": datetime.now(timezone.utc).isoformat(),
556
622
  }
557
-
623
+
558
624
  def get_security_status(self) -> Dict[str, Any]:
559
625
  """
560
626
  Get security framework status.
561
-
627
+
562
628
  Returns:
563
629
  Dict[str, Any]: Security status information
564
630
  """
@@ -570,84 +636,84 @@ class MicroserviceExample:
570
636
  "permissions_enabled": self.config.permissions.enabled,
571
637
  "logging_enabled": self.config.logging.enabled,
572
638
  "auth_methods": self.config.auth.methods,
573
- "timestamp": datetime.utcnow().isoformat()
639
+ "timestamp": datetime.now(timezone.utc).isoformat(),
574
640
  }
575
641
 
576
642
 
577
643
  # Example usage and testing
578
644
  class MicroserviceExampleTest:
579
645
  """Test class for microservice example functionality."""
580
-
646
+
581
647
  @staticmethod
582
648
  async def test_authentication():
583
649
  """Test authentication functionality."""
584
650
  example = MicroserviceExample("test-service")
585
-
651
+
586
652
  # Test API key authentication
587
653
  credentials = {"api_key": "service_key_123"}
588
654
  auth_result = example.authenticate_user(credentials)
589
655
  assert auth_result.is_valid
590
656
  assert auth_result.username == "user-service"
591
657
  assert "service" in auth_result.roles
592
-
658
+
593
659
  print("✅ API Key authentication test passed")
594
-
660
+
595
661
  @staticmethod
596
662
  async def test_permissions():
597
663
  """Test permission checking."""
598
664
  example = MicroserviceExample("test-service")
599
-
665
+
600
666
  # Test service permissions
601
667
  service_roles = ["service"]
602
668
  user_roles = ["user"]
603
669
  admin_roles = ["admin"]
604
-
670
+
605
671
  # Service should have service permissions
606
672
  assert example.check_permissions(service_roles, ["read", "write"])
607
-
673
+
608
674
  # User should have user permissions
609
675
  assert example.check_permissions(user_roles, ["read"])
610
-
676
+
611
677
  # Admin should have all permissions
612
678
  assert example.check_permissions(admin_roles, ["read", "write", "delete"])
613
-
679
+
614
680
  print("✅ Permission checking test passed")
615
-
681
+
616
682
  @staticmethod
617
683
  async def test_rate_limiting():
618
684
  """Test rate limiting functionality."""
619
685
  example = MicroserviceExample("test-service")
620
-
686
+
621
687
  # Test rate limiting
622
688
  identifier = "test_service"
623
689
  for i in range(5):
624
690
  is_allowed = example.check_rate_limit(identifier)
625
691
  print(f"Request {i+1}: {'Allowed' if is_allowed else 'Blocked'}")
626
-
692
+
627
693
  print("✅ Rate limiting test completed")
628
-
694
+
629
695
  @staticmethod
630
696
  async def test_health_check():
631
697
  """Test health check functionality."""
632
698
  example = MicroserviceExample("test-service")
633
-
699
+
634
700
  health = await example.health_check()
635
701
  assert "status" in health
636
702
  assert "service" in health
637
703
  assert health["service"] == "test-service"
638
-
704
+
639
705
  print("✅ Health check test passed")
640
-
706
+
641
707
  @staticmethod
642
708
  async def test_metrics():
643
709
  """Test metrics functionality."""
644
710
  example = MicroserviceExample("test-service")
645
-
711
+
646
712
  metrics = await example.get_metrics()
647
713
  assert "service" in metrics
648
714
  assert "timestamp" in metrics
649
715
  assert metrics["service"] == "test-service"
650
-
716
+
651
717
  print("✅ Metrics test passed")
652
718
 
653
719
 
@@ -660,27 +726,27 @@ async def main():
660
726
  await MicroserviceExampleTest.test_rate_limiting()
661
727
  await MicroserviceExampleTest.test_health_check()
662
728
  await MicroserviceExampleTest.test_metrics()
663
-
729
+
664
730
  # Example usage
665
731
  print("\nExample Usage:")
666
732
  example = MicroserviceExample("user-service")
667
-
733
+
668
734
  # Process a request
669
735
  request_data = {
670
736
  "credentials": {"api_key": "service_key_123"},
671
737
  "action": "get_user",
672
738
  "resource": "user_data",
673
739
  "identifier": "192.168.1.100",
674
- "data": {"user_id": "123"}
740
+ "data": {"user_id": "123"},
675
741
  }
676
-
742
+
677
743
  result = await example.process_request(request_data)
678
744
  print(f"Request result: {result}")
679
-
745
+
680
746
  # Get health status
681
747
  health = await example.health_check()
682
748
  print(f"Health status: {health}")
683
-
749
+
684
750
  # Get metrics
685
751
  metrics = await example.get_metrics()
686
752
  print(f"Metrics: {metrics}")
@@ -688,3 +754,102 @@ async def main():
688
754
 
689
755
  if __name__ == "__main__":
690
756
  asyncio.run(main())
757
+
758
+ # Start HTTP server in background for testing
759
+ print("\nStarting Microservice HTTP Server in background...")
760
+
761
+ import json
762
+ import threading
763
+ import time
764
+ from http.server import BaseHTTPRequestHandler, HTTPServer
765
+
766
+ import requests
767
+
768
+ class MicroserviceHandler(BaseHTTPRequestHandler):
769
+ def do_GET(self):
770
+ if self.path == "/health":
771
+ self.send_response(200)
772
+ self.send_header("Content-type", "application/json")
773
+ self.end_headers()
774
+ response = {"status": "healthy", "service": "user-service"}
775
+ self.wfile.write(json.dumps(response).encode())
776
+
777
+ elif self.path == "/metrics":
778
+ self.send_response(200)
779
+ self.send_header("Content-type", "application/json")
780
+ self.end_headers()
781
+ response = {
782
+ "service": "user-service",
783
+ "requests_total": 50,
784
+ "requests_per_minute": 5,
785
+ }
786
+ self.wfile.write(json.dumps(response).encode())
787
+
788
+ elif self.path == "/api/v1/users/123":
789
+ api_key = self.headers.get("X-API-Key")
790
+ if not api_key:
791
+ self.send_response(401)
792
+ self.send_header("Content-type", "application/json")
793
+ self.end_headers()
794
+ response = {"error": "API key required"}
795
+ self.wfile.write(json.dumps(response).encode())
796
+ else:
797
+ self.send_response(200)
798
+ self.send_header("Content-type", "application/json")
799
+ self.end_headers()
800
+ response = {
801
+ "success": True,
802
+ "data": {
803
+ "user_id": "123",
804
+ "username": "test_user",
805
+ "email": "test@example.com",
806
+ },
807
+ "service": "user-service",
808
+ }
809
+ self.wfile.write(json.dumps(response).encode())
810
+
811
+ else:
812
+ self.send_response(404)
813
+ self.end_headers()
814
+
815
+ def log_message(self, format, *args):
816
+ # Suppress logging
817
+ pass
818
+
819
+ def run_server():
820
+ """Run the HTTP server."""
821
+ server = HTTPServer(("0.0.0.0", 8081), MicroserviceHandler)
822
+ server.serve_forever()
823
+
824
+ # Start server in background thread
825
+ server_thread = threading.Thread(target=run_server, daemon=True)
826
+ server_thread.start()
827
+
828
+ # Wait for server to start
829
+ time.sleep(2)
830
+
831
+ try:
832
+ # Test server endpoints
833
+ print("Testing Microservice server endpoints...")
834
+
835
+ # Test health endpoint
836
+ response = requests.get("http://localhost:8081/health", timeout=5)
837
+ print(f"Health endpoint: {response.status_code}")
838
+
839
+ # Test metrics endpoint
840
+ response = requests.get("http://localhost:8081/metrics", timeout=5)
841
+ print(f"Metrics endpoint: {response.status_code}")
842
+
843
+ # Test API endpoint with API key
844
+ headers = {"X-API-Key": "service_key_123"}
845
+ response = requests.get(
846
+ "http://localhost:8081/api/v1/users/123", headers=headers, timeout=5
847
+ )
848
+ print(f"API endpoint: {response.status_code}")
849
+
850
+ print("✅ Microservice server testing completed successfully")
851
+
852
+ except requests.exceptions.RequestException as e:
853
+ print(f"⚠️ Microservice server testing failed: {e}")
854
+
855
+ print("Microservice example completed")