fix: allow job tokens to read the automate_username_creation setting (#10869)

* fix: let a job token read the automate_username_creation setting

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* test: use an ungated global setting as the confinement control

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
hugocasa
2026-08-27 11:37:37 +02:00
committed by GitHub
co-authored by Claude Opus 5
parent 2302e58c24
commit c2279db8a9
2 changed files with 35 additions and 0 deletions
+27
View File
@@ -318,6 +318,33 @@ async fn test_wm_token_is_confined_to_its_workspace(db: Pool<Postgres>) -> anyho
resp.text().await?
);
}
// ...and the one `settings/global` key on the allowlist, which the CLI reads before
// creating a user on a git-sync push. `ws_base_url` is the control: the handler leaves
// it as ungated as `automate_username_creation`, so only the allowlist stops it.
let resp = authed(
client().get(format!("{api}/settings/global/automate_username_creation")),
&user_wm,
)
.send()
.await?;
assert_eq!(
resp.status(),
200,
"WM_TOKEN must still read automate_username_creation: {}",
resp.text().await?
);
let resp = authed(
client().get(format!("{api}/settings/global/ws_base_url")),
&user_wm,
)
.send()
.await?;
assert_eq!(
resp.status(),
403,
"WM_TOKEN must not read any other global setting: {}",
resp.text().await?
);
let resp = authed(client().post(format!("{api}/schedules/preview")), &user_wm)
.json(&json!({ "schedule": "0 0 12 * * *", "timezone": "UTC" }))
.send()
+8
View File
@@ -996,6 +996,13 @@ fn scope_grants_access(
/// the caller's own row; `email` and `allowed_domain_auto_invite` are derived from the
/// token itself and touch no table.
///
/// `settings/global/automate_username_creation` is the one instance setting on the list.
/// `get_global_setting` exempts a handful of keys from its own super-admin gate, that one
/// among them, so the boolean is already readable by every authenticated user; it is here
/// because the CLI reads it before creating a user during a git-sync push, which runs as a
/// job. The other ungated keys have no such caller, so they stay confined — being ungated
/// earns a key nothing on its own.
///
/// Deliberately absent, as each crosses that line: `users/list_invites` (returns the
/// workspace ids the identity was invited to), `users/tokens/list` (credential metadata
/// of the borrowed identity), `users/exists/{email}` (an oracle over arbitrary
@@ -1011,6 +1018,7 @@ fn is_global_read_open_to_job_token(route_path: &str) -> bool {
| "/api/users/usage"
| "/api/users/tutorial_progress"
| "/api/workspaces/allowed_domain_auto_invite"
| "/api/settings/global/automate_username_creation"
| "/api/docs/search"
| "/api/docs/page"
| "/api/integrations/hub/list"