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