oidcauthlib 3.0.18__tar.gz → 3.0.19__tar.gz
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.
- {oidcauthlib-3.0.18/oidcauthlib.egg-info → oidcauthlib-3.0.19}/PKG-INFO +1 -1
- oidcauthlib-3.0.19/VERSION +1 -0
- oidcauthlib-3.0.19/oidcauthlib/cache_gate/__init__.py +4 -0
- oidcauthlib-3.0.19/oidcauthlib/cache_gate/advisory_lock.py +168 -0
- oidcauthlib-3.0.19/oidcauthlib/cache_gate/version_gate.py +103 -0
- {oidcauthlib-3.0.18 → oidcauthlib-3.0.19/oidcauthlib.egg-info}/PKG-INFO +1 -1
- {oidcauthlib-3.0.18 → oidcauthlib-3.0.19}/oidcauthlib.egg-info/SOURCES.txt +3 -0
- oidcauthlib-3.0.18/VERSION +0 -1
- {oidcauthlib-3.0.18 → oidcauthlib-3.0.19}/LICENSE +0 -0
- {oidcauthlib-3.0.18 → oidcauthlib-3.0.19}/MANIFEST.in +0 -0
- {oidcauthlib-3.0.18 → oidcauthlib-3.0.19}/README.md +0 -0
- {oidcauthlib-3.0.18 → oidcauthlib-3.0.19}/oidcauthlib/__init__.py +0 -0
- {oidcauthlib-3.0.18 → oidcauthlib-3.0.19}/oidcauthlib/auth/__init__.py +0 -0
- {oidcauthlib-3.0.18 → oidcauthlib-3.0.19}/oidcauthlib/auth/auth_helper.py +0 -0
- {oidcauthlib-3.0.18 → oidcauthlib-3.0.19}/oidcauthlib/auth/auth_manager.py +0 -0
- {oidcauthlib-3.0.18 → oidcauthlib-3.0.19}/oidcauthlib/auth/cache/__init__.py +0 -0
- {oidcauthlib-3.0.18 → oidcauthlib-3.0.19}/oidcauthlib/auth/cache/oauth_cache.py +0 -0
- {oidcauthlib-3.0.18 → oidcauthlib-3.0.19}/oidcauthlib/auth/cache/oauth_memory_cache.py +0 -0
- {oidcauthlib-3.0.18 → oidcauthlib-3.0.19}/oidcauthlib/auth/cache/oauth_mongo_cache.py +0 -0
- {oidcauthlib-3.0.18 → oidcauthlib-3.0.19}/oidcauthlib/auth/config/__init__.py +0 -0
- {oidcauthlib-3.0.18 → oidcauthlib-3.0.19}/oidcauthlib/auth/config/auth_config.py +0 -0
- {oidcauthlib-3.0.18 → oidcauthlib-3.0.19}/oidcauthlib/auth/config/auth_config_reader.py +0 -0
- {oidcauthlib-3.0.18 → oidcauthlib-3.0.19}/oidcauthlib/auth/dcr/__init__.py +0 -0
- {oidcauthlib-3.0.18 → oidcauthlib-3.0.19}/oidcauthlib/auth/dcr/dcr_client.py +0 -0
- {oidcauthlib-3.0.18 → oidcauthlib-3.0.19}/oidcauthlib/auth/dcr/dcr_manager.py +0 -0
- {oidcauthlib-3.0.18 → oidcauthlib-3.0.19}/oidcauthlib/auth/dcr/dcr_registration.py +0 -0
- {oidcauthlib-3.0.18 → oidcauthlib-3.0.19}/oidcauthlib/auth/exceptions/__init__.py +0 -0
- {oidcauthlib-3.0.18 → oidcauthlib-3.0.19}/oidcauthlib/auth/exceptions/authorization_bearer_token_expired_exception.py +0 -0
- {oidcauthlib-3.0.18 → oidcauthlib-3.0.19}/oidcauthlib/auth/exceptions/authorization_bearer_token_invalid_exception.py +0 -0
- {oidcauthlib-3.0.18 → oidcauthlib-3.0.19}/oidcauthlib/auth/exceptions/authorization_bearer_token_missing_exception.py +0 -0
- {oidcauthlib-3.0.18 → oidcauthlib-3.0.19}/oidcauthlib/auth/exceptions/authorization_needed_exception.py +0 -0
- {oidcauthlib-3.0.18 → oidcauthlib-3.0.19}/oidcauthlib/auth/fastapi_auth_manager.py +0 -0
- {oidcauthlib-3.0.18 → oidcauthlib-3.0.19}/oidcauthlib/auth/middleware/__init__.py +0 -0
- {oidcauthlib-3.0.18 → oidcauthlib-3.0.19}/oidcauthlib/auth/middleware/request_scope_middleware.py +0 -0
- {oidcauthlib-3.0.18 → oidcauthlib-3.0.19}/oidcauthlib/auth/middleware/token_reader_middleware.py +0 -0
- {oidcauthlib-3.0.18 → oidcauthlib-3.0.19}/oidcauthlib/auth/models/__init__.py +0 -0
- {oidcauthlib-3.0.18 → oidcauthlib-3.0.19}/oidcauthlib/auth/models/auth.py +0 -0
- {oidcauthlib-3.0.18 → oidcauthlib-3.0.19}/oidcauthlib/auth/models/base_db_model.py +0 -0
- {oidcauthlib-3.0.18 → oidcauthlib-3.0.19}/oidcauthlib/auth/models/cache_item.py +0 -0
- {oidcauthlib-3.0.18 → oidcauthlib-3.0.19}/oidcauthlib/auth/models/client_key_set.py +0 -0
- {oidcauthlib-3.0.18 → oidcauthlib-3.0.19}/oidcauthlib/auth/models/token.py +0 -0
- {oidcauthlib-3.0.18 → oidcauthlib-3.0.19}/oidcauthlib/auth/repository/__init__.py +0 -0
- {oidcauthlib-3.0.18 → oidcauthlib-3.0.19}/oidcauthlib/auth/repository/base_repository.py +0 -0
- {oidcauthlib-3.0.18 → oidcauthlib-3.0.19}/oidcauthlib/auth/repository/memory/__init__.py +0 -0
- {oidcauthlib-3.0.18 → oidcauthlib-3.0.19}/oidcauthlib/auth/repository/memory/memory_repository.py +0 -0
- {oidcauthlib-3.0.18 → oidcauthlib-3.0.19}/oidcauthlib/auth/repository/mongo/__init__.py +0 -0
- {oidcauthlib-3.0.18 → oidcauthlib-3.0.19}/oidcauthlib/auth/repository/mongo/mongo_repository.py +0 -0
- {oidcauthlib-3.0.18 → oidcauthlib-3.0.19}/oidcauthlib/auth/repository/repository_factory.py +0 -0
- {oidcauthlib-3.0.18 → oidcauthlib-3.0.19}/oidcauthlib/auth/routers/__init__.py +0 -0
- {oidcauthlib-3.0.18 → oidcauthlib-3.0.19}/oidcauthlib/auth/routers/auth_router.py +0 -0
- {oidcauthlib-3.0.18 → oidcauthlib-3.0.19}/oidcauthlib/auth/token_reader.py +0 -0
- {oidcauthlib-3.0.18 → oidcauthlib-3.0.19}/oidcauthlib/auth/well_known_configuration/__init__.py +0 -0
- {oidcauthlib-3.0.18 → oidcauthlib-3.0.19}/oidcauthlib/auth/well_known_configuration/auth_server_metadata.py +0 -0
- {oidcauthlib-3.0.18 → oidcauthlib-3.0.19}/oidcauthlib/auth/well_known_configuration/auth_server_metadata_discovery.py +0 -0
- {oidcauthlib-3.0.18 → oidcauthlib-3.0.19}/oidcauthlib/auth/well_known_configuration/well_known_configuration_cache.py +0 -0
- {oidcauthlib-3.0.18 → oidcauthlib-3.0.19}/oidcauthlib/auth/well_known_configuration/well_known_configuration_cache_result.py +0 -0
- {oidcauthlib-3.0.18 → oidcauthlib-3.0.19}/oidcauthlib/auth/well_known_configuration/well_known_configuration_manager.py +0 -0
- {oidcauthlib-3.0.18 → oidcauthlib-3.0.19}/oidcauthlib/container/__init__.py +0 -0
- {oidcauthlib-3.0.18 → oidcauthlib-3.0.19}/oidcauthlib/container/oidc_authlib_container_factory.py +0 -0
- {oidcauthlib-3.0.18 → oidcauthlib-3.0.19}/oidcauthlib/open_telemetry/__init__.py +0 -0
- {oidcauthlib-3.0.18 → oidcauthlib-3.0.19}/oidcauthlib/open_telemetry/attribute_names.py +0 -0
- {oidcauthlib-3.0.18 → oidcauthlib-3.0.19}/oidcauthlib/open_telemetry/span_names.py +0 -0
- {oidcauthlib-3.0.18 → oidcauthlib-3.0.19}/oidcauthlib/py.typed +0 -0
- {oidcauthlib-3.0.18 → oidcauthlib-3.0.19}/oidcauthlib/storage/__init__.py +0 -0
- {oidcauthlib-3.0.18 → oidcauthlib-3.0.19}/oidcauthlib/storage/cache_to_collection_mapper.py +0 -0
- {oidcauthlib-3.0.18 → oidcauthlib-3.0.19}/oidcauthlib/storage/memory_storage_factory.py +0 -0
- {oidcauthlib-3.0.18 → oidcauthlib-3.0.19}/oidcauthlib/storage/mongo_gridfs_db.py +0 -0
- {oidcauthlib-3.0.18 → oidcauthlib-3.0.19}/oidcauthlib/storage/mongo_gridfs_exception.py +0 -0
- {oidcauthlib-3.0.18 → oidcauthlib-3.0.19}/oidcauthlib/storage/mongo_storage_factory.py +0 -0
- {oidcauthlib-3.0.18 → oidcauthlib-3.0.19}/oidcauthlib/storage/storage_factory.py +0 -0
- {oidcauthlib-3.0.18 → oidcauthlib-3.0.19}/oidcauthlib/storage/storage_factory_creator.py +0 -0
- {oidcauthlib-3.0.18 → oidcauthlib-3.0.19}/oidcauthlib/utilities/__init__.py +0 -0
- {oidcauthlib-3.0.18 → oidcauthlib-3.0.19}/oidcauthlib/utilities/cached.py +0 -0
- {oidcauthlib-3.0.18 → oidcauthlib-3.0.19}/oidcauthlib/utilities/environment/__init__.py +0 -0
- {oidcauthlib-3.0.18 → oidcauthlib-3.0.19}/oidcauthlib/utilities/environment/abstract_environment_variables.py +0 -0
- {oidcauthlib-3.0.18 → oidcauthlib-3.0.19}/oidcauthlib/utilities/environment/oidc_environment_variables.py +0 -0
- {oidcauthlib-3.0.18 → oidcauthlib-3.0.19}/oidcauthlib/utilities/logger/__init__.py +0 -0
- {oidcauthlib-3.0.18 → oidcauthlib-3.0.19}/oidcauthlib/utilities/logger/log_levels.py +0 -0
- {oidcauthlib-3.0.18 → oidcauthlib-3.0.19}/oidcauthlib/utilities/logger/logging_response.py +0 -0
- {oidcauthlib-3.0.18 → oidcauthlib-3.0.19}/oidcauthlib/utilities/logger/logging_transport.py +0 -0
- {oidcauthlib-3.0.18 → oidcauthlib-3.0.19}/oidcauthlib/utilities/mongo_url_utils.py +0 -0
- {oidcauthlib-3.0.18 → oidcauthlib-3.0.19}/oidcauthlib/utilities/url_validator.py +0 -0
- {oidcauthlib-3.0.18 → oidcauthlib-3.0.19}/oidcauthlib.egg-info/dependency_links.txt +0 -0
- {oidcauthlib-3.0.18 → oidcauthlib-3.0.19}/oidcauthlib.egg-info/requires.txt +0 -0
- {oidcauthlib-3.0.18 → oidcauthlib-3.0.19}/oidcauthlib.egg-info/top_level.txt +0 -0
- {oidcauthlib-3.0.18 → oidcauthlib-3.0.19}/pyproject.toml +0 -0
- {oidcauthlib-3.0.18 → oidcauthlib-3.0.19}/setup.cfg +0 -0
- {oidcauthlib-3.0.18 → oidcauthlib-3.0.19}/tests/test_mongo_url_utils.py +0 -0
- {oidcauthlib-3.0.18 → oidcauthlib-3.0.19}/tests/test_simple.py +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.0.19
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
"""Staged here pending extraction into a standalone shared-infra package —
|
|
2
|
+
this module has no OIDC-specific logic and doesn't belong in oidc-auth-lib
|
|
3
|
+
long-term; it's here because this is the one package every consumer of
|
|
4
|
+
VersionGate already depends on.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import asyncio
|
|
10
|
+
import contextlib
|
|
11
|
+
import logging
|
|
12
|
+
import os
|
|
13
|
+
import uuid
|
|
14
|
+
from types import TracebackType
|
|
15
|
+
|
|
16
|
+
from key_value.aio.protocols.key_value import AsyncKeyValueProtocol
|
|
17
|
+
|
|
18
|
+
logger = logging.getLogger(__name__)
|
|
19
|
+
|
|
20
|
+
LOCK_COLLECTION = "advisory_locks"
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class AdvisoryLock:
|
|
24
|
+
"""Distributed advisory lock backed by any AsyncKeyValueProtocol store.
|
|
25
|
+
|
|
26
|
+
Uses put-with-TTL so that stale locks auto-expire if the holder crashes.
|
|
27
|
+
Acquire semantics rely on get-then-put — not atomic compare-and-swap —
|
|
28
|
+
so there is a small race window. For leader-election among cooperating
|
|
29
|
+
workers at startup this is acceptable; do not use for high-contention
|
|
30
|
+
critical sections.
|
|
31
|
+
|
|
32
|
+
While held, a background task refreshes the lock's TTL every
|
|
33
|
+
`heartbeat_interval_seconds` (default `ttl_seconds / 2`) — so `ttl_seconds`
|
|
34
|
+
only bounds how quickly a *crashed* holder's lock is reclaimed, not how
|
|
35
|
+
long the guarded critical section may run. Without this, a critical
|
|
36
|
+
section that legitimately outlives `ttl_seconds` would have its lock
|
|
37
|
+
entry expire while still running, letting a second holder acquire the
|
|
38
|
+
same lock and re-enter concurrently — exactly what the lock exists to
|
|
39
|
+
prevent. A single missed heartbeat is not fatal: it's logged and retried
|
|
40
|
+
on the next tick, with a full `heartbeat_interval_seconds` of margin
|
|
41
|
+
before the lock would actually expire.
|
|
42
|
+
|
|
43
|
+
Usage:
|
|
44
|
+
async with AdvisoryLock(store, "skill_sync", ttl_seconds=300) as acquired:
|
|
45
|
+
if acquired:
|
|
46
|
+
await do_sync()
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
def __init__(
|
|
50
|
+
self,
|
|
51
|
+
store: AsyncKeyValueProtocol,
|
|
52
|
+
lock_name: str,
|
|
53
|
+
*,
|
|
54
|
+
ttl_seconds: int = 300,
|
|
55
|
+
collection: str = LOCK_COLLECTION,
|
|
56
|
+
heartbeat_interval_seconds: float | None = None,
|
|
57
|
+
) -> None:
|
|
58
|
+
self._store = store
|
|
59
|
+
self._lock_name = lock_name
|
|
60
|
+
self._ttl_seconds = ttl_seconds
|
|
61
|
+
self._collection = collection
|
|
62
|
+
self._heartbeat_interval_seconds = heartbeat_interval_seconds or (ttl_seconds / 2)
|
|
63
|
+
self._holder_id = f"{os.getpid()}-{uuid.uuid4().hex[:8]}"
|
|
64
|
+
self._acquired = False
|
|
65
|
+
self._heartbeat_task: asyncio.Task[None] | None = None
|
|
66
|
+
|
|
67
|
+
@property
|
|
68
|
+
def acquired(self) -> bool:
|
|
69
|
+
return self._acquired
|
|
70
|
+
|
|
71
|
+
async def __aenter__(self) -> bool:
|
|
72
|
+
existing = await self._store.get(self._lock_name, collection=self._collection)
|
|
73
|
+
|
|
74
|
+
if existing is not None:
|
|
75
|
+
logger.info(
|
|
76
|
+
"AdvisoryLock: lock '%s' held by '%s' — skipping.",
|
|
77
|
+
self._lock_name,
|
|
78
|
+
existing.get("holder", "unknown"),
|
|
79
|
+
)
|
|
80
|
+
self._acquired = False
|
|
81
|
+
return False
|
|
82
|
+
|
|
83
|
+
await self._store.put(
|
|
84
|
+
self._lock_name,
|
|
85
|
+
{"holder": self._holder_id},
|
|
86
|
+
collection=self._collection,
|
|
87
|
+
ttl=self._ttl_seconds,
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
# Verify we won the race (read-after-write check)
|
|
91
|
+
written = await self._store.get(self._lock_name, collection=self._collection)
|
|
92
|
+
if written is not None and written.get("holder") == self._holder_id:
|
|
93
|
+
self._acquired = True
|
|
94
|
+
logger.info(
|
|
95
|
+
"AdvisoryLock: acquired lock '%s' (holder=%s, ttl=%ds)",
|
|
96
|
+
self._lock_name,
|
|
97
|
+
self._holder_id,
|
|
98
|
+
self._ttl_seconds,
|
|
99
|
+
)
|
|
100
|
+
self._heartbeat_task = asyncio.create_task(self._heartbeat_loop())
|
|
101
|
+
else:
|
|
102
|
+
self._acquired = False
|
|
103
|
+
logger.info(
|
|
104
|
+
"AdvisoryLock: lost race for lock '%s' — another holder won.",
|
|
105
|
+
self._lock_name,
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
return self._acquired
|
|
109
|
+
|
|
110
|
+
async def _heartbeat_loop(self) -> None:
|
|
111
|
+
while True:
|
|
112
|
+
await asyncio.sleep(self._heartbeat_interval_seconds)
|
|
113
|
+
try:
|
|
114
|
+
await self._store.put(
|
|
115
|
+
self._lock_name,
|
|
116
|
+
{"holder": self._holder_id},
|
|
117
|
+
collection=self._collection,
|
|
118
|
+
ttl=self._ttl_seconds,
|
|
119
|
+
)
|
|
120
|
+
logger.debug(
|
|
121
|
+
"AdvisoryLock: refreshed lock '%s' (holder=%s, ttl=%ds)",
|
|
122
|
+
self._lock_name,
|
|
123
|
+
self._holder_id,
|
|
124
|
+
self._ttl_seconds,
|
|
125
|
+
)
|
|
126
|
+
except Exception:
|
|
127
|
+
logger.warning(
|
|
128
|
+
"AdvisoryLock: failed to refresh lock '%s' (holder=%s) — will retry "
|
|
129
|
+
"in %.1fs; lock still expires in up to %ds if refreshes keep failing.",
|
|
130
|
+
self._lock_name,
|
|
131
|
+
self._holder_id,
|
|
132
|
+
self._heartbeat_interval_seconds,
|
|
133
|
+
self._ttl_seconds,
|
|
134
|
+
exc_info=True,
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
async def __aexit__(
|
|
138
|
+
self,
|
|
139
|
+
exc_type: type[BaseException] | None,
|
|
140
|
+
exc_val: BaseException | None,
|
|
141
|
+
exc_tb: TracebackType | None,
|
|
142
|
+
) -> None:
|
|
143
|
+
if self._heartbeat_task is not None:
|
|
144
|
+
self._heartbeat_task.cancel()
|
|
145
|
+
with contextlib.suppress(asyncio.CancelledError):
|
|
146
|
+
await self._heartbeat_task
|
|
147
|
+
self._heartbeat_task = None
|
|
148
|
+
|
|
149
|
+
if self._acquired:
|
|
150
|
+
try:
|
|
151
|
+
await self._store.delete(self._lock_name, collection=self._collection)
|
|
152
|
+
logger.info(
|
|
153
|
+
"AdvisoryLock: released lock '%s' (holder=%s)",
|
|
154
|
+
self._lock_name,
|
|
155
|
+
self._holder_id,
|
|
156
|
+
)
|
|
157
|
+
except Exception:
|
|
158
|
+
# Don't let a failed release mask an exception already propagating
|
|
159
|
+
# from the caller's critical section. The heartbeat has stopped, so
|
|
160
|
+
# the lock still self-clears once its TTL elapses.
|
|
161
|
+
logger.warning(
|
|
162
|
+
"AdvisoryLock: failed to release lock '%s' (holder=%s) — it will "
|
|
163
|
+
"still expire via TTL in up to %ds.",
|
|
164
|
+
self._lock_name,
|
|
165
|
+
self._holder_id,
|
|
166
|
+
self._ttl_seconds,
|
|
167
|
+
exc_info=True,
|
|
168
|
+
)
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"""Staged here pending extraction into a standalone shared-infra package —
|
|
2
|
+
this module has no OIDC-specific logic and doesn't belong in oidc-auth-lib
|
|
3
|
+
long-term; it's here because this is the one package both current consumers
|
|
4
|
+
(baileyai, baileyai-skills-service) already depend on.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import logging
|
|
10
|
+
from collections.abc import Awaitable, Callable
|
|
11
|
+
|
|
12
|
+
from key_value.aio.protocols.key_value import AsyncKeyValueProtocol
|
|
13
|
+
|
|
14
|
+
from oidcauthlib.cache_gate.advisory_lock import AdvisoryLock
|
|
15
|
+
|
|
16
|
+
logger = logging.getLogger(__name__)
|
|
17
|
+
|
|
18
|
+
VERSION_GATE_COLLECTION = "version_gate"
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class VersionGate:
|
|
22
|
+
"""Runs an action at most once per distinct version, across a pod fleet.
|
|
23
|
+
|
|
24
|
+
Compares `current_version` against a marker persisted in `store`. If it
|
|
25
|
+
differs, runs `action` and — only once `action` completes without raising
|
|
26
|
+
— persists the new marker. The marker is written *after* the action
|
|
27
|
+
succeeds, never before: writing it first would let a crashed/incomplete
|
|
28
|
+
action look "done" to every other pod, forever, until the version next
|
|
29
|
+
changes.
|
|
30
|
+
|
|
31
|
+
Pass `lock_name` when `action` is not safe to run concurrently from
|
|
32
|
+
multiple pods (e.g. a multi-step load with partial-write visibility).
|
|
33
|
+
Omit it when `action` is idempotent under concurrent execution (e.g. a
|
|
34
|
+
pure cache clear followed by lazy repopulation) — redundant concurrent
|
|
35
|
+
runs are then harmless and a lock only adds latency/contention.
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
def __init__(
|
|
39
|
+
self,
|
|
40
|
+
*,
|
|
41
|
+
store: AsyncKeyValueProtocol,
|
|
42
|
+
key: str,
|
|
43
|
+
collection: str = VERSION_GATE_COLLECTION,
|
|
44
|
+
lock_name: str | None = None,
|
|
45
|
+
lock_ttl_seconds: int = 300,
|
|
46
|
+
lock_heartbeat_interval_seconds: float | None = None,
|
|
47
|
+
) -> None:
|
|
48
|
+
self._store = store
|
|
49
|
+
self._key = key
|
|
50
|
+
self._collection = collection
|
|
51
|
+
self._lock_name = lock_name
|
|
52
|
+
self._lock_ttl_seconds = lock_ttl_seconds
|
|
53
|
+
self._lock_heartbeat_interval_seconds = lock_heartbeat_interval_seconds
|
|
54
|
+
|
|
55
|
+
async def run_if_changed_async(
|
|
56
|
+
self,
|
|
57
|
+
*,
|
|
58
|
+
current_version: str,
|
|
59
|
+
action: Callable[[], Awaitable[None]],
|
|
60
|
+
) -> bool:
|
|
61
|
+
"""Runs `action` and records `current_version` iff it differs from the
|
|
62
|
+
stored marker. Returns True iff `action` ran.
|
|
63
|
+
"""
|
|
64
|
+
if await self._is_current(current_version):
|
|
65
|
+
return False
|
|
66
|
+
|
|
67
|
+
if self._lock_name is None:
|
|
68
|
+
await action()
|
|
69
|
+
await self._mark(current_version)
|
|
70
|
+
return True
|
|
71
|
+
|
|
72
|
+
async with AdvisoryLock(
|
|
73
|
+
self._store,
|
|
74
|
+
self._lock_name,
|
|
75
|
+
ttl_seconds=self._lock_ttl_seconds,
|
|
76
|
+
heartbeat_interval_seconds=self._lock_heartbeat_interval_seconds,
|
|
77
|
+
) as acquired:
|
|
78
|
+
if not acquired:
|
|
79
|
+
logger.info(
|
|
80
|
+
"VersionGate: lock '%s' held by another pod — skipping.",
|
|
81
|
+
self._lock_name,
|
|
82
|
+
)
|
|
83
|
+
return False
|
|
84
|
+
|
|
85
|
+
# Re-check: whoever held the lock before us may have already
|
|
86
|
+
# handled this version change while we were waiting to acquire.
|
|
87
|
+
if await self._is_current(current_version):
|
|
88
|
+
return False
|
|
89
|
+
|
|
90
|
+
await action()
|
|
91
|
+
await self._mark(current_version)
|
|
92
|
+
return True
|
|
93
|
+
|
|
94
|
+
async def _is_current(self, current_version: str) -> bool:
|
|
95
|
+
stored = await self._store.get(self._key, collection=self._collection)
|
|
96
|
+
return stored is not None and stored.get("version") == current_version
|
|
97
|
+
|
|
98
|
+
async def _mark(self, current_version: str) -> None:
|
|
99
|
+
await self._store.put(
|
|
100
|
+
self._key,
|
|
101
|
+
{"version": current_version},
|
|
102
|
+
collection=self._collection,
|
|
103
|
+
)
|
|
@@ -55,6 +55,9 @@ oidcauthlib/auth/well_known_configuration/auth_server_metadata_discovery.py
|
|
|
55
55
|
oidcauthlib/auth/well_known_configuration/well_known_configuration_cache.py
|
|
56
56
|
oidcauthlib/auth/well_known_configuration/well_known_configuration_cache_result.py
|
|
57
57
|
oidcauthlib/auth/well_known_configuration/well_known_configuration_manager.py
|
|
58
|
+
oidcauthlib/cache_gate/__init__.py
|
|
59
|
+
oidcauthlib/cache_gate/advisory_lock.py
|
|
60
|
+
oidcauthlib/cache_gate/version_gate.py
|
|
58
61
|
oidcauthlib/container/__init__.py
|
|
59
62
|
oidcauthlib/container/oidc_authlib_container_factory.py
|
|
60
63
|
oidcauthlib/open_telemetry/__init__.py
|
oidcauthlib-3.0.18/VERSION
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
3.0.18
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{oidcauthlib-3.0.18 → oidcauthlib-3.0.19}/oidcauthlib/auth/middleware/request_scope_middleware.py
RENAMED
|
File without changes
|
{oidcauthlib-3.0.18 → oidcauthlib-3.0.19}/oidcauthlib/auth/middleware/token_reader_middleware.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{oidcauthlib-3.0.18 → oidcauthlib-3.0.19}/oidcauthlib/auth/repository/memory/memory_repository.py
RENAMED
|
File without changes
|
|
File without changes
|
{oidcauthlib-3.0.18 → oidcauthlib-3.0.19}/oidcauthlib/auth/repository/mongo/mongo_repository.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{oidcauthlib-3.0.18 → oidcauthlib-3.0.19}/oidcauthlib/auth/well_known_configuration/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{oidcauthlib-3.0.18 → oidcauthlib-3.0.19}/oidcauthlib/container/oidc_authlib_container_factory.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|