diff --git a/backend/.sqlx/query-8c56f79cb3f940b29a35f31a48f23ea068d451cf4bfd0c3c9b2f957df82173d1.json b/backend/.sqlx/query-8c56f79cb3f940b29a35f31a48f23ea068d451cf4bfd0c3c9b2f957df82173d1.json new file mode 100644 index 0000000000..5c9eca6f51 --- /dev/null +++ b/backend/.sqlx/query-8c56f79cb3f940b29a35f31a48f23ea068d451cf4bfd0c3c9b2f957df82173d1.json @@ -0,0 +1,22 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT ws.teams_team_id FROM v2_job j\n JOIN workspace_settings ws ON ws.workspace_id = j.workspace_id\n WHERE j.id = $1", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "teams_team_id", + "type_info": "Text" + } + ], + "parameters": { + "Left": [ + "Uuid" + ] + }, + "nullable": [ + true + ] + }, + "hash": "8c56f79cb3f940b29a35f31a48f23ea068d451cf4bfd0c3c9b2f957df82173d1" +} diff --git a/backend/ee-repo-ref.txt b/backend/ee-repo-ref.txt index ec48fc9a62..3b37119381 100644 --- a/backend/ee-repo-ref.txt +++ b/backend/ee-repo-ref.txt @@ -1 +1 @@ -0253ffdd6c0b0f1ba162a43d19aed56bc7bd7a71 +3b6bd2b15d9bddb0bb8f1de08b30b897db0415b4 diff --git a/backend/tests/wm_token_confinement.rs b/backend/tests/wm_token_confinement.rs index e75170ec6f..e15d33354e 100644 --- a/backend/tests/wm_token_confinement.rs +++ b/backend/tests/wm_token_confinement.rs @@ -381,6 +381,22 @@ async fn test_wm_token_is_confined_to_its_workspace(db: Pool) -> anyho "WM_TOKEN must still record its own tutorial progress: {}", resp.text().await? ); + // Every Teams notification a job sends posts here. An empty body is a 422 from the + // handler's extractor, so a 403 means confinement refused it. The real route is only + // mounted in an EE build. + #[cfg(all(feature = "enterprise", feature = "private"))] + { + let resp = authed(client().post(format!("{api}/teams/activities")), &user_wm) + .json(&json!({})) + .send() + .await?; + assert_eq!( + resp.status(), + 422, + "WM_TOKEN must still reach the Teams message route: {}", + resp.text().await? + ); + } // 11. ...and the caller-scoped reads deliberately left out of it, each because it // names another workspace or the identity's credentials. diff --git a/backend/windmill-api-auth/src/scopes.rs b/backend/windmill-api-auth/src/scopes.rs index e6108b1387..1b605e70eb 100644 --- a/backend/windmill-api-auth/src/scopes.rs +++ b/backend/windmill-api-auth/src/scopes.rs @@ -940,11 +940,15 @@ fn is_global_read_open_to_job_token(route_path: &str) -> bool { /// not send. /// - tutorial progress upserts a UI bitfield keyed on the caller's own email. Its path /// serves a `GET` too, which the read list above carries. -const GLOBAL_WRITES_OPEN_TO_JOB_TOKEN: [&str; 4] = [ +/// - a Teams message from a job (the Teams error, recovery and success handlers) posts as +/// the instance-wide bot. The handler refuses a job token whose conversation is not a +/// channel of the team connected to the job's workspace. +const GLOBAL_WRITES_OPEN_TO_JOB_TOKEN: [&str; 5] = [ "/api/settings/test_object_storage_config", "/api/workspaces/exists", "/api/schedules/preview", "/api/users/tutorial_progress", + "/api/teams/activities", ]; /// Confines a job token (`$WM_TOKEN`) to routes that name a workspace. It is minted