From c9ed9aa73382adcc37e0c7bf59a06f72c8774281 Mon Sep 17 00:00:00 2001 From: BlackDex Date: Tue, 24 Jan 2023 23:31:38 +0100 Subject: [PATCH] Fix Javascript issue on non sqlite databases When a non sqlite database is used, loading the admin interface fails because the backup button is not generated. This PR is solves it by checking if the elements are valid. Also made some other changes and fixed some eslint errors. Showing `_post` errors is better now. Update jquery to latest version. Fixes #3166 --- src/api/web.rs | 4 +- src/static/scripts/admin.css | 21 +++-- src/static/scripts/admin.js | 26 ++++-- src/static/scripts/admin_diagnostics.js | 22 +++-- src/static/scripts/admin_organizations.js | 26 +++++- src/static/scripts/admin_settings.js | 65 ++++++++----- src/static/scripts/admin_users.js | 91 ++++++++++++------- ...ery-3.6.2.slim.js => jquery-3.6.3.slim.js} | 17 ++-- src/static/templates/admin/organizations.hbs | 12 +-- src/static/templates/admin/users.hbs | 14 +-- 10 files changed, 190 insertions(+), 108 deletions(-) rename src/static/scripts/{jquery-3.6.2.slim.js => jquery-3.6.3.slim.js} (99%) diff --git a/src/api/web.rs b/src/api/web.rs index 6e3921ed..7f9a77da 100644 --- a/src/api/web.rs +++ b/src/api/web.rs @@ -118,8 +118,8 @@ pub fn static_files(filename: String) -> Result<(ContentType, &'static [u8]), Er "jdenticon.js" => Ok((ContentType::JavaScript, include_bytes!("../static/scripts/jdenticon.js"))), "datatables.js" => Ok((ContentType::JavaScript, include_bytes!("../static/scripts/datatables.js"))), "datatables.css" => Ok((ContentType::CSS, include_bytes!("../static/scripts/datatables.css"))), - "jquery-3.6.2.slim.js" => { - Ok((ContentType::JavaScript, include_bytes!("../static/scripts/jquery-3.6.2.slim.js"))) + "jquery-3.6.3.slim.js" => { + Ok((ContentType::JavaScript, include_bytes!("../static/scripts/jquery-3.6.3.slim.js"))) } _ => err!(format!("Static file not found: {filename}")), } diff --git a/src/static/scripts/admin.css b/src/static/scripts/admin.css index d77b5372..67f2c00d 100644 --- a/src/static/scripts/admin.css +++ b/src/static/scripts/admin.css @@ -18,24 +18,31 @@ img { border: var(--bs-alert-border); } +#users-table .vw-account-details { + min-width: 250px; +} #users-table .vw-created-at, #users-table .vw-last-active { - width: 85px; - min-width: 70px; + min-width: 85px; + max-width: 85px; } -#users-table .vw-items { - width: 35px; +#users-table .vw-items, #orgs-table .vw-items, #orgs-table .vw-users { min-width: 35px; + max-width: 40px; } -#users-table .vw-organizations { - min-width: 120px; +#users-table .vw-attachments, #orgs-table .vw-attachments { + min-width: 100px; + max-width: 130px; } #users-table .vw-actions, #orgs-table .vw-actions { - width: 130px; min-width: 130px; + max-width: 130px; } #users-table .vw-org-cell { max-height: 120px; } +#orgs-table .vw-org-details { + min-width: 285px; +} #support-string { height: 16rem; diff --git a/src/static/scripts/admin.js b/src/static/scripts/admin.js index 7849ac19..7408c955 100644 --- a/src/static/scripts/admin.js +++ b/src/static/scripts/admin.js @@ -1,4 +1,6 @@ "use strict"; +/* eslint-env es2017, browser */ +/* exported BASE_URL, _post */ function getBaseUrl() { // If the base URL is `https://vaultwarden.example.com/base/path/`, @@ -26,6 +28,8 @@ function msg(text, reload_page = true) { } function _post(url, successMsg, errMsg, body, reload_page = true) { + let respStatus; + let respStatusText; fetch(url, { method: "POST", body: body, @@ -33,22 +37,30 @@ function _post(url, successMsg, errMsg, body, reload_page = true) { credentials: "same-origin", headers: { "Content-Type": "application/json" } }).then( resp => { - if (resp.ok) { msg(successMsg, reload_page); return Promise.reject({error: false}); } - const respStatus = resp.status; - const respStatusText = resp.statusText; + if (resp.ok) { + msg(successMsg, reload_page); + // Abuse the catch handler by setting error to false and continue + return Promise.reject({error: false}); + } + respStatus = resp.status; + respStatusText = resp.statusText; return resp.text(); }).then( respText => { try { const respJson = JSON.parse(respText); - return respJson ? respJson.ErrorModel.Message : "Unknown error"; + if (respJson.ErrorModel && respJson.ErrorModel.Message) { + return respJson.ErrorModel.Message; + } else { + return Promise.reject({body:`${respStatus} - ${respStatusText}\n\nUnknown error`, error: true}); + } } catch (e) { - return Promise.reject({body:respStatus + " - " + respStatusText, error: true}); + return Promise.reject({body:`${respStatus} - ${respStatusText}\n\n[Catch] ${e}`, error: true}); } }).then( apiMsg => { - msg(errMsg + "\n" + apiMsg, reload_page); + msg(`${errMsg}\n${apiMsg}`, reload_page); }).catch( e => { if (e.error === false) { return true; } - else { msg(errMsg + "\n" + e.body, reload_page); } + else { msg(`${errMsg}\n${e.body}`, reload_page); } }); } diff --git a/src/static/scripts/admin_diagnostics.js b/src/static/scripts/admin_diagnostics.js index 84a7ecc5..a7a574fc 100644 --- a/src/static/scripts/admin_diagnostics.js +++ b/src/static/scripts/admin_diagnostics.js @@ -1,4 +1,6 @@ "use strict"; +/* eslint-env es2017, browser */ +/* global BASE_URL:readable, BSN:readable */ var dnsCheck = false; var timeCheck = false; @@ -65,7 +67,7 @@ function checkVersions(platform, installed, latest, commit=null) { // ================================ // Generate support string to be pasted on github or the forum -async function generateSupportString(dj) { +async function generateSupportString(event, dj) { event.preventDefault(); event.stopPropagation(); @@ -114,7 +116,7 @@ async function generateSupportString(dj) { document.getElementById("copy-support").classList.remove("d-none"); } -function copyToClipboard() { +function copyToClipboard(event) { event.preventDefault(); event.stopPropagation(); @@ -208,12 +210,18 @@ function init(dj) { } // onLoad events -document.addEventListener("DOMContentLoaded", (/*event*/) => { +document.addEventListener("DOMContentLoaded", (event) => { const diag_json = JSON.parse(document.getElementById("diagnostics_json").innerText); init(diag_json); - document.getElementById("gen-support").addEventListener("click", () => { - generateSupportString(diag_json); - }); - document.getElementById("copy-support").addEventListener("click", copyToClipboard); + const btnGenSupport = document.getElementById("gen-support"); + if (btnGenSupport) { + btnGenSupport.addEventListener("click", () => { + generateSupportString(event, diag_json); + }); + } + const btnCopySupport = document.getElementById("copy-support"); + if (btnCopySupport) { + btnCopySupport.addEventListener("click", copyToClipboard); + } }); \ No newline at end of file diff --git a/src/static/scripts/admin_organizations.js b/src/static/scripts/admin_organizations.js index ae15e2fd..db4037b4 100644 --- a/src/static/scripts/admin_organizations.js +++ b/src/static/scripts/admin_organizations.js @@ -1,6 +1,8 @@ "use strict"; +/* eslint-env es2017, browser, jquery */ +/* global _post:readable, BASE_URL:readable, reload:readable, jdenticon:readable */ -function deleteOrganization() { +function deleteOrganization(event) { event.preventDefault(); event.stopPropagation(); const org_uuid = event.target.dataset.vwOrgUuid; @@ -28,9 +30,22 @@ function deleteOrganization() { } } +function initActions() { + document.querySelectorAll("button[vw-delete-organization]").forEach(btn => { + btn.addEventListener("click", deleteOrganization); + }); + + if (jdenticon) { + jdenticon(); + } +} + // onLoad events document.addEventListener("DOMContentLoaded", (/*event*/) => { jQuery("#orgs-table").DataTable({ + "drawCallback": function() { + initActions(); + }, "stateSave": true, "responsive": true, "lengthMenu": [ @@ -46,9 +61,10 @@ document.addEventListener("DOMContentLoaded", (/*event*/) => { }); // Add click events for organization actions - document.querySelectorAll("button[vw-delete-organization]").forEach(btn => { - btn.addEventListener("click", deleteOrganization); - }); + initActions(); - document.getElementById("reload").addEventListener("click", reload); + const btnReload = document.getElementById("reload"); + if (btnReload) { + btnReload.addEventListener("click", reload); + } }); \ No newline at end of file diff --git a/src/static/scripts/admin_settings.js b/src/static/scripts/admin_settings.js index 4f248cbd..2e36795f 100644 --- a/src/static/scripts/admin_settings.js +++ b/src/static/scripts/admin_settings.js @@ -1,6 +1,8 @@ "use strict"; +/* eslint-env es2017, browser */ +/* global _post:readable, BASE_URL:readable */ -function smtpTest() { +function smtpTest(event) { event.preventDefault(); event.stopPropagation(); if (formHasChanges(config_form)) { @@ -41,7 +43,7 @@ function getFormData() { return data; } -function saveConfig() { +function saveConfig(event) { const data = JSON.stringify(getFormData()); _post(`${BASE_URL}/admin/config/`, "Config saved correctly", @@ -51,7 +53,7 @@ function saveConfig() { event.preventDefault(); } -function deleteConf() { +function deleteConf(event) { event.preventDefault(); event.stopPropagation(); const input = prompt( @@ -68,7 +70,7 @@ function deleteConf() { } } -function backupDatabase() { +function backupDatabase(event) { event.preventDefault(); event.stopPropagation(); _post(`${BASE_URL}/admin/config/backup_db`, @@ -94,24 +96,26 @@ function formHasChanges(form) { // This function will prevent submitting a from when someone presses enter. function preventFormSubmitOnEnter(form) { - form.onkeypress = function(e) { - const key = e.charCode || e.keyCode || 0; - if (key == 13) { - e.preventDefault(); - } - }; + if (form) { + form.addEventListener("keypress", (event) => { + if (event.key == "Enter") { + event.preventDefault(); + } + }); + } } // This function will hook into the smtp-test-email input field and will call the smtpTest() function when enter is pressed. function submitTestEmailOnEnter() { const smtp_test_email_input = document.getElementById("smtp-test-email"); - smtp_test_email_input.onkeypress = function(e) { - const key = e.charCode || e.keyCode || 0; - if (key == 13) { - e.preventDefault(); - smtpTest(); - } - }; + if (smtp_test_email_input) { + smtp_test_email_input.addEventListener("keypress", (event) => { + if (event.key == "Enter") { + event.preventDefault(); + smtpTest(event); + } + }); + } } // Colorize some settings which are high risk @@ -124,11 +128,11 @@ function colorRiskSettings() { }); } -function toggleVis(evt) { +function toggleVis(event) { event.preventDefault(); event.stopPropagation(); - const elem = document.getElementById(evt.target.dataset.vwPwToggle); + const elem = document.getElementById(event.target.dataset.vwPwToggle); const type = elem.getAttribute("type"); if (type === "text") { elem.setAttribute("type", "password"); @@ -146,9 +150,11 @@ function masterCheck(check_id, inputs_query) { } const checkbox = document.getElementById(check_id); - const onChange = onChanged(checkbox, inputs_query); - onChange(); // Trigger the event initially - checkbox.addEventListener("change", onChange); + if (checkbox) { + const onChange = onChanged(checkbox, inputs_query); + onChange(); // Trigger the event initially + checkbox.addEventListener("change", onChange); + } } const config_form = document.getElementById("config-form"); @@ -172,9 +178,18 @@ document.addEventListener("DOMContentLoaded", (/*event*/) => { password_toggle_btn.addEventListener("click", toggleVis); }); - document.getElementById("backupDatabase").addEventListener("click", backupDatabase); - document.getElementById("deleteConf").addEventListener("click", deleteConf); - document.getElementById("smtpTest").addEventListener("click", smtpTest); + const btnBackupDatabase = document.getElementById("backupDatabase"); + if (btnBackupDatabase) { + btnBackupDatabase.addEventListener("click", backupDatabase); + } + const btnDeleteConf = document.getElementById("deleteConf"); + if (btnDeleteConf) { + btnDeleteConf.addEventListener("click", deleteConf); + } + const btnSmtpTest = document.getElementById("smtpTest"); + if (btnSmtpTest) { + btnSmtpTest.addEventListener("click", smtpTest); + } config_form.addEventListener("submit", saveConfig); }); \ No newline at end of file diff --git a/src/static/scripts/admin_users.js b/src/static/scripts/admin_users.js index 8f7ddf20..b4da0f97 100644 --- a/src/static/scripts/admin_users.js +++ b/src/static/scripts/admin_users.js @@ -1,6 +1,8 @@ "use strict"; +/* eslint-env es2017, browser, jquery */ +/* global _post:readable, BASE_URL:readable, reload:readable, jdenticon:readable */ -function deleteUser() { +function deleteUser(event) { event.preventDefault(); event.stopPropagation(); const id = event.target.parentNode.dataset.vwUserUuid; @@ -22,7 +24,7 @@ function deleteUser() { } } -function remove2fa() { +function remove2fa(event) { event.preventDefault(); event.stopPropagation(); const id = event.target.parentNode.dataset.vwUserUuid; @@ -36,7 +38,7 @@ function remove2fa() { ); } -function deauthUser() { +function deauthUser(event) { event.preventDefault(); event.stopPropagation(); const id = event.target.parentNode.dataset.vwUserUuid; @@ -50,7 +52,7 @@ function deauthUser() { ); } -function disableUser() { +function disableUser(event) { event.preventDefault(); event.stopPropagation(); const id = event.target.parentNode.dataset.vwUserUuid; @@ -68,7 +70,7 @@ function disableUser() { } } -function enableUser() { +function enableUser(event) { event.preventDefault(); event.stopPropagation(); const id = event.target.parentNode.dataset.vwUserUuid; @@ -86,7 +88,7 @@ function enableUser() { } } -function updateRevisions() { +function updateRevisions(event) { event.preventDefault(); event.stopPropagation(); _post(`${BASE_URL}/admin/users/update_revision`, @@ -95,7 +97,7 @@ function updateRevisions() { ); } -function inviteUser() { +function inviteUser(event) { event.preventDefault(); event.stopPropagation(); const email = document.getElementById("inviteEmail"); @@ -182,7 +184,7 @@ userOrgTypeDialog.addEventListener("hide.bs.modal", function() { document.getElementById("userOrgTypeOrgUuid").value = ""; }, false); -function updateUserOrgType() { +function updateUserOrgType(event) { event.preventDefault(); event.stopPropagation(); @@ -195,26 +197,7 @@ function updateUserOrgType() { ); } -// onLoad events -document.addEventListener("DOMContentLoaded", (/*event*/) => { - jQuery("#users-table").DataTable({ - "stateSave": true, - "responsive": true, - "lengthMenu": [ - [-1, 5, 10, 25, 50], - ["All", 5, 10, 25, 50] - ], - "pageLength": -1, // Default show all - "columnDefs": [{ - "targets": [1, 2], - "type": "date-iso" - }, { - "targets": 6, - "searchable": false, - "orderable": false - }] - }); - +function initUserTable() { // Color all the org buttons per type document.querySelectorAll("button[data-vw-org-type]").forEach(function(e) { const orgType = ORG_TYPES[e.dataset.vwOrgType]; @@ -222,7 +205,6 @@ document.addEventListener("DOMContentLoaded", (/*event*/) => { e.title = orgType.name; }); - // Add click events for user actions document.querySelectorAll("button[vw-remove2fa]").forEach(btn => { btn.addEventListener("click", remove2fa); }); @@ -239,8 +221,51 @@ document.addEventListener("DOMContentLoaded", (/*event*/) => { btn.addEventListener("click", enableUser); }); - document.getElementById("updateRevisions").addEventListener("click", updateRevisions); - document.getElementById("reload").addEventListener("click", reload); - document.getElementById("userOrgTypeForm").addEventListener("submit", updateUserOrgType); - document.getElementById("inviteUserForm").addEventListener("submit", inviteUser); + if (jdenticon) { + jdenticon(); + } +} + +// onLoad events +document.addEventListener("DOMContentLoaded", (/*event*/) => { + jQuery("#users-table").DataTable({ + "drawCallback": function() { + initUserTable(); + }, + "stateSave": true, + "responsive": true, + "lengthMenu": [ + [-1, 2, 5, 10, 25, 50], + ["All", 2, 5, 10, 25, 50] + ], + "pageLength": 2, // Default show all + "columnDefs": [{ + "targets": [1, 2], + "type": "date-iso" + }, { + "targets": 6, + "searchable": false, + "orderable": false + }] + }); + + // Add click events for user actions + initUserTable(); + + const btnUpdateRevisions = document.getElementById("updateRevisions"); + if (btnUpdateRevisions) { + btnUpdateRevisions.addEventListener("click", updateRevisions); + } + const btnReload = document.getElementById("reload"); + if (btnReload) { + btnReload.addEventListener("click", reload); + } + const btnUserOrgTypeForm = document.getElementById("userOrgTypeForm"); + if (btnUserOrgTypeForm) { + btnUserOrgTypeForm.addEventListener("submit", updateUserOrgType); + } + const btnInviteUserForm = document.getElementById("inviteUserForm"); + if (btnInviteUserForm) { + btnInviteUserForm.addEventListener("submit", inviteUser); + } }); \ No newline at end of file diff --git a/src/static/scripts/jquery-3.6.2.slim.js b/src/static/scripts/jquery-3.6.3.slim.js similarity index 99% rename from src/static/scripts/jquery-3.6.2.slim.js rename to src/static/scripts/jquery-3.6.3.slim.js index 4c41f3eb..d7e1a94c 100644 --- a/src/static/scripts/jquery-3.6.2.slim.js +++ b/src/static/scripts/jquery-3.6.3.slim.js @@ -1,5 +1,5 @@ /*! - * jQuery JavaScript Library v3.6.2 -ajax,-ajax/jsonp,-ajax/load,-ajax/script,-ajax/var/location,-ajax/var/nonce,-ajax/var/rquery,-ajax/xhr,-manipulation/_evalUrl,-deprecated/ajax-event-alias,-effects,-effects/Tween,-effects/animatedSelector + * jQuery JavaScript Library v3.6.3 -ajax,-ajax/jsonp,-ajax/load,-ajax/script,-ajax/var/location,-ajax/var/nonce,-ajax/var/rquery,-ajax/xhr,-manipulation/_evalUrl,-deprecated/ajax-event-alias,-effects,-effects/Tween,-effects/animatedSelector * https://jquery.com/ * * Includes Sizzle.js @@ -9,7 +9,7 @@ * Released under the MIT license * https://jquery.org/license * - * Date: 2022-12-13T14:56Z + * Date: 2022-12-20T21:28Z */ ( function( global, factory ) { @@ -151,7 +151,7 @@ function toType( obj ) { var - version = "3.6.2 -ajax,-ajax/jsonp,-ajax/load,-ajax/script,-ajax/var/location,-ajax/var/nonce,-ajax/var/rquery,-ajax/xhr,-manipulation/_evalUrl,-deprecated/ajax-event-alias,-effects,-effects/Tween,-effects/animatedSelector", + version = "3.6.3 -ajax,-ajax/jsonp,-ajax/load,-ajax/script,-ajax/var/location,-ajax/var/nonce,-ajax/var/rquery,-ajax/xhr,-manipulation/_evalUrl,-deprecated/ajax-event-alias,-effects,-effects/Tween,-effects/animatedSelector", // Define a local copy of jQuery jQuery = function( selector, context ) { @@ -522,14 +522,14 @@ function isArrayLike( obj ) { } var Sizzle = /*! - * Sizzle CSS Selector Engine v2.3.8 + * Sizzle CSS Selector Engine v2.3.9 * https://sizzlejs.com/ * * Copyright JS Foundation and other contributors * Released under the MIT license * https://js.foundation/ * - * Date: 2022-11-16 + * Date: 2022-12-19 */ ( function( window ) { var i, @@ -890,7 +890,7 @@ function Sizzle( selector, context, results, seed ) { if ( support.cssSupportsSelector && // eslint-disable-next-line no-undef - !CSS.supports( "selector(" + newSelector + ")" ) ) { + !CSS.supports( "selector(:is(" + newSelector + "))" ) ) { // Support: IE 11+ // Throw to get to the same code path as an error directly in qSA. @@ -1492,9 +1492,8 @@ setDocument = Sizzle.setDocument = function( node ) { // `:has()` uses a forgiving selector list as an argument so our regular // `try-catch` mechanism fails to catch `:has()` with arguments not supported // natively like `:has(:contains("Foo"))`. Where supported & spec-compliant, - // we now use `CSS.supports("selector(SELECTOR_TO_BE_TESTED)")` but outside - // that, let's mark `:has` as buggy to always use jQuery traversal for - // `:has()`. + // we now use `CSS.supports("selector(:is(SELECTOR_TO_BE_TESTED))")`, but + // outside that we mark `:has` as buggy. rbuggyQSA.push( ":has" ); } diff --git a/src/static/templates/admin/organizations.hbs b/src/static/templates/admin/organizations.hbs index eef6ae1a..d95370c4 100644 --- a/src/static/templates/admin/organizations.hbs +++ b/src/static/templates/admin/organizations.hbs @@ -5,10 +5,10 @@ - - - - + + + + @@ -38,7 +38,7 @@ {{/if}} {{/each}} @@ -53,7 +53,7 @@ - + diff --git a/src/static/templates/admin/users.hbs b/src/static/templates/admin/users.hbs index 3dbee11c..b08df02e 100644 --- a/src/static/templates/admin/users.hbs +++ b/src/static/templates/admin/users.hbs @@ -5,7 +5,7 @@
OrganizationUsersItemsAttachmentsOrganizationUsersItemsAttachments Actions
- +
- + @@ -63,14 +63,14 @@ @@ -137,7 +137,7 @@ - +
User Created at Last Active Items {{#if TwoFactorEnabled}} - + {{/if}} - - + + {{#if user_enabled}} - + {{else}} - + {{/if}}