From 8ba9afbce30648f1531a2a7bc5f2141b616d0480 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20P=C3=A9rez?= Date: Wed, 14 Aug 2024 17:34:50 +0100 Subject: [PATCH] chore: update to new crates versions and fix sources for it --- Cargo.toml | 74 +++---- src/defs.rs | 5 + src/defs/app_connect_info.rs | 18 +- src/defs/config.rs | 17 ++ src/defs/form_defs.rs | 247 ++++++++++++++++++++++++ src/defs/openid.rs | 7 +- src/defs/req_handler.rs | 15 +- src/defs/req_settings.rs | 10 +- src/handlers.rs | 1 + src/handlers/other_handlers.rs | 8 +- src/handlers/users.rs | 2 + src/handlers/users/forms_handlers.rs | 207 ++++++++++++++++++++ src/handlers/users/login_handlers.rs | 10 +- src/handlers/users/settings_handlers.rs | 6 +- src/login_password.rs | 4 +- src/main.rs | 19 +- src/tools.rs | 19 +- src/users/openid.rs | 5 + src/users/user.rs | 82 ++++---- src/users/userdata.rs | 2 +- 20 files changed, 628 insertions(+), 130 deletions(-) create mode 100644 src/defs/form_defs.rs create mode 100644 src/handlers/users/forms_handlers.rs diff --git a/Cargo.toml b/Cargo.toml index d1210ab..e215d6a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,65 +14,67 @@ default = "inrepo" #rustdoc-args = ["--html-in-header", "rusdoc/header.html"] [dependencies] -log = { version = "0.4.20", features = ["max_level_trace","release_max_level_trace"], package = "log" } -axum = { git = "https://github.com/tokio-rs/axum.git", branch = "main" } -#axum = { version = "0.6.20" } # use wrks axum_6 -axum-server = { version = "0.5.1", features = ["tls-rustls"] } -tokio = { version = "1.32.0", features = ["full"] } -tower = { version = "0.4.13", features = ["util", "filter"] } -tower-http = { version = "0.4.4", features = ["fs", "cors", "trace", "add-extension", "auth", "compression-full"] } -tower-cookies = "0.9" -tracing = "0.1.37" -tracing-subscriber = { version = "0.3.17", features = ["env-filter"] } +log = { version = "0.4.22", features = ["max_level_trace","release_max_level_trace"], package = "log" } +#axum = { git = "https://github.com/tokio-rs/axum.git", branch = "main" } +axum = { version = "0.7.5" } # use wrks axum_6 +axum-server = { version = "0.7.1", features = ["tls-rustls"] } +tokio = { version = "1.39.2", features = ["full"] } +tower = { version = "0.5.0", features = ["util", "filter"] } +tower-http = { version = "0.5.2", features = ["fs", "cors", "trace", "add-extension", "auth", "compression-full"] } +tower-cookies = "0.10.0" +tracing = "0.1.40" +tracing-subscriber = { version = "0.3.18", features = ["env-filter"] } -casbin = { version = "2.0.9", features = ["cached","explain","logging"], optional = true} +casbin = { version = "2.2.0", features = ["cached","explain","logging"], optional = true} pasetoken-lib= {path = "./pasetoken", package = "pasetoken-lib" } -pasetors = { version = "0.6.7" } +pasetors = { version = "0.6.8" } -serde = { version = "1.0.188", features = ["derive"] } -serde_derive = "1.0.188" -serde_json = "1.0.107" -toml = "0.8.0" +serde = { version = "1.0.207", features = ["derive"] } +serde_derive = "1.0.207" +serde_json = "1.0.124" +toml = "0.8.19" -clap = { version = "4.4.4", features = ["derive"] } -git-version = "0.3.5" -once_cell = "1.18.0" +clap = { version = "4.5.15", features = ["derive"] } +git-version = "0.3.9" +once_cell = "1.19.0" -hyper = { version = "0.14.27", features = ["full"] } -tera = "1.19.1" -html-minifier = "4.0.0" +hyper = { version = "1.4.1", features = ["full"] } +tera = "1.20.0" +html-minifier = "5.0.0" urlencoding = "2.1.3" password-hash = { version = "0.5", features = ["alloc", "rand_core"] } -rand_core = { version = "0.6", features = ["getrandom"] } +rand_core = { version = "0.6.4", features = ["getrandom"] } argon2 = { version = "0.5", default-features = false, features = ["alloc", "simple"]} rand_chacha = "0.3.1" -async-trait = "0.1.73" +async-trait = "0.1.81" async-session = "3.0.0" async-sqlx-session = { version = "0.4.0", features = ["sqlite"] } forwarded-header-value = "0.1.1" -lettre = { version = "0.10.4", features = ["smtp-transport", "tokio1", "tokio1-native-tls", "builder"] } +lettre = { version = "0.11.7", features = ["smtp-transport", "tokio1", "tokio1-native-tls", "builder"] } -chrono = { version = "0.4.*" } -encoding = "0.*" +chrono = { version = "0.4.38" } +encoding = "0.2.33" uuid = { version = "1.4.1", features = ["v4", "serde"] } rand = "0.8.5" -walkdir = "2.4.0" +walkdir = "2.5.0" binascii = "0.1.4" -anyhow = "1.0.75" +anyhow = "1.0.86" -totp-rs = { version = "5.0.2", features = ["qr","otpauth"] } -base32 = "0.4.0" -zxcvbn = "2.2.2" +totp-rs = { version = "5.6.0", features = ["qr","otpauth"] } +base32 = "0.5.1" +zxcvbn = "3.1.0" -futures= "0.3.28" +futures= "0.3.30" +#sqlx = { version = "0.8.0", features = ["sqlite", "runtime-tokio-native-tls"] } +#sqlx = { version = "0.8", features = [ "runtime-tokio", "tls-native-tls" ] } +#sqlx = { version = "0.8.0", features = ["all-types", "all-databases","runtime-tokio-native-tls"] } # async-sqlx-session 0.4.0 still running in 0.6.2 -sqlx = { version = "0.6.3", features = ["all-types", "all-databases","runtime-tokio-native-tls"] } -#sqlx = { version = "0.7.1", features = ["all-databases","runtime-tokio-native-tls"] } +sqlx = { version = "0.6.3", features = ["all-databases","runtime-tokio-native-tls"] } openidconnect = { version = "3.3.1", default-features = true, features = [] } url = { version = "2.4", features = [] } -jsonwebtoken = "8" +jsonwebtoken = "9.3.0" [dev-dependencies] async-std = { version = "1.12.0", features = ["default","attributes"] } diff --git a/src/defs.rs b/src/defs.rs index 4ee7c5f..e59a64e 100644 --- a/src/defs.rs +++ b/src/defs.rs @@ -5,6 +5,7 @@ mod filestore; mod session; #[cfg(feature = "authstore")] mod authz; +mod form_defs; mod appdbs; mod req_headermap; mod req_handler; @@ -15,6 +16,7 @@ mod totp_mode; mod totp_algorithm; mod app_connect_info; mod openid; +mod req_settings; pub(crate) use appdbs::AppDBs; pub(crate) use session::{ @@ -32,6 +34,7 @@ pub(crate) use filestore::FileStore; #[cfg(feature = "authstore")] pub(crate) use authz::AuthStore; pub(crate) use config::{ServPath,Config}; +pub(crate) use form_defs::FormDefs; pub(crate) use cli::parse_args; pub(crate) use req_handler::ReqHandler; pub(crate) use req_headermap::ReqHeaderMap; @@ -46,6 +49,8 @@ pub(crate) use app_connect_info::AppConnectInfo; pub(crate) use openid::{OpenidConf,OpenidData,collect_openid_clients,OpenidCli}; +pub(crate) use req_settings::ReqSettings; + pub const TOKEN_KEY_VALUE: &str = "tii-cl"; pub const TOKEN_AUTH_VALUE: &str = "tii-cl-token"; pub const CLAIM_UID: &str = "uid"; diff --git a/src/defs/app_connect_info.rs b/src/defs/app_connect_info.rs index 47b5121..2070033 100644 --- a/src/defs/app_connect_info.rs +++ b/src/defs/app_connect_info.rs @@ -1,26 +1,28 @@ use std::net::SocketAddr; -use hyper::server::conn::AddrStream; +//use hyper::server::conn::AddrStream; use axum::extract::connect_info::Connected; use axum::serve::IncomingStream; #[derive(Clone, Debug)] pub struct AppConnectInfo { - pub remote_addr: SocketAddr, - pub local_addr: SocketAddr, + pub remote_addr: SocketAddr, +// pub local_addr: SocketAddr, } impl Connected> for AppConnectInfo { fn connect_info(target: IncomingStream<'_>) -> Self { AppConnectInfo { remote_addr: target.remote_addr(), - local_addr: target.local_addr().unwrap_or(target.remote_addr()), +// local_addr: target.local_addr().unwrap_or(target.remote_addr()), } } } -impl Connected<&AddrStream> for AppConnectInfo { - fn connect_info(target: &AddrStream) -> Self { +// impl Connected<&AddrStream> for AppConnectInfo { +// fn connect_info(target: &AddrStream) -> Self { +impl Connected for AppConnectInfo { + fn connect_info(target: SocketAddr) -> Self { AppConnectInfo { - remote_addr: target.remote_addr(), - local_addr: target.local_addr(), + remote_addr: target.to_owned(), +// local_addr: target.to_owned(), } } } diff --git a/src/defs/config.rs b/src/defs/config.rs index a0d0935..6ae97c6 100644 --- a/src/defs/config.rs +++ b/src/defs/config.rs @@ -14,6 +14,7 @@ use crate::{ FromFile, load_from_file, load_dict_from_file, + ReqSettings, }, }; @@ -70,6 +71,9 @@ fn default_web_menu_item_roles() -> Vec { fn default_auth_roles() -> Vec { Vec::new() } +fn default_roles() -> Vec { + Vec::new() +} fn default_sub_menu_items() -> Vec { Vec::new() } @@ -85,6 +89,9 @@ fn default_config_totp_mode() -> TotpMode { fn default_config_password_score() -> u8 { 0 } +fn default_config_req_settings() -> ReqSettings { + ReqSettings::default() +} fn default_config_trace_level() -> u8 { 1 } fn default_config_openid_auths() -> HashMap { HashMap::new() } #[derive(Debug, Clone, Serialize, Deserialize,Default)] @@ -229,6 +236,8 @@ pub struct Config { pub user_store_access: String, #[serde(default = "default_auth_roles")] pub auth_roles: Vec, + #[serde(default = "default_roles")] + pub default_roles: Vec, #[serde(default = "default_config_empty")] pub trace_store_uri: String, #[serde(default = "default_config_trace_level")] @@ -294,6 +303,12 @@ pub struct Config { pub openid_access_token_url: String, #[serde(default = "default_config_openid_auths")] pub openid_auths: HashMap, + #[serde(default = "default_config_resource")] + pub forms_path: String, + #[serde(default = "default_config_resource")] + pub forms_responses_path: String, + #[serde(default = "default_config_req_settings")] + pub req_settings: ReqSettings, } impl FromFile for Config { @@ -372,6 +387,8 @@ impl Config { self.paseto.footer = ConfigPaSeToken::make_footer( self.paseto.map_footer.to_owned() ).unwrap_or(Footer::new()); + self.forms_path = self.fix_item_path(self.forms_path.to_owned()); + self.forms_responses_path = self.fix_item_path(self.forms_responses_path.to_owned()); } #[allow(dead_code)] pub fn to_json(&self) -> String { diff --git a/src/defs/form_defs.rs b/src/defs/form_defs.rs new file mode 100644 index 0000000..c9efee9 --- /dev/null +++ b/src/defs/form_defs.rs @@ -0,0 +1,247 @@ +// use std::collections::HashMap; +use std::{ + io::Write, + // sync::Arc, + fmt::Debug, + fs, + path::{Path, PathBuf}, + io::{Error, ErrorKind}, +}; +use serde::{Serialize, Deserialize}; +//use serde_json::value::{to_value, Value}; +// use log::error; + +use crate::{ + defs::{ + FromFile, + load_from_file, + }, +}; + +// use std::path::Path; + +// use crate::tools::generate_uuid; +// fn default_server_uid() -> String { +// generate_uuid(String::from("abcdef0123456789")) +// } + +fn default_form_defs_empty() -> String { + "".to_string() +} +fn default_form_selections() -> Vec { + Vec::new() +} +fn default_form_border() -> bool { + false +} +fn default_form_required() -> bool { + false +} +fn default_form_items_empty() -> Vec { + Vec::new() +} +fn default_form_overwrite_response() -> bool { + false +} +#[derive(Debug, Clone, Serialize, Deserialize,Default)] +pub struct FormSelectItem { + #[serde(default = "default_form_defs_empty")] + pub title: String, + #[serde(default = "default_form_defs_empty")] + pub defs: String, + #[serde(default = "default_form_defs_empty")] + pub value: String, +} +#[derive(Debug, Clone, Serialize, Deserialize,Default)] +pub struct FormField { + #[serde(default = "default_form_defs_empty")] + pub name: String, + #[serde(default = "default_form_defs_empty")] + pub id: String, + #[serde(default = "default_form_defs_empty")] + pub placeholder: String, + #[serde(default = "default_form_defs_empty")] + pub typ: String, + #[serde(default = "default_form_defs_empty")] + pub css: String, + #[serde(default = "default_form_border")] + pub border: bool, + #[serde(default = "default_form_defs_empty")] + pub label_top_text: String, + #[serde(default = "default_form_defs_empty")] + pub label_css: String, + #[serde(default = "default_form_selections")] + pub selections: Vec, + #[serde(default = "default_form_required")] + pub required: bool, +} + +#[derive(Debug, Clone, Serialize, Deserialize,Default)] +pub struct FormDefs { + #[serde(default = "default_form_defs_empty")] + pub title: String, + #[serde(default = "default_form_defs_empty")] + pub name: String, + #[serde(default = "default_form_defs_empty")] + pub id: String, + #[serde(default = "default_form_defs_empty")] + pub css: String, + #[serde(default = "default_form_defs_empty")] + pub action: String, + #[serde(default = "default_form_items_empty")] + pub fields: Vec, + #[serde(default = "default_form_overwrite_response")] + pub overwrite_response: bool, +} +impl FromFile for FormDefs { + fn fix_root_path(&mut self, _root_path: String ) { + // if root_path != self.root_path { + // self.root_path = root_path.to_owned(); + // } + // if self.root_path.is_empty() || ! Path::new(&self.root_path).exists() { + // return; + // } + } +} +impl FormDefs { + pub fn load_from(path: &str, name: &str) -> Self { + load_from_file(path,name).unwrap_or_else(|e|{ + println!("FormDefs error: {}",e); + FormDefs::default() + }) + } + pub fn write_data(&self, file_path: &str, data: &str, overwrite: bool) -> std::io::Result<()> { + let check_path = |path: &Path| -> std::io::Result<()> { + if ! Path::new(&path).exists() { + if let Err(e) = std::fs::create_dir(&path) { + return Err(Error::new( ErrorKind::InvalidInput, + format!("Error create path {}: {}",&path.display(), e) + )); + // std::process::exit(2) + } + } + Ok(()) + }; + if file_path.is_empty() || data.is_empty() { + return Err(Error::new( + ErrorKind::InvalidInput, + format!("Error save {}",&file_path) + )); + } + if ! Path::new(&file_path).exists() { + let path = PathBuf::from(&file_path); + if let Some(dir_path) = path.parent() { + if ! Path::new(&dir_path).exists() { + if let Some(parent_dir_path) = dir_path.parent() { + if ! Path::new(&parent_dir_path).exists() { + let res = check_path(&parent_dir_path); + if res.is_err() { return res; } + } + } + let res = check_path(&dir_path); + if res.is_err() { return res; } + } + } + } + if overwrite || ! Path::new(&file_path).exists() { + fs::write(&file_path, data)?; + println!("Overwrite: {}",&file_path); + } else { + let sid_settings_file = fs::OpenOptions::new() + .write(true) + .append(true) // This is needed to append to file + .open(&file_path); + if let Ok(mut file) = sid_settings_file { + file.write_all(data.as_bytes())?; + } + println!("write: {}",&file_path); + } + Ok(()) + } + /* + fn fix_item_path(&mut self, item: String ) -> String { + if !item.is_empty() && ! Path::new(&item).exists() { + format!("{}/{}",&self.root_path,&item) + } else { + item + } + } + pub fn load_items(&mut self) { + self.fix_root_path::(self.root_path.to_owned()); + if !self.path_menu_items.is_empty() { + self.path_menu_items = self.fix_item_path(self.path_menu_items.to_owned()); + let data_menu_items = load_from_file(&self.path_menu_items.to_owned(), "menu_items").unwrap_or_else(|e|{ + error!("Error loading menu_items from {}: {}",&self.path_menu_items,e); + DataMenuItems::default() + }); + self.ui.web_menu_items = data_menu_items.web_menu_items; + } + if !self.path_serv_paths.is_empty() { + self.path_serv_paths = self.fix_item_path(self.path_serv_paths.to_owned()); + let data_serv_paths = load_from_file(&self.path_serv_paths.to_owned(), "serv_paths").unwrap_or_else(|e|{ + error!("Error loading serv_paths from {}: {}",&self.path_serv_paths,e); + DataServPath::default() + }); + self.serv_paths = data_serv_paths.serv_paths; + } + if !self.path_locales_config.is_empty() { + self.path_locales_config = self.fix_item_path(self.path_locales_config.to_owned()); + self.locales = load_dict_from_file(&self.path_locales_config.to_owned(), "locales").unwrap_or_else(|e|{ + error!("Error loading locales from {}: {}",&self.path_locales_config,e); + HashMap::new() + }); + } + if self.users_store_uri.starts_with(FILE_SCHEME) { + self.users_store_uri = format!("{}{}", + FILE_SCHEME, self.fix_item_path(self.users_store_uri.replace(FILE_SCHEME,""))); + } + if self.session_store_uri.starts_with(FILE_SCHEME) { + self.session_store_uri = format!("{}{}", + FILE_SCHEME, self.fix_item_path(self.session_store_uri.replace(FILE_SCHEME,""))); + } + if self.trace_store_uri.starts_with(FILE_SCHEME) { + self.trace_store_uri = format!("{}{}", + FILE_SCHEME, self.fix_item_path(self.trace_store_uri.replace(FILE_SCHEME,""))); + } + self.cert_file = self.fix_item_path(self.cert_file.to_owned()); + self.key_file = self.fix_item_path(self.key_file.to_owned()); + self.templates_path = self.fix_item_path(self.templates_path.to_owned()); + self.defaults_path = self.fix_item_path(self.defaults_path.to_owned()); + + #[cfg(feature = "authstore")] + if self.authz_store_uri.starts_with(FILE_SCHEME) { + self.authz_store_uri = format!("{}{}", + FILE_SCHEME, self.fix_item_path(self.authz_store_uri.replace(FILE_SCHEME,""))); + } + #[cfg(feature = "casbin")] + { + self.authz_model_path = self.fix_item_path(self.authz_model_path.to_owned()); + self.authz_policy_path = self.fix_item_path(self.authz_policy_path.to_owned()); + } + self.paseto.public_path = self.fix_item_path(self.paseto.public_path.to_owned()); + self.paseto.secret_path = self.fix_item_path(self.paseto.secret_path.to_owned()); + (self.paseto.public_data, self.paseto.secret_data) = self.paseto.load_data(); + self.paseto.footer = ConfigPaSeToken::make_footer( + self.paseto.map_footer.to_owned() + ).unwrap_or(Footer::new()); + } + #[allow(dead_code)] + pub fn to_json(&self) -> String { + serde_json::to_string(self).unwrap_or_else(|e|{ + println!("Error to convert Config to json: {}",e); + String::from("") + }) + } + // #[allow(dead_code)] + // pub fn st_html_path(&self) -> &'static str { + // Box::leak(self.html_path.to_owned().into_boxed_str()) + // } + #[allow(dead_code)] + pub fn full_html_url(&self, url: &str) -> String { + format!("{}://{}:{}/{}", + &self.protocol,&self.bind,&self.port, + url + ) + } + */ +} \ No newline at end of file diff --git a/src/defs/openid.rs b/src/defs/openid.rs index 9830eb0..ea2fe5d 100644 --- a/src/defs/openid.rs +++ b/src/defs/openid.rs @@ -12,13 +12,18 @@ use openidconnect::{ CoreProviderMetadata, CoreResponseType, }, + http::{ + HeaderMap, + header::CONTENT_TYPE, + Method, + }, reqwest::async_http_client, HttpRequest, AuthenticationFlow, ClientId, ClientSecret, CsrfToken, IssuerUrl, Nonce, RedirectUrl, Scope, PkceCodeChallenge, PkceCodeVerifier, }; -use axum::{http::method::Method, http::HeaderMap, http::header::CONTENT_TYPE}; +// use axum::{http::method::Method, http::HeaderMap, http::header::CONTENT_TYPE}; use crate::defs::Config; diff --git a/src/defs/req_handler.rs b/src/defs/req_handler.rs index b733eba..553b1ac 100644 --- a/src/defs/req_handler.rs +++ b/src/defs/req_handler.rs @@ -1,4 +1,4 @@ -use axum::http::{header::{HeaderValue},uri::Uri}; +use axum::http::{header::HeaderValue,uri::Uri}; use html_minifier::HTMLMinifier; use std::{ error::Error, @@ -65,6 +65,7 @@ impl<'a> ReqHandler<'a> { context.insert("main_name", &app_dbs.config.ui.main_name); context.insert("title", &app_dbs.config.ui.title); context.insert("subtitle", &app_dbs.config.ui.subtitle); + context.insert("req_settings", &app_dbs.config.req_settings); context.insert("usr_roles", &auth_state.user_roles()); let user_items = User::hash_items(&auth_state.user_items()); context.insert("usr_items", &user_items); @@ -98,16 +99,16 @@ impl<'a> ReqHandler<'a> { pub fn prepare_response(&mut self) { // self.req_header.header.contains_key(axum::http::header::CONTENT_TYPE) if self.req_header.is_browser() || self.req_header.is_wget() { - self.req_header.header.append(axum::http::header::CONTENT_TYPE, + self.req_header.header.append(axum::http::header::CONTENT_TYPE, HeaderValue::try_from("text/html; charset=utf-8") .expect("URI isn't a valid header value") ); } } - pub fn render_template(&mut self,template_file: &str, dflt_content: &str) -> String { + pub fn render_template(&mut self,template_file: &str, dflt_content: &str) -> String { self.context.insert("page", &template_file.replace(".j2","").replace("html/", "").as_str()); if template_file.contains("md") || template_file.contains("sample") || template_file.contains("code") { - self.context.insert("with_code", "true"); + self.context.insert("with_code", "true"); } match self.app_dbs.tera.render(&template_file, &self.context) { Ok(s) => { @@ -125,7 +126,7 @@ impl<'a> ReqHandler<'a> { }, } } else { - s + s } }, Err(e) => { @@ -161,7 +162,7 @@ impl<'a> ReqHandler<'a> { } #[allow(dead_code)] pub fn get_lang(&self, key: &str) -> Local { - Local::get_lang(&self.app_dbs.config.locales, key, &self.app_dbs.config.dflt_lang) + Local::get_lang(&self.app_dbs.config.locales, key, &self.app_dbs.config.dflt_lang) } pub fn new_token(&self) -> String { if self.app_dbs.config.use_token { @@ -236,7 +237,7 @@ impl<'a> ReqHandler<'a> { pub fn otp_generate(&self) -> Result { let mut rng = rand::thread_rng(); let data_byte: [u8; 21] = rng.gen(); - let base32_string = base32::encode(base32::Alphabet::RFC4648 { padding: false }, &data_byte); + let base32_string = base32::encode(base32::Alphabet::Rfc4648 { padding: false }, &data_byte); match self.otp_make(&base32_string, "") { Ok(totp) => Ok(totp), Err(e) => diff --git a/src/defs/req_settings.rs b/src/defs/req_settings.rs index 408d3ea..6db1ad7 100644 --- a/src/defs/req_settings.rs +++ b/src/defs/req_settings.rs @@ -1,5 +1,4 @@ -use serde::{Serialize, Deserialize, Deserializer}; -use std::collections::HashMap; +use serde::{Serialize, Deserialize }; fn default_config_resource() -> String { String::from("") @@ -7,9 +6,6 @@ fn default_config_resource() -> String { fn default_sitewith() -> Vec { Vec::new() } -fn default_config_tpls() -> HashMap { - HashMap::new() -} #[derive(Debug, Clone, Serialize, Deserialize, Default)] pub struct ReqSettings { pub name: String, @@ -19,6 +15,8 @@ pub struct ReqSettings { #[serde(default = "default_config_resource")] pub url: String, #[serde(default = "default_config_resource")] + pub author_url: String, + #[serde(default = "default_config_resource")] pub trace_url: String, #[serde(default = "default_config_resource")] pub title: String, @@ -26,8 +24,6 @@ pub struct ReqSettings { pub subtitle: String, #[serde(default = "default_config_resource")] pub pagetitle: String, - #[serde(default = "default_config_tpls")] - pub tpls: HashMap, #[serde(default = "default_config_resource")] pub sid: String, #[serde(default = "default_sitewith")] diff --git a/src/handlers.rs b/src/handlers.rs index 3586083..541f36e 100644 --- a/src/handlers.rs +++ b/src/handlers.rs @@ -13,6 +13,7 @@ pub(crate) use users::{ users_password_router_handlers, users_settings_router_handlers, users_invite_router_handlers, + forms_router_handlers, }; pub(crate) use other_handlers::{ diff --git a/src/handlers/other_handlers.rs b/src/handlers/other_handlers.rs index a5d972e..06be66b 100644 --- a/src/handlers/other_handlers.rs +++ b/src/handlers/other_handlers.rs @@ -51,12 +51,12 @@ pub async fn add_session_cookie(make: bool, cookies: &Cookies, session_token: &s if result_store.is_empty() { eprintln!("Unable to store session {}", &app_dbs.config.session_store_uri); } else { - let cookie = Cookie::build(SESSION_COOKIE_NAME, result_store.to_owned()) + let cookie = Cookie::build((SESSION_COOKIE_NAME, result_store.to_owned())) // .domain(domain) .path(format!("{}",cookie_path)) .secure(true) .http_only(true) - .finish(); + .build(); if make { cookies.add(cookie); } @@ -171,7 +171,7 @@ pub async fn rewrite_request_uri( // try with email let mut lock = enforcer.write().await; let result = lock.enforce_mut( - vec![ + vec![ name, target_path.to_owned(), req_method.to_owned() @@ -184,7 +184,7 @@ pub async fn rewrite_request_uri( if result { return Ok(next.run(req).await); } let new_uri = format!("{}",serv_path.not_auth); let agent = if let Some(user_agent) = req.headers().get(USER_AGENT) { - user_agent.to_str().unwrap_or("").to_owned() + user_agent.to_str().unwrap_or("").to_owned() } else { String::from("") }; diff --git a/src/handlers/users.rs b/src/handlers/users.rs index 1e16a04..8a7d6ed 100644 --- a/src/handlers/users.rs +++ b/src/handlers/users.rs @@ -2,6 +2,7 @@ mod login_handlers; mod password_handlers; mod settings_handlers; mod invite_handlers; +mod forms_handlers; mod login; pub(crate) use login::login_user; @@ -9,3 +10,4 @@ pub(crate) use login_handlers::users_login_router_handlers; pub(crate) use password_handlers::users_password_router_handlers; pub(crate) use invite_handlers::users_invite_router_handlers; pub(crate) use settings_handlers::users_settings_router_handlers; +pub(crate) use forms_handlers::forms_router_handlers; diff --git a/src/handlers/users/forms_handlers.rs b/src/handlers/users/forms_handlers.rs new file mode 100644 index 0000000..f9cfa7f --- /dev/null +++ b/src/handlers/users/forms_handlers.rs @@ -0,0 +1,207 @@ +use std::sync::Arc; +use axum::{ + http::{ + StatusCode, + Uri, + header::HeaderMap, + }, + Json, + routing::{get,post}, + Extension, + extract::ConnectInfo, + response::{IntoResponse,Response,Redirect}, + Router, +}; +use serde_json::{json,Value}; +use tower_cookies::Cookies; +//use log::{trace,error}; +use log::error; + +use crate::{ + route, + defs::{ + AppDBs, + ReqHandler, + ReqHeaderMap, + Random, + AppConnectInfo, + FormDefs, + }, + users::{ + User, +// UserData, +// UserItem, + OpenidUser, + }, + handlers::{ +// add_session_cookie, + get_auth_state, +// is_allowed, + }, +}; + +pub fn forms_router_handlers() -> Router { + async fn form_handler( + header: HeaderMap, + uri: Uri, + Extension(app_dbs): Extension>, + Extension(cookies): Extension, + Extension(random): Extension, + ConnectInfo(app_connect_info): ConnectInfo, + axum::extract::Path(data): axum::extract::Path, + ) -> Response { + let auth_state = get_auth_state(true, &cookies, &app_dbs).await; + if auth_state.session.is_none() { + return Redirect::temporary( &format!("/login?o={}",uri.path().to_string())).into_response(); + } + let mut req_handler = ReqHandler::new( + ReqHeaderMap::new(header, &format!("{}",&uri.path().to_string()), &app_connect_info), + &app_dbs, + &uri, + &auth_state, + &random, + "user_settings_handler" + ); + let mut res_headers = HeaderMap::new(); + if req_handler.req_header.is_browser() { + res_headers.append(axum::http::header::CONTENT_TYPE,"text/html; charset=utf-8".parse().unwrap()); + } + let user_id = auth_state.user_id(); + if user_id.is_empty() { + let _ = req_handler.trace_req(format!("user not id found")); + // User not exists + return Redirect::temporary( &format!("/")).into_response(); + } + let user_sel = User::select("id", &user_id, true, &app_dbs.user_store).await.unwrap_or_default(); + if user_sel.name.is_empty() { + let _ = req_handler.trace_req(format!("Edit user id '{}' not found ",&user_id)); + // User not exists + return Redirect::temporary( &format!("/")).into_response(); + } + let openid_sel = OpenidUser::list_selection("userid", &user_sel.id.to_string(), &app_dbs.user_store, true,false, "|").await.unwrap_or_else(|e| { + error!("Error list selection {}: {}", &user_sel.name, e); + Vec::new() + }); + + let form_defs = FormDefs::load_from(&format!("{}/{}.toml",&app_dbs.config.forms_path, &data), &data); + + req_handler.context.insert("openid_sel", &openid_sel); + let openid_sel_appkeys = openid_sel.iter().map(|id| id.appkey.to_string()).collect::>().join(","); + req_handler.context.insert("openid_sel_appkeys", &openid_sel_appkeys); + req_handler.context.insert("with_menu", "1"); + req_handler.context.insert("user", &user_sel); + req_handler.context.insert("admin_fields", &app_dbs.config.admin_fields); + req_handler.context.insert("totp_mode", &format!("{}",&app_dbs.config.totp_mode)); + // let user_items = User::hash_items(&user_sel.items); + // req_handler.context.insert("usr_items", &user_items); + req_handler.context.insert("no_edit", "true"); + req_handler.context.insert("edit_target", "main"); + req_handler.context.insert("form_defs", &form_defs); + let result = if let Some(tpl) = app_dbs.config.tpls.get("form") { + req_handler.render_template(&tpl,"form") + } else { + String::from("user settings") + }; + let _ = req_handler.trace_req(format!("User '{}' form",&user_sel.id)); + ( + res_headers, + result.to_owned() + ).into_response() + } + async fn post_form_handler( + header: HeaderMap, + uri: Uri, + Extension(app_dbs): Extension>, + Extension(cookies): Extension, + Extension(random): Extension, + ConnectInfo(app_connect_info): ConnectInfo, + axum::extract::Path(form): axum::extract::Path, + Json(form_data): Json, + + ) -> Response { + dbg!(&form_data); + let str_data=serde_json::to_string(&form_data).unwrap_or_else(|e|{ + println!("from_data error: {}",e); + String::from("") + }); + dbg!(&str_data); + let auth_state = get_auth_state(true, &cookies, &app_dbs).await; + if auth_state.session.is_none() { + return Redirect::temporary( &format!("/login?o={}",uri.path().to_string())).into_response(); + } + let req_handler = ReqHandler::new( + ReqHeaderMap::new(header, &format!("{}",&uri.path().to_string()), &app_connect_info), + &app_dbs, + &uri, + &auth_state, + &random, + "user_settings_handler" + ); + //let mut res_headers = HeaderMap::new(); + //if req_handler.req_header.is_browser() { + // res_headers.append(axum::http::header::CONTENT_TYPE,"text/html; charset=utf-8".parse().unwrap()); + //} + let user_id = auth_state.user_id(); + if user_id.is_empty() { + let _ = req_handler.trace_req(format!("user not id found")); + // User not exists + return Redirect::temporary( &format!("/")).into_response(); + } + let user_sel = User::select("id", &user_id, true, &app_dbs.user_store).await.unwrap_or_default(); + if user_sel.name.is_empty() { + let _ = req_handler.trace_req(format!("Edit user id '{}' not found ",&user_id)); + // User not exists + return Redirect::temporary( &format!("/")).into_response(); + } + let response_path = format!("{}/{}/{}.json",&app_dbs.config.forms_responses_path,&user_id,&form); + let form_defs = FormDefs::load_from(&format!("{}/{}.toml",&app_dbs.config.forms_path, &form), &form); + let write_res = match form_defs.write_data(&response_path, &str_data, form_defs.overwrite_response) { + Ok(_) => "done", + Err(e) => { + println!("Error write {}: {}",&response_path, e); + "error" + } + }; + let json_result = json!( + { + "form": form, + "status": write_res, + } + ); + let result=serde_json::to_string(&json_result).unwrap_or_default(); + ( + StatusCode::OK, + req_handler.req_header.header, + result + ).into_response() + // Redirect::temporary( &format!("/")).into_response() + /* + //let form_defs = FormDefs::load_from(&format!("{}/{}.toml",&app_dbs.config.forms_path, &data), &data); + //req_handler.context.insert("openid_sel", &openid_sel); + //let openid_sel_appkeys = openid_sel.iter().map(|id| id.appkey.to_string()).collect::>().join(","); + ..req_handler.context.insert("openid_sel_appkeys", &openid_sel_appkeys); + req_handler.context.insert("with_menu", "1"); + req_handler.context.insert("user", &user_sel); + req_handler.context.insert("admin_fields", &app_dbs.config.admin_fields); + req_handler.context.insert("totp_mode", &format!("{}",&app_dbs.config.totp_mode)); + // let user_items = User::hash_items(&user_sel.items); + // req_handler.context.insert("usr_items", &user_items); + req_handler.context.insert("no_edit", "true"); + req_handler.context.insert("edit_target", "main"); + req_handler.context.insert("form_defs", &form_defs); + let result = if let Some(tpl) = app_dbs.config.tpls.get("form") { + req_handler.render_template(&tpl,"form") + } else { + String::from("user settings") + }; + let _ = req_handler.trace_req(format!("User '{}' form",&user_sel.id)); + ( + res_headers, + result.to_owned() + ).into_response() + */ + } + route( + "/form/:item", get(form_handler)) + .route("/form/:item", post(post_form_handler)) +} diff --git a/src/handlers/users/login_handlers.rs b/src/handlers/users/login_handlers.rs index 210a7f4..cd4d980 100644 --- a/src/handlers/users/login_handlers.rs +++ b/src/handlers/users/login_handlers.rs @@ -18,7 +18,6 @@ use log::{trace,error}; use crate::{ SESSION_COOKIE_NAME, - DEFAULT_ROLES, route, defs::{ AppDBs, @@ -90,8 +89,10 @@ pub fn users_login_router_handlers() -> Router { if req_handler.req_header.is_browser() { res_headers.append(axum::http::header::CONTENT_TYPE,"text/html; charset=utf-8".parse().unwrap()); } + req_handler.context.insert("default_roles", &app_dbs.config.default_roles.join(",")); req_handler.context.insert("password_score", &app_dbs.config.password_score); req_handler.context.insert("totp_mode", &format!("{}",&app_dbs.config.totp_mode)); + req_handler.context.insert("openid_auths", &app_dbs.config.openid_auths.clone().into_keys().collect::>()); // req_handler.context.insert("with_menu", "1"); if app_dbs.config.totp_mode != TotpMode::No { match req_handler.otp_generate() { @@ -229,7 +230,7 @@ pub fn users_login_router_handlers() -> Router { let roles = if ! user_data.roles.is_empty() { user_data.roles.to_owned() } else { - DEFAULT_ROLES.to_owned() + app_dbs.config.default_roles.clone().join(",") }; let is_admin = if user_data.id == "A" || get_total_users(&app_dbs.user_store).await < 1 { true @@ -256,6 +257,7 @@ pub fn users_login_router_handlers() -> Router { status: UserStatus::Created, items: User::json_items(new_items), isadmin: is_admin, + openids: user_data.openids.to_owned(), roles, }; let usr_sel = User::select("name", &user_data.name, false, &app_dbs.user_store).await.unwrap_or_default(); @@ -381,7 +383,8 @@ pub fn users_login_router_handlers() -> Router { match openid_conf.client().await { Ok(openid_cli) => openid_cli.get_data(&data), Err(e) => { - error!("Error openid client data found for {} handler: {}", &data, e); + dbg!("Error openid client data found for {} handler: {}", &data, e); + // error!("Error openid client data found for {} handler: {}", &data, e); (String::from(""), String::from("/login")) } } @@ -390,6 +393,7 @@ pub fn users_login_router_handlers() -> Router { error!("Error no openid client found for: {}", &data); (String::from(""), String::from("/login")) }; + dbg!(&str_user_data, &url); let session_token = req_handler.new_token(); let session_cookie = add_session_cookie(true,&cookies, &session_token, &str_user_data, 0, &app_dbs, "/").await; if app_dbs.config.verbose > 1 { println!("session cookie: {}", &session_cookie) }; diff --git a/src/handlers/users/settings_handlers.rs b/src/handlers/users/settings_handlers.rs index 51ad4da..40931fa 100644 --- a/src/handlers/users/settings_handlers.rs +++ b/src/handlers/users/settings_handlers.rs @@ -122,6 +122,7 @@ pub fn users_settings_router_handlers() -> Router { // req_handler.context.insert("usr_items", &user_items); req_handler.context.insert("no_edit", "true"); req_handler.context.insert("edit_target", "main"); + req_handler.context.insert("openid_auths", &app_dbs.config.openid_auths.clone().into_keys().collect::>()); let result = if let Some(tpl) = app_dbs.config.tpls.get("user_settings") { req_handler.render_template(&tpl,"user setting") } else { @@ -186,6 +187,7 @@ pub fn users_settings_router_handlers() -> Router { req_handler.context.insert("edit_target", &data); req_handler.context.insert("admin_fields", &app_dbs.config.admin_fields); req_handler.context.insert("totp_mode", &format!("{}",&app_dbs.config.totp_mode)); + req_handler.context.insert("openid_auths", &app_dbs.config.openid_auths.clone().into_keys().collect::>()); if data == "totp" && app_dbs.config.totp_mode != TotpMode::No { if !user_sel.otp_base32.is_empty() { match req_handler.otp_make(&user_sel.otp_base32, &user_sel.otp_defs) { @@ -329,7 +331,7 @@ pub fn users_settings_router_handlers() -> Router { user_sel.otp_auth_url = String::from(""); user_sel.otp_defs = String::from(""); } - let user_openids = user_data.opendis.to_owned(); + let user_openid = user_data.openids.to_owned(); user_sel.from_data(user_data); let new_id= user_sel.id.to_string(); let user_data = user_sel.session_data(); @@ -339,7 +341,7 @@ pub fn users_settings_router_handlers() -> Router { let session_cookie = add_session_cookie(true,&cookies, &session_token, &user_data, 0, &app_dbs, "/").await; if app_dbs.config.verbose > 1 { println!("session cookie: {}", &session_cookie) }; let _ = req_handler.trace_req(format!("User '{}' updated",&user_id)); - OpenidUser::sync_ids(&new_id, &user_openids, &app_dbs.user_store).await; + OpenidUser::sync_ids(&new_id, &user_openid, &app_dbs.user_store).await; let result =String::from("OK"); ( req_handler.req_header.header, diff --git a/src/login_password.rs b/src/login_password.rs index 5d97327..6733693 100644 --- a/src/login_password.rs +++ b/src/login_password.rs @@ -17,8 +17,8 @@ // )] extern crate alloc; -#[cfg(feature = "std")] -extern crate std; +//#[cfg(feature = "std")] +//extern crate std; use alloc::string::{String, ToString}; use core::fmt; diff --git a/src/main.rs b/src/main.rs index 3cd837e..62b8fa9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -74,6 +74,7 @@ use handlers::{ admin_router_handlers, users_router_handlers, pages_router_handlers, + forms_router_handlers, }; use crate::tools::get_socket_addr; @@ -99,7 +100,6 @@ pub const USERS_TABLENAME: &str = "users"; pub const OPENID_USERS_TABLENAME: &str = "openid_users"; pub const USERS_FILESTORE: &str = "users"; pub const OPENID_USERS_FILESTORE: &str = "openid_users"; -pub const DEFAULT_ROLES: &str = "user"; pub fn route(path: &str, method_router: MethodRouter) -> Router { Router::new().route(path, method_router) @@ -288,6 +288,7 @@ async fn main() { .merge(users_router_handlers()) .merge(admin_router_handlers()) .merge(pages_router_handlers()) + .merge(forms_router_handlers()) .layer(ServiceBuilder::new().layer(middleware)) .layer(CookieManagerLayer::new()) .layer(Extension(app_dbs)) @@ -297,7 +298,7 @@ async fn main() { ; if config.verbose > 2 { dbg!("{:?}",&origins); } if config.allow_origin.len() > 0 { - web_router = web_router.layer(CorsLayer::new() + web_router = web_router.layer(CorsLayer::new() .allow_origin(origins) .allow_methods(vec![Method::GET, Method::POST]) .allow_headers(tower_http::cors::Any) @@ -313,10 +314,10 @@ async fn main() { let listener = tokio::net::TcpListener::bind(addr).await.unwrap(); // web_router.into_make_service_with_connect_info::() axum::serve(listener, web_router - .into_make_service_with_connect_info::() - ) - .await - .unwrap(); + .into_make_service_with_connect_info::() + ) + .await + .unwrap(); } else { let tls_config = RustlsConfig::from_pem_file( PathBuf::from(&config.cert_file), @@ -331,10 +332,8 @@ async fn main() { web_router = web_router.layer(TraceLayer::new_for_http()); } axum_server::bind_rustls(addr, tls_config) - .serve( - // web_router.layer(TraceLayer::new_for_http()) - web_router - .into_make_service_with_connect_info::() + .serve( + web_router.into_make_service_with_connect_info::() ) .await .unwrap(); diff --git a/src/tools.rs b/src/tools.rs index 4a7a4ce..6426e12 100644 --- a/src/tools.rs +++ b/src/tools.rs @@ -1,5 +1,6 @@ use std::net::{IpAddr, Ipv4Addr, SocketAddr, ToSocketAddrs}; -use chrono::NaiveDateTime; +use chrono::DateTime; +//use chrono::NaiveDateTime; //use chrono::{DateTime,Local, Utc,NaiveDateTime}; //use std::time::{UNIX_EPOCH, Duration}; @@ -7,13 +8,13 @@ pub fn get_socket_addr(bind: &str, port: u16) -> SocketAddr { let url = format!("{}:{}",&bind,&port); match url.to_socket_addrs() { Ok(addrs_op) => if let Some(addr) = addrs_op.to_owned().next() { - addr - } else { - SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), port.to_owned()) - } + addr + } else { + SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), port.to_owned()) + } Err(e) => { - eprintln!("Evironment load error: {} {}", e, url); - SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), port.to_owned()) + eprintln!("Evironment load error: {} {}", e, url); + SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), port.to_owned()) } } } @@ -50,7 +51,7 @@ pub fn path_timestamp(filepath: &str) -> u32 { pub fn str_date_from_timestamp(timestamp: &str) -> String { if timestamp.is_empty() { return String::from(""); } let val: i64 = timestamp.parse().unwrap_or_default(); - let datetime = NaiveDateTime::from_timestamp_opt(val, 0).unwrap_or_default(); // (Local::now()); + let datetime = DateTime::from_timestamp(val, 0).unwrap_or_default(); // (Local::now()); /* let naive_utc = dt.naive_utc(); let offset = dt.offset().clone(); @@ -63,5 +64,5 @@ pub fn str_date_from_timestamp(timestamp: &str) -> String { // let str_timestamp = UNIX_EPOCH + Duration::from_millis(val); // let datetime = DateTime::::from(str_timestamp); */ - datetime.format("%Y-%m-%d %H:%M:%S").to_string() + datetime.format("%Y-%m-%d %H:%M:%S").to_string() } diff --git a/src/users/openid.rs b/src/users/openid.rs index db17e25..c04c383 100644 --- a/src/users/openid.rs +++ b/src/users/openid.rs @@ -489,6 +489,11 @@ impl OpenidUser { println!("Error delete {}: {}",&itm.appkey,e); false }); + } else { + let _res = OpenidUser::delete(itm.id, &store).await.unwrap_or_else(|e| { + println!("Error delete {}: {}",&itm.appkey,e); + false + }); } } } diff --git a/src/users/user.rs b/src/users/user.rs index 7aa5aef..667d22d 100644 --- a/src/users/user.rs +++ b/src/users/user.rs @@ -6,6 +6,7 @@ use std::{ fmt, collections::HashMap, }; +use zxcvbn::zxcvbn; // use std::{ // // sync::Arc, // fmt::Debug, @@ -37,7 +38,7 @@ use crate::{ const DISPLAY_SEPARATOR: &str = "="; fn default_user_status() -> UserStatus { - UserStatus::default() + UserStatus::default() } #[derive(Debug, Clone, PartialEq, PartialOrd, Serialize, Deserialize,Default)] @@ -60,6 +61,7 @@ pub struct User { pub status: UserStatus, pub items: String, pub isadmin: bool, + pub openids: String, } impl fmt::Display for User { @@ -86,7 +88,8 @@ Created{} {} Last access{} {} Status{} {} Items{} {} -IsAdmin{} {}", +IsAdmin{} {} +OpenIds{} {}", sep, self.id, sep, self.name, sep, self.fullname, sep, self.description, @@ -101,7 +104,8 @@ IsAdmin{} {}", sep, self.created, sep, self.lastaccess, sep, self.status, sep, self.items, - sep, self.isadmin + sep, self.isadmin, + sep, self.openids ); write!(f, "{}", content) } @@ -128,6 +132,7 @@ impl Entry for User { status: UserStatus::from_str(&parts[14].to_string()), items: parts[15].to_string(), isadmin: if parts[16] == "TRUE" { true } else { false}, + openids: parts[17].to_string(), }) } } @@ -142,8 +147,8 @@ impl User { otp_base32, otp_auth_url, otp_defs, - roles, created, lastaccess, status, items, isadmin - ) VALUES ( ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", USERS_TABLENAME).as_str() + roles, created, lastaccess, status, items, openids, isadmin + ) VALUES ( ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", USERS_TABLENAME).as_str() ) .bind(self.name) .bind(self.fullname) @@ -160,6 +165,7 @@ impl User { .bind(self.lastaccess) .bind(format!("{}",self.status)) .bind(self.items) + .bind(self.openids) .bind(self.isadmin) .execute(pool).await?; Ok(query_result.last_insert_id().unwrap_or_default()) @@ -236,6 +242,7 @@ impl User { lastaccess, status, items: row.try_get("items")?, + openids: row.try_get("openids")?, isadmin: row.try_get("isadmin")?, }) } else { @@ -258,6 +265,7 @@ impl User { "TRUE" => it.isadmin, _ => !it.isadmin, }, + "openids" => it.openids == value, _ => false, } ) @@ -279,12 +287,12 @@ impl User { ) .bind(id) .execute(pool).await?; - Ok(query_result.rows_affected() > 0) + Ok(query_result.rows_affected() > 0) }, UserStore::File(file_path) => { let new_entries: Vec = Entries::::new(&file_path).filter(|it| it.id != id).map(|user| - user.line_format() + user.line_format() ).collect(); let entries = Entries::::new(&file_path); match entries.write(&new_entries) { @@ -309,8 +317,9 @@ impl User { created = ?, lastaccess = ?, status = ?, items = ?, + openids = ?, isadmin = ? - WHERE id = ? ", + WHERE id = ? ", USERS_TABLENAME ); let query_result = sqlx::query( @@ -331,10 +340,11 @@ impl User { .bind(self.lastaccess) .bind(format!("{}",self.status)) .bind(self.items) + .bind(self.openids) .bind(self.isadmin) .bind(self.id) .execute(pool).await?; - Ok(query_result.rows_affected() > 0) + Ok(query_result.rows_affected() > 0) }, UserStore::File(file_path) => { let new_entries: Vec = Entries::new(&file_path).map(|user: User|{ @@ -357,7 +367,7 @@ impl User { } pub fn show(&self, sep: &str) { let content = if sep.is_empty() { - format!( "{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}", + format!( "{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}", self.id, self.name, self.fullname, self.description, @@ -373,7 +383,8 @@ impl User { self.lastaccess, self.status, self.items, - self.isadmin + self.isadmin, + self.openids ) } else { format!("{}",&self).replace(DISPLAY_SEPARATOR, sep) @@ -425,6 +436,7 @@ impl User { status, items: row.try_get("items")?, isadmin: row.try_get("isadmin")?, + openids: row.try_get("openids")?, }; if show { user.show(sep); } usrs.push(user); @@ -461,7 +473,7 @@ impl User { } } fn line_format(self) -> String { - format!( "{}:{}:{}:{}:{}:{}:{}:{}:{}:{}:{}:{}:{}:{}:{}:{}:{}", + format!( "{}:{}:{}:{}:{}:{}:{}:{}:{}:{}:{}:{}:{}:{}:{}:{}:{}:{}", self.id, self.name, self.fullname, self.description, @@ -476,7 +488,8 @@ impl User { self.created, self.lastaccess, self.status, self.items, - self.isadmin + self.isadmin, + self.openids ) } pub fn hash_items(items: &str) -> HashMap { @@ -527,6 +540,9 @@ impl User { } self.items = Self::json_items(items_hash); } + if !user_data.openids.is_empty() { + self.openids = user_data.openids.to_owned(); + } } pub fn disable_totp(&mut self) { self.otp_base32 = String::from(""); @@ -587,37 +603,23 @@ impl User { ) } pub fn estimate_password(word: &str) -> String { - match zxcvbn::zxcvbn(word, &[]) { - Ok(estimate) => { - if let Some(feedback) = estimate.feedback() { - let arr_suggestions: Vec = feedback.suggestions().iter().map(|s| format!("{}",s)).collect(); - let suggestions = arr_suggestions.join("\n"); - if let Some(warning) = feedback.warning() { - let warning = format!("{}", warning); - format!("{}|{}|{}",estimate.score(),suggestions,warning) - } else { - format!("{}|{}|",estimate.score(),suggestions) - } - } else { - format!("{}||",estimate.score()) - } - }, - Err(e) => { - println!("Error password strength estimator: {}", e); - String::from("-1|||") + let estimate = zxcvbn(word, &[]); + if let Some(feedback) = estimate.feedback() { + let arr_suggestions: Vec = feedback.suggestions().iter().map(|s| format!("{}",s)).collect(); + let suggestions = arr_suggestions.join("\n"); + if let Some(warning) = feedback.warning() { + let warning = format!("{}", warning); + format!("{}|{}|{}",estimate.score(),suggestions,warning) + } else { + format!("{}|{}|",estimate.score(),suggestions) } + } else { + format!("{}||",estimate.score()) } } pub fn password_score(word: &str) -> u8 { - match zxcvbn::zxcvbn(word, &[]) { - Ok(estimate) => { - estimate.score() - }, - Err(e) => { - println!("Error password strength estimator: {}", e); - 0 - } - } + let estimate = zxcvbn(word, &[]); + u8::from(estimate.score()) } #[allow(dead_code)] pub fn has_auth_role(&self, auth_roles: Vec) -> bool { diff --git a/src/users/userdata.rs b/src/users/userdata.rs index 77da15c..c06c6d5 100644 --- a/src/users/userdata.rs +++ b/src/users/userdata.rs @@ -57,7 +57,7 @@ pub struct UserData { #[serde(default = "default_items")] pub items: HashMap, #[serde(default = "default_empty")] - pub opendis: String, + pub openids: String, } // impl UserData { // pub fn from_id(id: String, _app_dbs: &AppDBs) -> Self {