miso-client 0.1.0__py3-none-any.whl → 0.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of miso-client might be problematic. Click here for more details.
- miso_client/__init__.py +83 -81
- miso_client/errors.py +9 -4
- miso_client/models/config.py +56 -35
- miso_client/services/__init__.py +5 -5
- miso_client/services/auth.py +65 -48
- miso_client/services/cache.py +42 -41
- miso_client/services/encryption.py +18 -17
- miso_client/services/logger.py +109 -95
- miso_client/services/permission.py +27 -36
- miso_client/services/redis.py +17 -15
- miso_client/services/role.py +25 -36
- miso_client/utils/__init__.py +3 -3
- miso_client/utils/config_loader.py +24 -16
- miso_client/utils/data_masker.py +27 -28
- miso_client/utils/http_client.py +91 -81
- miso_client/utils/jwt_tools.py +14 -17
- {miso_client-0.1.0.dist-info → miso_client-0.2.0.dist-info}/METADATA +37 -1
- miso_client-0.2.0.dist-info/RECORD +23 -0
- miso_client-0.1.0.dist-info/RECORD +0 -23
- {miso_client-0.1.0.dist-info → miso_client-0.2.0.dist-info}/WHEEL +0 -0
- {miso_client-0.1.0.dist-info → miso_client-0.2.0.dist-info}/licenses/LICENSE +0 -0
- {miso_client-0.1.0.dist-info → miso_client-0.2.0.dist-info}/top_level.txt +0 -0
miso_client/__init__.py
CHANGED
|
@@ -7,34 +7,34 @@ for authentication, role-based access control, permission management, and loggin
|
|
|
7
7
|
|
|
8
8
|
from typing import Any, Optional
|
|
9
9
|
|
|
10
|
+
from .errors import (
|
|
11
|
+
AuthenticationError,
|
|
12
|
+
AuthorizationError,
|
|
13
|
+
ConfigurationError,
|
|
14
|
+
ConnectionError,
|
|
15
|
+
MisoClientError,
|
|
16
|
+
)
|
|
10
17
|
from .models.config import (
|
|
11
|
-
RedisConfig,
|
|
12
|
-
MisoClientConfig,
|
|
13
|
-
UserInfo,
|
|
14
18
|
AuthResult,
|
|
15
|
-
|
|
16
|
-
RoleResult,
|
|
17
|
-
PermissionResult,
|
|
19
|
+
ClientLoggingOptions,
|
|
18
20
|
ClientTokenResponse,
|
|
21
|
+
LogEntry,
|
|
22
|
+
MisoClientConfig,
|
|
19
23
|
PerformanceMetrics,
|
|
20
|
-
|
|
24
|
+
PermissionResult,
|
|
25
|
+
RedisConfig,
|
|
26
|
+
RoleResult,
|
|
27
|
+
UserInfo,
|
|
21
28
|
)
|
|
22
29
|
from .services.auth import AuthService
|
|
23
|
-
from .services.
|
|
30
|
+
from .services.cache import CacheService
|
|
31
|
+
from .services.encryption import EncryptionService
|
|
32
|
+
from .services.logger import LoggerChain, LoggerService
|
|
24
33
|
from .services.permission import PermissionService
|
|
25
|
-
from .services.logger import LoggerService, LoggerChain
|
|
26
34
|
from .services.redis import RedisService
|
|
27
|
-
from .services.
|
|
28
|
-
from .services.cache import CacheService
|
|
29
|
-
from .utils.http_client import HttpClient
|
|
35
|
+
from .services.role import RoleService
|
|
30
36
|
from .utils.config_loader import load_config
|
|
31
|
-
from .
|
|
32
|
-
MisoClientError,
|
|
33
|
-
AuthenticationError,
|
|
34
|
-
AuthorizationError,
|
|
35
|
-
ConnectionError,
|
|
36
|
-
ConfigurationError,
|
|
37
|
-
)
|
|
37
|
+
from .utils.http_client import HttpClient
|
|
38
38
|
|
|
39
39
|
__version__ = "0.1.0"
|
|
40
40
|
__author__ = "AI Fabrix Team"
|
|
@@ -44,18 +44,18 @@ __license__ = "MIT"
|
|
|
44
44
|
class MisoClient:
|
|
45
45
|
"""
|
|
46
46
|
Main MisoClient SDK class for authentication, authorization, and logging.
|
|
47
|
-
|
|
47
|
+
|
|
48
48
|
This client provides a unified interface for:
|
|
49
49
|
- Token validation and user management
|
|
50
50
|
- Role-based access control
|
|
51
51
|
- Permission management
|
|
52
52
|
- Application logging with Redis caching
|
|
53
53
|
"""
|
|
54
|
-
|
|
54
|
+
|
|
55
55
|
def __init__(self, config: MisoClientConfig):
|
|
56
56
|
"""
|
|
57
57
|
Initialize MisoClient with configuration.
|
|
58
|
-
|
|
58
|
+
|
|
59
59
|
Args:
|
|
60
60
|
config: MisoClient configuration including controller URL, client credentials, etc.
|
|
61
61
|
"""
|
|
@@ -75,7 +75,7 @@ class MisoClient:
|
|
|
75
75
|
async def initialize(self) -> None:
|
|
76
76
|
"""
|
|
77
77
|
Initialize the client (connect to Redis if configured).
|
|
78
|
-
|
|
78
|
+
|
|
79
79
|
This method should be called before using the client. It will attempt
|
|
80
80
|
to connect to Redis if configured, but will gracefully fall back to
|
|
81
81
|
controller-only mode if Redis is unavailable.
|
|
@@ -105,16 +105,18 @@ class MisoClient:
|
|
|
105
105
|
def get_token(self, req: dict) -> str | None:
|
|
106
106
|
"""
|
|
107
107
|
Extract Bearer token from request headers.
|
|
108
|
-
|
|
108
|
+
|
|
109
109
|
Supports common request object patterns (dict with headers).
|
|
110
|
-
|
|
110
|
+
|
|
111
111
|
Args:
|
|
112
112
|
req: Request object with headers dict containing 'authorization' key
|
|
113
|
-
|
|
113
|
+
|
|
114
114
|
Returns:
|
|
115
115
|
Bearer token string or None if not found
|
|
116
116
|
"""
|
|
117
|
-
headers_obj =
|
|
117
|
+
headers_obj = (
|
|
118
|
+
req.get("headers", {}) if isinstance(req, dict) else getattr(req, "headers", {})
|
|
119
|
+
)
|
|
118
120
|
headers: dict[str, Any] = headers_obj if isinstance(headers_obj, dict) else {}
|
|
119
121
|
auth_value = headers.get("authorization") or headers.get("Authorization")
|
|
120
122
|
if not isinstance(auth_value, str):
|
|
@@ -123,16 +125,16 @@ class MisoClient:
|
|
|
123
125
|
# Support "Bearer <token>" format
|
|
124
126
|
if auth_value.startswith("Bearer "):
|
|
125
127
|
return auth_value[7:]
|
|
126
|
-
|
|
128
|
+
|
|
127
129
|
# If no Bearer prefix, assume the whole header is the token
|
|
128
130
|
return auth_value
|
|
129
131
|
|
|
130
132
|
async def get_environment_token(self) -> str:
|
|
131
133
|
"""
|
|
132
134
|
Get environment token using client credentials.
|
|
133
|
-
|
|
135
|
+
|
|
134
136
|
This is called automatically by HttpClient but can be called manually.
|
|
135
|
-
|
|
137
|
+
|
|
136
138
|
Returns:
|
|
137
139
|
Client token string
|
|
138
140
|
"""
|
|
@@ -141,12 +143,12 @@ class MisoClient:
|
|
|
141
143
|
def login(self, redirect_uri: str) -> str:
|
|
142
144
|
"""
|
|
143
145
|
Initiate login flow by redirecting to controller.
|
|
144
|
-
|
|
146
|
+
|
|
145
147
|
Returns the login URL for browser redirect or manual navigation.
|
|
146
|
-
|
|
148
|
+
|
|
147
149
|
Args:
|
|
148
150
|
redirect_uri: URI to redirect to after successful login
|
|
149
|
-
|
|
151
|
+
|
|
150
152
|
Returns:
|
|
151
153
|
Login URL string
|
|
152
154
|
"""
|
|
@@ -155,10 +157,10 @@ class MisoClient:
|
|
|
155
157
|
async def validate_token(self, token: str) -> bool:
|
|
156
158
|
"""
|
|
157
159
|
Validate token with controller.
|
|
158
|
-
|
|
160
|
+
|
|
159
161
|
Args:
|
|
160
162
|
token: JWT token to validate
|
|
161
|
-
|
|
163
|
+
|
|
162
164
|
Returns:
|
|
163
165
|
True if token is valid, False otherwise
|
|
164
166
|
"""
|
|
@@ -167,10 +169,10 @@ class MisoClient:
|
|
|
167
169
|
async def get_user(self, token: str) -> UserInfo | None:
|
|
168
170
|
"""
|
|
169
171
|
Get user information from token.
|
|
170
|
-
|
|
172
|
+
|
|
171
173
|
Args:
|
|
172
174
|
token: JWT token
|
|
173
|
-
|
|
175
|
+
|
|
174
176
|
Returns:
|
|
175
177
|
UserInfo if token is valid, None otherwise
|
|
176
178
|
"""
|
|
@@ -179,10 +181,10 @@ class MisoClient:
|
|
|
179
181
|
async def get_user_info(self, token: str) -> UserInfo | None:
|
|
180
182
|
"""
|
|
181
183
|
Get user information from GET /api/auth/user endpoint.
|
|
182
|
-
|
|
184
|
+
|
|
183
185
|
Args:
|
|
184
186
|
token: JWT token
|
|
185
|
-
|
|
187
|
+
|
|
186
188
|
Returns:
|
|
187
189
|
UserInfo if token is valid, None otherwise
|
|
188
190
|
"""
|
|
@@ -191,10 +193,10 @@ class MisoClient:
|
|
|
191
193
|
async def is_authenticated(self, token: str) -> bool:
|
|
192
194
|
"""
|
|
193
195
|
Check if user is authenticated.
|
|
194
|
-
|
|
196
|
+
|
|
195
197
|
Args:
|
|
196
198
|
token: JWT token
|
|
197
|
-
|
|
199
|
+
|
|
198
200
|
Returns:
|
|
199
201
|
True if user is authenticated, False otherwise
|
|
200
202
|
"""
|
|
@@ -209,10 +211,10 @@ class MisoClient:
|
|
|
209
211
|
async def get_roles(self, token: str) -> list[str]:
|
|
210
212
|
"""
|
|
211
213
|
Get user roles (cached in Redis if available).
|
|
212
|
-
|
|
214
|
+
|
|
213
215
|
Args:
|
|
214
216
|
token: JWT token
|
|
215
|
-
|
|
217
|
+
|
|
216
218
|
Returns:
|
|
217
219
|
List of user roles
|
|
218
220
|
"""
|
|
@@ -221,11 +223,11 @@ class MisoClient:
|
|
|
221
223
|
async def has_role(self, token: str, role: str) -> bool:
|
|
222
224
|
"""
|
|
223
225
|
Check if user has specific role.
|
|
224
|
-
|
|
226
|
+
|
|
225
227
|
Args:
|
|
226
228
|
token: JWT token
|
|
227
229
|
role: Role to check
|
|
228
|
-
|
|
230
|
+
|
|
229
231
|
Returns:
|
|
230
232
|
True if user has the role, False otherwise
|
|
231
233
|
"""
|
|
@@ -234,11 +236,11 @@ class MisoClient:
|
|
|
234
236
|
async def has_any_role(self, token: str, roles: list[str]) -> bool:
|
|
235
237
|
"""
|
|
236
238
|
Check if user has any of the specified roles.
|
|
237
|
-
|
|
239
|
+
|
|
238
240
|
Args:
|
|
239
241
|
token: JWT token
|
|
240
242
|
roles: List of roles to check
|
|
241
|
-
|
|
243
|
+
|
|
242
244
|
Returns:
|
|
243
245
|
True if user has any of the roles, False otherwise
|
|
244
246
|
"""
|
|
@@ -247,11 +249,11 @@ class MisoClient:
|
|
|
247
249
|
async def has_all_roles(self, token: str, roles: list[str]) -> bool:
|
|
248
250
|
"""
|
|
249
251
|
Check if user has all of the specified roles.
|
|
250
|
-
|
|
252
|
+
|
|
251
253
|
Args:
|
|
252
254
|
token: JWT token
|
|
253
255
|
roles: List of roles to check
|
|
254
|
-
|
|
256
|
+
|
|
255
257
|
Returns:
|
|
256
258
|
True if user has all roles, False otherwise
|
|
257
259
|
"""
|
|
@@ -260,10 +262,10 @@ class MisoClient:
|
|
|
260
262
|
async def refresh_roles(self, token: str) -> list[str]:
|
|
261
263
|
"""
|
|
262
264
|
Force refresh roles from controller (bypass cache).
|
|
263
|
-
|
|
265
|
+
|
|
264
266
|
Args:
|
|
265
267
|
token: JWT token
|
|
266
|
-
|
|
268
|
+
|
|
267
269
|
Returns:
|
|
268
270
|
Fresh list of user roles
|
|
269
271
|
"""
|
|
@@ -272,10 +274,10 @@ class MisoClient:
|
|
|
272
274
|
async def get_permissions(self, token: str) -> list[str]:
|
|
273
275
|
"""
|
|
274
276
|
Get user permissions (cached in Redis if available).
|
|
275
|
-
|
|
277
|
+
|
|
276
278
|
Args:
|
|
277
279
|
token: JWT token
|
|
278
|
-
|
|
280
|
+
|
|
279
281
|
Returns:
|
|
280
282
|
List of user permissions
|
|
281
283
|
"""
|
|
@@ -284,11 +286,11 @@ class MisoClient:
|
|
|
284
286
|
async def has_permission(self, token: str, permission: str) -> bool:
|
|
285
287
|
"""
|
|
286
288
|
Check if user has specific permission.
|
|
287
|
-
|
|
289
|
+
|
|
288
290
|
Args:
|
|
289
291
|
token: JWT token
|
|
290
292
|
permission: Permission to check
|
|
291
|
-
|
|
293
|
+
|
|
292
294
|
Returns:
|
|
293
295
|
True if user has the permission, False otherwise
|
|
294
296
|
"""
|
|
@@ -297,11 +299,11 @@ class MisoClient:
|
|
|
297
299
|
async def has_any_permission(self, token: str, permissions: list[str]) -> bool:
|
|
298
300
|
"""
|
|
299
301
|
Check if user has any of the specified permissions.
|
|
300
|
-
|
|
302
|
+
|
|
301
303
|
Args:
|
|
302
304
|
token: JWT token
|
|
303
305
|
permissions: List of permissions to check
|
|
304
|
-
|
|
306
|
+
|
|
305
307
|
Returns:
|
|
306
308
|
True if user has any of the permissions, False otherwise
|
|
307
309
|
"""
|
|
@@ -310,11 +312,11 @@ class MisoClient:
|
|
|
310
312
|
async def has_all_permissions(self, token: str, permissions: list[str]) -> bool:
|
|
311
313
|
"""
|
|
312
314
|
Check if user has all of the specified permissions.
|
|
313
|
-
|
|
315
|
+
|
|
314
316
|
Args:
|
|
315
317
|
token: JWT token
|
|
316
318
|
permissions: List of permissions to check
|
|
317
|
-
|
|
319
|
+
|
|
318
320
|
Returns:
|
|
319
321
|
True if user has all permissions, False otherwise
|
|
320
322
|
"""
|
|
@@ -323,10 +325,10 @@ class MisoClient:
|
|
|
323
325
|
async def refresh_permissions(self, token: str) -> list[str]:
|
|
324
326
|
"""
|
|
325
327
|
Force refresh permissions from controller (bypass cache).
|
|
326
|
-
|
|
328
|
+
|
|
327
329
|
Args:
|
|
328
330
|
token: JWT token
|
|
329
|
-
|
|
331
|
+
|
|
330
332
|
Returns:
|
|
331
333
|
Fresh list of user permissions
|
|
332
334
|
"""
|
|
@@ -335,7 +337,7 @@ class MisoClient:
|
|
|
335
337
|
async def clear_permissions_cache(self, token: str) -> None:
|
|
336
338
|
"""
|
|
337
339
|
Clear cached permissions for a user.
|
|
338
|
-
|
|
340
|
+
|
|
339
341
|
Args:
|
|
340
342
|
token: JWT token
|
|
341
343
|
"""
|
|
@@ -347,7 +349,7 @@ class MisoClient:
|
|
|
347
349
|
def log(self) -> LoggerService:
|
|
348
350
|
"""
|
|
349
351
|
Get logger service for application logging.
|
|
350
|
-
|
|
352
|
+
|
|
351
353
|
Returns:
|
|
352
354
|
LoggerService instance
|
|
353
355
|
"""
|
|
@@ -358,12 +360,12 @@ class MisoClient:
|
|
|
358
360
|
def encrypt(self, plaintext: str) -> str:
|
|
359
361
|
"""
|
|
360
362
|
Encrypt sensitive data.
|
|
361
|
-
|
|
363
|
+
|
|
362
364
|
Convenience method that delegates to encryption service.
|
|
363
|
-
|
|
365
|
+
|
|
364
366
|
Args:
|
|
365
367
|
plaintext: Plain text string to encrypt
|
|
366
|
-
|
|
368
|
+
|
|
367
369
|
Returns:
|
|
368
370
|
Base64-encoded encrypted string
|
|
369
371
|
"""
|
|
@@ -372,12 +374,12 @@ class MisoClient:
|
|
|
372
374
|
def decrypt(self, encrypted_text: str) -> str:
|
|
373
375
|
"""
|
|
374
376
|
Decrypt sensitive data.
|
|
375
|
-
|
|
377
|
+
|
|
376
378
|
Convenience method that delegates to encryption service.
|
|
377
|
-
|
|
379
|
+
|
|
378
380
|
Args:
|
|
379
381
|
encrypted_text: Base64-encoded encrypted string
|
|
380
|
-
|
|
382
|
+
|
|
381
383
|
Returns:
|
|
382
384
|
Decrypted plain text string
|
|
383
385
|
"""
|
|
@@ -388,12 +390,12 @@ class MisoClient:
|
|
|
388
390
|
async def cache_get(self, key: str) -> Optional[Any]:
|
|
389
391
|
"""
|
|
390
392
|
Get cached value.
|
|
391
|
-
|
|
393
|
+
|
|
392
394
|
Convenience method that delegates to cache service.
|
|
393
|
-
|
|
395
|
+
|
|
394
396
|
Args:
|
|
395
397
|
key: Cache key
|
|
396
|
-
|
|
398
|
+
|
|
397
399
|
Returns:
|
|
398
400
|
Cached value if found, None otherwise
|
|
399
401
|
"""
|
|
@@ -402,14 +404,14 @@ class MisoClient:
|
|
|
402
404
|
async def cache_set(self, key: str, value: Any, ttl: int) -> bool:
|
|
403
405
|
"""
|
|
404
406
|
Set cached value with TTL.
|
|
405
|
-
|
|
407
|
+
|
|
406
408
|
Convenience method that delegates to cache service.
|
|
407
|
-
|
|
409
|
+
|
|
408
410
|
Args:
|
|
409
411
|
key: Cache key
|
|
410
412
|
value: Value to cache
|
|
411
413
|
ttl: Time to live in seconds
|
|
412
|
-
|
|
414
|
+
|
|
413
415
|
Returns:
|
|
414
416
|
True if successful, False otherwise
|
|
415
417
|
"""
|
|
@@ -418,12 +420,12 @@ class MisoClient:
|
|
|
418
420
|
async def cache_delete(self, key: str) -> bool:
|
|
419
421
|
"""
|
|
420
422
|
Delete cached value.
|
|
421
|
-
|
|
423
|
+
|
|
422
424
|
Convenience method that delegates to cache service.
|
|
423
|
-
|
|
425
|
+
|
|
424
426
|
Args:
|
|
425
427
|
key: Cache key
|
|
426
|
-
|
|
428
|
+
|
|
427
429
|
Returns:
|
|
428
430
|
True if deleted, False otherwise
|
|
429
431
|
"""
|
|
@@ -432,7 +434,7 @@ class MisoClient:
|
|
|
432
434
|
async def cache_clear(self) -> None:
|
|
433
435
|
"""
|
|
434
436
|
Clear all cached values.
|
|
435
|
-
|
|
437
|
+
|
|
436
438
|
Convenience method that delegates to cache service.
|
|
437
439
|
"""
|
|
438
440
|
await self.cache.clear()
|
|
@@ -442,7 +444,7 @@ class MisoClient:
|
|
|
442
444
|
def get_config(self) -> MisoClientConfig:
|
|
443
445
|
"""
|
|
444
446
|
Get current configuration.
|
|
445
|
-
|
|
447
|
+
|
|
446
448
|
Returns:
|
|
447
449
|
Copy of current configuration
|
|
448
450
|
"""
|
|
@@ -451,7 +453,7 @@ class MisoClient:
|
|
|
451
453
|
def is_redis_connected(self) -> bool:
|
|
452
454
|
"""
|
|
453
455
|
Check if Redis is connected.
|
|
454
|
-
|
|
456
|
+
|
|
455
457
|
Returns:
|
|
456
458
|
True if Redis is connected, False otherwise
|
|
457
459
|
"""
|
miso_client/errors.py
CHANGED
|
@@ -7,11 +7,13 @@ This module defines custom exceptions for the MisoClient SDK.
|
|
|
7
7
|
|
|
8
8
|
class MisoClientError(Exception):
|
|
9
9
|
"""Base exception for MisoClient SDK errors."""
|
|
10
|
-
|
|
11
|
-
def __init__(
|
|
10
|
+
|
|
11
|
+
def __init__(
|
|
12
|
+
self, message: str, status_code: int | None = None, error_body: dict | None = None
|
|
13
|
+
):
|
|
12
14
|
"""
|
|
13
15
|
Initialize MisoClient error.
|
|
14
|
-
|
|
16
|
+
|
|
15
17
|
Args:
|
|
16
18
|
message: Error message
|
|
17
19
|
status_code: HTTP status code if applicable
|
|
@@ -25,20 +27,23 @@ class MisoClientError(Exception):
|
|
|
25
27
|
|
|
26
28
|
class AuthenticationError(MisoClientError):
|
|
27
29
|
"""Raised when authentication fails."""
|
|
30
|
+
|
|
28
31
|
pass
|
|
29
32
|
|
|
30
33
|
|
|
31
34
|
class AuthorizationError(MisoClientError):
|
|
32
35
|
"""Raised when authorization check fails."""
|
|
36
|
+
|
|
33
37
|
pass
|
|
34
38
|
|
|
35
39
|
|
|
36
40
|
class ConnectionError(MisoClientError):
|
|
37
41
|
"""Raised when connection to controller or Redis fails."""
|
|
42
|
+
|
|
38
43
|
pass
|
|
39
44
|
|
|
40
45
|
|
|
41
46
|
class ConfigurationError(MisoClientError):
|
|
42
47
|
"""Raised when configuration is invalid."""
|
|
43
|
-
pass
|
|
44
48
|
|
|
49
|
+
pass
|