This is an automated email from the ASF dual-hosted git repository.

sbp pushed a commit to branch sbp
in repository https://gitbox.apache.org/repos/asf/tooling-trusted-releases.git

commit 731a2962b5d5a6420064c97fb230732d55233614
Author: Sean B. Palmer <[email protected]>
AuthorDate: Fri Feb 20 19:15:19 2026 +0000

    Check for banned ASF accounts in more places
---
 atr/blueprints/get.py  |  4 ++++
 atr/blueprints/post.py |  4 ++++
 atr/jwtoken.py         | 13 ++++++++++---
 atr/ldap.py            | 13 +++++++++++++
 4 files changed, 31 insertions(+), 3 deletions(-)

diff --git a/atr/blueprints/get.py b/atr/blueprints/get.py
index 396b5a33..f715defd 100644
--- a/atr/blueprints/get.py
+++ b/atr/blueprints/get.py
@@ -25,6 +25,7 @@ import asfquart.base as base
 import asfquart.session
 import quart
 
+import atr.ldap as ldap
 import atr.log as log
 import atr.web as web
 
@@ -39,6 +40,9 @@ def committer(path: str) -> 
Callable[[web.CommitterRouteFunction[Any]], web.Rout
             web_session = await asfquart.session.read()
             if web_session is None:
                 raise base.ASFQuartException("Not authenticated", 
errorcode=401)
+            if (web_session.uid is None) or (not await 
ldap.is_active(web_session.uid)):
+                asfquart.session.clear()
+                raise base.ASFQuartException("Account is disabled", 
errorcode=401)
 
             enhanced_session = web.Committer(web_session)
             start_time_ns = time.perf_counter_ns()
diff --git a/atr/blueprints/post.py b/atr/blueprints/post.py
index e232b29e..8c9133a0 100644
--- a/atr/blueprints/post.py
+++ b/atr/blueprints/post.py
@@ -28,6 +28,7 @@ import pydantic
 import quart
 
 import atr.form
+import atr.ldap as ldap
 import atr.log as log
 import atr.web as web
 
@@ -42,6 +43,9 @@ def committer(path: str) -> 
Callable[[web.CommitterRouteFunction[Any]], web.Rout
             web_session = await asfquart.session.read()
             if web_session is None:
                 raise base.ASFQuartException("Not authenticated", 
errorcode=401)
+            if (web_session.uid is None) or (not await 
ldap.is_active(web_session.uid)):
+                asfquart.session.clear()
+                raise base.ASFQuartException("Account is disabled", 
errorcode=401)
 
             enhanced_session = web.Committer(web_session)
             start_time_ns = time.perf_counter_ns()
diff --git a/atr/jwtoken.py b/atr/jwtoken.py
index 27bc937d..3f1c8dee 100644
--- a/atr/jwtoken.py
+++ b/atr/jwtoken.py
@@ -28,6 +28,7 @@ import jwt
 import quart
 
 import atr.config as config
+import atr.ldap as ldap
 import atr.log as log
 import atr.util as util
 
@@ -68,7 +69,7 @@ def require[**P, R](func: Callable[P, Coroutine[Any, Any, 
R]]) -> Callable[P, Aw
     async def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
         token = _extract_bearer_token(quart.request)
         try:
-            claims = verify(token)
+            claims = await verify(token)
         except jwt.ExpiredSignatureError as exc:
             raise base.ASFQuartException("Token has expired", errorcode=401) 
from exc
         except jwt.InvalidTokenError as exc:
@@ -82,8 +83,8 @@ def require[**P, R](func: Callable[P, Coroutine[Any, Any, 
R]]) -> Callable[P, Aw
     return wrapper
 
 
-def verify(token: str) -> dict[str, Any]:
-    return jwt.decode(
+async def verify(token: str) -> dict[str, Any]:
+    claims = jwt.decode(
         token,
         _JWT_SECRET_KEY,
         algorithms=[_ALGORITHM],
@@ -91,6 +92,12 @@ def verify(token: str) -> dict[str, Any]:
         audience=_ATR_JWT_AUDIENCE,
         options={"require": ["sub", "iss", "aud", "iat", "exp", "jti"]},
     )
+    asf_uid = claims.get("sub")
+    if not isinstance(asf_uid, str):
+        raise jwt.InvalidTokenError("Invalid Bearer JWT subject")
+    if not await ldap.is_active(asf_uid):
+        raise base.ASFQuartException("Account is disabled", errorcode=401)
+    return claims
 
 
 async def verify_github_oidc(token: str) -> dict[str, Any]:
diff --git a/atr/ldap.py b/atr/ldap.py
index 039f6f1d..4a3661d9 100644
--- a/atr/ldap.py
+++ b/atr/ldap.py
@@ -183,6 +183,19 @@ async def github_to_apache(github_numeric_uid: int) -> str:
     return ldap_uid_val[0] if isinstance(ldap_uid_val, list) else ldap_uid_val
 
 
+async def is_active(asf_uid: str) -> bool:
+    import atr.config as config
+
+    if get_bind_credentials() is None:
+        return True
+    if config.get().ALLOW_TESTS and (asf_uid == "test"):
+        return True
+    account = await account_lookup(asf_uid)
+    if account is None:
+        return False
+    return not is_banned(account)
+
+
 def is_banned(account: dict[str, str | list[str]]) -> bool:
     banned_attr = account.get("asf-banned", "no")
     # This is mostly for the type checker, but since asf-banned is missing 
from non-banned accounts,


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to