added sending authSystem to user request

This commit is contained in:
Shiroyasha 2024-05-24 18:44:55 +03:00
parent 59440154c1
commit 2b5258d551
Signed by: shiroyashik
GPG key ID: E4953D3940D7860A
6 changed files with 81 additions and 47 deletions

View file

@ -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
- [ ] Autonomous working without reverse proxy server
- [ ] and many other...

View file

@ -15,7 +15,7 @@ pub fn router() -> Router<AppState> {
}
// Веб функции
// Web
#[derive(Deserialize)]
struct Id {username: String}
@ -25,7 +25,7 @@ async fn id( // First stage of authentication
State(state): State<AppState>,
) -> 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<AppState>,
) -> 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<String>);
@ -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<Option<(Uuid, AuthSystem)>> {
let client = reqwest::Client::new();
tokio::select! {

View file

@ -41,14 +41,43 @@ pub struct Userinfo {
}
#[derive(Debug, Clone)]
struct Authenticated {
user_data: DashMap<String, Userinfo>,
uuid: DashMap<Uuid, String>,
}
impl Authenticated {
fn new() -> Self {
Self { user_data: DashMap::new(), uuid: DashMap::new() }
}
pub fn insert(&self, uuid: Uuid, token: String, userinfo: Userinfo) -> Option<Userinfo> {
self.uuid.insert(uuid, token.clone());
self.user_data.insert(token, userinfo)
}
pub fn get(&self, token: &String) -> Option<dashmap::mapref::one::Ref<'_, std::string::String, Userinfo>> {
self.user_data.get(token)
}
pub fn get_by_uuid(&self, uuid: &Uuid) -> Option<dashmap::mapref::one::Ref<'_, std::string::String, Userinfo>> {
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<Mutex<DashMap<String, String>>>, // <SHA1 serverId, USERNAME>
pending: Arc<DashMap<String, String>>, // <SHA1 serverId, USERNAME>
// Authenticated users
authenticated: Arc<Mutex<DashMap<String, Userinfo>>>, // <SHA1 serverId, Userinfo> NOTE: In the future, try it in a separate LockRw branch
authenticated: Arc<Authenticated>, // <SHA1 serverId, Userinfo> NOTE: In the future, try it in a separate LockRw branch
// Ping broadcasts for WebSocket connections
broadcasts: Arc<Mutex<DashMap<Uuid, broadcast::Sender<Vec<u8>>>>>,
broadcasts: Arc<DashMap<Uuid, broadcast::Sender<Vec<u8>>>>,
// Advanced configured users
advanced_users: Arc<Mutex<toml::Table>>,
}
@ -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)),
};

View file

@ -14,10 +14,15 @@ pub async fn user_info(
) -> Json<Value> {
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<Uuid>,
) -> Result<Vec<u8>> {
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<AppState>,
) -> 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?;

View file

@ -38,7 +38,7 @@ pub fn get_correct_array(value: &toml::Value) -> Vec<u8> {
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()

View file

@ -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;
}
@ -203,8 +200,3 @@ fn into_s2c_ping(buf: Vec<u8>, uuid: Uuid) -> Vec<u8> {
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<tokio::sync::Mutex<DashMap<Uuid, broadcast::Sender<Vec<u8>>>>>, uuid: Uuid) {
let map = broadcasts.lock().await;
map.remove(&uuid);
}