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
@@ -40,6 +40,11 @@ from cryptography.hazmat.primitives import hashes, serialization
40
40
  from cryptography.hazmat.primitives.asymmetric import rsa
41
41
  from cryptography.x509.oid import ExtensionOID, NameOID
42
42
 
43
+ from mcp_security_framework.utils.datetime_compat import (
44
+ get_not_valid_after_utc,
45
+ get_not_valid_before_utc,
46
+ )
47
+
43
48
 
44
49
  class CertificateError(Exception):
45
50
  """Raised when certificate operations fail."""
@@ -125,8 +130,8 @@ def extract_certificate_info(cert_data: Union[str, bytes, Path]) -> Dict:
125
130
  "issuer": str(cert.issuer),
126
131
  "serial_number": str(cert.serial_number),
127
132
  "version": cert.version.name,
128
- "not_before": cert.not_valid_before,
129
- "not_after": cert.not_valid_after,
133
+ "not_before": get_not_valid_before_utc(cert),
134
+ "not_after": get_not_valid_after_utc(cert),
130
135
  "signature_algorithm": cert.signature_algorithm_oid._name,
131
136
  "public_key_algorithm": cert.public_key_algorithm_oid._name,
132
137
  "key_size": _get_key_size(cert.public_key()),
@@ -266,7 +271,8 @@ def extract_permissions_from_certificate(
266
271
 
267
272
 
268
273
  def validate_certificate_chain(
269
- cert_data: Union[str, bytes, Path], ca_cert_data: Union[str, bytes, Path, List[Union[str, bytes, Path]]]
274
+ cert_data: Union[str, bytes, Path],
275
+ ca_cert_data: Union[str, bytes, Path, List[Union[str, bytes, Path]]],
270
276
  ) -> bool:
271
277
  """
272
278
  Validate certificate chain against CA certificate(s).
@@ -283,7 +289,7 @@ def validate_certificate_chain(
283
289
  """
284
290
  try:
285
291
  cert = parse_certificate(cert_data)
286
-
292
+
287
293
  # Handle single CA certificate or list of CA certificates
288
294
  if isinstance(ca_cert_data, list):
289
295
  ca_certs = [parse_certificate(ca_cert) for ca_cert in ca_cert_data]
@@ -295,7 +301,7 @@ def validate_certificate_chain(
295
301
  for ca_cert in ca_certs:
296
302
  if cert.issuer == ca_cert.subject:
297
303
  return True
298
-
304
+
299
305
  return False
300
306
  except Exception as e:
301
307
  return False
@@ -319,7 +325,7 @@ def get_certificate_expiry(cert_data: Union[str, bytes, Path]) -> Dict:
319
325
  now = datetime.now(timezone.utc)
320
326
 
321
327
  # Calculate time until expiry
322
- time_until_expiry = cert.not_valid_after.replace(tzinfo=timezone.utc) - now
328
+ time_until_expiry = get_not_valid_after_utc(cert) - now
323
329
  days_until_expiry = time_until_expiry.days
324
330
 
325
331
  # Determine expiry status
@@ -331,8 +337,8 @@ def get_certificate_expiry(cert_data: Union[str, bytes, Path]) -> Dict:
331
337
  status = "valid"
332
338
 
333
339
  return {
334
- "not_after": cert.not_valid_after,
335
- "not_before": cert.not_valid_before,
340
+ "not_after": get_not_valid_after_utc(cert),
341
+ "not_before": get_not_valid_before_utc(cert),
336
342
  "days_until_expiry": days_until_expiry,
337
343
  "is_expired": time_until_expiry.total_seconds() < 0,
338
344
  "expires_soon": days_until_expiry <= 30,
@@ -0,0 +1,116 @@
1
+ """
2
+ Datetime Compatibility Module
3
+
4
+ This module provides compatibility functions for working with certificate
5
+ datetime fields across different versions of the cryptography library.
6
+
7
+ The module handles the transition from naive datetime fields to UTC-aware
8
+ fields introduced in cryptography>=42, ensuring backward compatibility with
9
+ older versions.
10
+
11
+ Key Features:
12
+ - Compatible datetime field access for certificates
13
+ - Automatic timezone normalization
14
+ - Backward compatibility with cryptography<42
15
+ - Forward compatibility with cryptography>=42
16
+
17
+ Author: Vasiliy Zdanovskiy
18
+ email: vasilyvz@gmail.com
19
+ """
20
+
21
+ from datetime import datetime, timezone
22
+ from typing import Optional
23
+
24
+ from cryptography.x509 import Certificate
25
+
26
+
27
+ def get_not_valid_before_utc(cert: Certificate) -> datetime:
28
+ """
29
+ Get certificate not_valid_before field in UTC timezone.
30
+
31
+ This function provides compatibility across different cryptography
32
+ versions:
33
+ - For cryptography>=42: Returns cert.not_valid_before_utc directly
34
+ - For cryptography<42: Returns cert.not_valid_before normalized to UTC
35
+
36
+ Args:
37
+ cert (Certificate): Certificate object from cryptography library
38
+
39
+ Returns:
40
+ datetime: UTC timezone-aware datetime representing certificate
41
+ start date
42
+
43
+ Example:
44
+ >>> from cryptography import x509
45
+ >>> cert = x509.load_pem_x509_certificate(cert_data)
46
+ >>> start_date = get_not_valid_before_utc(cert)
47
+ >>> print(f"Certificate valid from: {start_date}")
48
+ """
49
+ # Try to access the new UTC field first (cryptography>=42)
50
+ if hasattr(cert, "not_valid_before_utc"):
51
+ val = getattr(cert, "not_valid_before_utc")
52
+ if val is not None:
53
+ return val
54
+
55
+ # Fall back to the old field and normalize to UTC (cryptography<42)
56
+ v = cert.not_valid_before
57
+ return v if v.tzinfo else v.replace(tzinfo=timezone.utc)
58
+
59
+
60
+ def get_not_valid_after_utc(cert: Certificate) -> datetime:
61
+ """
62
+ Get certificate not_valid_after field in UTC timezone.
63
+
64
+ This function provides compatibility across different cryptography
65
+ versions:
66
+ - For cryptography>=42: Returns cert.not_valid_after_utc directly
67
+ - For cryptography<42: Returns cert.not_valid_after normalized to UTC
68
+
69
+ Args:
70
+ cert (Certificate): Certificate object from cryptography library
71
+
72
+ Returns:
73
+ datetime: UTC timezone-aware datetime representing certificate
74
+ expiry date
75
+
76
+ Example:
77
+ >>> from cryptography import x509
78
+ >>> cert = x509.load_pem_x509_certificate(cert_data)
79
+ >>> expiry_date = get_not_valid_after_utc(cert)
80
+ >>> print(f"Certificate expires: {expiry_date}")
81
+ """
82
+ # Try to access the new UTC field first (cryptography>=42)
83
+ if hasattr(cert, "not_valid_after_utc"):
84
+ val = getattr(cert, "not_valid_after_utc")
85
+ if val is not None:
86
+ return val
87
+
88
+ # Fall back to the old field and normalize to UTC (cryptography<42)
89
+ v = cert.not_valid_after
90
+ return v if v.tzinfo else v.replace(tzinfo=timezone.utc)
91
+
92
+
93
+ def is_cryptography_42_plus() -> bool:
94
+ """
95
+ Check if cryptography version is 42 or higher.
96
+
97
+ This function checks if the current cryptography version supports
98
+ the new UTC datetime fields (not_valid_before_utc, not_valid_after_utc).
99
+
100
+ Returns:
101
+ bool: True if cryptography>=42, False otherwise
102
+
103
+ Example:
104
+ >>> if is_cryptography_42_plus():
105
+ ... print("Using new UTC datetime fields")
106
+ ... else:
107
+ ... print("Using legacy datetime fields with normalization")
108
+ """
109
+ try:
110
+ import cryptography
111
+ from packaging import version
112
+
113
+ return version.parse(cryptography.__version__) >= version.parse("42.0.0")
114
+ except (ImportError, AttributeError):
115
+ # If we can't determine version, assume older version for safety
116
+ return False
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mcp-security-framework
3
- Version: 0.1.0
3
+ Version: 1.1.1
4
4
  Summary: Universal security framework for microservices with SSL/TLS, authentication, authorization, and rate limiting
5
5
  Author-email: Vasiliy Zdanovskiy <vasilyvz@gmail.com>
6
6
  Maintainer-email: Vasiliy Zdanovskiy <vasilyvz@gmail.com>
@@ -31,6 +31,7 @@ Requires-Dist: pydantic<3.0.0,>=1.8.0
31
31
  Requires-Dist: PyJWT>=2.0.0
32
32
  Requires-Dist: click>=8.0.0
33
33
  Requires-Dist: typing-extensions>=4.0.0
34
+ Requires-Dist: packaging>=20.0
34
35
  Provides-Extra: fastapi
35
36
  Requires-Dist: fastapi>=0.68.0; extra == "fastapi"
36
37
  Requires-Dist: uvicorn[standard]>=0.15.0; extra == "fastapi"
@@ -0,0 +1,84 @@
1
+ mcp_security_framework/__init__.py,sha256=TM8y71Navd_6woEab2cO07MefRsL0tRBItEYfVO7DS8,3172
2
+ mcp_security_framework/constants.py,sha256=k7NMSrgc83Cci8aoilybQxdC7jir7J-mVFE_EpqVrDk,5307
3
+ mcp_security_framework/cli/__init__.py,sha256=plpWdiWMp2dcLvUuGwXynRg5CDjz8YKnNTBn7lcta08,369
4
+ mcp_security_framework/cli/cert_cli.py,sha256=ME8RgTBrjNvOAMPVnXGLr3cbTpOZ2NN4Ei1SouGRPQs,18297
5
+ mcp_security_framework/cli/security_cli.py,sha256=Thine_Zzfesz7j29y2k_XZFYUK5YSrhCc6w2FilgEiE,28486
6
+ mcp_security_framework/core/__init__.py,sha256=LiX8_M5qWiTXccJFjSLxup9emhklp-poq57SvznsKEg,1729
7
+ mcp_security_framework/core/auth_manager.py,sha256=vCa6YBlz7P_vmif-KGWrCCUE54bHO5F3jN-bDJF2o9Y,38909
8
+ mcp_security_framework/core/cert_manager.py,sha256=s625nyMsmrglT7QaqRZtX4oAJVKla5zu0sptHmXJnh4,78750
9
+ mcp_security_framework/core/permission_manager.py,sha256=SADS_oXpwp9MhXHKJMCsvjEq8KWcz7vPYL05Yr-zfio,26478
10
+ mcp_security_framework/core/rate_limiter.py,sha256=6qjVBxK2YHouSxQuCcbr0PBpRqA5toQss_Ce178RElY,20682
11
+ mcp_security_framework/core/security_manager.py,sha256=mAF-5znqxin-MSSgXISB7t1kTkqHltEqGzzmlLAhRGs,37766
12
+ mcp_security_framework/core/ssl_manager.py,sha256=nCDrukaELONQs_uAfb30g69HDxRp2u4k0Bpq8eJH6Zo,27718
13
+ mcp_security_framework/examples/__init__.py,sha256=nfYPVvIQ5wHuDkQvyCYDd1VjCsZMw9HnvjeUORqKyuQ,1915
14
+ mcp_security_framework/examples/comprehensive_example.py,sha256=6CXkqLFjWIQ2rRMYPnDrBdBuWFKbeu2gd2GI_SFVjds,35421
15
+ mcp_security_framework/examples/django_example.py,sha256=IHk-aHsah-cEHjvsngUx91lup1aRC8W9XHzK6jfOMdA,24628
16
+ mcp_security_framework/examples/fastapi_example.py,sha256=8uVze78eyEZpnzW0lNLxAUi27amXY7RJtu4GcGGpFJE,35598
17
+ mcp_security_framework/examples/flask_example.py,sha256=jrqnBxr1eu91SLt0Zcf5949DX-ZyQirqMdKVclO5RSI,21636
18
+ mcp_security_framework/examples/gateway_example.py,sha256=u9CSdnBm3nB89n5uyTarMDys5xsZcVi0wLATBSBvSog,33390
19
+ mcp_security_framework/examples/microservice_example.py,sha256=7xw8mOIT1ZMxH4TQNXMv9DuTquitZou1DatO2_OE7DU,30358
20
+ mcp_security_framework/examples/standalone_example.py,sha256=jtrLJPrsgD8w3hNUv5SbCq4KLMu4z6WgF4deMPpehQM,29786
21
+ mcp_security_framework/examples/test_all_examples.py,sha256=9tRDD6lOCx69M27ESRTr5V5h6VnRh1EPuSfFCbQrvxM,19608
22
+ mcp_security_framework/middleware/__init__.py,sha256=Bx2DBPbhkikWNPXg1WWFoalp2omFPh7rbMYHYQZoZsk,7803
23
+ mcp_security_framework/middleware/auth_middleware.py,sha256=wMxJtlrTrlK7KSAXJ6yZawwqSpCsxXf-U87J05ogdKg,9833
24
+ mcp_security_framework/middleware/fastapi_auth_middleware.py,sha256=LWVEn90I1XpVkgu4q2LFqvjeVinCMAmI2-19UIcgKpw,16904
25
+ mcp_security_framework/middleware/fastapi_middleware.py,sha256=Ye0qJsEMwgeUqVJpqXgbjJJbbf-ZU-6SysMQPmQba-s,26658
26
+ mcp_security_framework/middleware/flask_auth_middleware.py,sha256=ubBlKO0ponOV_KuxkUK4xGcSoslXTaikrdsIZQtGeV0,20228
27
+ mcp_security_framework/middleware/flask_middleware.py,sha256=Ag0zYDKwlvU78LBQ-7Za14IYOAlEVZaXJPD0Qc4MUvA,20666
28
+ mcp_security_framework/middleware/mtls_middleware.py,sha256=iCOX3PVro49GMlTSUtYQFaWLrqtqoWPgI5DEa6Cnst4,13881
29
+ mcp_security_framework/middleware/rate_limit_middleware.py,sha256=deCwwigI0Pt7pBUnk2jDurI9ZyjujWTsexEWWndXm3g,13177
30
+ mcp_security_framework/middleware/security_middleware.py,sha256=PQ251Fr2UrYVPgGfhXq6QJyqK2tRk0WCIg9_FBvfVkg,16844
31
+ mcp_security_framework/schemas/__init__.py,sha256=lefkbRlbj2ICfasSj51MQ04o3z1YycnbnknSJCFfXbU,2590
32
+ mcp_security_framework/schemas/config.py,sha256=kpJJBupR4ZpQyouZyxiZqm7U7AHfLoGncuRltpo_QEM,25825
33
+ mcp_security_framework/schemas/models.py,sha256=n-Ug8O1cpMeA0mIdOd9h1i3kkBZOJsCQMxj3IcNpBFY,26957
34
+ mcp_security_framework/schemas/responses.py,sha256=nVXaqF5GTSprXTa_wiUEu38nvSw9WAXtKViAJNbO-Xg,23206
35
+ mcp_security_framework/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
36
+ mcp_security_framework/utils/__init__.py,sha256=wwwdmQYHTSz0Puvs9FD6aIKmWp3NFARe3JPWNH-b_wk,3098
37
+ mcp_security_framework/utils/cert_utils.py,sha256=DNzCqFKbFgYvQxxUUJPgeWe1XCnM4DpUBDqQYRYtlOQ,17266
38
+ mcp_security_framework/utils/crypto_utils.py,sha256=OH2V7_C3FjStxFTIXMUPfNXZuWG2-QjgoBrIH4Lv4p0,12392
39
+ mcp_security_framework/utils/datetime_compat.py,sha256=ool-xs-EevhuYygdzhiAenLAacLuZwGwjPkF43i-9gg,3859
40
+ mcp_security_framework/utils/validation_utils.py,sha256=e9BX3kw9gdXSmFsc7lmG-qnzSlK0-Ynn7Xs4uKHquF4,16279
41
+ tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
42
+ tests/conftest.py,sha256=4DKwzXAOqikfPAUNDHRcn-C3ZoWmLRcn9Kz_3nJ9hZ8,8812
43
+ tests/test_cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
44
+ tests/test_cli/test_cert_cli.py,sha256=Rm7z-20VAvnmYKY3sgxS-qVNks1vbniQJSpSxjsx_wo,14677
45
+ tests/test_cli/test_security_cli.py,sha256=Bpd31IPJSUl_V1Xzy74ZCOvQpwlbj8Da83C46T8Jewg,25569
46
+ tests/test_core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
47
+ tests/test_core/test_auth_manager.py,sha256=tyJUe3yBP95SFwbhSr_IloKhEQw0BuEdvjAC1Hnwu4s,22540
48
+ tests/test_core/test_cert_manager.py,sha256=4YMAkRedkAZW3PEZYEbo1PyrzMntUrKfl7arPsHXDCE,36356
49
+ tests/test_core/test_permission_manager.py,sha256=0XeghWXZqVpKyyRuhuDu1dkLUSwuZaFWkRQxQhkkFVI,14966
50
+ tests/test_core/test_rate_limiter.py,sha256=YzzlhlxZm-A7YGMiIV8LXDA0zmb_6uRF9GRx9s21Q0U,22544
51
+ tests/test_core/test_security_manager.py,sha256=C5uPFALAkitmHbi-L8xF1OyfOmVHQSq1g-PLkwl_LDU,35007
52
+ tests/test_core/test_ssl_manager.py,sha256=Vm_Nw4SoVro_iwPPc_uD9CwzXpVBkGyVH7EqDtHawvU,20362
53
+ tests/test_examples/__init__.py,sha256=VC0N1wB9d01Acnqfos-9x68o23xxHkRhAh4-1qJKUTc,157
54
+ tests/test_examples/test_comprehensive_example.py,sha256=2Q9ZfyEt42dDuwPbxwiJbJjRfLp68l5KClALeVO9JOQ,26020
55
+ tests/test_examples/test_fastapi_example.py,sha256=J_dpdq7ZIvi1DfcYpEAltTEfbHbLqk-Q9B5XpkrgMOk,12978
56
+ tests/test_examples/test_flask_example.py,sha256=makDlkLEkCPU94ZIomrw-5-ff4NDP7uhWLTDuk34Jbg,12540
57
+ tests/test_examples/test_standalone_example.py,sha256=nzSMJ4kglMcFYHWJ130_cNfHghpBcMgNoNU7PMQ7NII,8165
58
+ tests/test_integration/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
59
+ tests/test_integration/test_auth_flow.py,sha256=lzfrI2Yl-nDDQX5Y3TYK6CrUKtkf4oXk0zYPPdoVjSs,18656
60
+ tests/test_integration/test_certificate_flow.py,sha256=6eyMjIKEviPJtkFaRmG7v9Ua-szvlOF6gfYnHF3JUeA,19612
61
+ tests/test_integration/test_fastapi_integration.py,sha256=HFXNRfm0x0ihzVeBZFD6L1fl19mx5Bg5wvWcXmIzjXw,12957
62
+ tests/test_integration/test_flask_integration.py,sha256=mqb9g3H7lwwKajsG0Ee6eeWH25mNLtI6-ZmLiPKvO2g,15087
63
+ tests/test_integration/test_standalone_integration.py,sha256=chMzo1tG4p0DNCdsWbkhFvSAWqNCEj3Tuoi8miF6C3o,19389
64
+ tests/test_middleware/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
65
+ tests/test_middleware/test_fastapi_auth_middleware.py,sha256=6pKnVDrr-inr51bHqije6oa6m44hAqM1nJovInBJcNg,29386
66
+ tests/test_middleware/test_fastapi_middleware.py,sha256=qX0nXM9SrBoAuPs2-LRKT-EzHiwnGCD0c89FYAYnPMw,20607
67
+ tests/test_middleware/test_flask_auth_middleware.py,sha256=NA74wnBq7AR-YsUqlibMSs4B60q7lWTziTPKZtfq_l0,25034
68
+ tests/test_middleware/test_flask_middleware.py,sha256=JqWr5MknE6AvnUUf2Cr0ME6l_wSbze0BqbEIQO8B5qs,22731
69
+ tests/test_middleware/test_security_middleware.py,sha256=J69rVgsnohQp2ucUnGRyWCWZxt6RF2tQ9vQNLFlDXEg,19199
70
+ tests/test_schemas/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
71
+ tests/test_schemas/test_config.py,sha256=Bp_yZ_HBIl7jcR7Wb1S9iCB0tCu_Ov26YKHTulDs1RU,29430
72
+ tests/test_schemas/test_models.py,sha256=bBeZOPqveuVJuEi_BTVWdVsdj08JXJTEFwvBM4eFRVU,34311
73
+ tests/test_schemas/test_responses.py,sha256=ZSbO7A3ThPBovTXO8PFF-2ONWAjJx2dMOoV2lQIfd8s,40774
74
+ tests/test_schemas/test_serialization.py,sha256=jCugAyrdD6Mw1U7Kxni9oTukarZmMMl6KUcl6cq_NTk,18599
75
+ tests/test_utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
76
+ tests/test_utils/test_cert_utils.py,sha256=2k1kUCJy0T2HPBwOcU5USGfEBQZ_rGzumAGkDdywLak,21232
77
+ tests/test_utils/test_crypto_utils.py,sha256=yEb4hzG6-irj2DPoXY0DUboJfbeR87ussgTuBpxLGz4,20737
78
+ tests/test_utils/test_datetime_compat.py,sha256=n8S4X5HN-_ejSNpgymDXRyZkmxhnyxwwjxFPdX23I40,5656
79
+ tests/test_utils/test_validation_utils.py,sha256=lus_wHJ2WyVnBGQ28S7dSv78uWcCIuLhn5uflJw-uGw,18569
80
+ mcp_security_framework-1.1.1.dist-info/METADATA,sha256=3Kh50icpzF53SGGXCU8XWt1Ov2xJAiTMoyLLkGqyuvk,11711
81
+ mcp_security_framework-1.1.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
82
+ mcp_security_framework-1.1.1.dist-info/entry_points.txt,sha256=qBh92fVDmd1m2f3xeW0hTu3Ksg8QfGJyV8UEkdA2itg,142
83
+ mcp_security_framework-1.1.1.dist-info/top_level.txt,sha256=ifUiGrTDcD574MXSOoAN2rp2wpUvWlb4jD9LTUgDWCA,29
84
+ mcp_security_framework-1.1.1.dist-info/RECORD,,
tests/conftest.py ADDED
@@ -0,0 +1,303 @@
1
+ """
2
+ Test Configuration and Fixtures
3
+
4
+ This module provides test configuration, fixtures, and mock objects
5
+ for the MCP Security Framework test suite.
6
+
7
+ Author: Vasiliy Zdanovskiy
8
+ email: vasilyvz@gmail.com
9
+ """
10
+
11
+ from typing import Any, Dict, Optional
12
+ from unittest.mock import MagicMock, Mock, patch
13
+
14
+ import pytest
15
+
16
+ from mcp_security_framework.core.security_manager import SecurityManager
17
+ from mcp_security_framework.schemas.models import (
18
+ AuthMethod,
19
+ AuthResult,
20
+ AuthStatus,
21
+ ValidationResult,
22
+ ValidationStatus,
23
+ )
24
+
25
+
26
+ @pytest.fixture
27
+ def mock_security_manager():
28
+ """
29
+ Create a mock SecurityManager instance for testing.
30
+
31
+ This fixture provides a properly configured mock SecurityManager
32
+ that can be used in tests without conflicts with the real implementation.
33
+
34
+ Returns:
35
+ Mock: Mock SecurityManager instance
36
+ """
37
+ mock_manager = Mock(spec=SecurityManager)
38
+
39
+ # Mock authentication methods
40
+ def mock_authenticate_api_key(api_key: str) -> AuthResult:
41
+ if api_key == "admin_key_123":
42
+ return AuthResult(
43
+ is_valid=True,
44
+ status=AuthStatus.SUCCESS,
45
+ username="admin",
46
+ roles=["admin"],
47
+ auth_method=AuthMethod.API_KEY,
48
+ )
49
+ elif api_key == "user_key_456":
50
+ return AuthResult(
51
+ is_valid=True,
52
+ status=AuthStatus.SUCCESS,
53
+ username="user",
54
+ roles=["user"],
55
+ auth_method=AuthMethod.API_KEY,
56
+ )
57
+ elif api_key == "readonly_key_789":
58
+ return AuthResult(
59
+ is_valid=True,
60
+ status=AuthStatus.SUCCESS,
61
+ username="readonly",
62
+ roles=["readonly"],
63
+ auth_method=AuthMethod.API_KEY,
64
+ )
65
+ else:
66
+ return AuthResult(
67
+ is_valid=False,
68
+ status=AuthStatus.FAILED,
69
+ username=None,
70
+ roles=[],
71
+ auth_method=None,
72
+ error_code=-32002,
73
+ error_message="Invalid API key",
74
+ )
75
+
76
+ def mock_authenticate_jwt_token(token: str) -> AuthResult:
77
+ if token == "valid_jwt_token":
78
+ return AuthResult(
79
+ is_valid=True,
80
+ status=AuthStatus.SUCCESS,
81
+ username="jwt_user",
82
+ roles=["user"],
83
+ auth_method=AuthMethod.JWT,
84
+ )
85
+ else:
86
+ return AuthResult(
87
+ is_valid=False,
88
+ status=AuthStatus.FAILED,
89
+ username=None,
90
+ roles=[],
91
+ auth_method=None,
92
+ error_code=-32003,
93
+ error_message="Invalid JWT token",
94
+ )
95
+
96
+ def mock_check_permissions(
97
+ user_roles: list, required_permissions: list
98
+ ) -> ValidationResult:
99
+ if "admin" in user_roles:
100
+ return ValidationResult(is_valid=True, status=ValidationStatus.VALID)
101
+ elif "user" in user_roles and "read" in required_permissions:
102
+ return ValidationResult(is_valid=True, status=ValidationStatus.VALID)
103
+ elif "readonly" in user_roles and "read" in required_permissions:
104
+ return ValidationResult(is_valid=True, status=ValidationStatus.VALID)
105
+ else:
106
+ return ValidationResult(
107
+ is_valid=False,
108
+ status=ValidationStatus.INVALID,
109
+ error_code=-32007,
110
+ error_message="Insufficient permissions",
111
+ )
112
+
113
+ def mock_check_rate_limit(identifier: str) -> bool:
114
+ # Simple rate limiting logic for testing
115
+ if not hasattr(mock_manager, "_request_counts"):
116
+ mock_manager._request_counts = {}
117
+
118
+ if identifier not in mock_manager._request_counts:
119
+ mock_manager._request_counts[identifier] = 0
120
+
121
+ mock_manager._request_counts[identifier] += 1
122
+
123
+ # Allow up to 100 requests per identifier
124
+ return mock_manager._request_counts[identifier] <= 100
125
+
126
+ # Assign mock methods
127
+ mock_manager.authenticate_api_key = mock_authenticate_api_key
128
+ mock_manager.authenticate_jwt_token = mock_authenticate_jwt_token
129
+ mock_manager.check_permissions = mock_check_permissions
130
+ mock_manager.check_rate_limit = mock_check_rate_limit
131
+
132
+ # Mock other properties and methods
133
+ mock_manager.is_authenticated = True
134
+ mock_manager.user_roles = ["user"]
135
+ mock_manager.effective_permissions = {"read", "write"}
136
+
137
+ return mock_manager
138
+
139
+
140
+ @pytest.fixture
141
+ def mock_security_manager_class():
142
+ """
143
+ Create a mock SecurityManager class for testing.
144
+
145
+ This fixture provides a mock class that can be used to patch
146
+ SecurityManager imports in tests.
147
+
148
+ Returns:
149
+ Mock: Mock SecurityManager class
150
+ """
151
+ mock_class = Mock()
152
+ mock_class.return_value = Mock(spec=SecurityManager)
153
+ return mock_class
154
+
155
+
156
+ @pytest.fixture
157
+ def mock_certificate_manager():
158
+ """
159
+ Create a mock CertificateManager instance for testing.
160
+
161
+ Returns:
162
+ Mock: Mock CertificateManager instance
163
+ """
164
+ mock_manager = Mock()
165
+
166
+ # Mock certificate validation
167
+ mock_manager.validate_certificate_chain.return_value = True
168
+ mock_manager.get_certificate_info.return_value = Mock(
169
+ subject={"CN": "test.example.com"},
170
+ issuer={"CN": "Test CA"},
171
+ serial_number="123456789",
172
+ not_before="2023-01-01",
173
+ not_after="2024-01-01",
174
+ key_size=2048,
175
+ certificate_type="SERVER",
176
+ subject_alt_names=["test.example.com"],
177
+ )
178
+ mock_manager.revoke_certificate.return_value = True
179
+
180
+ return mock_manager
181
+
182
+
183
+ @pytest.fixture
184
+ def mock_ssl_manager():
185
+ """
186
+ Create a mock SSLManager instance for testing.
187
+
188
+ Returns:
189
+ Mock: Mock SSLManager instance
190
+ """
191
+ mock_manager = Mock()
192
+ mock_manager.create_server_context.return_value = Mock()
193
+ mock_manager.create_client_context.return_value = Mock()
194
+ mock_manager.validate_certificate.return_value = True
195
+ return mock_manager
196
+
197
+
198
+ @pytest.fixture
199
+ def test_config():
200
+ """
201
+ Create a test configuration for testing.
202
+
203
+ Returns:
204
+ Dict[str, Any]: Test configuration
205
+ """
206
+ return {
207
+ "auth": {
208
+ "enabled": True,
209
+ "methods": ["api_key", "jwt"],
210
+ "api_keys": {
211
+ "admin_key_123": {
212
+ "username": "admin",
213
+ "roles": ["admin"],
214
+ "permissions": ["read", "write", "delete", "admin"],
215
+ },
216
+ "user_key_456": {
217
+ "username": "user",
218
+ "roles": ["user"],
219
+ "permissions": ["read", "write"],
220
+ },
221
+ "readonly_key_789": {
222
+ "username": "readonly",
223
+ "roles": ["readonly"],
224
+ "permissions": ["read"],
225
+ },
226
+ },
227
+ "jwt": {
228
+ "secret": "test_secret_key",
229
+ "algorithm": "HS256",
230
+ "expiry_hours": 24,
231
+ },
232
+ },
233
+ "ssl": {
234
+ "enabled": False,
235
+ "cert_file": None,
236
+ "key_file": None,
237
+ "ca_cert_file": None,
238
+ "verify_mode": "CERT_NONE",
239
+ "min_version": "TLSv1.2",
240
+ },
241
+ "rate_limiting": {
242
+ "enabled": True,
243
+ "requests_per_minute": 100,
244
+ "window_seconds": 60,
245
+ },
246
+ "logging": {
247
+ "level": "INFO",
248
+ "format": "%(asctime)s - %(name)s - %(levelname)s - %(message)s",
249
+ },
250
+ }
251
+
252
+
253
+ @pytest.fixture
254
+ def temp_config_file(tmp_path, test_config):
255
+ """
256
+ Create a temporary configuration file for testing.
257
+
258
+ Args:
259
+ tmp_path: Pytest temporary directory fixture
260
+ test_config: Test configuration fixture
261
+
262
+ Returns:
263
+ str: Path to temporary configuration file
264
+ """
265
+ import json
266
+ import os
267
+
268
+ config_file = tmp_path / "test_config.json"
269
+ with open(config_file, "w") as f:
270
+ json.dump(test_config, f)
271
+
272
+ return str(config_file)
273
+
274
+
275
+ # Patch decorators for common mocks
276
+ def patch_security_manager():
277
+ """
278
+ Create a patch decorator for SecurityManager.
279
+
280
+ Returns:
281
+ function: Patch decorator
282
+ """
283
+ return patch("mcp_security_framework.core.security_manager.SecurityManager")
284
+
285
+
286
+ def patch_certificate_manager():
287
+ """
288
+ Create a patch decorator for CertificateManager.
289
+
290
+ Returns:
291
+ function: Patch decorator
292
+ """
293
+ return patch("mcp_security_framework.core.cert_manager.CertificateManager")
294
+
295
+
296
+ def patch_ssl_manager():
297
+ """
298
+ Create a patch decorator for SSLManager.
299
+
300
+ Returns:
301
+ function: Patch decorator
302
+ """
303
+ return patch("mcp_security_framework.core.ssl_manager.SSLManager")