Configuration auto-update fixed and hex replaced with faster_hex

This commit is contained in:
Shiroyasha 2024-10-30 21:21:37 +03:00
parent 4c0871e26c
commit 2f8ab4cfa8
Signed by: shiroyashik
GPG key ID: E4953D3940D7860A
14 changed files with 589 additions and 210 deletions

View file

@ -71,7 +71,7 @@ async fn index_assets(version: &str) -> anyhow::Result<IndexMap<String, Value>>
entry.path().strip_prefix(version_path.clone())?.to_string_lossy().to_string()
};
map.insert(path, Value::from(hex::encode(digest(&SHA256, &data).as_ref())));
map.insert(path, Value::from(faster_hex::hex_string(digest(&SHA256, &data).as_ref())));
}
Ok(map)

View file

@ -19,7 +19,7 @@ async fn id(
State(state): State<AppState>,
) -> String {
let server_id =
hex::encode(&digest(&digest::SHA1_FOR_LEGACY_USE_ONLY, &rand()).as_ref()[0..20]);
faster_hex::hex_string(&digest(&digest::SHA1_FOR_LEGACY_USE_ONLY, &rand()).as_ref()[0..20]);
let state = state.user_manager;
state.pending_insert(server_id.clone(), query.username);
server_id
@ -32,11 +32,11 @@ async fn verify(
State(state): State<AppState>,
) -> Response {
let server_id = query.id.clone();
let username = state.user_manager.pending_remove(&server_id).unwrap().1; // TODO: Add error check
let nickname = state.user_manager.pending_remove(&server_id).unwrap().1; // TODO: Add error check
let userinfo = match has_joined(
state.config.read().await.auth_providers.clone(),
&server_id,
&username
&nickname
).await {
Ok(d) => d,
Err(_e) => {
@ -47,12 +47,12 @@ async fn verify(
if let Some((uuid, auth_provider)) = userinfo {
let umanager = state.user_manager;
if umanager.is_banned(&uuid) {
info!("[Authentication] {username} tried to log in, but was banned");
info!("[Authentication] {nickname} tried to log in, but was banned");
return (StatusCode::BAD_REQUEST, "You're banned!".to_string()).into_response();
}
info!("[Authentication] {username} logged in using {}", auth_provider.name);
info!("[Authentication] {nickname} logged in using {}", auth_provider.name);
let userinfo = Userinfo {
username,
nickname,
uuid,
token: Some(server_id.clone()),
auth_provider,
@ -70,7 +70,7 @@ async fn verify(
}
(StatusCode::OK, server_id.to_string()).into_response()
} else {
info!("[Authentication] failed to verify {username}");
info!("[Authentication] failed to verify {nickname}");
(StatusCode::BAD_REQUEST, "failed to verify".to_string()).into_response()
}
}

View file

@ -100,7 +100,7 @@ pub async fn upload_avatar(
tracing::info!(
"{} ({}) trying to upload an avatar",
user_info.uuid,
user_info.username
user_info.nickname
);
let avatar_file = format!("{}/{}.moon", *AVATARS_VAR, user_info.uuid);
let mut file = BufWriter::new(fs::File::create(&avatar_file).await.map_err(internal_and_log)?);
@ -121,7 +121,7 @@ pub async fn delete_avatar(Token(token): Token, State(state): State<AppState>) -
tracing::info!(
"{} ({}) is trying to delete the avatar",
user_info.uuid,
user_info.username
user_info.nickname
);
let avatar_file = format!("{}/{}.moon", *AVATARS_VAR, user_info.uuid);
fs::remove_file(avatar_file).await.map_err(internal_and_log)?;

View file

@ -65,7 +65,7 @@ async fn handle_socket(mut ws: WebSocket, state: AppState) {
}
async fn main_worker(session: &mut WSSession, ws: &mut WebSocket, state: &AppState) -> anyhow::Result<()> {
tracing::debug!("WebSocket control for {} is transferred to the main worker", session.user.username);
tracing::debug!("WebSocket control for {} is transferred to the main worker", session.user.nickname);
loop {
tokio::select! {
external_msg = ws.recv_and_decode() => {
@ -96,7 +96,7 @@ async fn main_worker(session: &mut WSSession, ws: &mut WebSocket, state: &AppSta
let _ = session.subs_tx.send(s2c_ping);
},
C2SMessage::Sub(uuid) => {
tracing::debug!("[WebSocket] {} subscribes to {}", session.user.username, uuid);
tracing::debug!("[WebSocket] {} subscribes to {}", session.user.nickname, uuid);
// Doesn't allow to subscribe to yourself
if session.user.uuid != uuid {
@ -114,11 +114,11 @@ async fn main_worker(session: &mut WSSession, ws: &mut WebSocket, state: &AppSta
}
},
C2SMessage::Unsub(uuid) => {
tracing::debug!("[WebSocket] {} unsubscribes from {}", session.user.username, uuid);
tracing::debug!("[WebSocket] {} unsubscribes from {}", session.user.nickname, uuid);
match session.sub_workers_aborthandles.get(&uuid) {
Some(handle) => handle.abort(),
None => tracing::warn!("[WebSocket] {} was not subscribed.", session.user.username),
None => tracing::warn!("[WebSocket] {} was not subscribed.", session.user.nickname),
};
},
}
@ -134,7 +134,7 @@ async fn main_worker(session: &mut WSSession, ws: &mut WebSocket, state: &AppSta
.inspect_err(
|kind| tracing::warn!("[WebSocket] Didn't get the ban message due to {}", kind)
);
bail!("{} banned!", session.user.username)
bail!("{} banned!", session.user.nickname)
},
}
}
@ -178,7 +178,7 @@ async fn authenticate(socket: &mut WebSocket, state: &AppState) -> Result<Userin
.inspect_err(
|kind| tracing::warn!("[WebSocket] Didn't get the ban message due to {}", kind)
);
Err(AuthModeError::Banned(user.username.clone()))
Err(AuthModeError::Banned(user.nickname.clone()))
}
},
None => {

View file

@ -17,7 +17,7 @@ impl RecvAndDecode for WebSocket {
match C2SMessage::try_from(msg.clone().into_data().as_slice()) {
Ok(decoded) => Ok(decoded),
Err(e) => {
Err(RADError::DecodeError(e, hex::encode(msg.into_data())))
Err(RADError::DecodeError(e, faster_hex::hex_string(&msg.into_data())))
},
}
}

View file

@ -21,7 +21,8 @@ pub(super) async fn raw(
) -> ApiResult<&'static str> {
trace!(body = body);
state.config.read().await.clone().verify_token(&token)?;
let payload = hex::decode(body).map_err(|err| { warn!("not raw data"); error_and_log(err, crate::ApiError::NotAcceptable) })?;
let mut payload = vec![0; body.len() / 2];
faster_hex::hex_decode(body.as_bytes(), &mut payload).map_err(|err| { warn!("not raw data"); error_and_log(err, crate::ApiError::NotAcceptable) })?;
debug!("{:?}", payload);
match query.uuid {
@ -47,7 +48,8 @@ pub(super) async fn sub_raw(
) -> ApiResult<&'static str> {
trace!(body = body);
state.config.read().await.clone().verify_token(&token)?;
let payload = hex::decode(body).map_err(|err| { warn!("not raw data"); error_and_log(err, crate::ApiError::NotAcceptable) })?;
let mut payload = vec![0; body.len() / 2];
faster_hex::hex_decode(body.as_bytes(), &mut payload).map_err(|err| { warn!("not raw data"); error_and_log(err, crate::ApiError::NotAcceptable) })?;
debug!("{:?}", payload);
match query.uuid {

View file

@ -19,8 +19,12 @@ pub struct Token(pub String);
impl Token {
pub async fn check_auth(self, state: &AppState) -> ApiResult<()> {
if state.user_manager.is_authenticated(&self.0) {
Ok(())
if let Some(user) = state.user_manager.get(&self.0) {
if !user.banned {
Ok(())
} else {
Err(ApiError::Unauthorized)
}
} else {
Err(ApiError::Unauthorized)
}
@ -191,7 +195,7 @@ impl UManager {
warn!("Rejected attempt to create a second session for the same user!");
return Err(())
}
debug!("`{}` already have token in registered profile (old token already removed from 'authenticated')", userinfo.username);
debug!("`{}` already have token in registered profile (old token already removed from 'authenticated')", userinfo.nickname);
}
}
@ -205,7 +209,7 @@ impl UManager {
let usercopy = userinfo.clone();
self.registered.entry(uuid)
.and_modify(|exist| {
if !userinfo.username.is_empty() { exist.username = userinfo.username };
if !userinfo.nickname.is_empty() { exist.nickname = userinfo.nickname };
if !userinfo.auth_provider.is_empty() { exist.auth_provider = userinfo.auth_provider };
if userinfo.rank != Userinfo::default().rank { exist.rank = userinfo.rank };
if userinfo.token.is_some() { exist.token = userinfo.token };
@ -236,7 +240,7 @@ impl UManager {
user.banned = false;
};
}
pub fn is_authenticated(&self, token: &String) -> bool {
pub fn _is_authenticated(&self, token: &String) -> bool {
self.authenticated.contains_key(token)
}
pub fn _is_registered(&self, uuid: &Uuid) -> bool {

View file

@ -6,21 +6,20 @@ use uuid::Uuid;
#[serde(rename_all = "camelCase")]
pub struct Userinfo {
pub uuid: Uuid,
pub username: String,
pub nickname: String,
pub rank: String,
pub last_used: String,
pub auth_provider: AuthProvider,
pub token: Option<String>,
pub version: String,
pub banned: bool
}
impl Default for Userinfo {
fn default() -> Self {
Self {
uuid: Default::default(),
username: Default::default(),
nickname: Default::default(),
rank: "default".to_string(),
last_used: Utc::now().to_rfc3339_opts(chrono::SecondsFormat::Millis, true),
auth_provider: Default::default(),

View file

@ -93,7 +93,6 @@ async fn main() -> Result<()> {
// 3. Display info about current instance and check updates
tracing::info!("The Sculptor v{SCULPTOR_VERSION} ({REPOSITORY})");
// let _ = check_updates(REPOSITORY, SCULPTOR_VERSION).await; // Currently, there is no need to do anything with the result of the function
match get_latest_version(REPOSITORY).await {
Ok(latest_version) => {
@ -168,25 +167,13 @@ async fn app() -> Result<bool> {
config,
};
// FIXME: FIXME: FIXME: ПЕРЕДЕЛАЙ ЭТО! НЕМЕДЛЕННО! ЕБУЧИЙ ПОЗОР :<
// Automatic update of configuration while the server is running
let config_update = Arc::clone(&state.config);
let umanager = Arc::clone(&state.user_manager);
let session = Arc::clone(&state.session);
update_advanced_users(&config_update.read().await.advanced_users.clone(), &umanager, &session).await;
tokio::spawn(async move {
loop {
tokio::time::sleep(std::time::Duration::from_secs(10)).await;
let new_config = Config::parse(CONFIG_VAR.clone().into());
let mut config = config_update.write().await;
if new_config != *config {
tracing::info!("Server configuration modification detected!");
*config = new_config;
update_advanced_users(&config.advanced_users.clone(), &umanager, &session).await;
}
}
});
// Automatic update of configuration/ban list while the server is running
tokio::spawn(update_advanced_users(
CONFIG_VAR.clone().into(),
Arc::clone(&state.user_manager),
Arc::clone(&state.session),
Arc::clone(&state.config)
));
if state.config.read().await.mc_folder.exists() {
tokio::spawn(update_bans_from_minecraft(
state.config.read().await.mc_folder.clone(),
@ -242,11 +229,9 @@ async fn shutdown_signal() {
let terminate = std::future::pending::<()>();
tokio::select! {
() = ctrl_c => {
println!();
tracing::info!("Ctrl+C signal received");
},
() = terminate => {
println!();
tracing::info!("Terminate signal received");
},
}

View file

@ -66,7 +66,7 @@ impl From<BannedPlayer> for Userinfo {
fn from(val: BannedPlayer) -> Self {
Userinfo {
uuid: val.uuid,
username: val.name,
nickname: val.name,
banned: true,
..Default::default()
}

View file

@ -1,16 +1,15 @@
use std::{fs::File, io::Read, path::{Path, PathBuf}};
use std::{fs::File, io::Read, path::{Path, PathBuf}, sync::Arc};
use notify::{Event, Watcher};
use tokio::{io::AsyncReadExt, sync::RwLock};
use base64::prelude::*;
use rand::{distributions::Alphanumeric, thread_rng, Rng};
use rand::{thread_rng, Rng};
use ring::digest::{self, digest};
use tokio::io::AsyncReadExt;
use tracing::{error, info};
use uuid::Uuid;
use chrono::prelude::*;
use crate::{auth::{UManager, Userinfo}, state::{AdvancedUsers, BannedPlayer}};
use crate::{auth::Userinfo, state::{BannedPlayer, Config}, UManager};
// Core functions
pub fn rand() -> [u8; 50] {
let mut rng = thread_rng();
let distr = rand::distributions::Uniform::new_inclusive(0, 255);
@ -20,48 +19,63 @@ pub fn rand() -> [u8; 50] {
}
nums
}
// End of Core functions
pub fn _generate_hex_string(length: usize) -> String {
// FIXME: Variable doesn't using!
let rng = thread_rng();
let random_bytes: Vec<u8> = rng.sample_iter(&Alphanumeric).take(length / 2).collect();
hex::encode(random_bytes)
}
pub async fn update_advanced_users(
value: &std::collections::HashMap<Uuid, AdvancedUsers>,
umanager: &UManager,
sessions: &dashmap::DashMap<Uuid, tokio::sync::mpsc::Sender<crate::api::figura::SessionMessage>>
path: PathBuf,
umanager: Arc<UManager>,
sessions: Arc<dashmap::DashMap<Uuid, tokio::sync::mpsc::Sender<crate::api::figura::SessionMessage>>>,
config: Arc<RwLock<Config>>,
) {
let users: Vec<(Uuid, Userinfo)> = value
.iter()
.map( |(uuid, userdata)| {
(
*uuid,
Userinfo {
uuid: *uuid,
username: userdata.username.clone(),
banned: userdata.banned,
..Default::default()
}
)})
.collect();
let (tx, mut rx) = tokio::sync::mpsc::channel::<notify::Result<Event>>(1);
tx.send(Ok(notify::Event::default())).await.unwrap();
let mut watcher = notify::RecommendedWatcher::new(
move |res| {
tx.blocking_send(res).unwrap();
},
notify::Config::default(),
).unwrap();
watcher.watch(&path, notify::RecursiveMode::NonRecursive).unwrap();
for (uuid, userinfo) in users {
umanager.insert_user(uuid, userinfo.clone());
if userinfo.banned {
umanager.ban(&userinfo);
if let Some(tx) = sessions.get(&uuid) {let _ = tx.send(crate::api::figura::SessionMessage::Banned).await;}
let mut first_time = true;
while rx.recv().await.is_some() {
let new_config = Config::parse(path.clone());
let mut config = config.write().await;
if new_config != *config || first_time {
if !first_time { tracing::info!("Server configuration modification detected!") }
first_time = false;
*config = new_config;
let users: Vec<(Uuid, Userinfo)> = config.advanced_users
.iter()
.map( |(uuid, userdata)| {
(
*uuid,
Userinfo {
uuid: *uuid,
nickname: userdata.username.clone(),
banned: userdata.banned,
..Default::default()
}
)})
.collect();
for (uuid, userinfo) in users {
umanager.insert_user(uuid, userinfo.clone());
if userinfo.banned {
umanager.ban(&userinfo);
if let Some(tx) = sessions.get(&uuid) {let _ = tx.send(crate::api::figura::SessionMessage::Banned).await;}
} else {
umanager.unban(&uuid);
}
}
}
}
}
pub async fn update_bans_from_minecraft(
folder: PathBuf,
umanager: std::sync::Arc<UManager>,
sessions: std::sync::Arc<dashmap::DashMap<Uuid, tokio::sync::mpsc::Sender<crate::api::figura::SessionMessage>>>
umanager: Arc<UManager>,
sessions: Arc<dashmap::DashMap<Uuid, tokio::sync::mpsc::Sender<crate::api::figura::SessionMessage>>>
) {
let path = folder.join("banned-players.json");
let mut file = tokio::fs::File::open(path.clone()).await.expect("Access denied or banned-players.json doesn't exists!");
@ -71,10 +85,10 @@ pub async fn update_bans_from_minecraft(
// initialize
file.read_to_string(&mut data).await.expect("cant read banned-players.json");
let mut old_bans: Vec<BannedPlayer> = serde_json::from_str(&data).expect("cant parse banned-players.json");
if !old_bans.is_empty() {
let names: Vec<String> = old_bans.iter().map(|user| user.name.clone()).collect();
info!("Banned players: {}", names.join(", "));
tracing::info!("Banned players: {}", names.join(", "));
}
for player in &old_bans {
@ -82,19 +96,27 @@ pub async fn update_bans_from_minecraft(
if let Some(tx) = sessions.get(&player.uuid) {let _ = tx.send(crate::api::figura::SessionMessage::Banned).await;}
}
let (tx, mut rx) = tokio::sync::mpsc::channel::<notify::Result<Event>>(1);
let mut watcher = notify::RecommendedWatcher::new(
move |res| {
tx.blocking_send(res).unwrap();
},
notify::Config::default(),
).unwrap();
watcher.watch(&path, notify::RecursiveMode::NonRecursive).unwrap();
// old_bans
loop {
tokio::time::sleep(std::time::Duration::from_secs(10)).await;
while rx.recv().await.is_some() {
let mut file = tokio::fs::File::open(path.clone()).await.expect("Access denied or file doesn't exists!");
let mut data = String::new();
file.read_to_string(&mut data).await.expect("cant read banned-players.json");
let new_bans: Vec<BannedPlayer> = if let Ok(res) = serde_json::from_str(&data) { res } else {
error!("Error occured while parsing a banned-players.json");
tracing::error!("Error occured while parsing a banned-players.json");
continue;
};
if new_bans != old_bans {
info!("Minecraft ban list modification detected!");
tracing::info!("Minecraft ban list modification detected!");
let unban: Vec<&BannedPlayer> = old_bans.iter().filter(|user| !new_bans.contains(user)).collect();
let mut unban_names = unban.iter().map(|user| user.name.clone()).collect::<Vec<String>>().join(", ");
if !unban.is_empty() {
@ -110,14 +132,13 @@ pub async fn update_bans_from_minecraft(
if let Some(tx) = sessions.get(&player.uuid) {let _ = tx.send(crate::api::figura::SessionMessage::Banned).await;}
}
} else { ban_names = String::from("-")};
info!("List of changes:\n Banned: {ban_names}\n Unbanned: {unban_names}");
tracing::info!("List of changes:\n Banned: {ban_names}\n Unbanned: {unban_names}");
// Write new to old for next iteration
old_bans = new_bans;
}
}
}
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"))?;
@ -138,7 +159,7 @@ pub fn calculate_file_sha256(file_path: &str) -> Result<String, std::io::Error>
let hash = binding.as_ref();
// Convert the hash to a hexadecimal string
let hex_hash = hex::encode(hash);
let hex_hash = faster_hex::hex_string(hash);
Ok(hex_hash)
}