blackant-sdk 1.0.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.
- blackant/__init__.py +31 -0
- blackant/auth/__init__.py +10 -0
- blackant/auth/blackant_auth.py +518 -0
- blackant/auth/keycloak_manager.py +363 -0
- blackant/auth/request_id.py +52 -0
- blackant/auth/role_assignment.py +443 -0
- blackant/auth/tokens.py +57 -0
- blackant/client.py +400 -0
- blackant/config/__init__.py +0 -0
- blackant/config/docker_config.py +457 -0
- blackant/config/keycloak_admin_config.py +107 -0
- blackant/docker/__init__.py +12 -0
- blackant/docker/builder.py +616 -0
- blackant/docker/client.py +983 -0
- blackant/docker/dao.py +462 -0
- blackant/docker/registry.py +172 -0
- blackant/exceptions.py +111 -0
- blackant/http/__init__.py +8 -0
- blackant/http/client.py +125 -0
- blackant/patterns/__init__.py +1 -0
- blackant/patterns/singleton.py +20 -0
- blackant/services/__init__.py +10 -0
- blackant/services/dao.py +414 -0
- blackant/services/registry.py +635 -0
- blackant/utils/__init__.py +8 -0
- blackant/utils/initialization.py +32 -0
- blackant/utils/logging.py +337 -0
- blackant/utils/request_id.py +13 -0
- blackant/utils/store.py +50 -0
- blackant_sdk-1.0.2.dist-info/METADATA +117 -0
- blackant_sdk-1.0.2.dist-info/RECORD +70 -0
- blackant_sdk-1.0.2.dist-info/WHEEL +5 -0
- blackant_sdk-1.0.2.dist-info/top_level.txt +5 -0
- calculation/__init__.py +0 -0
- calculation/base.py +26 -0
- calculation/errors.py +2 -0
- calculation/impl/__init__.py +0 -0
- calculation/impl/my_calculation.py +144 -0
- calculation/impl/simple_calc.py +53 -0
- calculation/impl/test.py +1 -0
- calculation/impl/test_calc.py +36 -0
- calculation/loader.py +227 -0
- notifinations/__init__.py +8 -0
- notifinations/mail_sender.py +212 -0
- storage/__init__.py +0 -0
- storage/errors.py +10 -0
- storage/factory.py +26 -0
- storage/interface.py +19 -0
- storage/minio.py +106 -0
- task/__init__.py +0 -0
- task/dao.py +38 -0
- task/errors.py +10 -0
- task/log_adapter.py +11 -0
- task/parsers/__init__.py +0 -0
- task/parsers/base.py +13 -0
- task/parsers/callback.py +40 -0
- task/parsers/cmd_args.py +52 -0
- task/parsers/freetext.py +19 -0
- task/parsers/objects.py +50 -0
- task/parsers/request.py +56 -0
- task/resource.py +84 -0
- task/states/__init__.py +0 -0
- task/states/base.py +14 -0
- task/states/error.py +47 -0
- task/states/idle.py +12 -0
- task/states/ready.py +51 -0
- task/states/running.py +21 -0
- task/states/set_up.py +40 -0
- task/states/tear_down.py +29 -0
- task/task.py +358 -0
|
@@ -0,0 +1,443 @@
|
|
|
1
|
+
"""Role Assignment Manager
|
|
2
|
+
|
|
3
|
+
Orchestrates automatic role assignment after service publication.
|
|
4
|
+
Extracts user information from JWT token and assigns science_module role.
|
|
5
|
+
|
|
6
|
+
Supports two modes:
|
|
7
|
+
1. Direct Keycloak access (requires admin credentials in SDK)
|
|
8
|
+
2. SDK Services (credentials stay server-side, more secure)
|
|
9
|
+
|
|
10
|
+
Author: Balázs Milán (milan.balazs@uni-obuda.hu)
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from typing import Optional, Dict, Any
|
|
14
|
+
import logging
|
|
15
|
+
import jwt
|
|
16
|
+
import requests
|
|
17
|
+
from datetime import datetime, timezone
|
|
18
|
+
|
|
19
|
+
from .keycloak_manager import KeycloakManager, get_keycloak_manager
|
|
20
|
+
from ..config.keycloak_admin_config import KeycloakAdminConfig
|
|
21
|
+
from ..config.docker_config import get_docker_config
|
|
22
|
+
from ..exceptions import (
|
|
23
|
+
RoleAssignmentError,
|
|
24
|
+
TokenValidationError,
|
|
25
|
+
BlackAntException
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
logger = logging.getLogger(__name__)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class RoleAssignmentManager:
|
|
33
|
+
"""Manager for automatic role assignment after service publication.
|
|
34
|
+
|
|
35
|
+
Handles the workflow of:
|
|
36
|
+
1. Extracting user ID from JWT token
|
|
37
|
+
2. Checking if science_module role exists
|
|
38
|
+
3. Assigning role if missing
|
|
39
|
+
4. Audit logging
|
|
40
|
+
|
|
41
|
+
Supports two modes:
|
|
42
|
+
- Direct Keycloak: Uses KeycloakManager with admin credentials
|
|
43
|
+
- SDK Services: Delegates to backend, credentials stay server-side
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
keycloak_manager: Optional KeycloakManager instance
|
|
47
|
+
role_name: Role to assign (default: "science_module")
|
|
48
|
+
|
|
49
|
+
Example:
|
|
50
|
+
>>> manager = RoleAssignmentManager()
|
|
51
|
+
>>> success = manager.assign_role_after_publish(
|
|
52
|
+
... user_token="eyJhbGciOiJSUzI1NiIs...",
|
|
53
|
+
... service_name="my-calculation"
|
|
54
|
+
... )
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
DEFAULT_ROLE = "science_module"
|
|
58
|
+
|
|
59
|
+
def __init__(
|
|
60
|
+
self,
|
|
61
|
+
keycloak_manager: Optional[KeycloakManager] = None,
|
|
62
|
+
role_name: str = DEFAULT_ROLE
|
|
63
|
+
):
|
|
64
|
+
"""Initialize role assignment manager.
|
|
65
|
+
|
|
66
|
+
Args:
|
|
67
|
+
keycloak_manager: Optional KeycloakManager (creates new if None)
|
|
68
|
+
role_name: Role to assign (default: "science_module")
|
|
69
|
+
"""
|
|
70
|
+
self.keycloak_manager = keycloak_manager or get_keycloak_manager()
|
|
71
|
+
self.role_name = role_name
|
|
72
|
+
self.logger = logger
|
|
73
|
+
|
|
74
|
+
# Check SDK Services configuration
|
|
75
|
+
try:
|
|
76
|
+
self._docker_config = get_docker_config()
|
|
77
|
+
self._use_sdk_services = (
|
|
78
|
+
self._docker_config.sdk_services.use_for_roles and
|
|
79
|
+
self._docker_config.sdk_services.url
|
|
80
|
+
)
|
|
81
|
+
except Exception:
|
|
82
|
+
self._docker_config = None
|
|
83
|
+
self._use_sdk_services = False
|
|
84
|
+
|
|
85
|
+
def extract_user_id_from_token(self, token: str) -> str:
|
|
86
|
+
"""Extract Keycloak user UUID from JWT token.
|
|
87
|
+
|
|
88
|
+
Args:
|
|
89
|
+
token: JWT token (Bearer token from authentication)
|
|
90
|
+
|
|
91
|
+
Returns:
|
|
92
|
+
User UUID string
|
|
93
|
+
|
|
94
|
+
Raises:
|
|
95
|
+
TokenValidationError: If token is invalid or user ID not found
|
|
96
|
+
|
|
97
|
+
Example:
|
|
98
|
+
>>> user_id = manager.extract_user_id_from_token(token)
|
|
99
|
+
>>> print(user_id)
|
|
100
|
+
'a1b2c3d4-e5f6-7890-abcd-ef1234567890'
|
|
101
|
+
"""
|
|
102
|
+
try:
|
|
103
|
+
# Decode JWT without verification (we trust our own tokens)
|
|
104
|
+
# In production, you should verify signature with public key
|
|
105
|
+
decoded = jwt.decode(
|
|
106
|
+
token,
|
|
107
|
+
options={"verify_signature": False} # Trust our own tokens
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
# Extract user ID from 'sub' claim (standard JWT claim)
|
|
111
|
+
user_id = decoded.get('sub')
|
|
112
|
+
if not user_id:
|
|
113
|
+
raise TokenValidationError(
|
|
114
|
+
"Token does not contain 'sub' claim (user ID)"
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
# Additional info for logging
|
|
118
|
+
username = decoded.get('preferred_username', 'unknown')
|
|
119
|
+
email = decoded.get('email', 'unknown')
|
|
120
|
+
|
|
121
|
+
self.logger.debug(
|
|
122
|
+
f"Extracted user ID from token: {user_id} "
|
|
123
|
+
f"(username: {username}, email: {email})"
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
return user_id
|
|
127
|
+
|
|
128
|
+
except jwt.InvalidTokenError as e:
|
|
129
|
+
self.logger.error(f"Invalid JWT token: {e}")
|
|
130
|
+
raise TokenValidationError(f"Invalid JWT token: {e}")
|
|
131
|
+
except Exception as e:
|
|
132
|
+
self.logger.error(f"Failed to extract user ID from token: {e}")
|
|
133
|
+
raise TokenValidationError(f"Token parsing failed: {e}")
|
|
134
|
+
|
|
135
|
+
def assign_role_after_publish(
|
|
136
|
+
self,
|
|
137
|
+
user_token: str,
|
|
138
|
+
service_name: str,
|
|
139
|
+
metadata: Optional[Dict[str, Any]] = None
|
|
140
|
+
) -> bool:
|
|
141
|
+
"""Assign science_module role to user after service publication.
|
|
142
|
+
|
|
143
|
+
This is the main entry point called by ServiceRegistry after
|
|
144
|
+
successful Docker image push to registry.
|
|
145
|
+
|
|
146
|
+
If SDK Services is configured, delegates to backend.
|
|
147
|
+
Otherwise, uses direct Keycloak access with admin credentials.
|
|
148
|
+
|
|
149
|
+
Args:
|
|
150
|
+
user_token: JWT token of the user who published the service
|
|
151
|
+
service_name: Name of the published service
|
|
152
|
+
metadata: Optional metadata for audit logging
|
|
153
|
+
|
|
154
|
+
Returns:
|
|
155
|
+
True if role was assigned or already exists, False on failure
|
|
156
|
+
|
|
157
|
+
Raises:
|
|
158
|
+
RoleAssignmentError: If assignment fails critically
|
|
159
|
+
|
|
160
|
+
Example:
|
|
161
|
+
>>> success = manager.assign_role_after_publish(
|
|
162
|
+
... user_token=jwt_token,
|
|
163
|
+
... service_name="sentiment-analysis",
|
|
164
|
+
... metadata={"image": "env.blackant.app/..."}
|
|
165
|
+
... )
|
|
166
|
+
"""
|
|
167
|
+
# Check if SDK Services should handle role assignment
|
|
168
|
+
if self._use_sdk_services:
|
|
169
|
+
return self._assign_role_via_sdk_services(
|
|
170
|
+
user_token, service_name, metadata
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
# Use direct Keycloak access
|
|
174
|
+
return self._assign_role_direct(user_token, service_name, metadata)
|
|
175
|
+
|
|
176
|
+
def _assign_role_via_sdk_services(
|
|
177
|
+
self,
|
|
178
|
+
user_token: str,
|
|
179
|
+
service_name: str,
|
|
180
|
+
metadata: Optional[Dict[str, Any]] = None
|
|
181
|
+
) -> bool:
|
|
182
|
+
"""Assign role via SDK Services endpoint.
|
|
183
|
+
|
|
184
|
+
Delegates role assignment to sdk-services backend,
|
|
185
|
+
keeping admin credentials server-side.
|
|
186
|
+
|
|
187
|
+
Args:
|
|
188
|
+
user_token: JWT token of the user
|
|
189
|
+
service_name: Name of the published service
|
|
190
|
+
metadata: Optional metadata
|
|
191
|
+
|
|
192
|
+
Returns:
|
|
193
|
+
True if successful, False otherwise
|
|
194
|
+
"""
|
|
195
|
+
try:
|
|
196
|
+
# Extract user ID from token
|
|
197
|
+
user_id = self.extract_user_id_from_token(user_token)
|
|
198
|
+
|
|
199
|
+
assign_url = self._docker_config.sdk_services.roles_assign_url
|
|
200
|
+
self.logger.info(f"Assigning role via SDK Services: {assign_url}")
|
|
201
|
+
|
|
202
|
+
# Call SDK Services endpoint
|
|
203
|
+
response = requests.post(
|
|
204
|
+
assign_url,
|
|
205
|
+
json={
|
|
206
|
+
"user_id": user_id,
|
|
207
|
+
"role_name": self.role_name
|
|
208
|
+
},
|
|
209
|
+
headers={
|
|
210
|
+
"Content-Type": "application/json",
|
|
211
|
+
"Authorization": f"Bearer {user_token}"
|
|
212
|
+
},
|
|
213
|
+
timeout=self._docker_config.sdk_services.timeout,
|
|
214
|
+
verify=False # For dev environments
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
if response.status_code == 200:
|
|
218
|
+
result = response.json()
|
|
219
|
+
if result.get("success"):
|
|
220
|
+
self.logger.info(
|
|
221
|
+
f"Role '{self.role_name}' assigned to user {user_id} "
|
|
222
|
+
f"via SDK Services"
|
|
223
|
+
)
|
|
224
|
+
self._audit_log(
|
|
225
|
+
user_id=user_id,
|
|
226
|
+
service_name=service_name,
|
|
227
|
+
action="role_assigned_via_sdk_services",
|
|
228
|
+
result="success",
|
|
229
|
+
metadata=metadata
|
|
230
|
+
)
|
|
231
|
+
return True
|
|
232
|
+
else:
|
|
233
|
+
error_msg = result.get("message", "Unknown error")
|
|
234
|
+
self.logger.error(f"SDK Services role assign failed: {error_msg}")
|
|
235
|
+
return False
|
|
236
|
+
else:
|
|
237
|
+
self.logger.error(
|
|
238
|
+
f"SDK Services role assign failed with status {response.status_code}"
|
|
239
|
+
)
|
|
240
|
+
return False
|
|
241
|
+
|
|
242
|
+
except TokenValidationError as e:
|
|
243
|
+
self.logger.error(f"Token validation failed: {e}")
|
|
244
|
+
return False
|
|
245
|
+
except Exception as e:
|
|
246
|
+
self.logger.error(f"SDK Services role assign error: {e}")
|
|
247
|
+
return False
|
|
248
|
+
|
|
249
|
+
def _assign_role_direct(
|
|
250
|
+
self,
|
|
251
|
+
user_token: str,
|
|
252
|
+
service_name: str,
|
|
253
|
+
metadata: Optional[Dict[str, Any]] = None
|
|
254
|
+
) -> bool:
|
|
255
|
+
"""Assign role directly via Keycloak Manager.
|
|
256
|
+
|
|
257
|
+
Uses local admin credentials from configuration.
|
|
258
|
+
|
|
259
|
+
Args:
|
|
260
|
+
user_token: JWT token of the user
|
|
261
|
+
service_name: Name of the published service
|
|
262
|
+
metadata: Optional metadata
|
|
263
|
+
|
|
264
|
+
Returns:
|
|
265
|
+
True if successful, False otherwise
|
|
266
|
+
"""
|
|
267
|
+
try:
|
|
268
|
+
# Step 1: Extract user ID from JWT token
|
|
269
|
+
self.logger.info(
|
|
270
|
+
f"Starting role assignment for service '{service_name}'"
|
|
271
|
+
)
|
|
272
|
+
|
|
273
|
+
user_id = self.extract_user_id_from_token(user_token)
|
|
274
|
+
|
|
275
|
+
default = True
|
|
276
|
+
if not default:
|
|
277
|
+
self.role_name=service_name;
|
|
278
|
+
|
|
279
|
+
#check if role exists
|
|
280
|
+
if not self.keycloak_manager.role_exists(self.role_name):
|
|
281
|
+
self.keycloak_manager.create_realm_role(self.role_name)
|
|
282
|
+
|
|
283
|
+
# Step 2: Check if user already has the role (idempotent)
|
|
284
|
+
has_role = self.keycloak_manager.has_role(user_id, self.role_name)
|
|
285
|
+
|
|
286
|
+
if has_role:
|
|
287
|
+
self.logger.info(
|
|
288
|
+
f"User {user_id} already has role '{self.role_name}', "
|
|
289
|
+
f"no assignment needed"
|
|
290
|
+
)
|
|
291
|
+
self._audit_log(
|
|
292
|
+
user_id=user_id,
|
|
293
|
+
service_name=service_name,
|
|
294
|
+
action="role_check",
|
|
295
|
+
result="already_exists",
|
|
296
|
+
metadata=metadata
|
|
297
|
+
)
|
|
298
|
+
return True
|
|
299
|
+
|
|
300
|
+
# Step 3: Assign the role
|
|
301
|
+
self.logger.info(
|
|
302
|
+
f"Assigning role '{self.role_name}' to user {user_id} "
|
|
303
|
+
f"for service '{service_name}'"
|
|
304
|
+
)
|
|
305
|
+
|
|
306
|
+
success = self.keycloak_manager.assign_role_to_user(
|
|
307
|
+
user_id=user_id,
|
|
308
|
+
role_name=self.role_name,
|
|
309
|
+
idempotent=True
|
|
310
|
+
)
|
|
311
|
+
|
|
312
|
+
if success:
|
|
313
|
+
self.logger.info(
|
|
314
|
+
f"Successfully assigned role '{self.role_name}' to user {user_id}"
|
|
315
|
+
)
|
|
316
|
+
self._audit_log(
|
|
317
|
+
user_id=user_id,
|
|
318
|
+
service_name=service_name,
|
|
319
|
+
action="role_assigned",
|
|
320
|
+
result="success",
|
|
321
|
+
metadata=metadata
|
|
322
|
+
)
|
|
323
|
+
return True
|
|
324
|
+
else:
|
|
325
|
+
self.logger.warning(
|
|
326
|
+
f"Role assignment returned False for user {user_id}"
|
|
327
|
+
)
|
|
328
|
+
self._audit_log(
|
|
329
|
+
user_id=user_id,
|
|
330
|
+
service_name=service_name,
|
|
331
|
+
action="role_assignment",
|
|
332
|
+
result="failed",
|
|
333
|
+
metadata=metadata
|
|
334
|
+
)
|
|
335
|
+
return False
|
|
336
|
+
|
|
337
|
+
except TokenValidationError as e:
|
|
338
|
+
# Token parsing error - log and return False (don't raise)
|
|
339
|
+
self.logger.error(
|
|
340
|
+
f"Token validation failed for service '{service_name}': {e}"
|
|
341
|
+
)
|
|
342
|
+
self._audit_log(
|
|
343
|
+
user_id="unknown",
|
|
344
|
+
service_name=service_name,
|
|
345
|
+
action="role_assignment",
|
|
346
|
+
result="token_error",
|
|
347
|
+
error=str(e),
|
|
348
|
+
metadata=metadata
|
|
349
|
+
)
|
|
350
|
+
return False
|
|
351
|
+
|
|
352
|
+
except RoleAssignmentError as e:
|
|
353
|
+
# Keycloak communication error - log and return False
|
|
354
|
+
self.logger.error(
|
|
355
|
+
f"Role assignment failed for service '{service_name}': {e}"
|
|
356
|
+
)
|
|
357
|
+
self._audit_log(
|
|
358
|
+
user_id=user_id if 'user_id' in locals() else "unknown",
|
|
359
|
+
service_name=service_name,
|
|
360
|
+
action="role_assignment",
|
|
361
|
+
result="keycloak_error",
|
|
362
|
+
error=str(e),
|
|
363
|
+
metadata=metadata
|
|
364
|
+
)
|
|
365
|
+
return False
|
|
366
|
+
|
|
367
|
+
except Exception as e:
|
|
368
|
+
# Unexpected error - log and return False
|
|
369
|
+
self.logger.exception(
|
|
370
|
+
f"Unexpected error during role assignment for service '{service_name}': {e}"
|
|
371
|
+
)
|
|
372
|
+
self._audit_log(
|
|
373
|
+
user_id=user_id if 'user_id' in locals() else "unknown",
|
|
374
|
+
service_name=service_name,
|
|
375
|
+
action="role_assignment",
|
|
376
|
+
result="unexpected_error",
|
|
377
|
+
error=str(e),
|
|
378
|
+
metadata=metadata
|
|
379
|
+
)
|
|
380
|
+
return False
|
|
381
|
+
|
|
382
|
+
def _audit_log(
|
|
383
|
+
self,
|
|
384
|
+
user_id: str,
|
|
385
|
+
service_name: str,
|
|
386
|
+
action: str,
|
|
387
|
+
result: str,
|
|
388
|
+
error: Optional[str] = None,
|
|
389
|
+
metadata: Optional[Dict[str, Any]] = None
|
|
390
|
+
) -> None:
|
|
391
|
+
"""Create audit log entry for role assignment.
|
|
392
|
+
|
|
393
|
+
Logs all role assignment operations for security auditing.
|
|
394
|
+
|
|
395
|
+
Args:
|
|
396
|
+
user_id: Keycloak user UUID
|
|
397
|
+
service_name: Service name
|
|
398
|
+
action: Action performed (e.g., "role_assigned")
|
|
399
|
+
result: Result of action (e.g., "success", "failed")
|
|
400
|
+
error: Optional error message
|
|
401
|
+
metadata: Optional additional metadata
|
|
402
|
+
"""
|
|
403
|
+
audit_entry = {
|
|
404
|
+
"timestamp": datetime.now(timezone.utc).isoformat(),
|
|
405
|
+
"user_id": user_id,
|
|
406
|
+
"service_name": service_name,
|
|
407
|
+
"action": action,
|
|
408
|
+
"role": self.role_name,
|
|
409
|
+
"result": result,
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
if error:
|
|
413
|
+
audit_entry["error"] = error
|
|
414
|
+
|
|
415
|
+
if metadata:
|
|
416
|
+
audit_entry["metadata"] = metadata
|
|
417
|
+
|
|
418
|
+
# Log as structured JSON for audit trail
|
|
419
|
+
self.logger.info(
|
|
420
|
+
f"AUDIT: {audit_entry}",
|
|
421
|
+
extra={"audit": audit_entry}
|
|
422
|
+
)
|
|
423
|
+
|
|
424
|
+
# TODO: Send to centralized audit logging system
|
|
425
|
+
# (e.g., Elasticsearch, CloudWatch, etc.)
|
|
426
|
+
|
|
427
|
+
|
|
428
|
+
# Singleton instance (optional)
|
|
429
|
+
_role_assignment_manager: Optional[RoleAssignmentManager] = None
|
|
430
|
+
|
|
431
|
+
|
|
432
|
+
def get_role_assignment_manager() -> RoleAssignmentManager:
|
|
433
|
+
"""Get or create RoleAssignmentManager singleton.
|
|
434
|
+
|
|
435
|
+
Returns:
|
|
436
|
+
RoleAssignmentManager instance
|
|
437
|
+
"""
|
|
438
|
+
global _role_assignment_manager
|
|
439
|
+
|
|
440
|
+
if _role_assignment_manager is None:
|
|
441
|
+
_role_assignment_manager = RoleAssignmentManager()
|
|
442
|
+
|
|
443
|
+
return _role_assignment_manager
|
blackant/auth/tokens.py
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"""Authentication token storage module.
|
|
2
|
+
|
|
3
|
+
Provides storage for user and admin authentication tokens
|
|
4
|
+
using the Store base class with thread-safe access.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from ..utils.store import Store
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class AuthTokenStore(Store):
|
|
11
|
+
"""Authentication token storage class.
|
|
12
|
+
|
|
13
|
+
Stores user and admin authentication tokens for later use
|
|
14
|
+
in API requests. Uses thread-local storage for thread safety.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
@property
|
|
18
|
+
def user_token(self):
|
|
19
|
+
"""str or None: User-level Bearer token for API authentication."""
|
|
20
|
+
return self._get("user")
|
|
21
|
+
|
|
22
|
+
@user_token.setter
|
|
23
|
+
def user_token(self, value):
|
|
24
|
+
"""Set the user-level Bearer token.
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
value (str or None): The Bearer token string or None to clear.
|
|
28
|
+
"""
|
|
29
|
+
self._set("user", value)
|
|
30
|
+
|
|
31
|
+
@property
|
|
32
|
+
def admin_token(self):
|
|
33
|
+
"""str or None: Admin-level Bearer token for privileged API access."""
|
|
34
|
+
return self._get("admin")
|
|
35
|
+
|
|
36
|
+
@admin_token.setter
|
|
37
|
+
def admin_token(self, value):
|
|
38
|
+
"""Set the admin-level Bearer token.
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
value (str or None): The admin Bearer token string or None to clear.
|
|
42
|
+
"""
|
|
43
|
+
self._set("admin", value)
|
|
44
|
+
|
|
45
|
+
def clean_up(self):
|
|
46
|
+
"""Clean up stored tokens.
|
|
47
|
+
|
|
48
|
+
Removes both user and admin tokens from storage.
|
|
49
|
+
"""
|
|
50
|
+
try:
|
|
51
|
+
self._del("user")
|
|
52
|
+
except AttributeError:
|
|
53
|
+
pass
|
|
54
|
+
try:
|
|
55
|
+
self._del("admin")
|
|
56
|
+
except AttributeError:
|
|
57
|
+
pass
|