mcp-security-framework 0.1.0__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 (76) hide show
  1. mcp_security_framework/__init__.py +96 -0
  2. mcp_security_framework/cli/__init__.py +18 -0
  3. mcp_security_framework/cli/cert_cli.py +511 -0
  4. mcp_security_framework/cli/security_cli.py +791 -0
  5. mcp_security_framework/constants.py +209 -0
  6. mcp_security_framework/core/__init__.py +61 -0
  7. mcp_security_framework/core/auth_manager.py +1011 -0
  8. mcp_security_framework/core/cert_manager.py +1663 -0
  9. mcp_security_framework/core/permission_manager.py +735 -0
  10. mcp_security_framework/core/rate_limiter.py +602 -0
  11. mcp_security_framework/core/security_manager.py +943 -0
  12. mcp_security_framework/core/ssl_manager.py +735 -0
  13. mcp_security_framework/examples/__init__.py +75 -0
  14. mcp_security_framework/examples/django_example.py +615 -0
  15. mcp_security_framework/examples/fastapi_example.py +472 -0
  16. mcp_security_framework/examples/flask_example.py +506 -0
  17. mcp_security_framework/examples/gateway_example.py +803 -0
  18. mcp_security_framework/examples/microservice_example.py +690 -0
  19. mcp_security_framework/examples/standalone_example.py +576 -0
  20. mcp_security_framework/middleware/__init__.py +250 -0
  21. mcp_security_framework/middleware/auth_middleware.py +292 -0
  22. mcp_security_framework/middleware/fastapi_auth_middleware.py +447 -0
  23. mcp_security_framework/middleware/fastapi_middleware.py +757 -0
  24. mcp_security_framework/middleware/flask_auth_middleware.py +465 -0
  25. mcp_security_framework/middleware/flask_middleware.py +591 -0
  26. mcp_security_framework/middleware/mtls_middleware.py +439 -0
  27. mcp_security_framework/middleware/rate_limit_middleware.py +403 -0
  28. mcp_security_framework/middleware/security_middleware.py +507 -0
  29. mcp_security_framework/schemas/__init__.py +109 -0
  30. mcp_security_framework/schemas/config.py +694 -0
  31. mcp_security_framework/schemas/models.py +709 -0
  32. mcp_security_framework/schemas/responses.py +686 -0
  33. mcp_security_framework/tests/__init__.py +0 -0
  34. mcp_security_framework/utils/__init__.py +121 -0
  35. mcp_security_framework/utils/cert_utils.py +525 -0
  36. mcp_security_framework/utils/crypto_utils.py +475 -0
  37. mcp_security_framework/utils/validation_utils.py +571 -0
  38. mcp_security_framework-0.1.0.dist-info/METADATA +411 -0
  39. mcp_security_framework-0.1.0.dist-info/RECORD +76 -0
  40. mcp_security_framework-0.1.0.dist-info/WHEEL +5 -0
  41. mcp_security_framework-0.1.0.dist-info/entry_points.txt +3 -0
  42. mcp_security_framework-0.1.0.dist-info/top_level.txt +2 -0
  43. tests/__init__.py +0 -0
  44. tests/test_cli/__init__.py +0 -0
  45. tests/test_cli/test_cert_cli.py +379 -0
  46. tests/test_cli/test_security_cli.py +657 -0
  47. tests/test_core/__init__.py +0 -0
  48. tests/test_core/test_auth_manager.py +582 -0
  49. tests/test_core/test_cert_manager.py +795 -0
  50. tests/test_core/test_permission_manager.py +395 -0
  51. tests/test_core/test_rate_limiter.py +626 -0
  52. tests/test_core/test_security_manager.py +841 -0
  53. tests/test_core/test_ssl_manager.py +532 -0
  54. tests/test_examples/__init__.py +8 -0
  55. tests/test_examples/test_fastapi_example.py +264 -0
  56. tests/test_examples/test_flask_example.py +238 -0
  57. tests/test_examples/test_standalone_example.py +292 -0
  58. tests/test_integration/__init__.py +0 -0
  59. tests/test_integration/test_auth_flow.py +502 -0
  60. tests/test_integration/test_certificate_flow.py +527 -0
  61. tests/test_integration/test_fastapi_integration.py +341 -0
  62. tests/test_integration/test_flask_integration.py +398 -0
  63. tests/test_integration/test_standalone_integration.py +493 -0
  64. tests/test_middleware/__init__.py +0 -0
  65. tests/test_middleware/test_fastapi_middleware.py +523 -0
  66. tests/test_middleware/test_flask_middleware.py +582 -0
  67. tests/test_middleware/test_security_middleware.py +493 -0
  68. tests/test_schemas/__init__.py +0 -0
  69. tests/test_schemas/test_config.py +811 -0
  70. tests/test_schemas/test_models.py +879 -0
  71. tests/test_schemas/test_responses.py +1054 -0
  72. tests/test_schemas/test_serialization.py +493 -0
  73. tests/test_utils/__init__.py +0 -0
  74. tests/test_utils/test_cert_utils.py +510 -0
  75. tests/test_utils/test_crypto_utils.py +603 -0
  76. tests/test_utils/test_validation_utils.py +477 -0
