sbp commented on code in PR #874: URL: https://github.com/apache/tooling-trusted-releases/pull/874#discussion_r2927142903
########## atr/docs/asfquart-usage.md: ########## @@ -0,0 +1,157 @@ +# 3.16. ASFQuart usage + +**Up**: `3.` [Developer guide](developer-guide) + +**Prev**: `3.15.` [TLS security configuration](tls-security-configuration) + +**Next**: (none) + +**Sections**: + +* [Overview](#overview) +* [Application construction](#application-construction) +* [Session management](#session-management) +* [OAuth authentication](#oauth-authentication) +* [Authentication decorators](#authentication-decorators) +* [Exception handling](#exception-handling) +* [Application secret and session encryption](#application-secret-and-session-encryption) +* [JWT signing key storage](#jwt-signing-key-storage) +* [Application extensions](#application-extensions) +* [What ATR does not use from ASFQuart](#what-atr-does-not-use-from-asfquart) +* [Security considerations for auditors](#security-considerations-for-auditors) + +## Overview + +[ASFQuart](https://github.com/apache/infrastructure-asfquart) is a shared framework library maintained by ASF Infrastructure that provides common functionality for ASF web applications built on [Quart](https://quart.palletsprojects.com/) (an async Python web framework). ATR uses ASFQuart as the foundation of its web application, relying on it for application construction, session management, OAuth integration with the ASF identity provider, and authentication enforcement. + +ASFQuart is not an ATR dependency in the usual sense — it is infrastructure-level code shared across multiple ASF services. ATR does not modify or vendor ASFQuart; it uses the library as published. This document describes what ATR uses from ASFQuart and what security-relevant behaviour is inherited from it. + +## Application construction + +ATR creates its application instance via `asfquart.construct()` in [`server.py`](/ref/atr/server.py). This call produces a `QuartApp` instance (a subclass of `quart.Quart`) with ASF-standard defaults applied: + +```python +app = asfquart.construct(__name__, token_file="secrets/generated/apptoken.txt") +``` + +The `construct()` function performs the following setup that ATR inherits: + +* Creates the Quart application with a persistent secret key (see [Application secret and session encryption](#application-secret-and-session-encryption)) +* Sets secure cookie defaults: `SameSite=Strict`, `Secure=True`, `HttpOnly=True` +* Registers a URL converter for filenames (`FilenameConverter`) +* Sets up the ASF OAuth endpoint at the default `/auth` URI +* Registers an error handler that redirects unauthenticated users to the OAuth login flow + +ATR then overrides the OAuth URLs to use the non-OIDC ASF OAuth endpoints and applies its own configuration via `app.config.from_object()`, taking care to preserve the ASFQuart-generated secret key. + +## Session management + +ATR uses ASFQuart's session module (`asfquart.session`) for all cookie-based user sessions. ASFQuart sessions are server-side Quart sessions keyed by the application ID, which means multiple ASFQuart applications on the same host do not share sessions. Review Comment: I think I know what you mean, but "server-side" is a bit confusing because we don't at all store session state in ATR. We are planning to, but it's not done yet! ########## atr/docs/asfquart-usage.md: ########## @@ -0,0 +1,157 @@ +# 3.16. ASFQuart usage + +**Up**: `3.` [Developer guide](developer-guide) + +**Prev**: `3.15.` [TLS security configuration](tls-security-configuration) + +**Next**: (none) + +**Sections**: + +* [Overview](#overview) +* [Application construction](#application-construction) +* [Session management](#session-management) +* [OAuth authentication](#oauth-authentication) +* [Authentication decorators](#authentication-decorators) +* [Exception handling](#exception-handling) +* [Application secret and session encryption](#application-secret-and-session-encryption) +* [JWT signing key storage](#jwt-signing-key-storage) +* [Application extensions](#application-extensions) +* [What ATR does not use from ASFQuart](#what-atr-does-not-use-from-asfquart) +* [Security considerations for auditors](#security-considerations-for-auditors) + +## Overview + +[ASFQuart](https://github.com/apache/infrastructure-asfquart) is a shared framework library maintained by ASF Infrastructure that provides common functionality for ASF web applications built on [Quart](https://quart.palletsprojects.com/) (an async Python web framework). ATR uses ASFQuart as the foundation of its web application, relying on it for application construction, session management, OAuth integration with the ASF identity provider, and authentication enforcement. + +ASFQuart is not an ATR dependency in the usual sense — it is infrastructure-level code shared across multiple ASF services. ATR does not modify or vendor ASFQuart; it uses the library as published. This document describes what ATR uses from ASFQuart and what security-relevant behaviour is inherited from it. + +## Application construction + +ATR creates its application instance via `asfquart.construct()` in [`server.py`](/ref/atr/server.py). This call produces a `QuartApp` instance (a subclass of `quart.Quart`) with ASF-standard defaults applied: + +```python +app = asfquart.construct(__name__, token_file="secrets/generated/apptoken.txt") +``` + +The `construct()` function performs the following setup that ATR inherits: + +* Creates the Quart application with a persistent secret key (see [Application secret and session encryption](#application-secret-and-session-encryption)) +* Sets secure cookie defaults: `SameSite=Strict`, `Secure=True`, `HttpOnly=True` +* Registers a URL converter for filenames (`FilenameConverter`) +* Sets up the ASF OAuth endpoint at the default `/auth` URI +* Registers an error handler that redirects unauthenticated users to the OAuth login flow + +ATR then overrides the OAuth URLs to use the non-OIDC ASF OAuth endpoints and applies its own configuration via `app.config.from_object()`, taking care to preserve the ASFQuart-generated secret key. + +## Session management + +ATR uses ASFQuart's session module (`asfquart.session`) for all cookie-based user sessions. ASFQuart sessions are server-side Quart sessions keyed by the application ID, which means multiple ASFQuart applications on the same host do not share sessions. + +### Reading sessions + +ATR calls `asfquart.session.read()` throughout the codebase to obtain the current user's session. This function: + +1. Looks up the session cookie by application ID +2. Checks whether the session has expired (default: 7 days of inactivity) +3. Checks whether the session exceeds the maximum lifetime if `MAX_SESSION_AGE` is configured +4. If valid, updates the last-access timestamp and returns a `ClientSession` object +5. If expired or absent, returns `None` + +The returned `ClientSession` object provides: `uid` (ASF user ID), `fullname`, `email`, `committees` (PMC memberships), `projects` (committer memberships), `isMember`, `isChair`, `isRoot`, `mfa`, and a `metadata` dict for application-specific data. Review Comment: Missing a couple here, `self.dn = raw_data.get("dn")` and `self.isRole = raw_data.get("roleaccount", False)`. ########## atr/docs/asfquart-usage.md: ########## @@ -0,0 +1,157 @@ +# 3.16. ASFQuart usage + +**Up**: `3.` [Developer guide](developer-guide) + +**Prev**: `3.15.` [TLS security configuration](tls-security-configuration) + +**Next**: (none) + +**Sections**: + +* [Overview](#overview) +* [Application construction](#application-construction) +* [Session management](#session-management) +* [OAuth authentication](#oauth-authentication) +* [Authentication decorators](#authentication-decorators) +* [Exception handling](#exception-handling) +* [Application secret and session encryption](#application-secret-and-session-encryption) +* [JWT signing key storage](#jwt-signing-key-storage) +* [Application extensions](#application-extensions) +* [What ATR does not use from ASFQuart](#what-atr-does-not-use-from-asfquart) +* [Security considerations for auditors](#security-considerations-for-auditors) + +## Overview + +[ASFQuart](https://github.com/apache/infrastructure-asfquart) is a shared framework library maintained by ASF Infrastructure that provides common functionality for ASF web applications built on [Quart](https://quart.palletsprojects.com/) (an async Python web framework). ATR uses ASFQuart as the foundation of its web application, relying on it for application construction, session management, OAuth integration with the ASF identity provider, and authentication enforcement. + +ASFQuart is not an ATR dependency in the usual sense — it is infrastructure-level code shared across multiple ASF services. ATR does not modify or vendor ASFQuart; it uses the library as published. This document describes what ATR uses from ASFQuart and what security-relevant behaviour is inherited from it. + +## Application construction + +ATR creates its application instance via `asfquart.construct()` in [`server.py`](/ref/atr/server.py). This call produces a `QuartApp` instance (a subclass of `quart.Quart`) with ASF-standard defaults applied: + +```python +app = asfquart.construct(__name__, token_file="secrets/generated/apptoken.txt") +``` + +The `construct()` function performs the following setup that ATR inherits: + +* Creates the Quart application with a persistent secret key (see [Application secret and session encryption](#application-secret-and-session-encryption)) +* Sets secure cookie defaults: `SameSite=Strict`, `Secure=True`, `HttpOnly=True` +* Registers a URL converter for filenames (`FilenameConverter`) +* Sets up the ASF OAuth endpoint at the default `/auth` URI +* Registers an error handler that redirects unauthenticated users to the OAuth login flow + +ATR then overrides the OAuth URLs to use the non-OIDC ASF OAuth endpoints and applies its own configuration via `app.config.from_object()`, taking care to preserve the ASFQuart-generated secret key. + +## Session management + +ATR uses ASFQuart's session module (`asfquart.session`) for all cookie-based user sessions. ASFQuart sessions are server-side Quart sessions keyed by the application ID, which means multiple ASFQuart applications on the same host do not share sessions. + +### Reading sessions + +ATR calls `asfquart.session.read()` throughout the codebase to obtain the current user's session. This function: + +1. Looks up the session cookie by application ID +2. Checks whether the session has expired (default: 7 days of inactivity) +3. Checks whether the session exceeds the maximum lifetime if `MAX_SESSION_AGE` is configured +4. If valid, updates the last-access timestamp and returns a `ClientSession` object +5. If expired or absent, returns `None` + +The returned `ClientSession` object provides: `uid` (ASF user ID), `fullname`, `email`, `committees` (PMC memberships), `projects` (committer memberships), `isMember`, `isChair`, `isRoot`, `mfa`, and a `metadata` dict for application-specific data. + +ATR uses the `metadata` dict to track admin impersonation state (the `admin` key stores the original admin's UID when browsing as another user). + +### Writing and clearing sessions + +ATR calls `asfquart.session.write()` to create sessions after OAuth login and during admin impersonation. The write function stores the session data with creation (`cts`) and last-update (`uts`) timestamps. ATR calls `asfquart.session.clear()` to destroy sessions on logout, session expiry, and when ending admin impersonation. + +### Session expiry + +ASFQuart enforces two expiry checks within `session.read()`: + +* **Inactivity expiry**: sessions unused for longer than `expiry_time` (default 7 days) are deleted +* **Maximum lifetime**: if `MAX_SESSION_AGE` is set in the application config, sessions older than this value are deleted regardless of activity + +ATR additionally enforces its own absolute session maximum via `ABSOLUTE_SESSION_MAX_SECONDS` in a `before_request` hook in [`server.py`](/ref/atr/server.py), which is independent of ASFQuart's expiry. + +## OAuth authentication + +ATR uses ASFQuart's generic OAuth endpoint (`asfquart.generics.setup_oauth`) to handle the ASF OAuth login flow. The endpoint is registered at `/auth` and handles: + +* `/auth?login` — initiates the OAuth flow by generating a cryptographic state token (`secrets.token_hex(16)`) and redirecting to the ASF OAuth service +* `/auth?login=/path` — same as above, but redirects the user to `/path` after successful login +* `/auth?logout` — clears the user's session +* `/auth?state=...&code=...` — callback from the ASF OAuth service that exchanges the authorization code for user data + +ATR overrides the default OAuth URLs to use the non-OIDC endpoints: + +```python +asfquart.generics.OAUTH_URL_INIT = "https://oauth.apache.org/auth?state=%s&redirect_uri=%s" +asfquart.generics.OAUTH_URL_CALLBACK = "https://oauth.apache.org/token?code=%s" +``` + +The OAuth flow has a configurable workflow timeout (default 15 minutes) after which pending state tokens expire. State tokens are consumed immediately on use to prevent replay. The callback enforces HTTPS on the redirect URI and uses a `Refresh` header (instead of a 302 redirect) when `SameSite=Strict` cookies are in use, because a cross-site redirect would cause the browser to discard the session cookie. Review Comment: ``` if redirect_uri: # if called with /auth=login=/foo, redirect to /foo # If SameSite is set, we cannot redirect with a 30x response, as that may invalidate the set-cookie # instead, we issue a 200 Okay with a Refresh header, instructing the browser to immediately go # someplace else. This counts as a samesite request. return quart.Response( status=200, response=f"Successfully logged in! Welcome, {oauth_data['uid']}\n", headers={"Refresh": f"0; url={redirect_uri}"} ) ``` Doesn't seem to check the value of `SameSite`. -- 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] --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
