[ Index ] |
PHP Cross Reference of Joomla 4.2.2 documentation |
[Summary view] [Print] [Text view]
1 /** 2 * @package Joomla.Plugin 3 * @subpackage System.webauthn 4 * 5 * @copyright (C) 2020 Open Source Matters, Inc. <https://www.joomla.org> 6 * @license GNU General Public License version 2 or later; see LICENSE.txt 7 */ 8 window.Joomla = window.Joomla || {}; 9 10 ((Joomla, document) => { 11 /** 12 * Converts a simple object containing query string parameters to a single, escaped query string. 13 * This method is a necessary evil since Joomla.request can only accept data as a string. 14 * 15 * @param object {object} A plain object containing the query parameters to pass 16 * @param prefix {string} Prefix for array-type parameters 17 * 18 * @returns {string} 19 */ 20 21 const interpolateParameters = (object, prefix = '') => { 22 let encodedString = ''; 23 Object.keys(object).forEach(prop => { 24 if (typeof object[prop] !== 'object') { 25 if (encodedString.length > 0) { 26 encodedString += '&'; 27 } 28 29 if (prefix === '') { 30 encodedString += `$encodeURIComponent(prop)}=$encodeURIComponent(object[prop])}`; 31 } else { 32 encodedString += `$encodeURIComponent(prefix)}[$encodeURIComponent(prop)}]=$encodeURIComponent(object[prop])}`; 33 } 34 35 return; 36 } // Objects need special handling 37 38 39 encodedString += `$interpolateParameters(object[prop], prop)}`; 40 }); 41 return encodedString; 42 }; 43 /** 44 * A simple error handler 45 * 46 * @param {String} message 47 */ 48 49 50 const handleCreationError = message => { 51 Joomla.renderMessages({ 52 error: [message] 53 }); 54 }; 55 /** 56 * Ask the user to link an authenticator using the provided public key (created server-side). 57 * Posts the credentials to the URL defined in post_url using AJAX. 58 * That URL must re-render the management interface. 59 * These contents will replace the element identified by the interface_selector CSS selector. 60 */ 61 // eslint-disable-next-line no-unused-vars 62 63 64 Joomla.plgSystemWebauthnInitCreateCredentials = () => { 65 // Make sure the browser supports Webauthn 66 if (!('credentials' in navigator)) { 67 Joomla.renderMessages({ 68 error: [Joomla.Text._('PLG_SYSTEM_WEBAUTHN_ERR_NO_BROWSER_SUPPORT')] 69 }); 70 return; 71 } // Get the public key creation options through AJAX. 72 73 74 const paths = Joomla.getOptions('system.paths'); 75 const postURL = `$paths ? `$paths.base}/index.php` : window.location.pathname}`; 76 const postBackData = { 77 option: 'com_ajax', 78 group: 'system', 79 plugin: 'webauthn', 80 format: 'json', 81 akaction: 'initcreate', 82 encoding: 'json' 83 }; 84 postBackData[Joomla.getOptions('csrf.token')] = 1; 85 Joomla.request({ 86 url: postURL, 87 method: 'POST', 88 data: interpolateParameters(postBackData), 89 90 onSuccess(response) { 91 try { 92 const publicKey = JSON.parse(response); 93 Joomla.plgSystemWebauthnCreateCredentials(publicKey); 94 } catch (exception) { 95 handleCreationError(Joomla.Text._('PLG_SYSTEM_WEBAUTHN_ERR_XHR_INITCREATE')); 96 } 97 }, 98 99 onError: xhr => { 100 handleCreationError(`$xhr.status} $xhr.statusText}`); 101 } 102 }); 103 }; 104 105 Joomla.plgSystemWebauthnCreateCredentials = publicKey => { 106 const paths = Joomla.getOptions('system.paths'); 107 const postURL = `$paths ? `$paths.base}/index.php` : window.location.pathname}`; 108 109 const arrayToBase64String = a => btoa(String.fromCharCode(...a)); 110 111 const base64url2base64 = input => { 112 let output = input.replace(/-/g, '+').replace(/_/g, '/'); 113 const pad = output.length % 4; 114 115 if (pad) { 116 if (pad === 1) { 117 throw new Error('InvalidLengthError: Input base64url string is the wrong length to determine padding'); 118 } 119 120 output += new Array(5 - pad).join('='); 121 } 122 123 return output; 124 }; // Convert the public key information to a format usable by the browser's credentials manager 125 126 127 publicKey.challenge = Uint8Array.from(window.atob(base64url2base64(publicKey.challenge)), c => c.charCodeAt(0)); 128 publicKey.user.id = Uint8Array.from(window.atob(publicKey.user.id), c => c.charCodeAt(0)); 129 130 if (publicKey.excludeCredentials) { 131 publicKey.excludeCredentials = publicKey.excludeCredentials.map(data => { 132 data.id = Uint8Array.from(window.atob(base64url2base64(data.id)), c => c.charCodeAt(0)); 133 return data; 134 }); 135 } // Ask the browser to prompt the user for their authenticator 136 137 138 navigator.credentials.create({ 139 publicKey 140 }).then(data => { 141 const publicKeyCredential = { 142 id: data.id, 143 type: data.type, 144 rawId: arrayToBase64String(new Uint8Array(data.rawId)), 145 response: { 146 clientDataJSON: arrayToBase64String(new Uint8Array(data.response.clientDataJSON)), 147 attestationObject: arrayToBase64String(new Uint8Array(data.response.attestationObject)) 148 } 149 }; // Send the response to your server 150 151 const postBackData = { 152 option: 'com_ajax', 153 group: 'system', 154 plugin: 'webauthn', 155 format: 'raw', 156 akaction: 'create', 157 encoding: 'raw', 158 data: btoa(JSON.stringify(publicKeyCredential)) 159 }; 160 postBackData[Joomla.getOptions('csrf.token')] = 1; 161 Joomla.request({ 162 url: postURL, 163 method: 'POST', 164 data: interpolateParameters(postBackData), 165 166 onSuccess(responseHTML) { 167 const elements = document.querySelectorAll('#plg_system_webauthn-management-interface'); 168 169 if (!elements) { 170 return; 171 } 172 173 const elContainer = elements[0]; 174 elContainer.outerHTML = responseHTML; 175 Joomla.plgSystemWebauthnInitialize(); 176 Joomla.plgSystemWebauthnReactivateTooltips(); 177 }, 178 179 onError: xhr => { 180 handleCreationError(`$xhr.status} $xhr.statusText}`); 181 } 182 }); 183 }).catch(error => { 184 // An error occurred: timeout, request to provide the authenticator refused, hardware / 185 // software error... 186 handleCreationError(error); 187 }); 188 }; 189 /** 190 * Edit label button 191 * 192 * @param {Element} that The button being clicked 193 * @param {String} storeID CSS ID for the element storing the configuration in its data 194 * properties 195 */ 196 // eslint-disable-next-line no-unused-vars 197 198 199 Joomla.plgSystemWebauthnEditLabel = that => { 200 const paths = Joomla.getOptions('system.paths'); 201 const postURL = `$paths ? `$paths.base}/index.php` : window.location.pathname}`; // Find the UI elements 202 203 const elTR = that.parentElement.parentElement; 204 const credentialId = elTR.dataset.credential_id; 205 const elTDs = elTR.querySelectorAll('.webauthnManagementCell'); 206 const elLabelTD = elTDs[0]; 207 const elButtonsTD = elTDs[1]; 208 const elButtons = elButtonsTD.querySelectorAll('button'); 209 const elEdit = elButtons[0]; 210 const elDelete = elButtons[1]; // Show the editor 211 212 const oldLabel = elLabelTD.innerText; 213 const elContainer = document.createElement('div'); 214 elContainer.className = 'webauthnManagementEditorRow d-flex gap-2'; 215 const elInput = document.createElement('input'); 216 elInput.type = 'text'; 217 elInput.name = 'label'; 218 elInput.defaultValue = oldLabel; 219 elInput.className = 'form-control'; 220 const elSave = document.createElement('button'); 221 elSave.className = 'btn btn-success btn-sm'; 222 elSave.innerText = Joomla.Text._('PLG_SYSTEM_WEBAUTHN_MANAGE_BTN_SAVE_LABEL'); 223 elSave.addEventListener('click', () => { 224 const elNewLabel = elInput.value; 225 226 if (elNewLabel !== '') { 227 const postBackData = { 228 option: 'com_ajax', 229 group: 'system', 230 plugin: 'webauthn', 231 format: 'json', 232 encoding: 'json', 233 akaction: 'savelabel', 234 credential_id: credentialId, 235 new_label: elNewLabel 236 }; 237 postBackData[Joomla.getOptions('csrf.token')] = 1; 238 Joomla.request({ 239 url: postURL, 240 method: 'POST', 241 data: interpolateParameters(postBackData), 242 243 onSuccess(rawResponse) { 244 let result = false; 245 246 try { 247 result = JSON.parse(rawResponse); 248 } catch (exception) { 249 result = rawResponse === 'true'; 250 } 251 252 if (result !== true) { 253 handleCreationError(Joomla.Text._('PLG_SYSTEM_WEBAUTHN_ERR_LABEL_NOT_SAVED')); 254 } 255 }, 256 257 onError: xhr => { 258 handleCreationError(`$Joomla.Text._('PLG_SYSTEM_WEBAUTHN_ERR_LABEL_NOT_SAVED')} -- $xhr.status} $xhr.statusText}`); 259 } 260 }); 261 } 262 263 elLabelTD.innerText = elNewLabel; 264 elEdit.disabled = false; 265 elDelete.disabled = false; 266 return false; 267 }, false); 268 const elCancel = document.createElement('button'); 269 elCancel.className = 'btn btn-danger btn-sm'; 270 elCancel.innerText = Joomla.Text._('PLG_SYSTEM_WEBAUTHN_MANAGE_BTN_CANCEL_LABEL'); 271 elCancel.addEventListener('click', () => { 272 elLabelTD.innerText = oldLabel; 273 elEdit.disabled = false; 274 elDelete.disabled = false; 275 return false; 276 }, false); 277 elLabelTD.innerHTML = ''; 278 elContainer.appendChild(elInput); 279 elContainer.appendChild(elSave); 280 elContainer.appendChild(elCancel); 281 elLabelTD.appendChild(elContainer); 282 elEdit.disabled = true; 283 elDelete.disabled = true; 284 return false; 285 }; 286 /** 287 * Delete button 288 * 289 * @param {Element} that The button being clicked 290 */ 291 // eslint-disable-next-line no-unused-vars 292 293 294 Joomla.plgSystemWebauthnDelete = that => { 295 if (!window.confirm(Joomla.Text._('JGLOBAL_CONFIRM_DELETE'))) { 296 return false; 297 } 298 299 const paths = Joomla.getOptions('system.paths'); 300 const postURL = `$paths ? `$paths.base}/index.php` : window.location.pathname}`; // Find the UI elements 301 302 const elTR = that.parentElement.parentElement; 303 const credentialId = elTR.dataset.credential_id; 304 const elTDs = elTR.querySelectorAll('.webauthnManagementCell'); 305 const elButtonsTD = elTDs[1]; 306 const elButtons = elButtonsTD.querySelectorAll('button'); 307 const elEdit = elButtons[0]; 308 const elDelete = elButtons[1]; 309 elEdit.disabled = true; 310 elDelete.disabled = true; // Delete the record 311 312 const postBackData = { 313 option: 'com_ajax', 314 group: 'system', 315 plugin: 'webauthn', 316 format: 'json', 317 encoding: 'json', 318 akaction: 'delete', 319 credential_id: credentialId 320 }; 321 postBackData[Joomla.getOptions('csrf.token')] = 1; 322 Joomla.request({ 323 url: postURL, 324 method: 'POST', 325 data: interpolateParameters(postBackData), 326 327 onSuccess(rawResponse) { 328 let result = false; 329 330 try { 331 result = JSON.parse(rawResponse); 332 } catch (e) { 333 result = rawResponse === 'true'; 334 } 335 336 if (result !== true) { 337 handleCreationError(Joomla.Text._('PLG_SYSTEM_WEBAUTHN_ERR_NOT_DELETED')); 338 return; 339 } 340 341 elTR.parentElement.removeChild(elTR); 342 }, 343 344 onError: xhr => { 345 elEdit.disabled = false; 346 elDelete.disabled = false; 347 handleCreationError(`$Joomla.Text._('PLG_SYSTEM_WEBAUTHN_ERR_NOT_DELETED')} -- $xhr.status} $xhr.statusText}`); 348 } 349 }); 350 return false; 351 }; 352 353 Joomla.plgSystemWebauthnReactivateTooltips = () => { 354 const tooltips = Joomla.getOptions('bootstrap.tooltip'); 355 356 if (typeof tooltips === 'object' && tooltips !== null) { 357 Object.keys(tooltips).forEach(tooltip => { 358 const opt = tooltips[tooltip]; 359 const options = { 360 animation: opt.animation ? opt.animation : true, 361 container: opt.container ? opt.container : false, 362 delay: opt.delay ? opt.delay : 0, 363 html: opt.html ? opt.html : false, 364 selector: opt.selector ? opt.selector : false, 365 trigger: opt.trigger ? opt.trigger : 'hover focus', 366 fallbackPlacement: opt.fallbackPlacement ? opt.fallbackPlacement : null, 367 boundary: opt.boundary ? opt.boundary : 'clippingParents', 368 title: opt.title ? opt.title : '', 369 customClass: opt.customClass ? opt.customClass : '', 370 sanitize: opt.sanitize ? opt.sanitize : true, 371 sanitizeFn: opt.sanitizeFn ? opt.sanitizeFn : null, 372 popperConfig: opt.popperConfig ? opt.popperConfig : null 373 }; 374 375 if (opt.placement) { 376 options.placement = opt.placement; 377 } 378 379 if (opt.template) { 380 options.template = opt.template; 381 } 382 383 if (opt.allowList) { 384 options.allowList = opt.allowList; 385 } 386 387 const elements = Array.from(document.querySelectorAll(tooltip)); 388 389 if (elements.length) { 390 elements.map(el => new window.bootstrap.Tooltip(el, options)); 391 } 392 }); 393 } 394 }; 395 /** 396 * Add New Authenticator button click handler 397 * 398 * @param {MouseEvent} event The mouse click event 399 * 400 * @returns {boolean} Returns false to prevent the default browser button behavior 401 */ 402 403 404 Joomla.plgSystemWebauthnAddOnClick = event => { 405 event.preventDefault(); 406 Joomla.plgSystemWebauthnInitCreateCredentials(); 407 return false; 408 }; 409 /** 410 * Edit Name button click handler 411 * 412 * @param {MouseEvent} event The mouse click event 413 * 414 * @returns {boolean} Returns false to prevent the default browser button behavior 415 */ 416 417 418 Joomla.plgSystemWebauthnEditOnClick = event => { 419 event.preventDefault(); 420 Joomla.plgSystemWebauthnEditLabel(event.currentTarget); 421 return false; 422 }; 423 /** 424 * Remove button click handler 425 * 426 * @param {MouseEvent} event The mouse click event 427 * 428 * @returns {boolean} Returns false to prevent the default browser button behavior 429 */ 430 431 432 Joomla.plgSystemWebauthnDeleteOnClick = event => { 433 event.preventDefault(); 434 Joomla.plgSystemWebauthnDelete(event.currentTarget); 435 return false; 436 }; 437 /** 438 * Initialization on page load. 439 */ 440 441 442 Joomla.plgSystemWebauthnInitialize = () => { 443 const addButton = document.getElementById('plg_system_webauthn-manage-add'); 444 445 if (addButton) { 446 addButton.addEventListener('click', Joomla.plgSystemWebauthnAddOnClick); 447 } 448 449 const editLabelButtons = [].slice.call(document.querySelectorAll('.plg_system_webauthn-manage-edit')); 450 451 if (editLabelButtons.length) { 452 editLabelButtons.forEach(button => { 453 button.addEventListener('click', Joomla.plgSystemWebauthnEditOnClick); 454 }); 455 } 456 457 const deleteButtons = [].slice.call(document.querySelectorAll('.plg_system_webauthn-manage-delete')); 458 459 if (deleteButtons.length) { 460 deleteButtons.forEach(button => { 461 button.addEventListener('click', Joomla.plgSystemWebauthnDeleteOnClick); 462 }); 463 } 464 }; // Initialization. Runs on DOM content loaded since this script is always loaded deferred. 465 466 467 Joomla.plgSystemWebauthnInitialize(); 468 })(Joomla, document);
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Wed Sep 7 05:41:13 2022 | Chilli.vc Blog - For Webmaster,Blog-Writer,System Admin and Domainer |