chore: update to new crates versions and fix sources for it
Some checks are pending
Install / Cargo install on ubuntu-latest (push) Waiting to run
Some checks are pending
Install / Cargo install on ubuntu-latest (push) Waiting to run
This commit is contained in:
parent
7fe3d25788
commit
8ba9afbce3
74
Cargo.toml
74
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"] }
|
||||
|
@ -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";
|
||||
|
@ -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<IncomingStream<'_>> 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<SocketAddr> 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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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<String> {
|
||||
fn default_auth_roles() -> Vec<String> {
|
||||
Vec::new()
|
||||
}
|
||||
fn default_roles() -> Vec<String> {
|
||||
Vec::new()
|
||||
}
|
||||
fn default_sub_menu_items() -> Vec<SubMenuItem> {
|
||||
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<String,OpenidConf> { 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<String>,
|
||||
#[serde(default = "default_roles")]
|
||||
pub default_roles: Vec<String>,
|
||||
#[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<String,OpenidConf>,
|
||||
#[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 {
|
||||
|
247
src/defs/form_defs.rs
Normal file
247
src/defs/form_defs.rs
Normal file
@ -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<FormSelectItem> {
|
||||
Vec::new()
|
||||
}
|
||||
fn default_form_border() -> bool {
|
||||
false
|
||||
}
|
||||
fn default_form_required() -> bool {
|
||||
false
|
||||
}
|
||||
fn default_form_items_empty() -> Vec<FormField> {
|
||||
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<FormSelectItem>,
|
||||
#[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<FormField>,
|
||||
#[serde(default = "default_form_overwrite_response")]
|
||||
pub overwrite_response: bool,
|
||||
}
|
||||
impl FromFile for FormDefs {
|
||||
fn fix_root_path<Config>(&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::<Config>(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
|
||||
)
|
||||
}
|
||||
*/
|
||||
}
|
@ -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;
|
||||
|
||||
|
@ -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<TOTP> {
|
||||
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) =>
|
||||
|
@ -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<String> {
|
||||
Vec::new()
|
||||
}
|
||||
fn default_config_tpls() -> HashMap<String,String> {
|
||||
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<String,String>,
|
||||
#[serde(default = "default_config_resource")]
|
||||
pub sid: String,
|
||||
#[serde(default = "default_sitewith")]
|
||||
|
@ -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::{
|
||||
|
@ -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("")
|
||||
};
|
||||
|
@ -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;
|
||||
|
207
src/handlers/users/forms_handlers.rs
Normal file
207
src/handlers/users/forms_handlers.rs
Normal file
@ -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<Arc<AppDBs>>,
|
||||
Extension(cookies): Extension<Cookies>,
|
||||
Extension(random): Extension<Random>,
|
||||
ConnectInfo(app_connect_info): ConnectInfo<AppConnectInfo>,
|
||||
axum::extract::Path(data): axum::extract::Path<String>,
|
||||
) -> 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::<Vec<String>>().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<Arc<AppDBs>>,
|
||||
Extension(cookies): Extension<Cookies>,
|
||||
Extension(random): Extension<Random>,
|
||||
ConnectInfo(app_connect_info): ConnectInfo<AppConnectInfo>,
|
||||
axum::extract::Path(form): axum::extract::Path<String>,
|
||||
Json(form_data): Json<Value>,
|
||||
|
||||
) -> 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::<Vec<String>>().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))
|
||||
}
|
@ -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::<Vec<String>>());
|
||||
// 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) };
|
||||
|
@ -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::<Vec<String>>());
|
||||
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::<Vec<String>>());
|
||||
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,
|
||||
|
@ -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;
|
||||
|
19
src/main.rs
19
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::<SocketAddr>()
|
||||
axum::serve(listener, web_router
|
||||
.into_make_service_with_connect_info::<AppConnectInfo>()
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
.into_make_service_with_connect_info::<AppConnectInfo>()
|
||||
)
|
||||
.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::<AppConnectInfo>()
|
||||
.serve(
|
||||
web_router.into_make_service_with_connect_info::<AppConnectInfo>()
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
19
src/tools.rs
19
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::<Utc>::from(str_timestamp);
|
||||
*/
|
||||
datetime.format("%Y-%m-%d %H:%M:%S").to_string()
|
||||
datetime.format("%Y-%m-%d %H:%M:%S").to_string()
|
||||
}
|
||||
|
@ -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
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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<String> =
|
||||
Entries::<User>::new(&file_path).filter(|it| it.id != id).map(|user|
|
||||
user.line_format()
|
||||
user.line_format()
|
||||
).collect();
|
||||
let entries = Entries::<User>::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<String> = 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<String,String> {
|
||||
@ -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<String> = 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<String> = 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<String>) -> bool {
|
||||
|
@ -57,7 +57,7 @@ pub struct UserData {
|
||||
#[serde(default = "default_items")]
|
||||
pub items: HashMap<String,String>,
|
||||
#[serde(default = "default_empty")]
|
||||
pub opendis: String,
|
||||
pub openids: String,
|
||||
}
|
||||
// impl UserData {
|
||||
// pub fn from_id(id: String, _app_dbs: &AppDBs) -> Self {
|
||||
|
Loading…
Reference in New Issue
Block a user