diff --git a/.env.template b/.env.template index 9d6f75a1..425741ae 100644 --- a/.env.template +++ b/.env.template @@ -264,6 +264,8 @@ ## For details see: https://github.com/dani-garcia/vaultwarden/wiki/Enabling-admin-page#secure-the-admin_token ## If not set, the admin panel is disabled ## New Argon2 PHC string +## Note that for some environments, like docker-compose you need to escape all the dollar signs `$` with an extra dollar sign like `$$` +## Also, use single quotes (') instead of double quotes (") to enclose the string when needed # ADMIN_TOKEN='$argon2id$v=19$m=65540,t=3,p=4$MmeKRnGK5RW5mJS7h3TOL89GrpLPXJPAtTK8FTqj9HM$DqsstvoSAETl9YhnsXbf43WeaUwJC6JhViIvuPoig78' ## Old plain text string (Will generate warnings in favor of Argon2) # ADMIN_TOKEN=Vy2VyYTTsKPv8W5aEOWUbB/Bt3DEKePbHmI4m9VcemUMS2rEviDowNAFqYi1xjmp diff --git a/src/api/web.rs b/src/api/web.rs index f7fbeec4..f447fb82 100644 --- a/src/api/web.rs +++ b/src/api/web.rs @@ -136,8 +136,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.3.slim.js" => { - Ok((ContentType::JavaScript, include_bytes!("../static/scripts/jquery-3.6.3.slim.js"))) + "jquery-3.6.4.slim.js" => { + Ok((ContentType::JavaScript, include_bytes!("../static/scripts/jquery-3.6.4.slim.js"))) } _ => err!(format!("Static file not found: {filename}")), } diff --git a/src/config.rs b/src/config.rs index 6ed19a79..2a42b5f5 100644 --- a/src/config.rs +++ b/src/config.rs @@ -476,7 +476,7 @@ make_config! { /// provides unauthenticated access to potentially sensitive data. show_password_hint: bool, true, def, false; - /// Admin page token |> The token used to authenticate in this very same page. Changing it here won't deauthorize the current session + /// Admin token/Argon2 PHC |> The plain text token or Argon2 PHC string used to authenticate in this very same page. Changing it here will not deauthorize the current session! admin_token: Pass, true, option; /// Invitation organization name |> Name shown in the invitation emails that don't come from a specific organization @@ -603,7 +603,7 @@ make_config! { /// Global Duo settings (Note that users can override them) duo: _enable_duo { /// Enabled - _enable_duo: bool, true, def, false; + _enable_duo: bool, true, def, true; /// Integration Key duo_ikey: String, true, option; /// Secret Key diff --git a/src/static/scripts/admin.css b/src/static/scripts/admin.css index d700af3c..1db8d4c0 100644 --- a/src/static/scripts/admin.css +++ b/src/static/scripts/admin.css @@ -25,7 +25,7 @@ img { min-width: 85px; max-width: 85px; } -#users-table .vw-ciphers, #orgs-table .vw-users, #orgs-table .vw-ciphers { +#users-table .vw-entries, #orgs-table .vw-users, #orgs-table .vw-entries { min-width: 35px; max-width: 40px; } @@ -53,4 +53,4 @@ img { } .vw-copy-toast { width: 15rem; -} \ No newline at end of file +} diff --git a/src/static/scripts/admin.js b/src/static/scripts/admin.js index 7408c955..a9c19739 100644 --- a/src/static/scripts/admin.js +++ b/src/static/scripts/admin.js @@ -3,16 +3,17 @@ /* exported BASE_URL, _post */ function getBaseUrl() { - // If the base URL is `https://vaultwarden.example.com/base/path/`, + // If the base URL is `https://vaultwarden.example.com/base/path/admin/`, // `window.location.href` should have one of the following forms: // - // - `https://vaultwarden.example.com/base/path/` - // - `https://vaultwarden.example.com/base/path/#/some/route[?queryParam=...]` + // - `https://vaultwarden.example.com/base/path/admin` + // - `https://vaultwarden.example.com/base/path/admin/#/some/route[?queryParam=...]` // // We want to get to just `https://vaultwarden.example.com/base/path`. - const baseUrl = window.location.href; - const adminPos = baseUrl.indexOf("/admin"); - return baseUrl.substring(0, adminPos != -1 ? adminPos : baseUrl.length); + const pathname = window.location.pathname; + const adminPos = pathname.indexOf("/admin"); + const newPathname = pathname.substring(0, adminPos != -1 ? adminPos : pathname.length); + return `${window.location.origin}${newPathname}`; } const BASE_URL = getBaseUrl(); diff --git a/src/static/scripts/datatables.css b/src/static/scripts/datatables.css index d22b2250..0dd6669c 100644 --- a/src/static/scripts/datatables.css +++ b/src/static/scripts/datatables.css @@ -4,10 +4,10 @@ * * To rebuild or modify this file with the latest versions of the included * software please visit: - * https://datatables.net/download/#bs5/dt-1.13.2 + * https://datatables.net/download/#bs5/dt-1.13.4 * * Included libraries: - * DataTables 1.13.2 + * DataTables 1.13.4 */ @charset "UTF-8"; @@ -79,6 +79,7 @@ table.dataTable thead > tr > td.sorting_asc_disabled:before, table.dataTable thead > tr > td.sorting_desc_disabled:before { bottom: 50%; content: "▲"; + content: "▲"/""; } table.dataTable thead > tr > th.sorting:after, table.dataTable thead > tr > th.sorting_asc:after, table.dataTable thead > tr > th.sorting_desc:after, table.dataTable thead > tr > th.sorting_asc_disabled:after, table.dataTable thead > tr > th.sorting_desc_disabled:after, table.dataTable thead > tr > td.sorting:after, @@ -88,6 +89,7 @@ table.dataTable thead > tr > td.sorting_asc_disabled:after, table.dataTable thead > tr > td.sorting_desc_disabled:after { top: 50%; content: "▼"; + content: "▼"/""; } table.dataTable thead > tr > th.sorting_asc:before, table.dataTable thead > tr > th.sorting_desc:after, table.dataTable thead > tr > td.sorting_asc:before, @@ -104,9 +106,9 @@ table.dataTable thead > tr > td:active { outline: none; } -div.dataTables_scrollBody table.dataTable thead > tr > th:before, div.dataTables_scrollBody table.dataTable thead > tr > th:after, -div.dataTables_scrollBody table.dataTable thead > tr > td:before, -div.dataTables_scrollBody table.dataTable thead > tr > td:after { +div.dataTables_scrollBody > table.dataTable > thead > tr > th:before, div.dataTables_scrollBody > table.dataTable > thead > tr > th:after, +div.dataTables_scrollBody > table.dataTable > thead > tr > td:before, +div.dataTables_scrollBody > table.dataTable > thead > tr > td:after { display: none; } @@ -132,7 +134,8 @@ div.dataTables_processing > div:last-child > div { width: 13px; height: 13px; border-radius: 50%; - background: 13 110 253; + background: rgb(13, 110, 253); + background: rgb(var(--dt-row-selected)); animation-timing-function: cubic-bezier(0, 1, 1, 0); } div.dataTables_processing > div:last-child > div:nth-child(1) { diff --git a/src/static/scripts/datatables.js b/src/static/scripts/datatables.js index 9854358e..520de77c 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/#bs5/dt-1.13.2 + * https://datatables.net/download/#bs5/dt-1.13.4 * * Included libraries: - * DataTables 1.13.2 + * DataTables 1.13.4 */ -/*! DataTables 1.13.2 +/*! DataTables 1.13.4 * ©2008-2023 SpryMedia Ltd - datatables.net/license */ /** * @summary DataTables * @description Paginate, search and order HTML tables - * @version 1.13.2 + * @version 1.13.4 * @author SpryMedia Ltd * @contact www.datatables.net * @copyright SpryMedia Ltd. @@ -46,21 +46,28 @@ } else if ( typeof exports === 'object' ) { // CommonJS - module.exports = function (root, $) { - if ( ! root ) { - // CommonJS environments without a window global must pass a - // root. This will give an error otherwise - root = window; - } + // jQuery's factory checks for a global window - if it isn't present then it + // returns a factory function that expects the window object + var jq = require('jquery'); - if ( ! $ ) { - $ = typeof window !== 'undefined' ? // jQuery's factory checks for a global window - require('jquery') : - require('jquery')( root ); - } + if (typeof window !== 'undefined') { + module.exports = function (root, $) { + if ( ! root ) { + // CommonJS environments without a window global must pass a + // root. This will give an error otherwise + root = window; + } - return factory( $, root, root.document ); - }; + if ( ! $ ) { + $ = jq( root ); + } + + return factory( $, root, root.document ); + }; + } + else { + return factory( jq, window, window.document ); + } } else { // Browser @@ -73,6 +80,12 @@ var DataTable = function ( selector, options ) { + // Check if called with a window or jQuery object for DOM less applications + // This is for backwards compatibility + if (DataTable.factory(selector, options)) { + return DataTable; + } + // When creating with `new`, create a new DataTable, returning the API instance if (this instanceof DataTable) { return $(selector).DataTable(options); @@ -1177,6 +1190,7 @@ type: sort !== null ? i+'.@data-'+sort : undefined, filter: filter !== null ? i+'.@data-'+filter : undefined }; + col._isArrayHost = true; _fnColumnOptions( oSettings, i ); } @@ -2365,7 +2379,7 @@ // Indicate if DataTables should read DOM data as an object or array // Used in _fnGetRowElements - if ( typeof mDataSrc !== 'number' ) { + if ( typeof mDataSrc !== 'number' && ! oCol._isArrayHost ) { oSettings._rowReadObject = true; } @@ -5119,7 +5133,8 @@ { return $('
', { 'id': ! settings.aanFeatures.r ? settings.sTableId+'_processing' : null, - 'class': settings.oClasses.sProcessing + 'class': settings.oClasses.sProcessing, + 'role': 'status' } ) .html( settings.oLanguage.sProcessing ) .append('
') @@ -9367,6 +9382,48 @@ + /** + * Set the jQuery or window object to be used by DataTables + * + * @param {*} module Library / container object + * @param {string} type Library or container type `lib` or `win`. + */ + DataTable.use = function (module, type) { + if (type === 'lib' || module.fn) { + $ = module; + } + else if (type == 'win' || module.document) { + window = module; + document = module.document; + } + } + + /** + * CommonJS factory function pass through. This will check if the arguments + * given are a window object or a jQuery object. If so they are set + * accordingly. + * @param {*} root Window + * @param {*} jq jQUery + * @returns {boolean} Indicator + */ + DataTable.factory = function (root, jq) { + var is = false; + + // Test if the first parameter is a window object + if (root && root.document) { + window = root; + document = root.document; + } + + // Test if the second parameter is a jQuery object + if (jq && jq.fn && jq.fn.jquery) { + $ = jq; + is = true; + } + + return is; + } + /** * Provide a common method for plug-ins to check the version of DataTables being * used, in order to ensure compatibility. @@ -9708,7 +9765,7 @@ * @type string * @default Version number */ - DataTable.version = "1.13.2"; + DataTable.version = "1.13.4"; /** * Private data store, containing all of the settings objects that are @@ -14132,7 +14189,7 @@ * * @type string */ - build:"bs5/dt-1.13.2", + build:"bs5/dt-1.13.4", /** @@ -15654,25 +15711,33 @@ } else if ( typeof exports === 'object' ) { // CommonJS - module.exports = function (root, $) { - if ( ! root ) { - // CommonJS environments without a window global must pass a - // root. This will give an error otherwise - root = window; - } - - if ( ! $ ) { - $ = typeof window !== 'undefined' ? // jQuery's factory checks for a global window - require('jquery') : - require('jquery')( root ); - } - + var jq = require('jquery'); + var cjsRequires = function (root, $) { if ( ! $.fn.dataTable ) { require('datatables.net')(root, $); } - - return factory( $, root, root.document ); }; + + if (typeof window !== 'undefined') { + module.exports = function (root, $) { + if ( ! root ) { + // CommonJS environments without a window global must pass a + // root. This will give an error otherwise + root = window; + } + + if ( ! $ ) { + $ = jq( root ); + } + + cjsRequires( root, $ ); + return factory( $, root, root.document ); + }; + } + else { + cjsRequires( window, jq ); + module.exports = factory( jq, window, window.document ); + } } else { // Browser diff --git a/src/static/scripts/jquery-3.6.3.slim.js b/src/static/scripts/jquery-3.6.4.slim.js similarity index 98% rename from src/static/scripts/jquery-3.6.3.slim.js rename to src/static/scripts/jquery-3.6.4.slim.js index d7e1a94c..edf6ce9c 100644 --- a/src/static/scripts/jquery-3.6.3.slim.js +++ b/src/static/scripts/jquery-3.6.4.slim.js @@ -1,5 +1,5 @@ /*! - * 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 + * jQuery JavaScript Library v3.6.4 -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/animatedSelector,-effects/Tween * https://jquery.com/ * * Includes Sizzle.js @@ -9,7 +9,7 @@ * Released under the MIT license * https://jquery.org/license * - * Date: 2022-12-20T21:28Z + * Date: 2023-03-08T15:29Z */ ( function( global, factory ) { @@ -151,7 +151,7 @@ function toType( obj ) { var - 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", + version = "3.6.4 -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/animatedSelector,-effects/Tween", // 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.9 + * Sizzle CSS Selector Engine v2.3.10 * https://sizzlejs.com/ * * Copyright JS Foundation and other contributors * Released under the MIT license * https://js.foundation/ * - * Date: 2022-12-19 + * Date: 2023-02-14 */ ( function( window ) { var i, @@ -633,7 +633,7 @@ var i, whitespace + "+$", "g" ), rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ), - rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + + rleadingCombinator = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*" ), rdescend = new RegExp( whitespace + "|>" ), @@ -850,7 +850,7 @@ function Sizzle( selector, context, results, seed ) { // as such selectors are not recognized by querySelectorAll. // Thanks to Andrew Dupont for this technique. if ( nodeType === 1 && - ( rdescend.test( selector ) || rcombinators.test( selector ) ) ) { + ( rdescend.test( selector ) || rleadingCombinator.test( selector ) ) ) { // Expand context for sibling selectors newContext = rsibling.test( selector ) && testContext( context.parentNode ) || @@ -879,27 +879,6 @@ function Sizzle( selector, context, results, seed ) { } try { - - // `qSA` may not throw for unrecognized parts using forgiving parsing: - // https://drafts.csswg.org/selectors/#forgiving-selector - // like the `:has()` pseudo-class: - // https://drafts.csswg.org/selectors/#relational - // `CSS.supports` is still expected to return `false` then: - // https://drafts.csswg.org/css-conditional-4/#typedef-supports-selector-fn - // https://drafts.csswg.org/css-conditional-4/#dfn-support-selector - if ( support.cssSupportsSelector && - - // eslint-disable-next-line no-undef - !CSS.supports( "selector(:is(" + newSelector + "))" ) ) { - - // Support: IE 11+ - // Throw to get to the same code path as an error directly in qSA. - // Note: once we only support browser supporting - // `CSS.supports('selector(...)')`, we can most likely drop - // the `try-catch`. IE doesn't implement the API. - throw new Error(); - } - push.apply( results, newContext.querySelectorAll( newSelector ) ); @@ -1195,29 +1174,22 @@ setDocument = Sizzle.setDocument = function( node ) { !el.querySelectorAll( ":scope fieldset div" ).length; } ); - // Support: Chrome 105+, Firefox 104+, Safari 15.4+ - // Make sure forgiving mode is not used in `CSS.supports( "selector(...)" )`. - // - // `:is()` uses a forgiving selector list as an argument and is widely - // implemented, so it's a good one to test against. - support.cssSupportsSelector = assert( function() { - /* eslint-disable no-undef */ - - return CSS.supports( "selector(*)" ) && - - // Support: Firefox 78-81 only - // In old Firefox, `:is()` didn't use forgiving parsing. In that case, - // fail this test as there's no selector to test against that. - // `CSS.supports` uses unforgiving parsing - document.querySelectorAll( ":is(:jqfake)" ) && - - // `*` is needed as Safari & newer Chrome implemented something in between - // for `:has()` - it throws in `qSA` if it only contains an unsupported - // argument but multiple ones, one of which is supported, are fine. - // We want to play safe in case `:is()` gets the same treatment. - !CSS.supports( "selector(:is(*,:jqfake))" ); - - /* eslint-enable */ + // Support: Chrome 105 - 110+, Safari 15.4 - 16.3+ + // Make sure the the `:has()` argument is parsed unforgivingly. + // We include `*` in the test to detect buggy implementations that are + // _selectively_ forgiving (specifically when the list includes at least + // one valid selector). + // Note that we treat complete lack of support for `:has()` as if it were + // spec-compliant support, which is fine because use of `:has()` in such + // environments will fail in the qSA path and fall back to jQuery traversal + // anyway. + support.cssHas = assert( function() { + try { + document.querySelector( ":has(*,:jqfake)" ); + return false; + } catch ( e ) { + return true; + } } ); /* Attributes @@ -1486,14 +1458,14 @@ setDocument = Sizzle.setDocument = function( node ) { } ); } - if ( !support.cssSupportsSelector ) { + if ( !support.cssHas ) { - // Support: Chrome 105+, Safari 15.4+ - // `: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(:is(SELECTOR_TO_BE_TESTED))")`, but - // outside that we mark `:has` as buggy. + // Support: Chrome 105 - 110+, Safari 15.4 - 16.3+ + // Our regular `try-catch` mechanism fails to detect natively-unsupported + // pseudo-classes inside `:has()` (such as `:has(:contains("Foo"))`) + // in browsers that parse the `:has()` argument as a forgiving selector list. + // https://drafts.csswg.org/selectors/#relational now requires the argument + // to be parsed unforgivingly, but browsers have not yet fully adjusted. rbuggyQSA.push( ":has" ); } @@ -2406,7 +2378,7 @@ tokenize = Sizzle.tokenize = function( selector, parseOnly ) { matched = false; // Combinators - if ( ( match = rcombinators.exec( soFar ) ) ) { + if ( ( match = rleadingCombinator.exec( soFar ) ) ) { matched = match.shift(); tokens.push( { value: matched, diff --git a/src/static/templates/admin/organizations.hbs b/src/static/templates/admin/organizations.hbs index 9dd86622..7ac2b6ba 100644 --- a/src/static/templates/admin/organizations.hbs +++ b/src/static/templates/admin/organizations.hbs @@ -7,7 +7,7 @@ Organization Users - Ciphers + Entries Attachments Misc Actions @@ -59,7 +59,7 @@ - + diff --git a/src/static/templates/admin/users.hbs b/src/static/templates/admin/users.hbs index bdb2870f..637dfb22 100644 --- a/src/static/templates/admin/users.hbs +++ b/src/static/templates/admin/users.hbs @@ -8,7 +8,7 @@ User Created at Last Active - Ciphers + Entries Attachments Organizations Actions @@ -140,7 +140,7 @@ - +