2021-09-01 16:14:54 +00:00
|
|
|
use std::collections::HashMap;
|
|
|
|
use std::fs;
|
2021-09-07 17:09:50 +00:00
|
|
|
use std::path::Path;
|
2021-09-01 16:14:54 +00:00
|
|
|
use std::sync::Arc;
|
|
|
|
use thiserror::Error;
|
|
|
|
use serde::{Deserialize,Serialize};
|
|
|
|
use tokio::sync::RwLock;
|
|
|
|
use casbin::prelude::*;
|
|
|
|
use warp::{Rejection};
|
|
|
|
|
2021-09-07 13:57:05 +00:00
|
|
|
use app_env::{AppStore,config::WebServer};
|
2021-09-01 16:14:54 +00:00
|
|
|
use app_tools::{trim_newline,from_base64};
|
|
|
|
|
|
|
|
pub const BEARER_PREFIX: &str = "Bearer ";
|
|
|
|
|
|
|
|
pub type WebResult<T> = std::result::Result<T, Rejection>;
|
|
|
|
|
|
|
|
#[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<anyhow::Error>) -> warp::Rejection {
|
|
|
|
pub fn custom_reject(error: impl Into<anyhow::Error>) -> warp::Rejection {
|
|
|
|
warp::reject::custom(CustomReject(error.into()))
|
|
|
|
}
|
|
|
|
|
|
|
|
pub type UserMap = Arc<RwLock<HashMap<String, User>>>;
|
|
|
|
pub type UserShadowMap = Arc<RwLock<HashMap<String, UserShadow>>>;
|
|
|
|
pub type Sessions = Arc<RwLock<HashMap<String, String>>>;
|
|
|
|
pub type SharedEnforcer = Arc<Enforcer>;
|
|
|
|
|
|
|
|
#[derive(Clone)]
|
|
|
|
pub struct AuthStore {
|
|
|
|
pub users: UserMap,
|
|
|
|
pub shadows: UserShadowMap,
|
|
|
|
pub sessions: Sessions,
|
|
|
|
pub enforcer: Arc<casbin::Enforcer>, // SharedEnforcer,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl AuthStore {
|
|
|
|
#[must_use]
|
2021-09-17 16:07:26 +00:00
|
|
|
pub fn new(config: &WebServer, enforcer: SharedEnforcer,verbose: isize) -> Self {
|
2021-09-01 16:14:54 +00:00
|
|
|
Self {
|
2021-09-17 16:07:26 +00:00
|
|
|
users: Arc::new(RwLock::new(AuthStore::create_user_map(config,verbose))),
|
|
|
|
shadows: Arc::new(RwLock::new(AuthStore::create_shadows_map(config,verbose))),
|
2021-09-01 16:14:54 +00:00
|
|
|
sessions: Arc::new(RwLock::new(HashMap::new())),
|
|
|
|
enforcer,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#[must_use]
|
|
|
|
pub fn load_users_from_fs(store: &str,target: &str) -> HashMap<String, User> {
|
|
|
|
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<String, User> = 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<String, UserShadow> {
|
|
|
|
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<String, UserShadow> = toml::from_str(&data_content).unwrap_or_else(|e| {
|
|
|
|
println!("Error loading users shadows from store: {} error: {}", &store,e);
|
|
|
|
HashMap::new()
|
|
|
|
});
|
|
|
|
shadows
|
|
|
|
}
|
|
|
|
#[must_use]
|
2021-09-17 16:07:26 +00:00
|
|
|
pub fn create_user_map(config: &WebServer,verbose: isize) -> HashMap<String, User> {
|
2021-09-01 16:14:54 +00:00
|
|
|
// 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);
|
2021-09-17 16:07:26 +00:00
|
|
|
if !usrs.is_empty() && verbose > 0 {
|
2021-09-01 16:14:54 +00:00
|
|
|
println!("Users loaded successfully ({})", &usrs.len());
|
|
|
|
}
|
|
|
|
},
|
|
|
|
_ => println!("Store {} not set for users store: ", config.usrs_store),
|
|
|
|
}
|
|
|
|
usrs
|
|
|
|
}
|
|
|
|
#[must_use]
|
2021-09-17 16:07:26 +00:00
|
|
|
pub fn create_shadows_map(config: &WebServer,verbose: isize) -> HashMap<String, UserShadow> {
|
2021-09-01 16:14:54 +00:00
|
|
|
// 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);
|
2021-09-17 16:07:26 +00:00
|
|
|
if !shadows.is_empty() && verbose > 0 {
|
2021-09-01 16:14:54 +00:00
|
|
|
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 {
|
2021-09-07 17:09:50 +00:00
|
|
|
if ! Path::new(&model_path).exists() {
|
|
|
|
panic!("model path: {} not exists",&model_path);
|
|
|
|
}
|
|
|
|
if ! Path::new(&policy_path).exists() {
|
|
|
|
panic!("policy path: {} not exists",&policy_path);
|
|
|
|
}
|
2021-09-01 16:14:54 +00:00
|
|
|
// 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,
|
|
|
|
}
|