Fix error reporting in admin and some small fixes

- Fixed a bug in JavaScript which caused no messages to be shown to the
user in-case of an error send by the server.
- Changed mail error handling for better error messages
- Changed user/org actions from a to buttons, this should prevent
strange issues in-case of javascript issues and the page does re-load.
- Added Alpine and Debian info for the running docker image

During the mail error testing i encountered a bug which caused lettre to
panic. This panic only happens on debug builds and not release builds,
so no need to update anything on that part. This bug is also already
fixed. See https://github.com/lettre/lettre/issues/678 and https://github.com/lettre/lettre/pull/679

Resolves #2021
Could also fix the issue reported here #2022, or at least no hash `#` in
the url.
This commit is contained in:
BlackDex 2021-10-08 00:01:24 +02:00
parent 9930a0d752
commit 338756550a
No known key found for this signature in database
GPG Key ID: 58C80A2AA6C765E1
8 changed files with 52 additions and 24 deletions

View File

@ -18,7 +18,9 @@ use crate::{
db::{backup_database, get_sql_server_version, models::*, DbConn, DbConnType}, db::{backup_database, get_sql_server_version, models::*, DbConn, DbConnType},
error::{Error, MapResult}, error::{Error, MapResult},
mail, mail,
util::{format_naive_datetime_local, get_display_size, get_reqwest_client, is_running_in_docker}, util::{
docker_base_image, format_naive_datetime_local, get_display_size, get_reqwest_client, is_running_in_docker,
},
CONFIG, CONFIG,
}; };
@ -492,6 +494,7 @@ fn diagnostics(_token: AdminToken, ip_header: IpHeader, conn: DbConn) -> ApiResu
// Execute some environment checks // Execute some environment checks
let running_within_docker = is_running_in_docker(); let running_within_docker = is_running_in_docker();
let docker_base_image = docker_base_image();
let has_http_access = has_http_access(); let has_http_access = has_http_access();
let uses_proxy = env::var_os("HTTP_PROXY").is_some() let uses_proxy = env::var_os("HTTP_PROXY").is_some()
|| env::var_os("http_proxy").is_some() || env::var_os("http_proxy").is_some()
@ -549,6 +552,7 @@ fn diagnostics(_token: AdminToken, ip_header: IpHeader, conn: DbConn) -> ApiResu
"web_vault_version": web_vault_version.version, "web_vault_version": web_vault_version.version,
"latest_web_build": latest_web_build, "latest_web_build": latest_web_build,
"running_within_docker": running_within_docker, "running_within_docker": running_within_docker,
"docker_base_image": docker_base_image,
"has_http_access": has_http_access, "has_http_access": has_http_access,
"ip_header_exists": &ip_header.0.is_some(), "ip_header_exists": &ip_header.0.is_some(),
"ip_header_match": ip_header_name == CONFIG.ip_header(), "ip_header_match": ip_header_name == CONFIG.ip_header(),

View File

@ -473,15 +473,28 @@ fn send_email(address: &str, subject: &str, body_html: String, body_text: String
// Match some common errors and make them more user friendly // Match some common errors and make them more user friendly
Err(e) => { Err(e) => {
if e.is_client() { if e.is_client() {
err!(format!("SMTP Client error: {}", e)); debug!("SMTP Client error: {:#?}", e);
err!(format!("SMTP Client error: {}", e.to_string()));
} else if e.is_transient() { } else if e.is_transient() {
err!(format!("SMTP 4xx error: {:?}", e)); debug!("SMTP 4xx error: {:#?}", e);
err!(format!("SMTP 4xx error: {}", e.to_string()));
} else if e.is_permanent() { } else if e.is_permanent() {
err!(format!("SMTP 5xx error: {:?}", e)); debug!("SMTP 5xx error: {:#?}", e);
let mut msg = e.to_string();
// Add a special check for 535 to add a more descriptive message
if msg.contains("(535)") {
msg = format!("{} - Authentication credentials invalid", msg);
}
err!(format!("SMTP 5xx error: {}", msg));
} else if e.is_timeout() { } else if e.is_timeout() {
err!(format!("SMTP timeout error: {:?}", e)); debug!("SMTP timeout error: {:#?}", e);
err!(format!("SMTP timeout error: {}", e.to_string()));
} else if e.is_tls() {
debug!("SMTP Encryption error: {:#?}", e);
err!(format!("SMTP Encryption error: {}", e.to_string()));
} else { } else {
Err(e.into()) debug!("SMTP {:#?}", e);
err!(format!("SMTP {}", e.to_string()));
} }
} }
} }

View File

