miso-client 0.1.0__py3-none-any.whl → 3.7.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.
Files changed (69) hide show
  1. miso_client/__init__.py +523 -130
  2. miso_client/api/__init__.py +35 -0
  3. miso_client/api/auth_api.py +367 -0
  4. miso_client/api/logs_api.py +91 -0
  5. miso_client/api/permissions_api.py +88 -0
  6. miso_client/api/roles_api.py +88 -0
  7. miso_client/api/types/__init__.py +75 -0
  8. miso_client/api/types/auth_types.py +183 -0
  9. miso_client/api/types/logs_types.py +71 -0
  10. miso_client/api/types/permissions_types.py +31 -0
  11. miso_client/api/types/roles_types.py +31 -0
  12. miso_client/errors.py +30 -4
  13. miso_client/models/__init__.py +4 -0
  14. miso_client/models/config.py +275 -72
  15. miso_client/models/error_response.py +39 -0
  16. miso_client/models/filter.py +255 -0
  17. miso_client/models/pagination.py +44 -0
  18. miso_client/models/sort.py +25 -0
  19. miso_client/services/__init__.py +6 -5
  20. miso_client/services/auth.py +496 -87
  21. miso_client/services/cache.py +42 -41
  22. miso_client/services/encryption.py +18 -17
  23. miso_client/services/logger.py +467 -328
  24. miso_client/services/logger_chain.py +288 -0
  25. miso_client/services/permission.py +130 -67
  26. miso_client/services/redis.py +28 -23
  27. miso_client/services/role.py +145 -62
  28. miso_client/utils/__init__.py +3 -3
  29. miso_client/utils/audit_log_queue.py +222 -0
  30. miso_client/utils/auth_strategy.py +88 -0
  31. miso_client/utils/auth_utils.py +65 -0
  32. miso_client/utils/circuit_breaker.py +125 -0
  33. miso_client/utils/client_token_manager.py +244 -0
  34. miso_client/utils/config_loader.py +88 -17
  35. miso_client/utils/controller_url_resolver.py +80 -0
  36. miso_client/utils/data_masker.py +104 -33
  37. miso_client/utils/environment_token.py +126 -0
  38. miso_client/utils/error_utils.py +216 -0
  39. miso_client/utils/fastapi_endpoints.py +166 -0
  40. miso_client/utils/filter.py +364 -0
  41. miso_client/utils/filter_applier.py +143 -0
  42. miso_client/utils/filter_parser.py +110 -0
  43. miso_client/utils/flask_endpoints.py +169 -0
  44. miso_client/utils/http_client.py +494 -262
  45. miso_client/utils/http_client_logging.py +352 -0
  46. miso_client/utils/http_client_logging_helpers.py +197 -0
  47. miso_client/utils/http_client_query_helpers.py +138 -0
  48. miso_client/utils/http_error_handler.py +92 -0
  49. miso_client/utils/http_log_formatter.py +115 -0
  50. miso_client/utils/http_log_masker.py +203 -0
  51. miso_client/utils/internal_http_client.py +435 -0
  52. miso_client/utils/jwt_tools.py +125 -16
  53. miso_client/utils/logger_helpers.py +206 -0
  54. miso_client/utils/logging_helpers.py +70 -0
  55. miso_client/utils/origin_validator.py +128 -0
  56. miso_client/utils/pagination.py +275 -0
  57. miso_client/utils/request_context.py +285 -0
  58. miso_client/utils/sensitive_fields_loader.py +116 -0
  59. miso_client/utils/sort.py +116 -0
  60. miso_client/utils/token_utils.py +114 -0
  61. miso_client/utils/url_validator.py +66 -0
  62. miso_client/utils/user_token_refresh.py +245 -0
  63. miso_client-3.7.2.dist-info/METADATA +1021 -0
  64. miso_client-3.7.2.dist-info/RECORD +68 -0
  65. miso_client-0.1.0.dist-info/METADATA +0 -551
  66. miso_client-0.1.0.dist-info/RECORD +0 -23
  67. {miso_client-0.1.0.dist-info → miso_client-3.7.2.dist-info}/WHEEL +0 -0
  68. {miso_client-0.1.0.dist-info → miso_client-3.7.2.dist-info}/licenses/LICENSE +0 -0
  69. {miso_client-0.1.0.dist-info → miso_client-3.7.2.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,1021 @@
1
+ Metadata-Version: 2.4
2
+ Name: miso-client
3
+ Version: 3.7.2
4
+ Summary: Python client SDK for AI Fabrix authentication, authorization, and logging
5
+ Home-page: https://github.com/aifabrix/miso-client-python
6
+ Author: AI Fabrix Team
7
+ Author-email: AI Fabrix Team <team@aifabrix.ai>
8
+ Maintainer-email: AI Fabrix Team <team@aifabrix.ai>
9
+ License-Expression: MIT
10
+ Project-URL: Homepage, https://github.com/aifabrix/miso-client-python
11
+ Project-URL: Documentation, https://docs.aifabrix.ai/miso-client-python
12
+ Project-URL: Repository, https://github.com/aifabrix/miso-client-python
13
+ Project-URL: Issues, https://github.com/aifabrix/miso-client-python/issues
14
+ Keywords: authentication,authorization,rbac,jwt,redis,logging,aifabrix,miso
15
+ Classifier: Development Status :: 4 - Beta
16
+ Classifier: Intended Audience :: Developers
17
+ Classifier: Operating System :: OS Independent
18
+ Classifier: Programming Language :: Python :: 3
19
+ Classifier: Programming Language :: Python :: 3.8
20
+ Classifier: Programming Language :: Python :: 3.9
21
+ Classifier: Programming Language :: Python :: 3.10
22
+ Classifier: Programming Language :: Python :: 3.11
23
+ Classifier: Programming Language :: Python :: 3.12
24
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
25
+ Classifier: Topic :: Security
26
+ Classifier: Topic :: System :: Logging
27
+ Requires-Python: >=3.8
28
+ Description-Content-Type: text/markdown
29
+ License-File: LICENSE
30
+ Requires-Dist: pydantic>=2.0.0
31
+ Requires-Dist: httpx>=0.25.0
32
+ Requires-Dist: redis[hiredis]>=5.0.0
33
+ Requires-Dist: PyJWT>=2.8.0
34
+ Requires-Dist: cryptography>=41.0.0
35
+ Requires-Dist: python-dotenv>=1.0.0
36
+ Provides-Extra: dev
37
+ Requires-Dist: pytest>=7.4.3; extra == "dev"
38
+ Requires-Dist: pytest-asyncio>=0.21.1; extra == "dev"
39
+ Requires-Dist: pytest-mock>=3.12.0; extra == "dev"
40
+ Requires-Dist: pytest-cov>=4.1.0; extra == "dev"
41
+ Requires-Dist: black>=23.0.0; extra == "dev"
42
+ Requires-Dist: isort>=5.12.0; extra == "dev"
43
+ Requires-Dist: mypy>=1.5.0; extra == "dev"
44
+ Requires-Dist: ruff>=0.1.0; extra == "dev"
45
+ Provides-Extra: flask
46
+ Requires-Dist: flask>=2.0.0; extra == "flask"
47
+ Provides-Extra: fastapi
48
+ Requires-Dist: fastapi>=0.100.0; extra == "fastapi"
49
+ Dynamic: author
50
+ Dynamic: home-page
51
+ Dynamic: license-file
52
+ Dynamic: requires-python
53
+
54
+ # AI Fabrix Miso Client SDK (Python)
55
+
56
+ [![PyPI version](https://badge.fury.io/py/miso-client.svg)](https://badge.fury.io/py/miso-client)
57
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
58
+
59
+ The **AI Fabrix Miso Client SDK** provides authentication, authorization, and logging for Python applications integrated with the AI Fabrix platform.
60
+
61
+ ## ✨ Benefits
62
+
63
+ ### 🔐 Enterprise Security
64
+
65
+ ### **SSO and Federated Identity**
66
+
67
+ - Single Sign-On (SSO) with Keycloak
68
+ - OAuth 2.0 and OpenID Connect (OIDC) support
69
+ - Multi-factor authentication (MFA) ready
70
+ - Social login integration (Google, Microsoft, etc.)
71
+
72
+ ### **Centralized Access Control**
73
+
74
+ - Role-based access control (RBAC)
75
+ - Fine-grained permissions
76
+ - Dynamic policy enforcement
77
+ - Attribute-based access control (ABAC)
78
+
79
+ ### **API Security**
80
+
81
+ - JWT token validation
82
+ - API key authentication
83
+ - Token revocation support
84
+ - Secure token storage
85
+ - Data encryption/decryption (AES-256-GCM)
86
+
87
+ ### 📊 Compliance & Audit
88
+
89
+ ### **ISO 27001 Compliance**
90
+
91
+ - Comprehensive audit trails for all user actions and HTTP requests
92
+ - Automatic data masking for all sensitive information in logs
93
+ - HTTP request/response audit logging with masked sensitive data
94
+ - Data access logging and monitoring
95
+ - Security event tracking
96
+ - Accountability and non-repudiation
97
+ - Configurable sensitive fields via JSON configuration
98
+
99
+ ### **Regulatory Compliance**
100
+
101
+ - GDPR-ready data protection
102
+ - HIPAA-compliant audit logging
103
+ - SOC 2 audit trail requirements
104
+ - Industry-standard security controls
105
+
106
+ ### **Audit Capabilities**
107
+
108
+ - Real-time audit event logging
109
+ - Immutable audit records
110
+ - Forensic analysis support
111
+ - Compliance reporting automation
112
+
113
+ ### ⚡ Performance & Scalability
114
+
115
+ ### **Intelligent Caching**
116
+
117
+ - Redis-based role and permission caching
118
+ - Generic cache service with Redis and in-memory fallback
119
+ - Configurable cache TTL (default: 15 minutes)
120
+ - Automatic cache invalidation
121
+ - Fallback to controller when Redis unavailable
122
+
123
+ ### **High Availability**
124
+
125
+ - Automatic failover to controller
126
+ - Redundant infrastructure support
127
+ - Load balancing compatible
128
+ - Zero-downtime deployments
129
+
130
+ ### **Optimized Network**
131
+
132
+ - Efficient API calls with caching
133
+ - Batch operations support
134
+ - Connection pooling
135
+ - Minimal latency
136
+
137
+ ### 🛠️ Developer Experience
138
+
139
+ ### **Easy Integration**
140
+
141
+ - Progressive activation (6-step setup)
142
+ - Works with any framework (FastAPI, Django, Flask, Starlette)
143
+ - Python 3.8+ support with full type hints
144
+ - Async/await support throughout
145
+
146
+ ### **Flexible Configuration**
147
+
148
+ - Environment-based configuration
149
+ - Support for dev, test, and production environments
150
+ - Docker and Kubernetes ready
151
+ - CI/CD friendly
152
+
153
+ ### **Observability**
154
+
155
+ - Centralized logging with correlation IDs
156
+ - Automatic HTTP request/response audit logging (ISO 27001 compliant)
157
+ - Debug logging with detailed request/response information (when `log_level='debug'`)
158
+ - Performance tracking and metrics
159
+ - Error tracking and debugging
160
+ - Health monitoring
161
+ - Automatic data masking for sensitive information in logs
162
+ - Configurable sensitive fields via JSON configuration
163
+
164
+ ---
165
+
166
+ ## 🚀 Quick Start
167
+
168
+ Get your application secured in 30 seconds.
169
+
170
+ ### Step 1: Install
171
+
172
+ ```bash
173
+ pip install miso-client
174
+ ```
175
+
176
+ ### Step 2: Create `.env`
177
+
178
+ ```bash
179
+ MISO_CLIENTID=ctrl-dev-my-app
180
+ MISO_CLIENTSECRET=your-secret
181
+ MISO_CONTROLLER_URL=http://localhost:3000
182
+ REDIS_HOST=localhost
183
+ ```
184
+
185
+ ### Step 3: Use It
186
+
187
+ ```python
188
+ from miso_client import MisoClient, load_config
189
+
190
+ client = MisoClient(load_config())
191
+ await client.initialize()
192
+
193
+ is_valid = await client.validate_token(token)
194
+ ```
195
+
196
+ **That's it!** You now have authentication, roles, and logging.
197
+
198
+ → [Full Getting Started Guide](docs/getting-started.md)
199
+
200
+ ---
201
+
202
+ ### Infrastructure Setup
203
+
204
+ **First time?** You'll need Keycloak and Miso Controller running.
205
+
206
+ Use the [AI Fabrix Builder](https://github.com/esystemsdev/aifabrix-builder/blob/main/docs/QUICK-START.md):
207
+
208
+ ```bash
209
+ # Start infrastructure (Postgres, Redis)
210
+ aifabrix up
211
+
212
+ # Install Keycloak for authentication
213
+ aifabrix create keycloak --port 8082 --database --template platform
214
+ aifabrix build keycloak
215
+ aifabrix run keycloak
216
+
217
+ # Install Miso Controller
218
+ aifabrix create miso-controller --port 3000 --database --redis --template platform
219
+ aifabrix build miso-controller
220
+ aifabrix run miso-controller
221
+ ```
222
+
223
+ → [Infrastructure Guide](https://github.com/esystemsdev/aifabrix-builder/blob/main/docs/INFRASTRUCTURE.md)
224
+
225
+ **Already have Keycloak and Controller?** Use the Quick Start above.
226
+
227
+ ---
228
+
229
+ ## 📚 Documentation
230
+
231
+ **What happens:** Your app validates user tokens from Keycloak.
232
+
233
+ ```python
234
+ from miso_client import MisoClient, load_config
235
+
236
+ # Create client (loads from .env automatically)
237
+ client = MisoClient(load_config())
238
+ await client.initialize()
239
+
240
+ # Get token from request (helper method)
241
+ token = client.get_token(req)
242
+
243
+ if token:
244
+ is_valid = await client.validate_token(token)
245
+ if is_valid:
246
+ user = await client.get_user(token)
247
+ print('User:', user)
248
+ ```
249
+
250
+ **Where to get tokens?** Users authenticate via Keycloak, then your app receives JWTs in the `Authorization` header.
251
+
252
+ → [Complete authentication example](examples/step-3-authentication.py)
253
+
254
+ ---
255
+
256
+ ### Step 4: Activate RBAC (Roles)
257
+
258
+ **What happens:** Check user roles to control access. Roles are cached in Redis for performance.
259
+
260
+ ```python
261
+ from miso_client import MisoClient, load_config
262
+
263
+ # Build on Step 3 - add Redis in .env file
264
+ client = MisoClient(load_config())
265
+ await client.initialize()
266
+
267
+ token = client.get_token(req)
268
+
269
+ # Check if user has role
270
+ is_admin = await client.has_role(token, 'admin')
271
+ roles = await client.get_roles(token)
272
+
273
+ # Gate features by role
274
+ if is_admin:
275
+ # Show admin panel
276
+ pass
277
+ ```
278
+
279
+ **Pro tip:** Without Redis, checks go to the controller. Add Redis to cache role lookups (15-minute default TTL).
280
+
281
+ → [Complete RBAC example](examples/step-4-rbac.py)
282
+ → [AI Fabrix Builder Quick Start](https://github.com/esystemsdev/aifabrix-builder/blob/main/docs/QUICK-START.md)
283
+
284
+ ---
285
+
286
+ ### Step 5: Activate Logging
287
+
288
+ **What happens:** Application logs are sent to the Miso Controller with client token authentication. All HTTP requests are automatically audited with ISO 27001 compliant data masking.
289
+
290
+ ```python
291
+ from miso_client import MisoClient, load_config
292
+
293
+ # Client token is automatically managed - no API key needed
294
+ client = MisoClient(load_config())
295
+ await client.initialize()
296
+
297
+ token = client.get_token(req)
298
+ user = await client.get_user(token)
299
+
300
+ # Log messages
301
+ await client.log.info('User accessed dashboard', {'userId': user.id if user else None})
302
+ await client.log.error('Operation failed', {'error': str(err)})
303
+ await client.log.warn('Unusual activity', {'details': '...'})
304
+
305
+ # HTTP requests are automatically audited
306
+ # All sensitive data is automatically masked before logging
307
+ result = await client.http_client.get('/api/users')
308
+ # This automatically creates an audit log: http.request.GET with masked sensitive data
309
+ ```
310
+
311
+ **What happens to logs?** They're sent to the Miso Controller for centralized monitoring and analysis. Client token is automatically included. Audit logs are automatically batched using `AuditLogQueue` for improved performance (configurable via `AuditConfig`).
312
+
313
+ **ISO 27001 Compliance:** All HTTP requests are automatically audited with sensitive data masked. Configure audit logging behavior using `AuditConfig`:
314
+
315
+ - **Audit Levels**: Choose from `minimal`, `standard`, `detailed`, or `full` (default: `detailed`)
316
+ - `minimal`: Only metadata, no masking
317
+ - `standard`: Metadata + basic context
318
+ - `detailed`: Full context with request/response sizes (default)
319
+ - `full`: Complete audit trail with all available data
320
+ - **Performance Optimizations**:
321
+ - Response body truncation based on `maxResponseSize` configuration (default: 10000 bytes)
322
+ - Size-based masking skip for large objects (prevents performance degradation)
323
+ - Automatic batching via `AuditLogQueue` reduces HTTP overhead for high-volume logging
324
+ - Set `log_level='debug'` to enable detailed request/response logging (all sensitive data is still masked).
325
+
326
+ → [Complete logging example](examples/step-5-logging.py)
327
+ → [Logging Reference](docs/api-reference.md#logger-service)
328
+
329
+ ---
330
+
331
+ ### Step 6: Activate Audit
332
+
333
+ **What happens:** Create audit trails for compliance and security monitoring.
334
+
335
+ ```python
336
+ from miso_client import MisoClient, load_config
337
+
338
+ # Complete configuration (all in .env)
339
+ client = MisoClient(load_config())
340
+ await client.initialize()
341
+
342
+ token = client.get_token(req)
343
+ is_valid = await client.validate_token(token)
344
+ can_edit = await client.has_permission(token, 'edit:content')
345
+ user = await client.get_user(token)
346
+
347
+ # Audit: User actions
348
+ await client.log.audit('user.login', 'authentication', {
349
+ 'userId': user.id if user else None,
350
+ 'ip': req.get('ip', ''),
351
+ 'userAgent': req.get('headers', {}).get('user-agent', ''),
352
+ })
353
+
354
+ # Audit: Content changes
355
+ await client.log.audit('post.created', 'content', {
356
+ 'userId': user.id if user else None,
357
+ 'postId': 'post-123',
358
+ 'postTitle': req.get('body', {}).get('title', ''),
359
+ })
360
+
361
+ # Audit: Permission checks
362
+ await client.log.audit('access.denied', 'authorization', {
363
+ 'userId': user.id if user else None,
364
+ 'requiredPermission': 'edit:content',
365
+ 'resource': 'posts',
366
+ })
367
+ ```
368
+
369
+ **What to audit:** Login/logout, permission checks, content creation/deletion, role changes, sensitive operations.
370
+
371
+ → [Complete audit example](examples/step-6-audit.py)
372
+ → [Best Practices](docs/getting-started.md#common-patterns)
373
+
374
+ ---
375
+
376
+ ### Encryption and Caching
377
+
378
+ **What happens:** Use encryption for sensitive data and generic caching for improved performance.
379
+
380
+ ```python
381
+ from miso_client import MisoClient, load_config
382
+
383
+ client = MisoClient(load_config())
384
+ await client.initialize()
385
+
386
+ # Encryption (requires ENCRYPTION_KEY in .env)
387
+ encrypted = client.encrypt('sensitive-data')
388
+ decrypted = client.decrypt(encrypted)
389
+ print('Decrypted:', decrypted)
390
+
391
+ # Generic caching (automatically uses Redis if available, falls back to memory)
392
+ await client.cache_set('user:123', {'name': 'John', 'age': 30}, 600) # 10 minutes TTL
393
+ user = await client.cache_get('user:123')
394
+ if user:
395
+ print('Cached user:', user)
396
+ ```
397
+
398
+ **Configuration:**
399
+
400
+ ```bash
401
+ # Add to .env
402
+ ENCRYPTION_KEY=your-32-byte-encryption-key
403
+ ```
404
+
405
+ → [API Reference](docs/api-reference.md#encryption-methods)
406
+ → [Cache Methods](docs/api-reference.md#cache-methods)
407
+
408
+ ---
409
+
410
+ ### Testing with API Key
411
+
412
+ **What happens:** When `API_KEY` is set in your `.env` file, you can authenticate requests using the API key as a bearer token, bypassing OAuth2 authentication. This is useful for testing without setting up Keycloak.
413
+
414
+ ```python
415
+ from miso_client import MisoClient, load_config
416
+
417
+ client = MisoClient(load_config())
418
+ await client.initialize()
419
+
420
+ # Use API_KEY as bearer token (for testing only)
421
+ api_key_token = "your-api-key-from-env"
422
+ is_valid = await client.validate_token(api_key_token)
423
+ # Returns True if token matches API_KEY from .env
424
+
425
+ user = await client.get_user(api_key_token)
426
+ # Returns None (API key auth doesn't provide user info)
427
+ ```
428
+
429
+ **Configuration:**
430
+
431
+ ```bash
432
+ # Add to .env for testing
433
+ API_KEY=your-test-api-key-here
434
+ ```
435
+
436
+ **Important:**
437
+
438
+ - API_KEY authentication bypasses OAuth2 validation completely
439
+ - User information methods (`get_user()`, `get_user_info()`) return `None` when using API_KEY
440
+ - Token validation returns `True` if the bearer token matches the configured `API_KEY`
441
+ - This feature is intended for testing and development only
442
+
443
+ ---
444
+
445
+ ## 🔧 Configuration
446
+
447
+ ```python
448
+ from miso_client import MisoClientConfig, RedisConfig, AuditConfig
449
+
450
+ config = MisoClientConfig(
451
+ controller_url="http://localhost:3000", # Required: Controller URL
452
+ client_id="ctrl-dev-my-app", # Required: Client ID
453
+ client_secret="your-secret", # Required: Client secret
454
+ redis=RedisConfig( # Optional: For caching
455
+ host="localhost",
456
+ port=6379,
457
+ ),
458
+ log_level="info", # Optional: 'debug' | 'info' | 'warn' | 'error'
459
+ # Set to 'debug' for detailed HTTP request/response logging
460
+ api_key="your-test-api-key", # Optional: API key for testing (bypasses OAuth2)
461
+ cache={ # Optional: Cache TTL settings
462
+ "role_ttl": 900, # Role cache TTL (default: 900s)
463
+ "permission_ttl": 900, # Permission cache TTL (default: 900s)
464
+ },
465
+ audit=AuditConfig( # Optional: Audit logging configuration
466
+ enabled=True, # Enable/disable audit logging (default: true)
467
+ level="detailed", # Audit detail level: 'minimal' | 'standard' | 'detailed' | 'full' (default: 'detailed')
468
+ maxResponseSize=10000, # Truncate responses larger than this in bytes (default: 10000)
469
+ maxMaskingSize=50000, # Skip masking for objects larger than this in bytes (default: 50000)
470
+ batchSize=10, # Batch size for queued logs (default: 10)
471
+ batchInterval=100, # Flush interval in milliseconds (default: 100)
472
+ skipEndpoints=None # Array of endpoint patterns to exclude from audit logging
473
+ )
474
+ )
475
+ ```
476
+
477
+ **Recommended:** Use `load_config()` to load from `.env` file automatically.
478
+
479
+ **ISO 27001 Data Masking Configuration:**
480
+
481
+ Sensitive fields are configured via `miso_client/utils/sensitive_fields_config.json`. You can customize this by:
482
+
483
+ 1. Setting `MISO_SENSITIVE_FIELDS_CONFIG` environment variable to point to a custom JSON file
484
+ 2. Using `DataMasker.set_config_path()` to set a custom path programmatically
485
+
486
+ The default configuration includes ISO 27001 compliant sensitive fields:
487
+
488
+ - Authentication: password, token, secret, key, auth, authorization
489
+ - PII: ssn, creditcard, cc, cvv, pin, otp
490
+ - Security: apikey, accesstoken, refreshtoken, privatekey, secretkey, cookie, session
491
+
492
+ **Audit Logging Configuration:**
493
+
494
+ Configure audit logging behavior using `AuditConfig` (see Configuration section above):
495
+
496
+ - **Audit Levels**: Control detail level (`minimal`, `standard`, `detailed`, `full`)
497
+ - **Response Truncation**: Configure `maxResponseSize` to truncate large responses (default: 10000 bytes)
498
+ - **Performance**: Set `maxMaskingSize` to skip masking for very large objects (default: 50000 bytes)
499
+ - **Batching**: Configure `batchSize` and `batchInterval` for audit log queuing (reduces HTTP overhead)
500
+
501
+ → [Complete Configuration Reference](docs/configuration.md)
502
+
503
+ ---
504
+
505
+ ## 📚 Read more
506
+
507
+ - **[Getting Started](docs/getting-started.md)** - Detailed setup guide
508
+ - **[API Reference](docs/api-reference.md)** - Complete API documentation
509
+ - **[Configuration](docs/configuration.md)** - Configuration options
510
+ - **[Examples](docs/examples.md)** - Framework-specific examples
511
+ - **[Troubleshooting](docs/troubleshooting.md)** - Common issues and solutions
512
+
513
+ ---
514
+
515
+ ## 🏗️ Architecture
516
+
517
+ The SDK consists of five core services:
518
+
519
+ - **AuthService** - Token validation and user authentication
520
+ - **RoleService** - Role management with Redis caching
521
+ - **PermissionService** - Fine-grained permissions
522
+ - **LoggerService** - Centralized logging with API key authentication
523
+ - **RedisService** - Caching and queue management (optional)
524
+
525
+ ### HTTP Client Architecture
526
+
527
+ The SDK uses a two-layer HTTP client architecture for ISO 27001 compliance:
528
+
529
+ - **InternalHttpClient** - Core HTTP functionality with automatic client token management (internal)
530
+ - **HttpClient** - Public wrapper that adds automatic ISO 27001 compliant audit and debug logging
531
+
532
+ **Features:**
533
+
534
+ - Automatic audit logging for all HTTP requests (`http.request.{METHOD}`)
535
+ - Configurable audit levels (`minimal`, `standard`, `detailed`, `full`) via `AuditConfig`
536
+ - Debug logging when `log_level === 'debug'` with detailed request/response information
537
+ - Automatic data masking using `DataMasker` before logging (ISO 27001 compliant)
538
+ - Sensitive endpoints (`/api/logs`, `/api/auth/token`) are excluded from audit logging to prevent infinite loops
539
+ - All sensitive data (headers, bodies, query params) is automatically masked before logging
540
+ - `AuditLogQueue` integration for automatic batching of audit logs (reduces HTTP overhead)
541
+ - Performance optimizations: response body truncation and size-based masking skip for large objects
542
+
543
+ **ISO 27001 Compliance:**
544
+
545
+ - All request headers are masked (Authorization, x-client-token, Cookie, etc.)
546
+ - All request bodies are recursively masked for sensitive fields (password, token, secret, SSN, etc.)
547
+ - All response bodies are masked and truncated based on `maxResponseSize` configuration (default: 10000 bytes)
548
+ - Query parameters are automatically masked
549
+ - Error messages are masked if they contain sensitive data
550
+ - Sensitive fields configuration can be customized via `sensitive_fields_config.json`
551
+ - Configurable audit levels control the detail level of audit logs (minimal, standard, detailed, full)
552
+
553
+ → [Architecture Details](docs/api-reference.md#architecture)
554
+
555
+ ---
556
+
557
+ ## 🌐 Setup Your Application
558
+
559
+ **First time setup?** Use the AI Fabrix Builder:
560
+
561
+ 1. **Create your app:**
562
+
563
+ ```bash
564
+ aifabrix create myapp --port 3000 --database --language python
565
+ ```
566
+
567
+ 2. **Login to controller:**
568
+
569
+ ```bash
570
+ aifabrix login
571
+ ```
572
+
573
+ 3. **Register your application:**
574
+
575
+ ```bash
576
+ aifabrix app register myapp --environment dev
577
+ ```
578
+
579
+ 4. **Start development** and then deploy to Docker or Azure.
580
+
581
+ → [Full Quick Start Guide](https://github.com/esystemsdev/aifabrix-builder/blob/main/docs/QUICK-START.md)
582
+
583
+ ---
584
+
585
+ ## 💡 Next Steps
586
+
587
+ ### Learn More
588
+
589
+ - [FastAPI Integration](docs/examples.md#fastapi-integration) - Protect API routes
590
+ - [Django Middleware](docs/examples.md#django-middleware) - Django integration
591
+ - [Flask Decorators](docs/examples.md#flask-decorators) - Decorator-based auth
592
+ - [Error Handling](docs/examples.md#error-handling) - Best practices
593
+
594
+ ---
595
+
596
+ ### Structured Error Responses
597
+
598
+ **What happens:** The SDK automatically parses structured error responses from the API (RFC 7807-style format) and makes them available through the `MisoClientError` and `ApiErrorException` exceptions.
599
+
600
+ ```python
601
+ from miso_client import MisoClient, MisoClientError, ApiErrorException, ErrorResponse, load_config, handleApiError
602
+
603
+ client = MisoClient(load_config())
604
+ await client.initialize()
605
+
606
+ try:
607
+ result = await client.http_client.get("/api/some-endpoint")
608
+ except MisoClientError as e:
609
+ # Check if structured error response is available
610
+ if e.error_response:
611
+ print(f"Error Type: {e.error_response.type}")
612
+ print(f"Error Title: {e.error_response.title}")
613
+ print(f"Status Code: {e.error_response.statusCode}")
614
+ print(f"Errors: {e.error_response.errors}")
615
+ print(f"Instance: {e.error_response.instance}")
616
+ else:
617
+ # Fallback to traditional error handling
618
+ print(f"Error: {e.message}")
619
+ print(f"Status Code: {e.status_code}")
620
+ print(f"Error Body: {e.error_body}")
621
+
622
+ # Using handleApiError() for structured error handling
623
+ try:
624
+ response_data = {"errors": ["Validation failed"], "type": "/Errors/Validation", "title": "Validation Error", "statusCode": 422}
625
+ error = handleApiError(response_data, 422, "/api/endpoint")
626
+ # handleApiError() returns ApiErrorException (extends MisoClientError)
627
+ if isinstance(error, ApiErrorException):
628
+ print(f"Structured Error: {error.error_response.title}")
629
+ except ApiErrorException as e:
630
+ # ApiErrorException provides better structured error information
631
+ print(f"API Error: {e.error_response.title}")
632
+ print(f"Errors: {e.error_response.errors}")
633
+ ```
634
+
635
+ **Error Response Structure:**
636
+
637
+ The `ErrorResponse` model follows RFC 7807-style format:
638
+
639
+ ```json
640
+ {
641
+ "errors": [
642
+ "The user has provided input that the browser is unable to convert.",
643
+ "There are multiple rows in the database for the same value"
644
+ ],
645
+ "type": "/Errors/Bad Input",
646
+ "title": "Bad Request",
647
+ "statusCode": 400,
648
+ "instance": "/OpenApi/rest/Xzy"
649
+ }
650
+ ```
651
+
652
+ **Features:**
653
+
654
+ - **Automatic Parsing**: Structured error responses are automatically parsed from HTTP responses
655
+ - **ApiErrorException**: New exception class (extends `MisoClientError`) for better structured error handling
656
+ - `handleApiError()` returns `ApiErrorException` with structured error response support
657
+ - Legacy `handle_api_error_snake_case()` still returns `MisoClientError` for backward compatibility
658
+ - **Backward Compatible**: Falls back to traditional error handling when structured format is not available
659
+ - **Type Safety**: Full type hints with Pydantic models for reliable error handling
660
+ - **Generic Interface**: `ErrorResponse` model can be reused across different applications
661
+ - **Instance URI**: Automatically extracted from request URL if not provided in response
662
+
663
+ **Using ErrorResponse directly:**
664
+
665
+ ```python
666
+ from miso_client import ErrorResponse
667
+
668
+ # Create ErrorResponse from dict
669
+ error_data = {
670
+ "errors": ["Validation failed"],
671
+ "type": "/Errors/Validation",
672
+ "title": "Validation Error",
673
+ "statusCode": 422,
674
+ "instance": "/api/endpoint"
675
+ }
676
+ error_response = ErrorResponse(**error_data)
677
+
678
+ # Access fields
679
+ print(error_response.errors) # ["Validation failed"]
680
+ print(error_response.type) # "/Errors/Validation"
681
+ print(error_response.title) # "Validation Error"
682
+ print(error_response.statusCode) # 422
683
+ print(error_response.instance) # "/api/endpoint"
684
+ ```
685
+
686
+ ---
687
+
688
+ ### Pagination, Filtering, and Sorting Utilities
689
+
690
+ **What happens:** The SDK provides reusable utilities for pagination, filtering, sorting, and error handling that work with any API endpoint.
691
+
692
+ #### Pagination
693
+
694
+ **Pagination Parameters:**
695
+
696
+ - `page`: Page number (1-based, defaults to 1)
697
+ - `page_size`: Number of items per page (defaults to 20)
698
+
699
+ ```python
700
+ from miso_client import (
701
+ parse_pagination_params,
702
+ parsePaginationParams, # camelCase alternative
703
+ create_paginated_list_response,
704
+ createPaginatedListResponse, # camelCase alternative
705
+ PaginatedListResponse,
706
+ )
707
+
708
+ # Parse pagination from query parameters (snake_case - returns tuple)
709
+ params = {"page": "1", "page_size": "20"}
710
+ current_page, page_size = parse_pagination_params(params)
711
+
712
+ # Or use camelCase function (returns dict with currentPage/pageSize keys)
713
+ pagination = parsePaginationParams(params)
714
+ # Returns: {"currentPage": 1, "pageSize": 20}
715
+
716
+ # Create paginated response
717
+ items = [{"id": 1}, {"id": 2}]
718
+ response = create_paginated_list_response(
719
+ items,
720
+ total_items=120,
721
+ current_page=1,
722
+ page_size=20,
723
+ type="item"
724
+ )
725
+
726
+ # Response structure:
727
+ # {
728
+ # "meta": {
729
+ # "total_items": 120,
730
+ # "current_page": 1,
731
+ # "page_size": 20,
732
+ # "type": "item"
733
+ # },
734
+ # "data": [{"id": 1}, {"id": 2}]
735
+ # }
736
+ ```
737
+
738
+ #### Filtering
739
+
740
+ **Filter Operators:** `eq`, `neq`, `in`, `nin`, `gt`, `lt`, `gte`, `lte`, `contains`, `like`
741
+
742
+ **Filter Format:** `field:op:value` (e.g., `status:eq:active`)
743
+
744
+ ```python
745
+ from miso_client import FilterBuilder, parse_filter_params, build_query_string
746
+
747
+ # Dynamic filter building with FilterBuilder
748
+ filter_builder = FilterBuilder() \
749
+ .add('status', 'eq', 'active') \
750
+ .add('region', 'in', ['eu', 'us']) \
751
+ .add('created_at', 'gte', '2024-01-01')
752
+
753
+ # Get query string
754
+ query_string = filter_builder.to_query_string()
755
+ # Returns: "filter=status:eq:active&filter=region:in:eu,us&filter=created_at:gte:2024-01-01"
756
+
757
+ # Parse existing filter parameters
758
+ params = {'filter': ['status:eq:active', 'region:in:eu,us']}
759
+ filters = parse_filter_params(params)
760
+ # Returns: [FilterOption(field='status', op='eq', value='active'), ...]
761
+
762
+ # Use with HTTP client
763
+ response = await client.http_client.get_with_filters(
764
+ '/api/items',
765
+ filter_builder=filter_builder
766
+ )
767
+ ```
768
+
769
+ **Building Complete Filter Queries:**
770
+
771
+ ```python
772
+ from miso_client import FilterQuery, FilterOption, build_query_string
773
+
774
+ # Create filter query with filters, sort, pagination, and fields
775
+ filter_query = FilterQuery(
776
+ filters=[
777
+ FilterOption(field='status', op='eq', value='active'),
778
+ FilterOption(field='region', op='in', value=['eu', 'us'])
779
+ ],
780
+ sort=['-updated_at', 'created_at'],
781
+ page=1,
782
+ page_size=20,
783
+ fields=['id', 'name', 'status']
784
+ )
785
+
786
+ # Build query string
787
+ query_string = build_query_string(filter_query)
788
+ ```
789
+
790
+ #### Sorting
791
+
792
+ **Sort Format:** `-field` for descending, `field` for ascending (e.g., `-updated_at`, `created_at`)
793
+
794
+ ```python
795
+ from miso_client import parse_sort_params, build_sort_string, SortOption
796
+
797
+ # Parse sort parameters
798
+ params = {'sort': '-updated_at'}
799
+ sort_options = parse_sort_params(params)
800
+ # Returns: [SortOption(field='updated_at', order='desc')]
801
+
802
+ # Parse multiple sorts
803
+ params = {'sort': ['-updated_at', 'created_at']}
804
+ sort_options = parse_sort_params(params)
805
+ # Returns: [
806
+ # SortOption(field='updated_at', order='desc'),
807
+ # SortOption(field='created_at', order='asc')
808
+ # ]
809
+
810
+ # Build sort string
811
+ sort_options = [
812
+ SortOption(field='updated_at', order='desc'),
813
+ SortOption(field='created_at', order='asc')
814
+ ]
815
+ sort_string = build_sort_string(sort_options)
816
+ # Returns: "-updated_at,created_at"
817
+ ```
818
+
819
+ #### Combined Usage
820
+
821
+ **Pagination + Filter + Sort:**
822
+
823
+ ```python
824
+ from miso_client import (
825
+ FilterBuilder,
826
+ FilterQuery,
827
+ build_query_string,
828
+ parse_pagination_params,
829
+ )
830
+
831
+ # Build filters
832
+ filter_builder = FilterBuilder() \
833
+ .add('status', 'eq', 'active') \
834
+ .add('region', 'in', ['eu', 'us'])
835
+
836
+ # Parse pagination
837
+ params = {'page': '1', 'page_size': '20'}
838
+ current_page, page_size = parse_pagination_params(params)
839
+
840
+ # Create complete query
841
+ filter_query = FilterQuery(
842
+ filters=filter_builder.build(),
843
+ sort=['-updated_at'],
844
+ page=current_page,
845
+ page_size=page_size
846
+ )
847
+
848
+ # Build query string
849
+ query_string = build_query_string(filter_query)
850
+
851
+ # Use with HTTP client
852
+ response = await client.http_client.get_with_filters(
853
+ '/api/items',
854
+ filter_builder=filter_builder,
855
+ params={'page': current_page, 'page_size': page_size}
856
+ )
857
+ ```
858
+
859
+ **Or use pagination helper:**
860
+
861
+ ```python
862
+ # Get paginated response
863
+ response = await client.http_client.get_paginated(
864
+ '/api/items',
865
+ page=1,
866
+ page_size=20
867
+ )
868
+
869
+ # Response is automatically parsed as PaginatedListResponse
870
+ print(response.meta.total_items) # 120
871
+ print(response.meta.current_page) # 1
872
+ print(len(response.data)) # 25
873
+ ```
874
+
875
+ #### Metadata Filter Integration
876
+
877
+ **Working with `/metadata/filter` endpoint:**
878
+
879
+ ```python
880
+ # Get metadata filters from endpoint
881
+ metadata_response = await client.http_client.post(
882
+ "/api/v1/metadata/filter",
883
+ {"documentStorageKey": "my-doc-storage"}
884
+ )
885
+
886
+ # Convert AccessFieldFilter to FilterBuilder
887
+ filter_builder = FilterBuilder()
888
+ for access_filter in metadata_response.mandatoryFilters:
889
+ filter_builder.add(access_filter.field, 'in', access_filter.values)
890
+
891
+ # Use with query utilities
892
+ query_string = filter_builder.to_query_string()
893
+
894
+ # Apply to API requests
895
+ response = await client.http_client.get_with_filters(
896
+ '/api/items',
897
+ filter_builder=filter_builder
898
+ )
899
+ ```
900
+
901
+ **Features:**
902
+
903
+ - **Snake_case Convention**: All utilities use snake_case to match Miso/Dataplane API
904
+ - **camelCase Alternatives**: camelCase function names are available for all utilities (backward compatible)
905
+ - `parsePaginationParams()` - Returns dict with `currentPage`/`pageSize` keys (alias: `parse_pagination_params()`)
906
+ - `createMetaObject()` - Creates `Meta` objects with camelCase fields (alias: `create_meta_object()`)
907
+ - `applyPaginationToArray()` - Applies pagination to arrays (alias: `apply_pagination_to_array()`)
908
+ - `createPaginatedListResponse()` - Creates paginated list responses (alias: `create_paginated_list_response()`)
909
+ - `transformError()` - Transforms error dictionaries to `ErrorResponse` objects (alias: `transform_error_to_snake_case()`)
910
+ - `handleApiError()` - Creates `ApiErrorException` from API error responses (alias: `handle_api_error_snake_case()`)
911
+ - **Type Safety**: Full type hints with Pydantic models
912
+ - **Dynamic Filtering**: FilterBuilder supports method chaining for complex filters
913
+ - **Local Testing**: `apply_filters()` and `apply_pagination_to_array()` for local filtering/pagination in tests
914
+ - **URL Encoding**: Automatic URL encoding for field names and values
915
+ - **Backward Compatible**: Works alongside existing HTTP client methods
916
+
917
+ ---
918
+
919
+ ### Common Tasks
920
+
921
+ **Add authentication middleware (FastAPI):**
922
+
923
+ ```python
924
+ from fastapi import Depends, HTTPException, Security
925
+ from fastapi.security import HTTPBearer
926
+ from miso_client import MisoClient
927
+
928
+ security = HTTPBearer()
929
+ client = MisoClient(load_config())
930
+
931
+ async def get_current_user(credentials = Security(security)):
932
+ token = credentials.credentials
933
+ is_valid = await client.validate_token(token)
934
+ if not is_valid:
935
+ raise HTTPException(status_code=401, detail="Invalid token")
936
+ return await client.get_user(token)
937
+ ```
938
+
939
+ **Protect routes by role (FastAPI):**
940
+
941
+ ```python
942
+ @app.get('/admin')
943
+ async def admin_panel(user = Depends(get_current_user), credentials = Security(security)):
944
+ token = credentials.credentials
945
+ is_admin = await client.has_role(token, 'admin')
946
+ if not is_admin:
947
+ raise HTTPException(status_code=403, detail="Forbidden")
948
+
949
+ # Admin only code
950
+ return {"message": "Admin panel"}
951
+ ```
952
+
953
+ **Use environment variables:**
954
+
955
+ ```bash
956
+ MISO_CLIENTID=ctrl-dev-my-app
957
+ MISO_CLIENTSECRET=your-secret
958
+ MISO_CONTROLLER_URL=http://localhost:3000
959
+ REDIS_HOST=localhost
960
+ REDIS_PORT=6379
961
+ MISO_LOG_LEVEL=info
962
+ API_KEY=your-test-api-key # Optional: For testing (bypasses OAuth2)
963
+ ```
964
+
965
+ ---
966
+
967
+ ## 🐛 Troubleshooting
968
+
969
+ **"Cannot connect to controller"**
970
+ → Verify `controllerUrl` is correct and accessible
971
+ → Check network connectivity
972
+
973
+ **"Redis connection failed"**
974
+ → SDK falls back to controller-only mode (slower but works)
975
+ → Fix: `aifabrix up` to start Redis
976
+
977
+ **"Client token fetch failed"**
978
+ → Check `MISO_CLIENTID` and `MISO_CLIENTSECRET` are correct
979
+ → Verify credentials are configured in controller
980
+ → Ensure `ENCRYPTION_KEY` environment variable is set (required for encryption service)
981
+
982
+ **"Token validation fails"**
983
+ → Ensure Keycloak is running and configured correctly
984
+ → Verify token is from correct Keycloak instance
985
+ → Check that `python-dotenv` is installed if using `.env` files
986
+
987
+ → [More Help](docs/troubleshooting.md)
988
+
989
+ ---
990
+
991
+ ## 📦 Installation
992
+
993
+ ```bash
994
+ # pip
995
+ pip install miso-client
996
+
997
+ # Development mode
998
+ pip install -e .
999
+
1000
+ # With dev dependencies
1001
+ pip install "miso-client[dev]"
1002
+ ```
1003
+
1004
+ ---
1005
+
1006
+ ## 🔗 Links
1007
+
1008
+ - **GitHub Repository**: [https://github.com/esystemsdev/aifabrix-miso-client-python](https://github.com/esystemsdev/aifabrix-miso-client-python)
1009
+ - **PyPI Package**: [https://pypi.org/project/miso-client/](https://pypi.org/project/miso-client/)
1010
+ - **Builder Documentation**: [https://github.com/esystemsdev/aifabrix-builder](https://github.com/esystemsdev/aifabrix-builder)
1011
+ - **Issues**: [https://github.com/esystemsdev/aifabrix-miso-client-python/issues](https://github.com/esystemsdev/aifabrix-miso-client-python/issues)
1012
+
1013
+ ---
1014
+
1015
+ ## 📄 License
1016
+
1017
+ This project is licensed under the MIT License - see the [LICENSE](./LICENSE) file for details.
1018
+
1019
+ ---
1020
+
1021
+ **Made with ❤️ by eSystems Nordic Ltd.**