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,401 @@
1
+ """
2
+ Credential Manager Node for centralized credential handling.
3
+
4
+ This node provides enterprise-grade credential management with support for
5
+ multiple credential sources, validation, and secure handling.
6
+ """
7
+
8
+ import json
9
+ import os
10
+ import re
11
+ from datetime import datetime, timezone
12
+ from typing import Any, Dict, List, Literal, Optional
13
+
14
+ from kailash.nodes.base import Node, NodeParameter
15
+
16
+
17
+ class CredentialManagerNode(Node):
18
+ """
19
+ Node for centralized credential management with validation and masking.
20
+
21
+ Supports multiple credential sources:
22
+ - Environment variables
23
+ - JSON files
24
+ - AWS Secrets Manager (simulated)
25
+ - Azure Key Vault (simulated)
26
+ - HashiCorp Vault (simulated)
27
+
28
+ Example:
29
+ ```python
30
+ # Basic usage with environment variables
31
+ cred_node = CredentialManagerNode(
32
+ name="get_api_creds",
33
+ credential_name="openai_api",
34
+ credential_type="api_key"
35
+ )
36
+
37
+ # Advanced usage with multiple sources
38
+ cred_node = CredentialManagerNode(
39
+ name="get_db_creds",
40
+ credential_name="postgres_prod",
41
+ credential_type="database",
42
+ credential_sources=["vault", "env", "file"],
43
+ validate_on_fetch=True
44
+ )
45
+ ```
46
+ """
47
+
48
+ def __init__(
49
+ self,
50
+ credential_name: str,
51
+ credential_type: Literal[
52
+ "api_key", "oauth2", "database", "certificate", "basic_auth", "custom"
53
+ ] = "custom",
54
+ credential_sources: Optional[
55
+ List[Literal["env", "file", "vault", "aws_secrets", "azure_keyvault"]]
56
+ ] = None,
57
+ validate_on_fetch: bool = True,
58
+ mask_in_logs: bool = True,
59
+ cache_duration_seconds: Optional[int] = 300,
60
+ **kwargs,
61
+ ):
62
+ """
63
+ Initialize the CredentialManagerNode.
64
+
65
+ Args:
66
+ credential_name: Name/identifier of the credential to fetch
67
+ credential_type: Type of credential for validation
68
+ credential_sources: List of sources to check (default: ["env", "file"])
69
+ validate_on_fetch: Whether to validate credentials when fetched
70
+ mask_in_logs: Whether to mask credential values in logs
71
+ cache_duration_seconds: How long to cache credentials (None = no cache)
72
+ """
73
+ super().__init__(**kwargs)
74
+
75
+ self.credential_name = credential_name
76
+ self.credential_type = credential_type
77
+ self.credential_sources = credential_sources or ["env", "file"]
78
+ self.validate_on_fetch = validate_on_fetch
79
+ self.mask_in_logs = mask_in_logs
80
+ self.cache_duration_seconds = cache_duration_seconds
81
+
82
+ self._cache = {}
83
+ self._cache_timestamps = {}
84
+
85
+ # Define credential validation patterns
86
+ self._validation_patterns = {
87
+ "api_key": r"^[A-Za-z0-9\-_]{20,}$",
88
+ "oauth2": {
89
+ "client_id": r"^[A-Za-z0-9\-_]{10,}$",
90
+ "client_secret": r"^[A-Za-z0-9\-_]{20,}$",
91
+ },
92
+ "database": {
93
+ "host": r"^[A-Za-z0-9\-\._]+$",
94
+ "port": r"^\d{1,5}$",
95
+ "username": r"^[A-Za-z0-9\-_]+$",
96
+ "password": r".{8,}",
97
+ },
98
+ "basic_auth": {"username": r"^[A-Za-z0-9\-_@\.]+$", "password": r".{6,}"},
99
+ }
100
+
101
+ def _mask_value(self, value: str, mask_type: str = "partial") -> str:
102
+ """Mask sensitive credential values."""
103
+ if not value or not self.mask_in_logs:
104
+ return value
105
+
106
+ if mask_type == "full":
107
+ return "*" * len(value)
108
+ elif mask_type == "partial":
109
+ if len(value) <= 8:
110
+ return value[:2] + "*" * (len(value) - 2)
111
+ else:
112
+ return value[:4] + "*" * (len(value) - 8) + value[-4:]
113
+ return value
114
+
115
+ def _fetch_from_env(self, credential_name: str) -> Optional[Dict[str, Any]]:
116
+ """Fetch credentials from environment variables."""
117
+ # Try common patterns
118
+ prefixes = [credential_name.upper(), f"{credential_name.upper()}_", ""]
119
+
120
+ if self.credential_type == "api_key":
121
+ for prefix in prefixes:
122
+ key_names = [f"{prefix}API_KEY", f"{prefix}KEY", f"{prefix}TOKEN"]
123
+ for key_name in key_names:
124
+ if key_name in os.environ:
125
+ return {"api_key": os.environ[key_name]}
126
+
127
+ elif self.credential_type == "oauth2":
128
+ for prefix in prefixes:
129
+ client_id = os.environ.get(f"{prefix}CLIENT_ID")
130
+ client_secret = os.environ.get(f"{prefix}CLIENT_SECRET")
131
+ if client_id and client_secret:
132
+ return {
133
+ "client_id": client_id,
134
+ "client_secret": client_secret,
135
+ "token_url": os.environ.get(f"{prefix}TOKEN_URL", ""),
136
+ }
137
+
138
+ elif self.credential_type == "database":
139
+ for prefix in prefixes:
140
+ host = os.environ.get(f"{prefix}DB_HOST") or os.environ.get(
141
+ f"{prefix}HOST"
142
+ )
143
+ if host:
144
+ return {
145
+ "host": host,
146
+ "port": os.environ.get(f"{prefix}DB_PORT", "5432"),
147
+ "username": os.environ.get(f"{prefix}DB_USER")
148
+ or os.environ.get(f"{prefix}USER"),
149
+ "password": os.environ.get(f"{prefix}DB_PASSWORD")
150
+ or os.environ.get(f"{prefix}PASSWORD"),
151
+ "database": os.environ.get(f"{prefix}DB_NAME")
152
+ or os.environ.get(f"{prefix}DATABASE"),
153
+ }
154
+
155
+ elif self.credential_type == "basic_auth":
156
+ for prefix in prefixes:
157
+ username = os.environ.get(f"{prefix}USERNAME") or os.environ.get(
158
+ f"{prefix}USER"
159
+ )
160
+ password = os.environ.get(f"{prefix}PASSWORD") or os.environ.get(
161
+ f"{prefix}PASS"
162
+ )
163
+ if username and password:
164
+ return {"username": username, "password": password}
165
+
166
+ # Generic fetch for custom type
167
+ result = {}
168
+ prefix = credential_name.upper() + "_"
169
+ for key, value in os.environ.items():
170
+ if key.startswith(prefix):
171
+ result[key[len(prefix) :].lower()] = value
172
+
173
+ return result if result else None
174
+
175
+ def _fetch_from_file(self, credential_name: str) -> Optional[Dict[str, Any]]:
176
+ """Fetch credentials from JSON file."""
177
+ # Look in common credential file locations
178
+ search_paths = [
179
+ f".credentials/{credential_name}.json",
180
+ f"credentials/{credential_name}.json",
181
+ f".secrets/{credential_name}.json",
182
+ f"config/credentials/{credential_name}.json",
183
+ ".env.json",
184
+ ]
185
+
186
+ for path in search_paths:
187
+ if os.path.exists(path):
188
+ try:
189
+ with open(path, "r") as f:
190
+ data = json.load(f)
191
+ # If .env.json, look for the specific credential
192
+ if path == ".env.json" and credential_name in data:
193
+ return data[credential_name]
194
+ elif path != ".env.json":
195
+ return data
196
+ except Exception:
197
+ continue
198
+
199
+ return None
200
+
201
+ def _fetch_from_vault(self, credential_name: str) -> Optional[Dict[str, Any]]:
202
+ """Simulate fetching from HashiCorp Vault."""
203
+ # In production, this would use hvac client
204
+ # For now, return simulated data for testing
205
+ if credential_name == "test_vault_creds":
206
+ return {
207
+ "api_key": "vault_simulated_key_123456",
208
+ "metadata": {
209
+ "version": 1,
210
+ "created_at": datetime.now(timezone.utc).isoformat(),
211
+ },
212
+ }
213
+ return None
214
+
215
+ def _fetch_from_aws_secrets(self, credential_name: str) -> Optional[Dict[str, Any]]:
216
+ """Simulate fetching from AWS Secrets Manager."""
217
+ # In production, this would use boto3 client
218
+ # For now, return simulated data for testing
219
+ if credential_name == "test_aws_creds":
220
+ return {
221
+ "username": "aws_user",
222
+ "password": "aws_simulated_password_123",
223
+ "engine": "postgres",
224
+ "host": "test.rds.amazonaws.com",
225
+ }
226
+ return None
227
+
228
+ def _fetch_from_azure_keyvault(
229
+ self, credential_name: str
230
+ ) -> Optional[Dict[str, Any]]:
231
+ """Simulate fetching from Azure Key Vault."""
232
+ # In production, this would use azure-keyvault-secrets client
233
+ # For now, return simulated data for testing
234
+ if credential_name == "test_azure_creds":
235
+ return {
236
+ "client_id": "azure_client_123",
237
+ "client_secret": "azure_secret_456",
238
+ "tenant_id": "azure_tenant_789",
239
+ }
240
+ return None
241
+
242
+ def _validate_credential(self, credential: Dict[str, Any]) -> bool:
243
+ """Validate credential format based on type."""
244
+ if not self.validate_on_fetch:
245
+ return True
246
+
247
+ patterns = self._validation_patterns.get(self.credential_type)
248
+ if not patterns:
249
+ return True # No validation for custom type
250
+
251
+ if isinstance(patterns, str):
252
+ # Single pattern (e.g., api_key)
253
+ value = credential.get(self.credential_type) or credential.get("value")
254
+ if value and re.match(patterns, value):
255
+ return True
256
+ elif isinstance(patterns, dict):
257
+ # Multiple patterns (e.g., oauth2, database)
258
+ for field, pattern in patterns.items():
259
+ value = credential.get(field)
260
+ if value and not re.match(pattern, str(value)):
261
+ return False
262
+ return True
263
+
264
+ return False
265
+
266
+ def _is_cache_valid(self, credential_name: str) -> bool:
267
+ """Check if cached credential is still valid."""
268
+ if not self.cache_duration_seconds:
269
+ return False
270
+
271
+ if credential_name not in self._cache_timestamps:
272
+ return False
273
+
274
+ elapsed = (
275
+ datetime.now(timezone.utc) - self._cache_timestamps[credential_name]
276
+ ).total_seconds()
277
+ return elapsed < self.cache_duration_seconds
278
+
279
+ def run(self, **inputs) -> Dict[str, Any]:
280
+ """
281
+ Fetch and validate credentials from configured sources.
282
+
283
+ Returns:
284
+ Dict containing:
285
+ - credentials: The fetched credential data
286
+ - source: Which source provided the credentials
287
+ - validated: Whether credentials passed validation
288
+ - masked_display: Masked version for logging
289
+ - metadata: Additional information about the credentials
290
+ """
291
+ # Check cache first
292
+ if self._is_cache_valid(self.credential_name):
293
+ cached = self._cache[self.credential_name]
294
+ return {
295
+ "credentials": cached["credentials"],
296
+ "source": cached["source"],
297
+ "validated": cached["validated"],
298
+ "masked_display": cached["masked_display"],
299
+ "metadata": {**cached.get("metadata", {}), "from_cache": True},
300
+ }
301
+
302
+ # Try each source in order
303
+ credentials = None
304
+ source = None
305
+
306
+ source_methods = {
307
+ "env": self._fetch_from_env,
308
+ "file": self._fetch_from_file,
309
+ "vault": self._fetch_from_vault,
310
+ "aws_secrets": self._fetch_from_aws_secrets,
311
+ "azure_keyvault": self._fetch_from_azure_keyvault,
312
+ }
313
+
314
+ for src in self.credential_sources:
315
+ if src in source_methods:
316
+ try:
317
+ result = source_methods[src](self.credential_name)
318
+ if result:
319
+ credentials = result
320
+ source = src
321
+ break
322
+ except Exception as e:
323
+ # Log error but continue to next source
324
+ continue
325
+
326
+ if not credentials:
327
+ raise ValueError(
328
+ f"Credential '{self.credential_name}' not found in any configured source"
329
+ )
330
+
331
+ # Validate credentials
332
+ validated = self._validate_credential(credentials)
333
+
334
+ # Create masked display version
335
+ masked_display = {}
336
+ for key, value in credentials.items():
337
+ if isinstance(value, str) and key.lower() in [
338
+ "password",
339
+ "secret",
340
+ "key",
341
+ "token",
342
+ "api_key",
343
+ "client_secret",
344
+ ]:
345
+ masked_display[key] = self._mask_value(value)
346
+ else:
347
+ masked_display[key] = value
348
+
349
+ # Prepare result
350
+ result = {
351
+ "credentials": credentials,
352
+ "source": source,
353
+ "validated": validated,
354
+ "masked_display": masked_display,
355
+ "metadata": {
356
+ "credential_type": self.credential_type,
357
+ "fetched_at": datetime.now(timezone.utc).isoformat(),
358
+ "rotation_detected": False, # Placeholder for rotation detection
359
+ },
360
+ }
361
+
362
+ # Cache if enabled
363
+ if self.cache_duration_seconds:
364
+ self._cache[self.credential_name] = result
365
+ self._cache_timestamps[self.credential_name] = datetime.now(timezone.utc)
366
+
367
+ return result
368
+
369
+ def get_parameters(self) -> Dict[str, NodeParameter]:
370
+ """No input parameters required."""
371
+ return {}
372
+
373
+ def get_output_schema(self) -> Dict[str, NodeParameter]:
374
+ """Define output parameters."""
375
+ return {
376
+ "credentials": NodeParameter(
377
+ name="credentials", type=dict, description="The fetched credential data"
378
+ ),
379
+ "source": NodeParameter(
380
+ name="source",
381
+ type=str,
382
+ description="Which source provided the credentials",
383
+ ),
384
+ "validated": NodeParameter(
385
+ name="validated",
386
+ type=bool,
387
+ description="Whether credentials passed validation",
388
+ ),
389
+ "masked_display": NodeParameter(
390
+ name="masked_display",
391
+ type=dict,
392
+ description="Masked version for safe logging",
393
+ ),
394
+ "metadata": NodeParameter(
395
+ name="metadata", type=dict, description="Additional credential metadata"
396
+ ),
397
+ }
398
+
399
+ async def async_run(self, **kwargs) -> Dict[str, Any]:
400
+ """Async execution method for enterprise integration."""
401
+ return self.run(**kwargs)