This is an automated email from the ASF dual-hosted git repository.
arm pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tooling-trusted-releases.git
The following commit(s) were added to refs/heads/main by this push:
new 1d2e7ab #596 - finite session lifetime by config - 72 hour default.
1d2e7ab is described below
commit 1d2e7ab4239bec5fc3dea81ef97fbb76819616a9
Author: Alastair McFarlane <[email protected]>
AuthorDate: Wed Jan 28 14:32:15 2026 +0000
#596 - finite session lifetime by config - 72 hour default.
---
atr/config.py | 1 +
atr/server.py | 34 ++++++++++++++++++++++++++++++++++
2 files changed, 35 insertions(+)
diff --git a/atr/config.py b/atr/config.py
index e1ff073..42a834a 100644
--- a/atr/config.py
+++ b/atr/config.py
@@ -68,6 +68,7 @@ def _config_secrets_get(
class AppConfig:
+ ABSOLUTE_SESSION_MAX_SECONDS =
decouple.config("ABSOLUTE_SESSION_MAX_SECONDS", default=60 * 60 * 72, cast=int)
ALLOW_TESTS = decouple.config("ALLOW_TESTS", default=False, cast=bool)
DISABLE_CHECK_CACHE = decouple.config("DISABLE_CHECK_CACHE",
default=False, cast=bool)
APP_HOST = decouple.config("APP_HOST", default="127.0.0.1")
diff --git a/atr/server.py b/atr/server.py
index 28bb1df..032424e 100644
--- a/atr/server.py
+++ b/atr/server.py
@@ -413,6 +413,40 @@ def _app_setup_request_lifecycle(app: base.QuartApp) ->
None:
async def bind_request_context_vars() -> None:
await _reset_request_log_context()
+ @app.before_request
+ async def validate_session_lifetime() -> None:
+ """Enforce absolute maximum session lifetime per ASVS 7.3.2."""
+ session = await asfquart.session.read()
+ if session is None:
+ return
+
+ conf = config.get()
+ max_lifetime_seconds = conf.ABSOLUTE_SESSION_MAX_SECONDS
+ max_lifetime = datetime.timedelta(seconds=max_lifetime_seconds)
+
+ # Check if session has a creation timestamp in metadata
+ created_at_str = session.metadata.get("created_at")
+
+ if created_at_str is None:
+ # First time seeing this session, record creation time
+ session.metadata["created_at"] =
datetime.datetime.now(datetime.UTC).isoformat()
+ asfquart.session.write(session)
+ return
+
+ # Parse the creation timestamp and check session age
+ try:
+ created_at = datetime.datetime.fromisoformat(created_at_str)
+ except (ValueError, TypeError):
+ # Invalid timestamp, treat as expired
+ asfquart.session.clear()
+ raise base.ASFQuartException("Session expired", errorcode=401)
+
+ session_age = datetime.datetime.now(datetime.UTC) - created_at
+
+ if session_age > max_lifetime:
+ asfquart.session.clear()
+ raise base.ASFQuartException("Session expired", errorcode=401)
+
@app.after_request
async def log_request(response: quart.Response) -> quart.Response:
logger.info(
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]