From c59cfe33717c19caef913d35181bf3108b7b93f3 Mon Sep 17 00:00:00 2001 From: aaxdev Date: Mon, 31 Aug 2020 19:05:07 +0200 Subject: [PATCH 1/2] Fix MsgPack headers and support mobile SignalR --- src/api/notifications.rs | 72 ++++++++++++++++++++++++---------------- 1 file changed, 43 insertions(+), 29 deletions(-) diff --git a/src/api/notifications.rs b/src/api/notifications.rs index 1bd551ca..9aa33c24 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()) } @@ -148,6 +150,39 @@ 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, token: &mut String) { + let path = hs.request.resource(); + + match hs.request.header("Authorization") { + Some(header_value) => match std::str::from_utf8(header_value) { + Ok(converted) => match converted.split("Bearer ").nth(1) { + Some(token_part) => token.push_str(token_part), + _ => (), + }, + _ => (), + }, + _ => (), + }; + + match token.is_empty() { + true => { + match path.split('?').nth(1) { + Some(params) => { + let params_iter = params.split('&').take(2); + for val in params_iter { + if val.starts_with(ACCESS_TOKEN_KEY) { + token.push_str(&val[ACCESS_TOKEN_KEY.len()..]); + break; + } + } + } + _ => (), + }; + } + false => (), + } + } } impl Handler for WSHandler { @@ -156,35 +191,14 @@ 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 mut access_token = "".into(); + self.get_request_token(hs, &mut access_token); // Validate the user use crate::auth; - let claims = match auth::decode_login(access_token) { + let claims = match auth::decode_login(&mut access_token.as_str()) { Ok(claims) => claims, Err(_) => return self.err("Invalid access token provided"), }; @@ -335,7 +349,7 @@ impl WebSocketUsers { /* Message Structure [ 1, // MessageType.Invocation - {}, // Headers + {}, // Headers (map) null, // InvocationId "ReceiveMessage", // Target [ // Arguments @@ -352,7 +366,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![ From 260ffee093b8c7bea084a338ec8abb4544d0086d Mon Sep 17 00:00:00 2001 From: aaxdev Date: Mon, 31 Aug 2020 22:20:21 +0200 Subject: [PATCH 2/2] Improving code --- src/api/notifications.rs | 60 ++++++++++++++++++---------------------- 1 file changed, 27 insertions(+), 33 deletions(-) diff --git a/src/api/notifications.rs b/src/api/notifications.rs index 9aa33c24..0a01eaa8 100644 --- a/src/api/notifications.rs +++ b/src/api/notifications.rs @@ -21,7 +21,7 @@ static SHOW_WEBSOCKETS_MSG: AtomicBool = AtomicBool::new(true); fn websockets_err() -> EmptyResult { if CONFIG.websocket_enabled() && SHOW_WEBSOCKETS_MSG.compare_and_swap(true, false, Ordering::Relaxed) { 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. ###########################################################################################" @@ -139,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 { @@ -151,37 +150,30 @@ impl WSHandler { Err(ws::Error::new(ws::ErrorKind::Io(io_error), msg)) } - fn get_request_token(&self, hs: Handshake, token: &mut String) { - let path = hs.request.resource(); + fn get_request_token(&self, hs: Handshake) -> Option { + use std::str::from_utf8; - match hs.request.header("Authorization") { - Some(header_value) => match std::str::from_utf8(header_value) { - Ok(converted) => match converted.split("Bearer ").nth(1) { - Some(token_part) => token.push_str(token_part), - _ => (), - }, - _ => (), - }, - _ => (), + // 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()); + } + } }; - - match token.is_empty() { - true => { - match path.split('?').nth(1) { - Some(params) => { - let params_iter = params.split('&').take(2); - for val in params_iter { - if val.starts_with(ACCESS_TOKEN_KEY) { - token.push_str(&val[ACCESS_TOKEN_KEY.len()..]); - break; - } - } - } - _ => (), - }; + + // 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()); + } } - false => (), - } + }; + + None } } @@ -193,12 +185,14 @@ impl Handler for WSHandler { // no longer seem to pass `id` (only `access_token`). // Get user token from header or query parameter - let mut access_token = "".into(); - self.get_request_token(hs, &mut access_token); + 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(&mut access_token.as_str()) { + let claims = match auth::decode_login(access_token.as_str()) { Ok(claims) => claims, Err(_) => return self.err("Invalid access token provided"), };