mcp-security-framework 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mcp_security_framework/__init__.py +96 -0
- mcp_security_framework/cli/__init__.py +18 -0
- mcp_security_framework/cli/cert_cli.py +511 -0
- mcp_security_framework/cli/security_cli.py +791 -0
- mcp_security_framework/constants.py +209 -0
- mcp_security_framework/core/__init__.py +61 -0
- mcp_security_framework/core/auth_manager.py +1011 -0
- mcp_security_framework/core/cert_manager.py +1663 -0
- mcp_security_framework/core/permission_manager.py +735 -0
- mcp_security_framework/core/rate_limiter.py +602 -0
- mcp_security_framework/core/security_manager.py +943 -0
- mcp_security_framework/core/ssl_manager.py +735 -0
- mcp_security_framework/examples/__init__.py +75 -0
- mcp_security_framework/examples/django_example.py +615 -0
- mcp_security_framework/examples/fastapi_example.py +472 -0
- mcp_security_framework/examples/flask_example.py +506 -0
- mcp_security_framework/examples/gateway_example.py +803 -0
- mcp_security_framework/examples/microservice_example.py +690 -0
- mcp_security_framework/examples/standalone_example.py +576 -0
- mcp_security_framework/middleware/__init__.py +250 -0
- mcp_security_framework/middleware/auth_middleware.py +292 -0
- mcp_security_framework/middleware/fastapi_auth_middleware.py +447 -0
- mcp_security_framework/middleware/fastapi_middleware.py +757 -0
- mcp_security_framework/middleware/flask_auth_middleware.py +465 -0
- mcp_security_framework/middleware/flask_middleware.py +591 -0
- mcp_security_framework/middleware/mtls_middleware.py +439 -0
- mcp_security_framework/middleware/rate_limit_middleware.py +403 -0
- mcp_security_framework/middleware/security_middleware.py +507 -0
- mcp_security_framework/schemas/__init__.py +109 -0
- mcp_security_framework/schemas/config.py +694 -0
- mcp_security_framework/schemas/models.py +709 -0
- mcp_security_framework/schemas/responses.py +686 -0
- mcp_security_framework/tests/__init__.py +0 -0
- mcp_security_framework/utils/__init__.py +121 -0
- mcp_security_framework/utils/cert_utils.py +525 -0
- mcp_security_framework/utils/crypto_utils.py +475 -0
- mcp_security_framework/utils/validation_utils.py +571 -0
- mcp_security_framework-0.1.0.dist-info/METADATA +411 -0
- mcp_security_framework-0.1.0.dist-info/RECORD +76 -0
- mcp_security_framework-0.1.0.dist-info/WHEEL +5 -0
- mcp_security_framework-0.1.0.dist-info/entry_points.txt +3 -0
- mcp_security_framework-0.1.0.dist-info/top_level.txt +2 -0
- tests/__init__.py +0 -0
- tests/test_cli/__init__.py +0 -0
- tests/test_cli/test_cert_cli.py +379 -0
- tests/test_cli/test_security_cli.py +657 -0
- tests/test_core/__init__.py +0 -0
- tests/test_core/test_auth_manager.py +582 -0
- tests/test_core/test_cert_manager.py +795 -0
- tests/test_core/test_permission_manager.py +395 -0
- tests/test_core/test_rate_limiter.py +626 -0
- tests/test_core/test_security_manager.py +841 -0
- tests/test_core/test_ssl_manager.py +532 -0
- tests/test_examples/__init__.py +8 -0
- tests/test_examples/test_fastapi_example.py +264 -0
- tests/test_examples/test_flask_example.py +238 -0
- tests/test_examples/test_standalone_example.py +292 -0
- tests/test_integration/__init__.py +0 -0
- tests/test_integration/test_auth_flow.py +502 -0
- tests/test_integration/test_certificate_flow.py +527 -0
- tests/test_integration/test_fastapi_integration.py +341 -0
- tests/test_integration/test_flask_integration.py +398 -0
- tests/test_integration/test_standalone_integration.py +493 -0
- tests/test_middleware/__init__.py +0 -0
- tests/test_middleware/test_fastapi_middleware.py +523 -0
- tests/test_middleware/test_flask_middleware.py +582 -0
- tests/test_middleware/test_security_middleware.py +493 -0
- tests/test_schemas/__init__.py +0 -0
- tests/test_schemas/test_config.py +811 -0
- tests/test_schemas/test_models.py +879 -0
- tests/test_schemas/test_responses.py +1054 -0
- tests/test_schemas/test_serialization.py +493 -0
- tests/test_utils/__init__.py +0 -0
- tests/test_utils/test_cert_utils.py +510 -0
- tests/test_utils/test_crypto_utils.py +603 -0
- tests/test_utils/test_validation_utils.py +477 -0
@@ -0,0 +1,686 @@
|
|
1
|
+
"""
|
2
|
+
Response Models Module
|
3
|
+
|
4
|
+
This module provides comprehensive response models for all API responses,
|
5
|
+
error handling, and validation responses used throughout the MCP Security
|
6
|
+
Framework. It includes standardized response formats with proper HTTP
|
7
|
+
status codes and error handling.
|
8
|
+
|
9
|
+
Key Features:
|
10
|
+
- Standardized API response formats
|
11
|
+
- Comprehensive error handling models
|
12
|
+
- HTTP status code integration
|
13
|
+
- Validation response models
|
14
|
+
- Success response models
|
15
|
+
- Consistent error message formatting
|
16
|
+
|
17
|
+
Classes:
|
18
|
+
SecurityResponse: Base response model
|
19
|
+
ErrorResponse: Error response model
|
20
|
+
SuccessResponse: Success response model
|
21
|
+
ValidationResponse: Validation response model
|
22
|
+
AuthResponse: Authentication response model
|
23
|
+
CertificateResponse: Certificate response model
|
24
|
+
PermissionResponse: Permission response model
|
25
|
+
RateLimitResponse: Rate limiting response model
|
26
|
+
|
27
|
+
Author: MCP Security Team
|
28
|
+
Version: 1.0.0
|
29
|
+
License: MIT
|
30
|
+
"""
|
31
|
+
|
32
|
+
from datetime import datetime
|
33
|
+
from enum import Enum
|
34
|
+
from typing import Any, Dict, Generic, List, Optional, TypeVar
|
35
|
+
|
36
|
+
from pydantic import BaseModel, Field, field_validator, model_validator
|
37
|
+
|
38
|
+
from .models import AuthResult, CertificateInfo, RateLimitStatus, ValidationResult
|
39
|
+
|
40
|
+
|
41
|
+
class ResponseStatus(str, Enum):
|
42
|
+
"""Response status enumeration."""
|
43
|
+
|
44
|
+
SUCCESS = "success"
|
45
|
+
ERROR = "error"
|
46
|
+
WARNING = "warning"
|
47
|
+
INFO = "info"
|
48
|
+
|
49
|
+
|
50
|
+
class SecurityStatus(str, Enum):
|
51
|
+
"""Security status enumeration."""
|
52
|
+
|
53
|
+
HEALTHY = "healthy"
|
54
|
+
WARNING = "warning"
|
55
|
+
ERROR = "error"
|
56
|
+
DEGRADED = "degraded"
|
57
|
+
UNKNOWN = "unknown"
|
58
|
+
|
59
|
+
|
60
|
+
class ErrorCode(str, Enum):
|
61
|
+
"""Error code enumeration."""
|
62
|
+
|
63
|
+
# Authentication errors
|
64
|
+
AUTHENTICATION_FAILED = "AUTHENTICATION_FAILED"
|
65
|
+
INVALID_CREDENTIALS = "INVALID_CREDENTIALS"
|
66
|
+
TOKEN_EXPIRED = "TOKEN_EXPIRED"
|
67
|
+
INVALID_TOKEN = "INVALID_TOKEN"
|
68
|
+
INSUFFICIENT_PERMISSIONS = "INSUFFICIENT_PERMISSIONS"
|
69
|
+
|
70
|
+
# Certificate errors
|
71
|
+
CERTIFICATE_INVALID = "CERTIFICATE_INVALID"
|
72
|
+
CERTIFICATE_EXPIRED = "CERTIFICATE_EXPIRED"
|
73
|
+
CERTIFICATE_REVOKED = "CERTIFICATE_REVOKED"
|
74
|
+
CERTIFICATE_CHAIN_INVALID = "CERTIFICATE_CHAIN_INVALID"
|
75
|
+
CERTIFICATE_NOT_FOUND = "CERTIFICATE_NOT_FOUND"
|
76
|
+
|
77
|
+
# Rate limiting errors
|
78
|
+
RATE_LIMIT_EXCEEDED = "RATE_LIMIT_EXCEEDED"
|
79
|
+
TOO_MANY_REQUESTS = "TOO_MANY_REQUESTS"
|
80
|
+
|
81
|
+
# Configuration errors
|
82
|
+
CONFIGURATION_ERROR = "CONFIGURATION_ERROR"
|
83
|
+
INVALID_CONFIGURATION = "INVALID_CONFIGURATION"
|
84
|
+
MISSING_CONFIGURATION = "MISSING_CONFIGURATION"
|
85
|
+
|
86
|
+
# Validation errors
|
87
|
+
VALIDATION_ERROR = "VALIDATION_ERROR"
|
88
|
+
INVALID_INPUT = "INVALID_INPUT"
|
89
|
+
MISSING_REQUIRED_FIELD = "MISSING_REQUIRED_FIELD"
|
90
|
+
|
91
|
+
# System errors
|
92
|
+
INTERNAL_ERROR = "INTERNAL_ERROR"
|
93
|
+
SERVICE_UNAVAILABLE = "SERVICE_UNAVAILABLE"
|
94
|
+
DATABASE_ERROR = "DATABASE_ERROR"
|
95
|
+
NETWORK_ERROR = "NETWORK_ERROR"
|
96
|
+
|
97
|
+
# Security errors
|
98
|
+
SECURITY_VIOLATION = "SECURITY_VIOLATION"
|
99
|
+
ACCESS_DENIED = "ACCESS_DENIED"
|
100
|
+
FORBIDDEN = "FORBIDDEN"
|
101
|
+
UNAUTHORIZED = "UNAUTHORIZED"
|
102
|
+
|
103
|
+
|
104
|
+
# Generic type for response data
|
105
|
+
T = TypeVar("T")
|
106
|
+
|
107
|
+
|
108
|
+
class SecurityResponse(BaseModel, Generic[T]):
|
109
|
+
"""
|
110
|
+
Base Security Response Model
|
111
|
+
|
112
|
+
This is the base response model that provides a standardized
|
113
|
+
format for all API responses in the MCP Security Framework.
|
114
|
+
|
115
|
+
Attributes:
|
116
|
+
status: Response status (success, error, warning, info)
|
117
|
+
message: Human-readable response message
|
118
|
+
data: Response data payload
|
119
|
+
timestamp: Response timestamp
|
120
|
+
request_id: Unique request identifier
|
121
|
+
version: API version
|
122
|
+
metadata: Additional response metadata
|
123
|
+
"""
|
124
|
+
|
125
|
+
status: ResponseStatus = Field(..., description="Response status")
|
126
|
+
message: str = Field(..., description="Human-readable response message")
|
127
|
+
data: Optional[T] = Field(default=None, description="Response data payload")
|
128
|
+
timestamp: datetime = Field(
|
129
|
+
default_factory=datetime.utcnow, description="Response timestamp"
|
130
|
+
)
|
131
|
+
request_id: Optional[str] = Field(
|
132
|
+
default=None, description="Unique request identifier"
|
133
|
+
)
|
134
|
+
version: str = Field(default="1.0.0", description="API version")
|
135
|
+
metadata: Dict[str, Any] = Field(
|
136
|
+
default_factory=dict, description="Additional response metadata"
|
137
|
+
)
|
138
|
+
|
139
|
+
@field_validator("message")
|
140
|
+
@classmethod
|
141
|
+
def validate_message(cls, v):
|
142
|
+
"""Validate response message."""
|
143
|
+
if len(v.strip()) == 0:
|
144
|
+
raise ValueError("Response message cannot be empty")
|
145
|
+
return v.strip()
|
146
|
+
|
147
|
+
@property
|
148
|
+
def is_success(self) -> bool:
|
149
|
+
"""Check if response indicates success."""
|
150
|
+
return self.status == ResponseStatus.SUCCESS
|
151
|
+
|
152
|
+
@property
|
153
|
+
def is_error(self) -> bool:
|
154
|
+
"""Check if response indicates error."""
|
155
|
+
return self.status == ResponseStatus.ERROR
|
156
|
+
|
157
|
+
|
158
|
+
class ErrorResponse(SecurityResponse[None]):
|
159
|
+
"""
|
160
|
+
Error Response Model
|
161
|
+
|
162
|
+
This model represents error responses with detailed error
|
163
|
+
information including error codes, HTTP status codes, and
|
164
|
+
additional error context.
|
165
|
+
|
166
|
+
Attributes:
|
167
|
+
error_code: Specific error code
|
168
|
+
http_status_code: HTTP status code
|
169
|
+
details: Detailed error information
|
170
|
+
field_errors: Field-specific validation errors
|
171
|
+
stack_trace: Stack trace for debugging (optional)
|
172
|
+
retry_after: Retry after time in seconds
|
173
|
+
error_type: Type of error
|
174
|
+
"""
|
175
|
+
|
176
|
+
error_code: ErrorCode = Field(..., description="Specific error code")
|
177
|
+
http_status_code: int = Field(..., ge=400, le=599, description="HTTP status code")
|
178
|
+
details: Optional[str] = Field(
|
179
|
+
default=None, description="Detailed error information"
|
180
|
+
)
|
181
|
+
field_errors: Dict[str, List[str]] = Field(
|
182
|
+
default_factory=dict, description="Field-specific validation errors"
|
183
|
+
)
|
184
|
+
stack_trace: Optional[str] = Field(
|
185
|
+
default=None, description="Stack trace for debugging"
|
186
|
+
)
|
187
|
+
retry_after: Optional[int] = Field(
|
188
|
+
default=None, ge=0, description="Retry after time in seconds"
|
189
|
+
)
|
190
|
+
error_type: str = Field(default="SecurityError", description="Type of error")
|
191
|
+
|
192
|
+
@field_validator("status")
|
193
|
+
@classmethod
|
194
|
+
def validate_error_status(cls, v):
|
195
|
+
"""Validate that error responses have error status."""
|
196
|
+
if v != ResponseStatus.ERROR:
|
197
|
+
raise ValueError("Error responses must have ERROR status")
|
198
|
+
return v
|
199
|
+
|
200
|
+
@field_validator("http_status_code")
|
201
|
+
@classmethod
|
202
|
+
def validate_http_status_code(cls, v):
|
203
|
+
"""Validate HTTP status code for errors."""
|
204
|
+
if v < 400 or v > 599:
|
205
|
+
raise ValueError(
|
206
|
+
"Error responses must have HTTP status code between 400 and 599"
|
207
|
+
)
|
208
|
+
return v
|
209
|
+
|
210
|
+
@model_validator(mode="after")
|
211
|
+
def validate_error_response(self):
|
212
|
+
"""Validate error response consistency."""
|
213
|
+
if self.status != ResponseStatus.ERROR:
|
214
|
+
raise ValueError("Error responses must have ERROR status")
|
215
|
+
|
216
|
+
# Validate error code and HTTP status code consistency
|
217
|
+
if (
|
218
|
+
self.error_code == ErrorCode.AUTHENTICATION_FAILED
|
219
|
+
and self.http_status_code != 401
|
220
|
+
):
|
221
|
+
raise ValueError("Authentication failed should have HTTP status 401")
|
222
|
+
|
223
|
+
if (
|
224
|
+
self.error_code == ErrorCode.INSUFFICIENT_PERMISSIONS
|
225
|
+
and self.http_status_code != 403
|
226
|
+
):
|
227
|
+
raise ValueError("Insufficient permissions should have HTTP status 403")
|
228
|
+
|
229
|
+
if (
|
230
|
+
self.error_code == ErrorCode.RATE_LIMIT_EXCEEDED
|
231
|
+
and self.http_status_code != 429
|
232
|
+
):
|
233
|
+
raise ValueError("Rate limit exceeded should have HTTP status 429")
|
234
|
+
|
235
|
+
return self
|
236
|
+
|
237
|
+
@classmethod
|
238
|
+
def create_authentication_error(
|
239
|
+
cls, message: str, details: Optional[str] = None
|
240
|
+
) -> "ErrorResponse":
|
241
|
+
"""Create authentication error response."""
|
242
|
+
return cls(
|
243
|
+
status=ResponseStatus.ERROR,
|
244
|
+
message=message,
|
245
|
+
error_code=ErrorCode.AUTHENTICATION_FAILED,
|
246
|
+
http_status_code=401,
|
247
|
+
details=details,
|
248
|
+
error_type="AuthenticationError",
|
249
|
+
)
|
250
|
+
|
251
|
+
@classmethod
|
252
|
+
def create_permission_error(
|
253
|
+
cls, message: str, details: Optional[str] = None
|
254
|
+
) -> "ErrorResponse":
|
255
|
+
"""Create permission error response."""
|
256
|
+
return cls(
|
257
|
+
status=ResponseStatus.ERROR,
|
258
|
+
message=message,
|
259
|
+
error_code=ErrorCode.INSUFFICIENT_PERMISSIONS,
|
260
|
+
http_status_code=403,
|
261
|
+
details=details,
|
262
|
+
error_type="PermissionError",
|
263
|
+
)
|
264
|
+
|
265
|
+
@classmethod
|
266
|
+
def create_rate_limit_error(
|
267
|
+
cls, message: str, retry_after: Optional[int] = None
|
268
|
+
) -> "ErrorResponse":
|
269
|
+
"""Create rate limit error response."""
|
270
|
+
return cls(
|
271
|
+
status=ResponseStatus.ERROR,
|
272
|
+
message=message,
|
273
|
+
error_code=ErrorCode.RATE_LIMIT_EXCEEDED,
|
274
|
+
http_status_code=429,
|
275
|
+
retry_after=retry_after,
|
276
|
+
error_type="RateLimitError",
|
277
|
+
)
|
278
|
+
|
279
|
+
@classmethod
|
280
|
+
def create_validation_error(
|
281
|
+
cls, message: str, field_errors: Dict[str, List[str]]
|
282
|
+
) -> "ErrorResponse":
|
283
|
+
"""Create validation error response."""
|
284
|
+
return cls(
|
285
|
+
status=ResponseStatus.ERROR,
|
286
|
+
message=message,
|
287
|
+
error_code=ErrorCode.VALIDATION_ERROR,
|
288
|
+
http_status_code=400,
|
289
|
+
field_errors=field_errors,
|
290
|
+
error_type="ValidationError",
|
291
|
+
)
|
292
|
+
|
293
|
+
|
294
|
+
class SuccessResponse(SecurityResponse[T]):
|
295
|
+
"""
|
296
|
+
Success Response Model
|
297
|
+
|
298
|
+
This model represents successful responses with data payload
|
299
|
+
and optional success metadata.
|
300
|
+
|
301
|
+
Attributes:
|
302
|
+
data: Response data payload
|
303
|
+
total_count: Total count for paginated responses
|
304
|
+
page: Current page number
|
305
|
+
page_size: Page size
|
306
|
+
has_more: Whether there are more pages
|
307
|
+
"""
|
308
|
+
|
309
|
+
data: T = Field(..., description="Response data payload")
|
310
|
+
total_count: Optional[int] = Field(
|
311
|
+
default=None, ge=0, description="Total count for paginated responses"
|
312
|
+
)
|
313
|
+
page: Optional[int] = Field(default=None, ge=1, description="Current page number")
|
314
|
+
page_size: Optional[int] = Field(default=None, ge=1, description="Page size")
|
315
|
+
has_more: Optional[bool] = Field(
|
316
|
+
default=None, description="Whether there are more pages"
|
317
|
+
)
|
318
|
+
|
319
|
+
@field_validator("status")
|
320
|
+
@classmethod
|
321
|
+
def validate_success_status(cls, v):
|
322
|
+
"""Validate that success responses have success status."""
|
323
|
+
if v != ResponseStatus.SUCCESS:
|
324
|
+
raise ValueError("Success responses must have SUCCESS status")
|
325
|
+
return v
|
326
|
+
|
327
|
+
@model_validator(mode="after")
|
328
|
+
def validate_success_response(self):
|
329
|
+
"""Validate success response consistency."""
|
330
|
+
if self.status != ResponseStatus.SUCCESS:
|
331
|
+
raise ValueError("Success responses must have SUCCESS status")
|
332
|
+
|
333
|
+
if self.data is None:
|
334
|
+
raise ValueError("Success responses must have data")
|
335
|
+
|
336
|
+
return self
|
337
|
+
|
338
|
+
@classmethod
|
339
|
+
def create_success(
|
340
|
+
cls, data: T, message: str = "Operation completed successfully"
|
341
|
+
) -> "SuccessResponse[T]":
|
342
|
+
"""Create success response."""
|
343
|
+
return cls(status=ResponseStatus.SUCCESS, message=message, data=data)
|
344
|
+
|
345
|
+
|
346
|
+
class ValidationResponse(SecurityResponse[ValidationResult]):
|
347
|
+
"""
|
348
|
+
Validation Response Model
|
349
|
+
|
350
|
+
This model represents validation responses with detailed
|
351
|
+
validation results and field-specific error information.
|
352
|
+
|
353
|
+
Attributes:
|
354
|
+
validation_result: Validation result object
|
355
|
+
field_errors: Field-specific validation errors
|
356
|
+
warnings: List of validation warnings
|
357
|
+
suggestions: List of improvement suggestions
|
358
|
+
"""
|
359
|
+
|
360
|
+
validation_result: ValidationResult = Field(
|
361
|
+
..., description="Validation result object"
|
362
|
+
)
|
363
|
+
field_errors: Dict[str, List[str]] = Field(
|
364
|
+
default_factory=dict, description="Field-specific validation errors"
|
365
|
+
)
|
366
|
+
warnings: List[str] = Field(
|
367
|
+
default_factory=list, description="List of validation warnings"
|
368
|
+
)
|
369
|
+
suggestions: List[str] = Field(
|
370
|
+
default_factory=list, description="List of improvement suggestions"
|
371
|
+
)
|
372
|
+
|
373
|
+
@field_validator("status")
|
374
|
+
@classmethod
|
375
|
+
def validate_validation_status(cls, v):
|
376
|
+
"""Validate validation response status."""
|
377
|
+
# This validation will be handled by model_validator
|
378
|
+
return v
|
379
|
+
|
380
|
+
@model_validator(mode="after")
|
381
|
+
def validate_validation_response(self):
|
382
|
+
"""Validate validation response consistency."""
|
383
|
+
if self.validation_result.is_valid and self.status != ResponseStatus.SUCCESS:
|
384
|
+
raise ValueError("Valid validation must have SUCCESS status")
|
385
|
+
|
386
|
+
if not self.validation_result.is_valid and self.status != ResponseStatus.ERROR:
|
387
|
+
raise ValueError("Invalid validation must have ERROR status")
|
388
|
+
|
389
|
+
return self
|
390
|
+
|
391
|
+
@classmethod
|
392
|
+
def create_validation_success(
|
393
|
+
cls, validation_result: ValidationResult
|
394
|
+
) -> "ValidationResponse":
|
395
|
+
"""Create successful validation response."""
|
396
|
+
return cls(
|
397
|
+
status=ResponseStatus.SUCCESS,
|
398
|
+
message="Validation completed successfully",
|
399
|
+
validation_result=validation_result,
|
400
|
+
)
|
401
|
+
|
402
|
+
@classmethod
|
403
|
+
def create_validation_error(
|
404
|
+
cls, validation_result: ValidationResult, field_errors: Dict[str, List[str]]
|
405
|
+
) -> "ValidationResponse":
|
406
|
+
"""Create validation error response."""
|
407
|
+
return cls(
|
408
|
+
status=ResponseStatus.ERROR,
|
409
|
+
message="Validation failed",
|
410
|
+
validation_result=validation_result,
|
411
|
+
field_errors=field_errors,
|
412
|
+
)
|
413
|
+
|
414
|
+
|
415
|
+
class AuthResponse(SecurityResponse[AuthResult]):
|
416
|
+
"""
|
417
|
+
Authentication Response Model
|
418
|
+
|
419
|
+
This model represents authentication responses with detailed
|
420
|
+
authentication results and user information.
|
421
|
+
|
422
|
+
Attributes:
|
423
|
+
auth_result: Authentication result object
|
424
|
+
user_info: Additional user information
|
425
|
+
session_info: Session information
|
426
|
+
token_info: Token information
|
427
|
+
"""
|
428
|
+
|
429
|
+
auth_result: AuthResult = Field(..., description="Authentication result object")
|
430
|
+
user_info: Dict[str, Any] = Field(
|
431
|
+
default_factory=dict, description="Additional user information"
|
432
|
+
)
|
433
|
+
session_info: Dict[str, Any] = Field(
|
434
|
+
default_factory=dict, description="Session information"
|
435
|
+
)
|
436
|
+
token_info: Dict[str, Any] = Field(
|
437
|
+
default_factory=dict, description="Token information"
|
438
|
+
)
|
439
|
+
|
440
|
+
@field_validator("status")
|
441
|
+
@classmethod
|
442
|
+
def validate_auth_status(cls, v):
|
443
|
+
"""Validate authentication response status."""
|
444
|
+
# This validation will be handled by model_validator
|
445
|
+
return v
|
446
|
+
|
447
|
+
@model_validator(mode="after")
|
448
|
+
def validate_auth_response(self):
|
449
|
+
"""Validate authentication response consistency."""
|
450
|
+
if self.auth_result.is_valid and self.status != ResponseStatus.SUCCESS:
|
451
|
+
raise ValueError("Successful authentication must have SUCCESS status")
|
452
|
+
|
453
|
+
if not self.auth_result.is_valid and self.status != ResponseStatus.ERROR:
|
454
|
+
raise ValueError("Failed authentication must have ERROR status")
|
455
|
+
|
456
|
+
return self
|
457
|
+
|
458
|
+
@classmethod
|
459
|
+
def create_auth_success(
|
460
|
+
cls, auth_result: AuthResult, user_info: Optional[Dict[str, Any]] = None
|
461
|
+
) -> "AuthResponse":
|
462
|
+
"""Create successful authentication response."""
|
463
|
+
return cls(
|
464
|
+
status=ResponseStatus.SUCCESS,
|
465
|
+
message="Authentication successful",
|
466
|
+
auth_result=auth_result,
|
467
|
+
user_info=user_info or {},
|
468
|
+
)
|
469
|
+
|
470
|
+
@classmethod
|
471
|
+
def create_auth_error(cls, auth_result: AuthResult) -> "AuthResponse":
|
472
|
+
"""Create authentication error response."""
|
473
|
+
return cls(
|
474
|
+
status=ResponseStatus.ERROR,
|
475
|
+
message=auth_result.error_message or "Authentication failed",
|
476
|
+
auth_result=auth_result,
|
477
|
+
)
|
478
|
+
|
479
|
+
|
480
|
+
class CertificateResponse(SecurityResponse[CertificateInfo]):
|
481
|
+
"""
|
482
|
+
Certificate Response Model
|
483
|
+
|
484
|
+
This model represents certificate-related responses with
|
485
|
+
detailed certificate information and validation results.
|
486
|
+
|
487
|
+
Attributes:
|
488
|
+
certificate_info: Certificate information object
|
489
|
+
validation_result: Certificate validation result
|
490
|
+
chain_info: Certificate chain information
|
491
|
+
expiry_info: Certificate expiry information
|
492
|
+
"""
|
493
|
+
|
494
|
+
certificate_info: CertificateInfo = Field(
|
495
|
+
..., description="Certificate information object"
|
496
|
+
)
|
497
|
+
validation_result: Optional[ValidationResult] = Field(
|
498
|
+
default=None, description="Certificate validation result"
|
499
|
+
)
|
500
|
+
chain_info: Dict[str, Any] = Field(
|
501
|
+
default_factory=dict, description="Certificate chain information"
|
502
|
+
)
|
503
|
+
expiry_info: Dict[str, Any] = Field(
|
504
|
+
default_factory=dict, description="Certificate expiry information"
|
505
|
+
)
|
506
|
+
|
507
|
+
@model_validator(mode="after")
|
508
|
+
def validate_certificate_response(self):
|
509
|
+
"""Validate certificate response consistency."""
|
510
|
+
if (
|
511
|
+
self.validation_result
|
512
|
+
and not self.validation_result.is_valid
|
513
|
+
and self.status == ResponseStatus.SUCCESS
|
514
|
+
):
|
515
|
+
raise ValueError(
|
516
|
+
"Invalid certificate validation cannot have SUCCESS status"
|
517
|
+
)
|
518
|
+
|
519
|
+
return self
|
520
|
+
|
521
|
+
@classmethod
|
522
|
+
def create_certificate_success(
|
523
|
+
cls,
|
524
|
+
certificate_info: CertificateInfo,
|
525
|
+
validation_result: Optional[ValidationResult] = None,
|
526
|
+
) -> "CertificateResponse":
|
527
|
+
"""Create successful certificate response."""
|
528
|
+
return cls(
|
529
|
+
status=ResponseStatus.SUCCESS,
|
530
|
+
message="Certificate information retrieved successfully",
|
531
|
+
certificate_info=certificate_info,
|
532
|
+
validation_result=validation_result,
|
533
|
+
)
|
534
|
+
|
535
|
+
|
536
|
+
class PermissionResponse(SecurityResponse[Dict[str, Any]]):
|
537
|
+
"""
|
538
|
+
Permission Response Model
|
539
|
+
|
540
|
+
This model represents permission-related responses with
|
541
|
+
user roles, permissions, and access control information.
|
542
|
+
|
543
|
+
Attributes:
|
544
|
+
user_roles: List of user roles
|
545
|
+
user_permissions: Set of user permissions
|
546
|
+
effective_permissions: Effective permissions after inheritance
|
547
|
+
access_granted: Whether access is granted
|
548
|
+
required_permissions: Required permissions for the operation
|
549
|
+
missing_permissions: Missing permissions
|
550
|
+
"""
|
551
|
+
|
552
|
+
user_roles: List[str] = Field(
|
553
|
+
default_factory=list, description="List of user roles"
|
554
|
+
)
|
555
|
+
user_permissions: set = Field(
|
556
|
+
default_factory=set, description="Set of user permissions"
|
557
|
+
)
|
558
|
+
effective_permissions: set = Field(
|
559
|
+
default_factory=set, description="Effective permissions after inheritance"
|
560
|
+
)
|
561
|
+
access_granted: bool = Field(..., description="Whether access is granted")
|
562
|
+
required_permissions: List[str] = Field(
|
563
|
+
default_factory=list, description="Required permissions for the operation"
|
564
|
+
)
|
565
|
+
missing_permissions: List[str] = Field(
|
566
|
+
default_factory=list, description="Missing permissions"
|
567
|
+
)
|
568
|
+
|
569
|
+
@model_validator(mode="after")
|
570
|
+
def validate_permission_response(self):
|
571
|
+
"""Validate permission response consistency."""
|
572
|
+
if self.access_granted and self.status != ResponseStatus.SUCCESS:
|
573
|
+
raise ValueError("Granted access must have SUCCESS status")
|
574
|
+
|
575
|
+
if not self.access_granted and self.status != ResponseStatus.ERROR:
|
576
|
+
raise ValueError("Denied access must have ERROR status")
|
577
|
+
|
578
|
+
return self
|
579
|
+
|
580
|
+
@classmethod
|
581
|
+
def create_permission_granted(
|
582
|
+
cls,
|
583
|
+
user_roles: List[str],
|
584
|
+
user_permissions: set,
|
585
|
+
required_permissions: List[str],
|
586
|
+
) -> "PermissionResponse":
|
587
|
+
"""Create permission granted response."""
|
588
|
+
return cls(
|
589
|
+
status=ResponseStatus.SUCCESS,
|
590
|
+
message="Access granted",
|
591
|
+
user_roles=user_roles,
|
592
|
+
user_permissions=user_permissions,
|
593
|
+
effective_permissions=user_permissions,
|
594
|
+
access_granted=True,
|
595
|
+
required_permissions=required_permissions,
|
596
|
+
missing_permissions=[],
|
597
|
+
)
|
598
|
+
|
599
|
+
@classmethod
|
600
|
+
def create_permission_denied(
|
601
|
+
cls,
|
602
|
+
user_roles: List[str],
|
603
|
+
user_permissions: set,
|
604
|
+
required_permissions: List[str],
|
605
|
+
) -> "PermissionResponse":
|
606
|
+
"""Create permission denied response."""
|
607
|
+
missing_permissions = [
|
608
|
+
perm for perm in required_permissions if perm not in user_permissions
|
609
|
+
]
|
610
|
+
return cls(
|
611
|
+
status=ResponseStatus.ERROR,
|
612
|
+
message="Access denied - insufficient permissions",
|
613
|
+
user_roles=user_roles,
|
614
|
+
user_permissions=user_permissions,
|
615
|
+
effective_permissions=user_permissions,
|
616
|
+
access_granted=False,
|
617
|
+
required_permissions=required_permissions,
|
618
|
+
missing_permissions=missing_permissions,
|
619
|
+
)
|
620
|
+
|
621
|
+
|
622
|
+
class RateLimitResponse(SecurityResponse[RateLimitStatus]):
|
623
|
+
"""
|
624
|
+
Rate Limit Response Model
|
625
|
+
|
626
|
+
This model represents rate limiting responses with current
|
627
|
+
rate limit status and usage information.
|
628
|
+
|
629
|
+
Attributes:
|
630
|
+
rate_limit_status: Rate limit status object
|
631
|
+
usage_percentage: Current usage percentage
|
632
|
+
reset_time: Time when rate limit resets
|
633
|
+
retry_after: Retry after time in seconds
|
634
|
+
"""
|
635
|
+
|
636
|
+
rate_limit_status: RateLimitStatus = Field(
|
637
|
+
..., description="Rate limit status object"
|
638
|
+
)
|
639
|
+
usage_percentage: float = Field(..., ge=0.0, description="Current usage percentage")
|
640
|
+
reset_time: datetime = Field(..., description="Time when rate limit resets")
|
641
|
+
retry_after: Optional[int] = Field(
|
642
|
+
default=None, ge=0, description="Retry after time in seconds"
|
643
|
+
)
|
644
|
+
|
645
|
+
@model_validator(mode="after")
|
646
|
+
def validate_rate_limit_response(self):
|
647
|
+
"""Validate rate limit response consistency."""
|
648
|
+
is_exceeded = self.rate_limit_status.is_exceeded
|
649
|
+
|
650
|
+
if is_exceeded and self.status != ResponseStatus.ERROR:
|
651
|
+
raise ValueError("Exceeded rate limit must have ERROR status")
|
652
|
+
|
653
|
+
if not is_exceeded and self.status != ResponseStatus.SUCCESS:
|
654
|
+
raise ValueError("Within rate limit must have SUCCESS status")
|
655
|
+
|
656
|
+
return self
|
657
|
+
|
658
|
+
@classmethod
|
659
|
+
def create_rate_limit_status(
|
660
|
+
cls, rate_limit_status: RateLimitStatus
|
661
|
+
) -> "RateLimitResponse":
|
662
|
+
"""Create rate limit status response."""
|
663
|
+
is_exceeded = rate_limit_status.is_exceeded
|
664
|
+
return cls(
|
665
|
+
status=ResponseStatus.ERROR if is_exceeded else ResponseStatus.SUCCESS,
|
666
|
+
message="Rate limit exceeded" if is_exceeded else "Rate limit status",
|
667
|
+
rate_limit_status=rate_limit_status,
|
668
|
+
usage_percentage=rate_limit_status.utilization_percentage,
|
669
|
+
reset_time=rate_limit_status.reset_time,
|
670
|
+
retry_after=rate_limit_status.seconds_until_reset if is_exceeded else None,
|
671
|
+
)
|
672
|
+
|
673
|
+
|
674
|
+
__all__ = [
|
675
|
+
"ResponseStatus",
|
676
|
+
"SecurityStatus",
|
677
|
+
"ErrorCode",
|
678
|
+
"SecurityResponse",
|
679
|
+
"ErrorResponse",
|
680
|
+
"SuccessResponse",
|
681
|
+
"ValidationResponse",
|
682
|
+
"AuthResponse",
|
683
|
+
"CertificateResponse",
|
684
|
+
"PermissionResponse",
|
685
|
+
"RateLimitResponse",
|
686
|
+
]
|
File without changes
|