ashb commented on code in PR #68499:
URL: https://github.com/apache/airflow/pull/68499#discussion_r3408584877
##########
airflow-core/tests/unit/api_fastapi/execution_api/versions/head/test_router.py:
##########
@@ -61,9 +120,49 @@ def test_expiring_token_is_reissued(
lifespan.registry.register_value(JWTValidator, auth)
# In order to test this we need any endpoint to hit. The easiest one to
use is variable get
- response = client.get("/execution/variables/key1",
headers={"Authorization": "Bearer dummy"})
+ response = jwt_bearer_client.get("/execution/variables/key1",
headers={"Authorization": "Bearer dummy"})
if expect_refreshed_token:
assert "Refreshed-API-Token" in response.headers
else:
assert "Refreshed-API-Token" not in response.headers
+ # avalidated_claims must be called exactly once — by JWTBearer only, not
by the middleware.
+ auth.avalidated_claims.assert_awaited_once_with("dummy", {})
+
+
[email protected]_test
+def test_token_expiring_mid_request_is_reissued_without_revalidation(
+ jwt_bearer_client, exec_app: FastAPI, time_machine
+):
+ """Middleware reissues from cached JWTBearer claims without re-validating
the token.
+
+ Regression test for the TOCTOU race in JWTReissueMiddleware: a heartbeat
arrives with a
+ token that has ~0s left, JWTBearer validates it (still technically valid
at that moment),
+ the request starts, and the middleware runs. In the old code the
middleware would call
+ avalidated_claims a second time and get ExpiredSignatureError — no
Refreshed-API-Token
+ header would be set, and the task would die on the next heartbeat.
+
+ With the fix the middleware reads claims from request.scope (set by
JWTBearer) instead of
+ calling avalidated_claims again, so it still issues a fresh token even
when the original
+ has since expired.
+ """
+ moment = 1743451846
+ auth = AsyncMock(spec=JWTValidator)
+ auth.avalidated_claims.return_value = {
+ "sub": "edb09971-4e0e-4221-ad3f-800852d38085",
+ "iat": moment,
+ "exp": moment + 600,
+ }
+
+ # Move time to 1 second past the token's expiry. JWTBearer already
accepted the token
+ # (mocked); the middleware must still issue a refresh using the cached
claims rather than
+ # silently dropping it.
+ time_machine.move_to(moment + 601, tick=False)
+
+ lifespan.registry.register_value(JWTValidator, auth)
+
+ response = jwt_bearer_client.get("/execution/variables/key1",
headers={"Authorization": "Bearer dummy"})
+
+ assert "Refreshed-API-Token" in response.headers
Review Comment:
This isn't really testing the TOCTOU bug - fairly sure this would pass
without any code changes.
I think what you need to do is register a custom route that does a time
travel inside it
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]