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.
- miso_client/__init__.py +523 -130
- miso_client/api/__init__.py +35 -0
- miso_client/api/auth_api.py +367 -0
- miso_client/api/logs_api.py +91 -0
- miso_client/api/permissions_api.py +88 -0
- miso_client/api/roles_api.py +88 -0
- miso_client/api/types/__init__.py +75 -0
- miso_client/api/types/auth_types.py +183 -0
- miso_client/api/types/logs_types.py +71 -0
- miso_client/api/types/permissions_types.py +31 -0
- miso_client/api/types/roles_types.py +31 -0
- miso_client/errors.py +30 -4
- miso_client/models/__init__.py +4 -0
- miso_client/models/config.py +275 -72
- miso_client/models/error_response.py +39 -0
- miso_client/models/filter.py +255 -0
- miso_client/models/pagination.py +44 -0
- miso_client/models/sort.py +25 -0
- miso_client/services/__init__.py +6 -5
- miso_client/services/auth.py +496 -87
- miso_client/services/cache.py +42 -41
- miso_client/services/encryption.py +18 -17
- miso_client/services/logger.py +467 -328
- miso_client/services/logger_chain.py +288 -0
- miso_client/services/permission.py +130 -67
- miso_client/services/redis.py +28 -23
- miso_client/services/role.py +145 -62
- miso_client/utils/__init__.py +3 -3
- miso_client/utils/audit_log_queue.py +222 -0
- miso_client/utils/auth_strategy.py +88 -0
- miso_client/utils/auth_utils.py +65 -0
- miso_client/utils/circuit_breaker.py +125 -0
- miso_client/utils/client_token_manager.py +244 -0
- miso_client/utils/config_loader.py +88 -17
- miso_client/utils/controller_url_resolver.py +80 -0
- miso_client/utils/data_masker.py +104 -33
- miso_client/utils/environment_token.py +126 -0
- miso_client/utils/error_utils.py +216 -0
- miso_client/utils/fastapi_endpoints.py +166 -0
- miso_client/utils/filter.py +364 -0
- miso_client/utils/filter_applier.py +143 -0
- miso_client/utils/filter_parser.py +110 -0
- miso_client/utils/flask_endpoints.py +169 -0
- miso_client/utils/http_client.py +494 -262
- miso_client/utils/http_client_logging.py +352 -0
- miso_client/utils/http_client_logging_helpers.py +197 -0
- miso_client/utils/http_client_query_helpers.py +138 -0
- miso_client/utils/http_error_handler.py +92 -0
- miso_client/utils/http_log_formatter.py +115 -0
- miso_client/utils/http_log_masker.py +203 -0
- miso_client/utils/internal_http_client.py +435 -0
- miso_client/utils/jwt_tools.py +125 -16
- miso_client/utils/logger_helpers.py +206 -0
- miso_client/utils/logging_helpers.py +70 -0
- miso_client/utils/origin_validator.py +128 -0
- miso_client/utils/pagination.py +275 -0
- miso_client/utils/request_context.py +285 -0
- miso_client/utils/sensitive_fields_loader.py +116 -0
- miso_client/utils/sort.py +116 -0
- miso_client/utils/token_utils.py +114 -0
- miso_client/utils/url_validator.py +66 -0
- miso_client/utils/user_token_refresh.py +245 -0
- miso_client-3.7.2.dist-info/METADATA +1021 -0
- miso_client-3.7.2.dist-info/RECORD +68 -0
- miso_client-0.1.0.dist-info/METADATA +0 -551
- miso_client-0.1.0.dist-info/RECORD +0 -23
- {miso_client-0.1.0.dist-info → miso_client-3.7.2.dist-info}/WHEEL +0 -0
- {miso_client-0.1.0.dist-info → miso_client-3.7.2.dist-info}/licenses/LICENSE +0 -0
- {miso_client-0.1.0.dist-info → miso_client-3.7.2.dist-info}/top_level.txt +0 -0
miso_client/services/redis.py
CHANGED
|
@@ -5,18 +5,23 @@ This module provides Redis connectivity with graceful degradation when Redis
|
|
|
5
5
|
is unavailable. It handles caching of roles and permissions, and log queuing.
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
|
-
import
|
|
8
|
+
import logging
|
|
9
9
|
from typing import Optional
|
|
10
|
+
|
|
11
|
+
import redis.asyncio as redis
|
|
12
|
+
|
|
10
13
|
from ..models.config import RedisConfig
|
|
11
14
|
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
16
|
+
|
|
12
17
|
|
|
13
18
|
class RedisService:
|
|
14
19
|
"""Redis service for caching and log queuing."""
|
|
15
|
-
|
|
20
|
+
|
|
16
21
|
def __init__(self, config: Optional[RedisConfig] = None):
|
|
17
22
|
"""
|
|
18
23
|
Initialize Redis service.
|
|
19
|
-
|
|
24
|
+
|
|
20
25
|
Args:
|
|
21
26
|
config: Optional Redis configuration
|
|
22
27
|
"""
|
|
@@ -27,12 +32,12 @@ class RedisService:
|
|
|
27
32
|
async def connect(self) -> None:
|
|
28
33
|
"""
|
|
29
34
|
Connect to Redis.
|
|
30
|
-
|
|
35
|
+
|
|
31
36
|
Raises:
|
|
32
37
|
Exception: If connection fails and config is provided
|
|
33
38
|
"""
|
|
34
39
|
if not self.config:
|
|
35
|
-
|
|
40
|
+
logger.info("Redis not configured, using controller fallback")
|
|
36
41
|
return
|
|
37
42
|
|
|
38
43
|
try:
|
|
@@ -46,17 +51,17 @@ class RedisService:
|
|
|
46
51
|
socket_connect_timeout=5,
|
|
47
52
|
socket_timeout=5,
|
|
48
53
|
)
|
|
49
|
-
|
|
54
|
+
|
|
50
55
|
# Test connection
|
|
51
56
|
# Some redis stubs type ping as possibly non-awaitable; support both
|
|
52
57
|
resp = self.redis.ping()
|
|
53
58
|
if hasattr(resp, "__await__"):
|
|
54
59
|
await resp # type: ignore[misc]
|
|
55
60
|
self.connected = True
|
|
56
|
-
|
|
57
|
-
|
|
61
|
+
logger.info("Connected to Redis")
|
|
62
|
+
|
|
58
63
|
except Exception as error:
|
|
59
|
-
|
|
64
|
+
logger.error(f"Failed to connect to Redis: {error}", exc_info=error)
|
|
60
65
|
self.connected = False
|
|
61
66
|
if self.config: # Only raise if Redis was configured
|
|
62
67
|
raise error
|
|
@@ -66,12 +71,12 @@ class RedisService:
|
|
|
66
71
|
if self.redis:
|
|
67
72
|
await self.redis.aclose()
|
|
68
73
|
self.connected = False
|
|
69
|
-
|
|
74
|
+
logger.info("Disconnected from Redis")
|
|
70
75
|
|
|
71
76
|
def is_connected(self) -> bool:
|
|
72
77
|
"""
|
|
73
78
|
Check if Redis is connected.
|
|
74
|
-
|
|
79
|
+
|
|
75
80
|
Returns:
|
|
76
81
|
True if connected, False otherwise
|
|
77
82
|
"""
|
|
@@ -80,10 +85,10 @@ class RedisService:
|
|
|
80
85
|
async def get(self, key: str) -> Optional[str]:
|
|
81
86
|
"""
|
|
82
87
|
Get value from Redis.
|
|
83
|
-
|
|
88
|
+
|
|
84
89
|
Args:
|
|
85
90
|
key: Redis key
|
|
86
|
-
|
|
91
|
+
|
|
87
92
|
Returns:
|
|
88
93
|
Value if found, None otherwise
|
|
89
94
|
"""
|
|
@@ -100,18 +105,18 @@ class RedisService:
|
|
|
100
105
|
result = resp
|
|
101
106
|
return None if result is None else str(result)
|
|
102
107
|
except Exception as error:
|
|
103
|
-
|
|
108
|
+
logger.error("Redis get error", exc_info=error)
|
|
104
109
|
return None
|
|
105
110
|
|
|
106
111
|
async def set(self, key: str, value: str, ttl: int) -> bool:
|
|
107
112
|
"""
|
|
108
113
|
Set value in Redis with TTL.
|
|
109
|
-
|
|
114
|
+
|
|
110
115
|
Args:
|
|
111
116
|
key: Redis key
|
|
112
117
|
value: Value to store
|
|
113
118
|
ttl: Time to live in seconds
|
|
114
|
-
|
|
119
|
+
|
|
115
120
|
Returns:
|
|
116
121
|
True if successful, False otherwise
|
|
117
122
|
"""
|
|
@@ -126,16 +131,16 @@ class RedisService:
|
|
|
126
131
|
await resp # type: ignore[misc]
|
|
127
132
|
return True
|
|
128
133
|
except Exception as error:
|
|
129
|
-
|
|
134
|
+
logger.error("Redis set error", exc_info=error)
|
|
130
135
|
return False
|
|
131
136
|
|
|
132
137
|
async def delete(self, key: str) -> bool:
|
|
133
138
|
"""
|
|
134
139
|
Delete key from Redis.
|
|
135
|
-
|
|
140
|
+
|
|
136
141
|
Args:
|
|
137
142
|
key: Redis key
|
|
138
|
-
|
|
143
|
+
|
|
139
144
|
Returns:
|
|
140
145
|
True if successful, False otherwise
|
|
141
146
|
"""
|
|
@@ -150,17 +155,17 @@ class RedisService:
|
|
|
150
155
|
await resp # type: ignore[misc]
|
|
151
156
|
return True
|
|
152
157
|
except Exception as error:
|
|
153
|
-
|
|
158
|
+
logger.error("Redis delete error", exc_info=error)
|
|
154
159
|
return False
|
|
155
160
|
|
|
156
161
|
async def rpush(self, queue: str, value: str) -> bool:
|
|
157
162
|
"""
|
|
158
163
|
Push value to Redis list (for log queuing).
|
|
159
|
-
|
|
164
|
+
|
|
160
165
|
Args:
|
|
161
166
|
queue: Queue name
|
|
162
167
|
value: Value to push
|
|
163
|
-
|
|
168
|
+
|
|
164
169
|
Returns:
|
|
165
170
|
True if successful, False otherwise
|
|
166
171
|
"""
|
|
@@ -175,5 +180,5 @@ class RedisService:
|
|
|
175
180
|
await resp # type: ignore[misc]
|
|
176
181
|
return True
|
|
177
182
|
except Exception as error:
|
|
178
|
-
|
|
183
|
+
logger.error("Redis rpush error", exc_info=error)
|
|
179
184
|
return False
|
miso_client/services/role.py
CHANGED
|
@@ -6,39 +6,55 @@ Roles are cached with Redis and in-memory fallback using CacheService.
|
|
|
6
6
|
Optimized to extract userId from JWT token before API calls for cache optimization.
|
|
7
7
|
"""
|
|
8
8
|
|
|
9
|
+
import logging
|
|
9
10
|
import time
|
|
10
|
-
from typing import List, cast
|
|
11
|
-
|
|
11
|
+
from typing import TYPE_CHECKING, List, Optional, cast
|
|
12
|
+
|
|
13
|
+
from ..models.config import AuthStrategy, RoleResult
|
|
12
14
|
from ..services.cache import CacheService
|
|
15
|
+
from ..utils.auth_utils import validate_token_request
|
|
16
|
+
from ..utils.error_utils import extract_correlation_id_from_error
|
|
13
17
|
from ..utils.http_client import HttpClient
|
|
14
18
|
from ..utils.jwt_tools import extract_user_id
|
|
15
19
|
|
|
20
|
+
if TYPE_CHECKING:
|
|
21
|
+
from ..api import ApiClient
|
|
22
|
+
|
|
23
|
+
logger = logging.getLogger(__name__)
|
|
24
|
+
|
|
16
25
|
|
|
17
26
|
class RoleService:
|
|
18
27
|
"""Role service for user authorization with caching."""
|
|
19
|
-
|
|
20
|
-
def __init__(
|
|
28
|
+
|
|
29
|
+
def __init__(
|
|
30
|
+
self, http_client: HttpClient, cache: CacheService, api_client: Optional["ApiClient"] = None
|
|
31
|
+
):
|
|
21
32
|
"""
|
|
22
33
|
Initialize role service.
|
|
23
|
-
|
|
34
|
+
|
|
24
35
|
Args:
|
|
25
|
-
http_client: HTTP client instance
|
|
36
|
+
http_client: HTTP client instance (for backward compatibility)
|
|
26
37
|
cache: Cache service instance (handles Redis + in-memory fallback)
|
|
38
|
+
api_client: Optional API client instance (for typed API calls)
|
|
27
39
|
"""
|
|
28
40
|
self.config = http_client.config
|
|
29
41
|
self.http_client = http_client
|
|
30
42
|
self.cache = cache
|
|
43
|
+
self.api_client = api_client
|
|
31
44
|
self.role_ttl = self.config.role_ttl
|
|
32
45
|
|
|
33
|
-
async def get_roles(
|
|
46
|
+
async def get_roles(
|
|
47
|
+
self, token: str, auth_strategy: Optional[AuthStrategy] = None
|
|
48
|
+
) -> List[str]:
|
|
34
49
|
"""
|
|
35
50
|
Get user roles with Redis caching.
|
|
36
|
-
|
|
51
|
+
|
|
37
52
|
Optimized to extract userId from token first to check cache before API call.
|
|
38
|
-
|
|
53
|
+
|
|
39
54
|
Args:
|
|
40
55
|
token: JWT token
|
|
41
|
-
|
|
56
|
+
auth_strategy: Optional authentication strategy
|
|
57
|
+
|
|
42
58
|
Returns:
|
|
43
59
|
List of user roles
|
|
44
60
|
"""
|
|
@@ -56,10 +72,8 @@ class RoleService:
|
|
|
56
72
|
# Cache miss or no userId in token - fetch from controller
|
|
57
73
|
# If we don't have userId, get it from validate endpoint
|
|
58
74
|
if not user_id:
|
|
59
|
-
user_info = await
|
|
60
|
-
|
|
61
|
-
"/api/auth/validate",
|
|
62
|
-
token
|
|
75
|
+
user_info = await validate_token_request(
|
|
76
|
+
token, self.http_client, self.api_client, auth_strategy
|
|
63
77
|
)
|
|
64
78
|
user_id = user_info.get("user", {}).get("id") if user_info else None
|
|
65
79
|
if not user_id:
|
|
@@ -67,89 +81,110 @@ class RoleService:
|
|
|
67
81
|
cache_key = f"roles:{user_id}"
|
|
68
82
|
|
|
69
83
|
# Cache miss - fetch from controller
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
84
|
+
if self.api_client:
|
|
85
|
+
# Use ApiClient for typed API calls
|
|
86
|
+
response = await self.api_client.roles.get_roles(token, auth_strategy=auth_strategy)
|
|
87
|
+
roles = response.data.roles or []
|
|
88
|
+
else:
|
|
89
|
+
# Fallback to HttpClient for backward compatibility
|
|
90
|
+
if auth_strategy is not None:
|
|
91
|
+
role_result = await self.http_client.authenticated_request(
|
|
92
|
+
"GET", "/api/v1/auth/roles", token, auth_strategy=auth_strategy
|
|
93
|
+
)
|
|
94
|
+
else:
|
|
95
|
+
role_result = await self.http_client.authenticated_request(
|
|
96
|
+
"GET", "/api/v1/auth/roles", token
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
role_data = RoleResult(**role_result)
|
|
100
|
+
roles = role_data.roles or []
|
|
78
101
|
|
|
79
102
|
# Cache the result (CacheService handles Redis + in-memory automatically)
|
|
80
103
|
assert cache_key is not None
|
|
81
104
|
await self.cache.set(
|
|
82
|
-
cache_key,
|
|
83
|
-
{"roles": roles, "timestamp": int(time.time() * 1000)},
|
|
84
|
-
self.role_ttl
|
|
105
|
+
cache_key, {"roles": roles, "timestamp": int(time.time() * 1000)}, self.role_ttl
|
|
85
106
|
)
|
|
86
107
|
|
|
87
108
|
return roles
|
|
88
|
-
|
|
89
|
-
except Exception:
|
|
90
|
-
|
|
109
|
+
|
|
110
|
+
except Exception as error:
|
|
111
|
+
correlation_id = extract_correlation_id_from_error(error)
|
|
112
|
+
logger.error(
|
|
113
|
+
"Failed to get roles",
|
|
114
|
+
exc_info=error,
|
|
115
|
+
extra={"correlationId": correlation_id} if correlation_id else None,
|
|
116
|
+
)
|
|
91
117
|
return []
|
|
92
118
|
|
|
93
|
-
async def has_role(
|
|
119
|
+
async def has_role(
|
|
120
|
+
self, token: str, role: str, auth_strategy: Optional[AuthStrategy] = None
|
|
121
|
+
) -> bool:
|
|
94
122
|
"""
|
|
95
123
|
Check if user has specific role.
|
|
96
|
-
|
|
124
|
+
|
|
97
125
|
Args:
|
|
98
126
|
token: JWT token
|
|
99
127
|
role: Role to check
|
|
100
|
-
|
|
128
|
+
auth_strategy: Optional authentication strategy
|
|
129
|
+
|
|
101
130
|
Returns:
|
|
102
131
|
True if user has the role, False otherwise
|
|
103
132
|
"""
|
|
104
|
-
roles = await self.get_roles(token)
|
|
133
|
+
roles = await self.get_roles(token, auth_strategy=auth_strategy)
|
|
105
134
|
return role in roles
|
|
106
135
|
|
|
107
|
-
async def has_any_role(
|
|
136
|
+
async def has_any_role(
|
|
137
|
+
self, token: str, roles: List[str], auth_strategy: Optional[AuthStrategy] = None
|
|
138
|
+
) -> bool:
|
|
108
139
|
"""
|
|
109
140
|
Check if user has any of the specified roles.
|
|
110
|
-
|
|
141
|
+
|
|
111
142
|
Args:
|
|
112
143
|
token: JWT token
|
|
113
144
|
roles: List of roles to check
|
|
114
|
-
|
|
145
|
+
auth_strategy: Optional authentication strategy
|
|
146
|
+
|
|
115
147
|
Returns:
|
|
116
148
|
True if user has any of the roles, False otherwise
|
|
117
149
|
"""
|
|
118
|
-
user_roles = await self.get_roles(token)
|
|
150
|
+
user_roles = await self.get_roles(token, auth_strategy=auth_strategy)
|
|
119
151
|
return any(role in user_roles for role in roles)
|
|
120
152
|
|
|
121
|
-
async def has_all_roles(
|
|
153
|
+
async def has_all_roles(
|
|
154
|
+
self, token: str, roles: List[str], auth_strategy: Optional[AuthStrategy] = None
|
|
155
|
+
) -> bool:
|
|
122
156
|
"""
|
|
123
157
|
Check if user has all of the specified roles.
|
|
124
|
-
|
|
158
|
+
|
|
125
159
|
Args:
|
|
126
160
|
token: JWT token
|
|
127
161
|
roles: List of roles to check
|
|
128
|
-
|
|
162
|
+
auth_strategy: Optional authentication strategy
|
|
163
|
+
|
|
129
164
|
Returns:
|
|
130
165
|
True if user has all roles, False otherwise
|
|
131
166
|
"""
|
|
132
|
-
user_roles = await self.get_roles(token)
|
|
167
|
+
user_roles = await self.get_roles(token, auth_strategy=auth_strategy)
|
|
133
168
|
return all(role in user_roles for role in roles)
|
|
134
169
|
|
|
135
|
-
async def refresh_roles(
|
|
170
|
+
async def refresh_roles(
|
|
171
|
+
self, token: str, auth_strategy: Optional[AuthStrategy] = None
|
|
172
|
+
) -> List[str]:
|
|
136
173
|
"""
|
|
137
174
|
Force refresh roles from controller (bypass cache).
|
|
138
|
-
|
|
175
|
+
|
|
139
176
|
Args:
|
|
140
177
|
token: JWT token
|
|
141
|
-
|
|
178
|
+
auth_strategy: Optional authentication strategy
|
|
179
|
+
|
|
142
180
|
Returns:
|
|
143
181
|
Fresh list of user roles
|
|
144
182
|
"""
|
|
145
183
|
try:
|
|
146
184
|
# Get user info to extract userId
|
|
147
|
-
user_info = await
|
|
148
|
-
|
|
149
|
-
"/api/auth/validate",
|
|
150
|
-
token
|
|
185
|
+
user_info = await validate_token_request(
|
|
186
|
+
token, self.http_client, self.api_client, auth_strategy
|
|
151
187
|
)
|
|
152
|
-
|
|
153
188
|
user_id = user_info.get("user", {}).get("id") if user_info else None
|
|
154
189
|
if not user_id:
|
|
155
190
|
return []
|
|
@@ -157,24 +192,72 @@ class RoleService:
|
|
|
157
192
|
cache_key = f"roles:{user_id}"
|
|
158
193
|
|
|
159
194
|
# Fetch fresh roles from controller using refresh endpoint
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
195
|
+
if self.api_client:
|
|
196
|
+
# Use ApiClient for typed API calls
|
|
197
|
+
response = await self.api_client.roles.refresh_roles(
|
|
198
|
+
token, auth_strategy=auth_strategy
|
|
199
|
+
)
|
|
200
|
+
roles = response.data.roles or []
|
|
201
|
+
else:
|
|
202
|
+
# Fallback to HttpClient for backward compatibility
|
|
203
|
+
if auth_strategy is not None:
|
|
204
|
+
role_result = await self.http_client.authenticated_request(
|
|
205
|
+
"GET", "/api/v1/auth/roles/refresh", token, auth_strategy=auth_strategy
|
|
206
|
+
)
|
|
207
|
+
else:
|
|
208
|
+
role_result = await self.http_client.authenticated_request(
|
|
209
|
+
"GET", "/api/v1/auth/roles/refresh", token
|
|
210
|
+
)
|
|
211
|
+
|
|
212
|
+
role_data = RoleResult(**role_result)
|
|
213
|
+
roles = role_data.roles or []
|
|
168
214
|
|
|
169
215
|
# Update cache with fresh data (CacheService handles Redis + in-memory automatically)
|
|
170
216
|
await self.cache.set(
|
|
171
|
-
cache_key,
|
|
172
|
-
{"roles": roles, "timestamp": int(time.time() * 1000)},
|
|
173
|
-
self.role_ttl
|
|
217
|
+
cache_key, {"roles": roles, "timestamp": int(time.time() * 1000)}, self.role_ttl
|
|
174
218
|
)
|
|
175
219
|
|
|
176
220
|
return roles
|
|
177
|
-
|
|
178
|
-
except Exception:
|
|
179
|
-
|
|
221
|
+
|
|
222
|
+
except Exception as error:
|
|
223
|
+
correlation_id = extract_correlation_id_from_error(error)
|
|
224
|
+
logger.error(
|
|
225
|
+
"Failed to refresh roles",
|
|
226
|
+
exc_info=error,
|
|
227
|
+
extra={"correlationId": correlation_id} if correlation_id else None,
|
|
228
|
+
)
|
|
180
229
|
return []
|
|
230
|
+
|
|
231
|
+
async def clear_roles_cache(
|
|
232
|
+
self, token: str, auth_strategy: Optional[AuthStrategy] = None
|
|
233
|
+
) -> None:
|
|
234
|
+
"""
|
|
235
|
+
Clear cached roles for a user.
|
|
236
|
+
|
|
237
|
+
Args:
|
|
238
|
+
token: JWT token
|
|
239
|
+
auth_strategy: Optional authentication strategy
|
|
240
|
+
"""
|
|
241
|
+
try:
|
|
242
|
+
# Extract userId from token to avoid unnecessary API calls
|
|
243
|
+
user_id = extract_user_id(token)
|
|
244
|
+
if not user_id:
|
|
245
|
+
# If userId not in token, try to get it from validate endpoint
|
|
246
|
+
user_info = await validate_token_request(
|
|
247
|
+
token, self.http_client, self.api_client, auth_strategy
|
|
248
|
+
)
|
|
249
|
+
user_id = user_info.get("user", {}).get("id") if user_info else None
|
|
250
|
+
if not user_id:
|
|
251
|
+
return # Cannot clear cache without userId
|
|
252
|
+
|
|
253
|
+
cache_key = f"roles:{user_id}"
|
|
254
|
+
await self.cache.delete(cache_key)
|
|
255
|
+
|
|
256
|
+
except Exception as error:
|
|
257
|
+
correlation_id = extract_correlation_id_from_error(error)
|
|
258
|
+
logger.error(
|
|
259
|
+
"Failed to clear roles cache",
|
|
260
|
+
exc_info=error,
|
|
261
|
+
extra={"correlationId": correlation_id} if correlation_id else None,
|
|
262
|
+
)
|
|
263
|
+
# Silently continue per service method pattern
|
miso_client/utils/__init__.py
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
"""Utility modules for MisoClient SDK."""
|
|
2
2
|
|
|
3
|
-
from .http_client import HttpClient
|
|
4
3
|
from .config_loader import load_config
|
|
5
4
|
from .data_masker import DataMasker
|
|
6
|
-
from .
|
|
5
|
+
from .http_client import HttpClient
|
|
6
|
+
from .jwt_tools import decode_token, extract_session_id, extract_user_id
|
|
7
7
|
|
|
8
8
|
__all__ = [
|
|
9
9
|
"HttpClient",
|
|
@@ -12,4 +12,4 @@ __all__ = [
|
|
|
12
12
|
"decode_token",
|
|
13
13
|
"extract_user_id",
|
|
14
14
|
"extract_session_id",
|
|
15
|
-
]
|
|
15
|
+
]
|