kailash 0.3.2__py3-none-any.whl → 0.4.1__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.
- kailash/__init__.py +33 -1
- kailash/access_control/__init__.py +129 -0
- kailash/access_control/managers.py +461 -0
- kailash/access_control/rule_evaluators.py +467 -0
- kailash/access_control_abac.py +825 -0
- kailash/config/__init__.py +27 -0
- kailash/config/database_config.py +359 -0
- kailash/database/__init__.py +28 -0
- kailash/database/execution_pipeline.py +499 -0
- kailash/middleware/__init__.py +306 -0
- kailash/middleware/auth/__init__.py +33 -0
- kailash/middleware/auth/access_control.py +436 -0
- kailash/middleware/auth/auth_manager.py +422 -0
- kailash/middleware/auth/jwt_auth.py +477 -0
- kailash/middleware/auth/kailash_jwt_auth.py +616 -0
- kailash/middleware/communication/__init__.py +37 -0
- kailash/middleware/communication/ai_chat.py +989 -0
- kailash/middleware/communication/api_gateway.py +802 -0
- kailash/middleware/communication/events.py +470 -0
- kailash/middleware/communication/realtime.py +710 -0
- kailash/middleware/core/__init__.py +21 -0
- kailash/middleware/core/agent_ui.py +890 -0
- kailash/middleware/core/schema.py +643 -0
- kailash/middleware/core/workflows.py +396 -0
- kailash/middleware/database/__init__.py +63 -0
- kailash/middleware/database/base.py +113 -0
- kailash/middleware/database/base_models.py +525 -0
- kailash/middleware/database/enums.py +106 -0
- kailash/middleware/database/migrations.py +12 -0
- kailash/{api/database.py → middleware/database/models.py} +183 -291
- kailash/middleware/database/repositories.py +685 -0
- kailash/middleware/database/session_manager.py +19 -0
- kailash/middleware/mcp/__init__.py +38 -0
- kailash/middleware/mcp/client_integration.py +585 -0
- kailash/middleware/mcp/enhanced_server.py +576 -0
- kailash/nodes/__init__.py +27 -3
- kailash/nodes/admin/__init__.py +42 -0
- kailash/nodes/admin/audit_log.py +794 -0
- kailash/nodes/admin/permission_check.py +864 -0
- kailash/nodes/admin/role_management.py +823 -0
- kailash/nodes/admin/security_event.py +1523 -0
- kailash/nodes/admin/user_management.py +944 -0
- kailash/nodes/ai/a2a.py +24 -7
- kailash/nodes/ai/ai_providers.py +248 -40
- kailash/nodes/ai/embedding_generator.py +11 -11
- kailash/nodes/ai/intelligent_agent_orchestrator.py +99 -11
- kailash/nodes/ai/llm_agent.py +436 -5
- kailash/nodes/ai/self_organizing.py +85 -10
- kailash/nodes/ai/vision_utils.py +148 -0
- kailash/nodes/alerts/__init__.py +26 -0
- kailash/nodes/alerts/base.py +234 -0
- kailash/nodes/alerts/discord.py +499 -0
- kailash/nodes/api/auth.py +287 -6
- kailash/nodes/api/rest.py +151 -0
- kailash/nodes/auth/__init__.py +17 -0
- kailash/nodes/auth/directory_integration.py +1228 -0
- kailash/nodes/auth/enterprise_auth_provider.py +1328 -0
- kailash/nodes/auth/mfa.py +2338 -0
- kailash/nodes/auth/risk_assessment.py +872 -0
- kailash/nodes/auth/session_management.py +1093 -0
- kailash/nodes/auth/sso.py +1040 -0
- kailash/nodes/base.py +344 -13
- kailash/nodes/base_cycle_aware.py +4 -2
- kailash/nodes/base_with_acl.py +1 -1
- kailash/nodes/code/python.py +283 -10
- kailash/nodes/compliance/__init__.py +9 -0
- kailash/nodes/compliance/data_retention.py +1888 -0
- kailash/nodes/compliance/gdpr.py +2004 -0
- kailash/nodes/data/__init__.py +22 -2
- kailash/nodes/data/async_connection.py +469 -0
- kailash/nodes/data/async_sql.py +757 -0
- kailash/nodes/data/async_vector.py +598 -0
- kailash/nodes/data/readers.py +767 -0
- kailash/nodes/data/retrieval.py +360 -1
- kailash/nodes/data/sharepoint_graph.py +397 -21
- kailash/nodes/data/sql.py +94 -5
- kailash/nodes/data/streaming.py +68 -8
- kailash/nodes/data/vector_db.py +54 -4
- kailash/nodes/enterprise/__init__.py +13 -0
- kailash/nodes/enterprise/batch_processor.py +741 -0
- kailash/nodes/enterprise/data_lineage.py +497 -0
- kailash/nodes/logic/convergence.py +31 -9
- kailash/nodes/logic/operations.py +14 -3
- kailash/nodes/mixins/__init__.py +8 -0
- kailash/nodes/mixins/event_emitter.py +201 -0
- kailash/nodes/mixins/mcp.py +9 -4
- kailash/nodes/mixins/security.py +165 -0
- kailash/nodes/monitoring/__init__.py +7 -0
- kailash/nodes/monitoring/performance_benchmark.py +2497 -0
- kailash/nodes/rag/__init__.py +284 -0
- kailash/nodes/rag/advanced.py +1615 -0
- kailash/nodes/rag/agentic.py +773 -0
- kailash/nodes/rag/conversational.py +999 -0
- kailash/nodes/rag/evaluation.py +875 -0
- kailash/nodes/rag/federated.py +1188 -0
- kailash/nodes/rag/graph.py +721 -0
- kailash/nodes/rag/multimodal.py +671 -0
- kailash/nodes/rag/optimized.py +933 -0
- kailash/nodes/rag/privacy.py +1059 -0
- kailash/nodes/rag/query_processing.py +1335 -0
- kailash/nodes/rag/realtime.py +764 -0
- kailash/nodes/rag/registry.py +547 -0
- kailash/nodes/rag/router.py +837 -0
- kailash/nodes/rag/similarity.py +1854 -0
- kailash/nodes/rag/strategies.py +566 -0
- kailash/nodes/rag/workflows.py +575 -0
- kailash/nodes/security/__init__.py +19 -0
- kailash/nodes/security/abac_evaluator.py +1411 -0
- kailash/nodes/security/audit_log.py +103 -0
- kailash/nodes/security/behavior_analysis.py +1893 -0
- kailash/nodes/security/credential_manager.py +401 -0
- kailash/nodes/security/rotating_credentials.py +760 -0
- kailash/nodes/security/security_event.py +133 -0
- kailash/nodes/security/threat_detection.py +1103 -0
- kailash/nodes/testing/__init__.py +9 -0
- kailash/nodes/testing/credential_testing.py +499 -0
- kailash/nodes/transform/__init__.py +10 -2
- kailash/nodes/transform/chunkers.py +592 -1
- kailash/nodes/transform/processors.py +484 -14
- kailash/nodes/validation.py +321 -0
- kailash/runtime/access_controlled.py +1 -1
- kailash/runtime/async_local.py +41 -7
- kailash/runtime/docker.py +1 -1
- kailash/runtime/local.py +474 -55
- kailash/runtime/parallel.py +1 -1
- kailash/runtime/parallel_cyclic.py +1 -1
- kailash/runtime/testing.py +210 -2
- kailash/security.py +1 -1
- kailash/utils/migrations/__init__.py +25 -0
- kailash/utils/migrations/generator.py +433 -0
- kailash/utils/migrations/models.py +231 -0
- kailash/utils/migrations/runner.py +489 -0
- kailash/utils/secure_logging.py +342 -0
- kailash/workflow/__init__.py +16 -0
- kailash/workflow/cyclic_runner.py +3 -4
- kailash/workflow/graph.py +70 -2
- kailash/workflow/resilience.py +249 -0
- kailash/workflow/templates.py +726 -0
- {kailash-0.3.2.dist-info → kailash-0.4.1.dist-info}/METADATA +256 -20
- kailash-0.4.1.dist-info/RECORD +227 -0
- kailash/api/__init__.py +0 -17
- kailash/api/__main__.py +0 -6
- kailash/api/studio_secure.py +0 -893
- kailash/mcp/__main__.py +0 -13
- kailash/mcp/server_new.py +0 -336
- kailash/mcp/servers/__init__.py +0 -12
- kailash-0.3.2.dist-info/RECORD +0 -136
- {kailash-0.3.2.dist-info → kailash-0.4.1.dist-info}/WHEEL +0 -0
- {kailash-0.3.2.dist-info → kailash-0.4.1.dist-info}/entry_points.txt +0 -0
- {kailash-0.3.2.dist-info → kailash-0.4.1.dist-info}/licenses/LICENSE +0 -0
- {kailash-0.3.2.dist-info → kailash-0.4.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,9 @@
|
|
1
|
+
"""Testing nodes for the Kailash SDK.
|
2
|
+
|
3
|
+
This module provides specialized nodes for testing workflows, including
|
4
|
+
credential testing, mock data generation, and scenario simulation.
|
5
|
+
"""
|
6
|
+
|
7
|
+
from .credential_testing import CredentialTestingNode
|
8
|
+
|
9
|
+
__all__ = ["CredentialTestingNode"]
|
@@ -0,0 +1,499 @@
|
|
1
|
+
"""Credential testing node for workflow authentication testing.
|
2
|
+
|
3
|
+
This module provides a specialized node for testing credential flows in workflows,
|
4
|
+
including mock credential generation, validation simulation, and error scenarios.
|
5
|
+
"""
|
6
|
+
|
7
|
+
import base64
|
8
|
+
import time
|
9
|
+
from datetime import datetime, timedelta, timezone
|
10
|
+
from typing import Any
|
11
|
+
from uuid import uuid4
|
12
|
+
|
13
|
+
from kailash.nodes.base import Node, NodeParameter, register_node
|
14
|
+
from kailash.sdk_exceptions import NodeExecutionError, NodeValidationError
|
15
|
+
|
16
|
+
|
17
|
+
@register_node()
|
18
|
+
class CredentialTestingNode(Node):
|
19
|
+
"""Node for testing credential flows in workflows.
|
20
|
+
|
21
|
+
This node simulates various credential scenarios for testing authentication
|
22
|
+
workflows without requiring actual external services. It can generate mock
|
23
|
+
credentials, simulate validation, test expiration scenarios, and inject
|
24
|
+
various error conditions.
|
25
|
+
|
26
|
+
Design Purpose:
|
27
|
+
- Enable comprehensive testing of authentication flows
|
28
|
+
- Simulate various credential types and scenarios
|
29
|
+
- Test error handling and edge cases
|
30
|
+
- Validate security patterns in workflows
|
31
|
+
|
32
|
+
Use Cases:
|
33
|
+
- Unit testing authentication workflows
|
34
|
+
- Integration testing with mock services
|
35
|
+
- Security pattern validation
|
36
|
+
- Error scenario testing
|
37
|
+
- Token lifecycle simulation
|
38
|
+
|
39
|
+
Example:
|
40
|
+
>>> # Test OAuth2 token expiration
|
41
|
+
>>> tester = CredentialTestingNode()
|
42
|
+
>>> result = tester.run(
|
43
|
+
... credential_type='oauth2',
|
44
|
+
... scenario='expired',
|
45
|
+
... mock_data={'client_id': 'test_client'}
|
46
|
+
... )
|
47
|
+
>>> assert result['expired'] is True
|
48
|
+
>>> assert 'expired_token' in result['error_details']
|
49
|
+
>>>
|
50
|
+
>>> # Test successful API key validation
|
51
|
+
>>> result = tester.run(
|
52
|
+
... credential_type='api_key',
|
53
|
+
... scenario='success',
|
54
|
+
... validation_rules={'key_length': 32}
|
55
|
+
... )
|
56
|
+
>>> assert result['valid'] is True
|
57
|
+
>>> assert len(result['credentials']['api_key']) == 32
|
58
|
+
"""
|
59
|
+
|
60
|
+
def get_parameters(self) -> dict[str, NodeParameter]:
|
61
|
+
"""Define the parameters this node accepts.
|
62
|
+
|
63
|
+
Returns:
|
64
|
+
Dictionary of parameter definitions
|
65
|
+
"""
|
66
|
+
return {
|
67
|
+
"credential_type": NodeParameter(
|
68
|
+
name="credential_type",
|
69
|
+
type=str,
|
70
|
+
required=True,
|
71
|
+
description="Type of credential to test: oauth2, api_key, basic, jwt",
|
72
|
+
),
|
73
|
+
"scenario": NodeParameter(
|
74
|
+
name="scenario",
|
75
|
+
type=str,
|
76
|
+
required=True,
|
77
|
+
default="success",
|
78
|
+
description="Test scenario: success, expired, invalid, network_error, rate_limit",
|
79
|
+
),
|
80
|
+
"mock_data": NodeParameter(
|
81
|
+
name="mock_data",
|
82
|
+
type=dict,
|
83
|
+
required=False,
|
84
|
+
default={},
|
85
|
+
description="Custom mock data for the test scenario",
|
86
|
+
),
|
87
|
+
"validation_rules": NodeParameter(
|
88
|
+
name="validation_rules",
|
89
|
+
type=dict,
|
90
|
+
required=False,
|
91
|
+
default={},
|
92
|
+
description="Rules to validate generated credentials",
|
93
|
+
),
|
94
|
+
"delay_ms": NodeParameter(
|
95
|
+
name="delay_ms",
|
96
|
+
type=int,
|
97
|
+
required=False,
|
98
|
+
default=0,
|
99
|
+
description="Simulated network delay in milliseconds",
|
100
|
+
),
|
101
|
+
"ttl_seconds": NodeParameter(
|
102
|
+
name="ttl_seconds",
|
103
|
+
type=int,
|
104
|
+
required=False,
|
105
|
+
default=3600,
|
106
|
+
description="Time-to-live for generated credentials in seconds",
|
107
|
+
),
|
108
|
+
}
|
109
|
+
|
110
|
+
def get_output_schema(self) -> dict[str, NodeParameter]:
|
111
|
+
"""Define the output schema for this node.
|
112
|
+
|
113
|
+
Returns:
|
114
|
+
Dictionary of output parameter definitions
|
115
|
+
"""
|
116
|
+
return {
|
117
|
+
"valid": NodeParameter(
|
118
|
+
name="valid",
|
119
|
+
type=bool,
|
120
|
+
required=True,
|
121
|
+
description="Whether the credential validation succeeded",
|
122
|
+
),
|
123
|
+
"credentials": NodeParameter(
|
124
|
+
name="credentials",
|
125
|
+
type=dict,
|
126
|
+
required=False,
|
127
|
+
description="Generated mock credentials (if successful)",
|
128
|
+
),
|
129
|
+
"headers": NodeParameter(
|
130
|
+
name="headers",
|
131
|
+
type=dict,
|
132
|
+
required=False,
|
133
|
+
description="HTTP headers for the credentials (if applicable)",
|
134
|
+
),
|
135
|
+
"expires_at": NodeParameter(
|
136
|
+
name="expires_at",
|
137
|
+
type=str,
|
138
|
+
required=False,
|
139
|
+
description="ISO format expiration timestamp",
|
140
|
+
),
|
141
|
+
"expired": NodeParameter(
|
142
|
+
name="expired",
|
143
|
+
type=bool,
|
144
|
+
required=False,
|
145
|
+
description="Whether the credentials are expired",
|
146
|
+
),
|
147
|
+
"error": NodeParameter(
|
148
|
+
name="error",
|
149
|
+
type=str,
|
150
|
+
required=False,
|
151
|
+
description="Error message if validation failed",
|
152
|
+
),
|
153
|
+
"error_details": NodeParameter(
|
154
|
+
name="error_details",
|
155
|
+
type=dict,
|
156
|
+
required=False,
|
157
|
+
description="Detailed error information",
|
158
|
+
),
|
159
|
+
"metadata": NodeParameter(
|
160
|
+
name="metadata",
|
161
|
+
type=dict,
|
162
|
+
required=True,
|
163
|
+
description="Test metadata including scenario details",
|
164
|
+
),
|
165
|
+
}
|
166
|
+
|
167
|
+
def run(self, **kwargs) -> dict[str, Any]:
|
168
|
+
"""Execute credential testing based on the specified scenario.
|
169
|
+
|
170
|
+
Args:
|
171
|
+
credential_type: Type of credential to test
|
172
|
+
scenario: Test scenario to simulate
|
173
|
+
mock_data: Custom mock data
|
174
|
+
validation_rules: Validation rules to apply
|
175
|
+
delay_ms: Simulated network delay
|
176
|
+
ttl_seconds: Credential time-to-live
|
177
|
+
|
178
|
+
Returns:
|
179
|
+
Dictionary containing test results and generated credentials
|
180
|
+
|
181
|
+
Raises:
|
182
|
+
NodeValidationError: If parameters are invalid
|
183
|
+
NodeExecutionError: If simulating execution errors
|
184
|
+
"""
|
185
|
+
credential_type = kwargs.get("credential_type")
|
186
|
+
scenario = kwargs.get("scenario", "success")
|
187
|
+
mock_data = kwargs.get("mock_data", {})
|
188
|
+
validation_rules = kwargs.get("validation_rules", {})
|
189
|
+
delay_ms = kwargs.get("delay_ms", 0)
|
190
|
+
ttl_seconds = kwargs.get("ttl_seconds", 3600)
|
191
|
+
|
192
|
+
# Validate credential type
|
193
|
+
valid_types = ["oauth2", "api_key", "basic", "jwt"]
|
194
|
+
if credential_type not in valid_types:
|
195
|
+
raise NodeValidationError(
|
196
|
+
f"Invalid credential_type: {credential_type}. Must be one of: {valid_types}"
|
197
|
+
)
|
198
|
+
|
199
|
+
# Simulate network delay
|
200
|
+
if delay_ms > 0:
|
201
|
+
time.sleep(delay_ms / 1000.0)
|
202
|
+
|
203
|
+
# Handle different scenarios
|
204
|
+
if scenario == "network_error":
|
205
|
+
raise NodeExecutionError("Simulated network error: Connection timeout")
|
206
|
+
|
207
|
+
elif scenario == "rate_limit":
|
208
|
+
return {
|
209
|
+
"valid": False,
|
210
|
+
"error": "Rate limit exceeded",
|
211
|
+
"error_details": {
|
212
|
+
"error_code": "RATE_LIMIT_EXCEEDED",
|
213
|
+
"retry_after": 60,
|
214
|
+
"limit": 100,
|
215
|
+
"remaining": 0,
|
216
|
+
},
|
217
|
+
"metadata": {
|
218
|
+
"scenario": scenario,
|
219
|
+
"credential_type": credential_type,
|
220
|
+
"timestamp": datetime.now(timezone.utc).isoformat(),
|
221
|
+
},
|
222
|
+
}
|
223
|
+
|
224
|
+
# Generate credentials based on type
|
225
|
+
if credential_type == "oauth2":
|
226
|
+
result = self._generate_oauth2_credentials(
|
227
|
+
scenario, mock_data, validation_rules, ttl_seconds
|
228
|
+
)
|
229
|
+
elif credential_type == "api_key":
|
230
|
+
result = self._generate_api_key_credentials(
|
231
|
+
scenario, mock_data, validation_rules, ttl_seconds
|
232
|
+
)
|
233
|
+
elif credential_type == "basic":
|
234
|
+
result = self._generate_basic_credentials(
|
235
|
+
scenario, mock_data, validation_rules, ttl_seconds
|
236
|
+
)
|
237
|
+
elif credential_type == "jwt":
|
238
|
+
result = self._generate_jwt_credentials(
|
239
|
+
scenario, mock_data, validation_rules, ttl_seconds
|
240
|
+
)
|
241
|
+
|
242
|
+
# Add common metadata
|
243
|
+
result["metadata"] = {
|
244
|
+
"scenario": scenario,
|
245
|
+
"credential_type": credential_type,
|
246
|
+
"timestamp": datetime.now(timezone.utc).isoformat(),
|
247
|
+
"test_id": str(uuid4()),
|
248
|
+
}
|
249
|
+
|
250
|
+
return result
|
251
|
+
|
252
|
+
def _generate_oauth2_credentials(
|
253
|
+
self, scenario: str, mock_data: dict, validation_rules: dict, ttl_seconds: int
|
254
|
+
) -> dict[str, Any]:
|
255
|
+
"""Generate OAuth2 mock credentials."""
|
256
|
+
now = datetime.now(timezone.utc)
|
257
|
+
|
258
|
+
if scenario == "expired":
|
259
|
+
# Generate expired token
|
260
|
+
expires_at = now - timedelta(hours=1)
|
261
|
+
return {
|
262
|
+
"valid": False,
|
263
|
+
"expired": True,
|
264
|
+
"error": "Token expired",
|
265
|
+
"error_details": {
|
266
|
+
"error_code": "expired_token",
|
267
|
+
"expired_at": expires_at.isoformat(),
|
268
|
+
},
|
269
|
+
"expires_at": expires_at.isoformat(),
|
270
|
+
}
|
271
|
+
|
272
|
+
elif scenario == "invalid":
|
273
|
+
return {
|
274
|
+
"valid": False,
|
275
|
+
"error": "Invalid client credentials",
|
276
|
+
"error_details": {
|
277
|
+
"error_code": "invalid_client",
|
278
|
+
"error_description": "Client authentication failed",
|
279
|
+
},
|
280
|
+
}
|
281
|
+
|
282
|
+
else: # success scenario
|
283
|
+
expires_at = now + timedelta(seconds=ttl_seconds)
|
284
|
+
access_token = f"mock_access_{uuid4().hex[:16]}"
|
285
|
+
refresh_token = f"mock_refresh_{uuid4().hex[:16]}"
|
286
|
+
|
287
|
+
credentials = {
|
288
|
+
"access_token": access_token,
|
289
|
+
"token_type": "Bearer",
|
290
|
+
"expires_in": ttl_seconds,
|
291
|
+
"refresh_token": refresh_token,
|
292
|
+
"scope": mock_data.get("scope", "read write"),
|
293
|
+
}
|
294
|
+
|
295
|
+
# Apply custom mock data
|
296
|
+
credentials.update(mock_data)
|
297
|
+
|
298
|
+
# Validate if rules provided
|
299
|
+
if validation_rules:
|
300
|
+
valid, error = self._validate_oauth2(credentials, validation_rules)
|
301
|
+
if not valid:
|
302
|
+
return {
|
303
|
+
"valid": False,
|
304
|
+
"error": error,
|
305
|
+
"error_details": {"validation_failed": True},
|
306
|
+
}
|
307
|
+
|
308
|
+
return {
|
309
|
+
"valid": True,
|
310
|
+
"credentials": credentials,
|
311
|
+
"headers": {"Authorization": f"Bearer {access_token}"},
|
312
|
+
"expires_at": expires_at.isoformat(),
|
313
|
+
"expired": False,
|
314
|
+
}
|
315
|
+
|
316
|
+
def _generate_api_key_credentials(
|
317
|
+
self, scenario: str, mock_data: dict, validation_rules: dict, ttl_seconds: int
|
318
|
+
) -> dict[str, Any]:
|
319
|
+
"""Generate API key mock credentials."""
|
320
|
+
if scenario == "invalid":
|
321
|
+
return {
|
322
|
+
"valid": False,
|
323
|
+
"error": "Invalid API key",
|
324
|
+
"error_details": {
|
325
|
+
"error_code": "invalid_api_key",
|
326
|
+
"status_code": 401,
|
327
|
+
},
|
328
|
+
}
|
329
|
+
|
330
|
+
else: # success or expired scenario
|
331
|
+
key_length = validation_rules.get("key_length", 32)
|
332
|
+
api_key = f"sk_test_{uuid4().hex[:key_length]}"
|
333
|
+
|
334
|
+
credentials = {
|
335
|
+
"api_key": api_key,
|
336
|
+
"key_prefix": mock_data.get("key_prefix", "sk_test"),
|
337
|
+
}
|
338
|
+
|
339
|
+
# Apply custom mock data
|
340
|
+
credentials.update(mock_data)
|
341
|
+
|
342
|
+
header_name = validation_rules.get("header_name", "X-API-Key")
|
343
|
+
|
344
|
+
if scenario == "expired":
|
345
|
+
return {
|
346
|
+
"valid": False,
|
347
|
+
"expired": True,
|
348
|
+
"error": "API key expired",
|
349
|
+
"error_details": {
|
350
|
+
"error_code": "expired_api_key",
|
351
|
+
"key_id": api_key[:12] + "...",
|
352
|
+
},
|
353
|
+
"credentials": credentials,
|
354
|
+
}
|
355
|
+
|
356
|
+
return {
|
357
|
+
"valid": True,
|
358
|
+
"credentials": credentials,
|
359
|
+
"headers": {header_name: api_key},
|
360
|
+
"expired": False,
|
361
|
+
}
|
362
|
+
|
363
|
+
def _generate_basic_credentials(
|
364
|
+
self, scenario: str, mock_data: dict, validation_rules: dict, ttl_seconds: int
|
365
|
+
) -> dict[str, Any]:
|
366
|
+
"""Generate Basic Auth mock credentials."""
|
367
|
+
username = mock_data.get("username", "test_user")
|
368
|
+
password = mock_data.get("password", "test_pass123")
|
369
|
+
|
370
|
+
if scenario == "invalid":
|
371
|
+
return {
|
372
|
+
"valid": False,
|
373
|
+
"error": "Invalid username or password",
|
374
|
+
"error_details": {
|
375
|
+
"error_code": "invalid_credentials",
|
376
|
+
"status_code": 401,
|
377
|
+
},
|
378
|
+
}
|
379
|
+
|
380
|
+
else: # success scenario
|
381
|
+
credentials = {
|
382
|
+
"username": username,
|
383
|
+
"password": password,
|
384
|
+
}
|
385
|
+
|
386
|
+
# Generate Basic Auth header
|
387
|
+
auth_string = f"{username}:{password}"
|
388
|
+
encoded_auth = base64.b64encode(auth_string.encode()).decode()
|
389
|
+
|
390
|
+
return {
|
391
|
+
"valid": True,
|
392
|
+
"credentials": credentials,
|
393
|
+
"headers": {"Authorization": f"Basic {encoded_auth}"},
|
394
|
+
"expired": False,
|
395
|
+
}
|
396
|
+
|
397
|
+
def _generate_jwt_credentials(
|
398
|
+
self, scenario: str, mock_data: dict, validation_rules: dict, ttl_seconds: int
|
399
|
+
) -> dict[str, Any]:
|
400
|
+
"""Generate JWT mock credentials."""
|
401
|
+
now = datetime.now(timezone.utc)
|
402
|
+
|
403
|
+
# Mock JWT structure (not cryptographically valid)
|
404
|
+
header = {"alg": "HS256", "typ": "JWT"}
|
405
|
+
|
406
|
+
if scenario == "expired":
|
407
|
+
payload = {
|
408
|
+
"sub": mock_data.get("subject", "1234567890"),
|
409
|
+
"name": mock_data.get("name", "Test User"),
|
410
|
+
"iat": int((now - timedelta(hours=2)).timestamp()),
|
411
|
+
"exp": int((now - timedelta(hours=1)).timestamp()),
|
412
|
+
}
|
413
|
+
|
414
|
+
return {
|
415
|
+
"valid": False,
|
416
|
+
"expired": True,
|
417
|
+
"error": "JWT token expired",
|
418
|
+
"error_details": {
|
419
|
+
"error_code": "jwt_expired",
|
420
|
+
"expired_at": datetime.fromtimestamp(
|
421
|
+
payload["exp"], tz=timezone.utc
|
422
|
+
).isoformat(),
|
423
|
+
},
|
424
|
+
}
|
425
|
+
|
426
|
+
elif scenario == "invalid":
|
427
|
+
return {
|
428
|
+
"valid": False,
|
429
|
+
"error": "Invalid JWT signature",
|
430
|
+
"error_details": {
|
431
|
+
"error_code": "invalid_signature",
|
432
|
+
"status_code": 401,
|
433
|
+
},
|
434
|
+
}
|
435
|
+
|
436
|
+
else: # success scenario
|
437
|
+
expires_at = now + timedelta(seconds=ttl_seconds)
|
438
|
+
payload = {
|
439
|
+
"sub": mock_data.get("subject", "1234567890"),
|
440
|
+
"name": mock_data.get("name", "Test User"),
|
441
|
+
"iat": int(now.timestamp()),
|
442
|
+
"exp": int(expires_at.timestamp()),
|
443
|
+
"iss": mock_data.get("issuer", "test_issuer"),
|
444
|
+
"aud": mock_data.get("audience", "test_audience"),
|
445
|
+
}
|
446
|
+
|
447
|
+
# Apply additional claims from mock_data
|
448
|
+
for key, value in mock_data.items():
|
449
|
+
if key not in payload:
|
450
|
+
payload[key] = value
|
451
|
+
|
452
|
+
# Create mock JWT (base64 encoded parts separated by dots)
|
453
|
+
header_b64 = (
|
454
|
+
base64.urlsafe_b64encode(str(header).encode()).decode().rstrip("=")
|
455
|
+
)
|
456
|
+
payload_b64 = (
|
457
|
+
base64.urlsafe_b64encode(str(payload).encode()).decode().rstrip("=")
|
458
|
+
)
|
459
|
+
signature = uuid4().hex[:32]
|
460
|
+
|
461
|
+
jwt_token = f"{header_b64}.{payload_b64}.{signature}"
|
462
|
+
|
463
|
+
credentials = {
|
464
|
+
"jwt": jwt_token,
|
465
|
+
"header": header,
|
466
|
+
"payload": payload,
|
467
|
+
}
|
468
|
+
|
469
|
+
return {
|
470
|
+
"valid": True,
|
471
|
+
"credentials": credentials,
|
472
|
+
"headers": {"Authorization": f"Bearer {jwt_token}"},
|
473
|
+
"expires_at": expires_at.isoformat(),
|
474
|
+
"expired": False,
|
475
|
+
}
|
476
|
+
|
477
|
+
def _validate_oauth2(self, credentials: dict, rules: dict) -> tuple[bool, str]:
|
478
|
+
"""Validate OAuth2 credentials against rules."""
|
479
|
+
# Check required fields
|
480
|
+
required_fields = rules.get("required_fields", ["access_token", "token_type"])
|
481
|
+
for field in required_fields:
|
482
|
+
if field not in credentials:
|
483
|
+
return False, f"Missing required field: {field}"
|
484
|
+
|
485
|
+
# Check token format
|
486
|
+
if "token_format" in rules:
|
487
|
+
token = credentials.get("access_token", "")
|
488
|
+
if not token.startswith(rules["token_format"]):
|
489
|
+
return False, "Invalid token format"
|
490
|
+
|
491
|
+
# Check scope requirements
|
492
|
+
if "required_scopes" in rules:
|
493
|
+
granted_scopes = set(credentials.get("scope", "").split())
|
494
|
+
required_scopes = set(rules["required_scopes"])
|
495
|
+
if not required_scopes.issubset(granted_scopes):
|
496
|
+
missing = required_scopes - granted_scopes
|
497
|
+
return False, f"Missing required scopes: {missing}"
|
498
|
+
|
499
|
+
return True, ""
|
@@ -1,12 +1,17 @@
|
|
1
1
|
"""Transform processing nodes for the Kailash SDK."""
|
2
2
|
|
3
|
-
from kailash.nodes.transform.chunkers import
|
3
|
+
from kailash.nodes.transform.chunkers import (
|
4
|
+
HierarchicalChunkerNode,
|
5
|
+
SemanticChunkerNode,
|
6
|
+
StatisticalChunkerNode,
|
7
|
+
)
|
4
8
|
from kailash.nodes.transform.formatters import (
|
5
9
|
ChunkTextExtractorNode,
|
6
10
|
ContextFormatterNode,
|
7
11
|
QueryTextWrapperNode,
|
8
12
|
)
|
9
13
|
from kailash.nodes.transform.processors import (
|
14
|
+
ContextualCompressorNode,
|
10
15
|
DataTransformer,
|
11
16
|
Filter,
|
12
17
|
FilterNode,
|
@@ -15,12 +20,15 @@ from kailash.nodes.transform.processors import (
|
|
15
20
|
)
|
16
21
|
|
17
22
|
__all__ = [
|
23
|
+
"ContextualCompressorNode",
|
24
|
+
"DataTransformer",
|
18
25
|
"Filter",
|
19
26
|
"FilterNode",
|
20
27
|
"Map",
|
21
28
|
"Sort",
|
22
|
-
"DataTransformer",
|
23
29
|
"HierarchicalChunkerNode",
|
30
|
+
"SemanticChunkerNode",
|
31
|
+
"StatisticalChunkerNode",
|
24
32
|
"ChunkTextExtractorNode",
|
25
33
|
"QueryTextWrapperNode",
|
26
34
|
"ContextFormatterNode",
|