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 933c939a..744d9fb2 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}}