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
@@ -8,17 +8,18 @@ in-memory TTL-based caching when Redis is unavailable.
8
8
 
9
9
  import json
10
10
  import time
11
- from typing import Any, Optional, Dict, Tuple
11
+ from typing import Any, Dict, Optional, Tuple
12
+
12
13
  from ..services.redis import RedisService
13
14
 
14
15
 
15
16
  class CacheService:
16
17
  """Cache service with Redis and in-memory TTL fallback."""
17
-
18
+
18
19
  def __init__(self, redis: Optional[RedisService] = None):
19
20
  """
20
21
  Initialize cache service.
21
-
22
+
22
23
  Args:
23
24
  redis: Optional RedisService instance. If provided, Redis will be used
24
25
  as primary cache with in-memory as fallback. If None, only
@@ -29,31 +30,32 @@ class CacheService:
29
30
  self._memory_cache: Dict[str, Tuple[Any, float]] = {}
30
31
  # Cleanup threshold: clean expired entries if cache exceeds this size
31
32
  self._cleanup_threshold = 1000
32
-
33
+
33
34
  def _is_expired(self, expiration: float) -> bool:
34
35
  """Check if entry has expired."""
35
36
  return time.time() > expiration
36
-
37
+
37
38
  def _cleanup_expired(self) -> None:
38
39
  """Remove expired entries from memory cache."""
39
40
  if len(self._memory_cache) <= self._cleanup_threshold:
40
41
  return
41
-
42
+
42
43
  expired_keys = [
43
- key for key, (_, expiration) in self._memory_cache.items()
44
+ key
45
+ for key, (_, expiration) in self._memory_cache.items()
44
46
  if self._is_expired(expiration)
45
47
  ]
46
-
48
+
47
49
  for key in expired_keys:
48
50
  del self._memory_cache[key]
49
-
51
+
50
52
  def _serialize_value(self, value: Any) -> str:
51
53
  """
52
54
  Serialize value to JSON string.
53
-
55
+
54
56
  Args:
55
57
  value: Value to serialize (can be any JSON-serializable type)
56
-
58
+
57
59
  Returns:
58
60
  JSON string representation
59
61
  """
@@ -62,23 +64,23 @@ class CacheService:
62
64
  if isinstance(value, str):
63
65
  return value
64
66
  return json.dumps(value)
65
-
67
+
66
68
  # For complex types, use JSON serialization with a marker
67
69
  return json.dumps({"__cached_value__": value})
68
-
70
+
69
71
  def _deserialize_value(self, value_str: str) -> Any:
70
72
  """
71
73
  Deserialize JSON string back to original value.
72
-
74
+
73
75
  Args:
74
76
  value_str: JSON string to deserialize
75
-
77
+
76
78
  Returns:
77
79
  Deserialized value
78
80
  """
79
81
  if not value_str:
80
82
  return None
81
-
83
+
82
84
  try:
83
85
  # Try to parse as JSON
84
86
  parsed = json.loads(value_str)
@@ -90,16 +92,16 @@ class CacheService:
90
92
  except (json.JSONDecodeError, TypeError):
91
93
  # If JSON parsing fails, assume it's a plain string
92
94
  return value_str
93
-
95
+
94
96
  async def get(self, key: str) -> Optional[Any]:
95
97
  """
96
98
  Get cached value.
97
-
99
+
98
100
  Checks Redis first (if available), then falls back to in-memory cache.
99
-
101
+
100
102
  Args:
101
103
  key: Cache key
102
-
104
+
103
105
  Returns:
104
106
  Cached value if found, None otherwise
105
107
  """
@@ -112,7 +114,7 @@ class CacheService:
112
114
  except Exception:
113
115
  # Redis operation failed, fall through to memory cache
114
116
  pass
115
-
117
+
116
118
  # Fallback to in-memory cache
117
119
  if key in self._memory_cache:
118
120
  value, expiration = self._memory_cache[key]
@@ -121,26 +123,26 @@ class CacheService:
121
123
  else:
122
124
  # Entry expired, remove it
123
125
  del self._memory_cache[key]
124
-
126
+
125
127
  return None
126
-
128
+
127
129
  async def set(self, key: str, value: Any, ttl: int) -> bool:
128
130
  """
129
131
  Set cached value with TTL.
130
-
132
+
131
133
  Stores in both Redis (if available) and in-memory cache.
132
-
134
+
133
135
  Args:
134
136
  key: Cache key
135
137
  value: Value to cache (any JSON-serializable type)
136
138
  ttl: Time to live in seconds
137
-
139
+
138
140
  Returns:
139
141
  True if successful, False otherwise
140
142
  """
141
143
  serialized_value = self._serialize_value(value)
142
144
  success = False
143
-
145
+
144
146
  # Store in Redis if available
145
147
  if self.redis and self.redis.is_connected():
146
148
  try:
@@ -148,57 +150,56 @@ class CacheService:
148
150
  except Exception:
149
151
  # Redis operation failed, continue to memory cache
150
152
  pass
151
-
153
+
152
154
  # Also store in memory cache
153
155
  expiration = time.time() + ttl
154
156
  self._memory_cache[key] = (value, expiration)
155
-
157
+
156
158
  # Cleanup expired entries periodically
157
159
  self._cleanup_expired()
158
-
160
+
159
161
  return success or True # Return True if at least memory cache succeeded
160
-
162
+
161
163
  async def delete(self, key: str) -> bool:
162
164
  """
