Skip to content

Commit

Permalink
The user system has been refactored
Browse files Browse the repository at this point in the history
  • Loading branch information
shiroyashik committed Jun 15, 2024
1 parent 3d64d60 commit 703a272
Show file tree
Hide file tree
Showing 6 changed files with 149 additions and 87 deletions.
5 changes: 4 additions & 1 deletion Config.example.toml
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,16 @@ motd = """
maxAvatarSize = 100000
maxAvatars = 10

# Shiroyashik
[advancedUsers.66004548-4de5-49de-bade-9c3933d8eb97]
username = "Shiroyashik"
authSystem = "elyby"
special = [0,0,0,1,0,0] # 6
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] # 25

# Your nickname here
# [advancedUsers.your uuid here]
# username = "Bot"
# authSystem = "mojang" # can be: mojang, elyby, internal (cant be authenticated)
# special = [0,1,0,0,0,0] # and set badges what you want! :D
# 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]

Expand Down
108 changes: 94 additions & 14 deletions src/auth.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::{str::FromStr, sync::Arc};

use crate::utils::*;
use anyhow::anyhow;
use axum::{
Expand All @@ -8,6 +10,7 @@ use axum::{
routing::get,
Router,
};
use dashmap::DashMap;
use ring::digest::{self, digest};
use serde::Deserialize;
use tracing::{debug, error, info, trace};
Expand Down Expand Up @@ -35,8 +38,8 @@ async fn id(
) -> String {
let server_id =
hex::encode(&digest(&digest::SHA1_FOR_LEGACY_USE_ONLY, &rand()).as_ref()[0..20]);
let state = state.pending;
state.insert(server_id.clone(), query.username);
let state = state.user_manager;
state.pending_insert(server_id.clone(), query.username);
server_id
}

Expand All @@ -52,7 +55,7 @@ async fn verify(
State(state): State<AppState>,
) -> Response {
let server_id = query.id.clone();
let username = state.pending.remove(&server_id).unwrap().1; // TODO: Add error check
let username = state.user_manager.pending_remove(&server_id).unwrap().1; // TODO: Add error check
let userinfo = match has_joined(&server_id, &username).await {
Ok(d) => d,
Err(e) => {
Expand All @@ -62,14 +65,15 @@ async fn verify(
};
if let Some((uuid, auth_system)) = userinfo {
info!("[Authentication] {username} logged in using {auth_system:?}");
let authenticated = state.authenticated;
let authenticated = state.user_manager;
authenticated.insert(
uuid,
server_id.clone(),
crate::Userinfo {
Userinfo {
username,
uuid,
auth_system,
token: Some(server_id.clone()),
},
);
(StatusCode::OK, server_id.to_string()).into_response()
Expand All @@ -82,7 +86,7 @@ async fn verify(
pub async fn status(Token(token): Token, State(state): State<AppState>) -> Response {
match token {
Some(token) => {
if state.authenticated.contains_token(&token) {
if state.user_manager.is_authenticated(&token) {
(StatusCode::OK, "ok".to_string()).into_response()
} else {

Expand Down Expand Up @@ -121,31 +125,47 @@ where
}
// End Extractor

// Work with external APIs
#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq, Deserialize)]
pub enum AuthSystem {
Internal,
ElyBy,
Mojang,
}

impl ToString for AuthSystem {
fn to_string(&self) -> String {
match self {
AuthSystem::Internal => String::from("internal"),
AuthSystem::ElyBy => String::from("elyby"),
AuthSystem::Mojang => String::from("mojang"),
}
}
}

impl FromStr for AuthSystem {
type Err = anyhow::Error;

fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"internal" => Ok(Self::Internal),
"elyby" => Ok(Self::ElyBy),
"mojang" => Ok(Self::Mojang),
_ => Err(anyhow!("No auth system called: {s}"))
}
}
}

impl AuthSystem {
fn get_url(&self) -> String {
match self {
AuthSystem::Internal => panic!("Can't get internal URL!"),
AuthSystem::ElyBy => String::from("http://minecraft.ely.by/session/hasJoined"),
AuthSystem::Mojang => String::from("https://sessionserver.mojang.com/session/minecraft/hasJoined"),
}
}
}

// Work with external APIs
/// Get UUID from JSON response
#[inline]
fn get_id_json(json: &serde_json::Value) -> anyhow::Result<Uuid> {
Expand Down Expand Up @@ -199,11 +219,71 @@ pub async fn has_joined(
} else {
panic!("Impossible error!")
}
// FOR DELETE
// tokio::select! {
// Ok(res) = tokio::spawn(fetch_json(AuthSystem::ElyBy, server_id, username)) => {Ok(res)},
// Ok(res) = tokio::spawn(fetch_json(AuthSystem::Mojang, server_id, username)) => {Ok(res)},
// else => {Err(anyhow!("Something went wrong in external apis request process"))}
// }
}
// End of work with external APIs

// User manager
#[derive(Debug, Clone)]
pub struct UManager {
/// Users with incomplete authentication
pending: Arc<DashMap<String, String>>, // <SHA1 serverId, USERNAME> TODO: Add automatic purge
/// Authenticated users
authenticated: Arc<DashMap<String, Uuid>>, // <SHA1 serverId, Userinfo> NOTE: In the future, try it in a separate LockRw branch
/// Registered users
registered: Arc<DashMap<Uuid, Userinfo>>,
}

#[derive(Debug, Clone)]
pub struct Userinfo {
pub username: String,
pub uuid: Uuid,
pub auth_system: AuthSystem,
pub token: Option<String>,
}

impl UManager {
pub fn new() -> Self {
Self {
pending: Arc::new(DashMap::new()),
registered: Arc::new(DashMap::new()),
authenticated: Arc::new(DashMap::new()),
}
}
pub fn pending_insert(&self, server_id: String, username: String) {
self.pending.insert(server_id, username);
}
pub fn pending_remove(&self, server_id: &str) -> std::option::Option<(std::string::String, std::string::String)> {
self.pending.remove(server_id)
}
pub fn insert(&self, uuid: Uuid, token: String, userinfo: Userinfo) -> Option<Userinfo> {
self.authenticated.insert(token, uuid);
self.registered.insert(uuid, userinfo)
}
pub fn insert_user(&self, uuid: Uuid, userinfo: Userinfo) -> Option<Userinfo> {
self.registered.insert(uuid, userinfo)
}
pub fn get(
&self,
token: &String,
) -> Option<dashmap::mapref::one::Ref<'_, Uuid, Userinfo>> {
let uuid = self.authenticated.get(token)?;
self.registered.get(uuid.value())
}
pub fn get_by_uuid(
&self,
uuid: &Uuid,
) -> Option<dashmap::mapref::one::Ref<'_, Uuid, Userinfo>> {
self.registered.get(uuid)
}
pub fn is_authenticated(&self, token: &String) -> bool {
self.authenticated.contains_key(token)
}
pub fn is_registered(&self, uuid: &Uuid) -> bool {
self.registered.contains_key(uuid)
}
pub fn remove(&self, uuid: &Uuid) {
let token = self.registered.remove(uuid).unwrap().1.token.unwrap();
self.authenticated.remove(&token);
}
}
// End of User manager
81 changes: 21 additions & 60 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use axum::{
extract::DefaultBodyLimit, middleware::from_extractor, routing::{delete, get, post, put}, Router
};
use dashmap::DashMap;
use utils::collect_advanced_users;
use std::sync::Arc;
use tokio::sync::{broadcast, Mutex};
use tower_http::trace::TraceLayer;
Expand All @@ -15,7 +16,7 @@ use ws::handler;

// API: Auth
mod auth;
use auth as api_auth;
use auth::{self as api_auth, UManager};

// API: Server info
mod info;
Expand All @@ -31,64 +32,17 @@ mod utils;
// Config
mod config;

#[derive(Debug, Clone)]
pub struct Userinfo {
username: String,
uuid: Uuid,
auth_system: api_auth::AuthSystem,
}

#[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.clone())
} else {
None
}
}
pub fn contains_token(&self, token: &String) -> bool {
self.user_data.contains_key(token)
}
pub fn remove(&self, uuid: &Uuid) {
let token = self.uuid.remove(uuid).unwrap().1;
self.user_data.remove(&token);
}
}

#[derive(Debug, Clone)]
pub struct AppState {
// Users with incomplete authentication
pending: Arc<DashMap<String, String>>, // <SHA1 serverId, USERNAME>
// Authenticated users
authenticated: Arc<Authenticated>, // <SHA1 serverId, Userinfo> NOTE: In the future, try it in a separate LockRw branch
// Ping broadcasts for WebSocket connections
/// Users with incomplete authentication
//pending: Arc<DashMap<String, String>>, // <SHA1 serverId, USERNAME>
/// Authenticated users
//authenticated: Arc<Authenticated>, // <SHA1 serverId, Userinfo> NOTE: In the future, try it in a separate LockRw branch
/// User manager
user_manager: Arc<UManager>,
/// Ping broadcasts for WebSocket connections
broadcasts: Arc<DashMap<Uuid, broadcast::Sender<Vec<u8>>>>,
// Current configuration
/// Current configuration
config: Arc<Mutex<config::Config>>,
}

Expand Down Expand Up @@ -117,25 +71,32 @@ async fn main() -> Result<()> {

// State
let state = AppState {
pending: Arc::new(DashMap::new()),
authenticated: Arc::new(Authenticated::new()),
user_manager: Arc::new(UManager::new()),
broadcasts: Arc::new(DashMap::new()),
config: config,
};

// Automatic update of configuration while the server is running
let config_update = state.config.clone();
let user_manager = Arc::clone(&state.user_manager);
tokio::spawn(async move {
loop {
tokio::time::sleep(std::time::Duration::from_secs(10)).await;

let new_config = config::Config::parse(config_file.clone().into());
let mut config = config_update.lock().await;

if new_config != *config {
info!("Server configuration modification detected!");
*config = new_config;
// let collected = collect_advanced_users(&config.advanced_users);
// for (uuid, userinfo) in collected {
// user_manager.insert_user(uuid, userinfo);
// }
}
let collected = collect_advanced_users(&config.advanced_users);
for (uuid, userinfo) in collected {
user_manager.insert_user(uuid, userinfo);
}
tokio::time::sleep(std::time::Duration::from_secs(10)).await;
}
});

Expand Down
8 changes: 4 additions & 4 deletions src/profile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ pub async fn user_info(

let avatar_file = format!("avatars/{}.moon", formatted_uuid);

let auth_system = match state.authenticated.get_by_uuid(&uuid) {
let auth_system = match state.user_manager.get_by_uuid(&uuid) {
Some(d) => d.auth_system.to_string(),
None => return Json(json!("err")),
};
Expand Down Expand Up @@ -113,7 +113,7 @@ pub async fn upload_avatar(
None => http_error_ret!(UNAUTHORIZED, "Authentication error!"),
};

if let Some(user_info) = state.authenticated.get(&token) {
if let Some(user_info) = state.user_manager.get(&token) {
tracing::info!(
"{} ({}) trying to upload an avatar",
user_info.uuid,
Expand All @@ -128,7 +128,7 @@ pub async fn upload_avatar(

pub async fn equip_avatar(Token(token): Token, State(state): State<AppState>) -> String {
debug!("[API] S2C : Equip");
let uuid = state.authenticated.get(&token.unwrap()).unwrap().uuid;
let uuid = state.user_manager.get(&token.unwrap()).unwrap().uuid;
if state
.broadcasts
.get(&uuid)
Expand All @@ -147,7 +147,7 @@ pub async fn delete_avatar(Token(token): Token, State(state): State<AppState>) -
Some(t) => t,
None => http_error_ret!(UNAUTHORIZED, "Authentication error!"),
};
if let Some(user_info) = state.authenticated.get(&token) {
if let Some(user_info) = state.user_manager.get(&token) {
tracing::info!(
"{} ({}) is trying to delete the avatar",
user_info.uuid,
Expand Down
Loading

0 comments on commit 703a272

Please sign in to comment.