diff --git a/backend/Cargo.lock b/backend/Cargo.lock index 70fc1b4..14a0db8 100644 --- a/backend/Cargo.lock +++ b/backend/Cargo.lock @@ -643,8 +643,11 @@ dependencies = [ name = "booksman-orm" version = "0.1.0" dependencies = [ + "axum 0.6.1", + "axum-login", "chrono", "entity", + "eyre", "sea-orm", "serde", ] diff --git a/backend/api/src/lib.rs b/backend/api/src/lib.rs index eff807b..e5acee2 100644 --- a/backend/api/src/lib.rs +++ b/backend/api/src/lib.rs @@ -14,14 +14,13 @@ use std::str::FromStr; use std::io; use tower::ServiceBuilder; use tower_http::services::ServeDir; -use tokio::sync::RwLock; use tower_http::cors::{Any,CorsLayer}; use tower_http::trace::TraceLayer; use booksman_search::BookMeili; use booksman_search; use booksman_orm::{ sea_orm::{Database, DatabaseConnection}, - Mutation as MutationCore, Query as QueryCore, + Mutation as MutationCore, Query as QueryCore // BookAndMeta, }; use meilisearch_sdk::client::Client; @@ -32,29 +31,9 @@ use migration::{Migrator, MigratorTrait}; use chrono::Local; use axum_login::{ axum_sessions::{async_session::MemoryStore as SessionMemoryStore, SessionLayer}, - memory_store::MemoryStore as AuthMemoryStore, - secrecy::SecretVec, AuthLayer, AuthUser, RequireAuthorizationLayer, }; use rand::Rng; -use std::sync::Arc; - -#[derive(Debug, Default, Clone, Deserialize, Serialize)] -struct User { - id: i32, - password_hash: String, - name: String, -} - -impl AuthUser for User { - fn get_id(&self) -> String { - format!("{}", self.id) - } - - fn get_password_hash(&self) -> SecretVec { - SecretVec::new(self.password_hash.clone().into()) - } -} #[derive(Deserialize, Serialize, Debug, Clone)] struct BookUI { @@ -230,7 +209,7 @@ struct Opt { static_dir: String, } -type AuthContext = axum_login::extractors::AuthContext>; +type AuthContext = axum_login::extractors::AuthContext; #[tokio::main] pub async fn main() { @@ -253,9 +232,6 @@ pub async fn main() { let secret = rand::thread_rng().gen::<[u8; 64]>(); - let session_store = SessionMemoryStore::new(); - let session_layer = SessionLayer::new(session_store, &secret).with_secure(false); - let conn = Database::connect(db_url) .await .expect("Database connection failed"); @@ -263,21 +239,11 @@ pub async fn main() { Migrator::up(&conn, None).await.unwrap(); + let session_store = SessionMemoryStore::new(); + let session_layer = SessionLayer::new(session_store, &secret).with_secure(false); - let store = Arc::new(RwLock::new(HashMap::default())); - let usersmodels : Vec = QueryCore::list_all_users(&conn).await.unwrap_or(Vec::new()); - //let users : Vec = get_users_seaorm(conn); - //let user = User::get_rusty_user(); - for usermodel in usersmodels.iter() { - let user = User{ - id: usermodel.id, - name: usermodel.user_name.clone().unwrap(), - password_hash : usermodel.password_hash.clone(), - }; - store.write().await.insert(user.get_id(), user); - } - let user_store = AuthMemoryStore::new(&store); + let user_store = booksman_orm::AxumUserStore::new(&conn); let auth_layer = AuthLayer::new(user_store, &secret); let meili_client = Client::new(meili_url, meili_key); @@ -300,7 +266,6 @@ pub async fn main() { // .merge(SpaRouter::new("/assets", opt.static_dir)) .layer(auth_layer) .layer(session_layer) - .layer(Extension(store)) .layer(ServiceBuilder::new().layer(TraceLayer::new_for_http())) .layer(Extension(conn)) .layer(Extension(meili_client)) @@ -332,9 +297,8 @@ pub async fn main() { #[axum_macros::debug_handler] - async fn register_handler(mut auth: AuthContext, Extension(ref conn): Extension, - Extension(ref store): Extension>>, -Json(user_sent): Json) -> impl IntoResponse { + async fn register_handler(Extension(ref conn): Extension, +Json(user_sent): Json) -> impl IntoResponse { // add to db let user: user::Model = user::Model{ id: user_sent.id, @@ -342,8 +306,8 @@ Json(user_sent): Json) -> impl IntoResponse { password_hash: user_sent.clone().password_hash, }; let created_user = MutationCore::create_user(conn, user).await.expect("Failed to create user"); - store.write().await.insert(created_user.last_insert_id, user_sent.clone()); - auth.login(&user_sent).await.unwrap(); + + //auth.login(&user_sent_updated).await.unwrap(); return "success"; } @@ -364,7 +328,7 @@ async fn list_users( } return Json(users); } - async fn login_handler(mut auth: AuthContext, Json(user_sent): Json) -> impl IntoResponse { + async fn login_handler(mut auth: AuthContext, Json(user_sent): Json) -> impl IntoResponse { auth.login(&user_sent).await.unwrap(); return "success"; } @@ -382,7 +346,7 @@ async fn list_users( async fn create_by_isbn( Extension(ref conn): Extension, Extension(ref meili_client): Extension, - Extension(user): Extension, + Extension(user): Extension, axum::extract::Query(params): axum::extract::Query>, ) -> impl IntoResponse { @@ -636,7 +600,7 @@ let mut vec = Vec::with_capacity(12); async fn delete_book( Extension(ref conn): Extension, Extension(ref meili_client): Extension, - Extension(user): Extension, + Extension(user): Extension, Path(id): Path, ) -> impl IntoResponse { MutationCore::delete_book(conn, id) @@ -766,7 +730,7 @@ return Json(res); async fn create_book( Extension(ref conn): Extension, Extension(ref meili_client): Extension, - Extension(user): Extension, + Extension(user): Extension, Json(doc_sent_orig): Json, ) -> impl IntoResponse { println!("Creating book"); @@ -912,7 +876,7 @@ async fn create_book( async fn update_book( Extension(ref conn): Extension, Extension(ref meili_client): Extension, - Extension(user): Extension, + Extension(user): Extension, Json(doc_sent): Json, ) -> impl IntoResponse { println!("Updating book"); diff --git a/backend/orm/Cargo.toml b/backend/orm/Cargo.toml index 82e20ed..b12af95 100644 --- a/backend/orm/Cargo.toml +++ b/backend/orm/Cargo.toml @@ -8,6 +8,9 @@ publish = false serde = { version = "1", features = ["derive"] } entity = { path = "../entity" } chrono = "0.4" +axum-login = "0.4" +axum = "^0.6" +eyre = "0.6.8" [dependencies.sea-orm] version = "^0.10.6" # sea-orm version diff --git a/backend/orm/src/lib.rs b/backend/orm/src/lib.rs index 4a80f23..7919060 100644 --- a/backend/orm/src/lib.rs +++ b/backend/orm/src/lib.rs @@ -1,7 +1,9 @@ mod mutation; mod query; +mod userstore; pub use mutation::*; pub use query::*; +pub use userstore::*; pub use sea_orm; diff --git a/backend/orm/src/query.rs b/backend/orm/src/query.rs index 6a60679..255b119 100644 --- a/backend/orm/src/query.rs +++ b/backend/orm/src/query.rs @@ -26,6 +26,11 @@ impl Query { Book::find_by_id(id).one(db).await } + + pub async fn find_user_by_id(db: &DbConn, id: i32) -> Result, DbErr> { + User::find_by_id(id).one(db).await + } + pub async fn list_all_users(db: &DbConn) -> Result, DbErr> { User::find().all(db).await } diff --git a/backend/orm/src/userstore.rs b/backend/orm/src/userstore.rs new file mode 100644 index 0000000..457c118 --- /dev/null +++ b/backend/orm/src/userstore.rs @@ -0,0 +1,62 @@ +use sea_orm::*; +use axum::async_trait; +use axum_login::{secrecy::SecretVec, AuthUser, UserStore}; +use crate::Query; +use serde::{Serialize, Deserialize}; + + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct AxumUser { + pub id: i32, + pub password_hash: String, + pub name: String, +} + +type Result = std::result::Result; + +impl AuthUser for AxumUser +where +Role: PartialOrd + PartialEq + Clone + Send + Sync + 'static, +{ + fn get_id(&self) -> String { + format!("{}", self.id) + } + + fn get_password_hash(&self) -> axum_login::secrecy::SecretVec { + SecretVec::new(self.password_hash.clone().into()) + } +} + +#[derive(Debug, Clone)] +pub struct AxumUserStore { + conn: DatabaseConnection, +} + +impl AxumUserStore { + pub fn new(conn: &DatabaseConnection) -> Self { + Self { conn: conn.clone() } + } +} + +#[async_trait] +impl UserStore for AxumUserStore +where +Role: PartialOrd + PartialEq + Clone + Send + Sync + 'static, +{ + type User = AxumUser; + + async fn load_user(&self, user_id: &str) -> Result> { + // my user id is a Vec, so it's stored base64 encoded + let id: i32 = user_id.parse().unwrap(); + let user = Query::find_user_by_id(&self.conn, id).await?; + match user { + Some(u) => Ok(Some(AxumUser { + id: u.id, + password_hash: u.password_hash, + name: u.user_name.unwrap(), + })), + None => Ok(None), + } + } +} +