From b089f62bcd645b2597da8bc77b2b373857cdcfa3 Mon Sep 17 00:00:00 2001 From: Julia Lange Date: Thu, 5 Mar 2026 10:15:16 -0800 Subject: [PATCH] db&webapi, primitive session tokens adds a temporary auth method to users which does not require a password or similar. This is just for testing right now and assumes a self-hosted no-threats environment. Also adds a user_key state to keep track of authed users. These currently *DO NOT EXPIRE* which is pretty bad haha. The entire auth system will be redone. --- koucha/src/bin/webapi/main.rs | 24 +++++++++-- .../src/bin/webapi/routes/create_session.rs | 41 +++++++++++++++++++ koucha/src/bin/webapi/routes/mod.rs | 2 + koucha/src/bin/webapi/types.rs | 11 +++++ koucha/src/db/user.rs | 11 +++++ 5 files changed, 86 insertions(+), 3 deletions(-) create mode 100644 koucha/src/bin/webapi/routes/create_session.rs diff --git a/koucha/src/bin/webapi/main.rs b/koucha/src/bin/webapi/main.rs index 21cb1eb..bf09bad 100644 --- a/koucha/src/bin/webapi/main.rs +++ b/koucha/src/bin/webapi/main.rs @@ -1,24 +1,42 @@ +use std::collections::HashMap; use koucha::{ - AdapterBuilder, - Adapter, + Adapter, AdapterBuilder, db::{User, UserKey as DbUserKey} }; +use crate::types::UserKey; + mod routes; mod types; #[derive(Clone)] struct AppState { pub adapter: Adapter, + // TODO: Set up UserKey expirations + auth_map: HashMap, } +impl AppState { + fn create_user_key(&mut self, db_user_key: DbUserKey) -> UserKey { + let key = UserKey::new(); + self.auth_map.insert(key.clone(), db_user_key); + key + } + #[allow(dead_code)] + fn user_from_key(&self, key: &UserKey) -> Option { + self.auth_map.get(key).map(DbUserKey::clone) + } +} + + #[tokio::main] async fn main() { let db_url = std::env::var("DATABASE_URL").unwrap(); let adapter = AdapterBuilder::new() .database_url(&db_url) .create().await.unwrap(); + let auth_map = HashMap::new(); - let app = routes::router().with_state(AppState { adapter }); + let app = routes::router().with_state(AppState { adapter, auth_map }); let listener = tokio::net::TcpListener::bind("0.0.0.0:4142").await.unwrap(); axum::serve(listener, app).await.unwrap(); diff --git a/koucha/src/bin/webapi/routes/create_session.rs b/koucha/src/bin/webapi/routes/create_session.rs new file mode 100644 index 0000000..fd74294 --- /dev/null +++ b/koucha/src/bin/webapi/routes/create_session.rs @@ -0,0 +1,41 @@ +use axum::{Json, extract::State}; +use koucha::db::User as DbUser; +use reqwest::StatusCode; +use serde::{Serialize, Deserialize}; + +use crate::{ + AppState, routes::{ApiError, ApiResult, ApiResponse}, types::UserKey +}; + +#[derive(Deserialize)] +pub struct Input { + pub user_name: String, +} + +#[derive(Serialize, Deserialize)] +pub struct Output { + user_key: UserKey, +} + +pub async fn handler( + State(mut state): State, + Json(body): Json, +) -> ApiResult { + let dbuser = DbUser::temporary_auth( + state.adapter.get_pool(), + &body.user_name + ).await.map_err(|_e| { + // TODO: Logging + ApiError { + status: StatusCode::INTERNAL_SERVER_ERROR, + error: "InternalError", + message: String::from( + "Error authentiating user ".to_owned() + &body.user_name + ), + } + })?; + + let key = state.create_user_key(dbuser.key()); + + Ok(ApiResponse(StatusCode::OK, Output { user_key: key })) +} diff --git a/koucha/src/bin/webapi/routes/mod.rs b/koucha/src/bin/webapi/routes/mod.rs index ce4477a..58afe73 100644 --- a/koucha/src/bin/webapi/routes/mod.rs +++ b/koucha/src/bin/webapi/routes/mod.rs @@ -11,6 +11,7 @@ use axum::{ }; use reqwest::StatusCode; +mod create_session; mod get_users; mod new_user; @@ -28,6 +29,7 @@ pub struct ApiError { pub fn router() -> Router { Router::new() + .route("/create_session", post(create_session::handler)) .route("/get_users", get(get_users::handler)) .route("/new_user", post(new_user::handler)) } diff --git a/koucha/src/bin/webapi/types.rs b/koucha/src/bin/webapi/types.rs index 2216352..5a2846c 100644 --- a/koucha/src/bin/webapi/types.rs +++ b/koucha/src/bin/webapi/types.rs @@ -1,6 +1,17 @@ +use chrono::Utc; use serde::{Serialize, Deserialize}; #[derive(Serialize, Deserialize)] pub struct User { pub name: String, } + +#[derive(Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub struct UserKey(String); +impl UserKey { + pub fn new() -> Self { + UserKey( + Utc::now().to_rfc3339() + ) + } +} diff --git a/koucha/src/db/user.rs b/koucha/src/db/user.rs index 038b82a..ff58605 100644 --- a/koucha/src/db/user.rs +++ b/koucha/src/db/user.rs @@ -29,6 +29,17 @@ pub struct User { impl User { pub fn key(&self) -> UserKey { self.key } pub fn name(&self) -> &str { &self.name } + + pub async fn temporary_auth( + pool: &AdapterPool, + name: &str, + ) -> Result { + sqlx::query_as!( + UnparsedUser, + "SELECT id as `id!`, name FROM users WHERE name = ?", + name + ).fetch_one(&pool.0).await?.parse() + } pub async fn get(pool: &AdapterPool, key: UserKey) -> Result { sqlx::query_as!(