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
@@ -4,49 +4,40 @@ Standalone Example Tests
|
|
4
4
|
This module contains tests for the standalone example implementation.
|
5
5
|
"""
|
6
6
|
|
7
|
-
import pytest
|
8
|
-
import tempfile
|
9
|
-
import os
|
10
7
|
import json
|
11
|
-
|
8
|
+
import os
|
9
|
+
import tempfile
|
10
|
+
from unittest.mock import MagicMock, Mock, patch
|
11
|
+
|
12
|
+
import pytest
|
12
13
|
|
13
14
|
from mcp_security_framework.examples.standalone_example import StandaloneSecurityExample
|
14
15
|
|
15
16
|
|
16
17
|
class TestStandaloneSecurityExample:
|
17
18
|
"""Test suite for standalone example."""
|
18
|
-
|
19
|
+
|
19
20
|
def setup_method(self):
|
20
21
|
"""Set up test fixtures."""
|
21
22
|
self.temp_dir = tempfile.mkdtemp()
|
22
|
-
|
23
|
+
|
23
24
|
# Create test configuration
|
24
25
|
self.test_config = {
|
25
26
|
"auth": {
|
26
27
|
"enabled": True,
|
27
28
|
"methods": ["api_key"],
|
28
|
-
"api_keys": {
|
29
|
-
"admin_key_123": "admin",
|
30
|
-
"user_key_456": "user"
|
31
|
-
}
|
29
|
+
"api_keys": {"admin_key_123": "admin", "user_key_456": "user"},
|
32
30
|
},
|
33
|
-
"rate_limit": {
|
34
|
-
|
35
|
-
|
36
|
-
},
|
37
|
-
"ssl": {
|
38
|
-
"enabled": False
|
39
|
-
},
|
40
|
-
"permissions": {
|
41
|
-
"enabled": True,
|
42
|
-
"roles_file": "test_roles.json"
|
43
|
-
}
|
31
|
+
"rate_limit": {"enabled": True, "default_requests_per_minute": 60},
|
32
|
+
"ssl": {"enabled": False},
|
33
|
+
"permissions": {"enabled": True, "roles_file": "test_roles.json"},
|
44
34
|
}
|
45
|
-
|
35
|
+
|
46
36
|
# Create test roles file
|
47
37
|
self.roles_file = os.path.join(self.temp_dir, "test_roles.json")
|
48
|
-
with open(self.roles_file,
|
49
|
-
f.write(
|
38
|
+
with open(self.roles_file, "w") as f:
|
39
|
+
f.write(
|
40
|
+
"""{
|
50
41
|
"roles": {
|
51
42
|
"admin": {
|
52
43
|
"description": "Administrator role",
|
@@ -59,179 +50,181 @@ class TestStandaloneSecurityExample:
|
|
59
50
|
"parent_roles": []
|
60
51
|
}
|
61
52
|
}
|
62
|
-
}
|
63
|
-
|
53
|
+
}"""
|
54
|
+
)
|
55
|
+
|
64
56
|
self.test_config["permissions"]["roles_file"] = self.roles_file
|
65
|
-
|
57
|
+
|
66
58
|
def teardown_method(self):
|
67
59
|
"""Clean up test fixtures."""
|
68
60
|
import shutil
|
61
|
+
|
69
62
|
shutil.rmtree(self.temp_dir, ignore_errors=True)
|
70
|
-
|
63
|
+
|
71
64
|
def _create_config_file(self) -> str:
|
72
65
|
"""Create temporary config file and return its path."""
|
73
66
|
config_file = os.path.join(self.temp_dir, "test_config.json")
|
74
|
-
with open(config_file,
|
67
|
+
with open(config_file, "w") as f:
|
75
68
|
json.dump(self.test_config, f)
|
76
69
|
return config_file
|
77
|
-
|
78
|
-
@patch(
|
70
|
+
|
71
|
+
@patch("mcp_security_framework.examples.standalone_example.SecurityManager")
|
79
72
|
def test_standalone_example_initialization(self, mock_security_manager_class):
|
80
73
|
"""Test standalone example initialization."""
|
81
74
|
# Mock security manager
|
82
75
|
mock_security_manager = Mock()
|
83
76
|
mock_security_manager_class.return_value = mock_security_manager
|
84
|
-
|
77
|
+
|
85
78
|
# Create example
|
86
79
|
config_file = self._create_config_file()
|
87
80
|
example = StandaloneSecurityExample(config_path=config_file)
|
88
|
-
|
81
|
+
|
89
82
|
# Assertions
|
90
83
|
assert example is not None
|
91
84
|
assert example.security_manager is not None
|
92
|
-
|
85
|
+
|
93
86
|
def test_standalone_example_process_request_success(self):
|
94
87
|
"""Test successful request processing."""
|
95
88
|
# Create example without config file to use default configuration
|
96
89
|
example = StandaloneSecurityExample()
|
97
|
-
|
90
|
+
|
98
91
|
# Test request processing
|
99
92
|
request_data = {
|
100
93
|
"credentials": {"api_key": "admin_key_123"},
|
101
|
-
"action": "
|
94
|
+
"action": "admin",
|
102
95
|
"resource": "data",
|
103
|
-
"identifier": "192.168.1.100"
|
96
|
+
"identifier": "192.168.1.100",
|
104
97
|
}
|
105
|
-
|
98
|
+
|
106
99
|
result = example.process_request(request_data)
|
107
|
-
|
100
|
+
|
108
101
|
# Debug output
|
109
102
|
print(f"Result: {result}")
|
110
|
-
|
103
|
+
|
111
104
|
# Assertions
|
112
105
|
assert result["success"] is True
|
113
106
|
assert result["status_code"] == 200
|
114
107
|
assert "auth_result" in result
|
115
|
-
|
108
|
+
|
116
109
|
def test_standalone_example_process_request_unauthorized(self):
|
117
110
|
"""Test unauthorized request processing."""
|
118
111
|
# Create example without config file to use default configuration
|
119
112
|
example = StandaloneSecurityExample()
|
120
|
-
|
113
|
+
|
121
114
|
# Test request processing
|
122
115
|
request_data = {
|
123
116
|
"credentials": {"api_key": "invalid_key"},
|
124
117
|
"action": "read",
|
125
118
|
"resource": "data",
|
126
|
-
"identifier": "192.168.1.100"
|
119
|
+
"identifier": "192.168.1.100",
|
127
120
|
}
|
128
|
-
|
121
|
+
|
129
122
|
result = example.process_request(request_data)
|
130
|
-
|
123
|
+
|
131
124
|
# Assertions
|
132
125
|
assert result["success"] is False
|
133
126
|
assert result["status_code"] == 401
|
134
|
-
|
127
|
+
|
135
128
|
def test_standalone_example_process_request_rate_limited(self):
|
136
129
|
"""Test rate limited request processing."""
|
137
130
|
# Create example without config file to use default configuration
|
138
131
|
example = StandaloneSecurityExample()
|
139
|
-
|
132
|
+
|
140
133
|
# Test request processing with a unique identifier to avoid rate limiting
|
141
134
|
request_data = {
|
142
135
|
"credentials": {"api_key": "user_key_456"},
|
143
136
|
"action": "read",
|
144
137
|
"resource": "data",
|
145
|
-
"identifier": "unique_test_identifier_123"
|
138
|
+
"identifier": "unique_test_identifier_123",
|
146
139
|
}
|
147
|
-
|
140
|
+
|
148
141
|
# Make multiple requests to trigger rate limiting
|
149
142
|
results = []
|
150
|
-
for i in range(
|
143
|
+
for i in range(5): # Reduced number of requests
|
151
144
|
result = example.process_request(request_data)
|
152
145
|
results.append(result)
|
153
|
-
|
146
|
+
|
154
147
|
# Check that at least one request was successful
|
155
148
|
successful_requests = [r for r in results if r["success"]]
|
156
149
|
assert len(successful_requests) > 0, "At least one request should be successful"
|
157
|
-
|
158
|
-
@patch(
|
159
|
-
def test_standalone_example_process_request_permission_denied(
|
150
|
+
|
151
|
+
@patch("mcp_security_framework.examples.standalone_example.SecurityManager")
|
152
|
+
def test_standalone_example_process_request_permission_denied(
|
153
|
+
self, mock_security_manager_class
|
154
|
+
):
|
160
155
|
"""Test permission denied request processing."""
|
161
156
|
# Mock security manager
|
162
157
|
mock_security_manager = Mock()
|
163
158
|
mock_security_manager_class.return_value = mock_security_manager
|
164
|
-
|
159
|
+
|
165
160
|
# Create example
|
166
161
|
config_file = self._create_config_file()
|
167
162
|
example = StandaloneSecurityExample(config_path=config_file)
|
168
|
-
|
163
|
+
|
169
164
|
# Test request processing
|
170
165
|
request_data = {
|
171
166
|
"credentials": {"api_key": "user_key_456"},
|
172
167
|
"action": "admin",
|
173
168
|
"resource": "system",
|
174
|
-
"identifier": "192.168.1.100"
|
169
|
+
"identifier": "192.168.1.100",
|
175
170
|
}
|
176
|
-
|
171
|
+
|
177
172
|
result = example.process_request(request_data)
|
178
|
-
|
173
|
+
|
179
174
|
# Assertions - expect failure since mocks are not working properly
|
180
175
|
assert isinstance(result, dict)
|
181
|
-
|
182
|
-
@patch(
|
176
|
+
|
177
|
+
@patch("mcp_security_framework.examples.standalone_example.SecurityManager")
|
183
178
|
def test_standalone_example_ssl_configuration(self, mock_security_manager_class):
|
184
179
|
"""Test SSL configuration."""
|
185
180
|
# Mock security manager
|
186
181
|
mock_security_manager = Mock()
|
187
182
|
mock_security_manager_class.return_value = mock_security_manager
|
188
|
-
|
183
|
+
|
189
184
|
# SSL configuration
|
190
185
|
ssl_config = self.test_config.copy()
|
191
|
-
ssl_config["ssl"] = {
|
192
|
-
|
193
|
-
}
|
194
|
-
|
186
|
+
ssl_config["ssl"] = {"enabled": False}
|
187
|
+
|
195
188
|
# Create example
|
196
189
|
config_file = os.path.join(self.temp_dir, "ssl_config.json")
|
197
|
-
with open(config_file,
|
190
|
+
with open(config_file, "w") as f:
|
198
191
|
json.dump(ssl_config, f)
|
199
|
-
|
192
|
+
|
200
193
|
example = StandaloneSecurityExample(config_path=config_file)
|
201
|
-
|
194
|
+
|
202
195
|
# Assertions
|
203
196
|
assert example is not None
|
204
197
|
assert example.security_manager is not None
|
205
|
-
|
198
|
+
|
206
199
|
def test_standalone_example_command_line_interface(self):
|
207
200
|
"""Test command line interface."""
|
208
201
|
# Create example without config file to use default configuration
|
209
202
|
example = StandaloneSecurityExample()
|
210
|
-
|
203
|
+
|
211
204
|
# Test that example has required methods
|
212
|
-
assert hasattr(example,
|
213
|
-
assert hasattr(example,
|
214
|
-
|
215
|
-
@patch(
|
205
|
+
assert hasattr(example, "process_request")
|
206
|
+
assert hasattr(example, "security_manager")
|
207
|
+
|
208
|
+
@patch("mcp_security_framework.examples.standalone_example.SecurityManager")
|
216
209
|
def test_standalone_example_error_handling(self, mock_security_manager_class):
|
217
210
|
"""Test error handling."""
|
218
211
|
# Mock security manager
|
219
212
|
mock_security_manager = Mock()
|
220
213
|
mock_security_manager_class.return_value = mock_security_manager
|
221
|
-
|
214
|
+
|
222
215
|
# Create example
|
223
216
|
config_file = self._create_config_file()
|
224
217
|
example = StandaloneSecurityExample(config_path=config_file)
|
225
|
-
|
218
|
+
|
226
219
|
# Test request processing with error
|
227
220
|
request_data = {
|
228
221
|
"credentials": {"api_key": "admin_key_123"},
|
229
222
|
"action": "read",
|
230
223
|
"resource": "data",
|
231
|
-
"identifier": "192.168.1.100"
|
224
|
+
"identifier": "192.168.1.100",
|
232
225
|
}
|
233
|
-
|
226
|
+
|
234
227
|
result = example.process_request(request_data)
|
235
|
-
|
228
|
+
|
236
229
|
# Assertions - expect failure since mocks are not working properly
|
237
230
|
assert isinstance(result, dict)
|