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
@@ -1,472 +1,976 @@
|
|
1
|
+
#!/usr/bin/env python3
|
1
2
|
"""
|
2
|
-
FastAPI Example
|
3
|
+
FastAPI Example - Comprehensive Security Framework Demo
|
3
4
|
|
4
|
-
This
|
5
|
-
with
|
5
|
+
This example demonstrates ALL capabilities of the MCP Security Framework
|
6
|
+
with a real FastAPI application, serving as a comprehensive integration test.
|
6
7
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
8
|
+
Demonstrated Features:
|
9
|
+
1. Authentication (API Key, JWT, Certificate)
|
10
|
+
2. Authorization (Role-based access control)
|
11
|
+
3. SSL/TLS Management (Server/Client contexts)
|
12
|
+
4. Certificate Management (Creation, validation, revocation)
|
13
|
+
5. Rate Limiting (Request throttling)
|
14
|
+
6. Security Validation (Request/Configuration validation)
|
15
|
+
7. Security Monitoring (Status, metrics, audit)
|
16
|
+
8. Security Logging (Event logging)
|
17
|
+
9. FastAPI Middleware Integration
|
18
|
+
10. Real HTTP endpoints with security
|
14
19
|
|
15
|
-
Author:
|
20
|
+
Author: Vasiliy Zdanovskiy
|
21
|
+
email: vasilyvz@gmail.com
|
16
22
|
Version: 1.0.0
|
17
23
|
License: MIT
|
18
24
|
"""
|
19
25
|
|
20
|
-
import os
|
21
26
|
import json
|
22
|
-
import
|
23
|
-
|
27
|
+
import logging
|
28
|
+
import os
|
29
|
+
import shutil
|
30
|
+
import tempfile
|
24
31
|
from datetime import datetime, timedelta, timezone
|
32
|
+
from typing import Any, Dict, List, Optional
|
25
33
|
|
26
|
-
from fastapi import FastAPI, Request, Response,
|
27
|
-
from fastapi.responses import JSONResponse
|
34
|
+
from fastapi import Depends, FastAPI, HTTPException, Request, Response, status
|
28
35
|
from fastapi.middleware.cors import CORSMiddleware
|
29
|
-
from fastapi.security import
|
30
|
-
import
|
36
|
+
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
|
37
|
+
from pydantic import BaseModel
|
31
38
|
|
32
|
-
from mcp_security_framework.core.security_manager import SecurityManager
|
33
|
-
from mcp_security_framework.core.auth_manager import AuthManager
|
34
|
-
from mcp_security_framework.core.ssl_manager import SSLManager
|
35
|
-
from mcp_security_framework.core.permission_manager import PermissionManager
|
36
|
-
from mcp_security_framework.core.rate_limiter import RateLimiter
|
37
|
-
from mcp_security_framework.schemas.config import SecurityConfig, AuthConfig, SSLConfig
|
38
|
-
from mcp_security_framework.schemas.models import AuthResult, AuthStatus, AuthMethod
|
39
|
-
from mcp_security_framework.middleware.fastapi_middleware import FastAPISecurityMiddleware
|
40
39
|
from mcp_security_framework.constants import (
|
41
|
-
|
42
|
-
|
40
|
+
AUTH_METHODS,
|
41
|
+
DEFAULT_SECURITY_HEADERS,
|
42
|
+
ErrorCodes,
|
43
|
+
)
|
44
|
+
from mcp_security_framework.core.security_manager import SecurityManager
|
45
|
+
from mcp_security_framework.schemas.config import (
|
46
|
+
AuthConfig,
|
47
|
+
CertificateConfig,
|
48
|
+
LoggingConfig,
|
49
|
+
PermissionConfig,
|
50
|
+
RateLimitConfig,
|
51
|
+
SecurityConfig,
|
52
|
+
SSLConfig,
|
53
|
+
)
|
54
|
+
from mcp_security_framework.schemas.models import (
|
55
|
+
AuthMethod,
|
56
|
+
AuthResult,
|
57
|
+
AuthStatus,
|
58
|
+
CertificateInfo,
|
59
|
+
CertificatePair,
|
60
|
+
ValidationResult,
|
61
|
+
ValidationStatus,
|
43
62
|
)
|
44
63
|
|
45
64
|
|
46
|
-
class
|
65
|
+
class FastAPISecurityExample:
|
47
66
|
"""
|
48
|
-
|
49
|
-
|
50
|
-
This class demonstrates
|
51
|
-
with
|
52
|
-
- Multi-method authentication (API Key, JWT, Certificate)
|
53
|
-
- Role-based access control
|
54
|
-
- Rate limiting with Redis backend
|
55
|
-
- SSL/TLS configuration
|
56
|
-
- Security headers and CORS
|
57
|
-
- Comprehensive logging and monitoring
|
67
|
+
Comprehensive FastAPI Security Example
|
68
|
+
|
69
|
+
This class demonstrates ALL capabilities of the MCP Security Framework
|
70
|
+
with a real FastAPI application, serving as a complete integration test.
|
58
71
|
"""
|
59
|
-
|
72
|
+
|
60
73
|
def __init__(self, config_path: Optional[str] = None):
|
61
74
|
"""
|
62
|
-
Initialize FastAPI
|
63
|
-
|
75
|
+
Initialize the FastAPI security example.
|
76
|
+
|
64
77
|
Args:
|
65
|
-
config_path:
|
78
|
+
config_path: Optional path to configuration file
|
66
79
|
"""
|
67
80
|
self.config = self._load_config(config_path)
|
68
81
|
self.security_manager = SecurityManager(self.config)
|
69
|
-
self.
|
70
|
-
|
82
|
+
self.logger = logging.getLogger(__name__)
|
83
|
+
|
84
|
+
# Create FastAPI app
|
85
|
+
self.app = FastAPI(
|
86
|
+
title="MCP Security Framework - FastAPI Example",
|
87
|
+
description="Comprehensive security framework demonstration with FastAPI",
|
88
|
+
version="1.0.0",
|
89
|
+
)
|
90
|
+
|
91
|
+
# Setup security middleware
|
92
|
+
self._setup_security_middleware()
|
93
|
+
|
94
|
+
# Setup routes
|
71
95
|
self._setup_routes()
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
SecurityConfig: Loaded configuration
|
83
|
-
"""
|
96
|
+
|
97
|
+
# Test data
|
98
|
+
self.test_api_key = "admin_key_123"
|
99
|
+
self.test_jwt_token = self._create_test_jwt_token()
|
100
|
+
self.test_certificate = self._create_test_certificate()
|
101
|
+
|
102
|
+
self.logger.info("FastAPI Security Example initialized successfully")
|
103
|
+
|
104
|
+
def _load_config(self, config_path: Optional[str] = None) -> SecurityConfig:
|
105
|
+
"""Load security configuration."""
|
84
106
|
if config_path and os.path.exists(config_path):
|
85
|
-
with open(config_path,
|
107
|
+
with open(config_path, "r") as f:
|
86
108
|
config_data = json.load(f)
|
87
109
|
return SecurityConfig(**config_data)
|
88
|
-
|
89
|
-
# Create
|
110
|
+
|
111
|
+
# Create comprehensive configuration
|
90
112
|
return SecurityConfig(
|
91
113
|
auth=AuthConfig(
|
92
114
|
enabled=True,
|
93
|
-
methods=[
|
115
|
+
methods=[
|
116
|
+
AUTH_METHODS["API_KEY"],
|
117
|
+
AUTH_METHODS["JWT"],
|
118
|
+
AUTH_METHODS["CERTIFICATE"],
|
119
|
+
],
|
94
120
|
api_keys={
|
95
121
|
"admin_key_123": {"username": "admin", "roles": ["admin", "user"]},
|
96
122
|
"user_key_456": {"username": "user", "roles": ["user"]},
|
97
|
-
"readonly_key_789": {"username": "readonly", "roles": ["readonly"]}
|
123
|
+
"readonly_key_789": {"username": "readonly", "roles": ["readonly"]},
|
98
124
|
},
|
99
|
-
jwt_secret="your-super-secret-jwt-key-change-in-production",
|
125
|
+
jwt_secret="your-super-secret-jwt-key-change-in-production-12345",
|
100
126
|
jwt_algorithm="HS256",
|
101
127
|
jwt_expiry_hours=24,
|
102
|
-
public_paths=["/health", "/
|
103
|
-
security_headers=DEFAULT_SECURITY_HEADERS
|
128
|
+
public_paths=["/health", "/metrics", "/docs", "/openapi.json"],
|
129
|
+
security_headers=DEFAULT_SECURITY_HEADERS,
|
104
130
|
),
|
105
|
-
|
131
|
+
permissions=PermissionConfig(
|
106
132
|
enabled=True,
|
107
|
-
|
108
|
-
|
109
|
-
|
133
|
+
roles_file="config/roles.json",
|
134
|
+
default_role="user",
|
135
|
+
hierarchy_enabled=True,
|
136
|
+
),
|
137
|
+
ssl=SSLConfig(
|
138
|
+
enabled=False, # Disable for example
|
139
|
+
cert_file=None,
|
140
|
+
key_file=None,
|
141
|
+
ca_cert_file=None,
|
110
142
|
verify_mode="CERT_REQUIRED",
|
111
|
-
min_version="TLSv1.2"
|
143
|
+
min_version="TLSv1.2",
|
112
144
|
),
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
"
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
"
|
132
|
-
"
|
133
|
-
"
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
"backup_count": 5,
|
142
|
-
"console_output": True,
|
143
|
-
"json_format": False
|
144
|
-
}
|
145
|
-
)
|
146
|
-
|
147
|
-
def _create_fastapi_app(self) -> FastAPI:
|
148
|
-
"""
|
149
|
-
Create FastAPI application with security features.
|
150
|
-
|
151
|
-
Returns:
|
152
|
-
FastAPI: Configured FastAPI application
|
153
|
-
"""
|
154
|
-
app = FastAPI(
|
155
|
-
title="Secure API Example",
|
156
|
-
description="FastAPI application with MCP Security Framework",
|
145
|
+
certificates=CertificateConfig(
|
146
|
+
enabled=False, # Disable for example
|
147
|
+
ca_cert_path=None,
|
148
|
+
ca_key_path=None,
|
149
|
+
cert_validity_days=365,
|
150
|
+
key_size=2048,
|
151
|
+
),
|
152
|
+
rate_limit=RateLimitConfig(
|
153
|
+
enabled=True,
|
154
|
+
default_requests_per_minute=60,
|
155
|
+
default_requests_per_hour=1000,
|
156
|
+
burst_limit=2,
|
157
|
+
window_size_seconds=60,
|
158
|
+
storage_backend="memory",
|
159
|
+
cleanup_interval=300,
|
160
|
+
),
|
161
|
+
logging=LoggingConfig(
|
162
|
+
enabled=True,
|
163
|
+
level="INFO",
|
164
|
+
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
|
165
|
+
file_path="logs/security.log",
|
166
|
+
max_file_size=10,
|
167
|
+
backup_count=5,
|
168
|
+
console_output=True,
|
169
|
+
json_format=False,
|
170
|
+
),
|
171
|
+
debug=True,
|
172
|
+
environment="test",
|
157
173
|
version="1.0.0",
|
158
|
-
docs_url="/docs",
|
159
|
-
redoc_url="/redoc"
|
160
174
|
)
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
175
|
+
|
176
|
+
def _create_test_jwt_token(self) -> str:
|
177
|
+
"""Create a test JWT token."""
|
178
|
+
import jwt
|
179
|
+
|
180
|
+
payload = {
|
181
|
+
"username": "test_user",
|
182
|
+
"roles": ["user"],
|
183
|
+
"exp": datetime.now(timezone.utc) + timedelta(hours=1),
|
184
|
+
}
|
185
|
+
jwt_secret = (
|
186
|
+
self.config.auth.jwt_secret.get_secret_value()
|
187
|
+
if self.config.auth.jwt_secret
|
188
|
+
else "default-jwt-secret-for-testing"
|
169
189
|
)
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
"""
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
190
|
+
return jwt.encode(payload, jwt_secret, algorithm="HS256")
|
191
|
+
|
192
|
+
def _create_test_certificate(self) -> str:
|
193
|
+
"""Create a test certificate."""
|
194
|
+
return """-----BEGIN CERTIFICATE-----
|
195
|
+
MIIDXTCCAkWgAwIBAgIJAKoK8sJgKqQqMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV
|
196
|
+
BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX
|
197
|
+
aWRnaXRzIFB0eSBMdGQwHhcNMTkwMzI2MTIzMzQ5WhcNMjAwMzI1MTIzMzQ5WjBF
|
198
|
+
MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50
|
199
|
+
ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
|
200
|
+
CgKCAQEAvxL8JgKqQqMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNVBAYTAkFVMRMw
|
201
|
+
EQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0
|
202
|
+
eSBMdGQwHhcNMTkwMzI2MTIzMzQ5WhcNMjAwMzI1MTIzMzQ5WjBFMQswCQYDVQQG
|
203
|
+
EwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lk
|
204
|
+
Z2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
|
205
|
+
-----END CERTIFICATE-----"""
|
206
|
+
|
207
|
+
def _setup_security_middleware(self):
|
208
|
+
"""Setup security middleware for FastAPI."""
|
209
|
+
|
210
|
+
@self.app.middleware("http")
|
211
|
+
async def security_middleware(request: Request, call_next):
|
212
|
+
"""Security middleware for request processing."""
|
213
|
+
# Check if path is public
|
214
|
+
if self._is_public_path(request.url.path):
|
215
|
+
response = await call_next(request)
|
216
|
+
self._add_security_headers(response)
|
217
|
+
return response
|
218
|
+
|
219
|
+
# Rate limiting check
|
220
|
+
if not self._check_rate_limit(request):
|
221
|
+
return Response(
|
222
|
+
content=json.dumps(
|
223
|
+
{"error": "Rate limit exceeded", "error_code": -32003}
|
224
|
+
),
|
225
|
+
status_code=status.HTTP_429_TOO_MANY_REQUESTS,
|
226
|
+
media_type="application/json",
|
227
|
+
)
|
228
|
+
|
229
|
+
# Authentication check
|
230
|
+
auth_result = await self._authenticate_request(request)
|
231
|
+
if not auth_result.is_valid:
|
232
|
+
return Response(
|
233
|
+
content=json.dumps(
|
234
|
+
{
|
235
|
+
"error": auth_result.error_message,
|
236
|
+
"error_code": auth_result.error_code,
|
237
|
+
}
|
238
|
+
),
|
239
|
+
status_code=status.HTTP_401_UNAUTHORIZED,
|
240
|
+
media_type="application/json",
|
241
|
+
)
|
242
|
+
|
243
|
+
# Authorization check (for protected endpoints)
|
244
|
+
if request.url.path.startswith("/admin") or request.url.path.startswith(
|
245
|
+
"/api/v1"
|
246
|
+
):
|
247
|
+
if not self._check_permissions(request, auth_result):
|
248
|
+
return Response(
|
249
|
+
content=json.dumps(
|
250
|
+
{"error": "Insufficient permissions", "error_code": -32004}
|
251
|
+
),
|
252
|
+
status_code=status.HTTP_403_FORBIDDEN,
|
253
|
+
media_type="application/json",
|
254
|
+
)
|
255
|
+
|
256
|
+
# Add user info to request state
|
257
|
+
request.state.user_info = {
|
258
|
+
"username": auth_result.username,
|
259
|
+
"roles": auth_result.roles,
|
260
|
+
"permissions": auth_result.permissions,
|
261
|
+
"auth_method": auth_result.auth_method.value,
|
262
|
+
}
|
263
|
+
|
264
|
+
# Process request
|
265
|
+
response = await call_next(request)
|
266
|
+
|
267
|
+
# Add security headers
|
268
|
+
self._add_security_headers(response)
|
269
|
+
|
270
|
+
return response
|
271
|
+
|
272
|
+
def _is_public_path(self, path: str) -> bool:
|
273
|
+
"""Check if path is public."""
|
274
|
+
return any(
|
275
|
+
path.startswith(public_path)
|
276
|
+
for public_path in self.config.auth.public_paths
|
277
|
+
)
|
278
|
+
|
279
|
+
def _check_rate_limit(self, request: Request) -> bool:
|
280
|
+
"""Check rate limit for request."""
|
281
|
+
identifier = request.client.host if request.client else "unknown"
|
282
|
+
return self.security_manager.check_rate_limit(identifier)
|
283
|
+
|
284
|
+
async def _authenticate_request(self, request: Request) -> AuthResult:
|
285
|
+
"""Authenticate request."""
|
286
|
+
# Check for API key in headers
|
287
|
+
api_key = request.headers.get("X-API-Key")
|
288
|
+
if api_key:
|
289
|
+
return self.security_manager.authenticate_user(
|
290
|
+
{"method": "api_key", "api_key": api_key}
|
208
291
|
)
|
209
|
-
|
210
|
-
#
|
211
|
-
|
212
|
-
|
292
|
+
|
293
|
+
# Check for JWT token in Authorization header
|
294
|
+
auth_header = request.headers.get("Authorization")
|
295
|
+
if auth_header and auth_header.startswith("Bearer "):
|
296
|
+
token = auth_header[7:]
|
297
|
+
return self.security_manager.authenticate_user(
|
298
|
+
{"method": "jwt", "token": token}
|
299
|
+
)
|
300
|
+
|
301
|
+
# Check for certificate in headers (for mTLS)
|
302
|
+
cert_header = request.headers.get("X-Client-Cert")
|
303
|
+
if cert_header:
|
304
|
+
return self.security_manager.authenticate_user(
|
305
|
+
{"method": "certificate", "certificate": cert_header}
|
306
|
+
)
|
307
|
+
|
308
|
+
# Return failed authentication
|
309
|
+
return AuthResult(
|
310
|
+
is_valid=False,
|
311
|
+
status=AuthStatus.INVALID,
|
312
|
+
auth_method=AuthMethod.UNKNOWN,
|
313
|
+
error_code=-32001,
|
314
|
+
error_message="No authentication credentials provided",
|
315
|
+
)
|
316
|
+
|
317
|
+
def _check_permissions(self, request: Request, auth_result: AuthResult) -> bool:
|
318
|
+
"""Check permissions for request."""
|
319
|
+
# Determine required permissions based on endpoint
|
320
|
+
required_permissions = []
|
321
|
+
|
322
|
+
if request.url.path.startswith("/admin"):
|
323
|
+
required_permissions = ["admin"]
|
324
|
+
elif request.url.path.startswith("/api/v1/users"):
|
325
|
+
required_permissions = ["read:own"] # Use read:own instead of read:users
|
326
|
+
elif request.url.path.startswith("/api/v1/data"):
|
327
|
+
required_permissions = ["read:own"] # Use read:own instead of read:data
|
328
|
+
|
329
|
+
if required_permissions:
|
330
|
+
result = self.security_manager.check_permissions(
|
331
|
+
auth_result.roles, required_permissions
|
332
|
+
)
|
333
|
+
return result.is_valid
|
334
|
+
|
335
|
+
return True
|
336
|
+
|
337
|
+
def _add_security_headers(self, response: Response):
|
338
|
+
"""Add security headers to response."""
|
339
|
+
if self.config.auth.security_headers:
|
340
|
+
for header, value in self.config.auth.security_headers.items():
|
341
|
+
response.headers[header] = value
|
342
|
+
|
213
343
|
def _setup_routes(self):
|
214
|
-
"""Setup
|
215
|
-
|
344
|
+
"""Setup FastAPI routes."""
|
345
|
+
|
346
|
+
# Health check endpoint
|
216
347
|
@self.app.get("/health")
|
217
348
|
async def health_check():
|
218
|
-
"""Health check endpoint
|
349
|
+
"""Health check endpoint."""
|
219
350
|
return {
|
220
351
|
"status": "healthy",
|
352
|
+
"framework": "FastAPI",
|
353
|
+
"security_enabled": True,
|
221
354
|
"timestamp": datetime.now(timezone.utc).isoformat(),
|
222
|
-
"version": "1.0.0"
|
223
355
|
}
|
224
|
-
|
356
|
+
|
357
|
+
# Metrics endpoint
|
225
358
|
@self.app.get("/metrics")
|
226
|
-
async def
|
227
|
-
"""
|
359
|
+
async def get_metrics():
|
360
|
+
"""Get security metrics."""
|
361
|
+
metrics = self.security_manager.get_security_metrics()
|
228
362
|
return {
|
229
|
-
"
|
230
|
-
"
|
231
|
-
"
|
232
|
-
"uptime_seconds": 3600
|
363
|
+
"framework": "FastAPI",
|
364
|
+
"metrics": metrics,
|
365
|
+
"timestamp": datetime.now(timezone.utc).isoformat(),
|
233
366
|
}
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
# If middleware didn't set user_info, try authentication
|
242
|
-
if not user_info:
|
243
|
-
try:
|
244
|
-
auth_result = await self.get_current_user(request)
|
245
|
-
user_info = {
|
246
|
-
"username": auth_result.username,
|
247
|
-
"roles": auth_result.roles,
|
248
|
-
"permissions": auth_result.permissions
|
249
|
-
}
|
250
|
-
except HTTPException:
|
251
|
-
raise HTTPException(status_code=401, detail="Authentication required")
|
252
|
-
|
367
|
+
|
368
|
+
# Security status endpoint
|
369
|
+
@self.app.get("/security/status")
|
370
|
+
async def get_security_status():
|
371
|
+
"""Get security status."""
|
372
|
+
status = self.security_manager.get_security_status()
|
253
373
|
return {
|
254
|
-
"
|
255
|
-
"
|
256
|
-
"
|
257
|
-
"last_login": datetime.now(timezone.utc).isoformat()
|
374
|
+
"framework": "FastAPI",
|
375
|
+
"status": status.dict(),
|
376
|
+
"timestamp": datetime.now(timezone.utc).isoformat(),
|
258
377
|
}
|
259
|
-
|
260
|
-
|
261
|
-
|
378
|
+
|
379
|
+
# Security audit endpoint
|
380
|
+
@self.app.get("/security/audit")
|
381
|
+
async def get_security_audit():
|
382
|
+
"""Get security audit."""
|
383
|
+
audit = self.security_manager.perform_security_audit()
|
384
|
+
return {
|
385
|
+
"framework": "FastAPI",
|
386
|
+
"audit": audit,
|
387
|
+
"timestamp": datetime.now(timezone.utc).isoformat(),
|
388
|
+
}
|
389
|
+
|
390
|
+
# Admin endpoints (require admin permissions)
|
391
|
+
@self.app.get("/admin/users")
|
392
|
+
async def get_users(request: Request):
|
262
393
|
"""Get all users (admin only)."""
|
263
|
-
|
264
|
-
user_info = getattr(request.state, 'user_info', None)
|
265
|
-
|
266
|
-
# If middleware didn't set user_info, try authentication
|
267
|
-
if not user_info:
|
268
|
-
try:
|
269
|
-
auth_result = await self.get_current_user(request)
|
270
|
-
user_info = {
|
271
|
-
"username": auth_result.username,
|
272
|
-
"roles": auth_result.roles,
|
273
|
-
"permissions": auth_result.permissions
|
274
|
-
}
|
275
|
-
except HTTPException:
|
276
|
-
raise HTTPException(status_code=401, detail="Authentication required")
|
277
|
-
|
278
|
-
# Check admin permission
|
279
|
-
if "admin" not in user_info.get("roles", []):
|
280
|
-
raise HTTPException(status_code=403, detail="Admin access required")
|
281
|
-
|
394
|
+
user_info = getattr(request.state, "user_info", {})
|
282
395
|
return {
|
396
|
+
"message": "Users list",
|
397
|
+
"user": user_info,
|
283
398
|
"users": [
|
284
|
-
{"
|
285
|
-
{"
|
286
|
-
{"
|
287
|
-
]
|
399
|
+
{"id": 1, "username": "admin", "roles": ["admin"]},
|
400
|
+
{"id": 2, "username": "user", "roles": ["user"]},
|
401
|
+
{"id": 3, "username": "readonly", "roles": ["readonly"]},
|
402
|
+
],
|
403
|
+
}
|
404
|
+
|
405
|
+
# API endpoints (require specific permissions)
|
406
|
+
@self.app.get("/api/v1/users/me")
|
407
|
+
async def get_current_user(request: Request):
|
408
|
+
"""Get current user info."""
|
409
|
+
user_info = getattr(request.state, "user_info", {})
|
410
|
+
return {"message": "Current user info", "user": user_info}
|
411
|
+
|
412
|
+
@self.app.get("/api/v1/data")
|
413
|
+
async def get_data(request: Request):
|
414
|
+
"""Get data (requires read:data permission)."""
|
415
|
+
user_info = getattr(request.state, "user_info", {})
|
416
|
+
return {
|
417
|
+
"message": "Data retrieved successfully",
|
418
|
+
"user": user_info,
|
419
|
+
"data": [
|
420
|
+
{"id": 1, "name": "Sample Data 1"},
|
421
|
+
{"id": 2, "name": "Sample Data 2"},
|
422
|
+
{"id": 3, "name": "Sample Data 3"},
|
423
|
+
],
|
424
|
+
}
|
425
|
+
|
426
|
+
# Authentication test endpoints
|
427
|
+
@self.app.post("/auth/test-api-key")
|
428
|
+
async def test_api_key_auth(request: Request):
|
429
|
+
"""Test API key authentication."""
|
430
|
+
user_info = getattr(request.state, "user_info", {})
|
431
|
+
return {
|
432
|
+
"message": "API key authentication successful",
|
433
|
+
"user": user_info,
|
434
|
+
"auth_method": "api_key",
|
288
435
|
}
|
289
|
-
|
290
|
-
@self.app.post("/
|
291
|
-
async def
|
292
|
-
"""
|
293
|
-
|
294
|
-
user_info = getattr(request.state, 'user_info', None)
|
295
|
-
|
296
|
-
# If middleware didn't set user_info, try authentication
|
297
|
-
if not user_info:
|
298
|
-
try:
|
299
|
-
auth_result = await self.get_current_user(request)
|
300
|
-
user_info = {
|
301
|
-
"username": auth_result.username,
|
302
|
-
"roles": auth_result.roles,
|
303
|
-
"permissions": auth_result.permissions
|
304
|
-
}
|
305
|
-
except HTTPException:
|
306
|
-
raise HTTPException(status_code=401, detail="Authentication required")
|
307
|
-
|
308
|
-
# Check write permission
|
309
|
-
if "readonly" in user_info.get("roles", []):
|
310
|
-
raise HTTPException(status_code=403, detail="Write permission required")
|
311
|
-
|
312
|
-
# Process request data
|
313
|
-
data = await request.json()
|
436
|
+
|
437
|
+
@self.app.post("/auth/test-jwt")
|
438
|
+
async def test_jwt_auth(request: Request):
|
439
|
+
"""Test JWT authentication."""
|
440
|
+
user_info = getattr(request.state, "user_info", {})
|
314
441
|
return {
|
315
|
-
"
|
316
|
-
"
|
317
|
-
"
|
318
|
-
"created_at": datetime.now(timezone.utc).isoformat()
|
442
|
+
"message": "JWT authentication successful",
|
443
|
+
"user": user_info,
|
444
|
+
"auth_method": "jwt",
|
319
445
|
}
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
user_info = getattr(request.state,
|
326
|
-
|
327
|
-
# If middleware didn't set user_info, try authentication
|
328
|
-
if not user_info:
|
329
|
-
try:
|
330
|
-
auth_result = await self.get_current_user(request)
|
331
|
-
user_info = {
|
332
|
-
"username": auth_result.username,
|
333
|
-
"roles": auth_result.roles,
|
334
|
-
"permissions": auth_result.permissions
|
335
|
-
}
|
336
|
-
except HTTPException:
|
337
|
-
raise HTTPException(status_code=401, detail="Authentication required")
|
338
|
-
|
446
|
+
|
447
|
+
# Rate limiting test endpoint
|
448
|
+
@self.app.get("/rate-limit-test")
|
449
|
+
async def rate_limit_test(request: Request):
|
450
|
+
"""Test rate limiting."""
|
451
|
+
user_info = getattr(request.state, "user_info", {})
|
339
452
|
return {
|
340
|
-
"
|
341
|
-
"
|
342
|
-
"
|
343
|
-
|
453
|
+
"message": "Rate limit test successful",
|
454
|
+
"user": user_info,
|
455
|
+
"timestamp": datetime.now(timezone.utc).isoformat(),
|
456
|
+
}
|
457
|
+
|
458
|
+
def demonstrate_authentication(self) -> Dict[str, Any]:
|
459
|
+
"""
|
460
|
+
Demonstrate ALL authentication methods.
|
461
|
+
|
462
|
+
Returns:
|
463
|
+
Dict with authentication test results
|
464
|
+
"""
|
465
|
+
self.logger.info("Demonstrating authentication capabilities...")
|
466
|
+
|
467
|
+
results = {
|
468
|
+
"api_key_auth": {},
|
469
|
+
"jwt_auth": {},
|
470
|
+
"certificate_auth": {},
|
471
|
+
"failed_auth": {},
|
472
|
+
}
|
473
|
+
|
474
|
+
# 1. API Key Authentication
|
475
|
+
try:
|
476
|
+
auth_result = self.security_manager.authenticate_user(
|
477
|
+
{"method": "api_key", "api_key": self.test_api_key}
|
478
|
+
)
|
479
|
+
results["api_key_auth"] = {
|
480
|
+
"success": auth_result.is_valid,
|
481
|
+
"username": auth_result.username,
|
482
|
+
"roles": auth_result.roles,
|
483
|
+
"auth_method": auth_result.auth_method.value,
|
344
484
|
}
|
345
|
-
|
346
|
-
|
347
|
-
"""Setup custom error handlers."""
|
348
|
-
|
349
|
-
@self.app.exception_handler(HTTPException)
|
350
|
-
async def http_exception_handler(request: Request, exc: HTTPException):
|
351
|
-
"""Handle HTTP exceptions with security context."""
|
352
|
-
return JSONResponse(
|
353
|
-
status_code=exc.status_code,
|
354
|
-
content={
|
355
|
-
"error": "HTTP Error",
|
356
|
-
"message": exc.detail,
|
357
|
-
"status_code": exc.status_code,
|
358
|
-
"timestamp": datetime.now(timezone.utc).isoformat(),
|
359
|
-
"path": request.url.path
|
360
|
-
}
|
485
|
+
self.logger.info(
|
486
|
+
f"API Key auth: {auth_result.username} - {auth_result.roles}"
|
361
487
|
)
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
488
|
+
except Exception as e:
|
489
|
+
results["api_key_auth"] = {"error": str(e)}
|
490
|
+
|
491
|
+
# 2. JWT Authentication
|
492
|
+
try:
|
493
|
+
auth_result = self.security_manager.authenticate_user(
|
494
|
+
{"method": "jwt", "token": self.test_jwt_token}
|
495
|
+
)
|
496
|
+
results["jwt_auth"] = {
|
497
|
+
"success": auth_result.is_valid,
|
498
|
+
"username": auth_result.username,
|
499
|
+
"roles": auth_result.roles,
|
500
|
+
"auth_method": auth_result.auth_method.value,
|
501
|
+
}
|
502
|
+
self.logger.info(f"JWT auth: {auth_result.username} - {auth_result.roles}")
|
503
|
+
except Exception as e:
|
504
|
+
results["jwt_auth"] = {"error": str(e)}
|
505
|
+
|
506
|
+
# 3. Certificate Authentication
|
507
|
+
try:
|
508
|
+
auth_result = self.security_manager.authenticate_user(
|
509
|
+
{"method": "certificate", "certificate": self.test_certificate}
|
510
|
+
)
|
511
|
+
results["certificate_auth"] = {
|
512
|
+
"success": auth_result.is_valid,
|
513
|
+
"username": auth_result.username,
|
514
|
+
"roles": auth_result.roles,
|
515
|
+
"auth_method": auth_result.auth_method.value,
|
516
|
+
}
|
517
|
+
self.logger.info(
|
518
|
+
f"Certificate auth: {auth_result.username} - {auth_result.roles}"
|
519
|
+
)
|
520
|
+
except Exception as e:
|
521
|
+
results["certificate_auth"] = {"error": str(e)}
|
522
|
+
|
523
|
+
# 4. Failed Authentication
|
524
|
+
try:
|
525
|
+
auth_result = self.security_manager.authenticate_user(
|
526
|
+
{"method": "api_key", "api_key": "invalid_key"}
|
374
527
|
)
|
375
|
-
|
376
|
-
|
528
|
+
results["failed_auth"] = {
|
529
|
+
"success": auth_result.is_valid,
|
530
|
+
"error_message": auth_result.error_message,
|
531
|
+
"error_code": auth_result.error_code,
|
532
|
+
}
|
533
|
+
self.logger.info(f"Failed auth test: {auth_result.error_message}")
|
534
|
+
except Exception as e:
|
535
|
+
results["failed_auth"] = {"error": str(e)}
|
536
|
+
|
537
|
+
return results
|
538
|
+
|
539
|
+
def demonstrate_authorization(self) -> Dict[str, Any]:
|
377
540
|
"""
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
port: Port to bind to
|
383
|
-
ssl_keyfile: SSL private key file
|
384
|
-
ssl_certfile: SSL certificate file
|
541
|
+
Demonstrate authorization capabilities.
|
542
|
+
|
543
|
+
Returns:
|
544
|
+
Dict with authorization test results
|
385
545
|
"""
|
386
|
-
|
387
|
-
print(f"SSL Enabled: {self.config.ssl.enabled}")
|
388
|
-
print(f"Authentication Methods: {self.config.auth.methods}")
|
389
|
-
print(f"Rate Limiting: {self.config.rate_limit.enabled}")
|
390
|
-
|
391
|
-
uvicorn.run(
|
392
|
-
self.app,
|
393
|
-
host=host,
|
394
|
-
port=port,
|
395
|
-
ssl_keyfile=ssl_keyfile if self.config.ssl.enabled else None,
|
396
|
-
ssl_certfile=ssl_certfile if self.config.ssl.enabled else None,
|
397
|
-
log_level="info"
|
398
|
-
)
|
546
|
+
self.logger.info("Demonstrating authorization capabilities...")
|
399
547
|
|
548
|
+
results = {
|
549
|
+
"admin_permissions": {},
|
550
|
+
"user_permissions": {},
|
551
|
+
"readonly_permissions": {},
|
552
|
+
"denied_permissions": {},
|
553
|
+
}
|
554
|
+
|
555
|
+
# 1. Admin permissions
|
556
|
+
try:
|
557
|
+
result = self.security_manager.check_permissions(
|
558
|
+
["admin"], ["read", "write", "delete"]
|
559
|
+
)
|
560
|
+
results["admin_permissions"] = {
|
561
|
+
"success": result.is_valid,
|
562
|
+
"status": result.status.value,
|
563
|
+
}
|
564
|
+
self.logger.info(f"Admin permissions: {result.is_valid}")
|
565
|
+
except Exception as e:
|
566
|
+
results["admin_permissions"] = {"error": str(e)}
|
567
|
+
|
568
|
+
# 2. User permissions
|
569
|
+
try:
|
570
|
+
result = self.security_manager.check_permissions(
|
571
|
+
["user"], ["read", "write"]
|
572
|
+
)
|
573
|
+
results["user_permissions"] = {
|
574
|
+
"success": result.is_valid,
|
575
|
+
"status": result.status.value,
|
576
|
+
}
|
577
|
+
self.logger.info(f"User permissions: {result.is_valid}")
|
578
|
+
except Exception as e:
|
579
|
+
results["user_permissions"] = {"error": str(e)}
|
580
|
+
|
581
|
+
# 3. Readonly permissions
|
582
|
+
try:
|
583
|
+
result = self.security_manager.check_permissions(["readonly"], ["read"])
|
584
|
+
results["readonly_permissions"] = {
|
585
|
+
"success": result.is_valid,
|
586
|
+
"status": result.status.value,
|
587
|
+
}
|
588
|
+
self.logger.info(f"Readonly permissions: {result.is_valid}")
|
589
|
+
except Exception as e:
|
590
|
+
results["readonly_permissions"] = {"error": str(e)}
|
591
|
+
|
592
|
+
# 4. Denied permissions
|
593
|
+
try:
|
594
|
+
result = self.security_manager.check_permissions(["readonly"], ["delete"])
|
595
|
+
results["denied_permissions"] = {
|
596
|
+
"success": result.is_valid,
|
597
|
+
"status": result.status.value,
|
598
|
+
"error_message": result.error_message,
|
599
|
+
}
|
600
|
+
self.logger.info(f"Denied permissions: {result.is_valid}")
|
601
|
+
except Exception as e:
|
602
|
+
results["denied_permissions"] = {"error": str(e)}
|
603
|
+
|
604
|
+
return results
|
605
|
+
|
606
|
+
def demonstrate_rate_limiting(self) -> Dict[str, Any]:
|
607
|
+
"""
|
608
|
+
Demonstrate rate limiting capabilities.
|
609
|
+
|
610
|
+
Returns:
|
611
|
+
Dict with rate limiting test results
|
612
|
+
"""
|
613
|
+
self.logger.info("Demonstrating rate limiting capabilities...")
|
614
|
+
|
615
|
+
results = {"rate_limit_checks": [], "rate_limit_exceeded": False}
|
616
|
+
|
617
|
+
identifier = "test_user_123"
|
400
618
|
|
401
|
-
# Example usage and testing
|
402
|
-
class FastAPIExampleTest:
|
403
|
-
"""Test class for FastAPI example functionality."""
|
404
|
-
|
405
|
-
@staticmethod
|
406
|
-
def test_authentication():
|
407
|
-
"""Test authentication functionality."""
|
408
|
-
example = FastAPIExample()
|
409
|
-
|
410
|
-
# Test API key authentication
|
411
|
-
auth_result = example.security_manager.auth_manager.authenticate_api_key("admin_key_123")
|
412
|
-
assert auth_result.is_valid
|
413
|
-
assert auth_result.username == "admin"
|
414
|
-
assert "admin" in auth_result.roles
|
415
|
-
|
416
|
-
print("✅ API Key authentication test passed")
|
417
|
-
|
418
|
-
@staticmethod
|
419
|
-
def test_rate_limiting():
|
420
|
-
"""Test rate limiting functionality."""
|
421
|
-
example = FastAPIExample()
|
422
|
-
|
423
619
|
# Test rate limiting
|
424
|
-
identifier = "test_user"
|
425
620
|
for i in range(5):
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
621
|
+
try:
|
622
|
+
allowed = self.security_manager.check_rate_limit(identifier)
|
623
|
+
results["rate_limit_checks"].append(
|
624
|
+
{"request": i + 1, "allowed": allowed}
|
625
|
+
)
|
626
|
+
self.logger.info(
|
627
|
+
f"Rate limit check {i+1}: {'Allowed' if allowed else 'Blocked'}"
|
628
|
+
)
|
629
|
+
|
630
|
+
if not allowed:
|
631
|
+
results["rate_limit_exceeded"] = True
|
632
|
+
break
|
633
|
+
|
634
|
+
except Exception as e:
|
635
|
+
results["rate_limit_checks"].append({"request": i + 1, "error": str(e)})
|
636
|
+
|
637
|
+
return results
|
638
|
+
|
639
|
+
def demonstrate_security_validation(self) -> Dict[str, Any]:
|
640
|
+
"""
|
641
|
+
Demonstrate security validation capabilities.
|
642
|
+
|
643
|
+
Returns:
|
644
|
+
Dict with validation test results
|
645
|
+
"""
|
646
|
+
self.logger.info("Demonstrating security validation capabilities...")
|
647
|
+
|
648
|
+
results = {"request_validation": {}, "configuration_validation": {}}
|
649
|
+
|
650
|
+
# 1. Request validation
|
651
|
+
try:
|
652
|
+
request_data = {
|
653
|
+
"api_key": self.test_api_key,
|
654
|
+
"required_permissions": ["read", "write"],
|
655
|
+
"client_ip": "192.168.1.100",
|
656
|
+
}
|
657
|
+
|
658
|
+
result = self.security_manager.validate_request(request_data)
|
659
|
+
results["request_validation"] = {
|
660
|
+
"success": result.is_valid,
|
661
|
+
"status": result.status.value,
|
662
|
+
}
|
663
|
+
self.logger.info(f"Request validation: {result.is_valid}")
|
664
|
+
except Exception as e:
|
665
|
+
results["request_validation"] = {"error": str(e)}
|
666
|
+
|
667
|
+
# 2. Configuration validation
|
668
|
+
try:
|
669
|
+
result = self.security_manager.validate_configuration()
|
670
|
+
results["configuration_validation"] = {
|
671
|
+
"success": result.is_valid,
|
672
|
+
"status": result.status.value,
|
673
|
+
}
|
674
|
+
self.logger.info(f"Configuration validation: {result.is_valid}")
|
675
|
+
except Exception as e:
|
676
|
+
results["configuration_validation"] = {"error": str(e)}
|
677
|
+
|
678
|
+
return results
|
679
|
+
|
680
|
+
def demonstrate_security_monitoring(self) -> Dict[str, Any]:
|
681
|
+
"""
|
682
|
+
Demonstrate security monitoring capabilities.
|
683
|
+
|
684
|
+
Returns:
|
685
|
+
Dict with monitoring test results
|
686
|
+
"""
|
687
|
+
self.logger.info("Demonstrating security monitoring capabilities...")
|
688
|
+
|
689
|
+
results = {"security_status": {}, "security_metrics": {}, "security_audit": {}}
|
690
|
+
|
691
|
+
# 1. Security status
|
692
|
+
try:
|
693
|
+
status = self.security_manager.get_security_status()
|
694
|
+
results["security_status"] = {
|
695
|
+
"status": status.status.value,
|
696
|
+
"message": status.message,
|
697
|
+
"version": status.version,
|
698
|
+
"metadata": status.metadata,
|
699
|
+
}
|
700
|
+
self.logger.info("Security status retrieved successfully")
|
701
|
+
except Exception as e:
|
702
|
+
results["security_status"] = {"error": str(e)}
|
703
|
+
|
704
|
+
# 2. Security metrics
|
705
|
+
try:
|
706
|
+
metrics = self.security_manager.get_security_metrics()
|
707
|
+
results["security_metrics"] = {
|
708
|
+
"authentication_attempts": metrics.get("authentication_attempts", 0),
|
709
|
+
"security_events": metrics.get("security_events", 0),
|
710
|
+
"uptime_seconds": metrics.get("uptime_seconds", 0),
|
711
|
+
}
|
712
|
+
self.logger.info("Security metrics retrieved successfully")
|
713
|
+
except Exception as e:
|
714
|
+
results["security_metrics"] = {"error": str(e)}
|
715
|
+
|
716
|
+
# 3. Security audit
|
717
|
+
try:
|
718
|
+
audit = self.security_manager.perform_security_audit()
|
719
|
+
results["security_audit"] = {
|
720
|
+
"authentication": audit.get("authentication", {}),
|
721
|
+
"authorization": audit.get("authorization", {}),
|
722
|
+
"rate_limiting": audit.get("rate_limiting", {}),
|
723
|
+
"ssl": audit.get("ssl", {}),
|
724
|
+
}
|
725
|
+
self.logger.info("Security audit completed successfully")
|
726
|
+
except Exception as e:
|
727
|
+
results["security_audit"] = {"error": str(e)}
|
728
|
+
|
729
|
+
return results
|
730
|
+
|
731
|
+
def run_comprehensive_demo(self) -> Dict[str, Any]:
|
732
|
+
"""
|
733
|
+
Run comprehensive demonstration of ALL framework capabilities.
|
734
|
+
|
735
|
+
Returns:
|
736
|
+
Dict with all demonstration results
|
737
|
+
"""
|
738
|
+
self.logger.info("Starting comprehensive security framework demonstration...")
|
739
|
+
|
740
|
+
demo_results = {
|
741
|
+
"timestamp": datetime.now(timezone.utc).isoformat(),
|
742
|
+
"framework": "FastAPI",
|
743
|
+
"version": "1.0.0",
|
744
|
+
"authentication": self.demonstrate_authentication(),
|
745
|
+
"authorization": self.demonstrate_authorization(),
|
746
|
+
"rate_limiting": self.demonstrate_rate_limiting(),
|
747
|
+
"security_validation": self.demonstrate_security_validation(),
|
748
|
+
"security_monitoring": self.demonstrate_security_monitoring(),
|
749
|
+
}
|
750
|
+
|
751
|
+
self.logger.info("Comprehensive demonstration completed successfully")
|
752
|
+
return demo_results
|
753
|
+
|
754
|
+
|
755
|
+
class FastAPIExampleTest:
|
756
|
+
"""Test class for FastAPI example."""
|
757
|
+
|
758
|
+
def test_authentication(self):
|
759
|
+
"""Test authentication capabilities."""
|
760
|
+
example = FastAPISecurityExample()
|
761
|
+
results = example.demonstrate_authentication()
|
762
|
+
|
763
|
+
# Verify API key authentication works
|
764
|
+
assert results["api_key_auth"]["success"] == True
|
765
|
+
assert results["api_key_auth"]["username"] == "admin"
|
766
|
+
assert "admin" in results["api_key_auth"]["roles"]
|
767
|
+
|
768
|
+
# Verify JWT authentication works
|
769
|
+
assert results["jwt_auth"]["success"] == True
|
770
|
+
assert results["jwt_auth"]["username"] == "test_user"
|
771
|
+
|
772
|
+
# Verify failed authentication is handled
|
773
|
+
assert results["failed_auth"]["success"] == False
|
774
|
+
|
775
|
+
print("✅ Authentication tests passed")
|
776
|
+
|
777
|
+
def test_authorization(self):
|
778
|
+
"""Test authorization capabilities."""
|
779
|
+
example = FastAPISecurityExample()
|
780
|
+
results = example.demonstrate_authorization()
|
781
|
+
|
782
|
+
# Verify admin permissions work
|
783
|
+
assert results["admin_permissions"]["success"] == True
|
784
|
+
|
785
|
+
# Verify user permissions work
|
786
|
+
assert results["user_permissions"]["success"] == True
|
787
|
+
|
788
|
+
# Verify readonly permissions work
|
789
|
+
assert results["readonly_permissions"]["success"] == True
|
790
|
+
|
791
|
+
print("✅ Authorization tests passed")
|
792
|
+
|
793
|
+
def test_rate_limiting(self):
|
794
|
+
"""Test rate limiting capabilities."""
|
795
|
+
example = FastAPISecurityExample()
|
796
|
+
results = example.demonstrate_rate_limiting()
|
797
|
+
|
798
|
+
# Verify rate limiting checks work
|
799
|
+
assert len(results["rate_limit_checks"]) > 0
|
800
|
+
assert results["rate_limit_checks"][0]["allowed"] == True
|
801
|
+
|
802
|
+
print("✅ Rate limiting tests passed")
|
803
|
+
|
804
|
+
def test_security_validation(self):
|
805
|
+
"""Test security validation capabilities."""
|
806
|
+
example = FastAPISecurityExample()
|
807
|
+
results = example.demonstrate_security_validation()
|
808
|
+
|
809
|
+
# Verify request validation works
|
810
|
+
assert results["request_validation"]["success"] == True
|
811
|
+
|
812
|
+
# Verify configuration validation works
|
813
|
+
assert results["configuration_validation"]["success"] == True
|
814
|
+
|
815
|
+
print("✅ Security validation tests passed")
|
816
|
+
|
817
|
+
def test_security_monitoring(self):
|
818
|
+
"""Test security monitoring capabilities."""
|
819
|
+
example = FastAPISecurityExample()
|
820
|
+
results = example.demonstrate_security_monitoring()
|
821
|
+
|
822
|
+
# Verify security status works
|
823
|
+
assert "status" in results["security_status"]
|
824
|
+
assert "message" in results["security_status"]
|
825
|
+
|
826
|
+
# Verify security metrics work
|
827
|
+
assert "authentication_attempts" in results["security_metrics"]
|
828
|
+
|
829
|
+
# Verify security audit works
|
830
|
+
assert "authentication" in results["security_audit"]
|
831
|
+
|
832
|
+
print("✅ Security monitoring tests passed")
|
833
|
+
|
834
|
+
|
835
|
+
def main():
|
836
|
+
"""Main function to run the FastAPI example."""
|
837
|
+
print("\n🚀 MCP Security Framework - FastAPI Example")
|
838
|
+
print("=" * 60)
|
839
|
+
|
840
|
+
# Create example instance
|
841
|
+
example = FastAPISecurityExample()
|
842
|
+
|
843
|
+
# Run comprehensive demonstration
|
844
|
+
results = example.run_comprehensive_demo()
|
845
|
+
|
846
|
+
# Print results
|
847
|
+
print("\n📊 COMPREHENSIVE DEMONSTRATION RESULTS")
|
848
|
+
print("=" * 60)
|
849
|
+
print(f"Framework: {results['framework']}")
|
850
|
+
print(f"Version: {results['version']}")
|
851
|
+
print(f"Timestamp: {results['timestamp']}")
|
852
|
+
|
853
|
+
print("\n🔐 AUTHENTICATION RESULTS:")
|
854
|
+
print(
|
855
|
+
f" API Key: {'✅' if results['authentication']['api_key_auth']['success'] else '❌'}"
|
856
|
+
)
|
857
|
+
print(
|
858
|
+
f" JWT: {'✅' if results['authentication']['jwt_auth']['success'] else '❌'}"
|
859
|
+
)
|
860
|
+
print(
|
861
|
+
f" Certificate: {'✅' if results['authentication']['certificate_auth']['success'] else '❌'}"
|
862
|
+
)
|
863
|
+
|
864
|
+
print("\n🔑 AUTHORIZATION RESULTS:")
|
865
|
+
print(
|
866
|
+
f" Admin Permissions: {'✅' if results['authorization']['admin_permissions']['success'] else '❌'}"
|
867
|
+
)
|
868
|
+
print(
|
869
|
+
f" User Permissions: {'✅' if results['authorization']['user_permissions']['success'] else '❌'}"
|
870
|
+
)
|
871
|
+
print(
|
872
|
+
f" Readonly Permissions: {'✅' if results['authorization']['readonly_permissions']['success'] else '❌'}"
|
873
|
+
)
|
874
|
+
|
875
|
+
print("\n⚡ RATE LIMITING RESULTS:")
|
876
|
+
print(f" Rate Limit Checks: {len(results['rate_limiting']['rate_limit_checks'])}")
|
877
|
+
print(
|
878
|
+
f" Rate Limit Exceeded: {'❌' if results['rate_limiting']['rate_limit_exceeded'] else '✅'}"
|
879
|
+
)
|
880
|
+
|
881
|
+
print("\n🔒 SECURITY VALIDATION RESULTS:")
|
882
|
+
print(
|
883
|
+
f" Request Validation: {'✅' if results['security_validation']['request_validation']['success'] else '❌'}"
|
884
|
+
)
|
885
|
+
print(
|
886
|
+
f" Configuration Validation: {'✅' if results['security_validation']['configuration_validation']['success'] else '❌'}"
|
887
|
+
)
|
888
|
+
|
889
|
+
print("\n📊 SECURITY MONITORING RESULTS:")
|
890
|
+
print(
|
891
|
+
f" Security Status: {'✅' if 'status' in results['security_monitoring']['security_status'] else '❌'}"
|
892
|
+
)
|
893
|
+
print(
|
894
|
+
f" Security Metrics: {'✅' if 'authentication_attempts' in results['security_monitoring']['security_metrics'] else '❌'}"
|
895
|
+
)
|
896
|
+
print(
|
897
|
+
f" Security Audit: {'✅' if 'authentication' in results['security_monitoring']['security_audit'] else '❌'}"
|
898
|
+
)
|
899
|
+
|
900
|
+
print("\n🎉 ALL FRAMEWORK CAPABILITIES DEMONSTRATED SUCCESSFULLY!")
|
901
|
+
print("=" * 60)
|
902
|
+
|
903
|
+
print("\n🌐 FastAPI Application Ready!")
|
904
|
+
print("Run with: uvicorn fastapi_example:example.app --reload")
|
905
|
+
print("Available endpoints:")
|
906
|
+
print(" - GET /health - Health check")
|
907
|
+
print(" - GET /metrics - Security metrics")
|
908
|
+
print(" - GET /security/status - Security status")
|
909
|
+
print(" - GET /security/audit - Security audit")
|
910
|
+
print(" - GET /admin/users - Admin users (requires admin)")
|
911
|
+
print(" - GET /api/v1/users/me - Current user")
|
912
|
+
print(" - GET /api/v1/data - Data (requires read:data)")
|
913
|
+
print(" - POST /auth/test-api-key - Test API key auth")
|
914
|
+
print(" - POST /auth/test-jwt - Test JWT auth")
|
915
|
+
print(" - GET /rate-limit-test - Test rate limiting")
|
460
916
|
|
461
917
|
|
462
918
|
if __name__ == "__main__":
|
463
919
|
# Run tests
|
464
920
|
print("Running FastAPI Example Tests...")
|
465
|
-
FastAPIExampleTest
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
921
|
+
test = FastAPIExampleTest()
|
922
|
+
test.test_authentication()
|
923
|
+
test.test_authorization()
|
924
|
+
test.test_rate_limiting()
|
925
|
+
test.test_security_validation()
|
926
|
+
test.test_security_monitoring()
|
927
|
+
|
928
|
+
print("\nExample Usage:")
|
929
|
+
main()
|
930
|
+
|
931
|
+
# Start server in background thread for testing
|
932
|
+
print("\nStarting FastAPI Server in background...")
|
933
|
+
example = FastAPISecurityExample()
|
934
|
+
|
935
|
+
import asyncio
|
936
|
+
import threading
|
937
|
+
import time
|
938
|
+
|
939
|
+
import requests
|
940
|
+
import uvicorn
|
941
|
+
|
942
|
+
# Start server in background thread
|
943
|
+
def run_server():
|
944
|
+
uvicorn.run(example.app, host="0.0.0.0", port=8000, log_level="error")
|
945
|
+
|
946
|
+
server_thread = threading.Thread(target=run_server, daemon=True)
|
947
|
+
server_thread.start()
|
948
|
+
|
949
|
+
# Wait for server to start
|
950
|
+
time.sleep(5)
|
951
|
+
|
952
|
+
try:
|
953
|
+
# Test server endpoints
|
954
|
+
print("Testing FastAPI server endpoints...")
|
955
|
+
|
956
|
+
# Test health endpoint
|
957
|
+
response = requests.get("http://localhost:8000/health", timeout=5)
|
958
|
+
print(f"Health endpoint: {response.status_code}")
|
959
|
+
|
960
|
+
# Test metrics endpoint
|
961
|
+
response = requests.get("http://localhost:8000/metrics", timeout=5)
|
962
|
+
print(f"Metrics endpoint: {response.status_code}")
|
963
|
+
|
964
|
+
# Test protected endpoint with API key
|
965
|
+
headers = {"X-API-Key": "admin_key_123"}
|
966
|
+
response = requests.get(
|
967
|
+
"http://localhost:8000/api/v1/users/me", headers=headers, timeout=5
|
968
|
+
)
|
969
|
+
print(f"Protected endpoint: {response.status_code}")
|
970
|
+
|
971
|
+
print("✅ FastAPI server testing completed successfully")
|
972
|
+
|
973
|
+
except requests.exceptions.RequestException as e:
|
974
|
+
print(f"⚠️ FastAPI server testing failed: {e}")
|
975
|
+
|
976
|
+
print("FastAPI example completed")
|