@@ -0,0 +1,395 @@
1
+ """
2
+ Tests for Permission Manager Module
3
+
4
+ This module contains comprehensive tests for the PermissionManager class,
5
+ including role hierarchy management, permission validation, and caching.
6
+
7
+ Test Coverage:
8
+ - PermissionManager initialization
9
+ - Role and permission validation
10
+ - Role hierarchy management
11
+ - Permission caching
12
+ - CRUD operations for roles and permissions
13
+ - Error handling and edge cases
14
+
15
+ Author: MCP Security Team
16
+ Version: 1.0.0
17
+ License: MIT
18
+ """
19
+
20
+ import json
21
+ import tempfile
22
+ from pathlib import Path
23
+ from unittest.mock import mock_open, patch
24
+
25
+ import pytest
26
+
27
+ from mcp_security_framework.core.permission_manager import (
28
+ PermissionConfigurationError,
29
+ PermissionManager,
30
+ PermissionValidationError,
31
+ RoleNotFoundError,
32
+ )
33
+ from mcp_security_framework.schemas.config import PermissionConfig
34
+ from mcp_security_framework.schemas.models import ValidationResult
35
+
36
+
37
+ class TestPermissionManager:
38
+ """Test suite for PermissionManager class."""
39
+
40
+ def setup_method(self):
41
+ """Set up test fixtures before each test method."""
42
+ # Create temporary roles configuration
43
+ self.roles_config = {
44
+ "admin": {
45
+ "permissions": ["read:*", "write:*", "delete:*", "admin:*"],
46
+ "inherits": [],
47
+ },
48
+ "moderator": {
49
+ "permissions": ["read:*", "write:posts", "moderate:comments"],
50
+ "inherits": ["user"],
51
+ },
52
+ "user": {
53
+ "permissions": ["read:posts", "write:posts", "read:comments"],
54
+ "inherits": [],
55
+ },
56
+ "guest": {"permissions": ["read:posts"], "inherits": []},
57
+ }
58
+
59
+ # Create temporary file
60
+ self.temp_file = tempfile.NamedTemporaryFile(
61
+ mode="w", suffix=".json", delete=False
62
+ )
63
+ json.dump(self.roles_config, self.temp_file)
64
+ self.temp_file.close()
65
+
66
+ # Create config
67
+ self.config = PermissionConfig(
68
+ roles_file=self.temp_file.name, permission_cache_enabled=True
69
+ )
70
+
71
+ # Create permission manager
72
+ self.perm_manager = PermissionManager(self.config)
73
+
74
+ def teardown_method(self):
75
+ """Clean up after each test method."""
76
+ # Remove temporary file
77
+ Path(self.temp_file.name).unlink(missing_ok=True)
78
+
79
+ def test_permission_manager_initialization(self):
80
+ """Test PermissionManager initialization."""
81
+ assert self.perm_manager.config == self.config
82
+ assert self.perm_manager._cache_enabled is True
83
+ assert len(self.perm_manager._roles) == 4
84
+ assert "admin" in self.perm_manager._roles
85
+ assert "moderator" in self.perm_manager._roles
86
+ assert "user" in self.perm_manager._roles
87
+ assert "guest" in self.perm_manager._roles
88
+
89
+ def test_permission_manager_initialization_invalid_file(self):
90
+ """Test PermissionManager initialization with invalid file."""
91
+ # Create config without roles_file to bypass pydantic validation
92
+ config = PermissionConfig(permission_cache_enabled=True)
93
+ config.roles_file = "nonexistent.json"
94
+
95
+ with pytest.raises(PermissionConfigurationError):
96
+ PermissionManager(config)
97
+
98
+ def test_permission_manager_initialization_invalid_json(self):
99
+ """Test PermissionManager initialization with invalid JSON."""
100
+ with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as f:
101
+ f.write("invalid json content")
102
+ temp_file = f.name
103
+
104
+ config = PermissionConfig(roles_file=temp_file, permission_cache_enabled=True)
105
+
106
+ with pytest.raises(PermissionConfigurationError):
107
+ PermissionManager(config)
108
+
109
+ Path(temp_file).unlink()
110
+
111
+ def test_validate_access_success(self):
112
+ """Test successful access validation."""
113
+ result = self.perm_manager.validate_access(["admin"], ["read:*", "write:*"])
114
+
115
+ assert result.is_valid is True
116
+ assert result.status.value == "valid"
117
+
118
+ def test_validate_access_with_inheritance(self):
119
+ """Test access validation with role inheritance."""
120
+ result = self.perm_manager.validate_access(
121
+ ["moderator"], ["read:*", "write:posts", "moderate:comments"]
122
+ )
123
+
124
+ assert result.is_valid is True
125
+ assert result.status.value == "valid"
126
+
127
+ def test_validate_access_denied(self):
128
+ """Test access validation when access is denied."""
129
+ result = self.perm_manager.validate_access(
130
+ ["guest"], ["write:posts", "delete:posts"]
131
+ )
132
+
133
+ assert result.is_valid is False
134
+ assert result.status.value == "invalid"
135
+ assert result.error_code == -32003
136
+ assert "Missing permissions" in result.error_message
137
+
138
+ def test_validate_access_no_roles(self):
139
+ """Test access validation with no user roles."""
140
+ result = self.perm_manager.validate_access([], ["read:posts"])
141
+
142
+ assert result.is_valid is False
143
+ assert result.status.value == "invalid"
144
+ assert result.error_code == -32001
145
+ assert result.error_message == "No user roles provided"
146
+
147
+ def test_validate_access_no_permissions(self):
148
+ """Test access validation with no required permissions."""
149
+ result = self.perm_manager.validate_access(["admin"], [])
150
+
151
+ assert result.is_valid is True
152
+ assert result.status.value == "valid"
153
+
154
+ def test_validate_access_invalid_role(self):
155
+ """Test access validation with invalid role."""
156
+ result = self.perm_manager.validate_access(["invalid_role"], ["read:posts"])
157
+
158
+ assert result.is_valid is False
159
+ assert result.status.value == "invalid"
160
+ assert result.error_code == -32002
161
+ assert "Invalid roles" in result.error_message
162
+
163
+ def test_get_effective_permissions(self):
164
+ """Test getting effective permissions for user roles."""
165
+ permissions = self.perm_manager.get_effective_permissions(["admin"])
166
+
167
+ assert "read:*" in permissions
168
+ assert "write:*" in permissions
169
+ assert "delete:*" in permissions
170
+ assert "admin:*" in permissions
171
+
172
+ def test_get_effective_permissions_with_inheritance(self):
173
+ """Test getting effective permissions with role inheritance."""
174
+ permissions = self.perm_manager.get_effective_permissions(["moderator"])
175
+
176
+ # Direct permissions
177
+ assert "read:*" in permissions
178
+ assert "write:posts" in permissions
179
+ assert "moderate:comments" in permissions
180
+
181
+ # Inherited permissions from user role
182
+ assert "read:posts" in permissions
183
+ assert "read:comments" in permissions
184
+
185
+ def test_get_effective_permissions_multiple_roles(self):
186
+ """Test getting effective permissions for multiple roles."""
187
+ permissions = self.perm_manager.get_effective_permissions(["user", "guest"])
188
+
189
+ # User permissions
190
+ assert "read:posts" in permissions
191
+ assert "write:posts" in permissions
192
+ assert "read:comments" in permissions
193
+
194
+ # Guest permissions
195
+ assert "read:posts" in permissions # Duplicate, but should be in set
196
+
197
+ def test_get_effective_permissions_invalid_role(self):
198
+ """Test getting effective permissions with invalid role."""
199
+ with pytest.raises(RoleNotFoundError):
200
+ self.perm_manager.get_effective_permissions(["invalid_role"])
201
+
202
+ def test_check_role_hierarchy(self):
203
+ """Test role hierarchy checking."""
204
+ # Direct inheritance
205
+ assert self.perm_manager.check_role_hierarchy("moderator", "user") is True
206
+
207
+ # Self inheritance
208
+ assert self.perm_manager.check_role_hierarchy("admin", "admin") is True
209
+
210
+ # No inheritance
211
+ assert self.perm_manager.check_role_hierarchy("user", "admin") is False
212
+ assert self.perm_manager.check_role_hierarchy("guest", "moderator") is False
213
+
214
+ def test_check_role_hierarchy_invalid_roles(self):
215
+ """Test role hierarchy checking with invalid roles."""
216
+ with pytest.raises(RoleNotFoundError):
217
+ self.perm_manager.check_role_hierarchy("invalid_role", "admin")
218
+
219
+ with pytest.raises(RoleNotFoundError):
220
+ self.perm_manager.check_role_hierarchy("admin", "invalid_role")
221
+
222
+ def test_add_role_permission(self):
223
+ """Test adding permission to role."""
224
+ success = self.perm_manager.add_role_permission("user", "new:permission")
225
+
226
+ assert success is True
227
+ assert "new:permission" in self.perm_manager._roles["user"]["permissions"]
228
+
229
+ def test_add_role_permission_already_exists(self):
230
+ """Test adding permission that already exists."""
231
+ # Add permission first time
232
+ success1 = self.perm_manager.add_role_permission("user", "test:permission")
233
+ assert success1 is True
234
+
235
+ # Try to add same permission again
236
+ success2 = self.perm_manager.add_role_permission("user", "test:permission")
237
+ assert success2 is False
238
+
239
+ def test_add_role_permission_invalid_role(self):
240
+ """Test adding permission to invalid role."""
241
+ with pytest.raises(RoleNotFoundError):
242
+ self.perm_manager.add_role_permission("invalid_role", "test:permission")
243
+
244
+ def test_remove_role_permission(self):
245
+ """Test removing permission from role."""
246
+ # Add permission first
247
+ self.perm_manager.add_role_permission("user", "test:permission")
248
+
249
+ # Remove permission
250
+ success = self.perm_manager.remove_role_permission("user", "test:permission")
251
+
252
+ assert success is True
253
+ assert "test:permission" not in self.perm_manager._roles["user"]["permissions"]
254
+
255
+ def test_remove_role_permission_not_exists(self):
256
+ """Test removing permission that doesn't exist."""
257
+ success = self.perm_manager.remove_role_permission(
258
+ "user", "nonexistent:permission"
259
+ )
260
+ assert success is False
261
+
262
+ def test_remove_role_permission_invalid_role(self):
263
+ """Test removing permission from invalid role."""
264
+ with pytest.raises(RoleNotFoundError):
265
+ self.perm_manager.remove_role_permission("invalid_role", "test:permission")
266
+
267
+ def test_reload_roles_configuration(self):
268
+ """Test reloading roles configuration."""
269
+ # Modify the temporary file
270
+ new_config = {"new_role": {"permissions": ["new:permission"], "inherits": []}}
271
+
272
+ with open(self.temp_file.name, "w") as f:
273
+ json.dump(new_config, f)
274
+
275
+ # Reload configuration
276
+ success = self.perm_manager.reload_roles_configuration()
277
+
278
+ assert success is True
279
+ assert "new_role" in self.perm_manager._roles
280
+ assert "new:permission" in self.perm_manager._roles["new_role"]["permissions"]
281
+ assert "admin" not in self.perm_manager._roles # Old roles should be gone
282
+
283
+ def test_get_role_permissions(self):
284
+ """Test getting direct permissions for a role."""
285
+ permissions = self.perm_manager.get_role_permissions("admin")
286
+
287
+ assert "read:*" in permissions
288
+ assert "write:*" in permissions
289
+ assert "delete:*" in permissions
290
+ assert "admin:*" in permissions
291
+
292
+ def test_get_role_permissions_invalid_role(self):
293
+ """Test getting permissions for invalid role."""
294
+ with pytest.raises(RoleNotFoundError):
295
+ self.perm_manager.get_role_permissions("invalid_role")
296
+
297
+ def test_get_all_roles(self):
298
+ """Test getting all available roles."""
299
+ roles = self.perm_manager.get_all_roles()
300
+
301
+ assert "admin" in roles
302
+ assert "moderator" in roles
303
+ assert "user" in roles
304
+ assert "guest" in roles
305
+ assert len(roles) == 4
306
+
307
+ def test_get_role_hierarchy(self):
308
+ """Test getting role hierarchy."""
309
+ hierarchy = self.perm_manager.get_role_hierarchy()
310
+
311
+ assert hierarchy["admin"] == []
312
+ assert hierarchy["moderator"] == ["user"]
313
+ assert hierarchy["user"] == []
314
+ assert hierarchy["guest"] == []
315
+
316
+ def test_clear_cache(self):
317
+ """Test clearing permission cache."""
318
+ # Get permissions to populate cache
319
+ self.perm_manager.get_effective_permissions(["admin"])
320
+
321
+ # Clear cache
322
+ self.perm_manager.clear_cache()
323
+
324
+ assert len(self.perm_manager._permission_cache) == 0
325
+
326
+ def test_permission_caching(self):
327
+ """Test permission caching functionality."""
328
+ # First call should populate cache
329
+ permissions1 = self.perm_manager.get_effective_permissions(["admin"])
330
+
331
+ # Second call should use cache
332
+ permissions2 = self.perm_manager.get_effective_permissions(["admin"])
333
+
334
+ assert permissions1 == permissions2
335
+ assert len(self.perm_manager._permission_cache) > 0
336
+
337
+ def test_wildcard_permission_matching(self):
338
+ """Test wildcard permission matching."""
339
+ # Test action wildcard
340
+ result = self.perm_manager.validate_access(["user"], ["*:posts"])
341
+ assert result.is_valid is True
342
+ assert result.status.value == "valid"
343
+
344
+ # Test resource wildcard
345
+ result = self.perm_manager.validate_access(["admin"], ["read:*"])
346
+ assert result.is_valid is True
347
+ assert result.status.value == "valid"
348
+
349
+ # Test no match
350
+ result = self.perm_manager.validate_access(["guest"], ["write:*"])
351
+ assert result.is_valid is False
352
+ assert result.status.value == "invalid"
353
+
354
+ def test_permission_manager_with_cache_disabled(self):
355
+ """Test PermissionManager with caching disabled."""
356
+ config = PermissionConfig(
357
+ roles_file=self.temp_file.name, permission_cache_enabled=False
358
+ )
359
+
360
+ perm_manager = PermissionManager(config)
361
+
362
+ # Get permissions multiple times
363
+ permissions1 = perm_manager.get_effective_permissions(["admin"])
364
+ permissions2 = perm_manager.get_effective_permissions(["admin"])
365
+
366
+ assert permissions1 == permissions2
367
+ assert len(perm_manager._permission_cache) == 0 # No caching
368
+
369
+
370
+ class TestPermissionManagerErrors:
371
+ """Test suite for PermissionManager error handling."""
372
+
373
+ def test_permission_configuration_error(self):
374
+ """Test PermissionConfigurationError."""
375
+ error = PermissionConfigurationError("Test error", error_code=-32001)
376
+
377
+ assert error.message == "Test error"
378
+ assert error.error_code == -32001
379
+ assert str(error) == "Test error"
380
+
381
+ def test_role_not_found_error(self):
382
+ """Test RoleNotFoundError."""
383
+ error = RoleNotFoundError("Role not found", error_code=-32002)
384
+
385
+ assert error.message == "Role not found"
386
+ assert error.error_code == -32002
387
+ assert str(error) == "Role not found"
388
+
389
+ def test_permission_validation_error(self):
390
+ """Test PermissionValidationError."""
391
+ error = PermissionValidationError("Validation failed", error_code=-32003)
392
+
393
+ assert error.message == "Validation failed"
394
+ assert error.error_code == -32003
395
+ assert str(error) == "Validation failed"