mcp-security-framework 1.1.0__py3-none-any.whl → 1.1.2__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 +41 -22
  7. mcp_security_framework/core/cert_manager.py +210 -147
  8. mcp_security_framework/core/permission_manager.py +9 -9
  9. mcp_security_framework/core/rate_limiter.py +2 -2
  10. mcp_security_framework/core/security_manager.py +284 -229
  11. mcp_security_framework/examples/__init__.py +6 -0
  12. mcp_security_framework/examples/comprehensive_example.py +349 -279
  13. mcp_security_framework/examples/django_example.py +247 -206
  14. mcp_security_framework/examples/fastapi_example.py +315 -283
  15. mcp_security_framework/examples/flask_example.py +274 -203
  16. mcp_security_framework/examples/gateway_example.py +304 -237
  17. mcp_security_framework/examples/microservice_example.py +258 -189
  18. mcp_security_framework/examples/standalone_example.py +255 -230
  19. mcp_security_framework/examples/test_all_examples.py +151 -135
  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 +119 -118
  23. mcp_security_framework/middleware/fastapi_middleware.py +156 -148
  24. mcp_security_framework/middleware/flask_auth_middleware.py +160 -147
  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 +18 -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-1.1.0.dist-info → mcp_security_framework-1.1.2.dist-info}/METADATA +4 -3
  34. mcp_security_framework-1.1.2.dist-info/RECORD +84 -0
  35. tests/conftest.py +63 -66
  36. tests/test_cli/test_cert_cli.py +184 -146
  37. tests/test_cli/test_security_cli.py +274 -247
  38. tests/test_core/test_cert_manager.py +24 -10
  39. tests/test_core/test_security_manager.py +2 -2
  40. tests/test_examples/test_comprehensive_example.py +190 -137
  41. tests/test_examples/test_fastapi_example.py +124 -101
  42. tests/test_examples/test_flask_example.py +124 -101
  43. tests/test_examples/test_standalone_example.py +73 -80
  44. tests/test_integration/test_auth_flow.py +213 -197
  45. tests/test_integration/test_certificate_flow.py +180 -149
  46. tests/test_integration/test_fastapi_integration.py +108 -111
  47. tests/test_integration/test_flask_integration.py +141 -140
  48. tests/test_integration/test_standalone_integration.py +290 -259
  49. tests/test_middleware/test_fastapi_auth_middleware.py +195 -174
  50. tests/test_middleware/test_fastapi_middleware.py +147 -132
  51. tests/test_middleware/test_flask_auth_middleware.py +260 -202
  52. tests/test_middleware/test_flask_middleware.py +201 -179
  53. tests/test_middleware/test_security_middleware.py +145 -130
  54. tests/test_utils/test_datetime_compat.py +147 -0
  55. mcp_security_framework-1.1.0.dist-info/RECORD +0 -82
  56. {mcp_security_framework-1.1.0.dist-info → mcp_security_framework-1.1.2.dist-info}/WHEEL +0 -0
  57. {mcp_security_framework-1.1.0.dist-info → mcp_security_framework-1.1.2.dist-info}/entry_points.txt +0 -0
  58. {mcp_security_framework-1.1.0.dist-info → mcp_security_framework-1.1.2.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_utc,
129
- "not_after": cert.not_valid_after_utc,
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_utc.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_utc,
335
- "not_before": cert.not_valid_before_utc,
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,7 +1,7 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mcp-security-framework
3
- Version: 1.1.0
4
- Summary: Universal security framework for microservices with SSL/TLS, authentication, authorization, and rate limiting
3
+ Version: 1.1.2
4
+ Summary: Universal security framework for microservices with SSL/TLS, authentication, authorization, and rate limiting. Requires cryptography>=42.0.0 for certificate operations.
5
5
  Author-email: Vasiliy Zdanovskiy <vasilyvz@gmail.com>
6
6
  Maintainer-email: Vasiliy Zdanovskiy <vasilyvz@gmail.com>
7
7
  License: MIT
@@ -26,11 +26,12 @@ Classifier: Topic :: Software Development :: Libraries :: Python Modules
26
26
  Classifier: Topic :: System :: Systems Administration :: Authentication/Directory
27
27
  Requires-Python: >=3.8
28
28
  Description-Content-Type: text/markdown
29
- Requires-Dist: cryptography>=3.4.0
29
+ Requires-Dist: cryptography>=42.0.0
30
30
  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.2.dist-info/METADATA,sha256=xuXJme6tsfv0HEsky6X-Nd-S7sULfScqr9y6aU6w-ZM,11771
81
+ mcp_security_framework-1.1.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
82
+ mcp_security_framework-1.1.2.dist-info/entry_points.txt,sha256=qBh92fVDmd1m2f3xeW0hTu3Ksg8QfGJyV8UEkdA2itg,142
83
+ mcp_security_framework-1.1.2.dist-info/top_level.txt,sha256=ifUiGrTDcD574MXSOoAN2rp2wpUvWlb4jD9LTUgDWCA,29
84
+ mcp_security_framework-1.1.2.dist-info/RECORD,,
tests/conftest.py CHANGED
@@ -8,14 +8,18 @@ Author: Vasiliy Zdanovskiy
8
8
  email: vasilyvz@gmail.com
9
9
  """
10
10
 
11
+ from typing import Any, Dict, Optional
12
+ from unittest.mock import MagicMock, Mock, patch
13
+
11
14
  import pytest
12
- from unittest.mock import Mock, MagicMock, patch
13
- from typing import Dict, Any, Optional
14
15
 
15
16
  from mcp_security_framework.core.security_manager import SecurityManager
16
17
  from mcp_security_framework.schemas.models import (
17
- AuthResult, AuthStatus, AuthMethod,
18
- ValidationResult, ValidationStatus
18
+ AuthMethod,
19
+ AuthResult,
20
+ AuthStatus,
21
+ ValidationResult,
22
+ ValidationStatus,
19
23
  )
20
24
 
21
25
 
@@ -23,15 +27,15 @@ from mcp_security_framework.schemas.models import (
23
27
  def mock_security_manager():
24
28
  """
25
29
  Create a mock SecurityManager instance for testing.
26
-
30
+
27
31
  This fixture provides a properly configured mock SecurityManager
28
32
  that can be used in tests without conflicts with the real implementation.
29
-
33
+
30
34
  Returns:
31
35
  Mock: Mock SecurityManager instance
32
36
  """
33
37
  mock_manager = Mock(spec=SecurityManager)
34
-
38
+
35
39
  # Mock authentication methods
36
40
  def mock_authenticate_api_key(api_key: str) -> AuthResult:
37
41
  if api_key == "admin_key_123":
@@ -40,7 +44,7 @@ def mock_security_manager():
40
44
  status=AuthStatus.SUCCESS,
41
45
  username="admin",
42
46
  roles=["admin"],
43
- auth_method=AuthMethod.API_KEY
47
+ auth_method=AuthMethod.API_KEY,
44
48
  )
45
49
  elif api_key == "user_key_456":
46
50
  return AuthResult(
@@ -48,7 +52,7 @@ def mock_security_manager():
48
52
  status=AuthStatus.SUCCESS,
49
53
  username="user",
50
54
  roles=["user"],
51
- auth_method=AuthMethod.API_KEY
55
+ auth_method=AuthMethod.API_KEY,
52
56
  )
53
57
  elif api_key == "readonly_key_789":
54
58
  return AuthResult(
@@ -56,7 +60,7 @@ def mock_security_manager():
56
60
  status=AuthStatus.SUCCESS,
57
61
  username="readonly",
58
62
  roles=["readonly"],
59
- auth_method=AuthMethod.API_KEY
63
+ auth_method=AuthMethod.API_KEY,
60
64
  )
61
65
  else:
62
66
  return AuthResult(
@@ -66,9 +70,9 @@ def mock_security_manager():
66
70
  roles=[],
67
71
  auth_method=None,
68
72
  error_code=-32002,
69
- error_message="Invalid API key"
73
+ error_message="Invalid API key",
70
74
  )
71
-
75
+
72
76
  def mock_authenticate_jwt_token(token: str) -> AuthResult:
73
77
  if token == "valid_jwt_token":
74
78
  return AuthResult(
@@ -76,7 +80,7 @@ def mock_security_manager():
76
80
  status=AuthStatus.SUCCESS,
77
81
  username="jwt_user",
78
82
  roles=["user"],
79
- auth_method=AuthMethod.JWT
83
+ auth_method=AuthMethod.JWT,
80
84
  )
81
85
  else:
82
86
  return AuthResult(
@@ -86,57 +90,50 @@ def mock_security_manager():
86
90
  roles=[],
87
91
  auth_method=None,
88
92
  error_code=-32003,
89
- error_message="Invalid JWT token"
93
+ error_message="Invalid JWT token",
90
94
  )
91
-
92
- def mock_check_permissions(user_roles: list, required_permissions: list) -> ValidationResult:
95
+
96
+ def mock_check_permissions(
97
+ user_roles: list, required_permissions: list
98
+ ) -> ValidationResult:
93
99
  if "admin" in user_roles:
94
- return ValidationResult(
95
- is_valid=True,
96
- status=ValidationStatus.VALID
97
- )
100
+ return ValidationResult(is_valid=True, status=ValidationStatus.VALID)
98
101
  elif "user" in user_roles and "read" in required_permissions:
99
- return ValidationResult(
100
- is_valid=True,
101
- status=ValidationStatus.VALID
102
- )
102
+ return ValidationResult(is_valid=True, status=ValidationStatus.VALID)
103
103
  elif "readonly" in user_roles and "read" in required_permissions:
104
- return ValidationResult(
105
- is_valid=True,
106
- status=ValidationStatus.VALID
107
- )
104
+ return ValidationResult(is_valid=True, status=ValidationStatus.VALID)
108
105
  else:
109
106
  return ValidationResult(
110
107
  is_valid=False,
111
108
  status=ValidationStatus.INVALID,
112
109
  error_code=-32007,
113
- error_message="Insufficient permissions"
110
+ error_message="Insufficient permissions",
114
111
  )
115
-
112
+
116
113
  def mock_check_rate_limit(identifier: str) -> bool:
117
114
  # Simple rate limiting logic for testing
118
- if not hasattr(mock_manager, '_request_counts'):
115
+ if not hasattr(mock_manager, "_request_counts"):
119
116
  mock_manager._request_counts = {}
120
-
117
+
121
118
  if identifier not in mock_manager._request_counts:
122
119
  mock_manager._request_counts[identifier] = 0
123
-
120
+
124
121
  mock_manager._request_counts[identifier] += 1
125
-
122
+
126
123
  # Allow up to 100 requests per identifier
127
124
  return mock_manager._request_counts[identifier] <= 100
128
-
125
+
129
126
  # Assign mock methods
130
127
  mock_manager.authenticate_api_key = mock_authenticate_api_key
131
128
  mock_manager.authenticate_jwt_token = mock_authenticate_jwt_token
132
129
  mock_manager.check_permissions = mock_check_permissions
133
130
  mock_manager.check_rate_limit = mock_check_rate_limit
134
-
131
+
135
132
  # Mock other properties and methods
136
133
  mock_manager.is_authenticated = True
137
134
  mock_manager.user_roles = ["user"]
138
135
  mock_manager.effective_permissions = {"read", "write"}
139
-
136
+
140
137
  return mock_manager
141
138
 
142
139
 
@@ -144,10 +141,10 @@ def mock_security_manager():
144
141
  def mock_security_manager_class():
145
142
  """
146
143
  Create a mock SecurityManager class for testing.
147
-
144
+
148
145
  This fixture provides a mock class that can be used to patch
149
146
  SecurityManager imports in tests.
150
-
147
+
151
148
  Returns:
152
149
  Mock: Mock SecurityManager class
153
150
  """
@@ -160,12 +157,12 @@ def mock_security_manager_class():
160
157
  def mock_certificate_manager():
161
158
  """
162
159
  Create a mock CertificateManager instance for testing.
163
-
160
+
164
161
  Returns:
165
162
  Mock: Mock CertificateManager instance
166
163
  """
167
164
  mock_manager = Mock()
168
-
165
+
169
166
  # Mock certificate validation
170
167
  mock_manager.validate_certificate_chain.return_value = True
171
168
  mock_manager.get_certificate_info.return_value = Mock(
@@ -176,10 +173,10 @@ def mock_certificate_manager():
176
173
  not_after="2024-01-01",
177
174
  key_size=2048,
178
175
  certificate_type="SERVER",
179
- subject_alt_names=["test.example.com"]
176
+ subject_alt_names=["test.example.com"],
180
177
  )
181
178
  mock_manager.revoke_certificate.return_value = True
182
-
179
+
183
180
  return mock_manager
184
181
 
185
182
 
@@ -187,7 +184,7 @@ def mock_certificate_manager():
187
184
  def mock_ssl_manager():
188
185
  """
189
186
  Create a mock SSLManager instance for testing.
190
-
187
+
191
188
  Returns:
192
189
  Mock: Mock SSLManager instance
193
190
  """
@@ -202,7 +199,7 @@ def mock_ssl_manager():
202
199
  def test_config():
203
200
  """
204
201
  Create a test configuration for testing.
205
-
202
+
206
203
  Returns:
207
204
  Dict[str, Any]: Test configuration
208
205
  """
@@ -214,24 +211,24 @@ def test_config():
214
211
  "admin_key_123": {
215
212
  "username": "admin",
216
213
  "roles": ["admin"],
217
- "permissions": ["read", "write", "delete", "admin"]
214
+ "permissions": ["read", "write", "delete", "admin"],
218
215
  },
219
216
  "user_key_456": {
220
217
  "username": "user",
221
218
  "roles": ["user"],
222
- "permissions": ["read", "write"]
219
+ "permissions": ["read", "write"],
223
220
  },
224
221
  "readonly_key_789": {
225
222
  "username": "readonly",
226
223
  "roles": ["readonly"],
227
- "permissions": ["read"]
228
- }
224
+ "permissions": ["read"],
225
+ },
229
226
  },
230
227
  "jwt": {
231
228
  "secret": "test_secret_key",
232
229
  "algorithm": "HS256",
233
- "expiry_hours": 24
234
- }
230
+ "expiry_hours": 24,
231
+ },
235
232
  },
236
233
  "ssl": {
237
234
  "enabled": False,
@@ -239,17 +236,17 @@ def test_config():
239
236
  "key_file": None,
240
237
  "ca_cert_file": None,
241
238
  "verify_mode": "CERT_NONE",
242
- "min_version": "TLSv1.2"
239
+ "min_version": "TLSv1.2",
243
240
  },
244
241
  "rate_limiting": {
245
242
  "enabled": True,
246
243
  "requests_per_minute": 100,
247
- "window_seconds": 60
244
+ "window_seconds": 60,
248
245
  },
249
246
  "logging": {
250
247
  "level": "INFO",
251
- "format": "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
252
- }
248
+ "format": "%(asctime)s - %(name)s - %(levelname)s - %(message)s",
249
+ },
253
250
  }
254
251
 
255
252
 
@@ -257,21 +254,21 @@ def test_config():
257
254
  def temp_config_file(tmp_path, test_config):
258
255
  """
259
256
  Create a temporary configuration file for testing.
260
-
257
+
261
258
  Args:
262
259
  tmp_path: Pytest temporary directory fixture
263
260
  test_config: Test configuration fixture
264
-
261
+
265
262
  Returns:
266
263
  str: Path to temporary configuration file
267
264
  """
268
265
  import json
269
266
  import os
270
-
267
+
271
268
  config_file = tmp_path / "test_config.json"
272
- with open(config_file, 'w') as f:
269
+ with open(config_file, "w") as f:
273
270
  json.dump(test_config, f)
274
-
271
+
275
272
  return str(config_file)
276
273
 
277
274
 
@@ -279,28 +276,28 @@ def temp_config_file(tmp_path, test_config):
279
276
  def patch_security_manager():
280
277
  """
281
278
  Create a patch decorator for SecurityManager.
282
-
279
+
283
280
  Returns:
284
281
  function: Patch decorator
285
282
  """
286
- return patch('mcp_security_framework.core.security_manager.SecurityManager')
283
+ return patch("mcp_security_framework.core.security_manager.SecurityManager")
287
284
 
288
285
 
289
286
  def patch_certificate_manager():
290
287
  """
291
288
  Create a patch decorator for CertificateManager.
292
-
289
+
293
290
  Returns:
294
291
  function: Patch decorator
295
292
  """
296
- return patch('mcp_security_framework.core.cert_manager.CertificateManager')
293
+ return patch("mcp_security_framework.core.cert_manager.CertificateManager")
297
294
 
298
295
 
299
296
  def patch_ssl_manager():
300
297
  """
301
298
  Create a patch decorator for SSLManager.
302
-
299
+
303
300
  Returns:
304
301
  function: Patch decorator
305
302
  """
306
- return patch('mcp_security_framework.core.ssl_manager.SSLManager')
303
+ return patch("mcp_security_framework.core.ssl_manager.SSLManager")