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,439 @@
1
+ """
2
+ mTLS Middleware Module
3
+
4
+ This module provides specialized mTLS (mutual TLS) middleware that focuses
5
+ on mutual TLS authentication and certificate validation.
6
+
7
+ Key Features:
8
+ - mTLS authentication processing
9
+ - Client certificate validation
10
+ - Certificate chain verification
11
+ - Certificate-based role extraction
12
+ - mTLS event logging
13
+
14
+ Classes:
15
+ MTLSMiddleware: mTLS-specific middleware
16
+ MTLSMiddlewareError: mTLS middleware-specific error exception
17
+
18
+ Author: MCP Security Team
19
+ Version: 1.0.0
20
+ License: MIT
21
+ """
22
+
23
+ import logging
24
+ import ssl
25
+ from abc import ABC, abstractmethod
26
+ from typing import Any, Dict, List, Optional, Union
27
+
28
+ from .security_middleware import SecurityMiddleware, SecurityMiddlewareError
29
+ from ..schemas.models import AuthResult, AuthStatus, AuthMethod
30
+
31
+
32
+ class MTLSMiddlewareError(SecurityMiddlewareError):
33
+ """Raised when mTLS middleware encounters an error."""
34
+
35
+ def __init__(self, message: str, error_code: int = -32037):
36
+ self.message = message
37
+ self.error_code = error_code
38
+ super().__init__(self.message)
39
+
40
+
41
+ class MTLSMiddleware(SecurityMiddleware):
42
+ """
43
+ mTLS (Mutual TLS) Middleware Class
44
+
45
+ This class provides mTLS-specific middleware that focuses on mutual
46
+ TLS authentication and certificate validation. It's designed for
47
+ scenarios where client certificates are used for authentication.
48
+
49
+ The MTLSMiddleware implements:
50
+ - mTLS authentication processing
51
+ - Client certificate validation
52
+ - Certificate chain verification
53
+ - Certificate-based role extraction
54
+ - mTLS event logging
55
+
56
+ Key Responsibilities:
57
+ - Process requests through mTLS authentication pipeline
58
+ - Validate client certificates
59
+ - Verify certificate chains
60
+ - Extract roles and permissions from certificates
61
+ - Handle mTLS-specific error scenarios
62
+ - Log mTLS authentication events
63
+
64
+ Attributes:
65
+ Inherits all attributes from SecurityMiddleware
66
+ _certificate_cache (Dict): Cache for certificate validation results
67
+ _ca_certificates (List): List of trusted CA certificates
68
+
69
+ Example:
70
+ >>> from mcp_security_framework.middleware import MTLSMiddleware
71
+ >>>
72
+ >>> security_manager = SecurityManager(config)
73
+ >>> mtls_middleware = MTLSMiddleware(security_manager)
74
+ >>> app.add_middleware(mtls_middleware)
75
+
76
+ Note:
77
+ This middleware requires proper SSL/TLS configuration on the
78
+ server to handle client certificates.
79
+ """
80
+
81
+ def __init__(self, security_manager):
82
+ """
83
+ Initialize mTLS Middleware.
84
+
85
+ Args:
86
+ security_manager: Security manager instance containing
87
+ all security components and configuration.
88
+
89
+ Raises:
90
+ MTLSMiddlewareError: If initialization fails
91
+ """
92
+ super().__init__(security_manager)
93
+ self.logger = logging.getLogger(f"{__name__}.{self.__class__.__name__}")
94
+
95
+ # Initialize certificate cache
96
+ self._certificate_cache: Dict[str, Dict[str, Any]] = {}
97
+
98
+ # Load CA certificates if configured
99
+ self._ca_certificates = self._load_ca_certificates()
100
+
101
+ self.logger.info(
102
+ "mTLS middleware initialized",
103
+ extra={"ca_certificates_count": len(self._ca_certificates)}
104
+ )
105
+
106
+ @abstractmethod
107
+ def __call__(self, request: Any, call_next: Any) -> Any:
108
+ """
109
+ Process request through mTLS middleware.
110
+
111
+ This method implements the mTLS authentication processing
112
+ pipeline, focusing on client certificate validation.
113
+
114
+ Args:
115
+ request: Framework-specific request object
116
+ call_next: Framework-specific call_next function
117
+
118
+ Returns:
119
+ Framework-specific response object
120
+
121
+ Raises:
122
+ MTLSMiddlewareError: If mTLS processing fails
123
+ """
124
+ pass
125
+
126
+ def _authenticate_mtls(self, request: Any) -> AuthResult:
127
+ """
128
+ Perform mTLS authentication.
129
+
130
+ This method handles mutual TLS authentication by validating
131
+ client certificates and extracting user information.
132
+
133
+ Args:
134
+ request: Framework-specific request object
135
+
136
+ Returns:
137
+ AuthResult: Authentication result with user information
138
+
139
+ Raises:
140
+ MTLSMiddlewareError: If mTLS authentication fails
141
+ """
142
+ try:
143
+ # Get client certificate from request
144
+ client_cert = self._get_client_certificate(request)
145
+ if not client_cert:
146
+ return AuthResult(
147
+ is_valid=False,
148
+ status=AuthStatus.FAILED,
149
+ username=None,
150
+ roles=[],
151
+ auth_method=AuthMethod.CERTIFICATE,
152
+ error_code=-32038,
153
+ error_message="No client certificate provided"
154
+ )
155
+
156
+ # Validate client certificate
157
+ cert_validation = self._validate_client_certificate(client_cert)
158
+ if not cert_validation["is_valid"]:
159
+ return AuthResult(
160
+ is_valid=False,
161
+ status=AuthStatus.FAILED,
162
+ username=None,
163
+ roles=[],
164
+ auth_method=AuthMethod.CERTIFICATE,
165
+ error_code=-32039,
166
+ error_message=cert_validation["error_message"]
167
+ )
168
+
169
+ # Extract user information from certificate
170
+ user_info = self._extract_user_info_from_certificate(client_cert)
171
+
172
+ # Extract roles from certificate
173
+ roles = self._extract_roles_from_certificate(client_cert)
174
+
175
+ self.logger.info(
176
+ "mTLS authentication successful",
177
+ extra={
178
+ "username": user_info["username"],
179
+ "subject": user_info["subject"],
180
+ "issuer": user_info["issuer"],
181
+ "roles": roles
182
+ }
183
+ )
184
+
185
+ return AuthResult(
186
+ is_valid=True,
187
+ status=AuthStatus.SUCCESS,
188
+ username=user_info["username"],
189
+ roles=roles,
190
+ auth_method=AuthMethod.CERTIFICATE
191
+ )
192
+
193
+ except Exception as e:
194
+ self.logger.error(
195
+ "mTLS authentication failed",
196
+ extra={"error": str(e)},
197
+ exc_info=True
198
+ )
199
+ raise MTLSMiddlewareError(
200
+ f"mTLS authentication failed: {str(e)}",
201
+ error_code=-32040
202
+ )
203
+
204
+ def _get_client_certificate(self, request: Any) -> Optional[str]:
205
+ """
206
+ Get client certificate from request.
207
+
208
+ Args:
209
+ request: Framework-specific request object
210
+
211
+ Returns:
212
+ Optional[str]: Client certificate in PEM format if available
213
+ """
214
+ # This should be implemented by framework-specific subclasses
215
+ # to extract the client certificate from the request
216
+ return None
217
+
218
+ def _validate_client_certificate(self, cert_pem: str) -> Dict[str, Any]:
219
+ """
220
+ Validate client certificate.
221
+
222
+ Args:
223
+ cert_pem (str): Client certificate in PEM format
224
+
225
+ Returns:
226
+ Dict[str, Any]: Validation result with status and details
227
+ """
228
+ try:
229
+ # Use security manager's certificate validation
230
+ is_valid = self.security_manager.cert_manager.validate_certificate_chain(
231
+ cert_pem,
232
+ self.config.ssl.ca_cert_file if self.config.ssl else None
233
+ )
234
+
235
+ if is_valid:
236
+ return {
237
+ "is_valid": True,
238
+ "error_message": ""
239
+ }
240
+ else:
241
+ return {
242
+ "is_valid": False,
243
+ "error_message": "Certificate validation failed"
244
+ }
245
+
246
+ except Exception as e:
247
+ return {
248
+ "is_valid": False,
249
+ "error_message": f"Certificate validation error: {str(e)}"
250
+ }
251
+
252
+ def _extract_user_info_from_certificate(self, cert_pem: str) -> Dict[str, str]:
253
+ """
254
+ Extract user information from certificate.
255
+
256
+ Args:
257
+ cert_pem (str): Client certificate in PEM format
258
+
259
+ Returns:
260
+ Dict[str, str]: User information extracted from certificate
261
+ """
262
+ try:
263
+ # Use security manager's certificate utilities
264
+ cert_info = self.security_manager.cert_manager.get_certificate_info(cert_pem)
265
+
266
+ return {
267
+ "username": cert_info.get("common_name", "unknown"),
268
+ "subject": cert_info.get("subject", ""),
269
+ "issuer": cert_info.get("issuer", ""),
270
+ "serial_number": cert_info.get("serial_number", ""),
271
+ "valid_from": str(cert_info.get("valid_from", "")),
272
+ "valid_until": str(cert_info.get("valid_until", ""))
273
+ }
274
+
275
+ except Exception as e:
276
+ self.logger.error(
277
+ "Failed to extract user info from certificate",
278
+ extra={"error": str(e)}
279
+ )
280
+ return {
281
+ "username": "unknown",
282
+ "subject": "",
283
+ "issuer": "",
284
+ "serial_number": "",
285
+ "valid_from": "",
286
+ "valid_until": ""
287
+ }
288
+
289
+ def _extract_roles_from_certificate(self, cert_pem: str) -> List[str]:
290
+ """
291
+ Extract roles from certificate.
292
+
293
+ Args:
294
+ cert_pem (str): Client certificate in PEM format
295
+
296
+ Returns:
297
+ List[str]: List of roles extracted from certificate
298
+ """
299
+ try:
300
+ # Use security manager's certificate utilities
301
+ roles = self.security_manager.cert_manager.extract_roles_from_certificate(cert_pem)
302
+ return roles
303
+
304
+ except Exception as e:
305
+ self.logger.error(
306
+ "Failed to extract roles from certificate",
307
+ extra={"error": str(e)}
308
+ )
309
+ return []
310
+
311
+ def _load_ca_certificates(self) -> List[str]:
312
+ """
313
+ Load trusted CA certificates.
314
+
315
+ Returns:
316
+ List[str]: List of CA certificates in PEM format
317
+ """
318
+ ca_certs = []
319
+
320
+ try:
321
+ if self.config.ssl and self.config.ssl.ca_cert_file:
322
+ # Load CA certificate from file
323
+ with open(self.config.ssl.ca_cert_file, 'r') as f:
324
+ ca_certs.append(f.read())
325
+
326
+ except Exception as e:
327
+ self.logger.error(
328
+ "Failed to load CA certificates",
329
+ extra={"error": str(e)}
330
+ )
331
+
332
+ return ca_certs
333
+
334
+ def _verify_certificate_chain(self, cert_pem: str) -> bool:
335
+ """
336
+ Verify certificate chain against trusted CAs.
337
+
338
+ Args:
339
+ cert_pem (str): Client certificate in PEM format
340
+
341
+ Returns:
342
+ bool: True if chain is valid, False otherwise
343
+ """
344
+ try:
345
+ # This would implement certificate chain verification
346
+ # against the loaded CA certificates
347
+ return True
348
+
349
+ except Exception as e:
350
+ self.logger.error(
351
+ "Certificate chain verification failed",
352
+ extra={"error": str(e)}
353
+ )
354
+ return False
355
+
356
+ def _check_certificate_revocation(self, cert_pem: str) -> bool:
357
+ """
358
+ Check if certificate is revoked.
359
+
360
+ Args:
361
+ cert_pem (str): Client certificate in PEM format
362
+
363
+ Returns:
364
+ bool: True if certificate is not revoked, False otherwise
365
+ """
366
+ try:
367
+ # This would implement CRL or OCSP checking
368
+ # For now, assume certificate is not revoked
369
+ return True
370
+
371
+ except Exception as e:
372
+ self.logger.error(
373
+ "Certificate revocation check failed",
374
+ extra={"error": str(e)}
375
+ )
376
+ return False
377
+
378
+ def _log_mtls_event(self, event_type: str, cert_info: Dict[str, Any],
379
+ request_details: Dict[str, Any]) -> None:
380
+ """
381
+ Log mTLS event.
382
+
383
+ Args:
384
+ event_type (str): Type of mTLS event
385
+ cert_info (Dict[str, Any]): Certificate information
386
+ request_details (Dict[str, Any]): Request details
387
+ """
388
+ self.logger.info(
389
+ f"mTLS event: {event_type}",
390
+ extra={
391
+ "event_type": event_type,
392
+ "username": cert_info.get("username", "unknown"),
393
+ "subject": cert_info.get("subject", ""),
394
+ "issuer": cert_info.get("issuer", ""),
395
+ "serial_number": cert_info.get("serial_number", ""),
396
+ **request_details
397
+ }
398
+ )
399
+
400
+ def get_certificate_info(self, cert_pem: str) -> Dict[str, Any]:
401
+ """
402
+ Get detailed certificate information.
403
+
404
+ Args:
405
+ cert_pem (str): Certificate in PEM format
406
+
407
+ Returns:
408
+ Dict[str, Any]: Detailed certificate information
409
+ """
410
+ try:
411
+ return self.security_manager.cert_manager.get_certificate_info(cert_pem)
412
+ except Exception as e:
413
+ self.logger.error(
414
+ "Failed to get certificate info",
415
+ extra={"error": str(e)}
416
+ )
417
+ return {"error": str(e)}
418
+
419
+ def validate_certificate_chain(self, cert_pem: str) -> bool:
420
+ """
421
+ Validate certificate chain.
422
+
423
+ Args:
424
+ cert_pem (str): Certificate in PEM format
425
+
426
+ Returns:
427
+ bool: True if chain is valid, False otherwise
428
+ """
429
+ try:
430
+ return self.security_manager.cert_manager.validate_certificate_chain(
431
+ cert_pem,
432
+ self.config.ssl.ca_cert_file if self.config.ssl else None
433
+ )
434
+ except Exception as e:
435
+ self.logger.error(
436
+ "Certificate chain validation failed",
437
+ extra={"error": str(e)}
438
+ )
439
+ return False