From 235ff447367ec37adcef52921350271b3c5b9378 Mon Sep 17 00:00:00 2001 From: BlackDex Date: Tue, 19 Jan 2021 17:55:21 +0100 Subject: [PATCH] Updated the admin interface Mostly updated the admin interface, also some small other items. - Added more diagnostic information to (hopefully) decrease issue reporting, or at least solve them quicker. - Added an option to generate a support string which can be used to copy/paste on the forum or during the creation of an issue. It will try to hide the sensitive information automatically. - Changed the `Created At` and `Last Active` info to be in a column and able to sort them in the users overview. - Some small layout changes. - Updated javascript and css files to the latest versions available. - Decreased the png file sizes using `oxipng` - Updated target='_blank' links to have rel='noreferrer' to prevent javascript window.opener modifications. --- src/api/admin.rs | 100 +++++++++---- src/api/core/mod.rs | 2 +- src/config.rs | 75 ++++++++-- src/main.rs | 2 +- src/static/images/hibp.png | Bin 9890 -> 9419 bytes src/static/images/logo-gray.png | Bin 5901 -> 5414 bytes src/static/images/mail-github.png | Bin 1349 -> 1326 bytes src/static/images/shield-white.png | Bin 1905 -> 1824 bytes src/static/scripts/bootstrap-native.js | 55 +++++--- src/static/scripts/bootstrap.css | 11 +- src/static/scripts/datatables.css | 19 +-- src/static/scripts/datatables.js | 20 ++- src/static/templates/admin/base.hbs | 5 +- src/static/templates/admin/diagnostics.hbs | 139 ++++++++++++++++++- src/static/templates/admin/login.hbs | 2 +- src/static/templates/admin/organizations.hbs | 2 +- src/static/templates/admin/settings.hbs | 2 +- src/static/templates/admin/users.hbs | 57 ++++++-- 18 files changed, 383 insertions(+), 108 deletions(-) diff --git a/src/api/admin.rs b/src/api/admin.rs index 1d6f5f72..857850ad 100644 --- a/src/api/admin.rs +++ b/src/api/admin.rs @@ -1,8 +1,9 @@ use once_cell::sync::Lazy; use serde::de::DeserializeOwned; use serde_json::Value; -use std::process::Command; +use std::{env, process::Command, time::Duration}; +use reqwest::{blocking::Client, header::USER_AGENT}; use rocket::{ http::{Cookie, Cookies, SameSite}, request::{self, FlashMessage, Form, FromRequest, Outcome, Request}, @@ -18,7 +19,7 @@ use crate::{ db::{backup_database, models::*, DbConn, DbConnType}, error::{Error, MapResult}, mail, - util::{get_display_size, format_naive_datetime_local}, + util::{format_naive_datetime_local, get_display_size}, CONFIG, }; @@ -47,9 +48,20 @@ pub fn routes() -> Vec { users_overview, organizations_overview, diagnostics, + get_diagnostics_config ] } +static DB_TYPE: Lazy<&str> = Lazy::new(|| { + DbConnType::from_url(&CONFIG.database_url()) + .map(|t| match t { + DbConnType::sqlite => "SQLite", + DbConnType::mysql => "MySQL", + DbConnType::postgresql => "PostgreSQL", + }) + .unwrap_or("Unknown") +}); + static CAN_BACKUP: Lazy = Lazy::new(|| { DbConnType::from_url(&CONFIG.database_url()) .map(|t| t == DbConnType::sqlite) @@ -307,7 +319,8 @@ fn users_overview(_token: AdminToken, conn: DbConn) -> ApiResult> { None => json!("Never") }; usr - }).collect(); + }) + .collect(); let text = AdminTemplateData::users(users_json).render()?; Ok(Html(text)) @@ -362,14 +375,16 @@ fn update_revision_users(_token: AdminToken, conn: DbConn) -> EmptyResult { #[get("/organizations/overview")] fn organizations_overview(_token: AdminToken, conn: DbConn) -> ApiResult> { let organizations = Organization::get_all(&conn); - let organizations_json: Vec = organizations.iter().map(|o| { + let organizations_json: Vec = organizations.iter() + .map(|o| { let mut org = o.to_json(); org["user_count"] = json!(UserOrganization::count_by_org(&o.uuid, &conn)); org["cipher_count"] = json!(Cipher::count_by_org(&o.uuid, &conn)); org["attachment_count"] = json!(Attachment::count_by_org(&o.uuid, &conn)); org["attachment_size"] = json!(get_display_size(Attachment::size_by_org(&o.uuid, &conn) as i32)); org - }).collect(); + }) + .collect(); let text = AdminTemplateData::organizations(organizations_json).render()?; Ok(Html(text)) @@ -391,77 +406,104 @@ struct GitCommit { } fn get_github_api(url: &str) -> Result { - use reqwest::{blocking::Client, header::USER_AGENT}; - use std::time::Duration; let github_api = Client::builder().build()?; - Ok( - github_api.get(url) + Ok(github_api + .get(url) .timeout(Duration::from_secs(10)) .header(USER_AGENT, "Bitwarden_RS") .send()? .error_for_status()? - .json::()? - ) + .json::()?) +} + +fn has_http_access() -> bool { + let http_access = Client::builder().build().unwrap(); + + match http_access + .head("https://github.com/dani-garcia/bitwarden_rs") + .timeout(Duration::from_secs(10)) + .header(USER_AGENT, "Bitwarden_RS") + .send() + { + Ok(r) => r.status().is_success(), + _ => false, + } } #[get("/diagnostics")] fn diagnostics(_token: AdminToken, _conn: DbConn) -> ApiResult> { - use std::net::ToSocketAddrs; - use chrono::prelude::*; use crate::util::read_file_string; + use chrono::prelude::*; + use std::net::ToSocketAddrs; + // Get current running versions let vault_version_path = format!("{}/{}", CONFIG.web_vault_folder(), "version.json"); let vault_version_str = read_file_string(&vault_version_path)?; let web_vault_version: WebVaultVersion = serde_json::from_str(&vault_version_str)?; - let github_ips = ("github.com", 0).to_socket_addrs().map(|mut i| i.next()); - let (dns_resolved, dns_ok) = match github_ips { - Ok(Some(a)) => (a.ip().to_string(), true), - _ => ("Could not resolve domain name.".to_string(), false), + // Execute some environment checks + let running_within_docker = std::path::Path::new("/.dockerenv").exists(); + let has_http_access = has_http_access(); + let uses_proxy = env::var_os("HTTP_PROXY").is_some() + || env::var_os("http_proxy").is_some() + || env::var_os("HTTPS_PROXY").is_some() + || env::var_os("https_proxy").is_some(); + + // Check if we are able to resolve DNS entries + let dns_resolved = match ("github.com", 0).to_socket_addrs().map(|mut i| i.next()) { + Ok(Some(a)) => a.ip().to_string(), + _ => "Could not resolve domain name.".to_string(), }; - // If the DNS Check failed, do not even attempt to check for new versions since we were not able to resolve github.com - let (latest_release, latest_commit, latest_web_build) = if dns_ok { + // If the HTTP Check failed, do not even attempt to check for new versions since we were not able to connect with github.com anyway. + // TODO: Maybe we need to cache this using a LazyStatic or something. Github only allows 60 requests per hour, and we use 3 here already. + let (latest_release, latest_commit, latest_web_build) = if has_http_access { ( match get_github_api::("https://api.github.com/repos/dani-garcia/bitwarden_rs/releases/latest") { Ok(r) => r.tag_name, - _ => "-".to_string() + _ => "-".to_string(), }, match get_github_api::("https://api.github.com/repos/dani-garcia/bitwarden_rs/commits/master") { Ok(mut c) => { c.sha.truncate(8); c.sha - }, - _ => "-".to_string() + } + _ => "-".to_string(), }, match get_github_api::("https://api.github.com/repos/dani-garcia/bw_web_builds/releases/latest") { Ok(r) => r.tag_name.trim_start_matches('v').to_string(), - _ => "-".to_string() + _ => "-".to_string(), }, ) } else { ("-".to_string(), "-".to_string(), "-".to_string()) }; - // Run the date check as the last item right before filling the json. - // This should ensure that the time difference between the browser and the server is as minimal as possible. - let dt = Utc::now(); - let server_time = dt.format("%Y-%m-%d %H:%M:%S UTC").to_string(); - let diagnostics_json = json!({ "dns_resolved": dns_resolved, - "server_time": server_time, "web_vault_version": web_vault_version.version, "latest_release": latest_release, "latest_commit": latest_commit, "latest_web_build": latest_web_build, + "running_within_docker": running_within_docker, + "has_http_access": has_http_access, + "uses_proxy": uses_proxy, + "db_type": *DB_TYPE, + "admin_url": format!("{}/diagnostics", admin_url(Referer(None))), + "server_time": Utc::now().format("%Y-%m-%d %H:%M:%S UTC").to_string(), // Run the date/time check as the last item to minimize the difference }); let text = AdminTemplateData::diagnostics(diagnostics_json).render()?; Ok(Html(text)) } +#[get("/diagnostics/config")] +fn get_diagnostics_config(_token: AdminToken) -> JsonResult { + let support_json = CONFIG.get_support_json(); + Ok(Json(support_json)) +} + #[post("/config", data = "")] fn post_config(data: Json, _token: AdminToken) -> EmptyResult { let data: ConfigBuilder = data.into_inner(); diff --git a/src/api/core/mod.rs b/src/api/core/mod.rs index a925468b..a2af79a5 100644 --- a/src/api/core/mod.rs +++ b/src/api/core/mod.rs @@ -172,7 +172,7 @@ fn hibp_breach(username: String) -> JsonResult { "Domain": "haveibeenpwned.com", "BreachDate": "2019-08-18T00:00:00Z", "AddedDate": "2019-08-18T00:00:00Z", - "Description": format!("Go to: https://haveibeenpwned.com/account/{account} for a manual check.

HaveIBeenPwned API key not set!
Go to https://haveibeenpwned.com/API/Key to purchase an API key from HaveIBeenPwned.

", account=username), + "Description": format!("Go to: https://haveibeenpwned.com/account/{account} for a manual check.

HaveIBeenPwned API key not set!
Go to https://haveibeenpwned.com/API/Key to purchase an API key from HaveIBeenPwned.

", account=username), "LogoPath": "bwrs_static/hibp.png", "PwnCount": 0, "DataClasses": [ diff --git a/src/config.rs b/src/config.rs index b5047cec..1422ddf2 100644 --- a/src/config.rs +++ b/src/config.rs @@ -2,6 +2,7 @@ use std::process::exit; use std::sync::RwLock; use once_cell::sync::Lazy; +use regex::Regex; use reqwest::Url; use crate::{ @@ -22,6 +23,21 @@ pub static CONFIG: Lazy = Lazy::new(|| { }) }); +static PRIVACY_REGEX: Lazy = Lazy::new(|| Regex::new(r"[\w]").unwrap()); +const PRIVACY_CONFIG: &[&str] = &[ + "allowed_iframe_ancestors", + "database_url", + "domain_origin", + "domain_path", + "domain", + "helo_name", + "org_creation_users", + "signups_domains_whitelist", + "smtp_from", + "smtp_host", + "smtp_username", +]; + pub type Pass = String; macro_rules! make_config { @@ -52,6 +68,7 @@ macro_rules! make_config { } impl ConfigBuilder { + #[allow(clippy::field_reassign_with_default)] fn from_env() -> Self { match dotenv::from_path(".env") { Ok(_) => (), @@ -196,9 +213,38 @@ macro_rules! make_config { }, )+ ]}, )+ ]) } + + pub fn get_support_json(&self) -> serde_json::Value { + let cfg = { + let inner = &self.inner.read().unwrap(); + inner.config.clone() + }; + + json!({ $($( + stringify!($name): make_config!{ @supportstr $name, cfg.$name, $ty, $none_action }, + )+)+ }) + } } }; + // Support string print + ( @supportstr $name:ident, $value:expr, Pass, option ) => { $value.as_ref().map(|_| String::from("***")) }; // Optional pass, we map to an Option with "***" + ( @supportstr $name:ident, $value:expr, Pass, $none_action:ident ) => { String::from("***") }; // Required pass, we return "***" + ( @supportstr $name:ident, $value:expr, $ty:ty, option ) => { // Optional other value, we return as is or convert to string to apply the privacy config + if PRIVACY_CONFIG.contains(&stringify!($name)) { + json!($value.as_ref().map(|x| PRIVACY_REGEX.replace_all(&x.to_string(), "${1}*").to_string())) + } else { + json!($value) + } + }; + ( @supportstr $name:ident, $value:expr, $ty:ty, $none_action:ident ) => { // Required other value, we return as is or convert to string to apply the privacy config + if PRIVACY_CONFIG.contains(&stringify!($name)) { + json!(PRIVACY_REGEX.replace_all(&$value.to_string(), "${1}*").to_string()) + } else { + json!($value) + } + }; + // Group or empty string ( @show ) => { "" }; ( @show $lit:literal ) => { $lit }; @@ -458,7 +504,6 @@ make_config! { } fn validate_config(cfg: &ConfigItems) -> Result<(), Error> { - // Validate connection URL is valid and DB feature is enabled DbConnType::from_url(&cfg.database_url)?; @@ -472,7 +517,9 @@ fn validate_config(cfg: &ConfigItems) -> Result<(), Error> { let dom = cfg.domain.to_lowercase(); if !dom.starts_with("http://") && !dom.starts_with("https://") { - err!("DOMAIN variable needs to contain the protocol (http, https). Use 'http[s]://bw.example.com' instead of 'bw.example.com'"); + err!( + "DOMAIN variable needs to contain the protocol (http, https). Use 'http[s]://bw.example.com' instead of 'bw.example.com'" + ); } let whitelist = &cfg.signups_domains_whitelist; @@ -481,10 +528,10 @@ fn validate_config(cfg: &ConfigItems) -> Result<(), Error> { } let org_creation_users = cfg.org_creation_users.trim().to_lowercase(); - if !(org_creation_users.is_empty() || org_creation_users == "all" || org_creation_users == "none") { - if org_creation_users.split(',').any(|u| !u.contains('@')) { - err!("`ORG_CREATION_USERS` contains invalid email addresses"); - } + if !(org_creation_users.is_empty() || org_creation_users == "all" || org_creation_users == "none") + && org_creation_users.split(',').any(|u| !u.contains('@')) + { + err!("`ORG_CREATION_USERS` contains invalid email addresses"); } if let Some(ref token) = cfg.admin_token { @@ -529,7 +576,6 @@ fn validate_config(cfg: &ConfigItems) -> Result<(), Error> { // Check if the icon blacklist regex is valid if let Some(ref r) = cfg.icon_blacklist_regex { - use regex::Regex; let validate_regex = Regex::new(&r); match validate_regex { Ok(_) => (), @@ -577,7 +623,12 @@ impl Config { validate_config(&config)?; Ok(Config { - inner: RwLock::new(Inner { templates: load_templates(&config.templates_folder), config, _env, _usr }), + inner: RwLock::new(Inner { + templates: load_templates(&config.templates_folder), + config, + _env, + _usr, + }), }) } @@ -650,7 +701,7 @@ impl Config { /// Tests whether the specified user is allowed to create an organization. pub fn is_org_creation_allowed(&self, email: &str) -> bool { let users = self.org_creation_users(); - if users == "" || users == "all" { + if users.is_empty() || users == "all" { true } else if users == "none" { false @@ -704,8 +755,10 @@ impl Config { let akey_s = data_encoding::BASE64.encode(&akey); // Save the new value - let mut builder = ConfigBuilder::default(); - builder._duo_akey = Some(akey_s.clone()); + let builder = ConfigBuilder { + _duo_akey: Some(akey_s.clone()), + ..Default::default() + }; self.update_config_partial(builder).ok(); akey_s diff --git a/src/main.rs b/src/main.rs index f8d88af4..3099aabd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,6 @@ #![forbid(unsafe_code)] #![cfg_attr(feature = "unstable", feature(ip))] -#![recursion_limit = "256"] +#![recursion_limit = "512"] extern crate openssl; #[macro_use] diff --git a/src/static/images/hibp.png b/src/static/images/hibp.png index bf90bbf22dd336f26a3256042d37c6c19728be2e..99d485a5cdc27e5f9d7f265906978e43d93397d1 100644 GIT binary patch literal 9419 zcmd6NcT^K!*DgOP3Ivo+Kt!ohB@DeQf~XV`3002)N3h|)wLCHeWJPC+5?_WIS!#$H4AHI$F349`$>z89LARamW)YkcYK zwiP#QF|x7q`^%TpjXnY>&+4vTl~`hu-^iN+|wzN*LEu@OAn_05COv(NTciAcPYLxHYa}jNPgFsj5G|&|NcE!@3XL%WJCsM>erMcetU*f|iMR_B4U;Hw0){odzA(QY(v7Wu zixdE^pQGtmVx;QzRb8BmQd(2(^FA;`mjL{eHT{fQLFy(Og@uKOYqhfzZyu-y8dy$u zMxC~$3rr;M5gImN`l5gJ@BI3x+BaQQ$0Jxkx(omM zg{Pt=mAoWJ`@R}B%AWZB4F0HoRr_4$3TC&no)dR`<+8BV5=fn!-Of%eNg5MIq*^wM z?+wsYr*0DYhibtuu_6EVn-4ya0`ai0v?@}P6TiKkrG_JJS@x@EH;2aJ0vY7(sh#ov|`QkpIcEki?6UF9OMj?`be%_ZA0ah#RV$XqlXw=FG%aX|XCF zlgOsu6;3f^s1r*EPG!63Rkkq?e-;`0FtMv?vngtIxaM77@A4*{N{Q4xI@}U6c*aUe z5)j9<$n2iOSG0%l&$OQK3>PQym@*|Mt7CTK# zUjwbgCUvk?7oaOKmw(brbP~Hb4<`ig$_ll&pckO>=tNGzq_q? zXi{I#E7^OBR1%DQo)Tx!(T08x@ti%>buIW~oTy0BvXQ(*hTWLVX;O)D16bEX)DCY)a(ex_o{x&s<7bH0xYZ`F zm}u#x!sE}d7c93_LjKwG>zws&W6#BDlKJIWB)jD1rG5wY+ufgauJ&yfLd<4)1r6?R zG99k1b>H&x$tKELer-Mcih!6;xnQ-7H%l$fk7E`?snCiEHzr#EYPm9vpGJ|%knUDk zdK@&!GYRFNZNY+Gk`_)Eu2s=boo?>KN=)T~oAaq6?w5z-(QIcSYpf#PNyc1HF``b= z@$f-$XwHJM0DSfeA-?RgLElswz_0O?7nVauN5p7n@U5ZCM>1VVZ_#}|U|n+v;_5-8NwHY8s?q! zO~{j@Xf-(Er=3t6dR&l0;tQkQSkt?WgIa9gS3X~`>3ifQR$3X$uvP>;hlM$a!;m1> zSF>em+0aUGhEB!(8fLINC7fpBl|8PgZo#@sOY6h_A5}LHDCOZFx;)r#Q~ah%@Y|>; zAmINTwjbmv5GOaGyO}l^jjXb}>c-zH{;Aj?GXDt^jh4^Q9dXE=_>HvYu8RqIf<|I8 zmHoNn#_O?*#C^i=_Q9jJ&jY4*Z%397RF|AFxQfol@lGsb#1Y3D{1T_D5a`Y%RK6!y z9_QSgOwJeVQ7!@HK0I31H|syCVw2H|u&8f&hVWkv;{f)rnx58K;{{l`6KrwZC~lTk z*K){Xt}K&GH*z5~ND##BX(z(+K}%(rI((6yB6B~HmNZ&!5R1nX)CZGt=X+9u^(gymfG!G_Sq83C+ zdug2n=QnIs0OBxFTV!@G9I3P(hPUz6Jg~E~tuKl&wh|4CMmi4)?=={w4Gf;9@;hOH zCN|rP*=*v71w9@Vr-RRA((Ohghx5Zb^1GneMDd^8Ik3g27YAQovB)X7<3MK(`9l$< zq8U1n!uTJl*PoWg&Hj)~!I@tmiSG~G)!Po@Yf)x>wT<(mSd%AZ{eGx5acfoUH1PXO z>qw~^7dakONMr|V7Q#>uDw`KLUESsWfnv3P5f``%>}2le|4D<-GTk}YvwTB=?ip+? z=Q?Hofg-XTI2XC+-Y}V)wtcJ(r5ehBmx{j%t+4olJ#W%>Y7pjEQ+@eIP#*-XqLAj) zmswJ!+@2$sZcXY7XE27$$PXndi9=_0A*mn393b$

VGuU3iV{Rolle(Zs_=r$H95 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 Qxc~qF07*qoM6N<$f+_Hnw*UYD literal 5901 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 ld(>a>PuzfKW<$G8DEH!gsJ}N|g11Tn?6a^zl zDJl*zegDsX2bg$yEJJ}S{83noVq|2^`l*L2a6%`Fk&{!WDSvRjC`q_tJHa?R$v6+m zI97=hh{A~^;V6#a2vQ_ersDswoKdXldtpj&J%M$&9@LBV{C|?IXH?1l=YPa_ zIaKf8;P2Jq7Nr8`U%B8L{G|9K`=n&pa#j$2F05!PzWZ>LpUI*tV1LrWa0YECQ)=ZO(^OB3JGE;-& zxG|hI%5-tiMF`HLgL6Y7aK&bKK+@n!nh3@fZXg0@H9K{1){SJ0+)W+`f`j1L(b`+D z;=Qm>-Ok)CMBw<#Bp2*ew=<`i2%PImy^)(u2Cn8>hJSzrSUo7JW$B=nNB2+!zL6q4GB!gVPM;TDo{&11MB;^uB880Yk>a3r1Q zu#;e%->$+DbcfL52)-HynhCz~&u=E_MNWW>k#n0lHBS55)jvu&uUQDd!Ek6RNpCh6 z*c63Cgnz<#6mY-{Rqupx42IzxMWO0@*rl)~{WyFpp0C8A7IsSMp}#2Vm+E~bwQtKKr-~nTt9@83<@(Zv1D683_jh??b%}RwaI&sBZhUD94~5u?*Y3j;}y(I&iTp*A-r$q8m2=@ul>__aDiU+Jl-74IEbPWop12=2e+K0FR(DVhvZWncc{>bgmRl((O zBWy*zsl-wFj*A(q>g`M~Y{i?TI*;3apnqc$;a7t^IR?bDmCcu;4tX=y7+)t#vr)jeLZhr6i3_?BZF_K*+?qXW&vZU&8}C^pwm{wGU39vy3hW~H zr#y)aIx*;*Bz~AUcEM#-{`3NBmy#1oZf2g~-(#)fa%mg*+kHp!3Abn_!r)dzk8FNv>9?|CP?0cb$$K)cRzp3tJWj(LOHW7xQwJ)Q3{n}6sIHx}pk z9k^vkxIX`8ycVrP8(=F4#sPmy!Gi$0pmG^Uj%bd&PT>cd9NNWFqJSGd;Rv>HXr0q~hx5KK}47<4h&H zSMDJg<<)J(amNmYkNE16iGP#o%N~DoM?*5=^@7@mz)IZ5t+p51CJ4*$$WAN~-%8tL z+gza$_em?Ak63m{3eK8Ua)JFX_B*e+kWz#J4D(Rc;HyyDYlAJ5#(erq3Bb)hxO zken#rvGPdzK*4Cq{9!lYPt-3j2B;fsmH#wI_Qy+g6Vcc)KTYoa+6YYS?Bg=O* zBrjqa|9))8<)H6zABxe&J0OyIxS9V_+)3PN>P=;oqi^EZhE#`h zXWQ|mhb+mJpGEpSyng~k@nZkA8%)gg!czG|$#9u;!&)P_2E|7eukf8O@0r%zSqGP& zG;udWI)mEBUB;{#|KYfu<2y>8^DQ>Shsr;Fa_51a`Ce(6x18D6Fj`^BwCpwG2rAz& z5Wk-?wQ_3J_)`o6QI}^u;@iC}>J!>njK#kIfd97YWV=jv01E&B07*qoM6N<$f=EBC AWB>pF delta 1891 zcmV-p2b}nz4)G3tsjYcIVs3=O%iV{E+d^JWBl>`Kxk_KZ0QNR~cL^3h3(YbD2yY=1cU3=HN z-=k#$Za@`rb09ciDs0`OvmF?F4{Qg0{50hI&$_$*?vr2d_kZ~Q?so%v2ap>B{sTDE z)#i_dR)04e<)VSU;1fcKf{O@2RK~wW;Gc*%z_$B(^F+9cja(DzB_ry7DZN32KiO1q+$G#)G_IDY< zoeeePFbMNu_J4f%!i*$*=a2BQ^H4b05^R^__GSVXJXrq$*791P*3%a*tzB!Cggy4( z_%T|`X`wc>86QF2(sK5Racp{Oj=?kfH?(sa5JFvq;(QuD1ZB8AWZ>KyRsk0z1Lx5A zLV#49RYQ}E^OB3RXsF~k|7Dy!sugk2MF`HLfOA74aDSB=cSw-ojE4!vRkRR+GiaO& zIKxpgYHqFvg3cs3U#`a1S*#a!NXscdP6UpfDY#&}w4Ab5B5PqIiki09A)?8Q4`njMSxAU|(`)Vht6aMB85l(0$3iqc7hfk7> zYrTw15r0>Hf?%98NCZxKK8KwI<0J`Au5ijZ1R=PTQo~S=a4BW{dvb(RUdRcNQFCsM zUjZjsb`9nT=hbiu>vIwuIziIPX1z^>;}Pn{Iluu8l&lHkUf2!GM7Y|8zVG45?Bnp` zc#mYk`lPR+VvY#6qsTM8_I=!x_0({I(1;RJ1b^J@SZklIS4*Gq=b6ScuV)vh9prx= z{)iHiD`uH#{NE4JML5F*^Sjx_t-8Ky6EA9tw)e~vTwhY~jFtfP)E7 z3gR@5E#?oT6&EglVd)6H+BnKQIyRQ(UBbrsC$Fe@HgG7NI3Zm9!W%==va5f2 zk#z|;h&%j8>=m{zxdwc3GnSsfLECm)Irk&(5Dq$@)m@PmoIz#0$A7-X_n}u~oKiUr zl%dXxs;^G4yK>sDV&+lbdb{2-!=Osj`w*s~=E?LmT%j(1!$|InSo#?24A2>H0wtfj zL}F*1>FYKkoc^;>y8Ku}n`oYF8k)*=Q%&UZ&fT{9LFgkKn-&AR@BUNzj)8|u5&83#DQ_p=mG{kV@`Z2ov|NGlZ dmvQ}$zW~OnBN@VGrWODI002ovPDHLkV1n|5uN(jX diff --git a/src/static/scripts/bootstrap-native.js b/src/static/scripts/bootstrap-native.js index cfb6ae3f..8fafa16a 100644 --- a/src/static/scripts/bootstrap-native.js +++ b/src/static/scripts/bootstrap-native.js @@ -1,6 +1,6 @@ /*! - * Native JavaScript for Bootstrap v3.0.10 (https://thednp.github.io/bootstrap.native/) - * Copyright 2015-2020 © dnp_theme + * Native JavaScript for Bootstrap v3.0.15 (https://thednp.github.io/bootstrap.native/) + * Copyright 2015-2021 © dnp_theme * Licensed under MIT (https://github.com/thednp/bootstrap.native/blob/master/LICENSE) */ (function (global, factory) { @@ -15,10 +15,14 @@ var transitionDuration = 'webkitTransition' in document.head.style ? 'webkitTransitionDuration' : 'transitionDuration'; + var transitionProperty = 'webkitTransition' in document.head.style ? 'webkitTransitionProperty' : 'transitionProperty'; + function getElementTransitionDuration(element) { - var duration = supportTransition ? parseFloat(getComputedStyle(element)[transitionDuration]) : 0; - duration = typeof duration === 'number' && !isNaN(duration) ? duration * 1000 : 0; - return duration; + var computedStyle = getComputedStyle(element), + property = computedStyle[transitionProperty], + duration = supportTransition && property && property !== 'none' + ? parseFloat(computedStyle[transitionDuration]) : 0; + return !isNaN(duration) ? duration * 1000 : 0; } function emulateTransitionEnd(element,handler){ @@ -35,9 +39,15 @@ return selector instanceof Element ? selector : lookUp.querySelector(selector); } - function bootstrapCustomEvent(eventName, componentName, related) { + function bootstrapCustomEvent(eventName, componentName, eventProperties) { var OriginalCustomEvent = new CustomEvent( eventName + '.bs.' + componentName, {cancelable: true}); - OriginalCustomEvent.relatedTarget = related; + if (typeof eventProperties !== 'undefined') { + Object.keys(eventProperties).forEach(function (key) { + Object.defineProperty(OriginalCustomEvent, key, { + value: eventProperties[key] + }); + }); + } return OriginalCustomEvent; } @@ -352,7 +362,7 @@ }; self.slideTo = function (next) { if (vars.isSliding) { return; } - var activeItem = self.getActiveIndex(), orientation; + var activeItem = self.getActiveIndex(), orientation, eventProperties; if ( activeItem === next ) { return; } else if ( (activeItem < next ) || (activeItem === 0 && next === slides.length -1 ) ) { @@ -363,8 +373,9 @@ if ( next < 0 ) { next = slides.length - 1; } else if ( next >= slides.length ){ next = 0; } orientation = vars.direction === 'left' ? 'next' : 'prev'; - slideCustomEvent = bootstrapCustomEvent('slide', 'carousel', slides[next]); - slidCustomEvent = bootstrapCustomEvent('slid', 'carousel', slides[next]); + eventProperties = { relatedTarget: slides[next], direction: vars.direction, from: activeItem, to: next }; + slideCustomEvent = bootstrapCustomEvent('slide', 'carousel', eventProperties); + slidCustomEvent = bootstrapCustomEvent('slid', 'carousel', eventProperties); dispatchCustomEvent.call(element, slideCustomEvent); if (slideCustomEvent.defaultPrevented) { return; } vars.index = next; @@ -615,7 +626,7 @@ } } self.show = function () { - showCustomEvent = bootstrapCustomEvent('show', 'dropdown', relatedTarget); + showCustomEvent = bootstrapCustomEvent('show', 'dropdown', { relatedTarget: relatedTarget }); dispatchCustomEvent.call(parent, showCustomEvent); if ( showCustomEvent.defaultPrevented ) { return; } menu.classList.add('show'); @@ -626,12 +637,12 @@ setTimeout(function () { setFocus( menu.getElementsByTagName('INPUT')[0] || element ); toggleDismiss(); - shownCustomEvent = bootstrapCustomEvent( 'shown', 'dropdown', relatedTarget); + shownCustomEvent = bootstrapCustomEvent('shown', 'dropdown', { relatedTarget: relatedTarget }); dispatchCustomEvent.call(parent, shownCustomEvent); },1); }; self.hide = function () { - hideCustomEvent = bootstrapCustomEvent('hide', 'dropdown', relatedTarget); + hideCustomEvent = bootstrapCustomEvent('hide', 'dropdown', { relatedTarget: relatedTarget }); dispatchCustomEvent.call(parent, hideCustomEvent); if ( hideCustomEvent.defaultPrevented ) { return; } menu.classList.remove('show'); @@ -643,7 +654,7 @@ setTimeout(function () { element.Dropdown && element.addEventListener('click',clickHandler,false); },1); - hiddenCustomEvent = bootstrapCustomEvent('hidden', 'dropdown', relatedTarget); + hiddenCustomEvent = bootstrapCustomEvent('hidden', 'dropdown', { relatedTarget: relatedTarget }); dispatchCustomEvent.call(parent, hiddenCustomEvent); }; self.toggle = function () { @@ -749,7 +760,7 @@ setFocus(modal); modal.isAnimating = false; toggleEvents(1); - shownCustomEvent = bootstrapCustomEvent('shown', 'modal', relatedTarget); + shownCustomEvent = bootstrapCustomEvent('shown', 'modal', { relatedTarget: relatedTarget }); dispatchCustomEvent.call(modal, shownCustomEvent); } function triggerHide(force) { @@ -804,7 +815,7 @@ }; self.show = function () { if (modal.classList.contains('show') && !!modal.isAnimating ) {return} - showCustomEvent = bootstrapCustomEvent('show', 'modal', relatedTarget); + showCustomEvent = bootstrapCustomEvent('show', 'modal', { relatedTarget: relatedTarget }); dispatchCustomEvent.call(modal, showCustomEvent); if ( showCustomEvent.defaultPrevented ) { return; } modal.isAnimating = true; @@ -1193,7 +1204,7 @@ if (dropLink && !dropLink.classList.contains('active') ) { dropLink.classList.add('active'); } - dispatchCustomEvent.call(element, bootstrapCustomEvent( 'activate', 'scrollspy', vars.items[index])); + dispatchCustomEvent.call(element, bootstrapCustomEvent( 'activate', 'scrollspy', { relatedTarget: vars.items[index] })); } else if ( isActive && !inside ) { item.classList.remove('active'); if (dropLink && dropLink.classList.contains('active') && !item.parentNode.getElementsByClassName('active').length ) { @@ -1278,7 +1289,7 @@ } else { tabs.isAnimating = false; } - shownCustomEvent = bootstrapCustomEvent('shown', 'tab', activeTab); + shownCustomEvent = bootstrapCustomEvent('shown', 'tab', { relatedTarget: activeTab }); dispatchCustomEvent.call(next, shownCustomEvent); } function triggerHide() { @@ -1287,8 +1298,8 @@ nextContent.style.float = 'left'; containerHeight = activeContent.scrollHeight; } - showCustomEvent = bootstrapCustomEvent('show', 'tab', activeTab); - hiddenCustomEvent = bootstrapCustomEvent('hidden', 'tab', next); + showCustomEvent = bootstrapCustomEvent('show', 'tab', { relatedTarget: activeTab }); + hiddenCustomEvent = bootstrapCustomEvent('hidden', 'tab', { relatedTarget: next }); dispatchCustomEvent.call(next, showCustomEvent); if ( showCustomEvent.defaultPrevented ) { return; } nextContent.classList.add('active'); @@ -1331,7 +1342,7 @@ nextContent = queryElement(next.getAttribute('href')); activeTab = getActiveTab(); activeContent = getActiveContent(); - hideCustomEvent = bootstrapCustomEvent( 'hide', 'tab', next); + hideCustomEvent = bootstrapCustomEvent( 'hide', 'tab', { relatedTarget: next }); dispatchCustomEvent.call(activeTab, hideCustomEvent); if (hideCustomEvent.defaultPrevented) { return; } tabs.isAnimating = true; @@ -1637,7 +1648,7 @@ } } - var version = "3.0.10"; + var version = "3.0.15"; var index = { Alert: Alert, diff --git a/src/static/scripts/bootstrap.css b/src/static/scripts/bootstrap.css index 0bb38131..a3171bef 100644 --- a/src/static/scripts/bootstrap.css +++ b/src/static/scripts/bootstrap.css @@ -1,10 +1,10 @@ /*! - * Bootstrap v4.5.2 (https://getbootstrap.com/) + * Bootstrap v4.5.3 (https://getbootstrap.com/) * Copyright 2011-2020 The Bootstrap Authors * Copyright 2011-2020 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) */ - :root { +:root { --blue: #007bff; --indigo: #6610f2; --purple: #6f42c1; @@ -216,6 +216,7 @@ caption { th { text-align: inherit; + text-align: -webkit-match-parent; } label { @@ -3750,6 +3751,8 @@ input[type="button"].btn-block { display: block; min-height: 1.5rem; padding-left: 1.5rem; + -webkit-print-color-adjust: exact; + color-adjust: exact; } .custom-control-inline { @@ -5289,6 +5292,7 @@ a.badge-dark:focus, a.badge-dark.focus { position: absolute; top: 0; right: 0; + z-index: 2; padding: 0.75rem 1.25rem; color: inherit; } @@ -10163,7 +10167,7 @@ a.text-dark:hover, a.text-dark:focus { .text-break { word-break: break-word !important; - overflow-wrap: break-word !important; + word-wrap: break-word !important; } .text-reset { @@ -10256,3 +10260,4 @@ a.text-dark:hover, a.text-dark:focus { border-color: #dee2e6; } } +/*# sourceMappingURL=bootstrap.css.map */ \ No newline at end of file diff --git a/src/static/scripts/datatables.css b/src/static/scripts/datatables.css index 1c464bbd..590d541a 100644 --- a/src/static/scripts/datatables.css +++ b/src/static/scripts/datatables.css @@ -4,12 +4,13 @@ * * To rebuild or modify this file with the latest versions of the included * software please visit: - * https://datatables.net/download/#bs4/dt-1.10.22 + * https://datatables.net/download/#bs4/dt-1.10.23 * * Included libraries: - * DataTables 1.10.22 + * DataTables 1.10.23 */ +@charset "UTF-8"; table.dataTable { clear: both; margin-top: 6px !important; @@ -114,7 +115,7 @@ table.dataTable > thead .sorting_desc:before, table.dataTable > thead .sorting_asc_disabled:before, table.dataTable > thead .sorting_desc_disabled:before { right: 1em; - content: "\2191"; + content: "↑"; } table.dataTable > thead .sorting:after, table.dataTable > thead .sorting_asc:after, @@ -122,7 +123,7 @@ table.dataTable > thead .sorting_desc:after, table.dataTable > thead .sorting_asc_disabled:after, table.dataTable > thead .sorting_desc_disabled:after { right: 0.5em; - content: "\2193"; + content: "↓"; } table.dataTable > thead .sorting_asc:before, table.dataTable > thead .sorting_desc:after { @@ -165,9 +166,9 @@ div.dataTables_scrollFoot > .dataTables_scrollFootInner > table { @media screen and (max-width: 767px) { div.dataTables_wrapper div.dataTables_length, - div.dataTables_wrapper div.dataTables_filter, - div.dataTables_wrapper div.dataTables_info, - div.dataTables_wrapper div.dataTables_paginate { +div.dataTables_wrapper div.dataTables_filter, +div.dataTables_wrapper div.dataTables_info, +div.dataTables_wrapper div.dataTables_paginate { text-align: center; } div.dataTables_wrapper div.dataTables_paginate ul.pagination { @@ -213,10 +214,10 @@ div.dataTables_scrollHead table.table-bordered { div.table-responsive > div.dataTables_wrapper > div.row { margin: 0; } -div.table-responsive > div.dataTables_wrapper > div.row > div[class^="col-"]:first-child { +div.table-responsive > div.dataTables_wrapper > div.row > div[class^=col-]:first-child { padding-left: 0; } -div.table-responsive > div.dataTables_wrapper > div.row > div[class^="col-"]:last-child { +div.table-responsive > div.dataTables_wrapper > div.row > div[class^=col-]:last-child { padding-right: 0; } diff --git a/src/static/scripts/datatables.js b/src/static/scripts/datatables.js index 7f898cd6..e25c916c 100644 --- a/src/static/scripts/datatables.js +++ b/src/static/scripts/datatables.js @@ -4,20 +4,20 @@ * * To rebuild or modify this file with the latest versions of the included * software please visit: - * https://datatables.net/download/#bs4/dt-1.10.22 + * https://datatables.net/download/#bs4/dt-1.10.23 * * Included libraries: - * DataTables 1.10.22 + * DataTables 1.10.23 */ -/*! DataTables 1.10.22 +/*! DataTables 1.10.23 * ©2008-2020 SpryMedia Ltd - datatables.net/license */ /** * @summary DataTables * @description Paginate, search and order HTML tables - * @version 1.10.22 + * @version 1.10.23 * @file jquery.dataTables.js * @author SpryMedia Ltd * @contact www.datatables.net @@ -2775,7 +2775,7 @@ for ( var i=0, iLen=a.length-1 ; i + Bitwarden_rs Admin Panel