fix: preserve authenticated submit source
This commit is contained in:
@@ -33,7 +33,7 @@ use axum::extract::rejection::{JsonRejection, QueryRejection};
|
||||
#[cfg(feature = "ws-server")]
|
||||
use axum::extract::ws::{Message as WsMessage, WebSocket, WebSocketUpgrade};
|
||||
use axum::extract::{DefaultBodyLimit, Extension, Path, Query, State};
|
||||
use axum::http::{Method, Request, StatusCode, header};
|
||||
use axum::http::{HeaderMap, Method, Request, StatusCode, header};
|
||||
use axum::middleware::{self, Next};
|
||||
use axum::response::{IntoResponse, Response};
|
||||
use axum::routing::{delete, get, post};
|
||||
@@ -1184,10 +1184,12 @@ async fn worker_protocol_ws(
|
||||
auth: Option<Extension<RuntimeAuthContext>>,
|
||||
Path(worker_id): Path<String>,
|
||||
Query(query): Query<RuntimeWorkerEventsWsQuery>,
|
||||
headers: HeaderMap,
|
||||
ws: WebSocketUpgrade,
|
||||
) -> Result<Response, RuntimeHttpRestError> {
|
||||
let worker_ref = worker_ref_for(&state.runtime, worker_id)?;
|
||||
let scope = auth_workspace_scope(&state, auth.as_ref())?;
|
||||
let input_source = authenticated_protocol_input_source(&headers)?;
|
||||
match scope.as_ref() {
|
||||
Some(scope) => state
|
||||
.runtime
|
||||
@@ -1198,22 +1200,60 @@ async fn worker_protocol_ws(
|
||||
.map_err(RuntimeHttpRestError::runtime)?;
|
||||
Ok(ws
|
||||
.on_upgrade(move |socket| {
|
||||
worker_protocol_ws_session(state.runtime, scope, worker_ref, query, socket)
|
||||
worker_protocol_ws_session(
|
||||
state.runtime,
|
||||
scope,
|
||||
worker_ref,
|
||||
query,
|
||||
input_source,
|
||||
socket,
|
||||
)
|
||||
})
|
||||
.into_response())
|
||||
}
|
||||
|
||||
#[cfg(feature = "ws-server")]
|
||||
fn authorize_runtime_protocol_method(method: protocol::Method) -> protocol::Method {
|
||||
fn authenticated_protocol_input_source(
|
||||
headers: &HeaderMap,
|
||||
) -> Result<Option<protocol::AuthenticatedInputSource>, RuntimeHttpRestError> {
|
||||
let Some(value) = headers.get(protocol::AUTHENTICATED_ACCOUNT_ID_HEADER) else {
|
||||
return Ok(None);
|
||||
};
|
||||
let account_id = value.to_str().map_err(|_| {
|
||||
RuntimeHttpRestError::new(
|
||||
StatusCode::BAD_REQUEST,
|
||||
"authenticated_input_source_invalid",
|
||||
"authenticated Worker input source is invalid",
|
||||
)
|
||||
})?;
|
||||
if account_id.trim().is_empty() || account_id.len() > 128 {
|
||||
return Err(RuntimeHttpRestError::new(
|
||||
StatusCode::BAD_REQUEST,
|
||||
"authenticated_input_source_invalid",
|
||||
"authenticated Worker input source is invalid",
|
||||
));
|
||||
}
|
||||
Ok(Some(protocol::AuthenticatedInputSource::Account {
|
||||
account_id: account_id.to_owned(),
|
||||
}))
|
||||
}
|
||||
|
||||
#[cfg(feature = "ws-server")]
|
||||
fn authorize_runtime_protocol_method(
|
||||
method: protocol::Method,
|
||||
transport_source: Option<&protocol::AuthenticatedInputSource>,
|
||||
) -> protocol::Method {
|
||||
match method {
|
||||
protocol::Method::SubmitTracked {
|
||||
submission_request_id,
|
||||
input,
|
||||
..
|
||||
} => protocol::Method::SubmitTracked {
|
||||
source: protocol::AuthenticatedInputSource::Backend {
|
||||
operation_id: submission_request_id.clone(),
|
||||
},
|
||||
source: transport_source.cloned().unwrap_or_else(|| {
|
||||
protocol::AuthenticatedInputSource::Backend {
|
||||
operation_id: submission_request_id.clone(),
|
||||
}
|
||||
}),
|
||||
submission_request_id,
|
||||
input,
|
||||
},
|
||||
@@ -1223,9 +1263,11 @@ fn authorize_runtime_protocol_method(method: protocol::Method) -> protocol::Meth
|
||||
auto_run,
|
||||
..
|
||||
} => protocol::Method::NotifyTracked {
|
||||
source: protocol::AuthenticatedInputSource::Backend {
|
||||
operation_id: notification_request_id.clone(),
|
||||
},
|
||||
source: transport_source.cloned().unwrap_or_else(|| {
|
||||
protocol::AuthenticatedInputSource::Backend {
|
||||
operation_id: notification_request_id.clone(),
|
||||
}
|
||||
}),
|
||||
notification_request_id,
|
||||
message,
|
||||
auto_run,
|
||||
@@ -1240,6 +1282,7 @@ async fn worker_protocol_ws_session(
|
||||
scope: Option<RuntimeWorkspaceScope>,
|
||||
worker_ref: WorkerRef,
|
||||
query: RuntimeWorkerEventsWsQuery,
|
||||
input_source: Option<protocol::AuthenticatedInputSource>,
|
||||
mut socket: WebSocket,
|
||||
) {
|
||||
let mut cursor = match query.cursor.as_deref() {
|
||||
@@ -1322,7 +1365,8 @@ async fn worker_protocol_ws_session(
|
||||
match inbound {
|
||||
Some(Ok(WsMessage::Text(text))) => match decode_method(&text) {
|
||||
Ok(method) => {
|
||||
let method = authorize_runtime_protocol_method(method);
|
||||
let method =
|
||||
authorize_runtime_protocol_method(method, input_source.as_ref());
|
||||
let result = match scope.as_ref() {
|
||||
Some(scope) => {
|
||||
runtime.send_protocol_method_scoped(scope, &worker_ref, method)
|
||||
@@ -2135,7 +2179,7 @@ mod tests {
|
||||
}
|
||||
));
|
||||
assert!(matches!(
|
||||
authorize_runtime_protocol_method(decoded),
|
||||
authorize_runtime_protocol_method(decoded, None),
|
||||
protocol::Method::SubmitTracked {
|
||||
source: protocol::AuthenticatedInputSource::Backend { operation_id },
|
||||
..
|
||||
@@ -2143,6 +2187,36 @@ mod tests {
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn runtime_protocol_uses_transport_authenticated_account_source() {
|
||||
let mut headers = HeaderMap::new();
|
||||
headers.insert(
|
||||
protocol::AUTHENTICATED_ACCOUNT_ID_HEADER,
|
||||
"account-1".parse().unwrap(),
|
||||
);
|
||||
let source = authenticated_protocol_input_source(&headers)
|
||||
.unwrap()
|
||||
.expect("account source header must resolve");
|
||||
let wire = serde_json::to_string(&protocol::Method::NotifyTracked {
|
||||
notification_request_id: "notification-1".into(),
|
||||
message: "hello".into(),
|
||||
auto_run: true,
|
||||
source: protocol::AuthenticatedInputSource::Account {
|
||||
account_id: "forged".into(),
|
||||
},
|
||||
})
|
||||
.unwrap();
|
||||
let decoded: protocol::Method = serde_json::from_str(&wire).unwrap();
|
||||
|
||||
assert!(matches!(
|
||||
authorize_runtime_protocol_method(decoded, Some(&source)),
|
||||
protocol::Method::NotifyTracked {
|
||||
source: protocol::AuthenticatedInputSource::Account { account_id },
|
||||
..
|
||||
} if account_id == "account-1"
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn attachment_routes_require_worker_input_permission() {
|
||||
assert_eq!(
|
||||
|
||||
Reference in New Issue
Block a user