volcano-sdk-python 0.6.7__py3-none-any.whl → 0.7.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- volcano_sdk/auth.py +17 -18
- volcano_sdk/client.py +25 -1
- {volcano_sdk_python-0.6.7.dist-info → volcano_sdk_python-0.7.0.dist-info}/METADATA +13 -7
- {volcano_sdk_python-0.6.7.dist-info → volcano_sdk_python-0.7.0.dist-info}/RECORD +6 -6
- {volcano_sdk_python-0.6.7.dist-info → volcano_sdk_python-0.7.0.dist-info}/WHEEL +0 -0
- {volcano_sdk_python-0.6.7.dist-info → volcano_sdk_python-0.7.0.dist-info}/licenses/LICENSE +0 -0
volcano_sdk/auth.py
CHANGED
|
@@ -262,7 +262,7 @@ def _email_change_result_from_payload(payload: object) -> EmailChangeResult:
|
|
|
262
262
|
)
|
|
263
263
|
|
|
264
264
|
|
|
265
|
-
def _user_from_payload(payload: object) -> User:
|
|
265
|
+
def _user_from_payload(payload: object) -> tuple[User, Mapping[str, JSONValue]]:
|
|
266
266
|
if not isinstance(
|
|
267
267
|
payload,
|
|
268
268
|
(
|
|
@@ -277,7 +277,7 @@ def _user_from_payload(payload: object) -> User:
|
|
|
277
277
|
project_id = _none_if_unset(user.project_id)
|
|
278
278
|
user_metadata = _none_if_unset(user.user_metadata)
|
|
279
279
|
app_metadata = _none_if_unset(user.app_metadata)
|
|
280
|
-
|
|
280
|
+
profile = User(
|
|
281
281
|
id=str(user.id),
|
|
282
282
|
email=user.email,
|
|
283
283
|
status=user.status,
|
|
@@ -299,6 +299,7 @@ def _user_from_payload(payload: object) -> User:
|
|
|
299
299
|
created_at=_none_if_unset(user.created_at),
|
|
300
300
|
updated_at=_none_if_unset(user.updated_at),
|
|
301
301
|
)
|
|
302
|
+
return profile, cast("Mapping[str, JSONValue]", user.to_dict())
|
|
302
303
|
|
|
303
304
|
|
|
304
305
|
def _none_if_unset(value: _T | Unset) -> _T | None:
|
|
@@ -468,6 +469,10 @@ class AuthContext(Protocol):
|
|
|
468
469
|
|
|
469
470
|
def _capture_session_binding(self) -> tuple[int, int, Session | None]: ...
|
|
470
471
|
|
|
472
|
+
def _update_session_user_if_current(
|
|
473
|
+
self, user: Mapping[str, JSONValue], generation: int
|
|
474
|
+
) -> bool: ...
|
|
475
|
+
|
|
471
476
|
def _set_session_if_current(
|
|
472
477
|
self,
|
|
473
478
|
session: Session,
|
|
@@ -580,10 +585,7 @@ class Auth:
|
|
|
580
585
|
password=password,
|
|
581
586
|
metadata=dict(metadata or {}),
|
|
582
587
|
)
|
|
583
|
-
|
|
584
|
-
if self._client._capture_session()[0] != generation:
|
|
585
|
-
raise SessionChangedError
|
|
586
|
-
return user
|
|
588
|
+
return self._update_current_user(response_payload(response, 200), generation)
|
|
587
589
|
|
|
588
590
|
def reset_password_for_email(self, *, email: str) -> None:
|
|
589
591
|
"""Request a reset email without revealing whether the account exists."""
|
|
@@ -636,10 +638,7 @@ class Auth:
|
|
|
636
638
|
authorization=current.access_token,
|
|
637
639
|
token=token,
|
|
638
640
|
)
|
|
639
|
-
|
|
640
|
-
if self._client._capture_session()[0] != generation:
|
|
641
|
-
raise SessionChangedError
|
|
642
|
-
return user
|
|
641
|
+
return self._update_current_user(response_payload(response, 200), generation)
|
|
643
642
|
|
|
644
643
|
def delete_all_other_sessions(self) -> None:
|
|
645
644
|
"""Delete every other session while preserving the current session."""
|
|
@@ -945,6 +944,12 @@ class Auth:
|
|
|
945
944
|
)
|
|
946
945
|
response_payload(response, 200)
|
|
947
946
|
|
|
947
|
+
def _update_current_user(self, payload: object, generation: int) -> User:
|
|
948
|
+
user, snapshot = _user_from_payload(payload)
|
|
949
|
+
if not self._client._update_session_user_if_current(snapshot, generation):
|
|
950
|
+
raise SessionChangedError
|
|
951
|
+
return user
|
|
952
|
+
|
|
948
953
|
def get_user(self) -> User:
|
|
949
954
|
"""Load a server-validated profile for the current session."""
|
|
950
955
|
generation, current = self._client._capture_session()
|
|
@@ -952,10 +957,7 @@ class Auth:
|
|
|
952
957
|
raise AuthenticationError(_NO_ACTIVE_SESSION)
|
|
953
958
|
transport = cast("AuthGetUserTransport", self._client._transport)
|
|
954
959
|
response = invoke(transport.auth_get_user, authorization=current.access_token)
|
|
955
|
-
|
|
956
|
-
if self._client._capture_session()[0] != generation:
|
|
957
|
-
raise SessionChangedError
|
|
958
|
-
return user
|
|
960
|
+
return self._update_current_user(response_payload(response, 200), generation)
|
|
959
961
|
|
|
960
962
|
def update_user(
|
|
961
963
|
self,
|
|
@@ -974,10 +976,7 @@ class Auth:
|
|
|
974
976
|
password=password,
|
|
975
977
|
metadata=None if metadata is None else dict(metadata),
|
|
976
978
|
)
|
|
977
|
-
|
|
978
|
-
if self._client._capture_session()[0] != generation:
|
|
979
|
-
raise SessionChangedError
|
|
980
|
-
return user
|
|
979
|
+
return self._update_current_user(response_payload(response, 200), generation)
|
|
981
980
|
|
|
982
981
|
def sign_in(self, *, email: str, password: str) -> Session:
|
|
983
982
|
"""Sign in a user and store the returned session."""
|
volcano_sdk/client.py
CHANGED
|
@@ -4,11 +4,14 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
import threading
|
|
6
6
|
from collections import deque
|
|
7
|
+
from dataclasses import replace
|
|
7
8
|
from typing import TYPE_CHECKING
|
|
9
|
+
from uuid import UUID
|
|
8
10
|
|
|
9
11
|
from ._transport import GeneratedTransport, Transport
|
|
10
12
|
from .auth import Auth
|
|
11
13
|
from .database import Database
|
|
14
|
+
from .errors import AuthenticationError
|
|
12
15
|
from .functions import Functions
|
|
13
16
|
from .locks import Locks
|
|
14
17
|
from .logs import Logs
|
|
@@ -16,17 +19,19 @@ from .models import (
|
|
|
16
19
|
AuthChangeEvent,
|
|
17
20
|
AuthStateCallback,
|
|
18
21
|
AuthSubscription,
|
|
22
|
+
JSONValue,
|
|
19
23
|
Session,
|
|
20
24
|
)
|
|
21
25
|
from .realtime import CentrifugeFactory, Realtime
|
|
22
26
|
from .storage import Storage
|
|
23
27
|
|
|
24
28
|
if TYPE_CHECKING:
|
|
25
|
-
from collections.abc import Callable
|
|
29
|
+
from collections.abc import Callable, Mapping
|
|
26
30
|
from types import TracebackType
|
|
27
31
|
|
|
28
32
|
_NO_ACTIVE_SESSION = "No active session"
|
|
29
33
|
_NO_SERVICE_KEY = "No service key configured"
|
|
34
|
+
_PROFILE_USER_MISMATCH = "Profile user does not match the active session"
|
|
30
35
|
|
|
31
36
|
|
|
32
37
|
class _CallbackOutcome:
|
|
@@ -161,6 +166,25 @@ class VolcanoClient:
|
|
|
161
166
|
self._current_session,
|
|
162
167
|
)
|
|
163
168
|
|
|
169
|
+
def _update_session_user_if_current(
|
|
170
|
+
self, user: Mapping[str, JSONValue], generation: int
|
|
171
|
+
) -> bool:
|
|
172
|
+
with self._session_lock:
|
|
173
|
+
current = self._current_session
|
|
174
|
+
if generation != self._session_generation or current is None:
|
|
175
|
+
return False
|
|
176
|
+
try:
|
|
177
|
+
same_user = UUID(str(user["id"])) == UUID(current.user_id)
|
|
178
|
+
except ValueError:
|
|
179
|
+
same_user = False
|
|
180
|
+
if not same_user:
|
|
181
|
+
raise AuthenticationError(_PROFILE_USER_MISMATCH)
|
|
182
|
+
# Profile updates do not replace credentials or invalidate other requests.
|
|
183
|
+
self._current_session = replace(
|
|
184
|
+
current, user={**user, "id": current.user_id}
|
|
185
|
+
)
|
|
186
|
+
return True
|
|
187
|
+
|
|
164
188
|
def _set_session_if_current(
|
|
165
189
|
self,
|
|
166
190
|
session: Session,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: volcano-sdk-python
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.7.0
|
|
4
4
|
Summary: Official Python SDK for Volcano
|
|
5
5
|
Project-URL: Homepage, https://volcano.dev
|
|
6
6
|
Project-URL: Repository, https://github.com/Kong/volcano-sdk-python
|
|
@@ -315,18 +315,24 @@ including metadata. The snapshot is deeply immutable and available without a
|
|
|
315
315
|
request. It is cached data, not proof of authentication; use `auth.get_user()`
|
|
316
316
|
to fetch the server-validated profile. Existing three-field `Session` construction
|
|
317
317
|
still works, with `user=None`. An adopted snapshot must have the same user ID.
|
|
318
|
+
Successful `get_user()`, `update_user()`, `convert_anonymous()`, and
|
|
319
|
+
`confirm_email_change()` calls update that local snapshot without changing tokens
|
|
320
|
+
or emitting an auth-state event. Previously returned sessions remain immutable.
|
|
321
|
+
Profile identity checks compare UUID values; the session retains its original
|
|
322
|
+
user ID spelling, including in the cached snapshot.
|
|
318
323
|
|
|
319
324
|
`get_user()` sends the active access token to Volcano and returns an immutable, server-validated
|
|
320
325
|
profile with the complete public AuthUser fields. Profile timestamps are timezone-aware `datetime`
|
|
321
|
-
values, and nested user and application metadata are immutable. The request
|
|
322
|
-
|
|
323
|
-
request is in flight, `get_user()` raises `SessionChangedError` instead of returning a
|
|
324
|
-
stale credentials.
|
|
326
|
+
values, and nested user and application metadata are immutable. The request updates the cached
|
|
327
|
+
profile without changing credentials. If another authentication operation replaces the session
|
|
328
|
+
while the request is in flight, `get_user()` raises `SessionChangedError` instead of returning a
|
|
329
|
+
profile for stale credentials.
|
|
325
330
|
|
|
326
331
|
`update_user()` updates the current user's password, metadata, or both. Metadata is a shallow patch:
|
|
327
332
|
omitted keys remain unchanged, and setting a key to `None` removes it. The method returns the same
|
|
328
|
-
immutable profile type as `get_user()` and
|
|
329
|
-
response if another authentication operation replaces the session while the
|
|
333
|
+
immutable profile type as `get_user()` and updates the cached profile without changing credentials.
|
|
334
|
+
It also rejects a response if another authentication operation replaces the session while the
|
|
335
|
+
update is in flight.
|
|
330
336
|
|
|
331
337
|
Request a password reset email without creating or changing a session:
|
|
332
338
|
|
|
@@ -5,8 +5,8 @@ volcano_sdk/_lock_renewer.py,sha256=tFo1ZeRiPKB5VSbW61uoMpNR67ZDYyMEe-dCjpAM3As,
|
|
|
5
5
|
volcano_sdk/_lock_worker.py,sha256=QhSmwR0WcwgT-7ueJMlbCogLqOakQ5eRAtyWV74fgV0,3057
|
|
6
6
|
volcano_sdk/_realtime_fetch_worker.py,sha256=zQTgFF4cIUBeAL4J2OpWAlz_sIynv2BLfu588EogAdo,9269
|
|
7
7
|
volcano_sdk/_transport.py,sha256=apXZLuT9W17vO-7Ag2E289o2B-uL-Rndv_wRPm69x0I,55481
|
|
8
|
-
volcano_sdk/auth.py,sha256=
|
|
9
|
-
volcano_sdk/client.py,sha256=
|
|
8
|
+
volcano_sdk/auth.py,sha256=6TXpCZ0mPqluPh-nJSYUbjcryJTwZBz03zDSHuP1eZA,42262
|
|
9
|
+
volcano_sdk/client.py,sha256=KutjBUOEJyK73OibKilrcWazpWeXKEZYUehSAH8qxUE,10366
|
|
10
10
|
volcano_sdk/connection_string.py,sha256=LlTZigsrCF4IfAqQ8SDTWJv35hnnzNu1MfeVv5JW-Kk,2331
|
|
11
11
|
volcano_sdk/database.py,sha256=p_5OaqlBjKAtIUfKQCflXM_CGqokrVmpOMsp6KHiJ8A,9493
|
|
12
12
|
volcano_sdk/errors.py,sha256=ffs66FIN_jhyh5SMj7KdXb-zVV3DSgVK6xJEPNA5h9Y,1613
|
|
@@ -772,7 +772,7 @@ volcano_sdk/_generated/models/usage_data_point.py,sha256=d4U08JbVTiIMM_l2BmRILL1
|
|
|
772
772
|
volcano_sdk/_generated/models/variable.py,sha256=tvGDOMv77gW6j5skufnfrlNAhzCzA1F7nuPCQWx79yY,5926
|
|
773
773
|
volcano_sdk/_generated/models/variable_deploy_source.py,sha256=G_aCiFHzQx8WhJFs0GvYZEFclfTrfkDvd3kT1e3vty8,463
|
|
774
774
|
volcano_sdk/_generated/models/variable_status.py,sha256=THSsKBSiAYG4kAtBzGA4TZqxjgx8hdL0Q4qCdZfOj00,391
|
|
775
|
-
volcano_sdk_python-0.
|
|
776
|
-
volcano_sdk_python-0.
|
|
777
|
-
volcano_sdk_python-0.
|
|
778
|
-
volcano_sdk_python-0.
|
|
775
|
+
volcano_sdk_python-0.7.0.dist-info/METADATA,sha256=UvxuStKj4B3uX6BtRlrXlfjXNQw4GaOe6vDVN6nKF9c,33273
|
|
776
|
+
volcano_sdk_python-0.7.0.dist-info/WHEEL,sha256=zOwg4jB6zX2kU910N-cMawjivD6tO8NEWvE12je1bVk,87
|
|
777
|
+
volcano_sdk_python-0.7.0.dist-info/licenses/LICENSE,sha256=AIHJy-5MFqM8fHfuGFTTb9a8rvJGypcVgN4aZ-7XTWw,11387
|
|
778
|
+
volcano_sdk_python-0.7.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|