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.
- mcp_security_framework/__init__.py +96 -0
- mcp_security_framework/cli/__init__.py +18 -0
- mcp_security_framework/cli/cert_cli.py +511 -0
- mcp_security_framework/cli/security_cli.py +791 -0
- mcp_security_framework/constants.py +209 -0
- mcp_security_framework/core/__init__.py +61 -0
- mcp_security_framework/core/auth_manager.py +1011 -0
- mcp_security_framework/core/cert_manager.py +1663 -0
- mcp_security_framework/core/permission_manager.py +735 -0
- mcp_security_framework/core/rate_limiter.py +602 -0
- mcp_security_framework/core/security_manager.py +943 -0
- mcp_security_framework/core/ssl_manager.py +735 -0
- mcp_security_framework/examples/__init__.py +75 -0
- mcp_security_framework/examples/django_example.py +615 -0
- mcp_security_framework/examples/fastapi_example.py +472 -0
- mcp_security_framework/examples/flask_example.py +506 -0
- mcp_security_framework/examples/gateway_example.py +803 -0
- mcp_security_framework/examples/microservice_example.py +690 -0
- mcp_security_framework/examples/standalone_example.py +576 -0
- mcp_security_framework/middleware/__init__.py +250 -0
- mcp_security_framework/middleware/auth_middleware.py +292 -0
- mcp_security_framework/middleware/fastapi_auth_middleware.py +447 -0
- mcp_security_framework/middleware/fastapi_middleware.py +757 -0
- mcp_security_framework/middleware/flask_auth_middleware.py +465 -0
- mcp_security_framework/middleware/flask_middleware.py +591 -0
- mcp_security_framework/middleware/mtls_middleware.py +439 -0
- mcp_security_framework/middleware/rate_limit_middleware.py +403 -0
- mcp_security_framework/middleware/security_middleware.py +507 -0
- mcp_security_framework/schemas/__init__.py +109 -0
- mcp_security_framework/schemas/config.py +694 -0
- mcp_security_framework/schemas/models.py +709 -0
- mcp_security_framework/schemas/responses.py +686 -0
- mcp_security_framework/tests/__init__.py +0 -0
- mcp_security_framework/utils/__init__.py +121 -0
- mcp_security_framework/utils/cert_utils.py +525 -0
- mcp_security_framework/utils/crypto_utils.py +475 -0
- mcp_security_framework/utils/validation_utils.py +571 -0
- mcp_security_framework-0.1.0.dist-info/METADATA +411 -0
- mcp_security_framework-0.1.0.dist-info/RECORD +76 -0
- mcp_security_framework-0.1.0.dist-info/WHEEL +5 -0
- mcp_security_framework-0.1.0.dist-info/entry_points.txt +3 -0
- mcp_security_framework-0.1.0.dist-info/top_level.txt +2 -0
- tests/__init__.py +0 -0
- tests/test_cli/__init__.py +0 -0
- tests/test_cli/test_cert_cli.py +379 -0
- tests/test_cli/test_security_cli.py +657 -0
- tests/test_core/__init__.py +0 -0
- tests/test_core/test_auth_manager.py +582 -0
- tests/test_core/test_cert_manager.py +795 -0
- tests/test_core/test_permission_manager.py +395 -0
- tests/test_core/test_rate_limiter.py +626 -0
- tests/test_core/test_security_manager.py +841 -0
- tests/test_core/test_ssl_manager.py +532 -0
- tests/test_examples/__init__.py +8 -0
- tests/test_examples/test_fastapi_example.py +264 -0
- tests/test_examples/test_flask_example.py +238 -0
- tests/test_examples/test_standalone_example.py +292 -0
- tests/test_integration/__init__.py +0 -0
- tests/test_integration/test_auth_flow.py +502 -0
- tests/test_integration/test_certificate_flow.py +527 -0
- tests/test_integration/test_fastapi_integration.py +341 -0
- tests/test_integration/test_flask_integration.py +398 -0
- tests/test_integration/test_standalone_integration.py +493 -0
- tests/test_middleware/__init__.py +0 -0
- tests/test_middleware/test_fastapi_middleware.py +523 -0
- tests/test_middleware/test_flask_middleware.py +582 -0
- tests/test_middleware/test_security_middleware.py +493 -0
- tests/test_schemas/__init__.py +0 -0
- tests/test_schemas/test_config.py +811 -0
- tests/test_schemas/test_models.py +879 -0
- tests/test_schemas/test_responses.py +1054 -0
- tests/test_schemas/test_serialization.py +493 -0
- tests/test_utils/__init__.py +0 -0
- tests/test_utils/test_cert_utils.py +510 -0
- tests/test_utils/test_crypto_utils.py +603 -0
- tests/test_utils/test_validation_utils.py +477 -0
@@ -0,0 +1,690 @@
|
|
1
|
+
"""
|
2
|
+
Microservice Example Implementation
|
3
|
+
|
4
|
+
This module provides a complete example of how to implement the MCP Security Framework
|
5
|
+
in a microservice architecture, including all abstract method implementations.
|
6
|
+
|
7
|
+
The example demonstrates:
|
8
|
+
- Microservice with security framework
|
9
|
+
- Service-to-service authentication
|
10
|
+
- Rate limiting for microservices
|
11
|
+
- Certificate-based service 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 MicroserviceExample:
|
42
|
+
"""
|
43
|
+
Complete Microservice Example with Security Framework Implementation
|
44
|
+
|
45
|
+
This class demonstrates a production-ready microservice
|
46
|
+
with comprehensive security features including:
|
47
|
+
- Service-to-service authentication
|
48
|
+
- API gateway integration
|
49
|
+
- Rate limiting for microservices
|
50
|
+
- Certificate-based service authentication
|
51
|
+
- Comprehensive logging and monitoring
|
52
|
+
- Health checks and metrics
|
53
|
+
"""
|
54
|
+
|
55
|
+
def __init__(self, service_name: str, config_path: Optional[str] = None):
|
56
|
+
"""
|
57
|
+
Initialize microservice example with security configuration.
|
58
|
+
|
59
|
+
Args:
|
60
|
+
service_name: Name of the microservice
|
61
|
+
config_path: Path to security configuration file
|
62
|
+
"""
|
63
|
+
self.service_name = service_name
|
64
|
+
self.config = self._load_config(config_path)
|
65
|
+
self.security_manager = SecurityManager(self.config)
|
66
|
+
self.logger = logging.getLogger(__name__)
|
67
|
+
self._setup_logging()
|
68
|
+
self._setup_service_registry()
|
69
|
+
|
70
|
+
def _load_config(self, config_path: Optional[str]) -> SecurityConfig:
|
71
|
+
"""
|
72
|
+
Load security configuration from file or create default.
|
73
|
+
|
74
|
+
Args:
|
75
|
+
config_path: Path to configuration file
|
76
|
+
|
77
|
+
Returns:
|
78
|
+
SecurityConfig: Loaded configuration
|
79
|
+
"""
|
80
|
+
if config_path and os.path.exists(config_path):
|
81
|
+
with open(config_path, 'r') as f:
|
82
|
+
config_data = json.load(f)
|
83
|
+
return SecurityConfig(**config_data)
|
84
|
+
|
85
|
+
# Create production-ready microservice configuration
|
86
|
+
return SecurityConfig(
|
87
|
+
auth=AuthConfig(
|
88
|
+
enabled=True,
|
89
|
+
methods=[AUTH_METHODS["API_KEY"], AUTH_METHODS["JWT"], AUTH_METHODS["CERTIFICATE"]],
|
90
|
+
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"]}
|
94
|
+
},
|
95
|
+
jwt_secret="your-super-secret-jwt-key-change-in-production",
|
96
|
+
jwt_algorithm="HS256",
|
97
|
+
jwt_expiry_hours=24,
|
98
|
+
public_paths=["/health", "/metrics", "/ready"],
|
99
|
+
security_headers=DEFAULT_SECURITY_HEADERS
|
100
|
+
),
|
101
|
+
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",
|
106
|
+
verify_mode="CERT_REQUIRED",
|
107
|
+
min_version="TLSv1.2"
|
108
|
+
),
|
109
|
+
rate_limit={
|
110
|
+
"enabled": True,
|
111
|
+
"default_requests_per_minute": 1000, # Higher for microservices
|
112
|
+
"default_requests_per_hour": 10000,
|
113
|
+
"burst_limit": 5,
|
114
|
+
"window_size_seconds": 60,
|
115
|
+
"storage_backend": "redis",
|
116
|
+
"redis_config": {
|
117
|
+
"host": "redis-cluster",
|
118
|
+
"port": 6379,
|
119
|
+
"db": 0,
|
120
|
+
"password": None
|
121
|
+
},
|
122
|
+
"exempt_paths": ["/health", "/metrics", "/ready"],
|
123
|
+
"exempt_roles": ["service"]
|
124
|
+
},
|
125
|
+
permissions={
|
126
|
+
"enabled": True,
|
127
|
+
"roles_file": "config/roles.json",
|
128
|
+
"default_role": "service",
|
129
|
+
"hierarchy_enabled": True
|
130
|
+
},
|
131
|
+
logging={
|
132
|
+
"enabled": True,
|
133
|
+
"level": "INFO",
|
134
|
+
"format": "%(asctime)s - %(name)s - %(levelname)s - %(message)s",
|
135
|
+
"file_path": f"logs/{self.service_name}.log",
|
136
|
+
"max_file_size": 10,
|
137
|
+
"backup_count": 5,
|
138
|
+
"console_output": True,
|
139
|
+
"json_format": True # JSON format for microservices
|
140
|
+
}
|
141
|
+
)
|
142
|
+
|
143
|
+
def _setup_logging(self):
|
144
|
+
"""Setup logging configuration."""
|
145
|
+
if self.config.logging.enabled:
|
146
|
+
logging.basicConfig(
|
147
|
+
level=getattr(logging, self.config.logging.level),
|
148
|
+
format=self.config.logging.format,
|
149
|
+
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
|
+
]
|
153
|
+
)
|
154
|
+
|
155
|
+
def _setup_service_registry(self):
|
156
|
+
"""Setup service registry for microservice discovery."""
|
157
|
+
self.service_registry = {
|
158
|
+
"user-service": {
|
159
|
+
"url": "https://user-service:8080",
|
160
|
+
"health_check": "/health",
|
161
|
+
"api_key": "service_key_123"
|
162
|
+
},
|
163
|
+
"order-service": {
|
164
|
+
"url": "https://order-service:8081",
|
165
|
+
"health_check": "/health",
|
166
|
+
"api_key": "service_key_456"
|
167
|
+
},
|
168
|
+
"payment-service": {
|
169
|
+
"url": "https://payment-service:8082",
|
170
|
+
"health_check": "/health",
|
171
|
+
"api_key": "service_key_789"
|
172
|
+
}
|
173
|
+
}
|
174
|
+
|
175
|
+
async def call_service(self, service_name: str, endpoint: str, method: str = "GET", data: Optional[Dict] = None) -> Dict[str, Any]:
|
176
|
+
"""
|
177
|
+
Call another microservice with security.
|
178
|
+
|
179
|
+
Args:
|
180
|
+
service_name: Name of the service to call
|
181
|
+
endpoint: Service endpoint
|
182
|
+
method: HTTP method
|
183
|
+
data: Request data
|
184
|
+
|
185
|
+
Returns:
|
186
|
+
Dict[str, Any]: Service response
|
187
|
+
"""
|
188
|
+
try:
|
189
|
+
if service_name not in self.service_registry:
|
190
|
+
raise ValueError(f"Service {service_name} not found in registry")
|
191
|
+
|
192
|
+
service_info = self.service_registry[service_name]
|
193
|
+
url = f"{service_info['url']}{endpoint}"
|
194
|
+
|
195
|
+
# Get service API key
|
196
|
+
api_key = service_info['api_key']
|
197
|
+
|
198
|
+
# Prepare headers with authentication
|
199
|
+
headers = {
|
200
|
+
"X-API-Key": api_key,
|
201
|
+
"X-Service-Name": self.service_name,
|
202
|
+
"X-Request-ID": self._generate_request_id(),
|
203
|
+
"Content-Type": "application/json"
|
204
|
+
}
|
205
|
+
|
206
|
+
# Make request with SSL context
|
207
|
+
ssl_context = None
|
208
|
+
if self.config.ssl.enabled:
|
209
|
+
ssl_context = self.security_manager.ssl_manager.create_client_context()
|
210
|
+
|
211
|
+
async with aiohttp.ClientSession() as session:
|
212
|
+
if method.upper() == "GET":
|
213
|
+
async with session.get(url, headers=headers, ssl=ssl_context) as response:
|
214
|
+
return await response.json()
|
215
|
+
elif method.upper() == "POST":
|
216
|
+
async with session.post(url, headers=headers, json=data, ssl=ssl_context) as response:
|
217
|
+
return await response.json()
|
218
|
+
elif method.upper() == "PUT":
|
219
|
+
async with session.put(url, headers=headers, json=data, ssl=ssl_context) as response:
|
220
|
+
return await response.json()
|
221
|
+
elif method.upper() == "DELETE":
|
222
|
+
async with session.delete(url, headers=headers, ssl=ssl_context) as response:
|
223
|
+
return await response.json()
|
224
|
+
else:
|
225
|
+
raise ValueError(f"Unsupported HTTP method: {method}")
|
226
|
+
|
227
|
+
except Exception as e:
|
228
|
+
self.logger.error(f"Service call failed: {str(e)}")
|
229
|
+
return {
|
230
|
+
"error": "Service call failed",
|
231
|
+
"message": str(e),
|
232
|
+
"service": service_name,
|
233
|
+
"endpoint": endpoint
|
234
|
+
}
|
235
|
+
|
236
|
+
def _generate_request_id(self) -> str:
|
237
|
+
"""Generate unique request ID for tracing."""
|
238
|
+
import uuid
|
239
|
+
return str(uuid.uuid4())
|
240
|
+
|
241
|
+
async def process_request(self, request_data: Dict[str, Any]) -> Dict[str, Any]:
|
242
|
+
"""
|
243
|
+
Process a microservice request with full security validation.
|
244
|
+
|
245
|
+
Args:
|
246
|
+
request_data: Request data including credentials and action
|
247
|
+
|
248
|
+
Returns:
|
249
|
+
Dict[str, Any]: Response data
|
250
|
+
"""
|
251
|
+
try:
|
252
|
+
# Extract request components
|
253
|
+
credentials = request_data.get("credentials", {})
|
254
|
+
action = request_data.get("action", "")
|
255
|
+
resource = request_data.get("resource", "")
|
256
|
+
identifier = request_data.get("identifier", DEFAULT_CLIENT_IP)
|
257
|
+
request_id = request_data.get("request_id", self._generate_request_id())
|
258
|
+
|
259
|
+
# Step 1: Rate limiting check
|
260
|
+
if not self.check_rate_limit(identifier):
|
261
|
+
return {
|
262
|
+
"success": False,
|
263
|
+
"error": "Rate limit exceeded",
|
264
|
+
"error_code": ErrorCodes.RATE_LIMIT_EXCEEDED_ERROR,
|
265
|
+
"request_id": request_id,
|
266
|
+
"timestamp": datetime.utcnow().isoformat()
|
267
|
+
}
|
268
|
+
|
269
|
+
# Step 2: Authentication
|
270
|
+
auth_result = self.authenticate_user(credentials)
|
271
|
+
if not auth_result.is_valid:
|
272
|
+
return {
|
273
|
+
"success": False,
|
274
|
+
"error": "Authentication failed",
|
275
|
+
"error_code": auth_result.error_code,
|
276
|
+
"error_message": auth_result.error_message,
|
277
|
+
"request_id": request_id,
|
278
|
+
"timestamp": datetime.utcnow().isoformat()
|
279
|
+
}
|
280
|
+
|
281
|
+
# Step 3: Authorization
|
282
|
+
required_permissions = self._get_required_permissions(action, resource)
|
283
|
+
if not self.check_permissions(auth_result.roles, required_permissions):
|
284
|
+
return {
|
285
|
+
"success": False,
|
286
|
+
"error": "Insufficient permissions",
|
287
|
+
"error_code": ErrorCodes.PERMISSION_DENIED_ERROR,
|
288
|
+
"request_id": request_id,
|
289
|
+
"timestamp": datetime.utcnow().isoformat()
|
290
|
+
}
|
291
|
+
|
292
|
+
# Step 4: Process the action
|
293
|
+
result = await self._execute_action(action, resource, request_data.get("data", {}))
|
294
|
+
|
295
|
+
# 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
|
+
|
305
|
+
return {
|
306
|
+
"success": True,
|
307
|
+
"data": result,
|
308
|
+
"user": {
|
309
|
+
"username": auth_result.username,
|
310
|
+
"roles": auth_result.roles,
|
311
|
+
"auth_method": auth_result.auth_method
|
312
|
+
},
|
313
|
+
"request_id": request_id,
|
314
|
+
"timestamp": datetime.utcnow().isoformat()
|
315
|
+
}
|
316
|
+
|
317
|
+
except Exception as e:
|
318
|
+
self.logger.error(f"Request processing failed: {str(e)}")
|
319
|
+
return {
|
320
|
+
"success": False,
|
321
|
+
"error": "Internal server error",
|
322
|
+
"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()
|
325
|
+
}
|
326
|
+
|
327
|
+
def authenticate_user(self, credentials: Dict[str, Any]) -> AuthResult:
|
328
|
+
"""
|
329
|
+
Authenticate user with provided credentials.
|
330
|
+
|
331
|
+
Args:
|
332
|
+
credentials: User credentials (api_key, jwt_token, or certificate)
|
333
|
+
|
334
|
+
Returns:
|
335
|
+
AuthResult: Authentication result
|
336
|
+
"""
|
337
|
+
try:
|
338
|
+
if "api_key" in credentials:
|
339
|
+
return self.security_manager.auth_manager.authenticate_api_key(credentials["api_key"])
|
340
|
+
elif "jwt_token" in credentials:
|
341
|
+
return self.security_manager.auth_manager.authenticate_jwt_token(credentials["jwt_token"])
|
342
|
+
elif "certificate" in credentials:
|
343
|
+
return self.security_manager.auth_manager.authenticate_certificate(credentials["certificate"])
|
344
|
+
else:
|
345
|
+
return AuthResult(
|
346
|
+
is_valid=False,
|
347
|
+
status=AuthStatus.FAILED,
|
348
|
+
username=None,
|
349
|
+
roles=[],
|
350
|
+
auth_method=None,
|
351
|
+
error_code=ErrorCodes.AUTHENTICATION_ERROR,
|
352
|
+
error_message="No valid credentials provided"
|
353
|
+
)
|
354
|
+
except Exception as e:
|
355
|
+
self.logger.error(f"Authentication failed: {str(e)}")
|
356
|
+
return AuthResult(
|
357
|
+
is_valid=False,
|
358
|
+
status=AuthStatus.FAILED,
|
359
|
+
username=None,
|
360
|
+
roles=[],
|
361
|
+
auth_method=None,
|
362
|
+
error_code=ErrorCodes.AUTHENTICATION_ERROR,
|
363
|
+
error_message=str(e)
|
364
|
+
)
|
365
|
+
|
366
|
+
def check_permissions(self, user_roles: List[str], required_permissions: List[str]) -> bool:
|
367
|
+
"""
|
368
|
+
Check if user has required permissions.
|
369
|
+
|
370
|
+
Args:
|
371
|
+
user_roles: User roles
|
372
|
+
required_permissions: Required permissions
|
373
|
+
|
374
|
+
Returns:
|
375
|
+
bool: True if user has required permissions
|
376
|
+
"""
|
377
|
+
try:
|
378
|
+
return self.security_manager.permission_manager.validate_access(
|
379
|
+
user_roles, required_permissions
|
380
|
+
)
|
381
|
+
except Exception as e:
|
382
|
+
self.logger.error(f"Permission check failed: {str(e)}")
|
383
|
+
return False
|
384
|
+
|
385
|
+
def check_rate_limit(self, identifier: str) -> bool:
|
386
|
+
"""
|
387
|
+
Check if request is within rate limits.
|
388
|
+
|
389
|
+
Args:
|
390
|
+
identifier: Request identifier (IP, user ID, etc.)
|
391
|
+
|
392
|
+
Returns:
|
393
|
+
bool: True if request is within rate limits
|
394
|
+
"""
|
395
|
+
try:
|
396
|
+
return self.security_manager.rate_limiter.check_rate_limit(identifier)
|
397
|
+
except Exception as e:
|
398
|
+
self.logger.error(f"Rate limit check failed: {str(e)}")
|
399
|
+
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]:
|
402
|
+
"""
|
403
|
+
Execute the requested action.
|
404
|
+
|
405
|
+
Args:
|
406
|
+
action: Action to perform
|
407
|
+
resource: Resource to access
|
408
|
+
data: Action data
|
409
|
+
|
410
|
+
Returns:
|
411
|
+
Dict[str, Any]: Action result
|
412
|
+
"""
|
413
|
+
# Simulate different microservice actions
|
414
|
+
if action == "get_user":
|
415
|
+
# Call user service
|
416
|
+
return await self.call_service("user-service", f"/api/v1/users/{data.get('user_id')}")
|
417
|
+
elif action == "create_order":
|
418
|
+
# Call order service
|
419
|
+
return await self.call_service("order-service", "/api/v1/orders", "POST", data)
|
420
|
+
elif action == "process_payment":
|
421
|
+
# Call payment service
|
422
|
+
return await self.call_service("payment-service", "/api/v1/payments", "POST", data)
|
423
|
+
elif action == "read":
|
424
|
+
return {"resource": resource, "data": {"example": "data"}}
|
425
|
+
elif action == "write":
|
426
|
+
return {"resource": resource, "data": data, "status": "written"}
|
427
|
+
else:
|
428
|
+
return {"error": f"Unknown action: {action}"}
|
429
|
+
|
430
|
+
def _get_required_permissions(self, action: str, resource: str) -> List[str]:
|
431
|
+
"""
|
432
|
+
Get required permissions for action and resource.
|
433
|
+
|
434
|
+
Args:
|
435
|
+
action: Action to perform
|
436
|
+
resource: Resource to access
|
437
|
+
|
438
|
+
Returns:
|
439
|
+
List[str]: Required permissions
|
440
|
+
"""
|
441
|
+
# Define permission mappings for microservices
|
442
|
+
permission_mappings = {
|
443
|
+
"get_user": ["read", "user"],
|
444
|
+
"create_order": ["write", "order"],
|
445
|
+
"process_payment": ["write", "payment"],
|
446
|
+
"read": ["read"],
|
447
|
+
"write": ["read", "write"],
|
448
|
+
"delete": ["read", "write", "delete"]
|
449
|
+
}
|
450
|
+
|
451
|
+
return permission_mappings.get(action, ["read"])
|
452
|
+
|
453
|
+
def _log_security_event(self, event_type: str, details: Dict[str, Any]):
|
454
|
+
"""
|
455
|
+
Log security event.
|
456
|
+
|
457
|
+
Args:
|
458
|
+
event_type: Type of security event
|
459
|
+
details: Event details
|
460
|
+
"""
|
461
|
+
try:
|
462
|
+
self.logger.info(
|
463
|
+
f"Security event: {event_type}",
|
464
|
+
extra={
|
465
|
+
"event_type": event_type,
|
466
|
+
"service": self.service_name,
|
467
|
+
"timestamp": details.get("timestamp"),
|
468
|
+
"username": details.get("username"),
|
469
|
+
"action": details.get("action"),
|
470
|
+
"resource": details.get("resource"),
|
471
|
+
"success": details.get("success"),
|
472
|
+
"request_id": details.get("request_id"),
|
473
|
+
**details
|
474
|
+
}
|
475
|
+
)
|
476
|
+
except Exception as e:
|
477
|
+
self.logger.error(f"Failed to log security event: {str(e)}")
|
478
|
+
|
479
|
+
async def health_check(self) -> Dict[str, Any]:
|
480
|
+
"""
|
481
|
+
Perform health check for the microservice.
|
482
|
+
|
483
|
+
Returns:
|
484
|
+
Dict[str, Any]: Health check result
|
485
|
+
"""
|
486
|
+
try:
|
487
|
+
# Check security manager health
|
488
|
+
security_healthy = self.security_manager is not None
|
489
|
+
|
490
|
+
# Check rate limiter health
|
491
|
+
rate_limit_healthy = self.security_manager.rate_limiter is not None
|
492
|
+
|
493
|
+
# Check service registry health
|
494
|
+
registry_healthy = len(self.service_registry) > 0
|
495
|
+
|
496
|
+
# 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
|
+
|
506
|
+
return {
|
507
|
+
"status": "healthy" if overall_healthy else "unhealthy",
|
508
|
+
"service": self.service_name,
|
509
|
+
"timestamp": datetime.utcnow().isoformat(),
|
510
|
+
"checks": {
|
511
|
+
"security_manager": security_healthy,
|
512
|
+
"rate_limiter": rate_limit_healthy,
|
513
|
+
"service_registry": registry_healthy,
|
514
|
+
"ssl_configuration": ssl_healthy
|
515
|
+
}
|
516
|
+
}
|
517
|
+
except Exception as e:
|
518
|
+
self.logger.error(f"Health check failed: {str(e)}")
|
519
|
+
return {
|
520
|
+
"status": "unhealthy",
|
521
|
+
"service": self.service_name,
|
522
|
+
"error": str(e),
|
523
|
+
"timestamp": datetime.utcnow().isoformat()
|
524
|
+
}
|
525
|
+
|
526
|
+
async def get_metrics(self) -> Dict[str, Any]:
|
527
|
+
"""
|
528
|
+
Get microservice metrics.
|
529
|
+
|
530
|
+
Returns:
|
531
|
+
Dict[str, Any]: Metrics data
|
532
|
+
"""
|
533
|
+
try:
|
534
|
+
# Get rate limiter metrics
|
535
|
+
rate_limit_stats = self.security_manager.rate_limiter.get_statistics()
|
536
|
+
|
537
|
+
# Get security manager status
|
538
|
+
security_status = self.get_security_status()
|
539
|
+
|
540
|
+
return {
|
541
|
+
"service": self.service_name,
|
542
|
+
"timestamp": datetime.utcnow().isoformat(),
|
543
|
+
"rate_limiting": rate_limit_stats,
|
544
|
+
"security": security_status,
|
545
|
+
"service_registry": {
|
546
|
+
"registered_services": len(self.service_registry),
|
547
|
+
"services": list(self.service_registry.keys())
|
548
|
+
}
|
549
|
+
}
|
550
|
+
except Exception as e:
|
551
|
+
self.logger.error(f"Failed to get metrics: {str(e)}")
|
552
|
+
return {
|
553
|
+
"service": self.service_name,
|
554
|
+
"error": str(e),
|
555
|
+
"timestamp": datetime.utcnow().isoformat()
|
556
|
+
}
|
557
|
+
|
558
|
+
def get_security_status(self) -> Dict[str, Any]:
|
559
|
+
"""
|
560
|
+
Get security framework status.
|
561
|
+
|
562
|
+
Returns:
|
563
|
+
Dict[str, Any]: Security status information
|
564
|
+
"""
|
565
|
+
return {
|
566
|
+
"service": self.service_name,
|
567
|
+
"ssl_enabled": self.config.ssl.enabled,
|
568
|
+
"auth_enabled": self.config.auth.enabled,
|
569
|
+
"rate_limiting_enabled": self.config.rate_limit.enabled,
|
570
|
+
"permissions_enabled": self.config.permissions.enabled,
|
571
|
+
"logging_enabled": self.config.logging.enabled,
|
572
|
+
"auth_methods": self.config.auth.methods,
|
573
|
+
"timestamp": datetime.utcnow().isoformat()
|
574
|
+
}
|
575
|
+
|
576
|
+
|
577
|
+
# Example usage and testing
|
578
|
+
class MicroserviceExampleTest:
|
579
|
+
"""Test class for microservice example functionality."""
|
580
|
+
|
581
|
+
@staticmethod
|
582
|
+
async def test_authentication():
|
583
|
+
"""Test authentication functionality."""
|
584
|
+
example = MicroserviceExample("test-service")
|
585
|
+
|
586
|
+
# Test API key authentication
|
587
|
+
credentials = {"api_key": "service_key_123"}
|
588
|
+
auth_result = example.authenticate_user(credentials)
|
589
|
+
assert auth_result.is_valid
|
590
|
+
assert auth_result.username == "user-service"
|
591
|
+
assert "service" in auth_result.roles
|
592
|
+
|
593
|
+
print("✅ API Key authentication test passed")
|
594
|
+
|
595
|
+
@staticmethod
|
596
|
+
async def test_permissions():
|
597
|
+
"""Test permission checking."""
|
598
|
+
example = MicroserviceExample("test-service")
|
599
|
+
|
600
|
+
# Test service permissions
|
601
|
+
service_roles = ["service"]
|
602
|
+
user_roles = ["user"]
|
603
|
+
admin_roles = ["admin"]
|
604
|
+
|
605
|
+
# Service should have service permissions
|
606
|
+
assert example.check_permissions(service_roles, ["read", "write"])
|
607
|
+
|
608
|
+
# User should have user permissions
|
609
|
+
assert example.check_permissions(user_roles, ["read"])
|
610
|
+
|
611
|
+
# Admin should have all permissions
|
612
|
+
assert example.check_permissions(admin_roles, ["read", "write", "delete"])
|
613
|
+
|
614
|
+
print("✅ Permission checking test passed")
|
615
|
+
|
616
|
+
@staticmethod
|
617
|
+
async def test_rate_limiting():
|
618
|
+
"""Test rate limiting functionality."""
|
619
|
+
example = MicroserviceExample("test-service")
|
620
|
+
|
621
|
+
# Test rate limiting
|
622
|
+
identifier = "test_service"
|
623
|
+
for i in range(5):
|
624
|
+
is_allowed = example.check_rate_limit(identifier)
|
625
|
+
print(f"Request {i+1}: {'Allowed' if is_allowed else 'Blocked'}")
|
626
|
+
|
627
|
+
print("✅ Rate limiting test completed")
|
628
|
+
|
629
|
+
@staticmethod
|
630
|
+
async def test_health_check():
|
631
|
+
"""Test health check functionality."""
|
632
|
+
example = MicroserviceExample("test-service")
|
633
|
+
|
634
|
+
health = await example.health_check()
|
635
|
+
assert "status" in health
|
636
|
+
assert "service" in health
|
637
|
+
assert health["service"] == "test-service"
|
638
|
+
|
639
|
+
print("✅ Health check test passed")
|
640
|
+
|
641
|
+
@staticmethod
|
642
|
+
async def test_metrics():
|
643
|
+
"""Test metrics functionality."""
|
644
|
+
example = MicroserviceExample("test-service")
|
645
|
+
|
646
|
+
metrics = await example.get_metrics()
|
647
|
+
assert "service" in metrics
|
648
|
+
assert "timestamp" in metrics
|
649
|
+
assert metrics["service"] == "test-service"
|
650
|
+
|
651
|
+
print("✅ Metrics test passed")
|
652
|
+
|
653
|
+
|
654
|
+
async def main():
|
655
|
+
"""Main function for testing and example usage."""
|
656
|
+
# Run tests
|
657
|
+
print("Running Microservice Example Tests...")
|
658
|
+
await MicroserviceExampleTest.test_authentication()
|
659
|
+
await MicroserviceExampleTest.test_permissions()
|
660
|
+
await MicroserviceExampleTest.test_rate_limiting()
|
661
|
+
await MicroserviceExampleTest.test_health_check()
|
662
|
+
await MicroserviceExampleTest.test_metrics()
|
663
|
+
|
664
|
+
# Example usage
|
665
|
+
print("\nExample Usage:")
|
666
|
+
example = MicroserviceExample("user-service")
|
667
|
+
|
668
|
+
# Process a request
|
669
|
+
request_data = {
|
670
|
+
"credentials": {"api_key": "service_key_123"},
|
671
|
+
"action": "get_user",
|
672
|
+
"resource": "user_data",
|
673
|
+
"identifier": "192.168.1.100",
|
674
|
+
"data": {"user_id": "123"}
|
675
|
+
}
|
676
|
+
|
677
|
+
result = await example.process_request(request_data)
|
678
|
+
print(f"Request result: {result}")
|
679
|
+
|
680
|
+
# Get health status
|
681
|
+
health = await example.health_check()
|
682
|
+
print(f"Health status: {health}")
|
683
|
+
|
684
|
+
# Get metrics
|
685
|
+
metrics = await example.get_metrics()
|
686
|
+
print(f"Metrics: {metrics}")
|
687
|
+
|
688
|
+
|
689
|
+
if __name__ == "__main__":
|
690
|
+
asyncio.run(main())
|