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 @@
- Organization |
- Users |
- Items |
- Attachments |
+ Organization |
+ Users |
+ Items |
+ Attachments |
Actions |
@@ -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 @@
- User |
+ User |
Created at |
Last Active |
Items |
@@ -63,14 +63,14 @@
{{#if TwoFactorEnabled}}
-
+
{{/if}}
-
-
+
+
{{#if user_enabled}}
-
+
{{else}}
-
+
{{/if}}
|
@@ -137,7 +137,7 @@
-
+