use std::collections::HashMap; use std::fs; use std::sync::Arc; use thiserror::Error; use serde::{Deserialize,Serialize}; use tokio::sync::RwLock; use casbin::prelude::*; use warp::{Rejection}; use app_env::{AppStore,config::WebServer}; use app_tools::{trim_newline,from_base64}; pub const BEARER_PREFIX: &str = "Bearer "; pub type WebResult = std::result::Result; #[derive(Deserialize,Serialize,Clone,Debug,Default)] pub struct UserCtx { pub user_id: String, pub token: String, } #[derive(Deserialize,Serialize,Clone,Debug,Default)] pub struct User { pub user_id: String, pub name: String, pub role: String, } #[derive(Deserialize,Serialize,Clone,Debug,Default)] pub struct UserShadow { pub user_id: String, pub passwd: String, } #[derive(Deserialize, Debug,Default)] pub struct LoginRequest { pub name: String, pub passwd: String, pub mapkey: String, } #[derive(Deserialize, Debug,Default)] pub struct CheckinRequest { pub data: String, pub mapkey: String, } #[allow(clippy::pub_enum_variant_names)] #[derive(Error, Debug)] pub enum AuthError { #[error("error")] SomeError(), #[error("no authorization header found")] NoAuthHeaderFoundError, #[error("wrong authorization header format")] InvalidAuthHeaderFormatError, #[error("no user found for this token")] InvalidTokenError, #[error("error during authorization")] AuthorizationError, #[error("user is not unauthorized")] UnauthorizedError, #[error("no user found with this name")] UserNotFoundError, } // impl warp::reject::Reject for anyhow::Error {} impl warp::reject::Reject for AuthError {} // impl From<::Error> for warp::reject::Rejection {} // https://github.com/seanmonstar/warp/issues/307 #[derive(Debug)] pub struct CustomReject(anyhow::Error); impl warp::reject::Reject for CustomReject {} #[must_use] // pub(crate) fn custom_reject(error: impl Into) -> warp::Rejection { pub fn custom_reject(error: impl Into) -> warp::Rejection { warp::reject::custom(CustomReject(error.into())) } pub type UserMap = Arc>>; pub type UserShadowMap = Arc>>; pub type Sessions = Arc>>; pub type SharedEnforcer = Arc; #[derive(Clone)] pub struct AuthStore { pub users: UserMap, pub shadows: UserShadowMap, pub sessions: Sessions, pub enforcer: Arc, // SharedEnforcer, } impl AuthStore { #[must_use] pub fn new(config: &WebServer, enforcer: SharedEnforcer,verbose: &str) -> Self { Self { users: Arc::new(RwLock::new(AuthStore::create_user_map(config,&verbose))), shadows: Arc::new(RwLock::new(AuthStore::create_shadows_map(config,&verbose))), sessions: Arc::new(RwLock::new(HashMap::new())), enforcer, } } #[must_use] pub fn load_users_from_fs(store: &str,target: &str) -> HashMap { let mut usrs_content = fs::read_to_string(target).unwrap_or_else(|_|String::from("")); trim_newline(&mut usrs_content); let data_content = from_base64(&usrs_content); if ! data_content.contains("role") { println!("Error no 'role' in users from store: {}", &store); return HashMap::new() } let usrs: HashMap = toml::from_str(&data_content).unwrap_or_else(|e| { println!("Error loading users from store: {} error: {}", &store,e); HashMap::new() }); usrs } #[must_use] pub fn load_shadows_from_fs(store: &str,target: &str) -> HashMap { let mut shadow_content = fs::read_to_string(target).unwrap_or_else(|_|String::from("")); trim_newline(&mut shadow_content); let data_content = from_base64(&shadow_content); if ! data_content.contains("passwd") { println!("Error no 'passwd' in shadows from store: {}", &store); return HashMap::new() } let shadows: HashMap = toml::from_str(&data_content).unwrap_or_else(|e| { println!("Error loading users shadows from store: {} error: {}", &store,e); HashMap::new() }); shadows } #[must_use] pub fn create_user_map(config: &WebServer,verbose: &str) -> HashMap { // TODO load form YAML o CONFIG let mut usrs = HashMap::new(); match config.usrs_store.as_str() { "fs" => { usrs = AuthStore::load_users_from_fs(&config.usrs_store,&config.usrs_store_target); if !usrs.is_empty() && verbose != "quiet" { println!("Users loaded successfully ({})", &usrs.len()); } }, _ => println!("Store {} not set for users store: ", config.usrs_store), } usrs } #[must_use] pub fn create_shadows_map(config: &WebServer,verbose: &str) -> HashMap { // TODO load form YAML o CONFIG let mut shadows = HashMap::new(); match config.usrs_shadow_store.as_str() { "fs" => { shadows = AuthStore::load_shadows_from_fs(&config.usrs_shadow_store,&config.usrs_shadow_target); if !shadows.is_empty() && verbose != "quiet" { println!("Users info successfully ({})",&shadows.len()); } }, _ => println!("Store {} not set for shadow store: ", config.usrs_shadow_store), } shadows } // map.insert( // String::from("21"), // User { // user_id: String::from("21"), // name: String::from("herbert"), // role: String::from("member"), // }, // ); // map.insert( // String::from("100"), // User { // user_id: String::from("100"), // name: String::from("jesus"), // role: String::from("admin"), // }, // ); // map.insert( // String::from("1"), // User { // user_id: String::from("1"), // name: String::from("gordon"), // role: String::from("anonymous"), // }, // ); // map // } pub async fn create_enforcer(model_path: &'static str, policy_path: &'static str) -> SharedEnforcer { // Arc::new(Enforcer::new(super::super::MODEL_PATH, super::super::POLICY_PATH) Arc::new(Enforcer::new(model_path, policy_path) .await .expect("can read casbin model and policy files") ) } } #[derive(Clone)] pub struct AppAuthDBs { pub app: AppStore, pub auth: AuthStore, }