[ Index ]

PHP Cross Reference of Joomla 4.2.2 documentation

title

Body

[close]

/media/plg_system_webauthn/js/ -> management.js (source)

   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);


Generated: Wed Sep 7 05:41:13 2022 Chilli.vc Blog - For Webmaster,Blog-Writer,System Admin and Domainer