mock auth
This commit is contained in:
@@ -0,0 +1,89 @@
|
||||
use rocket::State;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Mutex;
|
||||
use uuid::Uuid;
|
||||
use crate::items;
|
||||
use crate::proto_utils::Proto;
|
||||
|
||||
pub struct AuthState {
|
||||
// Map token -> username
|
||||
tokens: Mutex<HashMap<String, String>>,
|
||||
}
|
||||
|
||||
impl AuthState {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
tokens: Mutex::new(HashMap::new()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[post("/login", data = "<request>")]
|
||||
pub fn login(
|
||||
state: &State<AuthState>,
|
||||
request: Proto<items::LoginRequest>,
|
||||
) -> items::LoginResponse {
|
||||
let req = request.into_inner();
|
||||
// Simple mock authentication: allow any non-empty username/password
|
||||
if !req.username.is_empty() && !req.password.is_empty() {
|
||||
let token = Uuid::new_v4().to_string();
|
||||
let mut tokens = state.tokens.lock().unwrap();
|
||||
tokens.insert(token.clone(), req.username);
|
||||
|
||||
items::LoginResponse {
|
||||
token,
|
||||
success: true,
|
||||
message: "Login successful".to_string(),
|
||||
}
|
||||
} else {
|
||||
items::LoginResponse {
|
||||
token: "".to_string(),
|
||||
success: false,
|
||||
message: "Invalid credentials".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[post("/logout", data = "<request>")]
|
||||
pub fn logout(
|
||||
state: &State<AuthState>,
|
||||
request: Proto<items::LogoutRequest>,
|
||||
) -> items::LogoutResponse {
|
||||
let req = request.into_inner();
|
||||
let mut tokens = state.tokens.lock().unwrap();
|
||||
|
||||
if tokens.remove(&req.token).is_some() {
|
||||
items::LogoutResponse {
|
||||
success: true,
|
||||
message: "Logged out successfully".to_string(),
|
||||
}
|
||||
} else {
|
||||
items::LogoutResponse {
|
||||
success: false,
|
||||
message: "Invalid token".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[post("/get_auth_status", data = "<request>")]
|
||||
pub fn get_auth_status(
|
||||
state: &State<AuthState>,
|
||||
request: Proto<items::AuthStatusRequest>,
|
||||
) -> items::AuthStatusResponse {
|
||||
let req = request.into_inner();
|
||||
let tokens = state.tokens.lock().unwrap();
|
||||
|
||||
if let Some(username) = tokens.get(&req.token) {
|
||||
items::AuthStatusResponse {
|
||||
authenticated: true,
|
||||
username: username.clone(),
|
||||
message: "Authenticated".to_string(),
|
||||
}
|
||||
} else {
|
||||
items::AuthStatusResponse {
|
||||
authenticated: false,
|
||||
username: "".to_string(),
|
||||
message: "Not authenticated".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,9 @@ pub mod items {
|
||||
include!(concat!(env!("OUT_DIR"), "/items.rs"));
|
||||
}
|
||||
|
||||
mod auth;
|
||||
mod proto_utils;
|
||||
|
||||
#[get("/<name>")]
|
||||
fn get_user(user_list: &rocket::State<Vec<items::Person>>, name: String) -> Option<items::Person> {
|
||||
user_list.iter().find(|user| user.name == name).cloned()
|
||||
@@ -53,6 +56,7 @@ fn rocket() -> _ {
|
||||
min_players: 1,
|
||||
max_players: 90,
|
||||
price: 0,
|
||||
remote_id: 0,
|
||||
}),
|
||||
would_play: true,
|
||||
}],
|
||||
@@ -60,7 +64,12 @@ fn rocket() -> _ {
|
||||
|
||||
rocket::build()
|
||||
.manage(user_list)
|
||||
.manage(auth::AuthState::new())
|
||||
.mount("/api", routes![get_users, get_user])
|
||||
.mount(
|
||||
"/auth",
|
||||
routes![auth::login, auth::logout, auth::get_auth_status],
|
||||
)
|
||||
.mount("/", routes![index_fallback])
|
||||
.mount("/", FileServer::new("../frontend/dist"))
|
||||
}
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
use rocket::data::{Data, FromData, Outcome, ToByteUnit};
|
||||
use rocket::http::{Status, ContentType};
|
||||
use rocket::Request;
|
||||
use prost::Message;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
pub struct Proto<T>(pub T);
|
||||
|
||||
impl<T> Proto<T> {
|
||||
pub fn into_inner(self) -> T {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Deref for Proto<T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> DerefMut for Proto<T> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[rocket::async_trait]
|
||||
impl<'r, T: Message + Default> FromData<'r> for Proto<T> {
|
||||
type Error = String;
|
||||
|
||||
async fn from_data(req: &'r Request<'_>, data: Data<'r>) -> Outcome<'r, Self> {
|
||||
if req.content_type() != Some(&ContentType::new("application", "protobuf")) {
|
||||
return Outcome::Forward((data, Status::NotFound));
|
||||
}
|
||||
|
||||
let limit = req.limits().get("protobuf").unwrap_or(1.mebibytes());
|
||||
let bytes = match data.open(limit).into_bytes().await {
|
||||
Ok(bytes) if bytes.is_complete() => bytes.into_inner(),
|
||||
Ok(_) => return Outcome::Error((Status::PayloadTooLarge, "Payload too large".into())),
|
||||
Err(e) => return Outcome::Error((Status::InternalServerError, e.to_string())),
|
||||
};
|
||||
|
||||
match T::decode(&bytes[..]) {
|
||||
Ok(msg) => Outcome::Success(Proto(msg)),
|
||||
Err(e) => Outcome::Error((Status::UnprocessableEntity, e.to_string())),
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user