@ -62,8 +62,8 @@
headers: { "Content-Type": "application/json" } headers: { "Content-Type": "application/json" }
}).then( resp => { }).then( resp => {
if (resp.ok) { msg(successMsg, reload_page); return Promise.reject({error: false}); } if (resp.ok) { msg(successMsg, reload_page); return Promise.reject({error: false}); }
respStatus = resp.status; const respStatus = resp.status;
respStatusText = resp.statusText; const respStatusText = resp.statusText;
return resp.text(); return resp.text();
}).then( respText => { }).then( respText => {
try { try {
@ -126,9 +126,9 @@
// get current URL path and assign 'active' class to the correct nav-item // get current URL path and assign 'active' class to the correct nav-item
(() => { (() => {
var pathname = window.location.pathname; const pathname = window.location.pathname;
if (pathname === "") return; if (pathname === "") return;
var navItem = document.querySelectorAll('.navbar-nav .nav-item a[href="'+pathname+'"]'); let navItem = document.querySelectorAll('.navbar-nav .nav-item a[href="'+pathname+'"]');
if (navItem.length === 1) { if (navItem.length === 1) {
navItem[0].className = navItem[0].className + ' active'; navItem[0].className = navItem[0].className + ' active';
navItem[0].setAttribute('aria-current', 'page'); navItem[0].setAttribute('aria-current', 'page');

View File

@ -58,7 +58,7 @@
<dt class="col-sm-5">Running within Docker</dt> <dt class="col-sm-5">Running within Docker</dt>
<dd class="col-sm-7"> <dd class="col-sm-7">
{{#if page_data.running_within_docker}} {{#if page_data.running_within_docker}}
<span class="d-block"><b>Yes</b></span> <span class="d-block"><b>Yes (Base: {{ page_data.docker_base_image }})</b></span>
{{/if}} {{/if}}
{{#unless page_data.running_within_docker}} {{#unless page_data.running_within_docker}}
<span class="d-block"><b>No</b></span> <span class="d-block"><b>No</b></span>
@ -329,7 +329,7 @@
supportString += "* Vaultwarden version: v{{ version }}\n"; supportString += "* Vaultwarden version: v{{ version }}\n";
supportString += "* Web-vault version: v{{ page_data.web_vault_version }}\n"; supportString += "* Web-vault version: v{{ page_data.web_vault_version }}\n";
supportString += "* Running within Docker: {{ page_data.running_within_docker }}\n"; supportString += "* Running within Docker: {{ page_data.running_within_docker }} (Base: {{ page_data.docker_base_image }})\n";
supportString += "* Environment settings overridden: "; supportString += "* Environment settings overridden: ";
{{#if page_data.overrides}} {{#if page_data.overrides}}
supportString += "true\n" supportString += "true\n"

View File

@ -37,8 +37,8 @@
<span class="d-block"><strong>Size:</strong> {{attachment_size}}</span> <span class="d-block"><strong>Size:</strong> {{attachment_size}}</span>
{{/if}} {{/if}}
</td> </td>
<td class="text-end pe-2 small"> <td class="text-end px-0 small">
<a class="d-block" href="#" onclick='deleteOrganization({{jsesc Id}}, {{jsesc Name}}, {{jsesc BillingEmail}})'>Delete Organization</a> <button type="button" class="btn btn-sm btn-link p-0 border-0" onclick='deleteOrganization({{jsesc Id}}, {{jsesc Name}}, {{jsesc BillingEmail}})'>Delete Organization</button>
</td> </td>
</tr> </tr>
{{/each}} {{/each}}

View File

@ -219,11 +219,10 @@
onChange(); // Trigger the event initially onChange(); // Trigger the event initially
checkbox.addEventListener("change", onChange); checkbox.addEventListener("change", onChange);
} }
// These are formatted because otherwise the
// VSCode formatter breaks But they still work {{#each config}} {{#if grouptoggle}}
// {{#each config}} {{#if grouptoggle}}
masterCheck("input_{{grouptoggle}}", "#g_{{group}} input"); masterCheck("input_{{grouptoggle}}", "#g_{{group}} input");
// {{/if}} {{/each}} {{/if}} {{/each}}
// Two functions to help check if there were changes to the form fields // Two functions to help check if there were changes to the form fields
// Useful for example during the smtp test to prevent people from clicking save before testing there new settings // Useful for example during the smtp test to prevent people from clicking save before testing there new settings

View File

@ -61,16 +61,16 @@
{{/each}} {{/each}}
</div> </div>
</td> </td>
<td class="text-end pe-2 small"> <td class="text-end px-0 small">
{{#if TwoFactorEnabled}} {{#if TwoFactorEnabled}}
<a class="d-block" href="#" onclick='remove2fa({{jsesc Id}})'>Remove all 2FA</a> <button type="button" class="btn btn-sm btn-link p-0 border-0" onclick='remove2fa({{jsesc Id}})'>Remove all 2FA</button>
{{/if}} {{/if}}
<a class="d-block" href="#" onclick='deauthUser({{jsesc Id}})'>Deauthorize sessions</a> <button type="button" class="btn btn-sm btn-link p-0 border-0" onclick='deauthUser({{jsesc Id}})'>Deauthorize sessions</button>
<a class="d-block" href="#" onclick='deleteUser({{jsesc Id}}, {{jsesc Email}})'>Delete User</a> <button type="button" class="btn btn-sm btn-link p-0 border-0" onclick='deleteUser({{jsesc Id}}, {{jsesc Email}})'>Delete User</button>
{{#if user_enabled}} {{#if user_enabled}}
<a class="d-block" href="#" onclick='disableUser({{jsesc Id}}, {{jsesc Email}})'>Disable User</a> <button type="button" class="btn btn-sm btn-link p-0 border-0" onclick='disableUser({{jsesc Id}}, {{jsesc Email}})'>Disable User</button>
{{else}} {{else}}
<a class="d-block" href="#" onclick='enableUser({{jsesc Id}}, {{jsesc Email}})'>Enable User</a> <button type="button" class="btn btn-sm btn-link p-0 border-0" onclick='enableUser({{jsesc Id}}, {{jsesc Email}})'>Enable User</button>
{{/if}} {{/if}}
</td> </td>
</tr> </tr>

View File

@ -419,6 +419,18 @@ pub fn is_running_in_docker() -> bool {
Path::new("/.dockerenv").exists() || Path::new("/run/.containerenv").exists() Path::new("/.dockerenv").exists() || Path::new("/run/.containerenv").exists()
} }
/// Simple check to determine on which docker base image vaultwarden is running.
/// We build images based upon Debian or Alpine, so these we check here.
pub fn docker_base_image() -> String {
if Path::new("/etc/debian_version").exists() {
"Debian".to_string()
} else if Path::new("/etc/alpine-release").exists() {
"Alpine".to_string()
} else {
"Unknown".to_string()
}
}
// //
// Deserialization methods // Deserialization methods
// //