mcp-hangar 0.2.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_hangar/__init__.py +139 -0
- mcp_hangar/application/__init__.py +1 -0
- mcp_hangar/application/commands/__init__.py +67 -0
- mcp_hangar/application/commands/auth_commands.py +118 -0
- mcp_hangar/application/commands/auth_handlers.py +296 -0
- mcp_hangar/application/commands/commands.py +59 -0
- mcp_hangar/application/commands/handlers.py +189 -0
- mcp_hangar/application/discovery/__init__.py +21 -0
- mcp_hangar/application/discovery/discovery_metrics.py +283 -0
- mcp_hangar/application/discovery/discovery_orchestrator.py +497 -0
- mcp_hangar/application/discovery/lifecycle_manager.py +315 -0
- mcp_hangar/application/discovery/security_validator.py +414 -0
- mcp_hangar/application/event_handlers/__init__.py +50 -0
- mcp_hangar/application/event_handlers/alert_handler.py +191 -0
- mcp_hangar/application/event_handlers/audit_handler.py +203 -0
- mcp_hangar/application/event_handlers/knowledge_base_handler.py +120 -0
- mcp_hangar/application/event_handlers/logging_handler.py +69 -0
- mcp_hangar/application/event_handlers/metrics_handler.py +152 -0
- mcp_hangar/application/event_handlers/persistent_audit_store.py +217 -0
- mcp_hangar/application/event_handlers/security_handler.py +604 -0
- mcp_hangar/application/mcp/tooling.py +158 -0
- mcp_hangar/application/ports/__init__.py +9 -0
- mcp_hangar/application/ports/observability.py +237 -0
- mcp_hangar/application/queries/__init__.py +52 -0
- mcp_hangar/application/queries/auth_handlers.py +237 -0
- mcp_hangar/application/queries/auth_queries.py +118 -0
- mcp_hangar/application/queries/handlers.py +227 -0
- mcp_hangar/application/read_models/__init__.py +11 -0
- mcp_hangar/application/read_models/provider_views.py +139 -0
- mcp_hangar/application/sagas/__init__.py +11 -0
- mcp_hangar/application/sagas/group_rebalance_saga.py +137 -0
- mcp_hangar/application/sagas/provider_failover_saga.py +266 -0
- mcp_hangar/application/sagas/provider_recovery_saga.py +172 -0
- mcp_hangar/application/services/__init__.py +9 -0
- mcp_hangar/application/services/provider_service.py +208 -0
- mcp_hangar/application/services/traced_provider_service.py +211 -0
- mcp_hangar/bootstrap/runtime.py +328 -0
- mcp_hangar/context.py +178 -0
- mcp_hangar/domain/__init__.py +117 -0
- mcp_hangar/domain/contracts/__init__.py +57 -0
- mcp_hangar/domain/contracts/authentication.py +225 -0
- mcp_hangar/domain/contracts/authorization.py +229 -0
- mcp_hangar/domain/contracts/event_store.py +178 -0
- mcp_hangar/domain/contracts/metrics_publisher.py +59 -0
- mcp_hangar/domain/contracts/persistence.py +383 -0
- mcp_hangar/domain/contracts/provider_runtime.py +146 -0
- mcp_hangar/domain/discovery/__init__.py +20 -0
- mcp_hangar/domain/discovery/conflict_resolver.py +267 -0
- mcp_hangar/domain/discovery/discovered_provider.py +185 -0
- mcp_hangar/domain/discovery/discovery_service.py +412 -0
- mcp_hangar/domain/discovery/discovery_source.py +192 -0
- mcp_hangar/domain/events.py +433 -0
- mcp_hangar/domain/exceptions.py +525 -0
- mcp_hangar/domain/model/__init__.py +70 -0
- mcp_hangar/domain/model/aggregate.py +58 -0
- mcp_hangar/domain/model/circuit_breaker.py +152 -0
- mcp_hangar/domain/model/event_sourced_api_key.py +413 -0
- mcp_hangar/domain/model/event_sourced_provider.py +423 -0
- mcp_hangar/domain/model/event_sourced_role_assignment.py +268 -0
- mcp_hangar/domain/model/health_tracker.py +183 -0
- mcp_hangar/domain/model/load_balancer.py +185 -0
- mcp_hangar/domain/model/provider.py +810 -0
- mcp_hangar/domain/model/provider_group.py +656 -0
- mcp_hangar/domain/model/tool_catalog.py +105 -0
- mcp_hangar/domain/policies/__init__.py +19 -0
- mcp_hangar/domain/policies/provider_health.py +187 -0
- mcp_hangar/domain/repository.py +249 -0
- mcp_hangar/domain/security/__init__.py +85 -0
- mcp_hangar/domain/security/input_validator.py +710 -0
- mcp_hangar/domain/security/rate_limiter.py +387 -0
- mcp_hangar/domain/security/roles.py +237 -0
- mcp_hangar/domain/security/sanitizer.py +387 -0
- mcp_hangar/domain/security/secrets.py +501 -0
- mcp_hangar/domain/services/__init__.py +20 -0
- mcp_hangar/domain/services/audit_service.py +376 -0
- mcp_hangar/domain/services/image_builder.py +328 -0
- mcp_hangar/domain/services/provider_launcher.py +1046 -0
- mcp_hangar/domain/value_objects.py +1138 -0
- mcp_hangar/errors.py +818 -0
- mcp_hangar/fastmcp_server.py +1105 -0
- mcp_hangar/gc.py +134 -0
- mcp_hangar/infrastructure/__init__.py +79 -0
- mcp_hangar/infrastructure/async_executor.py +133 -0
- mcp_hangar/infrastructure/auth/__init__.py +37 -0
- mcp_hangar/infrastructure/auth/api_key_authenticator.py +388 -0
- mcp_hangar/infrastructure/auth/event_sourced_store.py +567 -0
- mcp_hangar/infrastructure/auth/jwt_authenticator.py +360 -0
- mcp_hangar/infrastructure/auth/middleware.py +340 -0
- mcp_hangar/infrastructure/auth/opa_authorizer.py +243 -0
- mcp_hangar/infrastructure/auth/postgres_store.py +659 -0
- mcp_hangar/infrastructure/auth/projections.py +366 -0
- mcp_hangar/infrastructure/auth/rate_limiter.py +311 -0
- mcp_hangar/infrastructure/auth/rbac_authorizer.py +323 -0
- mcp_hangar/infrastructure/auth/sqlite_store.py +624 -0
- mcp_hangar/infrastructure/command_bus.py +112 -0
- mcp_hangar/infrastructure/discovery/__init__.py +110 -0
- mcp_hangar/infrastructure/discovery/docker_source.py +289 -0
- mcp_hangar/infrastructure/discovery/entrypoint_source.py +249 -0
- mcp_hangar/infrastructure/discovery/filesystem_source.py +383 -0
- mcp_hangar/infrastructure/discovery/kubernetes_source.py +247 -0
- mcp_hangar/infrastructure/event_bus.py +260 -0
- mcp_hangar/infrastructure/event_sourced_repository.py +443 -0
- mcp_hangar/infrastructure/event_store.py +396 -0
- mcp_hangar/infrastructure/knowledge_base/__init__.py +259 -0
- mcp_hangar/infrastructure/knowledge_base/contracts.py +202 -0
- mcp_hangar/infrastructure/knowledge_base/memory.py +177 -0
- mcp_hangar/infrastructure/knowledge_base/postgres.py +545 -0
- mcp_hangar/infrastructure/knowledge_base/sqlite.py +513 -0
- mcp_hangar/infrastructure/metrics_publisher.py +36 -0
- mcp_hangar/infrastructure/observability/__init__.py +10 -0
- mcp_hangar/infrastructure/observability/langfuse_adapter.py +534 -0
- mcp_hangar/infrastructure/persistence/__init__.py +33 -0
- mcp_hangar/infrastructure/persistence/audit_repository.py +371 -0
- mcp_hangar/infrastructure/persistence/config_repository.py +398 -0
- mcp_hangar/infrastructure/persistence/database.py +333 -0
- mcp_hangar/infrastructure/persistence/database_common.py +330 -0
- mcp_hangar/infrastructure/persistence/event_serializer.py +280 -0
- mcp_hangar/infrastructure/persistence/event_upcaster.py +166 -0
- mcp_hangar/infrastructure/persistence/in_memory_event_store.py +150 -0
- mcp_hangar/infrastructure/persistence/recovery_service.py +312 -0
- mcp_hangar/infrastructure/persistence/sqlite_event_store.py +386 -0
- mcp_hangar/infrastructure/persistence/unit_of_work.py +409 -0
- mcp_hangar/infrastructure/persistence/upcasters/README.md +13 -0
- mcp_hangar/infrastructure/persistence/upcasters/__init__.py +7 -0
- mcp_hangar/infrastructure/query_bus.py +153 -0
- mcp_hangar/infrastructure/saga_manager.py +401 -0
- mcp_hangar/logging_config.py +209 -0
- mcp_hangar/metrics.py +1007 -0
- mcp_hangar/models.py +31 -0
- mcp_hangar/observability/__init__.py +54 -0
- mcp_hangar/observability/health.py +487 -0
- mcp_hangar/observability/metrics.py +319 -0
- mcp_hangar/observability/tracing.py +433 -0
- mcp_hangar/progress.py +542 -0
- mcp_hangar/retry.py +613 -0
- mcp_hangar/server/__init__.py +120 -0
- mcp_hangar/server/__main__.py +6 -0
- mcp_hangar/server/auth_bootstrap.py +340 -0
- mcp_hangar/server/auth_cli.py +335 -0
- mcp_hangar/server/auth_config.py +305 -0
- mcp_hangar/server/bootstrap.py +735 -0
- mcp_hangar/server/cli.py +161 -0
- mcp_hangar/server/config.py +224 -0
- mcp_hangar/server/context.py +215 -0
- mcp_hangar/server/http_auth_middleware.py +165 -0
- mcp_hangar/server/lifecycle.py +467 -0
- mcp_hangar/server/state.py +117 -0
- mcp_hangar/server/tools/__init__.py +16 -0
- mcp_hangar/server/tools/discovery.py +186 -0
- mcp_hangar/server/tools/groups.py +75 -0
- mcp_hangar/server/tools/health.py +301 -0
- mcp_hangar/server/tools/provider.py +939 -0
- mcp_hangar/server/tools/registry.py +320 -0
- mcp_hangar/server/validation.py +113 -0
- mcp_hangar/stdio_client.py +229 -0
- mcp_hangar-0.2.0.dist-info/METADATA +347 -0
- mcp_hangar-0.2.0.dist-info/RECORD +160 -0
- mcp_hangar-0.2.0.dist-info/WHEEL +4 -0
- mcp_hangar-0.2.0.dist-info/entry_points.txt +2 -0
- mcp_hangar-0.2.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,433 @@
|
|
|
1
|
+
"""Domain events for the MCP Registry.
|
|
2
|
+
|
|
3
|
+
Events capture important business occurrences and allow decoupled reactions.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from abc import ABC
|
|
7
|
+
from dataclasses import dataclass, field
|
|
8
|
+
import time
|
|
9
|
+
from typing import Any, Dict
|
|
10
|
+
import uuid
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class DomainEvent(ABC):
|
|
14
|
+
"""
|
|
15
|
+
Base class for all domain events.
|
|
16
|
+
|
|
17
|
+
Note: Not a dataclass to avoid inheritance issues.
|
|
18
|
+
Subclasses should be dataclasses.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
def __init__(self):
|
|
22
|
+
self.event_id: str = str(uuid.uuid4())
|
|
23
|
+
self.occurred_at: float = time.time()
|
|
24
|
+
|
|
25
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
26
|
+
"""Convert event to dictionary for serialization."""
|
|
27
|
+
return {"event_type": self.__class__.__name__, **self.__dict__}
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
# Provider Lifecycle Events
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@dataclass
|
|
34
|
+
class ProviderStarted(DomainEvent):
|
|
35
|
+
"""Published when a provider successfully starts."""
|
|
36
|
+
|
|
37
|
+
provider_id: str
|
|
38
|
+
mode: str # subprocess, docker, remote
|
|
39
|
+
tools_count: int
|
|
40
|
+
startup_duration_ms: float
|
|
41
|
+
|
|
42
|
+
def __post_init__(self):
|
|
43
|
+
super().__init__()
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
@dataclass
|
|
47
|
+
class ProviderStopped(DomainEvent):
|
|
48
|
+
"""Published when a provider stops."""
|
|
49
|
+
|
|
50
|
+
provider_id: str
|
|
51
|
+
reason: str # "shutdown", "idle", "error", "degraded"
|
|
52
|
+
|
|
53
|
+
def __post_init__(self):
|
|
54
|
+
super().__init__()
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@dataclass
|
|
58
|
+
class ProviderDegraded(DomainEvent):
|
|
59
|
+
"""Published when a provider enters degraded state."""
|
|
60
|
+
|
|
61
|
+
provider_id: str
|
|
62
|
+
consecutive_failures: int
|
|
63
|
+
total_failures: int
|
|
64
|
+
reason: str
|
|
65
|
+
|
|
66
|
+
def __post_init__(self):
|
|
67
|
+
super().__init__()
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
@dataclass
|
|
71
|
+
class ProviderStateChanged(DomainEvent):
|
|
72
|
+
"""Published when provider state transitions."""
|
|
73
|
+
|
|
74
|
+
provider_id: str
|
|
75
|
+
old_state: str
|
|
76
|
+
new_state: str
|
|
77
|
+
|
|
78
|
+
def __post_init__(self):
|
|
79
|
+
super().__init__()
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
# Tool Invocation Events
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
@dataclass
|
|
86
|
+
class ToolInvocationRequested(DomainEvent):
|
|
87
|
+
"""Published when a tool invocation is requested."""
|
|
88
|
+
|
|
89
|
+
provider_id: str
|
|
90
|
+
tool_name: str
|
|
91
|
+
correlation_id: str
|
|
92
|
+
arguments: Dict[str, Any] = field(default_factory=dict)
|
|
93
|
+
|
|
94
|
+
def __post_init__(self):
|
|
95
|
+
super().__init__()
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
@dataclass
|
|
99
|
+
class ToolInvocationCompleted(DomainEvent):
|
|
100
|
+
"""Published when a tool invocation completes successfully."""
|
|
101
|
+
|
|
102
|
+
provider_id: str
|
|
103
|
+
tool_name: str
|
|
104
|
+
correlation_id: str
|
|
105
|
+
duration_ms: float
|
|
106
|
+
result_size_bytes: int = 0
|
|
107
|
+
|
|
108
|
+
def __post_init__(self):
|
|
109
|
+
super().__init__()
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
@dataclass
|
|
113
|
+
class ToolInvocationFailed(DomainEvent):
|
|
114
|
+
"""Published when a tool invocation fails."""
|
|
115
|
+
|
|
116
|
+
provider_id: str
|
|
117
|
+
tool_name: str
|
|
118
|
+
correlation_id: str
|
|
119
|
+
error_message: str
|
|
120
|
+
error_type: str
|
|
121
|
+
|
|
122
|
+
def __post_init__(self):
|
|
123
|
+
super().__init__()
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
# Health Check Events
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
@dataclass
|
|
130
|
+
class HealthCheckPassed(DomainEvent):
|
|
131
|
+
"""Published when a health check succeeds."""
|
|
132
|
+
|
|
133
|
+
provider_id: str
|
|
134
|
+
duration_ms: float
|
|
135
|
+
|
|
136
|
+
def __post_init__(self):
|
|
137
|
+
super().__init__()
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
@dataclass
|
|
141
|
+
class HealthCheckFailed(DomainEvent):
|
|
142
|
+
"""Published when a health check fails."""
|
|
143
|
+
|
|
144
|
+
provider_id: str
|
|
145
|
+
consecutive_failures: int
|
|
146
|
+
error_message: str
|
|
147
|
+
|
|
148
|
+
def __post_init__(self):
|
|
149
|
+
super().__init__()
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
# Resource Management Events
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
@dataclass
|
|
156
|
+
class ProviderIdleDetected(DomainEvent):
|
|
157
|
+
"""Published when a provider is detected as idle."""
|
|
158
|
+
|
|
159
|
+
provider_id: str
|
|
160
|
+
idle_duration_s: float
|
|
161
|
+
last_used_at: float
|
|
162
|
+
|
|
163
|
+
def __post_init__(self):
|
|
164
|
+
super().__init__()
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
# Provider Group Events are defined in mcp_hangar.domain.model.provider_group
|
|
168
|
+
# to avoid circular imports. Re-export them here for convenience.
|
|
169
|
+
# Import at runtime only when needed.
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
# Discovery Events
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
@dataclass
|
|
176
|
+
class ProviderDiscovered(DomainEvent):
|
|
177
|
+
"""Published when a new provider is discovered."""
|
|
178
|
+
|
|
179
|
+
provider_name: str
|
|
180
|
+
source_type: str
|
|
181
|
+
mode: str
|
|
182
|
+
fingerprint: str
|
|
183
|
+
|
|
184
|
+
def __post_init__(self):
|
|
185
|
+
super().__init__()
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
@dataclass
|
|
189
|
+
class ProviderDiscoveryLost(DomainEvent):
|
|
190
|
+
"""Published when a previously discovered provider is no longer found."""
|
|
191
|
+
|
|
192
|
+
provider_name: str
|
|
193
|
+
source_type: str
|
|
194
|
+
reason: str # "ttl_expired", "source_removed", etc.
|
|
195
|
+
|
|
196
|
+
def __post_init__(self):
|
|
197
|
+
super().__init__()
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
@dataclass
|
|
201
|
+
class ProviderDiscoveryConfigChanged(DomainEvent):
|
|
202
|
+
"""Published when discovered provider configuration changes."""
|
|
203
|
+
|
|
204
|
+
provider_name: str
|
|
205
|
+
source_type: str
|
|
206
|
+
old_fingerprint: str
|
|
207
|
+
new_fingerprint: str
|
|
208
|
+
|
|
209
|
+
def __post_init__(self):
|
|
210
|
+
super().__init__()
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
@dataclass
|
|
214
|
+
class ProviderQuarantined(DomainEvent):
|
|
215
|
+
"""Published when a discovered provider is quarantined."""
|
|
216
|
+
|
|
217
|
+
provider_name: str
|
|
218
|
+
source_type: str
|
|
219
|
+
reason: str
|
|
220
|
+
validation_result: str
|
|
221
|
+
|
|
222
|
+
def __post_init__(self):
|
|
223
|
+
super().__init__()
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
@dataclass
|
|
227
|
+
class ProviderApproved(DomainEvent):
|
|
228
|
+
"""Published when a quarantined provider is approved."""
|
|
229
|
+
|
|
230
|
+
provider_name: str
|
|
231
|
+
source_type: str
|
|
232
|
+
approved_by: str # "manual" or "auto"
|
|
233
|
+
|
|
234
|
+
def __post_init__(self):
|
|
235
|
+
super().__init__()
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
@dataclass
|
|
239
|
+
class DiscoveryCycleCompleted(DomainEvent):
|
|
240
|
+
"""Published when a discovery cycle completes."""
|
|
241
|
+
|
|
242
|
+
discovered_count: int
|
|
243
|
+
registered_count: int
|
|
244
|
+
deregistered_count: int
|
|
245
|
+
quarantined_count: int
|
|
246
|
+
error_count: int
|
|
247
|
+
duration_ms: float
|
|
248
|
+
|
|
249
|
+
def __post_init__(self):
|
|
250
|
+
super().__init__()
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
@dataclass
|
|
254
|
+
class DiscoverySourceHealthChanged(DomainEvent):
|
|
255
|
+
"""Published when a discovery source health status changes."""
|
|
256
|
+
|
|
257
|
+
source_type: str
|
|
258
|
+
is_healthy: bool
|
|
259
|
+
error_message: str | None = None
|
|
260
|
+
|
|
261
|
+
def __post_init__(self):
|
|
262
|
+
super().__init__()
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
# Authentication & Authorization Events
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
@dataclass
|
|
269
|
+
class AuthenticationSucceeded(DomainEvent):
|
|
270
|
+
"""Published when a principal successfully authenticates.
|
|
271
|
+
|
|
272
|
+
Attributes:
|
|
273
|
+
principal_id: The authenticated principal's identifier.
|
|
274
|
+
principal_type: Type of principal (user, service_account, system).
|
|
275
|
+
auth_method: Authentication method used (api_key, jwt, mtls).
|
|
276
|
+
source_ip: IP address of the request origin.
|
|
277
|
+
tenant_id: Optional tenant identifier if multi-tenancy is enabled.
|
|
278
|
+
"""
|
|
279
|
+
|
|
280
|
+
principal_id: str
|
|
281
|
+
principal_type: str
|
|
282
|
+
auth_method: str
|
|
283
|
+
source_ip: str
|
|
284
|
+
tenant_id: str | None = None
|
|
285
|
+
|
|
286
|
+
def __post_init__(self):
|
|
287
|
+
super().__init__()
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
@dataclass
|
|
291
|
+
class AuthenticationFailed(DomainEvent):
|
|
292
|
+
"""Published when authentication fails.
|
|
293
|
+
|
|
294
|
+
Attributes:
|
|
295
|
+
auth_method: Authentication method that was attempted.
|
|
296
|
+
source_ip: IP address of the request origin.
|
|
297
|
+
reason: Reason for failure (invalid_token, expired, revoked, unknown_key).
|
|
298
|
+
attempted_principal_id: Optional principal ID if it could be extracted.
|
|
299
|
+
"""
|
|
300
|
+
|
|
301
|
+
auth_method: str
|
|
302
|
+
source_ip: str
|
|
303
|
+
reason: str
|
|
304
|
+
attempted_principal_id: str | None = None
|
|
305
|
+
|
|
306
|
+
def __post_init__(self):
|
|
307
|
+
super().__init__()
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
@dataclass
|
|
311
|
+
class AuthorizationDenied(DomainEvent):
|
|
312
|
+
"""Published when an authorized principal is denied access.
|
|
313
|
+
|
|
314
|
+
Attributes:
|
|
315
|
+
principal_id: The principal who was denied.
|
|
316
|
+
action: The action that was attempted.
|
|
317
|
+
resource_type: Type of resource being accessed.
|
|
318
|
+
resource_id: Specific resource identifier.
|
|
319
|
+
reason: Why access was denied.
|
|
320
|
+
"""
|
|
321
|
+
|
|
322
|
+
principal_id: str
|
|
323
|
+
action: str
|
|
324
|
+
resource_type: str
|
|
325
|
+
resource_id: str
|
|
326
|
+
reason: str
|
|
327
|
+
|
|
328
|
+
def __post_init__(self):
|
|
329
|
+
super().__init__()
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
@dataclass
|
|
333
|
+
class AuthorizationGranted(DomainEvent):
|
|
334
|
+
"""Published when authorization is granted (for audit trail).
|
|
335
|
+
|
|
336
|
+
Attributes:
|
|
337
|
+
principal_id: The principal who was granted access.
|
|
338
|
+
action: The action that was authorized.
|
|
339
|
+
resource_type: Type of resource being accessed.
|
|
340
|
+
resource_id: Specific resource identifier.
|
|
341
|
+
granted_by_role: Role that granted the permission.
|
|
342
|
+
"""
|
|
343
|
+
|
|
344
|
+
principal_id: str
|
|
345
|
+
action: str
|
|
346
|
+
resource_type: str
|
|
347
|
+
resource_id: str
|
|
348
|
+
granted_by_role: str
|
|
349
|
+
|
|
350
|
+
def __post_init__(self):
|
|
351
|
+
super().__init__()
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
@dataclass
|
|
355
|
+
class RoleAssigned(DomainEvent):
|
|
356
|
+
"""Published when a role is assigned to a principal.
|
|
357
|
+
|
|
358
|
+
Attributes:
|
|
359
|
+
principal_id: Principal receiving the role.
|
|
360
|
+
role_name: Name of the role being assigned.
|
|
361
|
+
scope: Scope of the assignment (global, tenant:X, namespace:Y).
|
|
362
|
+
assigned_by: Principal who made the assignment.
|
|
363
|
+
"""
|
|
364
|
+
|
|
365
|
+
principal_id: str
|
|
366
|
+
role_name: str
|
|
367
|
+
scope: str
|
|
368
|
+
assigned_by: str
|
|
369
|
+
|
|
370
|
+
def __post_init__(self):
|
|
371
|
+
super().__init__()
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
@dataclass
|
|
375
|
+
class RoleRevoked(DomainEvent):
|
|
376
|
+
"""Published when a role is revoked from a principal.
|
|
377
|
+
|
|
378
|
+
Attributes:
|
|
379
|
+
principal_id: Principal losing the role.
|
|
380
|
+
role_name: Name of the role being revoked.
|
|
381
|
+
scope: Scope from which the role is being revoked.
|
|
382
|
+
revoked_by: Principal who made the revocation.
|
|
383
|
+
"""
|
|
384
|
+
|
|
385
|
+
principal_id: str
|
|
386
|
+
role_name: str
|
|
387
|
+
scope: str
|
|
388
|
+
revoked_by: str
|
|
389
|
+
|
|
390
|
+
def __post_init__(self):
|
|
391
|
+
super().__init__()
|
|
392
|
+
|
|
393
|
+
|
|
394
|
+
@dataclass
|
|
395
|
+
class ApiKeyCreated(DomainEvent):
|
|
396
|
+
"""Published when a new API key is created.
|
|
397
|
+
|
|
398
|
+
Attributes:
|
|
399
|
+
key_id: Unique identifier of the key (not the key itself).
|
|
400
|
+
principal_id: Principal the key authenticates as.
|
|
401
|
+
key_name: Human-readable name for the key.
|
|
402
|
+
expires_at: Optional expiration timestamp.
|
|
403
|
+
created_by: Principal who created the key.
|
|
404
|
+
"""
|
|
405
|
+
|
|
406
|
+
key_id: str
|
|
407
|
+
principal_id: str
|
|
408
|
+
key_name: str
|
|
409
|
+
expires_at: float | None
|
|
410
|
+
created_by: str
|
|
411
|
+
|
|
412
|
+
def __post_init__(self):
|
|
413
|
+
super().__init__()
|
|
414
|
+
|
|
415
|
+
|
|
416
|
+
@dataclass
|
|
417
|
+
class ApiKeyRevoked(DomainEvent):
|
|
418
|
+
"""Published when an API key is revoked.
|
|
419
|
+
|
|
420
|
+
Attributes:
|
|
421
|
+
key_id: Unique identifier of the revoked key.
|
|
422
|
+
principal_id: Principal the key belonged to.
|
|
423
|
+
revoked_by: Principal who revoked the key.
|
|
424
|
+
reason: Optional reason for revocation.
|
|
425
|
+
"""
|
|
426
|
+
|
|
427
|
+
key_id: str
|
|
428
|
+
principal_id: str
|
|
429
|
+
revoked_by: str
|
|
430
|
+
reason: str = ""
|
|
431
|
+
|
|
432
|
+
def __post_init__(self):
|
|
433
|
+
super().__init__()
|