From 2b5258d551500caf71b32ca4196bb5d1e36736d9 Mon Sep 17 00:00:00 2001 From: shiroyashik Date: Fri, 24 May 2024 18:44:55 +0300 Subject: [PATCH] added sending authSystem to user request --- README.md | 4 ++-- src/auth.rs | 27 ++++++++++++++++++--------- src/main.rs | 41 +++++++++++++++++++++++++++++++++++------ src/profile.rs | 22 +++++++++++++--------- src/utils.rs | 2 +- src/ws/handler.rs | 32 ++++++++++++-------------------- 6 files changed, 81 insertions(+), 47 deletions(-) diff --git a/README.md b/README.md index a22935f..4f072a1 100644 --- a/README.md +++ b/README.md @@ -9,5 +9,5 @@ And also a distinctive feature is the possibility of player identification throu ### TODO: - [ ] Realization of storing profiles in the database - [ ] Frontend for moderation -- [ ] Check the possibility of replacing assets to display the authorization system used by the player -- [ ] and many other... +- [ ] Autonomous working without reverse proxy server +- [ ] and many other... \ No newline at end of file diff --git a/src/auth.rs b/src/auth.rs index a335cc7..604b8db 100644 --- a/src/auth.rs +++ b/src/auth.rs @@ -15,7 +15,7 @@ pub fn router() -> Router { } -// Веб функции +// Web #[derive(Deserialize)] struct Id {username: String} @@ -25,7 +25,7 @@ async fn id( // First stage of authentication State(state): State, ) -> String { let server_id = bytes_into_string(&digest(&digest::SHA1_FOR_LEGACY_USE_ONLY, &rand()).as_ref()[0 .. 20]); - let state = state.pending.lock().await; + let state = state.pending; state.insert(server_id.clone(), query.username); server_id } @@ -39,12 +39,12 @@ async fn verify( // Second stage of authentication State(state): State, ) -> String { let server_id = query.id.clone(); - let username = state.pending.lock().await.remove(&server_id).unwrap().1; + let username = state.pending.remove(&server_id).unwrap().1; if let Some((uuid, auth_system)) = has_joined(&server_id, &username).await.unwrap() { info!("[Authorization] {username} logged in using {auth_system:?}"); - let authenticated = state.authenticated.lock().await; + let authenticated = state.authenticated; // let link = state.authenticated_link.lock().await; // // Реализация поиска пользователя в HashMap по UUID - authenticated.insert(server_id.clone(), crate::Userinfo { username, uuid, auth_system }); + authenticated.insert(uuid, server_id.clone(), crate::Userinfo { username, uuid, auth_system }); // link.insert(uuid, crate::AuthenticatedLink(server_id.clone())); // Реализация поиска пользователя в HashMap по UUID return format!("{server_id}") } else { @@ -58,7 +58,7 @@ pub async fn status( ) -> Response { match token { Some(token) => { - if state.authenticated.lock().await.contains_key(&token) { + if state.authenticated.contains_token(&token) { // format!("ok") // 200 (StatusCode::OK, format!("ok")).into_response() } else { @@ -72,10 +72,10 @@ pub async fn status( }, } } -// Конец веб функций +// Web End -// Это экстрактор достающий из Заголовка зовущегося токен, соответственно ТОКЕН. +// It's an extractor that pulls a token from the Header. #[derive(PartialEq, Debug)] pub struct Token(pub Option); @@ -98,7 +98,7 @@ where } } } -// Конец экстрактора +// End Extractor // Work with external APIs @@ -108,6 +108,15 @@ pub enum AuthSystem { Mojang, } +impl ToString for AuthSystem { + fn to_string(&self) -> String { + match self { + AuthSystem::ElyBy => String::from("elyby"), + AuthSystem::Mojang => String::from("mojang"), + } + } +} + pub async fn has_joined(server_id: &str, username: &str) -> anyhow::Result> { let client = reqwest::Client::new(); tokio::select! { diff --git a/src/main.rs b/src/main.rs index 245c9ad..7e009db 100644 --- a/src/main.rs +++ b/src/main.rs @@ -41,14 +41,43 @@ pub struct Userinfo { } +#[derive(Debug, Clone)] +struct Authenticated { + user_data: DashMap, + uuid: DashMap, +} + +impl Authenticated { + fn new() -> Self { + Self { user_data: DashMap::new(), uuid: DashMap::new() } + } + pub fn insert(&self, uuid: Uuid, token: String, userinfo: Userinfo) -> Option { + self.uuid.insert(uuid, token.clone()); + self.user_data.insert(token, userinfo) + } + pub fn get(&self, token: &String) -> Option> { + self.user_data.get(token) + } + pub fn get_by_uuid(&self, uuid: &Uuid) -> Option> { + if let Some(token) = self.uuid.get(uuid) { + self.user_data.get(&token.to_string()) + } else { + None + } + } + pub fn contains_token(&self, token: &String) -> bool { + self.user_data.contains_key(token) + } +} + #[derive(Debug, Clone)] pub struct AppState { // Users with incomplete authentication - pending: Arc>>, // + pending: Arc>, // // Authenticated users - authenticated: Arc>>, // NOTE: In the future, try it in a separate LockRw branch + authenticated: Arc, // NOTE: In the future, try it in a separate LockRw branch // Ping broadcasts for WebSocket connections - broadcasts: Arc>>>>, + broadcasts: Arc>>>, // Advanced configured users advanced_users: Arc>, } @@ -83,9 +112,9 @@ async fn main() -> Result<()> { // State let state = AppState { - pending: Arc::new(Mutex::new(DashMap::new())), - authenticated: Arc::new(Mutex::new(DashMap::new())), - broadcasts: Arc::new(Mutex::new(DashMap::new())), + pending: Arc::new(DashMap::new()), + authenticated: Arc::new(Authenticated::new()), + broadcasts: Arc::new(DashMap::new()), advanced_users: Arc::new(Mutex::new(config.advanced_users)), }; diff --git a/src/profile.rs b/src/profile.rs index 3a54406..96eda99 100644 --- a/src/profile.rs +++ b/src/profile.rs @@ -14,10 +14,15 @@ pub async fn user_info( ) -> Json { log::info!("Receiving profile information for {}",uuid); - let formatted_uuid = format_uuid(uuid); + let formatted_uuid = format_uuid(&uuid); let avatar_file = format!("avatars/{}.moon", formatted_uuid); + let auth_system = match state.authenticated.get_by_uuid(&uuid) { + Some(d) => d.auth_system.to_string(), + None => return Json(json!("err")), + }; + let mut user_info_response = json!({ "uuid": &formatted_uuid, "rank": "default", @@ -28,7 +33,8 @@ pub async fn user_info( "pride": [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] }, "version": "0.1.4+1.20.1", - "banned": false + "banned": false, + "authSystem": auth_system // add Trust }); if let Some(settings) = state.advanced_users.lock().await.get(&formatted_uuid) { @@ -64,7 +70,7 @@ pub async fn user_info( pub async fn download_avatar( Path(uuid): Path, ) -> Result> { - let uuid = format_uuid(uuid); + let uuid = format_uuid(&uuid); log::info!("Requesting an avatar: {}", uuid); let mut file = if let Ok(file) = fs::File::open(format!("avatars/{}.moon", uuid)).await { file @@ -94,9 +100,8 @@ pub async fn upload_avatar( Some(t) => t, None => http_error_ret!(UNAUTHORIZED, "Authentication error!"), }; - let userinfos = state.authenticated.lock().await; - if let Some(user_info) = userinfos.get(token.as_str()) { + if let Some(user_info) = state.authenticated.get(&token) { log::info!("{} ({}) trying to upload an avatar",user_info.uuid,user_info.username); let avatar_file = format!("avatars/{}.moon",user_info.uuid); let mut file = BufWriter::new(fs::File::create(&avatar_file).await?); @@ -110,8 +115,8 @@ pub async fn equip_avatar( State(state): State, ) -> String { debug!("[API] S2C : Equip"); - let uuid = state.authenticated.lock().await.get(&token.unwrap()).unwrap().uuid; - if state.broadcasts.lock().await.get(&uuid).unwrap().send(S2CMessage::Event(uuid).to_vec()).is_err() { + let uuid = state.authenticated.get(&token.unwrap()).unwrap().uuid; + if state.broadcasts.get(&uuid).unwrap().send(S2CMessage::Event(uuid).to_vec()).is_err() { warn!("[WebSocket] Failed to send Event! Maybe there is no one to send") // FIXME: Засунуть в Handler }; format!("ok") @@ -125,8 +130,7 @@ pub async fn delete_avatar( Some(t) => t, None => http_error_ret!(UNAUTHORIZED, "Authentication error!"), }; - let userinfos = state.authenticated.lock().await; - if let Some(user_info) = userinfos.get(token.as_str()) { + if let Some(user_info) = state.authenticated.get(&token) { log::info!("{} ({}) is trying to delete the avatar",user_info.uuid,user_info.username); let avatar_file = format!("avatars/{}.moon",user_info.uuid); fs::remove_file(avatar_file).await?; diff --git a/src/utils.rs b/src/utils.rs index 52e09d7..a75fd26 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -38,7 +38,7 @@ pub fn get_correct_array(value: &toml::Value) -> Vec { value.as_array().unwrap().iter().map(move |x| x.as_integer().unwrap() as u8).collect() } -pub fn format_uuid(uuid: Uuid) -> String { +pub fn format_uuid(uuid: &Uuid) -> String { // let uuid = Uuid::parse_str(&uuid)?; TODO: Вероятно format_uuid стоит убрать // .map_err(|_| tide::Error::from_str(StatusCode::InternalServerError, "Failed to parse UUID"))?; uuid.as_hyphenated().to_string() diff --git a/src/ws/handler.rs b/src/ws/handler.rs index c5a32ac..97186c6 100644 --- a/src/ws/handler.rs +++ b/src/ws/handler.rs @@ -49,7 +49,7 @@ async fn handle_socket(mut socket: WebSocket, state: AppState) { if let Message::Close(_) = msg { info!("[WebSocket{}] Connection successfully closed!", owner.name()); if let Some(u) = owner.0 { - remove_broadcast(state.broadcasts.clone(), u.uuid).await; + state.broadcasts.remove(&u.uuid); } return; } @@ -57,7 +57,7 @@ async fn handle_socket(mut socket: WebSocket, state: AppState) { } else { warn!("[WebSocket{}] Receive error! Connection terminated!", owner.name()); if let Some(u) = owner.0 { - remove_broadcast(state.broadcasts.clone(), u.uuid).await; + state.broadcasts.remove(&u.uuid); } return; }; @@ -70,7 +70,7 @@ async fn handle_socket(mut socket: WebSocket, state: AppState) { Err(e) => { error!("[WebSocket{}] This message is not from Figura! {e:?}", owner.name()); if let Some(u) = owner.0 { - remove_broadcast(state.broadcasts.clone(), u.uuid).await; + state.broadcasts.remove(&u.uuid); } return; }, @@ -82,20 +82,18 @@ async fn handle_socket(mut socket: WebSocket, state: AppState) { C2SMessage::Token(token) => { // FIXME: Написать переменную спомощью которой бужет проверяться авторизовался ли пользователь или нет debug!("[WebSocket{}] C2S : Token", owner.name()); let token = String::from_utf8(token.to_vec()).unwrap(); - let authenticated = state.authenticated.lock().await; - match authenticated.get(&token) { // Принцип прост: если токена в authenticated нет, значит это trash + match state.authenticated.get(&token) { // Принцип прост: если токена в authenticated нет, значит это trash Some(t) => { //username = t.username.clone(); owner.0 = Some(WSUser { username: t.username.clone(), token, uuid: t.uuid }); msg = Message::Binary(S2CMessage::Auth.to_vec()); - let bcs = state.broadcasts.lock().await; - match bcs.get(&t.uuid) { + match state.broadcasts.get(&t.uuid) { Some(tx) => { bctx = Some(tx.to_owned()); }, None => { let (tx, _rx) = broadcast::channel(64); - bcs.insert(t.uuid, tx.clone()); + state.broadcasts.insert(t.uuid, tx.clone()); bctx = Some(tx.to_owned()); }, }; @@ -104,7 +102,7 @@ async fn handle_socket(mut socket: WebSocket, state: AppState) { warn!("[WebSocket] Authenticaton error! Connection terminated!"); debug!("[WebSocket] Tried to log in with {token}"); // Tried to log in with token: {token} if let Some(u) = owner.0 { - remove_broadcast(state.broadcasts.clone(), u.uuid).await; + state.broadcasts.remove(&u.uuid); } return; // TODO: Прописать код отключения }, @@ -115,7 +113,7 @@ async fn handle_socket(mut socket: WebSocket, state: AppState) { let data = into_s2c_ping(msg_vec, owner.0.clone().unwrap().uuid); match bctx.clone().unwrap().send(data) { Ok(_) => (), - Err(_) => warn!("[WebSocket{}] Failed to send Ping! Maybe there's no one to send", owner.name()), + Err(_) => debug!("[WebSocket{}] Failed to send Ping! Maybe there's no one to send", owner.name()), }; continue; }, @@ -126,13 +124,12 @@ async fn handle_socket(mut socket: WebSocket, state: AppState) { continue; }; - let broadcast = state.broadcasts.lock().await; - let rx = match broadcast.get(&uuid) { + let rx = match state.broadcasts.get(&uuid) { Some(rx) => rx.to_owned().subscribe(), None => { warn!("[WebSocket{}] Attention! The required UUID for subscription was not found!", owner.name()); let (tx, rx) = broadcast::channel(64); - broadcast.insert(uuid, tx); + state.broadcasts.insert(uuid, tx); rx }, }; @@ -159,7 +156,7 @@ async fn handle_socket(mut socket: WebSocket, state: AppState) { if socket.send(msg).await.is_err() { warn!("[WebSocket{}] Send error! Connection terminated!", owner.name()); if let Some(u) = owner.0 { - remove_broadcast(state.broadcasts.clone(), u.uuid).await; + state.broadcasts.remove(&u.uuid); } return; } @@ -172,7 +169,7 @@ async fn handle_socket(mut socket: WebSocket, state: AppState) { Err(_) => { warn!("[WebSocketSubscriber{}] Send error! Connection terminated!", owner.name()); if let Some(u) = owner.0 { - remove_broadcast(state.broadcasts.clone(), u.uuid).await; + state.broadcasts.remove(&u.uuid); } return; } @@ -202,9 +199,4 @@ async fn subscribe(socket: mpsc::Sender>, mut rx: Receiver>, shu fn into_s2c_ping(buf: Vec, uuid: Uuid) -> Vec { use std::iter::once; once(1).chain(uuid.into_bytes().iter().copied()).chain(buf.as_slice()[1..].iter().copied()).collect() -} - -async fn remove_broadcast(broadcasts: Arc>>>>, uuid: Uuid) { - let map = broadcasts.lock().await; - map.remove(&uuid); } \ No newline at end of file