2025-11-29 13:15:09 -07:00
|
|
|
use serde_json::json;
|
|
|
|
|
use std::{collections::HashMap, sync::Arc, time::Duration};
|
2025-11-28 18:39:14 -07:00
|
|
|
|
2025-11-29 13:15:09 -07:00
|
|
|
use chrono::Utc;
|
2025-11-28 18:39:14 -07:00
|
|
|
use egui::{Align2, Area, Frame, Order, Sense, UiKind, Vec2, mutex::Mutex};
|
|
|
|
|
use wasm_bindgen::prelude::Closure;
|
|
|
|
|
|
|
|
|
|
#[derive(serde::Deserialize, serde::Serialize)]
|
|
|
|
|
pub struct Auth {
|
|
|
|
|
// Auth Stuff
|
|
|
|
|
token: Option<Token>,
|
|
|
|
|
#[serde(skip)]
|
|
|
|
|
auth_state: Arc<Mutex<AuthState>>,
|
|
|
|
|
|
|
|
|
|
// UI Stuff
|
|
|
|
|
username: String,
|
2025-11-29 13:15:09 -07:00
|
|
|
#[serde(skip)]
|
2025-11-28 18:39:14 -07:00
|
|
|
password: String,
|
|
|
|
|
show_password: bool,
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-29 13:15:09 -07:00
|
|
|
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize, PartialEq, Eq)]
|
2025-11-28 18:39:14 -07:00
|
|
|
struct Token {
|
2025-11-29 13:15:09 -07:00
|
|
|
expiration: u128,
|
|
|
|
|
token: String,
|
2025-11-28 18:39:14 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, PartialEq, Eq)]
|
|
|
|
|
enum AuthState {
|
2025-11-29 13:15:09 -07:00
|
|
|
Unset,
|
2025-11-28 18:39:14 -07:00
|
|
|
NotLoggedIn,
|
|
|
|
|
RequestSent,
|
|
|
|
|
Authorised(Token),
|
|
|
|
|
Error(String),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Default for AuthState {
|
|
|
|
|
fn default() -> Self {
|
2025-11-29 13:15:09 -07:00
|
|
|
Self::Unset
|
2025-11-28 18:39:14 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Auth {
|
2025-11-29 13:15:09 -07:00
|
|
|
/// Refresh the authentication state
|
2025-11-28 18:39:14 -07:00
|
|
|
pub fn logged_in(&mut self) -> bool {
|
2025-11-29 13:15:09 -07:00
|
|
|
match (self.token.is_some(), &*self.auth_state.lock()) {
|
|
|
|
|
// The client is actually authorized
|
|
|
|
|
(true, AuthState::Authorised(_)) => true,
|
|
|
|
|
|
|
|
|
|
// If the user has just reloaded the session,
|
|
|
|
|
// the AuthState is not automatically set by any other process
|
|
|
|
|
(true, AuthState::Unset) => true,
|
|
|
|
|
|
|
|
|
|
// If the authentication state has been updated to unauthorized, delete the token
|
|
|
|
|
(true, _) => {
|
|
|
|
|
self.token = None;
|
|
|
|
|
false
|
2025-11-28 18:39:14 -07:00
|
|
|
}
|
|
|
|
|
|
2025-11-29 13:15:09 -07:00
|
|
|
// If the authentication state has been updated to authorized, set the token
|
|
|
|
|
(false, AuthState::Authorised(token)) => {
|
|
|
|
|
self.token = Some(token.clone());
|
|
|
|
|
|
|
|
|
|
// Also clear the password because it is bad to store it
|
|
|
|
|
self.password.clear();
|
|
|
|
|
|
|
|
|
|
true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// The client is actually unauthorized
|
|
|
|
|
(false, _) => false,
|
|
|
|
|
}
|
2025-11-28 18:39:14 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn update(&mut self, ui: &mut egui::Ui) {
|
|
|
|
|
Area::new("Auth".into())
|
|
|
|
|
.kind(UiKind::Modal)
|
|
|
|
|
.sense(Sense::hover())
|
|
|
|
|
.anchor(Align2::CENTER_CENTER, Vec2::ZERO)
|
|
|
|
|
.order(Order::Foreground)
|
|
|
|
|
.interactable(true)
|
|
|
|
|
.show(ui.ctx(), |ui| {
|
|
|
|
|
Frame::popup(ui.style()).show(ui, |ui| {
|
|
|
|
|
ui.heading("UnShell Login");
|
|
|
|
|
|
|
|
|
|
ui.horizontal(|ui| {
|
|
|
|
|
ui.label("Username");
|
|
|
|
|
ui.text_edit_singleline(&mut self.username);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
ui.horizontal(|ui| {
|
|
|
|
|
ui.label("Password");
|
|
|
|
|
let _ = ui.add(
|
|
|
|
|
// [ui.available_width(), 24.],
|
|
|
|
|
egui::TextEdit::singleline(&mut self.password)
|
|
|
|
|
.password(!self.show_password),
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
self.show_password = ui.button("Show").is_pointer_button_down_on();
|
|
|
|
|
|
|
|
|
|
// ui.toggle_value(&mut self.show_password, "Show");
|
|
|
|
|
});
|
|
|
|
|
|
2025-11-29 13:15:09 -07:00
|
|
|
ui.horizontal(|ui| {
|
|
|
|
|
if ui.button("Login").clicked() {
|
|
|
|
|
let state = self.auth_state.clone();
|
|
|
|
|
|
|
|
|
|
crate::httpPost(
|
|
|
|
|
"/auth",
|
|
|
|
|
&json!({
|
|
|
|
|
"username": self.username.clone(),
|
|
|
|
|
"password": self.password.clone()
|
|
|
|
|
})
|
|
|
|
|
.to_string(),
|
|
|
|
|
Closure::once_into_js(move |ok: bool, response: String| {
|
|
|
|
|
*(state.lock()) = if ok {
|
|
|
|
|
if let Ok(token) = serde_json::from_str::<Token>(&response)
|
|
|
|
|
{
|
|
|
|
|
AuthState::Authorised(token)
|
|
|
|
|
} else {
|
|
|
|
|
AuthState::Error("Malformed Response".into())
|
|
|
|
|
}
|
2025-11-28 18:39:14 -07:00
|
|
|
} else {
|
2025-11-29 13:15:09 -07:00
|
|
|
AuthState::Error(response)
|
2025-11-28 18:39:14 -07:00
|
|
|
}
|
2025-11-29 13:15:09 -07:00
|
|
|
}),
|
|
|
|
|
);
|
2025-11-28 18:39:14 -07:00
|
|
|
|
2025-11-29 13:15:09 -07:00
|
|
|
*(self.auth_state.lock()) = AuthState::RequestSent;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ui.label(format!("{:?}", self.auth_state.lock()));
|
|
|
|
|
});
|
2025-11-28 18:39:14 -07:00
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
// });
|
|
|
|
|
// });
|
|
|
|
|
}
|
2025-11-29 13:15:09 -07:00
|
|
|
|
|
|
|
|
pub fn test(&self) {
|
|
|
|
|
if let Some(ref token) = self.token {
|
|
|
|
|
let state = self.auth_state.clone();
|
|
|
|
|
crate::httpGetAuth(
|
|
|
|
|
"/api/test1234/kjhejwer/kwherjwer/iuwehrhiwer/wiuerhjwer",
|
|
|
|
|
format!("Bearer {}", token.token),
|
|
|
|
|
Closure::once_into_js(move |ok: bool, response: String| {
|
|
|
|
|
if ok {
|
|
|
|
|
crate::log(&response);
|
|
|
|
|
} else {
|
|
|
|
|
*(state.lock()) = AuthState::Error(response);
|
|
|
|
|
}
|
|
|
|
|
}),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-11-28 18:39:14 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Default for Auth {
|
|
|
|
|
fn default() -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
token: Default::default(),
|
|
|
|
|
auth_state: Arc::new(Mutex::new(AuthState::NotLoggedIn)),
|
|
|
|
|
username: Default::default(),
|
|
|
|
|
password: Default::default(),
|
|
|
|
|
show_password: Default::default(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|