kailash 0.4.1__py3-none-any.whl → 0.4.2__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 +3 -4
- kailash/middleware/__init__.py +4 -2
- kailash/middleware/auth/__init__.py +55 -12
- kailash/middleware/auth/exceptions.py +80 -0
- kailash/middleware/auth/jwt_auth.py +265 -123
- kailash/middleware/auth/models.py +137 -0
- kailash/middleware/auth/utils.py +257 -0
- kailash/middleware/communication/api_gateway.py +49 -7
- kailash/middleware/core/agent_ui.py +108 -1
- kailash/middleware/mcp/enhanced_server.py +2 -2
- kailash/nodes/code/python.py +18 -0
- {kailash-0.4.1.dist-info → kailash-0.4.2.dist-info}/METADATA +1 -1
- {kailash-0.4.1.dist-info → kailash-0.4.2.dist-info}/RECORD +17 -15
- kailash/middleware/auth/kailash_jwt_auth.py +0 -616
- {kailash-0.4.1.dist-info → kailash-0.4.2.dist-info}/WHEEL +0 -0
- {kailash-0.4.1.dist-info → kailash-0.4.2.dist-info}/entry_points.txt +0 -0
- {kailash-0.4.1.dist-info → kailash-0.4.2.dist-info}/licenses/LICENSE +0 -0
- {kailash-0.4.1.dist-info → kailash-0.4.2.dist-info}/top_level.txt +0 -0
@@ -1,4 +1,4 @@
|
|
1
|
-
kailash/__init__.py,sha256=
|
1
|
+
kailash/__init__.py,sha256=DNNHLV-T781fKqEzh6k-FKu74CJHZG9hsjx6a9xxV48,1724
|
2
2
|
kailash/__main__.py,sha256=vr7TVE5o16V6LsTmRFKG6RDKUXHpIWYdZ6Dok2HkHnI,198
|
3
3
|
kailash/access_control.py,sha256=2ctdRFeSeu-d7DU04Aovxh6Rt_4t3IyQfkKEjTeQiMM,25519
|
4
4
|
kailash/access_control_abac.py,sha256=FPfa_8PuDP3AxTjdWfiH3ntwWO8NodA0py9W8SE5dno,30263
|
@@ -33,19 +33,21 @@ kailash/mcp/utils/cache.py,sha256=dLEseovPaXL4lRzMSw7tqd3tJHwnWfhdZ-HKGyScJXI,84
|
|
33
33
|
kailash/mcp/utils/config.py,sha256=DyZxgdy3vqI5pwhQ_E-42mhueVGNHiuOtTUOrM9HC_U,8124
|
34
34
|
kailash/mcp/utils/formatters.py,sha256=D-2j1nvmprApiUI13HWY-L2_WPSAcJDtVdHcshAuOdo,9740
|
35
35
|
kailash/mcp/utils/metrics.py,sha256=MNUjWGQyq1EGdeqzAKCCZJNgcWHOyaYAV8MlS2cb-4k,13754
|
36
|
-
kailash/middleware/__init__.py,sha256=
|
37
|
-
kailash/middleware/auth/__init__.py,sha256=
|
36
|
+
kailash/middleware/__init__.py,sha256=ZGo0qujL-qWn82nIrojY96N1rMPTWFKHumW6CGGpb4Y,10409
|
37
|
+
kailash/middleware/auth/__init__.py,sha256=VkKM8H-zVFx2PLGL7kyxE2IfSiV1HiwveSysbmxMcg0,2077
|
38
38
|
kailash/middleware/auth/access_control.py,sha256=u8jlfTXRkRteHXi-kiwSGfFFKzAfabxWR6t6L4dU2bk,14954
|
39
39
|
kailash/middleware/auth/auth_manager.py,sha256=d1XFJ9jOCrOTwV26qO0b7wBOSbroTvTxaJADII-mCz0,14057
|
40
|
-
kailash/middleware/auth/
|
41
|
-
kailash/middleware/auth/
|
40
|
+
kailash/middleware/auth/exceptions.py,sha256=tPDQgaX9nMQ9BJZR3Y5tv2LwLy8pZcUz-uFATQjALRA,2496
|
41
|
+
kailash/middleware/auth/jwt_auth.py,sha256=r4dauFKcoARHj0yb4f4WwBmY9YQpLNBGyk4gdBZEI1k,21626
|
42
|
+
kailash/middleware/auth/models.py,sha256=RSLiP3GeOFgQ6p1ZSV0zIsfo8a_3nkbF9ctPoAKtYEA,3509
|
43
|
+
kailash/middleware/auth/utils.py,sha256=AW4fYJ2opQvOr_1YgSQi_MsC_RoFPq0XVOEgbxoXWtw,6555
|
42
44
|
kailash/middleware/communication/__init__.py,sha256=keQ2db4WI2-oZ_nJ5sLE1Tum_RkUt7M2VLTmqOlt0zA,778
|
43
45
|
kailash/middleware/communication/ai_chat.py,sha256=T4R2dujQ40cFH3mI09FTpc6UCbjTItvFLwG0JGe48BQ,36045
|
44
|
-
kailash/middleware/communication/api_gateway.py,sha256=
|
46
|
+
kailash/middleware/communication/api_gateway.py,sha256=NMO-Kysvv_z0c8LORvPViPj31nTnkExV0vvlieVni9w,31724
|
45
47
|
kailash/middleware/communication/events.py,sha256=MEjgcibNyjA4tSFK8CeXDn1oCE75My7K_saxdCBz2HY,15367
|
46
48
|
kailash/middleware/communication/realtime.py,sha256=JS7lMV1_Ta5orvTJPQsetdCdvIiUdsgYt7M7NSQu6fs,24951
|
47
49
|
kailash/middleware/core/__init__.py,sha256=4yQkOWC4b88zSogs1YVqtKG1PugbncqNCwNNWxTdIZA,545
|
48
|
-
kailash/middleware/core/agent_ui.py,sha256=
|
50
|
+
kailash/middleware/core/agent_ui.py,sha256=MVmKznG2OcSF3quIr_uNR7rbPv7aaB2VgkHu2hA1AU4,36097
|
49
51
|
kailash/middleware/core/schema.py,sha256=uVF-5ZJlLYHOQdsKrG46FnTO1bq_QtDjhUSkIIDL9dY,23584
|
50
52
|
kailash/middleware/core/workflows.py,sha256=kjwwP69-T6eCY7kWIMLUBwVy2CapoPR34cqCETquq0s,12480
|
51
53
|
kailash/middleware/database/__init__.py,sha256=UMws94L-vja94AjfzPWIgn0h4_5BGQzI3YaSdqtzeLk,1682
|
@@ -58,7 +60,7 @@ kailash/middleware/database/repositories.py,sha256=3sdHvUv6a0K5ZOoijLt_MLnYPnd8x
|
|
58
60
|
kailash/middleware/database/session_manager.py,sha256=Pzj7c2TZnM3GRty2igSaxmLOf0-Fs67NVe2Q5lR_C-Q,379
|
59
61
|
kailash/middleware/mcp/__init__.py,sha256=EdZB8zOMSBEEmudRzs8ksz9QZJYWQMEx7Tm1MOwIWnI,922
|
60
62
|
kailash/middleware/mcp/client_integration.py,sha256=opzhB5TUts_ND8gARXh93nKCc1u4kwo6SqNMMWqMcSU,18258
|
61
|
-
kailash/middleware/mcp/enhanced_server.py,sha256=
|
63
|
+
kailash/middleware/mcp/enhanced_server.py,sha256=ydU265wAoC2d2sFLuuVzmETowzH9qsqvrzJ5mrwEVvU,18458
|
62
64
|
kailash/nodes/__init__.py,sha256=E6CEp1ooq4GgFhKtwVAczOhPt5N3x-AVQ-R0n3_IFyA,936
|
63
65
|
kailash/nodes/base.py,sha256=5selOpaQvLXoy4VaiTbMxB7NbUhQ4tXdUF31-nPSGnc,51291
|
64
66
|
kailash/nodes/base_async.py,sha256=cjp0tedoxvwqr9fdC385vqInb3WTR0Wcx3ecLxhHiY0,6593
|
@@ -102,7 +104,7 @@ kailash/nodes/auth/risk_assessment.py,sha256=ZVKYCXp8saTBsVxonh2YAM_BkqBPg5HtulR
|
|
102
104
|
kailash/nodes/auth/session_management.py,sha256=FzOxic3hjmRlMiAQy7ps8a7XO7AoxYToZL9ywAcwKxY,38694
|
103
105
|
kailash/nodes/auth/sso.py,sha256=m-sYRNGEWwKfpvAzaHikGC3Wy45h0o-JhqgeYLTTV24,38507
|
104
106
|
kailash/nodes/code/__init__.py,sha256=L3QBfnITPb6v-Wbq2ezNWt8xDlC4uGaTgrkqIJ9vGKU,1191
|
105
|
-
kailash/nodes/code/python.py,sha256=
|
107
|
+
kailash/nodes/code/python.py,sha256=v5pGJms-0CWKHGn8TI7c_lyPEYrNdl3A9azddTfDFuc,51417
|
106
108
|
kailash/nodes/compliance/__init__.py,sha256=6a_FL4ofc8MAVuZ-ARW5uYenZLS4mBFVM9AI2QsnoF8,214
|
107
109
|
kailash/nodes/compliance/data_retention.py,sha256=rP85W9mIbbVb4LqxPFYYFySgUJXI5Q5mdoYLY0o3Mnw,69419
|
108
110
|
kailash/nodes/compliance/gdpr.py,sha256=iEAVU7Faqp0HVHk2KLw51oXr6VyJPycvsVL5IQR4ciY,70528
|
@@ -219,9 +221,9 @@ kailash/workflow/state.py,sha256=UTZxs5-Ona6uvBhx1__i6-RX8gB4qazkBIWE7uyRmWQ,760
|
|
219
221
|
kailash/workflow/templates.py,sha256=MTIMHGyQML6omLbqeKDbTVIVykfXG6pIFWTTsGBUHN0,48591
|
220
222
|
kailash/workflow/validation.py,sha256=JIbIajWVIaWHSvWtgZ4WUVJaBaUOCz5B9cyTwM--dL4,33060
|
221
223
|
kailash/workflow/visualization.py,sha256=ICMWCWqh5fOQ7eJygbvu2PMWHxe-H5_0epwdZuz8cMw,19737
|
222
|
-
kailash-0.4.
|
223
|
-
kailash-0.4.
|
224
|
-
kailash-0.4.
|
225
|
-
kailash-0.4.
|
226
|
-
kailash-0.4.
|
227
|
-
kailash-0.4.
|
224
|
+
kailash-0.4.2.dist-info/licenses/LICENSE,sha256=Axe6g7bTrJkToK9h9j2SpRUKKNaDZDCo2lQ2zPxCE6s,1065
|
225
|
+
kailash-0.4.2.dist-info/METADATA,sha256=wIi6fCs-YFsZuXGSqGeqRd3-GpbioEwmLUhIaCQX32o,24793
|
226
|
+
kailash-0.4.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
227
|
+
kailash-0.4.2.dist-info/entry_points.txt,sha256=M_q3b8PG5W4XbhSgESzIJjh3_4OBKtZFYFsOdkr2vO4,45
|
228
|
+
kailash-0.4.2.dist-info/top_level.txt,sha256=z7GzH2mxl66498pVf5HKwo5wwfPtt9Aq95uZUpH6JV0,8
|
229
|
+
kailash-0.4.2.dist-info/RECORD,,
|
@@ -1,616 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
Kailash SDK-based JWT Authentication Manager
|
3
|
-
|
4
|
-
Built entirely with Kailash SDK components - uses nodes and workflows
|
5
|
-
for all authentication operations. This demonstrates enterprise-grade
|
6
|
-
authentication using only Kailash patterns.
|
7
|
-
"""
|
8
|
-
|
9
|
-
import json
|
10
|
-
import logging
|
11
|
-
import uuid
|
12
|
-
from datetime import datetime, timedelta, timezone
|
13
|
-
from typing import Any, Dict, List, Optional
|
14
|
-
|
15
|
-
# Import Kailash SDK components only
|
16
|
-
from kailash.nodes.base import Node, NodeParameter, register_node
|
17
|
-
from kailash.nodes.code import PythonCodeNode
|
18
|
-
from kailash.nodes.data import JSONReaderNode
|
19
|
-
from kailash.nodes.logic import MergeNode, SwitchNode
|
20
|
-
from kailash.nodes.security import CredentialManagerNode
|
21
|
-
from kailash.runtime.local import LocalRuntime
|
22
|
-
from kailash.workflow.builder import WorkflowBuilder
|
23
|
-
|
24
|
-
logger = logging.getLogger(__name__)
|
25
|
-
|
26
|
-
|
27
|
-
@register_node()
|
28
|
-
class JWTConfigNode(Node):
|
29
|
-
"""Kailash node for JWT configuration management."""
|
30
|
-
|
31
|
-
def __init__(self, name: str = "jwt_config"):
|
32
|
-
super().__init__(name=name)
|
33
|
-
self.algorithm = "HS256" # Use symmetric for simplicity with Kailash
|
34
|
-
self.access_token_expire_minutes = 15
|
35
|
-
self.refresh_token_expire_days = 7
|
36
|
-
self.issuer = "kailash-middleware"
|
37
|
-
self.audience = "kailash-api"
|
38
|
-
|
39
|
-
def get_parameters(self) -> Dict[str, NodeParameter]:
|
40
|
-
return {
|
41
|
-
"algorithm": NodeParameter(
|
42
|
-
name="algorithm",
|
43
|
-
type=str,
|
44
|
-
required=False,
|
45
|
-
default="HS256",
|
46
|
-
description="JWT signing algorithm",
|
47
|
-
),
|
48
|
-
"access_expire_minutes": NodeParameter(
|
49
|
-
name="access_expire_minutes",
|
50
|
-
type=int,
|
51
|
-
required=False,
|
52
|
-
default=15,
|
53
|
-
description="Access token expiration in minutes",
|
54
|
-
),
|
55
|
-
"refresh_expire_days": NodeParameter(
|
56
|
-
name="refresh_expire_days",
|
57
|
-
type=int,
|
58
|
-
required=False,
|
59
|
-
default=7,
|
60
|
-
description="Refresh token expiration in days",
|
61
|
-
),
|
62
|
-
}
|
63
|
-
|
64
|
-
def run(self, **kwargs) -> Dict[str, Any]:
|
65
|
-
"""Return JWT configuration."""
|
66
|
-
return {
|
67
|
-
"config": {
|
68
|
-
"algorithm": getattr(self, "algorithm", "HS256"),
|
69
|
-
"access_token_expire_minutes": getattr(
|
70
|
-
self, "access_token_expire_minutes", 15
|
71
|
-
),
|
72
|
-
"refresh_token_expire_days": getattr(
|
73
|
-
self, "refresh_token_expire_days", 7
|
74
|
-
),
|
75
|
-
"issuer": getattr(self, "issuer", "kailash-middleware"),
|
76
|
-
"audience": getattr(self, "audience", "kailash-api"),
|
77
|
-
}
|
78
|
-
}
|
79
|
-
|
80
|
-
async def process(self, inputs: Dict[str, Any]) -> Dict[str, Any]:
|
81
|
-
"""Async wrapper for middleware compatibility."""
|
82
|
-
return self.run(**inputs)
|
83
|
-
|
84
|
-
|
85
|
-
class TokenGeneratorNode(PythonCodeNode):
|
86
|
-
"""Kailash node for generating JWT tokens."""
|
87
|
-
|
88
|
-
def __init__(self, name: str = "token_generator"):
|
89
|
-
super().__init__(
|
90
|
-
name=name,
|
91
|
-
code="""
|
92
|
-
import jwt
|
93
|
-
import uuid
|
94
|
-
from datetime import datetime, timedelta, timezone
|
95
|
-
|
96
|
-
def generate_token(user_id, token_type='access', config=None, **claims):
|
97
|
-
'''Generate JWT token using Kailash patterns'''
|
98
|
-
config = config or {}
|
99
|
-
|
100
|
-
now = datetime.now(timezone.utc)
|
101
|
-
|
102
|
-
# Set expiration based on token type
|
103
|
-
if token_type == 'access':
|
104
|
-
expire_minutes = config.get('access_token_expire_minutes', 15)
|
105
|
-
exp = now + timedelta(minutes=expire_minutes)
|
106
|
-
else: # refresh
|
107
|
-
expire_days = config.get('refresh_token_expire_days', 7)
|
108
|
-
exp = now + timedelta(days=expire_days)
|
109
|
-
|
110
|
-
# Create payload with Kailash conventions
|
111
|
-
payload = {
|
112
|
-
'sub': user_id,
|
113
|
-
'iss': config.get('issuer', 'kailash-middleware'),
|
114
|
-
'aud': config.get('audience', 'kailash-api'),
|
115
|
-
'exp': int(exp.timestamp()),
|
116
|
-
'iat': int(now.timestamp()),
|
117
|
-
'jti': str(uuid.uuid4()),
|
118
|
-
'token_type': token_type,
|
119
|
-
**claims
|
120
|
-
}
|
121
|
-
|
122
|
-
# Use simple secret for Kailash demo (in production, use proper key management)
|
123
|
-
secret = config.get('secret', 'kailash-jwt-secret-key')
|
124
|
-
algorithm = config.get('algorithm', 'HS256')
|
125
|
-
|
126
|
-
token = jwt.encode(payload, secret, algorithm=algorithm)
|
127
|
-
|
128
|
-
return {
|
129
|
-
'token': token,
|
130
|
-
'payload': payload,
|
131
|
-
'expires_at': exp.isoformat(),
|
132
|
-
'token_type': token_type
|
133
|
-
}
|
134
|
-
|
135
|
-
# Main execution
|
136
|
-
user_id = input_data.get('user_id')
|
137
|
-
token_type = input_data.get('token_type', 'access')
|
138
|
-
config = input_data.get('config', {})
|
139
|
-
claims = input_data.get('claims', {})
|
140
|
-
|
141
|
-
if not user_id:
|
142
|
-
result = {'error': 'user_id is required'}
|
143
|
-
else:
|
144
|
-
result = generate_token(user_id, token_type, config, **claims)
|
145
|
-
""",
|
146
|
-
)
|
147
|
-
|
148
|
-
|
149
|
-
class TokenVerifierNode(PythonCodeNode):
|
150
|
-
"""Kailash node for verifying JWT tokens."""
|
151
|
-
|
152
|
-
def __init__(self, name: str = "token_verifier"):
|
153
|
-
super().__init__(
|
154
|
-
name=name,
|
155
|
-
code="""
|
156
|
-
import jwt
|
157
|
-
from datetime import datetime, timezone
|
158
|
-
|
159
|
-
def verify_token(token, config=None, blacklisted_tokens=None):
|
160
|
-
'''Verify JWT token using Kailash patterns'''
|
161
|
-
config = config or {}
|
162
|
-
blacklisted_tokens = blacklisted_tokens or set()
|
163
|
-
|
164
|
-
try:
|
165
|
-
# Check if token is blacklisted
|
166
|
-
if token in blacklisted_tokens:
|
167
|
-
return {
|
168
|
-
'valid': False,
|
169
|
-
'error': 'Token has been revoked',
|
170
|
-
'error_type': 'revoked'
|
171
|
-
}
|
172
|
-
|
173
|
-
# Verify token
|
174
|
-
secret = config.get('secret', 'kailash-jwt-secret-key')
|
175
|
-
algorithm = config.get('algorithm', 'HS256')
|
176
|
-
|
177
|
-
payload = jwt.decode(
|
178
|
-
token,
|
179
|
-
secret,
|
180
|
-
algorithms=[algorithm],
|
181
|
-
issuer=config.get('issuer'),
|
182
|
-
audience=config.get('audience')
|
183
|
-
)
|
184
|
-
|
185
|
-
return {
|
186
|
-
'valid': True,
|
187
|
-
'payload': payload,
|
188
|
-
'user_id': payload.get('sub'),
|
189
|
-
'token_type': payload.get('token_type', 'access'),
|
190
|
-
'expires_at': payload.get('exp'),
|
191
|
-
'permissions': payload.get('permissions', []),
|
192
|
-
'roles': payload.get('roles', []),
|
193
|
-
'tenant_id': payload.get('tenant_id'),
|
194
|
-
'session_id': payload.get('session_id')
|
195
|
-
}
|
196
|
-
|
197
|
-
except jwt.ExpiredSignatureError:
|
198
|
-
return {
|
199
|
-
'valid': False,
|
200
|
-
'error': 'Token has expired',
|
201
|
-
'error_type': 'expired'
|
202
|
-
}
|
203
|
-
except jwt.InvalidTokenError as e:
|
204
|
-
return {
|
205
|
-
'valid': False,
|
206
|
-
'error': str(e),
|
207
|
-
'error_type': 'invalid'
|
208
|
-
}
|
209
|
-
except Exception as e:
|
210
|
-
return {
|
211
|
-
'valid': False,
|
212
|
-
'error': f'Token verification failed: {str(e)}',
|
213
|
-
'error_type': 'error'
|
214
|
-
}
|
215
|
-
|
216
|
-
# Main execution
|
217
|
-
token = input_data.get('token')
|
218
|
-
config = input_data.get('config', {})
|
219
|
-
blacklisted_tokens = input_data.get('blacklisted_tokens', set())
|
220
|
-
|
221
|
-
if not token:
|
222
|
-
result = {'valid': False, 'error': 'Token is required', 'error_type': 'missing'}
|
223
|
-
else:
|
224
|
-
result = verify_token(token, config, blacklisted_tokens)
|
225
|
-
""",
|
226
|
-
)
|
227
|
-
|
228
|
-
|
229
|
-
class RefreshTokenNode(PythonCodeNode):
|
230
|
-
"""Kailash node for handling token refresh."""
|
231
|
-
|
232
|
-
def __init__(self, name: str = "refresh_token"):
|
233
|
-
super().__init__(
|
234
|
-
name=name,
|
235
|
-
code="""
|
236
|
-
def refresh_access_token(refresh_token_data, config, refresh_tracking):
|
237
|
-
'''Refresh access token using Kailash patterns'''
|
238
|
-
|
239
|
-
if not refresh_token_data.get('valid'):
|
240
|
-
return {
|
241
|
-
'success': False,
|
242
|
-
'error': 'Invalid refresh token',
|
243
|
-
'error_type': 'invalid_refresh'
|
244
|
-
}
|
245
|
-
|
246
|
-
payload = refresh_token_data.get('payload', {})
|
247
|
-
|
248
|
-
# Check token type
|
249
|
-
if payload.get('token_type') != 'refresh':
|
250
|
-
return {
|
251
|
-
'success': False,
|
252
|
-
'error': 'Token is not a refresh token',
|
253
|
-
'error_type': 'wrong_type'
|
254
|
-
}
|
255
|
-
|
256
|
-
jti = payload.get('jti')
|
257
|
-
user_id = payload.get('sub')
|
258
|
-
|
259
|
-
# Check refresh count (Kailash pattern for security)
|
260
|
-
refresh_info = refresh_tracking.get(jti, {})
|
261
|
-
max_refresh_count = config.get('max_refresh_count', 10)
|
262
|
-
|
263
|
-
if refresh_info.get('refresh_count', 0) >= max_refresh_count:
|
264
|
-
return {
|
265
|
-
'success': False,
|
266
|
-
'error': 'Refresh token has exceeded usage limit',
|
267
|
-
'error_type': 'limit_exceeded'
|
268
|
-
}
|
269
|
-
|
270
|
-
# Update refresh tracking
|
271
|
-
refresh_info['refresh_count'] = refresh_info.get('refresh_count', 0) + 1
|
272
|
-
refresh_info['last_used'] = datetime.now().isoformat()
|
273
|
-
refresh_tracking[jti] = refresh_info
|
274
|
-
|
275
|
-
# Prepare new token creation data
|
276
|
-
return {
|
277
|
-
'success': True,
|
278
|
-
'user_id': user_id,
|
279
|
-
'tenant_id': payload.get('tenant_id'),
|
280
|
-
'session_id': payload.get('session_id'),
|
281
|
-
'permissions': payload.get('permissions', []),
|
282
|
-
'roles': payload.get('roles', []),
|
283
|
-
'refresh_tracking': refresh_tracking
|
284
|
-
}
|
285
|
-
|
286
|
-
# Main execution
|
287
|
-
refresh_token_data = input_data.get('refresh_token_data', {})
|
288
|
-
config = input_data.get('config', {})
|
289
|
-
refresh_tracking = input_data.get('refresh_tracking', {})
|
290
|
-
|
291
|
-
result = refresh_access_token(refresh_token_data, config, refresh_tracking)
|
292
|
-
""",
|
293
|
-
)
|
294
|
-
|
295
|
-
|
296
|
-
class KailashJWTAuthManager:
|
297
|
-
"""
|
298
|
-
JWT Authentication Manager built entirely with Kailash SDK components.
|
299
|
-
|
300
|
-
This demonstrates enterprise-grade authentication using only Kailash
|
301
|
-
nodes and workflows - no external dependencies beyond JWT library.
|
302
|
-
"""
|
303
|
-
|
304
|
-
def __init__(self, secret_key: str = "kailash-jwt-secret-key"):
|
305
|
-
self.secret_key = secret_key
|
306
|
-
self.runtime = LocalRuntime()
|
307
|
-
|
308
|
-
# State management using Kailash patterns
|
309
|
-
self.blacklisted_tokens = set()
|
310
|
-
self.refresh_tracking = {}
|
311
|
-
|
312
|
-
# Create authentication workflows
|
313
|
-
self._create_workflows()
|
314
|
-
|
315
|
-
def _get_token_generator_code(self):
|
316
|
-
"""Get token generator code for PythonCodeNode."""
|
317
|
-
return """
|
318
|
-
import jwt
|
319
|
-
import uuid
|
320
|
-
from datetime import datetime, timedelta, timezone
|
321
|
-
|
322
|
-
def generate_token(user_id, token_type='access', config=None, **claims):
|
323
|
-
'''Generate JWT token using Kailash patterns'''
|
324
|
-
config = config or {}
|
325
|
-
|
326
|
-
now = datetime.now(timezone.utc)
|
327
|
-
|
328
|
-
# Set expiration based on token type
|
329
|
-
if token_type == 'access':
|
330
|
-
expire_minutes = config.get('access_token_expire_minutes', 15)
|
331
|
-
exp = now + timedelta(minutes=expire_minutes)
|
332
|
-
else: # refresh
|
333
|
-
expire_days = config.get('refresh_token_expire_days', 7)
|
334
|
-
exp = now + timedelta(days=expire_days)
|
335
|
-
|
336
|
-
# Create payload with Kailash conventions
|
337
|
-
payload = {
|
338
|
-
'sub': user_id,
|
339
|
-
'iss': config.get('issuer', 'kailash-middleware'),
|
340
|
-
'aud': config.get('audience', 'kailash-api'),
|
341
|
-
'exp': int(exp.timestamp()),
|
342
|
-
'iat': int(now.timestamp()),
|
343
|
-
'jti': str(uuid.uuid4()),
|
344
|
-
'token_type': token_type,
|
345
|
-
**claims
|
346
|
-
}
|
347
|
-
|
348
|
-
# Use simple secret for Kailash demo (in production, use proper key management)
|
349
|
-
secret = config.get('secret', 'kailash-jwt-secret-key')
|
350
|
-
algorithm = config.get('algorithm', 'HS256')
|
351
|
-
|
352
|
-
token = jwt.encode(payload, secret, algorithm=algorithm)
|
353
|
-
|
354
|
-
return {
|
355
|
-
'token': token,
|
356
|
-
'payload': payload,
|
357
|
-
'expires_at': exp.isoformat(),
|
358
|
-
'token_type': token_type
|
359
|
-
}
|
360
|
-
|
361
|
-
# Main execution
|
362
|
-
user_id = input_data.get('user_id')
|
363
|
-
token_type = input_data.get('token_type', 'access')
|
364
|
-
config = input_data.get('config', {})
|
365
|
-
claims = input_data.get('claims', {})
|
366
|
-
|
367
|
-
if not user_id:
|
368
|
-
result = {'error': 'user_id is required'}
|
369
|
-
else:
|
370
|
-
result = generate_token(user_id, token_type, config, **claims)
|
371
|
-
"""
|
372
|
-
|
373
|
-
def _get_token_verifier_code(self):
|
374
|
-
"""Get token verifier code for PythonCodeNode."""
|
375
|
-
return """
|
376
|
-
import jwt
|
377
|
-
from datetime import datetime, timezone
|
378
|
-
|
379
|
-
def verify_token(token, config=None, blacklisted_tokens=None):
|
380
|
-
config = config or {}
|
381
|
-
blacklisted_tokens = blacklisted_tokens or set()
|
382
|
-
|
383
|
-
try:
|
384
|
-
if token in blacklisted_tokens:
|
385
|
-
return {'valid': False, 'error': 'Token has been revoked', 'error_type': 'revoked'}
|
386
|
-
|
387
|
-
secret = config.get('secret', 'kailash-jwt-secret-key')
|
388
|
-
algorithm = config.get('algorithm', 'HS256')
|
389
|
-
|
390
|
-
payload = jwt.decode(token, secret, algorithms=[algorithm])
|
391
|
-
|
392
|
-
return {
|
393
|
-
'valid': True,
|
394
|
-
'payload': payload,
|
395
|
-
'user_id': payload.get('sub'),
|
396
|
-
'token_type': payload.get('token_type', 'access')
|
397
|
-
}
|
398
|
-
|
399
|
-
except jwt.ExpiredSignatureError:
|
400
|
-
return {'valid': False, 'error': 'Token has expired', 'error_type': 'expired'}
|
401
|
-
except jwt.InvalidTokenError as e:
|
402
|
-
return {'valid': False, 'error': str(e), 'error_type': 'invalid'}
|
403
|
-
except Exception as e:
|
404
|
-
return {'valid': False, 'error': f'Token verification failed: {str(e)}', 'error_type': 'error'}
|
405
|
-
|
406
|
-
token = input_data.get('token')
|
407
|
-
config = input_data.get('config', {})
|
408
|
-
blacklisted_tokens = input_data.get('blacklisted_tokens', set())
|
409
|
-
|
410
|
-
if not token:
|
411
|
-
result = {'valid': False, 'error': 'Token is required', 'error_type': 'missing'}
|
412
|
-
else:
|
413
|
-
result = verify_token(token, config, blacklisted_tokens)
|
414
|
-
"""
|
415
|
-
|
416
|
-
def _create_workflows(self):
|
417
|
-
"""Create Kailash workflows for authentication operations."""
|
418
|
-
|
419
|
-
# Token Creation Workflow
|
420
|
-
self.token_workflow = WorkflowBuilder()
|
421
|
-
|
422
|
-
config_id = self.token_workflow.add_node(
|
423
|
-
"JWTConfigNode", "jwt_config", {"name": "jwt_config"}
|
424
|
-
)
|
425
|
-
token_id = self.token_workflow.add_node(
|
426
|
-
"PythonCodeNode",
|
427
|
-
"generate_token",
|
428
|
-
{"name": "generate_token", "code": self._get_token_generator_code()},
|
429
|
-
)
|
430
|
-
|
431
|
-
self.token_workflow.add_connection(config_id, "config", token_id, "config")
|
432
|
-
|
433
|
-
# Token Verification Workflow
|
434
|
-
self.verify_workflow = WorkflowBuilder()
|
435
|
-
|
436
|
-
config_verify_id = self.verify_workflow.add_node(
|
437
|
-
"JWTConfigNode", "jwt_config_verify", {"name": "jwt_config_verify"}
|
438
|
-
)
|
439
|
-
verify_id = self.verify_workflow.add_node(
|
440
|
-
"PythonCodeNode",
|
441
|
-
"verify_token",
|
442
|
-
{"name": "verify_token", "code": self._get_token_verifier_code()},
|
443
|
-
)
|
444
|
-
|
445
|
-
self.verify_workflow.add_connection(
|
446
|
-
config_verify_id, "config", verify_id, "config"
|
447
|
-
)
|
448
|
-
|
449
|
-
# For simplicity, we'll use direct verification in methods instead of complex workflows
|
450
|
-
|
451
|
-
def create_access_token(
|
452
|
-
self,
|
453
|
-
user_id: str,
|
454
|
-
tenant_id: str = None,
|
455
|
-
session_id: str = None,
|
456
|
-
permissions: List[str] = None,
|
457
|
-
roles: List[str] = None,
|
458
|
-
) -> str:
|
459
|
-
"""Create access token using Kailash workflow."""
|
460
|
-
|
461
|
-
claims = {}
|
462
|
-
if tenant_id:
|
463
|
-
claims["tenant_id"] = tenant_id
|
464
|
-
if session_id:
|
465
|
-
claims["session_id"] = session_id
|
466
|
-
if permissions:
|
467
|
-
claims["permissions"] = permissions
|
468
|
-
if roles:
|
469
|
-
claims["roles"] = roles
|
470
|
-
|
471
|
-
inputs = {
|
472
|
-
"user_id": user_id,
|
473
|
-
"token_type": "access",
|
474
|
-
"claims": claims,
|
475
|
-
"config": {"secret": self.secret_key},
|
476
|
-
}
|
477
|
-
|
478
|
-
# Execute Kailash workflow
|
479
|
-
workflow = self.token_workflow.build()
|
480
|
-
results, _ = self.runtime.execute(workflow, parameters=inputs)
|
481
|
-
|
482
|
-
return results.get("generate_token", {}).get("token")
|
483
|
-
|
484
|
-
def create_refresh_token(
|
485
|
-
self, user_id: str, tenant_id: str = None, session_id: str = None
|
486
|
-
) -> str:
|
487
|
-
"""Create refresh token using Kailash workflow."""
|
488
|
-
|
489
|
-
claims = {}
|
490
|
-
if tenant_id:
|
491
|
-
claims["tenant_id"] = tenant_id
|
492
|
-
if session_id:
|
493
|
-
claims["session_id"] = session_id
|
494
|
-
|
495
|
-
inputs = {
|
496
|
-
"user_id": user_id,
|
497
|
-
"token_type": "refresh",
|
498
|
-
"claims": claims,
|
499
|
-
"config": {"secret": self.secret_key},
|
500
|
-
}
|
501
|
-
|
502
|
-
# Execute Kailash workflow
|
503
|
-
workflow = self.token_workflow.build()
|
504
|
-
results, _ = self.runtime.execute(workflow, parameters=inputs)
|
505
|
-
|
506
|
-
token_result = results.get("generate_token", {})
|
507
|
-
token = token_result.get("token")
|
508
|
-
|
509
|
-
# Track refresh token in Kailash pattern
|
510
|
-
if token:
|
511
|
-
payload = token_result.get("payload", {})
|
512
|
-
jti = payload.get("jti")
|
513
|
-
if jti:
|
514
|
-
self.refresh_tracking[jti] = {
|
515
|
-
"user_id": user_id,
|
516
|
-
"tenant_id": tenant_id,
|
517
|
-
"session_id": session_id,
|
518
|
-
"created_at": datetime.now(timezone.utc).isoformat(),
|
519
|
-
"refresh_count": 0,
|
520
|
-
}
|
521
|
-
|
522
|
-
return token
|
523
|
-
|
524
|
-
def create_token_pair(
|
525
|
-
self,
|
526
|
-
user_id: str,
|
527
|
-
tenant_id: str = None,
|
528
|
-
session_id: str = None,
|
529
|
-
permissions: List[str] = None,
|
530
|
-
roles: List[str] = None,
|
531
|
-
) -> Dict[str, Any]:
|
532
|
-
"""Create access and refresh token pair using Kailash workflows."""
|
533
|
-
|
534
|
-
access_token = self.create_access_token(
|
535
|
-
user_id, tenant_id, session_id, permissions, roles
|
536
|
-
)
|
537
|
-
refresh_token = self.create_refresh_token(user_id, tenant_id, session_id)
|
538
|
-
|
539
|
-
return {
|
540
|
-
"access_token": access_token,
|
541
|
-
"refresh_token": refresh_token,
|
542
|
-
"token_type": "Bearer",
|
543
|
-
"expires_in": 15 * 60, # 15 minutes in seconds
|
544
|
-
"scope": "kailash-api",
|
545
|
-
}
|
546
|
-
|
547
|
-
def verify_token(self, token: str) -> Dict[str, Any]:
|
548
|
-
"""Verify token using Kailash workflow."""
|
549
|
-
|
550
|
-
inputs = {
|
551
|
-
"token": token,
|
552
|
-
"config": {"secret": self.secret_key},
|
553
|
-
"blacklisted_tokens": self.blacklisted_tokens,
|
554
|
-
}
|
555
|
-
|
556
|
-
# Execute Kailash verification workflow
|
557
|
-
workflow = self.verify_workflow.build()
|
558
|
-
results, _ = self.runtime.execute(workflow, parameters=inputs)
|
559
|
-
|
560
|
-
return results.get("verify_token", {})
|
561
|
-
|
562
|
-
def refresh_access_token(self, refresh_token: str) -> Dict[str, Any]:
|
563
|
-
"""Refresh access token using simplified Kailash pattern."""
|
564
|
-
|
565
|
-
# Verify the refresh token
|
566
|
-
verify_result = self.verify_token(refresh_token)
|
567
|
-
|
568
|
-
if not verify_result.get("valid"):
|
569
|
-
return {"success": False, "error": "Invalid refresh token"}
|
570
|
-
|
571
|
-
payload = verify_result.get("payload", {})
|
572
|
-
|
573
|
-
if payload.get("token_type") != "refresh":
|
574
|
-
return {"success": False, "error": "Token is not a refresh token"}
|
575
|
-
|
576
|
-
# Create new token pair
|
577
|
-
return self.create_token_pair(
|
578
|
-
user_id=payload.get("sub"),
|
579
|
-
tenant_id=payload.get("tenant_id"),
|
580
|
-
session_id=payload.get("session_id"),
|
581
|
-
permissions=payload.get("permissions", []),
|
582
|
-
roles=payload.get("roles", []),
|
583
|
-
)
|
584
|
-
|
585
|
-
def revoke_token(self, token: str):
|
586
|
-
"""Revoke token by adding to blacklist (Kailash pattern)."""
|
587
|
-
self.blacklisted_tokens.add(token)
|
588
|
-
logger.info(f"Revoked token (length: {len(token)})")
|
589
|
-
|
590
|
-
def revoke_all_user_tokens(self, user_id: str):
|
591
|
-
"""Revoke all tokens for a user (Kailash pattern)."""
|
592
|
-
# Remove all refresh tokens for user
|
593
|
-
to_remove = []
|
594
|
-
for jti, data in self.refresh_tracking.items():
|
595
|
-
if data.get("user_id") == user_id:
|
596
|
-
to_remove.append(jti)
|
597
|
-
|
598
|
-
for jti in to_remove:
|
599
|
-
del self.refresh_tracking[jti]
|
600
|
-
|
601
|
-
logger.info(f"Revoked all tokens for user {user_id}")
|
602
|
-
|
603
|
-
def get_stats(self) -> Dict[str, Any]:
|
604
|
-
"""Get authentication statistics (Kailash pattern)."""
|
605
|
-
return {
|
606
|
-
"active_refresh_tokens": len(self.refresh_tracking),
|
607
|
-
"blacklisted_tokens": len(self.blacklisted_tokens),
|
608
|
-
"auth_manager": "KailashJWTAuthManager",
|
609
|
-
"workflow_runtime": "LocalRuntime",
|
610
|
-
"nodes_used": [
|
611
|
-
"JWTConfigNode",
|
612
|
-
"TokenGeneratorNode",
|
613
|
-
"TokenVerifierNode",
|
614
|
-
"RefreshTokenNode",
|
615
|
-
],
|
616
|
-
}
|
File without changes
|
File without changes
|
File without changes
|