mcp-security-framework 1.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 +41 -22
- mcp_security_framework/core/cert_manager.py +210 -147
- mcp_security_framework/core/permission_manager.py +9 -9
- mcp_security_framework/core/rate_limiter.py +2 -2
- mcp_security_framework/core/security_manager.py +284 -229
- mcp_security_framework/examples/__init__.py +6 -0
- mcp_security_framework/examples/comprehensive_example.py +349 -279
- mcp_security_framework/examples/django_example.py +247 -206
- mcp_security_framework/examples/fastapi_example.py +315 -283
- mcp_security_framework/examples/flask_example.py +274 -203
- mcp_security_framework/examples/gateway_example.py +304 -237
- mcp_security_framework/examples/microservice_example.py +258 -189
- mcp_security_framework/examples/standalone_example.py +255 -230
- mcp_security_framework/examples/test_all_examples.py +151 -135
- 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 +119 -118
- mcp_security_framework/middleware/fastapi_middleware.py +156 -148
- mcp_security_framework/middleware/flask_auth_middleware.py +160 -147
- 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 +18 -6
- mcp_security_framework/utils/cert_utils.py +14 -8
- mcp_security_framework/utils/datetime_compat.py +116 -0
- {mcp_security_framework-1.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 +63 -66
- tests/test_cli/test_cert_cli.py +184 -146
- tests/test_cli/test_security_cli.py +274 -247
- tests/test_core/test_cert_manager.py +24 -10
- tests/test_core/test_security_manager.py +2 -2
- tests/test_examples/test_comprehensive_example.py +190 -137
- tests/test_examples/test_fastapi_example.py +124 -101
- tests/test_examples/test_flask_example.py +124 -101
- tests/test_examples/test_standalone_example.py +73 -80
- tests/test_integration/test_auth_flow.py +213 -197
- tests/test_integration/test_certificate_flow.py +180 -149
- tests/test_integration/test_fastapi_integration.py +108 -111
- tests/test_integration/test_flask_integration.py +141 -140
- tests/test_integration/test_standalone_integration.py +290 -259
- tests/test_middleware/test_fastapi_auth_middleware.py +195 -174
- tests/test_middleware/test_fastapi_middleware.py +147 -132
- tests/test_middleware/test_flask_auth_middleware.py +260 -202
- tests/test_middleware/test_flask_middleware.py +201 -179
- tests/test_middleware/test_security_middleware.py +145 -130
- tests/test_utils/test_datetime_compat.py +147 -0
- mcp_security_framework-1.1.0.dist-info/RECORD +0 -82
- {mcp_security_framework-1.1.0.dist-info → mcp_security_framework-1.1.1.dist-info}/WHEEL +0 -0
- {mcp_security_framework-1.1.0.dist-info → mcp_security_framework-1.1.1.dist-info}/entry_points.txt +0 -0
- {mcp_security_framework-1.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 = {}
|
@@ -159,12 +164,12 @@ class AuthManager:
|
|
159
164
|
"jwt_expiry_hours": config.jwt_expiry_hours,
|
160
165
|
},
|
161
166
|
)
|
162
|
-
|
167
|
+
|
163
168
|
@property
|
164
169
|
def is_auth_enabled(self) -> bool:
|
165
170
|
"""
|
166
171
|
Check if authentication is enabled.
|
167
|
-
|
172
|
+
|
168
173
|
Returns:
|
169
174
|
bool: True if authentication is enabled, False otherwise
|
170
175
|
"""
|
@@ -287,16 +292,24 @@ class AuthManager:
|
|
287
292
|
user_permissions = set()
|
288
293
|
if self.permission_manager:
|
289
294
|
try:
|
290
|
-
permissions_result =
|
295
|
+
permissions_result = (
|
296
|
+
self.permission_manager.get_effective_permissions(user_roles)
|
297
|
+
)
|
291
298
|
# Handle both set and mock objects
|
292
|
-
if hasattr(permissions_result,
|
299
|
+
if hasattr(permissions_result, "__iter__") and not isinstance(
|
300
|
+
permissions_result, str
|
301
|
+
):
|
293
302
|
user_permissions = set(permissions_result)
|
294
303
|
else:
|
295
304
|
user_permissions = set()
|
296
305
|
except Exception as e:
|
297
306
|
self.logger.warning(
|
298
307
|
"Failed to get user permissions",
|
299
|
-
extra={
|
308
|
+
extra={
|
309
|
+
"username": username,
|
310
|
+
"roles": user_roles,
|
311
|
+
"error": str(e),
|
312
|
+
},
|
300
313
|
)
|
301
314
|
|
302
315
|
# Create successful authentication result
|
@@ -458,9 +471,13 @@ class AuthManager:
|
|
458
471
|
user_permissions = set()
|
459
472
|
if self.permission_manager:
|
460
473
|
try:
|
461
|
-
permissions_result =
|
474
|
+
permissions_result = (
|
475
|
+
self.permission_manager.get_effective_permissions(roles)
|
476
|
+
)
|
462
477
|
# Handle both set and mock objects
|
463
|
-
if hasattr(permissions_result,
|
478
|
+
if hasattr(permissions_result, "__iter__") and not isinstance(
|
479
|
+
permissions_result, str
|
480
|
+
):
|
464
481
|
user_permissions = set(permissions_result)
|
465
482
|
else:
|
466
483
|
user_permissions = set()
|
@@ -578,7 +595,7 @@ class AuthManager:
|
|
578
595
|
|
579
596
|
# Check certificate expiration
|
580
597
|
now = datetime.now(timezone.utc)
|
581
|
-
if cert
|
598
|
+
if get_not_valid_after_utc(cert) < now:
|
582
599
|
return AuthResult(
|
583
600
|
is_valid=False,
|
584
601
|
status=AuthStatus.EXPIRED,
|
@@ -636,7 +653,7 @@ class AuthManager:
|
|
636
653
|
roles=roles,
|
637
654
|
auth_method="certificate",
|
638
655
|
auth_timestamp=datetime.now(timezone.utc),
|
639
|
-
token_expiry=cert
|
656
|
+
token_expiry=get_not_valid_after_utc(cert),
|
640
657
|
)
|
641
658
|
|
642
659
|
self.logger.info(
|
@@ -906,37 +923,39 @@ class AuthManager:
|
|
906
923
|
def _get_user_roles(self, username: str) -> List[str]:
|
907
924
|
"""
|
908
925
|
Get user roles from configuration.
|
909
|
-
|
926
|
+
|
910
927
|
This method retrieves user roles from the authentication configuration.
|
911
928
|
It checks both the user_roles mapping and the permission manager.
|
912
|
-
|
929
|
+
|
913
930
|
Args:
|
914
931
|
username (str): Username to get roles for
|
915
|
-
|
932
|
+
|
916
933
|
Returns:
|
917
934
|
List[str]: List of user roles
|
918
935
|
"""
|
919
936
|
# Check user_roles mapping first
|
920
937
|
if self.config.user_roles and username in self.config.user_roles:
|
921
938
|
return self.config.user_roles[username]
|
922
|
-
|
939
|
+
|
923
940
|
# Fallback to permission manager
|
924
941
|
if self.permission_manager:
|
925
942
|
return self.permission_manager.get_user_roles(username)
|
926
|
-
|
943
|
+
|
927
944
|
return []
|
928
945
|
|
929
|
-
def _validate_external_user(
|
946
|
+
def _validate_external_user(
|
947
|
+
self, username: str, credentials: Dict[str, Any]
|
948
|
+
) -> bool:
|
930
949
|
"""
|
931
950
|
Validate user against external authentication system.
|
932
|
-
|
951
|
+
|
933
952
|
This method provides a hook for integrating with external
|
934
953
|
authentication systems (LDAP, Active Directory, etc.).
|
935
|
-
|
954
|
+
|
936
955
|
Args:
|
937
956
|
username (str): Username to validate
|
938
957
|
credentials (Dict[str, Any]): User credentials
|
939
|
-
|
958
|
+
|
940
959
|
Returns:
|
941
960
|
bool: True if user is valid in external system
|
942
961
|
"""
|
@@ -944,9 +963,9 @@ class AuthManager:
|
|
944
963
|
# In a real implementation, this would connect to external auth systems
|
945
964
|
self.logger.debug(
|
946
965
|
"External user validation called",
|
947
|
-
extra={"username": username, "method": "external"}
|
966
|
+
extra={"username": username, "method": "external"},
|
948
967
|
)
|
949
|
-
|
968
|
+
|
950
969
|
# For now, return False to indicate external validation not implemented
|
951
970
|
return False
|
952
971
|
|