|
4 | 4 |
|
5 | 5 | import sentry_sdk |
6 | 6 | from safir.datetime import format_datetime_for_logging |
| 7 | +from sqlalchemy.exc import IntegrityError |
7 | 8 | from sqlalchemy.ext.asyncio import AsyncSession |
8 | 9 | from structlog.stdlib import BoundLogger |
9 | 10 |
|
10 | 11 | from ..cache import InternalTokenCache, NotebookTokenCache |
11 | 12 | from ..config import Config |
| 13 | +from ..exceptions import InvalidTokenError |
12 | 14 | from ..models.enums import TokenChange, TokenType |
13 | 15 | from ..models.history import TokenChangeHistoryEntry |
14 | 16 | from ..models.token import Token, TokenData |
@@ -141,10 +143,9 @@ async def get_internal_token( |
141 | 143 | valid = await self._is_token_valid(token, minimum_lifetime, scopes) |
142 | 144 | if token and valid: |
143 | 145 | return token |
144 | | - async with self._session.begin(): |
145 | | - token = await self._create_internal_token( |
146 | | - token_data, service, scopes, ip_address, minimum_lifetime |
147 | | - ) |
| 146 | + token = await self._create_internal_token( |
| 147 | + token_data, service, scopes, ip_address, minimum_lifetime |
| 148 | + ) |
148 | 149 | self._internal_cache.store(token_data, service, scopes, token) |
149 | 150 | return token |
150 | 151 |
|
@@ -187,10 +188,9 @@ async def get_notebook_token( |
187 | 188 | token = self._notebook_cache.get(token_data) |
188 | 189 | if token and await self._is_token_valid(token, minimum_lifetime): |
189 | 190 | return token |
190 | | - async with self._session.begin(): |
191 | | - token = await self._create_notebook_token( |
192 | | - token_data, ip_address, minimum_lifetime |
193 | | - ) |
| 191 | + token = await self._create_notebook_token( |
| 192 | + token_data, ip_address, minimum_lifetime |
| 193 | + ) |
194 | 194 | self._notebook_cache.store(token_data, token) |
195 | 195 | return token |
196 | 196 |
|
@@ -225,14 +225,21 @@ async def _create_internal_token( |
225 | 225 | ------- |
226 | 226 | Token |
227 | 227 | Retrieved or newly-created internal token. |
| 228 | +
|
| 229 | + Raises |
| 230 | + ------ |
| 231 | + InvalidTokenError |
| 232 | + Raised if the parent token is invalid, probably due to a |
| 233 | + desynchronization between Redis and the SQL database. |
228 | 234 | """ |
229 | 235 | # See if there's already a matching internal token. |
230 | | - key = await self._token_db_store.get_internal_token_key( |
231 | | - token_data, |
232 | | - service, |
233 | | - scopes, |
234 | | - self._minimum_expiration(token_data, minimum_lifetime), |
235 | | - ) |
| 236 | + async with self._session.begin(): |
| 237 | + key = await self._token_db_store.get_internal_token_key( |
| 238 | + token_data, |
| 239 | + service, |
| 240 | + scopes, |
| 241 | + self._minimum_expiration(token_data, minimum_lifetime), |
| 242 | + ) |
236 | 243 | if key: |
237 | 244 | data = await self._token_redis_store.get_data_by_key(key) |
238 | 245 | if data: |
@@ -272,10 +279,17 @@ async def _create_internal_token( |
272 | 279 | event_time=created, |
273 | 280 | ) |
274 | 281 |
|
| 282 | + parent = token_data.token.key |
275 | 283 | await self._token_redis_store.store_data(data) |
276 | 284 | try: |
277 | | - await self._token_db_store.add(data, parent=token_data.token.key) |
278 | | - await self._token_change_store.add(history_entry) |
| 285 | + async with self._session.begin(): |
| 286 | + await self._token_db_store.add(data, parent=parent) |
| 287 | + await self._token_change_store.add(history_entry) |
| 288 | + except IntegrityError as e: |
| 289 | + msg = "Integrity error creating internal token" |
| 290 | + self._logger.exception(msg, error=str(e)) |
| 291 | + await self._token_redis_store.delete(data.token.key) |
| 292 | + raise InvalidTokenError(msg) from e |
279 | 293 | except Exception: |
280 | 294 | await self._token_redis_store.delete(data.token.key) |
281 | 295 | raise |
@@ -316,11 +330,19 @@ async def _create_notebook_token( |
316 | 330 | ------- |
317 | 331 | Token |
318 | 332 | The retrieved or newly-created notebook token. |
| 333 | +
|
| 334 | + Raises |
| 335 | + ------ |
| 336 | + InvalidTokenError |
| 337 | + Raised if the parent token is invalid, probably due to a |
| 338 | + desynchronization between Redis and the SQL database. |
319 | 339 | """ |
320 | 340 | # See if there's already a matching notebook token. |
321 | | - key = await self._token_db_store.get_notebook_token_key( |
322 | | - token_data, self._minimum_expiration(token_data, minimum_lifetime) |
323 | | - ) |
| 341 | + async with self._session.begin(): |
| 342 | + key = await self._token_db_store.get_notebook_token_key( |
| 343 | + token_data, |
| 344 | + self._minimum_expiration(token_data, minimum_lifetime), |
| 345 | + ) |
324 | 346 | if key: |
325 | 347 | data = await self._token_redis_store.get_data_by_key(key) |
326 | 348 | if data: |
@@ -358,10 +380,17 @@ async def _create_notebook_token( |
358 | 380 | event_time=created, |
359 | 381 | ) |
360 | 382 |
|
| 383 | + parent = token_data.token.key |
361 | 384 | await self._token_redis_store.store_data(data) |
362 | 385 | try: |
363 | | - await self._token_db_store.add(data, parent=token_data.token.key) |
364 | | - await self._token_change_store.add(history_entry) |
| 386 | + async with self._session.begin(): |
| 387 | + await self._token_db_store.add(data, parent=parent) |
| 388 | + await self._token_change_store.add(history_entry) |
| 389 | + except IntegrityError as e: |
| 390 | + msg = "Integrity error creating notebook token" |
| 391 | + self._logger.exception(msg, error=str(e)) |
| 392 | + await self._token_redis_store.delete(data.token.key) |
| 393 | + raise InvalidTokenError(msg) from e |
365 | 394 | except Exception: |
366 | 395 | await self._token_redis_store.delete(data.token.key) |
367 | 396 | raise |
|
0 commit comments