kailash 0.3.1__py3-none-any.whl → 0.4.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (146) hide show
  1. kailash/__init__.py +33 -1
  2. kailash/access_control/__init__.py +129 -0
  3. kailash/access_control/managers.py +461 -0
  4. kailash/access_control/rule_evaluators.py +467 -0
  5. kailash/access_control_abac.py +825 -0
  6. kailash/config/__init__.py +27 -0
  7. kailash/config/database_config.py +359 -0
  8. kailash/database/__init__.py +28 -0
  9. kailash/database/execution_pipeline.py +499 -0
  10. kailash/middleware/__init__.py +306 -0
  11. kailash/middleware/auth/__init__.py +33 -0
  12. kailash/middleware/auth/access_control.py +436 -0
  13. kailash/middleware/auth/auth_manager.py +422 -0
  14. kailash/middleware/auth/jwt_auth.py +477 -0
  15. kailash/middleware/auth/kailash_jwt_auth.py +616 -0
  16. kailash/middleware/communication/__init__.py +37 -0
  17. kailash/middleware/communication/ai_chat.py +989 -0
  18. kailash/middleware/communication/api_gateway.py +802 -0
  19. kailash/middleware/communication/events.py +470 -0
  20. kailash/middleware/communication/realtime.py +710 -0
  21. kailash/middleware/core/__init__.py +21 -0
  22. kailash/middleware/core/agent_ui.py +890 -0
  23. kailash/middleware/core/schema.py +643 -0
  24. kailash/middleware/core/workflows.py +396 -0
  25. kailash/middleware/database/__init__.py +63 -0
  26. kailash/middleware/database/base.py +113 -0
  27. kailash/middleware/database/base_models.py +525 -0
  28. kailash/middleware/database/enums.py +106 -0
  29. kailash/middleware/database/migrations.py +12 -0
  30. kailash/{api/database.py → middleware/database/models.py} +183 -291
  31. kailash/middleware/database/repositories.py +685 -0
  32. kailash/middleware/database/session_manager.py +19 -0
  33. kailash/middleware/mcp/__init__.py +38 -0
  34. kailash/middleware/mcp/client_integration.py +585 -0
  35. kailash/middleware/mcp/enhanced_server.py +576 -0
  36. kailash/nodes/__init__.py +25 -3
  37. kailash/nodes/admin/__init__.py +35 -0
  38. kailash/nodes/admin/audit_log.py +794 -0
  39. kailash/nodes/admin/permission_check.py +864 -0
  40. kailash/nodes/admin/role_management.py +823 -0
  41. kailash/nodes/admin/security_event.py +1519 -0
  42. kailash/nodes/admin/user_management.py +944 -0
  43. kailash/nodes/ai/a2a.py +24 -7
  44. kailash/nodes/ai/ai_providers.py +1 -0
  45. kailash/nodes/ai/embedding_generator.py +11 -11
  46. kailash/nodes/ai/intelligent_agent_orchestrator.py +99 -11
  47. kailash/nodes/ai/llm_agent.py +407 -2
  48. kailash/nodes/ai/self_organizing.py +85 -10
  49. kailash/nodes/api/auth.py +287 -6
  50. kailash/nodes/api/rest.py +151 -0
  51. kailash/nodes/auth/__init__.py +17 -0
  52. kailash/nodes/auth/directory_integration.py +1228 -0
  53. kailash/nodes/auth/enterprise_auth_provider.py +1328 -0
  54. kailash/nodes/auth/mfa.py +2338 -0
  55. kailash/nodes/auth/risk_assessment.py +872 -0
  56. kailash/nodes/auth/session_management.py +1093 -0
  57. kailash/nodes/auth/sso.py +1040 -0
  58. kailash/nodes/base.py +344 -13
  59. kailash/nodes/base_cycle_aware.py +4 -2
  60. kailash/nodes/base_with_acl.py +1 -1
  61. kailash/nodes/code/python.py +293 -12
  62. kailash/nodes/compliance/__init__.py +9 -0
  63. kailash/nodes/compliance/data_retention.py +1888 -0
  64. kailash/nodes/compliance/gdpr.py +2004 -0
  65. kailash/nodes/data/__init__.py +22 -2
  66. kailash/nodes/data/async_connection.py +469 -0
  67. kailash/nodes/data/async_sql.py +757 -0
  68. kailash/nodes/data/async_vector.py +598 -0
  69. kailash/nodes/data/readers.py +767 -0
  70. kailash/nodes/data/retrieval.py +360 -1
  71. kailash/nodes/data/sharepoint_graph.py +397 -21
  72. kailash/nodes/data/sql.py +94 -5
  73. kailash/nodes/data/streaming.py +68 -8
  74. kailash/nodes/data/vector_db.py +54 -4
  75. kailash/nodes/enterprise/__init__.py +13 -0
  76. kailash/nodes/enterprise/batch_processor.py +741 -0
  77. kailash/nodes/enterprise/data_lineage.py +497 -0
  78. kailash/nodes/logic/convergence.py +31 -9
  79. kailash/nodes/logic/operations.py +14 -3
  80. kailash/nodes/mixins/__init__.py +8 -0
  81. kailash/nodes/mixins/event_emitter.py +201 -0
  82. kailash/nodes/mixins/mcp.py +9 -4
  83. kailash/nodes/mixins/security.py +165 -0
  84. kailash/nodes/monitoring/__init__.py +7 -0
  85. kailash/nodes/monitoring/performance_benchmark.py +2497 -0
  86. kailash/nodes/rag/__init__.py +284 -0
  87. kailash/nodes/rag/advanced.py +1615 -0
  88. kailash/nodes/rag/agentic.py +773 -0
  89. kailash/nodes/rag/conversational.py +999 -0
  90. kailash/nodes/rag/evaluation.py +875 -0
  91. kailash/nodes/rag/federated.py +1188 -0
  92. kailash/nodes/rag/graph.py +721 -0
  93. kailash/nodes/rag/multimodal.py +671 -0
  94. kailash/nodes/rag/optimized.py +933 -0
  95. kailash/nodes/rag/privacy.py +1059 -0
  96. kailash/nodes/rag/query_processing.py +1335 -0
  97. kailash/nodes/rag/realtime.py +764 -0
  98. kailash/nodes/rag/registry.py +547 -0
  99. kailash/nodes/rag/router.py +837 -0
  100. kailash/nodes/rag/similarity.py +1854 -0
  101. kailash/nodes/rag/strategies.py +566 -0
  102. kailash/nodes/rag/workflows.py +575 -0
  103. kailash/nodes/security/__init__.py +19 -0
  104. kailash/nodes/security/abac_evaluator.py +1411 -0
  105. kailash/nodes/security/audit_log.py +91 -0
  106. kailash/nodes/security/behavior_analysis.py +1893 -0
  107. kailash/nodes/security/credential_manager.py +401 -0
  108. kailash/nodes/security/rotating_credentials.py +760 -0
  109. kailash/nodes/security/security_event.py +132 -0
  110. kailash/nodes/security/threat_detection.py +1103 -0
  111. kailash/nodes/testing/__init__.py +9 -0
  112. kailash/nodes/testing/credential_testing.py +499 -0
  113. kailash/nodes/transform/__init__.py +10 -2
  114. kailash/nodes/transform/chunkers.py +592 -1
  115. kailash/nodes/transform/processors.py +484 -14
  116. kailash/nodes/validation.py +321 -0
  117. kailash/runtime/access_controlled.py +1 -1
  118. kailash/runtime/async_local.py +41 -7
  119. kailash/runtime/docker.py +1 -1
  120. kailash/runtime/local.py +474 -55
  121. kailash/runtime/parallel.py +1 -1
  122. kailash/runtime/parallel_cyclic.py +1 -1
  123. kailash/runtime/testing.py +210 -2
  124. kailash/utils/migrations/__init__.py +25 -0
  125. kailash/utils/migrations/generator.py +433 -0
  126. kailash/utils/migrations/models.py +231 -0
  127. kailash/utils/migrations/runner.py +489 -0
  128. kailash/utils/secure_logging.py +342 -0
  129. kailash/workflow/__init__.py +16 -0
  130. kailash/workflow/cyclic_runner.py +3 -4
  131. kailash/workflow/graph.py +70 -2
  132. kailash/workflow/resilience.py +249 -0
  133. kailash/workflow/templates.py +726 -0
  134. {kailash-0.3.1.dist-info → kailash-0.4.0.dist-info}/METADATA +253 -20
  135. kailash-0.4.0.dist-info/RECORD +223 -0
  136. kailash/api/__init__.py +0 -17
  137. kailash/api/__main__.py +0 -6
  138. kailash/api/studio_secure.py +0 -893
  139. kailash/mcp/__main__.py +0 -13
  140. kailash/mcp/server_new.py +0 -336
  141. kailash/mcp/servers/__init__.py +0 -12
  142. kailash-0.3.1.dist-info/RECORD +0 -136
  143. {kailash-0.3.1.dist-info → kailash-0.4.0.dist-info}/WHEEL +0 -0
  144. {kailash-0.3.1.dist-info → kailash-0.4.0.dist-info}/entry_points.txt +0 -0
  145. {kailash-0.3.1.dist-info → kailash-0.4.0.dist-info}/licenses/LICENSE +0 -0
  146. {kailash-0.3.1.dist-info → kailash-0.4.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,467 @@
1
+ """Rule evaluator interfaces and implementations for access control.
2
+
3
+ This module provides a composition-based approach to access control rule evaluation,
4
+ replacing the problematic inheritance pattern with clear, testable interfaces.
5
+ """
6
+
7
+ import logging
8
+ from abc import ABC, abstractmethod
9
+ from datetime import UTC, datetime
10
+ from typing import Any, Dict, List, Optional, Union
11
+
12
+ # Import base access control components - use absolute imports to avoid circular import
13
+ try:
14
+ from kailash.access_control import (
15
+ AccessDecision,
16
+ NodePermission,
17
+ PermissionEffect,
18
+ PermissionRule,
19
+ UserContext,
20
+ WorkflowPermission,
21
+ )
22
+ except ImportError:
23
+ # Local definitions to handle circular import during initial setup
24
+ from dataclasses import dataclass
25
+ from datetime import datetime
26
+ from enum import Enum
27
+
28
+ class NodePermission(Enum):
29
+ EXECUTE = "execute"
30
+ READ_OUTPUT = "read_output"
31
+ WRITE_INPUT = "write_input"
32
+
33
+ class WorkflowPermission(Enum):
34
+ VIEW = "view"
35
+ EXECUTE = "execute"
36
+ MODIFY = "modify"
37
+
38
+ class PermissionEffect(Enum):
39
+ ALLOW = "allow"
40
+ DENY = "deny"
41
+ CONDITIONAL = "conditional"
42
+
43
+ @dataclass
44
+ class UserContext:
45
+ user_id: str
46
+ tenant_id: str
47
+ email: str
48
+ roles: List[str]
49
+ attributes: Dict[str, Any]
50
+
51
+ @dataclass
52
+ class AccessDecision:
53
+ allowed: bool
54
+ reason: str
55
+ applied_rules: List[str]
56
+ conditions_met: Optional[Dict[str, bool]] = None
57
+ masked_fields: Optional[List[str]] = None
58
+
59
+ @dataclass
60
+ class PermissionRule:
61
+ id: str
62
+ resource_type: str
63
+ resource_id: str
64
+ permission: Union[NodePermission, WorkflowPermission]
65
+ effect: PermissionEffect
66
+ user_id: Optional[str] = None
67
+ role: Optional[str] = None
68
+ tenant_id: Optional[str] = None
69
+ conditions: Optional[Dict[str, Any]] = None
70
+ priority: int = 0
71
+ expires_at: Optional[datetime] = None
72
+
73
+
74
+ logger = logging.getLogger(__name__)
75
+
76
+
77
+ class RuleEvaluator(ABC):
78
+ """Abstract base class for rule evaluation strategies."""
79
+
80
+ @abstractmethod
81
+ def evaluate_rules(
82
+ self,
83
+ rules: List[PermissionRule],
84
+ user: UserContext,
85
+ resource_type: str,
86
+ resource_id: str,
87
+ permission: Union[NodePermission, WorkflowPermission],
88
+ runtime_context: Dict[str, Any],
89
+ ) -> AccessDecision:
90
+ """Evaluate a set of rules for a user's access request.
91
+
92
+ Args:
93
+ rules: List of applicable permission rules
94
+ user: User making the request
95
+ resource_type: Type of resource (node/workflow)
96
+ resource_id: Specific resource identifier
97
+ permission: Permission being requested
98
+ runtime_context: Additional runtime context
99
+
100
+ Returns:
101
+ AccessDecision with allow/deny and reasoning
102
+ """
103
+ pass
104
+
105
+ @abstractmethod
106
+ def supports_conditions(self) -> bool:
107
+ """Return whether this evaluator supports conditional rules."""
108
+ pass
109
+
110
+
111
+ class RBACRuleEvaluator(RuleEvaluator):
112
+ """Role-Based Access Control rule evaluator.
113
+
114
+ This evaluator handles traditional RBAC rules based on:
115
+ - User roles
116
+ - Direct user assignments
117
+ - Tenant-based permissions
118
+ """
119
+
120
+ def __init__(self):
121
+ """Initialize RBAC evaluator."""
122
+ self.logger = logging.getLogger(f"{__name__}.RBACRuleEvaluator")
123
+
124
+ def evaluate_rules(
125
+ self,
126
+ rules: List[PermissionRule],
127
+ user: UserContext,
128
+ resource_type: str,
129
+ resource_id: str,
130
+ permission: Union[NodePermission, WorkflowPermission],
131
+ runtime_context: Dict[str, Any],
132
+ ) -> AccessDecision:
133
+ """Evaluate RBAC rules."""
134
+ # Filter applicable rules (only those that apply to this user)
135
+ applicable_rules = [
136
+ rule for rule in rules if self._rule_applies_to_user(rule, user)
137
+ ]
138
+
139
+ if not applicable_rules:
140
+ return AccessDecision(
141
+ allowed=False,
142
+ reason="No applicable RBAC rules found",
143
+ applied_rules=[],
144
+ )
145
+
146
+ # Sort by priority (higher first)
147
+ applicable_rules.sort(key=lambda r: r.priority, reverse=True)
148
+
149
+ # Evaluate rules in priority order
150
+ for rule in applicable_rules:
151
+ # RBAC doesn't support complex conditions
152
+ if rule.conditions:
153
+ self.logger.warning(
154
+ f"RBAC evaluator ignoring conditions in rule {rule.id}"
155
+ )
156
+
157
+ # Apply effect
158
+ if rule.effect == PermissionEffect.ALLOW:
159
+ return AccessDecision(
160
+ allowed=True,
161
+ reason=f"RBAC rule {rule.id} granted access",
162
+ applied_rules=[rule.id],
163
+ )
164
+ elif rule.effect == PermissionEffect.DENY:
165
+ return AccessDecision(
166
+ allowed=False,
167
+ reason=f"RBAC rule {rule.id} denied access",
168
+ applied_rules=[rule.id],
169
+ )
170
+
171
+ # Default deny
172
+ return AccessDecision(
173
+ allowed=False,
174
+ reason="No matching RBAC allow rules",
175
+ applied_rules=[rule.id for rule in applicable_rules],
176
+ )
177
+
178
+ def supports_conditions(self) -> bool:
179
+ """RBAC evaluator does not support complex conditions."""
180
+ return False
181
+
182
+ def _rule_applies_to_user(self, rule: PermissionRule, user: UserContext) -> bool:
183
+ """Check if a rule applies to a user based on RBAC criteria."""
184
+ # Direct user assignment
185
+ if rule.user_id and rule.user_id == user.user_id:
186
+ return True
187
+
188
+ # Role-based assignment
189
+ if rule.role and rule.role in user.roles:
190
+ return True
191
+
192
+ # Tenant-based assignment
193
+ if rule.tenant_id and rule.tenant_id == user.tenant_id:
194
+ return True
195
+
196
+ # No restrictions means applies to all
197
+ if not rule.user_id and not rule.role and not rule.tenant_id:
198
+ return True
199
+
200
+ return False
201
+
202
+
203
+ class ABACRuleEvaluator(RuleEvaluator):
204
+ """Attribute-Based Access Control rule evaluator.
205
+
206
+ This evaluator handles advanced ABAC rules with:
207
+ - Complex attribute expressions
208
+ - Dynamic condition evaluation
209
+ - Hierarchical attribute matching
210
+ """
211
+
212
+ def __init__(self):
213
+ """Initialize ABAC evaluator."""
214
+ self.logger = logging.getLogger(f"{__name__}.ABACRuleEvaluator")
215
+ # Import here to avoid circular dependencies
216
+ from kailash.access_control_abac import EnhancedConditionEvaluator
217
+
218
+ self.condition_evaluator = EnhancedConditionEvaluator()
219
+
220
+ def evaluate_rules(
221
+ self,
222
+ rules: List[PermissionRule],
223
+ user: UserContext,
224
+ resource_type: str,
225
+ resource_id: str,
226
+ permission: Union[NodePermission, WorkflowPermission],
227
+ runtime_context: Dict[str, Any],
228
+ ) -> AccessDecision:
229
+ """Evaluate ABAC rules with complex attribute conditions."""
230
+ # Build evaluation context
231
+ context = self._build_context(user, runtime_context)
232
+
233
+ # Filter applicable rules (basic RBAC filtering + condition evaluation)
234
+ applicable_rules = []
235
+ for rule in rules:
236
+ # First check basic RBAC criteria
237
+ if not self._rule_applies_to_user(rule, user):
238
+ continue
239
+
240
+ applicable_rules.append(rule)
241
+
242
+ if not applicable_rules:
243
+ return AccessDecision(
244
+ allowed=False,
245
+ reason="No applicable ABAC rules found",
246
+ applied_rules=[],
247
+ )
248
+
249
+ # Sort by priority (higher first)
250
+ applicable_rules.sort(key=lambda r: r.priority, reverse=True)
251
+
252
+ # Evaluate rules with conditions
253
+ for rule in applicable_rules:
254
+ if rule.conditions:
255
+ try:
256
+ # Evaluate ABAC conditions
257
+ cond_type = rule.conditions.get("type", "")
258
+ cond_value = rule.conditions.get("value", {})
259
+
260
+ result = self.condition_evaluator.evaluate(
261
+ cond_type, cond_value, context
262
+ )
263
+
264
+ if result:
265
+ # Conditions met - apply rule effect
266
+ if rule.effect == PermissionEffect.ALLOW:
267
+ return AccessDecision(
268
+ allowed=True,
269
+ reason=f"ABAC rule {rule.id} granted access",
270
+ applied_rules=[rule.id],
271
+ )
272
+ elif rule.effect == PermissionEffect.DENY:
273
+ return AccessDecision(
274
+ allowed=False,
275
+ reason=f"ABAC rule {rule.id} denied access",
276
+ applied_rules=[rule.id],
277
+ )
278
+ except Exception as e:
279
+ self.logger.warning(f"Error evaluating ABAC rule {rule.id}: {e}")
280
+ continue
281
+ else:
282
+ # No conditions - basic RBAC-style rule
283
+ if rule.effect == PermissionEffect.ALLOW:
284
+ return AccessDecision(
285
+ allowed=True,
286
+ reason=f"ABAC rule {rule.id} granted access (no conditions)",
287
+ applied_rules=[rule.id],
288
+ )
289
+ elif rule.effect == PermissionEffect.DENY:
290
+ return AccessDecision(
291
+ allowed=False,
292
+ reason=f"ABAC rule {rule.id} denied access (no conditions)",
293
+ applied_rules=[rule.id],
294
+ )
295
+
296
+ # Default deny
297
+ return AccessDecision(
298
+ allowed=False,
299
+ reason="No matching ABAC allow rules",
300
+ applied_rules=[rule.id for rule in applicable_rules],
301
+ )
302
+
303
+ def supports_conditions(self) -> bool:
304
+ """ABAC evaluator fully supports complex conditions."""
305
+ return True
306
+
307
+ def _rule_applies_to_user(self, rule: PermissionRule, user: UserContext) -> bool:
308
+ """Check if a rule applies to a user (same as RBAC for basic filtering)."""
309
+ # Direct user assignment
310
+ if rule.user_id and rule.user_id == user.user_id:
311
+ return True
312
+
313
+ # Role-based assignment
314
+ if rule.role and rule.role in user.roles:
315
+ return True
316
+
317
+ # Tenant-based assignment
318
+ if rule.tenant_id and rule.tenant_id == user.tenant_id:
319
+ return True
320
+
321
+ # No restrictions means applies to all
322
+ if not rule.user_id and not rule.role and not rule.tenant_id:
323
+ return True
324
+
325
+ return False
326
+
327
+ def _build_context(
328
+ self, user: UserContext, runtime_context: Dict[str, Any]
329
+ ) -> Dict[str, Any]:
330
+ """Build evaluation context for ABAC."""
331
+ now = datetime.now(UTC)
332
+ context = {
333
+ "user": {
334
+ "user_id": user.user_id,
335
+ "tenant_id": user.tenant_id,
336
+ "email": user.email,
337
+ "roles": user.roles,
338
+ "attributes": user.attributes,
339
+ },
340
+ "runtime": runtime_context,
341
+ "timestamp": now,
342
+ }
343
+ return context
344
+
345
+
346
+ class HybridRuleEvaluator(RuleEvaluator):
347
+ """Hybrid evaluator that combines RBAC and ABAC evaluation.
348
+
349
+ This evaluator:
350
+ 1. Uses RBAC for basic rules without conditions
351
+ 2. Uses ABAC for complex conditional rules
352
+ 3. Provides seamless transition between evaluation strategies
353
+ """
354
+
355
+ def __init__(self):
356
+ """Initialize hybrid evaluator with both RBAC and ABAC."""
357
+ self.rbac_evaluator = RBACRuleEvaluator()
358
+ self.abac_evaluator = ABACRuleEvaluator()
359
+ self.logger = logging.getLogger(f"{__name__}.HybridRuleEvaluator")
360
+
361
+ def evaluate_rules(
362
+ self,
363
+ rules: List[PermissionRule],
364
+ user: UserContext,
365
+ resource_type: str,
366
+ resource_id: str,
367
+ permission: Union[NodePermission, WorkflowPermission],
368
+ runtime_context: Dict[str, Any],
369
+ ) -> AccessDecision:
370
+ """Evaluate rules using appropriate strategy based on rule complexity."""
371
+ # Separate rules by complexity
372
+ simple_rules = [rule for rule in rules if not rule.conditions]
373
+ complex_rules = [rule for rule in rules if rule.conditions]
374
+
375
+ # Track all decisions for final reasoning
376
+ all_decisions = []
377
+
378
+ # First evaluate complex ABAC rules (higher precedence)
379
+ if complex_rules:
380
+ abac_decision = self.abac_evaluator.evaluate_rules(
381
+ complex_rules,
382
+ user,
383
+ resource_type,
384
+ resource_id,
385
+ permission,
386
+ runtime_context,
387
+ )
388
+ all_decisions.append(("ABAC", abac_decision))
389
+
390
+ # If ABAC explicitly allows or denies, use that decision
391
+ if abac_decision.allowed:
392
+ return abac_decision
393
+ elif any("denied access" in abac_decision.reason for _ in [abac_decision]):
394
+ return abac_decision
395
+
396
+ # Then evaluate simple RBAC rules
397
+ if simple_rules:
398
+ rbac_decision = self.rbac_evaluator.evaluate_rules(
399
+ simple_rules,
400
+ user,
401
+ resource_type,
402
+ resource_id,
403
+ permission,
404
+ runtime_context,
405
+ )
406
+ all_decisions.append(("RBAC", rbac_decision))
407
+
408
+ if rbac_decision.allowed:
409
+ return rbac_decision
410
+
411
+ # Combine reasoning from all evaluations
412
+ combined_reason = "; ".join(
413
+ [f"{eval_type}: {decision.reason}" for eval_type, decision in all_decisions]
414
+ )
415
+
416
+ combined_applied_rules = [
417
+ rule_id
418
+ for _, decision in all_decisions
419
+ for rule_id in decision.applied_rules
420
+ ]
421
+
422
+ return AccessDecision(
423
+ allowed=False,
424
+ reason=f"Hybrid evaluation - {combined_reason or 'No applicable rules'}",
425
+ applied_rules=combined_applied_rules,
426
+ )
427
+
428
+ def supports_conditions(self) -> bool:
429
+ """Hybrid evaluator supports conditions via ABAC component."""
430
+ return True
431
+
432
+
433
+ # Factory function for easy evaluator creation
434
+ def create_rule_evaluator(strategy: str = "hybrid") -> RuleEvaluator:
435
+ """Create a rule evaluator based on strategy.
436
+
437
+ Args:
438
+ strategy: One of 'rbac', 'abac', or 'hybrid'
439
+
440
+ Returns:
441
+ Appropriate RuleEvaluator instance
442
+
443
+ Raises:
444
+ ValueError: If strategy is not recognized
445
+ """
446
+ strategy = strategy.lower()
447
+
448
+ if strategy == "rbac":
449
+ return RBACRuleEvaluator()
450
+ elif strategy == "abac":
451
+ return ABACRuleEvaluator()
452
+ elif strategy == "hybrid":
453
+ return HybridRuleEvaluator()
454
+ else:
455
+ raise ValueError(
456
+ f"Unknown strategy: {strategy}. Use 'rbac', 'abac', or 'hybrid'"
457
+ )
458
+
459
+
460
+ # Export components
461
+ __all__ = [
462
+ "RuleEvaluator",
463
+ "RBACRuleEvaluator",
464
+ "ABACRuleEvaluator",
465
+ "HybridRuleEvaluator",
466
+ "create_rule_evaluator",
467
+ ]