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.
- mcp_security_framework/__init__.py +26 -15
- mcp_security_framework/cli/__init__.py +1 -1
- mcp_security_framework/cli/cert_cli.py +233 -197
- mcp_security_framework/cli/security_cli.py +324 -234
- mcp_security_framework/constants.py +21 -27
- mcp_security_framework/core/auth_manager.py +49 -20
- mcp_security_framework/core/cert_manager.py +398 -104
- mcp_security_framework/core/permission_manager.py +13 -9
- mcp_security_framework/core/rate_limiter.py +10 -0
- mcp_security_framework/core/security_manager.py +286 -229
- mcp_security_framework/examples/__init__.py +6 -0
- mcp_security_framework/examples/comprehensive_example.py +954 -0
- mcp_security_framework/examples/django_example.py +276 -202
- mcp_security_framework/examples/fastapi_example.py +897 -393
- mcp_security_framework/examples/flask_example.py +311 -200
- mcp_security_framework/examples/gateway_example.py +373 -214
- mcp_security_framework/examples/microservice_example.py +337 -172
- mcp_security_framework/examples/standalone_example.py +719 -478
- mcp_security_framework/examples/test_all_examples.py +572 -0
- mcp_security_framework/middleware/__init__.py +46 -55
- mcp_security_framework/middleware/auth_middleware.py +62 -63
- mcp_security_framework/middleware/fastapi_auth_middleware.py +179 -110
- mcp_security_framework/middleware/fastapi_middleware.py +156 -148
- mcp_security_framework/middleware/flask_auth_middleware.py +267 -107
- mcp_security_framework/middleware/flask_middleware.py +183 -157
- mcp_security_framework/middleware/mtls_middleware.py +106 -117
- mcp_security_framework/middleware/rate_limit_middleware.py +105 -101
- mcp_security_framework/middleware/security_middleware.py +109 -124
- mcp_security_framework/schemas/config.py +2 -1
- mcp_security_framework/schemas/models.py +19 -6
- mcp_security_framework/utils/cert_utils.py +14 -8
- mcp_security_framework/utils/datetime_compat.py +116 -0
- {mcp_security_framework-0.1.0.dist-info → mcp_security_framework-1.1.1.dist-info}/METADATA +2 -1
- mcp_security_framework-1.1.1.dist-info/RECORD +84 -0
- tests/conftest.py +303 -0
- tests/test_cli/test_cert_cli.py +194 -174
- tests/test_cli/test_security_cli.py +274 -247
- tests/test_core/test_cert_manager.py +33 -19
- tests/test_core/test_security_manager.py +2 -2
- tests/test_examples/test_comprehensive_example.py +613 -0
- tests/test_examples/test_fastapi_example.py +290 -169
- tests/test_examples/test_flask_example.py +304 -162
- tests/test_examples/test_standalone_example.py +106 -168
- tests/test_integration/test_auth_flow.py +214 -198
- tests/test_integration/test_certificate_flow.py +181 -150
- tests/test_integration/test_fastapi_integration.py +140 -149
- tests/test_integration/test_flask_integration.py +144 -141
- tests/test_integration/test_standalone_integration.py +331 -300
- tests/test_middleware/test_fastapi_auth_middleware.py +745 -0
- tests/test_middleware/test_fastapi_middleware.py +147 -132
- tests/test_middleware/test_flask_auth_middleware.py +696 -0
- tests/test_middleware/test_flask_middleware.py +201 -179
- tests/test_middleware/test_security_middleware.py +151 -130
- tests/test_utils/test_datetime_compat.py +147 -0
- mcp_security_framework-0.1.0.dist-info/RECORD +0 -76
- {mcp_security_framework-0.1.0.dist-info → mcp_security_framework-1.1.1.dist-info}/WHEEL +0 -0
- {mcp_security_framework-0.1.0.dist-info → mcp_security_framework-1.1.1.dist-info}/entry_points.txt +0 -0
- {mcp_security_framework-0.1.0.dist-info → mcp_security_framework-1.1.1.dist-info}/top_level.txt +0 -0
@@ -62,16 +62,17 @@ HTTP_NOT_FOUND = 404
|
|
62
62
|
HTTP_TOO_MANY_REQUESTS = 429
|
63
63
|
HTTP_INTERNAL_SERVER_ERROR = 500
|
64
64
|
|
65
|
+
|
65
66
|
# Error Codes
|
66
67
|
class ErrorCodes:
|
67
68
|
"""Error codes for the MCP Security Framework."""
|
68
|
-
|
69
|
+
|
69
70
|
# SSL/TLS Errors (-32001 to -32009)
|
70
71
|
SSL_CONFIGURATION_ERROR = -32001
|
71
72
|
CERTIFICATE_VALIDATION_ERROR = -32002
|
72
73
|
SSL_CONTEXT_CREATION_ERROR = -32003
|
73
74
|
SSL_HANDSHAKE_ERROR = -32004
|
74
|
-
|
75
|
+
|
75
76
|
# Authentication Errors (-32010 to -32019)
|
76
77
|
AUTHENTICATION_ERROR = -32010
|
77
78
|
AUTHENTICATION_CONFIGURATION_ERROR = -32011
|
@@ -80,46 +81,47 @@ class ErrorCodes:
|
|
80
81
|
CERTIFICATE_AUTH_ERROR = -32014
|
81
82
|
BASIC_AUTH_ERROR = -32015
|
82
83
|
AUTH_METHOD_NOT_SUPPORTED = -32016
|
83
|
-
|
84
|
+
|
84
85
|
# Authorization Errors (-32020 to -32029)
|
85
86
|
PERMISSION_DENIED_ERROR = -32020
|
86
87
|
INSUFFICIENT_PERMISSIONS = -32021
|
87
88
|
ROLE_NOT_FOUND = -32022
|
88
89
|
PERMISSION_NOT_FOUND = -32023
|
89
|
-
|
90
|
+
|
90
91
|
# Rate Limiting Errors (-32030 to -32039)
|
91
92
|
RATE_LIMIT_EXCEEDED_ERROR = -32030
|
92
93
|
RATE_LIMIT_CONFIGURATION_ERROR = -32031
|
93
94
|
RATE_LIMIT_STORAGE_ERROR = -32032
|
94
|
-
|
95
|
+
|
95
96
|
# Middleware Errors (-32040 to -32049)
|
96
97
|
SECURITY_MIDDLEWARE_ERROR = -32040
|
97
98
|
AUTH_MIDDLEWARE_ERROR = -32041
|
98
99
|
MTLS_MIDDLEWARE_ERROR = -32042
|
99
100
|
RATE_LIMIT_MIDDLEWARE_ERROR = -32043
|
100
|
-
|
101
|
+
|
101
102
|
# Certificate Management Errors (-32050 to -32059)
|
102
103
|
CERTIFICATE_GENERATION_ERROR = -32050
|
103
104
|
CERTIFICATE_REVOCATION_ERROR = -32051
|
104
105
|
CERTIFICATE_STORAGE_ERROR = -32052
|
105
106
|
CA_CONFIGURATION_ERROR = -32053
|
106
|
-
|
107
|
+
|
107
108
|
# Crypto Errors (-32060 to -32069)
|
108
109
|
CRYPTO_ERROR = -32060
|
109
110
|
KEY_GENERATION_ERROR = -32061
|
110
111
|
HASHING_ERROR = -32062
|
111
112
|
ENCRYPTION_ERROR = -32063
|
112
|
-
|
113
|
+
|
113
114
|
# Configuration Errors (-32070 to -32079)
|
114
115
|
CONFIGURATION_ERROR = -32070
|
115
116
|
VALIDATION_ERROR = -32071
|
116
117
|
SERIALIZATION_ERROR = -32072
|
117
|
-
|
118
|
+
|
118
119
|
# General Errors (-32080 to -32099)
|
119
120
|
GENERAL_ERROR = -32080
|
120
121
|
NOT_IMPLEMENTED_ERROR = -32081
|
121
122
|
UNSUPPORTED_OPERATION_ERROR = -32082
|
122
123
|
|
124
|
+
|
123
125
|
# Security Headers
|
124
126
|
DEFAULT_SECURITY_HEADERS = {
|
125
127
|
"X-Content-Type-Options": "nosniff",
|
@@ -127,7 +129,7 @@ DEFAULT_SECURITY_HEADERS = {
|
|
127
129
|
"X-XSS-Protection": "1; mode=block",
|
128
130
|
"Strict-Transport-Security": "max-age=31536000; includeSubDomains",
|
129
131
|
"Content-Security-Policy": "default-src 'self'",
|
130
|
-
"Referrer-Policy": "strict-origin-when-cross-origin"
|
132
|
+
"Referrer-Policy": "strict-origin-when-cross-origin",
|
131
133
|
}
|
132
134
|
|
133
135
|
# Authentication Methods
|
@@ -136,29 +138,21 @@ AUTH_METHODS = {
|
|
136
138
|
"JWT": "jwt",
|
137
139
|
"CERTIFICATE": "certificate",
|
138
140
|
"BASIC": "basic",
|
139
|
-
"OAUTH2": "oauth2"
|
141
|
+
"OAUTH2": "oauth2",
|
140
142
|
}
|
141
143
|
|
142
144
|
# Storage Backends
|
143
|
-
STORAGE_BACKENDS = {
|
144
|
-
"MEMORY": "memory",
|
145
|
-
"REDIS": "redis",
|
146
|
-
"DATABASE": "database"
|
147
|
-
}
|
145
|
+
STORAGE_BACKENDS = {"MEMORY": "memory", "REDIS": "redis", "DATABASE": "database"}
|
148
146
|
|
149
147
|
# Hash Algorithms
|
150
|
-
HASH_ALGORITHMS = {
|
151
|
-
"SHA256": "sha256",
|
152
|
-
"SHA512": "sha512",
|
153
|
-
"MD5": "md5"
|
154
|
-
}
|
148
|
+
HASH_ALGORITHMS = {"SHA256": "sha256", "SHA512": "sha512", "MD5": "md5"}
|
155
149
|
|
156
150
|
# TLS Versions
|
157
151
|
TLS_VERSIONS = {
|
158
152
|
"TLSv1.0": "TLSv1.0",
|
159
153
|
"TLSv1.1": "TLSv1.1",
|
160
154
|
"TLSv1.2": "TLSv1.2",
|
161
|
-
"TLSv1.3": "TLSv1.3"
|
155
|
+
"TLSv1.3": "TLSv1.3",
|
162
156
|
}
|
163
157
|
|
164
158
|
# Certificate Revocation Reasons
|
@@ -169,7 +163,7 @@ CERTIFICATE_REVOCATION_REASONS = {
|
|
169
163
|
"AFFILIATION_CHANGED": "affiliation_changed",
|
170
164
|
"SUPERSEDED": "superseded",
|
171
165
|
"CESSATION_OF_OPERATION": "cessation_of_operation",
|
172
|
-
"CERTIFICATE_HOLD": "certificate_hold"
|
166
|
+
"CERTIFICATE_HOLD": "certificate_hold",
|
173
167
|
}
|
174
168
|
|
175
169
|
# Log Levels
|
@@ -178,7 +172,7 @@ LOG_LEVELS = {
|
|
178
172
|
"INFO": "INFO",
|
179
173
|
"WARNING": "WARNING",
|
180
174
|
"ERROR": "ERROR",
|
181
|
-
"CRITICAL": "CRITICAL"
|
175
|
+
"CRITICAL": "CRITICAL",
|
182
176
|
}
|
183
177
|
|
184
178
|
# Time Constants (in seconds)
|
@@ -188,7 +182,7 @@ TIME_CONSTANTS = {
|
|
188
182
|
"DAY": 86400,
|
189
183
|
"WEEK": 604800,
|
190
184
|
"MONTH": 2592000, # 30 days
|
191
|
-
"YEAR": 31536000
|
185
|
+
"YEAR": 31536000, # 365 days
|
192
186
|
}
|
193
187
|
|
194
188
|
# Cache Keys
|
@@ -196,7 +190,7 @@ CACHE_KEY_PREFIXES = {
|
|
196
190
|
"AUTH": "auth",
|
197
191
|
"RATE_LIMIT": "rate_limit",
|
198
192
|
"PERMISSION": "permission",
|
199
|
-
"CERTIFICATE": "certificate"
|
193
|
+
"CERTIFICATE": "certificate",
|
200
194
|
}
|
201
195
|
|
202
196
|
# Environment Variables
|
@@ -205,5 +199,5 @@ ENV_VARS = {
|
|
205
199
|
"DEFAULT_SERVER_HOST": "DEFAULT_SERVER_HOST",
|
206
200
|
"DEFAULT_SERVER_PORT": "DEFAULT_SERVER_PORT",
|
207
201
|
"LOG_LEVEL": "LOG_LEVEL",
|
208
|
-
"ENVIRONMENT": "ENVIRONMENT"
|
202
|
+
"ENVIRONMENT": "ENVIRONMENT",
|
209
203
|
}
|
@@ -26,7 +26,7 @@ License: MIT
|
|
26
26
|
import logging
|
27
27
|
import time
|
28
28
|
from datetime import datetime, timedelta, timezone
|
29
|
-
from typing import Dict, List, Optional, Union
|
29
|
+
from typing import Any, Dict, List, Optional, Union
|
30
30
|
|
31
31
|
import jwt
|
32
32
|
from cryptography import x509
|
@@ -47,6 +47,9 @@ from ..utils.crypto_utils import (
|
|
47
47
|
validate_api_key_format,
|
48
48
|
verify_password,
|
49
49
|
)
|
50
|
+
from ..utils.datetime_compat import (
|
51
|
+
get_not_valid_after_utc,
|
52
|
+
)
|
50
53
|
|
51
54
|
|
52
55
|
class AuthManager:
|
@@ -136,7 +139,9 @@ class AuthManager:
|
|
136
139
|
self._api_keys[username] = key
|
137
140
|
self._api_key_metadata[key] = value
|
138
141
|
else:
|
139
|
-
self.logger.warning(
|
142
|
+
self.logger.warning(
|
143
|
+
f"Invalid API key format for key {key}: {value}"
|
144
|
+
)
|
140
145
|
else:
|
141
146
|
self._api_keys = {}
|
142
147
|
self._api_key_metadata = {}
|
@@ -160,6 +165,16 @@ class AuthManager:
|
|
160
165
|
},
|
161
166
|
)
|
162
167
|
|
168
|
+
@property
|
169
|
+
def is_auth_enabled(self) -> bool:
|
170
|
+
"""
|
171
|
+
Check if authentication is enabled.
|
172
|
+
|
173
|
+
Returns:
|
174
|
+
bool: True if authentication is enabled, False otherwise
|
175
|
+
"""
|
176
|
+
return self.config.enabled
|
177
|
+
|
163
178
|
def authenticate_api_key(self, api_key: str) -> AuthResult:
|
164
179
|
"""
|
165
180
|
Authenticate user using API key.
|
@@ -277,16 +292,24 @@ class AuthManager:
|
|
277
292
|
user_permissions = set()
|
278
293
|
if self.permission_manager:
|
279
294
|
try:
|
280
|
-
permissions_result =
|
295
|
+
permissions_result = (
|
296
|
+
self.permission_manager.get_effective_permissions(user_roles)
|
297
|
+
)
|
281
298
|
# Handle both set and mock objects
|
282
|
-
if hasattr(permissions_result,
|
299
|
+
if hasattr(permissions_result, "__iter__") and not isinstance(
|
300
|
+
permissions_result, str
|
301
|
+
):
|
283
302
|
user_permissions = set(permissions_result)
|
284
303
|
else:
|
285
304
|
user_permissions = set()
|
286
305
|
except Exception as e:
|
287
306
|
self.logger.warning(
|
288
307
|
"Failed to get user permissions",
|
289
|
-
extra={
|
308
|
+
extra={
|
309
|
+
"username": username,
|
310
|
+
"roles": user_roles,
|
311
|
+
"error": str(e),
|
312
|
+
},
|
290
313
|
)
|
291
314
|
|
292
315
|
# Create successful authentication result
|
@@ -448,9 +471,13 @@ class AuthManager:
|
|
448
471
|
user_permissions = set()
|
449
472
|
if self.permission_manager:
|
450
473
|
try:
|
451
|
-
permissions_result =
|
474
|
+
permissions_result = (
|
475
|
+
self.permission_manager.get_effective_permissions(roles)
|
476
|
+
)
|
452
477
|
# Handle both set and mock objects
|
453
|
-
if hasattr(permissions_result,
|
478
|
+
if hasattr(permissions_result, "__iter__") and not isinstance(
|
479
|
+
permissions_result, str
|
480
|
+
):
|
454
481
|
user_permissions = set(permissions_result)
|
455
482
|
else:
|
456
483
|
user_permissions = set()
|
@@ -568,7 +595,7 @@ class AuthManager:
|
|
568
595
|
|
569
596
|
# Check certificate expiration
|
570
597
|
now = datetime.now(timezone.utc)
|
571
|
-
if cert
|
598
|
+
if get_not_valid_after_utc(cert) < now:
|
572
599
|
return AuthResult(
|
573
600
|
is_valid=False,
|
574
601
|
status=AuthStatus.EXPIRED,
|
@@ -626,7 +653,7 @@ class AuthManager:
|
|
626
653
|
roles=roles,
|
627
654
|
auth_method="certificate",
|
628
655
|
auth_timestamp=datetime.now(timezone.utc),
|
629
|
-
token_expiry=cert
|
656
|
+
token_expiry=get_not_valid_after_utc(cert),
|
630
657
|
)
|
631
658
|
|
632
659
|
self.logger.info(
|
@@ -896,37 +923,39 @@ class AuthManager:
|
|
896
923
|
def _get_user_roles(self, username: str) -> List[str]:
|
897
924
|
"""
|
898
925
|
Get user roles from configuration.
|
899
|
-
|
926
|
+
|
900
927
|
This method retrieves user roles from the authentication configuration.
|
901
928
|
It checks both the user_roles mapping and the permission manager.
|
902
|
-
|
929
|
+
|
903
930
|
Args:
|
904
931
|
username (str): Username to get roles for
|
905
|
-
|
932
|
+
|
906
933
|
Returns:
|
907
934
|
List[str]: List of user roles
|
908
935
|
"""
|
909
936
|
# Check user_roles mapping first
|
910
937
|
if self.config.user_roles and username in self.config.user_roles:
|
911
938
|
return self.config.user_roles[username]
|
912
|
-
|
939
|
+
|
913
940
|
# Fallback to permission manager
|
914
941
|
if self.permission_manager:
|
915
942
|
return self.permission_manager.get_user_roles(username)
|
916
|
-
|
943
|
+
|
917
944
|
return []
|
918
945
|
|
919
|
-
def _validate_external_user(
|
946
|
+
def _validate_external_user(
|
947
|
+
self, username: str, credentials: Dict[str, Any]
|
948
|
+
) -> bool:
|
920
949
|
"""
|
921
950
|
Validate user against external authentication system.
|
922
|
-
|
951
|
+
|
923
952
|
This method provides a hook for integrating with external
|
924
953
|
authentication systems (LDAP, Active Directory, etc.).
|
925
|
-
|
954
|
+
|
926
955
|
Args:
|
927
956
|
username (str): Username to validate
|
928
957
|
credentials (Dict[str, Any]): User credentials
|
929
|
-
|
958
|
+
|
930
959
|
Returns:
|
931
960
|
bool: True if user is valid in external system
|
932
961
|
"""
|
@@ -934,9 +963,9 @@ class AuthManager:
|
|
934
963
|
# In a real implementation, this would connect to external auth systems
|
935
964
|
self.logger.debug(
|
936
965
|
"External user validation called",
|
937
|
-
extra={"username": username, "method": "external"}
|
966
|
+
extra={"username": username, "method": "external"},
|
938
967
|
)
|
939
|
-
|
968
|
+
|
940
969
|
# For now, return False to indicate external validation not implemented
|
941
970
|
return False
|
942
971
|
|