163
165
  Delete cached value.
164
-
166
+
165
167
  Deletes from both Redis (if available) and in-memory cache.
166
-
168
+
167
169
  Args:
168
170
  key: Cache key
169
-
171
+
170
172
  Returns:
171
173
  True if deleted from at least one cache, False otherwise
172
174
  """
173
175
  deleted = False
174
-
176
+
175
177
  # Delete from Redis if available
176
178
  if self.redis and self.redis.is_connected():
177
179
  try:
178
180
  deleted = await self.redis.delete(key)
179
181
  except Exception:
180
182
  pass
181
-
183
+
182
184
  # Delete from memory cache
183
185
  if key in self._memory_cache:
184
186
  del self._memory_cache[key]
185
187
  deleted = True
186
-
188
+
187
189
  return deleted
188
-
190
+
189
191
  async def clear(self) -> None:
190
192
  """
191
193
  Clear all cached values.
192
-
194
+
193
195
  Clears both Redis (if available) and in-memory cache.
194
196
  Note: Redis clear operation only clears keys with the configured prefix,
195
197
  not the entire Redis database.
196
198
  """
197
199
  # Clear memory cache
198
200
  self._memory_cache.clear()
199
-
201
+
200
202
  # For Redis, we would need to delete all keys with the prefix
201
203
  # This is more complex and potentially dangerous, so we'll skip it
202
204
  # Users should use delete() for specific keys or clear Redis manually
203
205
  # if needed
204
-
@@ -6,36 +6,38 @@ in the application. It supports reading the encryption key from environment vari
6
6
  or accepting it as a constructor parameter.
7
7
  """
8
8
 
9
- import os
10
9
  import base64
10
+ import os
11
11
  from typing import Optional
12
+
12
13
  from cryptography.fernet import Fernet
14
+
13
15
  from ..errors import ConfigurationError
14
16
 
15
17
 
16
18
  class EncryptionService:
17
19
  """Service for encrypting/decrypting sensitive data using Fernet encryption."""
18
-
20
+
19
21
  def __init__(self, encryption_key: Optional[str] = None):
20
22
  """
21
23
  Initialize encryption service with key from environment or parameter.
22
-
24
+
23
25
  Args:
24
26
  encryption_key: Optional encryption key. If not provided, reads from EXTENSION_KEY env var.
25
27
  If provided, overrides environment variable.
26
-
28
+
27
29
  Raises:
28
30
  ConfigurationError: If encryption key is not found or invalid
29
31
  """
30
32
  # Use provided key, or fall back to environment variable
31
33
  key = encryption_key or os.environ.get("ENCRYPTION_KEY")
32
-
34
+
33
35
  if not key:
34
36
  raise ConfigurationError(
35
37
  "ENCRYPTION_KEY not found. Either set ENCRYPTION_KEY environment variable "
36
38
  "or pass encryption_key parameter to EncryptionService constructor."
37
39
  )
38
-
40
+
39
41
  try:
40
42
  # Fernet.generate_key() returns bytes, but env vars are strings
41
43
  # Convert string to bytes if needed
@@ -45,49 +47,48 @@ class EncryptionService:
45
47
  raise ConfigurationError(
46
48
  f"Failed to initialize encryption service with provided key: {str(e)}"
47
49
  ) from e
48
-
50
+
49
51
  def encrypt(self, plaintext: str) -> str:
50
52
  """
51
53
  Encrypt sensitive data.
52
-
54
+
53
55
  Args:
54
56
  plaintext: Plain text string to encrypt
55
-
57
+
56
58
  Returns:
57
59
  Base64-encoded encrypted string
58
-
60
+
59
61
  Raises:
60
62
  ValueError: If encryption fails
61
63
  """
62
64
  if not plaintext:
63
65
  return ""
64
-
66
+
65
67
  try:
66
68
  encrypted = self.fernet.encrypt(plaintext.encode())
67
69
  return base64.b64encode(encrypted).decode()
68
70
  except Exception as e:
69
71
  raise ValueError(f"Failed to encrypt data: {str(e)}") from e
70
-
72
+
71
73
  def decrypt(self, encrypted_text: str) -> str:
72
74
  """
73
75
  Decrypt sensitive data.
74
-
76
+
75
77
  Args:
76
78
  encrypted_text: Base64-encoded encrypted string
77
-
79
+
78
80
  Returns:
79
81
  Decrypted plain text string
80
-
82
+
81
83
  Raises:
82
84
  ValueError: If decryption fails or data is invalid
83
85
  """
84
86
  if not encrypted_text:
85
87
  return ""
86
-
88
+
87
89
  try:
88
90
  decoded = base64.b64decode(encrypted_text.encode())
89
91
  decrypted = self.fernet.decrypt(decoded)
90
92
  return decrypted.decode()
91
93
  except Exception as e:
92
94
  raise ValueError(f"Failed to decrypt data: {str(e)}") from e
93
-