diff --git a/src/api/notifications.rs b/src/api/notifications.rs index 1bd551ca..0a01eaa8 100644 --- a/src/api/notifications.rs +++ b/src/api/notifications.rs @@ -20,10 +20,12 @@ static SHOW_WEBSOCKETS_MSG: AtomicBool = AtomicBool::new(true); #[get("/hub")] fn websockets_err() -> EmptyResult { if CONFIG.websocket_enabled() && SHOW_WEBSOCKETS_MSG.compare_and_swap(true, false, Ordering::Relaxed) { - err!("########################################################### + err!( + "########################################################### '/notifications/hub' should be proxied to the websocket server or notifications won't work. Go to the Wiki for more info, or disable WebSockets setting WEBSOCKET_ENABLED=false. - ###########################################################################################") + ###########################################################################################" + ) } else { Err(Error::empty()) } @@ -137,7 +139,6 @@ struct InitialMessage { const PING_MS: u64 = 15_000; const PING: Token = Token(1); -const ID_KEY: &str = "id="; const ACCESS_TOKEN_KEY: &str = "access_token="; impl WSHandler { @@ -148,6 +149,32 @@ impl WSHandler { let io_error = io::Error::from(io::ErrorKind::InvalidData); Err(ws::Error::new(ws::ErrorKind::Io(io_error), msg)) } + + fn get_request_token(&self, hs: Handshake) -> Option { + use std::str::from_utf8; + + // Verify we have a token header + if let Some(header_value) = hs.request.header("Authorization") { + if let Ok(converted) = from_utf8(header_value) { + if let Some(token_part) = converted.split("Bearer ").nth(1) { + return Some(token_part.into()); + } + } + }; + + // Otherwise verify the query parameter value + let path = hs.request.resource(); + if let Some(params) = path.split('?').nth(1) { + let params_iter = params.split('&').take(1); + for val in params_iter { + if val.starts_with(ACCESS_TOKEN_KEY) { + return Some(val[ACCESS_TOKEN_KEY.len()..].into()); + } + } + }; + + None + } } impl Handler for WSHandler { @@ -156,35 +183,16 @@ impl Handler for WSHandler { // // We don't use `id`, and as of around 2020-03-25, the official clients // no longer seem to pass `id` (only `access_token`). - let path = hs.request.resource(); - - let (_id, access_token) = match path.split('?').nth(1) { - Some(params) => { - let params_iter = params.split('&').take(2); - - let mut id = None; - let mut access_token = None; - for val in params_iter { - if val.starts_with(ID_KEY) { - id = Some(&val[ID_KEY.len()..]); - } else if val.starts_with(ACCESS_TOKEN_KEY) { - access_token = Some(&val[ACCESS_TOKEN_KEY.len()..]); - } - } - - match (id, access_token) { - (Some(a), Some(b)) => (a, b), - (None, Some(b)) => ("", b), // Ignore missing `id`. - _ => return self.err("Missing access token"), - } - } - None => return self.err("Missing query parameters"), + // Get user token from header or query parameter + let access_token = match self.get_request_token(hs) { + Some(token) => token, + _ => return self.err("Missing access token"), }; // Validate the user use crate::auth; - let claims = match auth::decode_login(access_token) { + let claims = match auth::decode_login(access_token.as_str()) { Ok(claims) => claims, Err(_) => return self.err("Invalid access token provided"), }; @@ -335,7 +343,7 @@ impl WebSocketUsers { /* Message Structure [ 1, // MessageType.Invocation - {}, // Headers + {}, // Headers (map) null, // InvocationId "ReceiveMessage", // Target [ // Arguments @@ -352,7 +360,7 @@ fn create_update(payload: Vec<(Value, Value)>, ut: UpdateType) -> Vec { let value = V::Array(vec![ 1.into(), - V::Array(vec![]), + V::Map(vec![]), V::Nil, "ReceiveMessage".into(), V::Array(vec![V::Map(vec![