diff options
author | dizzy <diosmosis@users.noreply.github.com> | 2022-04-06 16:42:49 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-04-06 16:42:49 +0300 |
commit | 698c83bb1e411b18c6ea7c4d1df8668f9f432855 (patch) | |
tree | 947d5becafe4844bce9fdbae3c799b027ea4114f /plugins/TwoFactorAuth | |
parent | 59f26bc0f5436426d5b42876a4c535973b519c2f (diff) |
[Vue] migrate SetupTwoFactorAuthController to Vue (#19024)
* finish migration and get to build
* test manually and fix UI tests
* remove whole paragraph in test
* update screenshots
* remove unneeded twig template
Co-authored-by: sgiehl <stefan@matomo.org>
Diffstat (limited to 'plugins/TwoFactorAuth')
20 files changed, 1000 insertions, 184 deletions
diff --git a/plugins/TwoFactorAuth/TwoFactorAuth.php b/plugins/TwoFactorAuth/TwoFactorAuth.php index 0b28bc6ef8..1c70efc08f 100644 --- a/plugins/TwoFactorAuth/TwoFactorAuth.php +++ b/plugins/TwoFactorAuth/TwoFactorAuth.php @@ -35,10 +35,36 @@ class TwoFactorAuth extends \Piwik\Plugin 'API.UsersManager.createAppSpecificTokenAuth.end' => 'onCreateAppSpecificTokenAuth', 'Request.dispatch.end' => array('function' => 'onRequestDispatchEnd', 'after' => true), 'Template.userSecurity.afterPassword' => 'render2FaUserSettings', - 'Login.authenticate.processSuccessfulSession.end' => 'onSuccessfulSession' + 'Login.authenticate.processSuccessfulSession.end' => 'onSuccessfulSession', + 'Translate.getClientSideTranslationKeys' => 'getClientSideTranslationKeys', ); } + public function getClientSideTranslationKeys(&$translations) + { + $translations[] = 'TwoFactorAuth_WarningChangingConfiguredDevice'; + $translations[] = 'TwoFactorAuth_SetupIntroFollowSteps'; + $translations[] = 'TwoFactorAuth_StepX'; + $translations[] = 'TwoFactorAuth_RecoveryCodes'; + $translations[] = 'TwoFactorAuth_RecoveryCodesExplanation'; + $translations[] = 'TwoFactorAuth_RecoveryCodesSecurity'; + $translations[] = 'TwoFactorAuth_RecoveryCodesAllUsed'; + $translations[] = 'General_Download'; + $translations[] = 'General_Print'; + $translations[] = 'General_Copy'; + $translations[] = 'TwoFactorAuth_SetupBackupRecoveryCodes'; + $translations[] = 'General_Next'; + $translations[] = 'TwoFactorAuth_SetupAuthenticatorOnDeviceStep1'; + $translations[] = 'General_Or'; + $translations[] = 'TwoFactorAuth_ConfirmSetup'; + $translations[] = 'TwoFactorAuth_VerifyAuthCodeIntro'; + $translations[] = 'TwoFactorAuth_AuthenticationCode'; + $translations[] = 'TwoFactorAuth_VerifyAuthCodeHelp'; + $translations[] = 'General_Confirm'; + $translations[] = 'TwoFactorAuth_SetupAuthenticatorOnDeviceStep2'; + $translations[] = 'TwoFactorAuth_SetupAuthenticatorOnDevice'; + } + public function getStylesheetFiles(&$stylesheets) { $stylesheets[] = "plugins/TwoFactorAuth/stylesheets/twofactorauth.less"; @@ -47,7 +73,6 @@ class TwoFactorAuth extends \Piwik\Plugin public function getJsFiles(&$jsFiles) { $jsFiles[] = "plugins/TwoFactorAuth/javascripts/twofactorauth.js"; - $jsFiles[] = "plugins/TwoFactorAuth/angularjs/setuptwofactor/setuptwofactor.controller.js"; $jsFiles[] = "node_modules/qrcodejs2/qrcode.min.js"; } diff --git a/plugins/TwoFactorAuth/angularjs/setuptwofactor/setuptwofactor.controller.js b/plugins/TwoFactorAuth/angularjs/setuptwofactor/setuptwofactor.controller.js deleted file mode 100644 index 9055b8a7c9..0000000000 --- a/plugins/TwoFactorAuth/angularjs/setuptwofactor/setuptwofactor.controller.js +++ /dev/null @@ -1,60 +0,0 @@ -/*! - * Matomo - free/libre analytics platform - * - * @link https://matomo.org - * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later - */ - -(function () { - angular.module('piwikApp').controller('SetupTwoFactorAuthController', SetupTwoFactorAuthController); - - SetupTwoFactorAuthController.$inject = ['$timeout', 'piwik', '$scope']; - - function SetupTwoFactorAuthController($timeout, piwik, $scope) { - - var self = this; - this.step = 1; - this.hasDownloadedRecoveryCode = false; - - this.scrollToEnd = function () { - $timeout(function () { - var id = ''; - if (self.step === 2) { - id = '#twoFactorStep2'; - } else if (self.step === 3) { - id = '#twoFactorStep3'; - } - if (id) { - piwik.helper.lazyScrollTo(id, 50, true); - } - }, 50); - } - - this.nextStep = function () - { - this.step++; - this.scrollToEnd(); - } - - $timeout(function () { - - var qrcode = new QRCode(document.getElementById("qrcode"), { - text: window.twoFaBarCodeSetupUrl - }); - angular.element('#qrcode').attr('title', ''); // do not show secret on hover - - angular.element('.backupRecoveryCode').click(function () { - self.hasDownloadedRecoveryCode = true; - $timeout(function () { - $scope.$apply(); - }, 1); - }); - - if (angular.element('.setupTwoFactorAuthentication .message_container').length) { - // user entered something wrong - self.step = 3; - self.scrollToEnd(); - } - }); - } -})(); diff --git a/plugins/TwoFactorAuth/templates/_setupTwoFactorAuth.twig b/plugins/TwoFactorAuth/templates/_setupTwoFactorAuth.twig index 3bfaddbd3a..c1a1e123c3 100644 --- a/plugins/TwoFactorAuth/templates/_setupTwoFactorAuth.twig +++ b/plugins/TwoFactorAuth/templates/_setupTwoFactorAuth.twig @@ -1,74 +1,16 @@ -<div ng-controller="SetupTwoFactorAuthController as setup2fa" class="setupTwoFactorAuthentication"> - {% if isAlreadyUsing2fa %} - <div class="alert alert-warning">{{ 'TwoFactorAuth_WarningChangingConfiguredDevice'|translate }}</div> - {% endif %} - - <p> - {{ 'TwoFactorAuth_SetupIntroFollowSteps'|translate }} - </p> - - <h2> - {{ 'TwoFactorAuth_StepX'|translate(1) }} - {{ 'TwoFactorAuth_RecoveryCodes'|translate }} - </h2> - {% include '@TwoFactorAuth/_showRecoveryCodes.twig' %} - - <div class="alert alert-info backupRecoveryCodesAlert" ng-show="setup2fa.step == 1">{{ 'TwoFactorAuth_SetupBackupRecoveryCodes'|translate }}</div> - - <p><button ng-click="setup2fa.nextStep()" ng-show="setup2fa.step == 1" ng-disabled="!setup2fa.hasDownloadedRecoveryCode" class="btn goToStep2">{{ 'General_Next'|translate }}</button></p> - - <a name="twoFactorStep2" id="twoFactorStep2" style="opacity: 0"></a> - <div ng-show="setup2fa.step >= 2"> - <h2> - {{ 'TwoFactorAuth_StepX'|translate(2) }} - {{ 'TwoFactorAuth_SetupAuthenticatorOnDevice'|translate }} - </h2> - <p>{{ 'TwoFactorAuth_SetupAuthenticatorOnDeviceStep1'|translate }} <a rel="noreferrer noopener" href="https://github.com/andOTP/andOTP#downloads">andOTP</a>, <a rel="noreferrer noopener" href="https://authy.com/guides/github/">Authy</a>, <a rel="noreferrer noopener" href="https://support.1password.com/one-time-passwords/">1Password</a>, <a rel="noreferrer noopener" href="https://helpdesk.lastpass.com/multifactor-authentication-options/lastpass-authenticator/">LastPass Authenticator</a>, {{ 'General_Or'|translate }} <a rel="noreferrer noopener" href="https://support.google.com/accounts/answer/1066447">Google Authenticator</a>. - </p> - <p>{{ 'TwoFactorAuth_SetupAuthenticatorOnDeviceStep2'|translate('<a href="javascript:void(0)" onclick="piwikHelper.modalConfirm(\'#setupTwoFAsecretConfirm\')">', '</a>')|raw }}<br/> - <div id="qrcode" title=""></div> - <br /> - <button ng-show="setup2fa.step == 2" ng-click="setup2fa.nextStep()" class="btn goToStep3">{{ 'General_Next'|translate }}</button> - </p> - </div> - - <a name="twoFactorStep3" id="twoFactorStep3" style="opacity: 0"></a> - <div ng-show="setup2fa.step >= 3"> - <h2>{{ 'TwoFactorAuth_StepX'|translate(3) }} - {{ 'TwoFactorAuth_ConfirmSetup'|translate }}</h2> - <p>{{ 'TwoFactorAuth_VerifyAuthCodeIntro'|translate }}</p> - - {% if AccessErrorString %} - <div class="message_container"> - <div piwik-notification - noclear="true" - context="error"> - <strong>{{ 'General_Error'|translate }}</strong>: {{ AccessErrorString|raw }}<br/> - </div> - </div> - {% endif %} - - <form method="post" - action="{{ linkTo({'module': 'TwoFactorAuth', 'action': submitAction}) }}" - class="setupConfirmAuthCodeForm" - autocorrect="off" autocapitalize="none" - autocomplete="off" - > - <div piwik-field uicontrol="text" name="authCode" maxlength="6" - data-title="{{ 'TwoFactorAuth_AuthenticationCode'|translate|e('html_attr') }}" - ng-model="setup2fa.authCode" - inline-help="{{ 'TwoFactorAuth_VerifyAuthCodeHelp'|translate|e('html_attr') }}" - placeholder="123456"> - </div> - - <input type="hidden" name="authCodeNonce" value="{{ authCodeNonce|e('html_attr') }}"> - <input type="submit" ng-disabled="setup2fa.authCode.length != 6" - class="btn confirmAuthCode" value="{{ 'General_Confirm'|translate }}"> - </form> - </div> - -</div> <script type="text/javascript"> window.twoFaBarCodeSetupUrl = {{ twoFaBarCodeSetupUrl|json_encode|raw }}; </script> +<div + vue-entry="TwoFactorAuth.SetupTwoFactorAuth" + is-already-using2fa="{{ isAlreadyUsing2fa|default(null)|json_encode|e('html_attr') }}" + access-error-string="{{ AccessErrorString|default(null)|json_encode|e('html_attr') }}" + submit-action="{{ submitAction|default(null)|json_encode|e('html_attr') }}" + auth-code-nonce="{{ authCodeNonce|default(null)|json_encode|e('html_attr') }}" + codes="{{ codes|default(null)|json_encode|e('html_attr') }}" +></div> + <div id="setupTwoFAsecretConfirm" class="ui-confirm"> <h2>{{ 'TwoFactorAuth_Your2FaAuthSecret'|translate }}</h2> <p style="text-align: center;"><code piwik-select-on-focus style="font-size: 30px;">{{ newSecret }}</code></p> diff --git a/plugins/TwoFactorAuth/templates/_showRecoveryCodes.twig b/plugins/TwoFactorAuth/templates/_showRecoveryCodes.twig deleted file mode 100644 index 0583089ba8..0000000000 --- a/plugins/TwoFactorAuth/templates/_showRecoveryCodes.twig +++ /dev/null @@ -1,38 +0,0 @@ - -<script type="text/javascript"> - function copyRecoveryCodesToClipboard() - { - var textarea = document.createElement('textarea'); - textarea.value = {{ codes|join("\n")|json_encode|raw }}; - textarea.setAttribute('readonly', ''); - textarea.style.position = 'absolute'; - textarea.style.left = '-9999px'; - document.body.appendChild(textarea); - textarea.select(); - document.execCommand('copy'); - document.body.removeChild(textarea); - } - function downloadRecoveryCodes() - { - piwikHelper.sendContentAsDownload('analytics_recovery_codes.txt', {{ codes|join("\n")|json_encode|raw }}); - } -</script> - - <p>{{ 'TwoFactorAuth_RecoveryCodesExplanation'|translate }}<br /><br /></p> - <div class="alert alert-warning">{{ 'TwoFactorAuth_RecoveryCodesSecurity'|translate }}</div> - - {% if codes|length > 0 %} - <ul piwik-select-on-focus class="twoFactorRecoveryCodes browser-default">{% for code in codes %} - <li>{{ code|upper|split('', 4)|join('-') }}</li> - {% endfor %} - </ul> - {% else %} - <div class="alert alert-danger">{{ 'TwoFactorAuth_RecoveryCodesAllUsed'|translate }}</div> - {% endif %} - - <p> - <br /> - <input type="button" class="btn backupRecoveryCode" onclick="downloadRecoveryCodes()" value="{{ 'General_Download'|translate }}"> - <input type="button" class="btn backupRecoveryCode" onclick="window.print()" value="{{ 'General_Print'|translate }}"> - <input type="button" class="btn backupRecoveryCode" onclick="copyRecoveryCodesToClipboard()" value="{{ 'General_Copy'|translate }}"> - </p> diff --git a/plugins/TwoFactorAuth/templates/showRecoveryCodes.twig b/plugins/TwoFactorAuth/templates/showRecoveryCodes.twig index 907048126a..319d2170b4 100644 --- a/plugins/TwoFactorAuth/templates/showRecoveryCodes.twig +++ b/plugins/TwoFactorAuth/templates/showRecoveryCodes.twig @@ -20,7 +20,7 @@ <div piwik-content-block content-title="{{ 'TwoFactorAuth_TwoFactorAuthentication'|translate }} - {{ 'TwoFactorAuth_RecoveryCodes'|translate }}"> - {% include '@TwoFactorAuth/_showRecoveryCodes.twig' %} + <div vue-entry="TwoFactorAuth.ShowRecoveryCodes" codes="{{ codes|default(null)|json_encode|e('html_attr') }}"></div> <h2>{{ 'TwoFactorAuth_GenerateNewRecoveryCodes'|translate }}</h2> <p>{{ 'TwoFactorAuth_GenerateNewRecoveryCodesInfo'|translate }}<br /><br /></p> @@ -38,5 +38,5 @@ <input type="submit" class="btn" value="{{ 'TwoFactorAuth_GenerateNewRecoveryCodes'|translate }}"> </form> - <div> + <div> {% endblock %}
\ No newline at end of file diff --git a/plugins/TwoFactorAuth/tests/UI/TwoFactorAuth_spec.js b/plugins/TwoFactorAuth/tests/UI/TwoFactorAuth_spec.js index 3ace46a354..a9ca4e1d31 100644 --- a/plugins/TwoFactorAuth/tests/UI/TwoFactorAuth_spec.js +++ b/plugins/TwoFactorAuth/tests/UI/TwoFactorAuth_spec.js @@ -223,7 +223,7 @@ describe("TwoFactorAuth", function () { await page.click('.setupTwoFactorAuthentication .goToStep2'); await page.waitForNetworkIdle(); await page.evaluate(function () { - $('#qrcode').hide(); + $('#qrcode').parent().hide(); }); const element = await page.$('#content'); expect(await element.screenshot()).to.matchImage('twofa_setup_step2'); diff --git a/plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_show_recovery_codes_step2.png b/plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_show_recovery_codes_step2.png index 7c3fe4293c..f954cb94b3 100644 --- a/plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_show_recovery_codes_step2.png +++ b/plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_show_recovery_codes_step2.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5e6cd69059dc9ddab922f00acdc798346d308ee7e0c0128f29e052c4d1d39f0b -size 60377 +oid sha256:d48acfb0bee9aea9e3e2b9847dba1f1ab62ae0aee6d88b12ff134cf06dd9da29 +size 60139 diff --git a/plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_twofa_forced_step1.png b/plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_twofa_forced_step1.png index 6ef0195023..b522f495a0 100644 --- a/plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_twofa_forced_step1.png +++ b/plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_twofa_forced_step1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e20d431ded80bf04f6c261d237e7599719af8eabac3821e3688ae6f17633da00 -size 92532 +oid sha256:d418c2c6a6ebf3e7c95ed839e18f330aeed156d2cc1371fa4ec008d1fb883d54 +size 92484 diff --git a/plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_twofa_forced_step2.png b/plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_twofa_forced_step2.png index 29d76b2fdf..77a35e6ce5 100644 --- a/plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_twofa_forced_step2.png +++ b/plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_twofa_forced_step2.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8617fad5da65d7bf308722f3002371f587418318ad4a41c7c9e0b854c18177a4 -size 125538 +oid sha256:7926caf73a1860283e2eef62d5aac0dea3bdd94aada0dd41bcece2e5f9c3197c +size 125529 diff --git a/plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_twofa_forced_step3.png b/plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_twofa_forced_step3.png index 2c4894f065..f017cb3c08 100644 --- a/plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_twofa_forced_step3.png +++ b/plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_twofa_forced_step3.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:857005b9df442b2e755f8a969f55c167aa7f44e91e154ddd470c29d3b9723c77 -size 162355 +oid sha256:c1c18522b3cec0b11757039987abd9ceba1b125c3e379657bce51522597c3ec2 +size 162328 diff --git a/plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_twofa_setup_step1.png b/plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_twofa_setup_step1.png index 6f9b490fcf..81943adc30 100644 --- a/plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_twofa_setup_step1.png +++ b/plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_twofa_setup_step1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b5652c92627d5e36e653847fc1978e26885b8c35ab8591d538feeed68ff67e0b -size 67109 +oid sha256:f8b37ab111c80837ddb451bd9c7eb7ab9e7dd9bc8994476088a6cfbaa4c5d6de +size 66859 diff --git a/plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_twofa_setup_step2.png b/plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_twofa_setup_step2.png index 2ee348fca1..d85d5a5bac 100644 --- a/plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_twofa_setup_step2.png +++ b/plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_twofa_setup_step2.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2792376e2b566f4bc16e271042538b1c878200f89b3ae53192a72bd9dc0908c6 -size 86462 +oid sha256:b1f3c1af5c9a2f6c2ce1c9222eec9d833c38f61b689f2aa1118461f8499aba3d +size 86228 diff --git a/plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_twofa_setup_step3.png b/plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_twofa_setup_step3.png index cf227b0622..389351c553 100644 --- a/plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_twofa_setup_step3.png +++ b/plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_twofa_setup_step3.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:52ec37f081e5a1b1624cf3f93551550cd87a97ae5eafc1c75b8fcefc39f2c68a -size 114302 +oid sha256:1188f261fc033679860dbdc5bf6cdaf9a0f0d4246f6435e04882390d73c4aadb +size 114098 diff --git a/plugins/TwoFactorAuth/vue/dist/TwoFactorAuth.umd.js b/plugins/TwoFactorAuth/vue/dist/TwoFactorAuth.umd.js new file mode 100644 index 0000000000..a6dbf9bb79 --- /dev/null +++ b/plugins/TwoFactorAuth/vue/dist/TwoFactorAuth.umd.js @@ -0,0 +1,565 @@ +(function webpackUniversalModuleDefinition(root, factory) { + if(typeof exports === 'object' && typeof module === 'object') + module.exports = factory(require("CoreHome"), require("vue"), require("CorePluginsAdmin")); + else if(typeof define === 'function' && define.amd) + define(["CoreHome", , "CorePluginsAdmin"], factory); + else if(typeof exports === 'object') + exports["TwoFactorAuth"] = factory(require("CoreHome"), require("vue"), require("CorePluginsAdmin")); + else + root["TwoFactorAuth"] = factory(root["CoreHome"], root["Vue"], root["CorePluginsAdmin"]); +})((typeof self !== 'undefined' ? self : this), function(__WEBPACK_EXTERNAL_MODULE__19dc__, __WEBPACK_EXTERNAL_MODULE__8bbf__, __WEBPACK_EXTERNAL_MODULE_a5a2__) { +return /******/ (function(modules) { // webpackBootstrap +/******/ // The module cache +/******/ var installedModules = {}; +/******/ +/******/ // The require function +/******/ function __webpack_require__(moduleId) { +/******/ +/******/ // Check if module is in cache +/******/ if(installedModules[moduleId]) { +/******/ return installedModules[moduleId].exports; +/******/ } +/******/ // Create a new module (and put it into the cache) +/******/ var module = installedModules[moduleId] = { +/******/ i: moduleId, +/******/ l: false, +/******/ exports: {} +/******/ }; +/******/ +/******/ // Execute the module function +/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); +/******/ +/******/ // Flag the module as loaded +/******/ module.l = true; +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/******/ +/******/ // expose the modules object (__webpack_modules__) +/******/ __webpack_require__.m = modules; +/******/ +/******/ // expose the module cache +/******/ __webpack_require__.c = installedModules; +/******/ +/******/ // define getter function for harmony exports +/******/ __webpack_require__.d = function(exports, name, getter) { +/******/ if(!__webpack_require__.o(exports, name)) { +/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter }); +/******/ } +/******/ }; +/******/ +/******/ // define __esModule on exports +/******/ __webpack_require__.r = function(exports) { +/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { +/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); +/******/ } +/******/ Object.defineProperty(exports, '__esModule', { value: true }); +/******/ }; +/******/ +/******/ // create a fake namespace object +/******/ // mode & 1: value is a module id, require it +/******/ // mode & 2: merge all properties of value into the ns +/******/ // mode & 4: return value when already ns object +/******/ // mode & 8|1: behave like require +/******/ __webpack_require__.t = function(value, mode) { +/******/ if(mode & 1) value = __webpack_require__(value); +/******/ if(mode & 8) return value; +/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; +/******/ var ns = Object.create(null); +/******/ __webpack_require__.r(ns); +/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value }); +/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key)); +/******/ return ns; +/******/ }; +/******/ +/******/ // getDefaultExport function for compatibility with non-harmony modules +/******/ __webpack_require__.n = function(module) { +/******/ var getter = module && module.__esModule ? +/******/ function getDefault() { return module['default']; } : +/******/ function getModuleExports() { return module; }; +/******/ __webpack_require__.d(getter, 'a', getter); +/******/ return getter; +/******/ }; +/******/ +/******/ // Object.prototype.hasOwnProperty.call +/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; +/******/ +/******/ // __webpack_public_path__ +/******/ __webpack_require__.p = "plugins/TwoFactorAuth/vue/dist/"; +/******/ +/******/ +/******/ // Load entry module and return exports +/******/ return __webpack_require__(__webpack_require__.s = "fae3"); +/******/ }) +/************************************************************************/ +/******/ ({ + +/***/ "19dc": +/***/ (function(module, exports) { + +module.exports = __WEBPACK_EXTERNAL_MODULE__19dc__; + +/***/ }), + +/***/ "8bbf": +/***/ (function(module, exports) { + +module.exports = __WEBPACK_EXTERNAL_MODULE__8bbf__; + +/***/ }), + +/***/ "a5a2": +/***/ (function(module, exports) { + +module.exports = __WEBPACK_EXTERNAL_MODULE_a5a2__; + +/***/ }), + +/***/ "fae3": +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +// ESM COMPAT FLAG +__webpack_require__.r(__webpack_exports__); + +// EXPORTS +__webpack_require__.d(__webpack_exports__, "ShowRecoveryCodes", function() { return /* reexport */ ShowRecoveryCodes; }); +__webpack_require__.d(__webpack_exports__, "SetupTwoFactorAuth", function() { return /* reexport */ SetupTwoFactorAuth; }); + +// CONCATENATED MODULE: ./node_modules/@vue/cli-service/lib/commands/build/setPublicPath.js +// This file is imported into lib/wc client bundles. + +if (typeof window !== 'undefined') { + var currentScript = window.document.currentScript + if (false) { var getCurrentScript; } + + var src = currentScript && currentScript.src.match(/(.+\/)[^/]+\.js(\?.*)?$/) + if (src) { + __webpack_require__.p = src[1] // eslint-disable-line + } +} + +// Indicate to webpack that this file can be concatenated +/* harmony default export */ var setPublicPath = (null); + +// EXTERNAL MODULE: external {"commonjs":"vue","commonjs2":"vue","root":"Vue"} +var external_commonjs_vue_commonjs2_vue_root_Vue_ = __webpack_require__("8bbf"); + +// CONCATENATED MODULE: ./node_modules/@vue/cli-plugin-babel/node_modules/cache-loader/dist/cjs.js??ref--12-0!./node_modules/@vue/cli-plugin-babel/node_modules/thread-loader/dist/cjs.js!./node_modules/babel-loader/lib!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist/templateLoader.js??ref--6!./node_modules/@vue/cli-service/node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist??ref--0-1!./plugins/TwoFactorAuth/vue/src/ShowRecoveryCodes/ShowRecoveryCodes.vue?vue&type=template&id=e6e67cfe + + +var _hoisted_1 = /*#__PURE__*/Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("br", null, null, -1); + +var _hoisted_2 = /*#__PURE__*/Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("br", null, null, -1); + +var _hoisted_3 = { + class: "alert alert-warning" +}; +var _hoisted_4 = { + key: 0, + class: "twoFactorRecoveryCodes browser-default" +}; +var _hoisted_5 = { + key: 1, + class: "alert alert-danger" +}; + +var _hoisted_6 = /*#__PURE__*/Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("br", null, null, -1); + +var _hoisted_7 = ["value"]; +var _hoisted_8 = ["value"]; +var _hoisted_9 = ["value"]; +function render(_ctx, _cache, $props, $setup, $data, $options) { + var _ctx$codes; + + var _directive_select_on_focus = Object(external_commonjs_vue_commonjs2_vue_root_Vue_["resolveDirective"])("select-on-focus"); + + return Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("div", null, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("p", null, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createTextVNode"])(Object(external_commonjs_vue_commonjs2_vue_root_Vue_["toDisplayString"])(_ctx.translate('TwoFactorAuth_RecoveryCodesExplanation')), 1), _hoisted_1, _hoisted_2]), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("div", _hoisted_3, Object(external_commonjs_vue_commonjs2_vue_root_Vue_["toDisplayString"])(_ctx.translate('TwoFactorAuth_RecoveryCodesSecurity')), 1), (_ctx$codes = _ctx.codes) !== null && _ctx$codes !== void 0 && _ctx$codes.length ? Object(external_commonjs_vue_commonjs2_vue_root_Vue_["withDirectives"])((Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("ul", _hoisted_4, [(Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(true), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])(external_commonjs_vue_commonjs2_vue_root_Vue_["Fragment"], null, Object(external_commonjs_vue_commonjs2_vue_root_Vue_["renderList"])(_ctx.codes, function (code, index) { + return Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("li", { + key: index + }, Object(external_commonjs_vue_commonjs2_vue_root_Vue_["toDisplayString"])(code.toUpperCase().match(/.{1,4}/g).join('-')), 1); + }), 128))], 512)), [[_directive_select_on_focus, {}]]) : (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("div", _hoisted_5, Object(external_commonjs_vue_commonjs2_vue_root_Vue_["toDisplayString"])(_ctx.translate('TwoFactorAuth_RecoveryCodesAllUsed')), 1)), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("p", null, [_hoisted_6, Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("input", { + type: "button", + class: "btn backupRecoveryCode", + onClick: _cache[0] || (_cache[0] = function ($event) { + _ctx.downloadRecoveryCodes(); + + _ctx.$emit('downloaded'); + }), + value: _ctx.translate('General_Download'), + style: { + "margin-right": "3.5px" + } + }, null, 8, _hoisted_7), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("input", { + type: "button", + class: "btn backupRecoveryCode", + onClick: _cache[1] || (_cache[1] = function ($event) { + _ctx.print(); + + _ctx.$emit('downloaded'); + }), + value: _ctx.translate('General_Print'), + style: { + "margin-right": "3.5px" + } + }, null, 8, _hoisted_8), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("input", { + type: "button", + class: "btn backupRecoveryCode", + onClick: _cache[2] || (_cache[2] = function ($event) { + _ctx.copyRecoveryCodesToClipboard(); + + _ctx.$emit('downloaded'); + }), + value: _ctx.translate('General_Copy') + }, null, 8, _hoisted_9)])]); +} +// CONCATENATED MODULE: ./plugins/TwoFactorAuth/vue/src/ShowRecoveryCodes/ShowRecoveryCodes.vue?vue&type=template&id=e6e67cfe + +// EXTERNAL MODULE: external "CoreHome" +var external_CoreHome_ = __webpack_require__("19dc"); + +// CONCATENATED MODULE: ./node_modules/@vue/cli-plugin-typescript/node_modules/cache-loader/dist/cjs.js??ref--14-0!./node_modules/babel-loader/lib!./node_modules/@vue/cli-plugin-typescript/node_modules/ts-loader??ref--14-2!./node_modules/@vue/cli-service/node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist??ref--0-1!./plugins/TwoFactorAuth/vue/src/ShowRecoveryCodes/ShowRecoveryCodes.vue?vue&type=script&lang=ts + + +/* harmony default export */ var ShowRecoveryCodesvue_type_script_lang_ts = (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["defineComponent"])({ + props: { + codes: { + type: Array, + default: function _default() { + return []; + } + } + }, + directives: { + SelectOnFocus: external_CoreHome_["SelectOnFocus"] + }, + emits: ['downloaded'], + methods: { + copyRecoveryCodesToClipboard: function copyRecoveryCodesToClipboard() { + var textarea = document.createElement('textarea'); + textarea.value = this.codes.join('\n'); + textarea.setAttribute('readonly', ''); + textarea.style.position = 'absolute'; + textarea.style.left = '-9999px'; + document.body.appendChild(textarea); + textarea.select(); + document.execCommand('copy'); + document.body.removeChild(textarea); + }, + downloadRecoveryCodes: function downloadRecoveryCodes() { + external_CoreHome_["Matomo"].helper.sendContentAsDownload('analytics_recovery_codes.txt', this.codes.join('\n')); + }, + print: function print() { + window.print(); + } + } +})); +// CONCATENATED MODULE: ./plugins/TwoFactorAuth/vue/src/ShowRecoveryCodes/ShowRecoveryCodes.vue?vue&type=script&lang=ts + +// CONCATENATED MODULE: ./plugins/TwoFactorAuth/vue/src/ShowRecoveryCodes/ShowRecoveryCodes.vue + + + +ShowRecoveryCodesvue_type_script_lang_ts.render = render + +/* harmony default export */ var ShowRecoveryCodes = (ShowRecoveryCodesvue_type_script_lang_ts); +// CONCATENATED MODULE: ./node_modules/@vue/cli-plugin-babel/node_modules/cache-loader/dist/cjs.js??ref--12-0!./node_modules/@vue/cli-plugin-babel/node_modules/thread-loader/dist/cjs.js!./node_modules/babel-loader/lib!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist/templateLoader.js??ref--6!./node_modules/@vue/cli-service/node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist??ref--0-1!./plugins/TwoFactorAuth/vue/src/SetupTwoFactorAuth/SetupTwoFactorAuth.vue?vue&type=template&id=ba50e374 + +var SetupTwoFactorAuthvue_type_template_id_ba50e374_hoisted_1 = { + class: "setupTwoFactorAuthentication", + ref: "root" +}; +var SetupTwoFactorAuthvue_type_template_id_ba50e374_hoisted_2 = { + key: 0, + class: "alert alert-warning" +}; +var SetupTwoFactorAuthvue_type_template_id_ba50e374_hoisted_3 = ["disabled"]; + +var SetupTwoFactorAuthvue_type_template_id_ba50e374_hoisted_4 = /*#__PURE__*/Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("a", { + name: "twoFactorStep2", + id: "twoFactorStep2", + style: { + "opacity": "0" + } +}, null, -1); + +var SetupTwoFactorAuthvue_type_template_id_ba50e374_hoisted_5 = /*#__PURE__*/Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("a", { + target: "_blank", + rel: "noreferrer noopener", + href: "https://github.com/andOTP/andOTP#downloads" +}, "andOTP", -1); + +var SetupTwoFactorAuthvue_type_template_id_ba50e374_hoisted_6 = /*#__PURE__*/Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createTextVNode"])(", "); + +var SetupTwoFactorAuthvue_type_template_id_ba50e374_hoisted_7 = /*#__PURE__*/Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("a", { + target: "_blank", + rel: "noreferrer noopener", + href: "https://authy.com/guides/github/" +}, "Authy", -1); + +var SetupTwoFactorAuthvue_type_template_id_ba50e374_hoisted_8 = /*#__PURE__*/Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createTextVNode"])(", "); + +var SetupTwoFactorAuthvue_type_template_id_ba50e374_hoisted_9 = /*#__PURE__*/Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("a", { + target: "_blank", + rel: "noreferrer noopener", + href: "https://support.1password.com/one-time-passwords/" +}, "1Password", -1); + +var _hoisted_10 = /*#__PURE__*/Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createTextVNode"])(", "); + +var _hoisted_11 = /*#__PURE__*/Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("a", { + target: "_blank", + rel: "noreferrer noopener", + href: "https://helpdesk.lastpass.com/multifactor-authentication-options/lastpass-authenticator/" +}, "LastPass Authenticator", -1); + +var _hoisted_12 = /*#__PURE__*/Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("a", { + target: "_blank", + rel: "noreferrer noopener", + href: "https://support.google.com/accounts/answer/1066447" +}, "Google Authenticator", -1); + +var _hoisted_13 = /*#__PURE__*/Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createTextVNode"])(". "); + +var _hoisted_14 = ["innerHTML"]; + +var _hoisted_15 = /*#__PURE__*/Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("br", null, null, -1); + +var _hoisted_16 = { + id: "qrcode", + ref: "qrcode", + title: "" +}; + +var _hoisted_17 = /*#__PURE__*/Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("br", null, null, -1); + +var _hoisted_18 = /*#__PURE__*/Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("a", { + name: "twoFactorStep3", + id: "twoFactorStep3", + style: { + "opacity": "0" + } +}, null, -1); + +var _hoisted_19 = { + key: 0, + class: "message_container" +}; + +var _hoisted_20 = /*#__PURE__*/Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createTextVNode"])(": "); + +var _hoisted_21 = ["innerHTML"]; + +var _hoisted_22 = /*#__PURE__*/Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("br", null, null, -1); + +var _hoisted_23 = ["action"]; +var _hoisted_24 = ["value"]; +var _hoisted_25 = ["disabled", "value"]; +function SetupTwoFactorAuthvue_type_template_id_ba50e374_render(_ctx, _cache, $props, $setup, $data, $options) { + var _this = this; + + var _component_ShowRecoveryCodes = Object(external_commonjs_vue_commonjs2_vue_root_Vue_["resolveComponent"])("ShowRecoveryCodes"); + + var _component_Notification = Object(external_commonjs_vue_commonjs2_vue_root_Vue_["resolveComponent"])("Notification"); + + var _component_Field = Object(external_commonjs_vue_commonjs2_vue_root_Vue_["resolveComponent"])("Field"); + + return Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("div", SetupTwoFactorAuthvue_type_template_id_ba50e374_hoisted_1, [_ctx.isAlreadyUsing2fa ? (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("div", SetupTwoFactorAuthvue_type_template_id_ba50e374_hoisted_2, Object(external_commonjs_vue_commonjs2_vue_root_Vue_["toDisplayString"])(_ctx.translate('TwoFactorAuth_WarningChangingConfiguredDevice')), 1)) : Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createCommentVNode"])("", true), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("p", null, Object(external_commonjs_vue_commonjs2_vue_root_Vue_["toDisplayString"])(_ctx.translate('TwoFactorAuth_SetupIntroFollowSteps')), 1), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("h2", null, Object(external_commonjs_vue_commonjs2_vue_root_Vue_["toDisplayString"])(_ctx.translate('TwoFactorAuth_StepX', 1)) + " - " + Object(external_commonjs_vue_commonjs2_vue_root_Vue_["toDisplayString"])(_ctx.translate('TwoFactorAuth_RecoveryCodes')), 1), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createVNode"])(_component_ShowRecoveryCodes, { + codes: _ctx.codes, + onDownloaded: _cache[0] || (_cache[0] = function ($event) { + return _this.hasDownloadedRecoveryCode = true; + }) + }, null, 8, ["codes"]), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["withDirectives"])(Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("div", { + class: "alert alert-info backupRecoveryCodesAlert" + }, Object(external_commonjs_vue_commonjs2_vue_root_Vue_["toDisplayString"])(_ctx.translate('TwoFactorAuth_SetupBackupRecoveryCodes')), 513), [[external_commonjs_vue_commonjs2_vue_root_Vue_["vShow"], _ctx.step === 1]]), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("p", null, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["withDirectives"])(Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("button", { + class: "btn goToStep2", + onClick: _cache[1] || (_cache[1] = function ($event) { + return _ctx.nextStep(); + }), + disabled: !_ctx.hasDownloadedRecoveryCode + }, Object(external_commonjs_vue_commonjs2_vue_root_Vue_["toDisplayString"])(_ctx.translate('General_Next')), 9, SetupTwoFactorAuthvue_type_template_id_ba50e374_hoisted_3), [[external_commonjs_vue_commonjs2_vue_root_Vue_["vShow"], _ctx.step === 1]])]), SetupTwoFactorAuthvue_type_template_id_ba50e374_hoisted_4, Object(external_commonjs_vue_commonjs2_vue_root_Vue_["withDirectives"])(Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("div", null, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("h2", null, Object(external_commonjs_vue_commonjs2_vue_root_Vue_["toDisplayString"])(_ctx.translate('TwoFactorAuth_StepX', 2)) + " - " + Object(external_commonjs_vue_commonjs2_vue_root_Vue_["toDisplayString"])(_ctx.translate('TwoFactorAuth_SetupAuthenticatorOnDevice')), 1), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("p", null, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createTextVNode"])(Object(external_commonjs_vue_commonjs2_vue_root_Vue_["toDisplayString"])(_ctx.translate('TwoFactorAuth_SetupAuthenticatorOnDeviceStep1')) + " ", 1), SetupTwoFactorAuthvue_type_template_id_ba50e374_hoisted_5, SetupTwoFactorAuthvue_type_template_id_ba50e374_hoisted_6, SetupTwoFactorAuthvue_type_template_id_ba50e374_hoisted_7, SetupTwoFactorAuthvue_type_template_id_ba50e374_hoisted_8, SetupTwoFactorAuthvue_type_template_id_ba50e374_hoisted_9, _hoisted_10, _hoisted_11, Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createTextVNode"])(", " + Object(external_commonjs_vue_commonjs2_vue_root_Vue_["toDisplayString"])(_ctx.translate('General_Or')) + " ", 1), _hoisted_12, _hoisted_13]), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("p", null, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("span", { + innerHTML: _ctx.$sanitize(_ctx.setupAuthenticatorOnDeviceStep2) + }, null, 8, _hoisted_14)]), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("p", null, [_hoisted_15, Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("span", _hoisted_16, null, 512)]), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("p", null, [_hoisted_17, Object(external_commonjs_vue_commonjs2_vue_root_Vue_["withDirectives"])(Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("button", { + class: "btn goToStep3", + onClick: _cache[2] || (_cache[2] = function ($event) { + return _ctx.nextStep(); + }) + }, Object(external_commonjs_vue_commonjs2_vue_root_Vue_["toDisplayString"])(_ctx.translate('General_Next')), 513), [[external_commonjs_vue_commonjs2_vue_root_Vue_["vShow"], _ctx.step === 2]])])], 512), [[external_commonjs_vue_commonjs2_vue_root_Vue_["vShow"], _ctx.step >= 2]]), _hoisted_18, Object(external_commonjs_vue_commonjs2_vue_root_Vue_["withDirectives"])(Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("div", null, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("h2", null, Object(external_commonjs_vue_commonjs2_vue_root_Vue_["toDisplayString"])(_ctx.translate('TwoFactorAuth_StepX', 3)) + " - " + Object(external_commonjs_vue_commonjs2_vue_root_Vue_["toDisplayString"])(_ctx.translate('TwoFactorAuth_ConfirmSetup')), 1), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("p", null, Object(external_commonjs_vue_commonjs2_vue_root_Vue_["toDisplayString"])(_ctx.translate('TwoFactorAuth_VerifyAuthCodeIntro')), 1), _ctx.accessErrorString ? (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("div", _hoisted_19, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("div", null, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createVNode"])(_component_Notification, { + noclear: true, + context: "error" + }, { + default: Object(external_commonjs_vue_commonjs2_vue_root_Vue_["withCtx"])(function () { + return [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("strong", null, Object(external_commonjs_vue_commonjs2_vue_root_Vue_["toDisplayString"])(_ctx.translate('General_Error')), 1), _hoisted_20, Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("span", { + innerHTML: _ctx.$sanitize(_ctx.accessErrorString) + }, null, 8, _hoisted_21), _hoisted_22]; + }), + _: 1 + })])])) : Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createCommentVNode"])("", true), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("form", { + method: "post", + class: "setupConfirmAuthCodeForm", + autocorrect: "off", + autocapitalize: "none", + autocomplete: "off", + action: _ctx.linkTo({ + 'module': 'TwoFactorAuth', + 'action': _ctx.submitAction + }) + }, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("div", null, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createVNode"])(_component_Field, { + uicontrol: "text", + name: "authCode", + title: _ctx.translate('TwoFactorAuth_AuthenticationCode'), + modelValue: _ctx.authCode, + "onUpdate:modelValue": _cache[3] || (_cache[3] = function ($event) { + return _ctx.authCode = $event; + }), + maxlength: 6, + placeholder: '123456', + "inline-help": _ctx.translate('TwoFactorAuth_VerifyAuthCodeHelp') + }, null, 8, ["title", "modelValue", "inline-help"])]), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("input", { + type: "hidden", + name: "authCodeNonce", + value: _ctx.authCodeNonce + }, null, 8, _hoisted_24), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("input", { + type: "submit", + class: "btn confirmAuthCode", + disabled: _ctx.authCode.length !== 6, + value: _ctx.translate('General_Confirm') + }, null, 8, _hoisted_25)], 8, _hoisted_23)], 512), [[external_commonjs_vue_commonjs2_vue_root_Vue_["vShow"], _ctx.step >= 3]])], 512); +} +// CONCATENATED MODULE: ./plugins/TwoFactorAuth/vue/src/SetupTwoFactorAuth/SetupTwoFactorAuth.vue?vue&type=template&id=ba50e374 + +// EXTERNAL MODULE: external "CorePluginsAdmin" +var external_CorePluginsAdmin_ = __webpack_require__("a5a2"); + +// CONCATENATED MODULE: ./plugins/TwoFactorAuth/vue/src/types.ts +/*! + * Matomo - free/libre analytics platform + * + * @link https://matomo.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +// CONCATENATED MODULE: ./node_modules/@vue/cli-plugin-typescript/node_modules/cache-loader/dist/cjs.js??ref--14-0!./node_modules/babel-loader/lib!./node_modules/@vue/cli-plugin-typescript/node_modules/ts-loader??ref--14-2!./node_modules/@vue/cli-service/node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist??ref--0-1!./plugins/TwoFactorAuth/vue/src/SetupTwoFactorAuth/SetupTwoFactorAuth.vue?vue&type=script&lang=ts + + + + + +var _window = window, + QRCode = _window.QRCode, + $ = _window.$; +/* harmony default export */ var SetupTwoFactorAuthvue_type_script_lang_ts = (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["defineComponent"])({ + props: { + isAlreadyUsing2fa: Boolean, + accessErrorString: String, + submitAction: { + type: String, + required: true + }, + authCodeNonce: { + type: String, + required: true + }, + codes: Array + }, + components: { + ShowRecoveryCodes: ShowRecoveryCodes, + Notification: external_CoreHome_["Notification"], + Field: external_CorePluginsAdmin_["Field"] + }, + directives: { + SelectOnFocus: external_CoreHome_["SelectOnFocus"] + }, + data: function data() { + return { + step: 1, + hasDownloadedRecoveryCode: false, + authCode: '' + }; + }, + mounted: function mounted() { + var _this = this; + + setTimeout(function () { + var qrcode = _this.$refs.qrcode; // eslint-disable-next-line no-new + + new QRCode(qrcode, { + text: window.twoFaBarCodeSetupUrl + }); + $(qrcode).attr('title', ''); // do not show secret on hover + + if (_this.accessErrorString) { + // user entered something wrong + _this.step = 3; + + _this.scrollToEnd(); + } + + $(_this.$refs.root).on('click', '.setupStep2Link', function (e) { + e.preventDefault(); + external_CoreHome_["Matomo"].helper.modalConfirm('#setupTwoFAsecretConfirm'); + }); + }); + }, + methods: { + scrollToEnd: function scrollToEnd() { + var _this2 = this; + + setTimeout(function () { + var id = ''; + + if (_this2.step === 2) { + id = '#twoFactorStep2'; + } else if (_this2.step === 3) { + id = '#twoFactorStep3'; + } + + if (id) { + external_CoreHome_["Matomo"].helper.lazyScrollTo(id, 50, true); + } + }, 50); + }, + nextStep: function nextStep() { + this.step += 1; + this.scrollToEnd(); + }, + linkTo: function linkTo(params) { + return "?".concat(external_CoreHome_["MatomoUrl"].stringify(Object.assign(Object.assign({}, external_CoreHome_["MatomoUrl"].urlParsed.value), params))); + } + }, + computed: { + setupAuthenticatorOnDeviceStep2: function setupAuthenticatorOnDeviceStep2() { + return Object(external_CoreHome_["translate"])('TwoFactorAuth_SetupAuthenticatorOnDeviceStep2', '<a class="setupStep2Link">', '</a>'); + } + } +})); +// CONCATENATED MODULE: ./plugins/TwoFactorAuth/vue/src/SetupTwoFactorAuth/SetupTwoFactorAuth.vue?vue&type=script&lang=ts + +// CONCATENATED MODULE: ./plugins/TwoFactorAuth/vue/src/SetupTwoFactorAuth/SetupTwoFactorAuth.vue + + + +SetupTwoFactorAuthvue_type_script_lang_ts.render = SetupTwoFactorAuthvue_type_template_id_ba50e374_render + +/* harmony default export */ var SetupTwoFactorAuth = (SetupTwoFactorAuthvue_type_script_lang_ts); +// CONCATENATED MODULE: ./plugins/TwoFactorAuth/vue/src/index.ts +/*! + * Matomo - free/libre analytics platform + * + * @link https://matomo.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + + +// CONCATENATED MODULE: ./node_modules/@vue/cli-service/lib/commands/build/entry-lib-no-default.js + + + + +/***/ }) + +/******/ }); +}); +//# sourceMappingURL=TwoFactorAuth.umd.js.map
\ No newline at end of file diff --git a/plugins/TwoFactorAuth/vue/dist/TwoFactorAuth.umd.min.js b/plugins/TwoFactorAuth/vue/dist/TwoFactorAuth.umd.min.js new file mode 100644 index 0000000000..6c35b2cb19 --- /dev/null +++ b/plugins/TwoFactorAuth/vue/dist/TwoFactorAuth.umd.min.js @@ -0,0 +1,14 @@ +(function(e,t){"object"===typeof exports&&"object"===typeof module?module.exports=t(require("CoreHome"),require("vue"),require("CorePluginsAdmin")):"function"===typeof define&&define.amd?define(["CoreHome",,"CorePluginsAdmin"],t):"object"===typeof exports?exports["TwoFactorAuth"]=t(require("CoreHome"),require("vue"),require("CorePluginsAdmin")):e["TwoFactorAuth"]=t(e["CoreHome"],e["Vue"],e["CorePluginsAdmin"])})("undefined"!==typeof self?self:this,(function(e,t,o){return function(e){var t={};function o(n){if(t[n])return t[n].exports;var r=t[n]={i:n,l:!1,exports:{}};return e[n].call(r.exports,r,r.exports,o),r.l=!0,r.exports}return o.m=e,o.c=t,o.d=function(e,t,n){o.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},o.r=function(e){"undefined"!==typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},o.t=function(e,t){if(1&t&&(e=o(e)),8&t)return e;if(4&t&&"object"===typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(o.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var r in e)o.d(n,r,function(t){return e[t]}.bind(null,r));return n},o.n=function(e){var t=e&&e.__esModule?function(){return e["default"]}:function(){return e};return o.d(t,"a",t),t},o.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},o.p="plugins/TwoFactorAuth/vue/dist/",o(o.s="fae3")}({"19dc":function(t,o){t.exports=e},"8bbf":function(e,o){e.exports=t},a5a2:function(e,t){e.exports=o},fae3:function(e,t,o){"use strict";if(o.r(t),o.d(t,"ShowRecoveryCodes",(function(){return h})),o.d(t,"SetupTwoFactorAuth",(function(){return J})),"undefined"!==typeof window){var n=window.document.currentScript,r=n&&n.src.match(/(.+\/)[^/]+\.js(\?.*)?$/);r&&(o.p=r[1])}var c=o("8bbf"),a=Object(c["createElementVNode"])("br",null,null,-1),l=Object(c["createElementVNode"])("br",null,null,-1),i={class:"alert alert-warning"},u={key:0,class:"twoFactorRecoveryCodes browser-default"},s={key:1,class:"alert alert-danger"},d=Object(c["createElementVNode"])("br",null,null,-1),p=["value"],b=["value"],m=["value"];function O(e,t,o,n,r,O){var j,f=Object(c["resolveDirective"])("select-on-focus");return Object(c["openBlock"])(),Object(c["createElementBlock"])("div",null,[Object(c["createElementVNode"])("p",null,[Object(c["createTextVNode"])(Object(c["toDisplayString"])(e.translate("TwoFactorAuth_RecoveryCodesExplanation")),1),a,l]),Object(c["createElementVNode"])("div",i,Object(c["toDisplayString"])(e.translate("TwoFactorAuth_RecoveryCodesSecurity")),1),null!==(j=e.codes)&&void 0!==j&&j.length?Object(c["withDirectives"])((Object(c["openBlock"])(),Object(c["createElementBlock"])("ul",u,[(Object(c["openBlock"])(!0),Object(c["createElementBlock"])(c["Fragment"],null,Object(c["renderList"])(e.codes,(function(e,t){return Object(c["openBlock"])(),Object(c["createElementBlock"])("li",{key:t},Object(c["toDisplayString"])(e.toUpperCase().match(/.{1,4}/g).join("-")),1)})),128))],512)),[[f,{}]]):(Object(c["openBlock"])(),Object(c["createElementBlock"])("div",s,Object(c["toDisplayString"])(e.translate("TwoFactorAuth_RecoveryCodesAllUsed")),1)),Object(c["createElementVNode"])("p",null,[d,Object(c["createElementVNode"])("input",{type:"button",class:"btn backupRecoveryCode",onClick:t[0]||(t[0]=function(t){e.downloadRecoveryCodes(),e.$emit("downloaded")}),value:e.translate("General_Download"),style:{"margin-right":"3.5px"}},null,8,p),Object(c["createElementVNode"])("input",{type:"button",class:"btn backupRecoveryCode",onClick:t[1]||(t[1]=function(t){e.print(),e.$emit("downloaded")}),value:e.translate("General_Print"),style:{"margin-right":"3.5px"}},null,8,b),Object(c["createElementVNode"])("input",{type:"button",class:"btn backupRecoveryCode",onClick:t[2]||(t[2]=function(t){e.copyRecoveryCodesToClipboard(),e.$emit("downloaded")}),value:e.translate("General_Copy")},null,8,m)])])}var j=o("19dc"),f=Object(c["defineComponent"])({props:{codes:{type:Array,default:function(){return[]}}},directives:{SelectOnFocus:j["SelectOnFocus"]},emits:["downloaded"],methods:{copyRecoveryCodesToClipboard:function(){var e=document.createElement("textarea");e.value=this.codes.join("\n"),e.setAttribute("readonly",""),e.style.position="absolute",e.style.left="-9999px",document.body.appendChild(e),e.select(),document.execCommand("copy"),document.body.removeChild(e)},downloadRecoveryCodes:function(){j["Matomo"].helper.sendContentAsDownload("analytics_recovery_codes.txt",this.codes.join("\n"))},print:function(){window.print()}}});f.render=O;var h=f,y={class:"setupTwoFactorAuthentication",ref:"root"},v={key:0,class:"alert alert-warning"},w=["disabled"],S=Object(c["createElementVNode"])("a",{name:"twoFactorStep2",id:"twoFactorStep2",style:{opacity:"0"}},null,-1),g=Object(c["createElementVNode"])("a",{target:"_blank",rel:"noreferrer noopener",href:"https://github.com/andOTP/andOTP#downloads"},"andOTP",-1),C=Object(c["createTextVNode"])(", "),N=Object(c["createElementVNode"])("a",{target:"_blank",rel:"noreferrer noopener",href:"https://authy.com/guides/github/"},"Authy",-1),V=Object(c["createTextVNode"])(", "),E=Object(c["createElementVNode"])("a",{target:"_blank",rel:"noreferrer noopener",href:"https://support.1password.com/one-time-passwords/"},"1Password",-1),T=Object(c["createTextVNode"])(", "),A=Object(c["createElementVNode"])("a",{target:"_blank",rel:"noreferrer noopener",href:"https://helpdesk.lastpass.com/multifactor-authentication-options/lastpass-authenticator/"},"LastPass Authenticator",-1),k=Object(c["createElementVNode"])("a",{target:"_blank",rel:"noreferrer noopener",href:"https://support.google.com/accounts/answer/1066447"},"Google Authenticator",-1),F=Object(c["createTextVNode"])(". "),_=["innerHTML"],D=Object(c["createElementVNode"])("br",null,null,-1),x={id:"qrcode",ref:"qrcode",title:""},R=Object(c["createElementVNode"])("br",null,null,-1),B=Object(c["createElementVNode"])("a",{name:"twoFactorStep3",id:"twoFactorStep3",style:{opacity:"0"}},null,-1),P={key:0,class:"message_container"},M=Object(c["createTextVNode"])(": "),q=["innerHTML"],G=Object(c["createElementVNode"])("br",null,null,-1),H=["action"],$=["value"],L=["disabled","value"];function U(e,t,o,n,r,a){var l=this,i=Object(c["resolveComponent"])("ShowRecoveryCodes"),u=Object(c["resolveComponent"])("Notification"),s=Object(c["resolveComponent"])("Field");return Object(c["openBlock"])(),Object(c["createElementBlock"])("div",y,[e.isAlreadyUsing2fa?(Object(c["openBlock"])(),Object(c["createElementBlock"])("div",v,Object(c["toDisplayString"])(e.translate("TwoFactorAuth_WarningChangingConfiguredDevice")),1)):Object(c["createCommentVNode"])("",!0),Object(c["createElementVNode"])("p",null,Object(c["toDisplayString"])(e.translate("TwoFactorAuth_SetupIntroFollowSteps")),1),Object(c["createElementVNode"])("h2",null,Object(c["toDisplayString"])(e.translate("TwoFactorAuth_StepX",1))+" - "+Object(c["toDisplayString"])(e.translate("TwoFactorAuth_RecoveryCodes")),1),Object(c["createVNode"])(i,{codes:e.codes,onDownloaded:t[0]||(t[0]=function(e){return l.hasDownloadedRecoveryCode=!0})},null,8,["codes"]),Object(c["withDirectives"])(Object(c["createElementVNode"])("div",{class:"alert alert-info backupRecoveryCodesAlert"},Object(c["toDisplayString"])(e.translate("TwoFactorAuth_SetupBackupRecoveryCodes")),513),[[c["vShow"],1===e.step]]),Object(c["createElementVNode"])("p",null,[Object(c["withDirectives"])(Object(c["createElementVNode"])("button",{class:"btn goToStep2",onClick:t[1]||(t[1]=function(t){return e.nextStep()}),disabled:!e.hasDownloadedRecoveryCode},Object(c["toDisplayString"])(e.translate("General_Next")),9,w),[[c["vShow"],1===e.step]])]),S,Object(c["withDirectives"])(Object(c["createElementVNode"])("div",null,[Object(c["createElementVNode"])("h2",null,Object(c["toDisplayString"])(e.translate("TwoFactorAuth_StepX",2))+" - "+Object(c["toDisplayString"])(e.translate("TwoFactorAuth_SetupAuthenticatorOnDevice")),1),Object(c["createElementVNode"])("p",null,[Object(c["createTextVNode"])(Object(c["toDisplayString"])(e.translate("TwoFactorAuth_SetupAuthenticatorOnDeviceStep1"))+" ",1),g,C,N,V,E,T,A,Object(c["createTextVNode"])(", "+Object(c["toDisplayString"])(e.translate("General_Or"))+" ",1),k,F]),Object(c["createElementVNode"])("p",null,[Object(c["createElementVNode"])("span",{innerHTML:e.$sanitize(e.setupAuthenticatorOnDeviceStep2)},null,8,_)]),Object(c["createElementVNode"])("p",null,[D,Object(c["createElementVNode"])("span",x,null,512)]),Object(c["createElementVNode"])("p",null,[R,Object(c["withDirectives"])(Object(c["createElementVNode"])("button",{class:"btn goToStep3",onClick:t[2]||(t[2]=function(t){return e.nextStep()})},Object(c["toDisplayString"])(e.translate("General_Next")),513),[[c["vShow"],2===e.step]])])],512),[[c["vShow"],e.step>=2]]),B,Object(c["withDirectives"])(Object(c["createElementVNode"])("div",null,[Object(c["createElementVNode"])("h2",null,Object(c["toDisplayString"])(e.translate("TwoFactorAuth_StepX",3))+" - "+Object(c["toDisplayString"])(e.translate("TwoFactorAuth_ConfirmSetup")),1),Object(c["createElementVNode"])("p",null,Object(c["toDisplayString"])(e.translate("TwoFactorAuth_VerifyAuthCodeIntro")),1),e.accessErrorString?(Object(c["openBlock"])(),Object(c["createElementBlock"])("div",P,[Object(c["createElementVNode"])("div",null,[Object(c["createVNode"])(u,{noclear:!0,context:"error"},{default:Object(c["withCtx"])((function(){return[Object(c["createElementVNode"])("strong",null,Object(c["toDisplayString"])(e.translate("General_Error")),1),M,Object(c["createElementVNode"])("span",{innerHTML:e.$sanitize(e.accessErrorString)},null,8,q),G]})),_:1})])])):Object(c["createCommentVNode"])("",!0),Object(c["createElementVNode"])("form",{method:"post",class:"setupConfirmAuthCodeForm",autocorrect:"off",autocapitalize:"none",autocomplete:"off",action:e.linkTo({module:"TwoFactorAuth",action:e.submitAction})},[Object(c["createElementVNode"])("div",null,[Object(c["createVNode"])(s,{uicontrol:"text",name:"authCode",title:e.translate("TwoFactorAuth_AuthenticationCode"),modelValue:e.authCode,"onUpdate:modelValue":t[3]||(t[3]=function(t){return e.authCode=t}),maxlength:6,placeholder:"123456","inline-help":e.translate("TwoFactorAuth_VerifyAuthCodeHelp")},null,8,["title","modelValue","inline-help"])]),Object(c["createElementVNode"])("input",{type:"hidden",name:"authCodeNonce",value:e.authCodeNonce},null,8,$),Object(c["createElementVNode"])("input",{type:"submit",class:"btn confirmAuthCode",disabled:6!==e.authCode.length,value:e.translate("General_Confirm")},null,8,L)],8,H)],512),[[c["vShow"],e.step>=3]])],512)}var z=o("a5a2"),X=window,I=X.QRCode,Q=X.$,W=Object(c["defineComponent"])({props:{isAlreadyUsing2fa:Boolean,accessErrorString:String,submitAction:{type:String,required:!0},authCodeNonce:{type:String,required:!0},codes:Array},components:{ShowRecoveryCodes:h,Notification:j["Notification"],Field:z["Field"]},directives:{SelectOnFocus:j["SelectOnFocus"]},data:function(){return{step:1,hasDownloadedRecoveryCode:!1,authCode:""}},mounted:function(){var e=this;setTimeout((function(){var t=e.$refs.qrcode;new I(t,{text:window.twoFaBarCodeSetupUrl}),Q(t).attr("title",""),e.accessErrorString&&(e.step=3,e.scrollToEnd()),Q(e.$refs.root).on("click",".setupStep2Link",(function(e){e.preventDefault(),j["Matomo"].helper.modalConfirm("#setupTwoFAsecretConfirm")}))}))},methods:{scrollToEnd:function(){var e=this;setTimeout((function(){var t="";2===e.step?t="#twoFactorStep2":3===e.step&&(t="#twoFactorStep3"),t&&j["Matomo"].helper.lazyScrollTo(t,50,!0)}),50)},nextStep:function(){this.step+=1,this.scrollToEnd()},linkTo:function(e){return"?".concat(j["MatomoUrl"].stringify(Object.assign(Object.assign({},j["MatomoUrl"].urlParsed.value),e)))}},computed:{setupAuthenticatorOnDeviceStep2:function(){return Object(j["translate"])("TwoFactorAuth_SetupAuthenticatorOnDeviceStep2",'<a class="setupStep2Link">',"</a>")}}}); +/*! + * Matomo - free/libre analytics platform + * + * @link https://matomo.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */W.render=U;var J=W; +/*! + * Matomo - free/libre analytics platform + * + * @link https://matomo.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */}})})); +//# sourceMappingURL=TwoFactorAuth.umd.min.js.map
\ No newline at end of file diff --git a/plugins/TwoFactorAuth/vue/dist/umd.metadata.json b/plugins/TwoFactorAuth/vue/dist/umd.metadata.json new file mode 100644 index 0000000000..dce4477a3c --- /dev/null +++ b/plugins/TwoFactorAuth/vue/dist/umd.metadata.json @@ -0,0 +1,6 @@ +{ + "dependsOn": [ + "CoreHome", + "CorePluginsAdmin" + ] +}
\ No newline at end of file diff --git a/plugins/TwoFactorAuth/vue/src/SetupTwoFactorAuth/SetupTwoFactorAuth.vue b/plugins/TwoFactorAuth/vue/src/SetupTwoFactorAuth/SetupTwoFactorAuth.vue new file mode 100644 index 0000000000..8123dd5c59 --- /dev/null +++ b/plugins/TwoFactorAuth/vue/src/SetupTwoFactorAuth/SetupTwoFactorAuth.vue @@ -0,0 +1,254 @@ +<!-- + Matomo - free/libre analytics platform + @link https://matomo.org + @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later +--> + +<template> + <div class="setupTwoFactorAuthentication" ref="root"> + <div class="alert alert-warning" v-if="isAlreadyUsing2fa"> + {{ translate('TwoFactorAuth_WarningChangingConfiguredDevice') }} + </div> + <p> + {{ translate('TwoFactorAuth_SetupIntroFollowSteps') }} + </p> + <h2> + {{ translate('TwoFactorAuth_StepX', 1) }} - {{ translate('TwoFactorAuth_RecoveryCodes') }} + </h2> + + <ShowRecoveryCodes + :codes="codes" + @downloaded="this.hasDownloadedRecoveryCode = true" + /> + + <div + class="alert alert-info backupRecoveryCodesAlert" + v-show="step === 1" + > + {{ translate('TwoFactorAuth_SetupBackupRecoveryCodes') }} + </div> + <p> + <button + class="btn goToStep2" + v-show="step === 1" + @click="nextStep()" + :disabled="!hasDownloadedRecoveryCode" + >{{ translate('General_Next') }}</button> + </p> + <a + name="twoFactorStep2" + id="twoFactorStep2" + style="opacity: 0" + /> + <div v-show="step >= 2"> + <h2> + {{ translate('TwoFactorAuth_StepX', 2) }} - + {{ translate('TwoFactorAuth_SetupAuthenticatorOnDevice') }} + </h2> + <p>{{ translate('TwoFactorAuth_SetupAuthenticatorOnDeviceStep1') }} <a + target="_blank" + rel="noreferrer noopener" + href="https://github.com/andOTP/andOTP#downloads" + >andOTP</a>, <a + target="_blank" + rel="noreferrer noopener" + href="https://authy.com/guides/github/" + >Authy</a>, <a + target="_blank" + rel="noreferrer noopener" + href="https://support.1password.com/one-time-passwords/" + >1Password</a>, <a + target="_blank" + rel="noreferrer noopener" + href="https://helpdesk.lastpass.com/multifactor-authentication-options/lastpass-authenticator/" + >LastPass Authenticator</a>, {{ translate('General_Or') }} <a + target="_blank" + rel="noreferrer noopener" + href="https://support.google.com/accounts/answer/1066447" + >Google Authenticator</a>. + </p> + <p><span v-html="$sanitize(setupAuthenticatorOnDeviceStep2)"></span></p> + <p> + <br /> + <span + id="qrcode" + ref="qrcode" + title + /> + </p> + <p> + <br /> + <button + class="btn goToStep3" + v-show="step === 2" + @click="nextStep()" + >{{ translate('General_Next') }}</button> + </p> + </div> + <a + name="twoFactorStep3" + id="twoFactorStep3" + style="opacity: 0" + /> + <div v-show="step >= 3"> + <h2>{{ translate('TwoFactorAuth_StepX', 3) }} - {{ translate('TwoFactorAuth_ConfirmSetup') }} + </h2> + <p>{{ translate('TwoFactorAuth_VerifyAuthCodeIntro') }}</p> + <div class="message_container" v-if="accessErrorString"> + <div> + <Notification + :noclear="true" + context="error" + > + <strong> + {{ translate('General_Error') }} + </strong>: <span v-html="$sanitize(accessErrorString)"/><br /> + </Notification> + </div> + </div> + <form + method="post" + class="setupConfirmAuthCodeForm" + autocorrect="off" + autocapitalize="none" + autocomplete="off" + :action="linkTo({'module': 'TwoFactorAuth', 'action': submitAction})" + > + <div> + <Field + uicontrol="text" + name="authCode" + :title="translate('TwoFactorAuth_AuthenticationCode')" + v-model="authCode" + :maxlength="6" + :placeholder="'123456'" + :inline-help="translate('TwoFactorAuth_VerifyAuthCodeHelp')" + > + </Field> + </div> + <input + type="hidden" + name="authCodeNonce" + :value="authCodeNonce" + /> + <input + type="submit" + class="btn confirmAuthCode" + :disabled="authCode.length !== 6" + :value="translate('General_Confirm')" + /> + </form> + </div> + </div> +</template> + +<script lang="ts"> +import { defineComponent } from 'vue'; +import { + translate, + Matomo, + Notification, + SelectOnFocus, + MatomoUrl, +} from 'CoreHome'; +import { Field } from 'CorePluginsAdmin'; +import '../types'; +import ShowRecoveryCodes from '../ShowRecoveryCodes/ShowRecoveryCodes.vue'; + +interface SetupTwoFactorAuthState { + step: number; + hasDownloadedRecoveryCode: boolean; + authCode: string; +} + +const { QRCode, $ } = window; + +export default defineComponent({ + props: { + isAlreadyUsing2fa: Boolean, + accessErrorString: String, + submitAction: { + type: String, + required: true, + }, + authCodeNonce: { + type: String, + required: true, + }, + codes: Array, + }, + components: { + ShowRecoveryCodes, + Notification, + Field, + }, + directives: { + SelectOnFocus, + }, + data(): SetupTwoFactorAuthState { + return { + step: 1, + hasDownloadedRecoveryCode: false, + authCode: '', + }; + }, + mounted() { + setTimeout(() => { + const qrcode = this.$refs.qrcode as HTMLElement; + + // eslint-disable-next-line no-new + new QRCode(qrcode, { + text: window.twoFaBarCodeSetupUrl, + }); + + $(qrcode).attr('title', ''); // do not show secret on hover + + if (this.accessErrorString) { + // user entered something wrong + this.step = 3; + this.scrollToEnd(); + } + + $(this.$refs.root as HTMLElement).on('click', '.setupStep2Link', (e) => { + e.preventDefault(); + Matomo.helper.modalConfirm('#setupTwoFAsecretConfirm'); + }); + }); + }, + methods: { + scrollToEnd() { + setTimeout(() => { + let id = ''; + if (this.step === 2) { + id = '#twoFactorStep2'; + } else if (this.step === 3) { + id = '#twoFactorStep3'; + } + + if (id) { + Matomo.helper.lazyScrollTo(id, 50, true); + } + }, 50); + }, + nextStep() { + this.step += 1; + this.scrollToEnd(); + }, + linkTo(params: QueryParameters) { + return `?${MatomoUrl.stringify({ + ...MatomoUrl.urlParsed.value, + ...params, + })}`; + }, + }, + computed: { + setupAuthenticatorOnDeviceStep2() { + return translate( + 'TwoFactorAuth_SetupAuthenticatorOnDeviceStep2', + '<a class="setupStep2Link">', + '</a>', + ); + }, + }, +}); +</script> diff --git a/plugins/TwoFactorAuth/vue/src/ShowRecoveryCodes/ShowRecoveryCodes.vue b/plugins/TwoFactorAuth/vue/src/ShowRecoveryCodes/ShowRecoveryCodes.vue new file mode 100644 index 0000000000..d88b74b5ea --- /dev/null +++ b/plugins/TwoFactorAuth/vue/src/ShowRecoveryCodes/ShowRecoveryCodes.vue @@ -0,0 +1,82 @@ +<!-- + Matomo - free/libre analytics platform + @link https://matomo.org + @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later +--> + +<template> + <div> + <p>{{ translate('TwoFactorAuth_RecoveryCodesExplanation') }}<br /><br /></p> + <div class="alert alert-warning">{{ translate('TwoFactorAuth_RecoveryCodesSecurity') }}</div> + + <ul v-select-on-focus="{}" class="twoFactorRecoveryCodes browser-default" v-if="codes?.length"> + <li v-for="(code, index) in codes" :key="index"> + {{ code.toUpperCase().match(/.{1,4}/g).join('-') }} + </li> + </ul> + <div class="alert alert-danger" v-else> + {{ translate('TwoFactorAuth_RecoveryCodesAllUsed') }} + </div> + + <p> + <br /> + <input + type="button" + class="btn backupRecoveryCode" + @click="downloadRecoveryCodes(); $emit('downloaded');" + :value="translate('General_Download')" + style="margin-right:3.5px" + /> + <input + type="button" + class="btn backupRecoveryCode" + @click="print(); $emit('downloaded');" + :value="translate('General_Print')" + style="margin-right:3.5px" + /> + <input + type="button" + class="btn backupRecoveryCode" + @click="copyRecoveryCodesToClipboard(); $emit('downloaded');" + :value="translate('General_Copy')" + /> + </p> + </div> +</template> + +<script lang="ts"> +import { defineComponent } from 'vue'; +import { Matomo, SelectOnFocus } from 'CoreHome'; + +export default defineComponent({ + props: { + codes: { + type: Array, + default() { return []; }, + }, + }, + directives: { + SelectOnFocus, + }, + emits: ['downloaded'], + methods: { + copyRecoveryCodesToClipboard() { + const textarea = document.createElement('textarea'); + textarea.value = this.codes.join('\n'); + textarea.setAttribute('readonly', ''); + textarea.style.position = 'absolute'; + textarea.style.left = '-9999px'; + document.body.appendChild(textarea); + textarea.select(); + document.execCommand('copy'); + document.body.removeChild(textarea); + }, + downloadRecoveryCodes() { + Matomo.helper.sendContentAsDownload('analytics_recovery_codes.txt', this.codes.join('\n')); + }, + print() { + window.print(); + }, + }, +}); +</script> diff --git a/plugins/TwoFactorAuth/vue/src/index.ts b/plugins/TwoFactorAuth/vue/src/index.ts new file mode 100644 index 0000000000..f8e95a08f6 --- /dev/null +++ b/plugins/TwoFactorAuth/vue/src/index.ts @@ -0,0 +1,9 @@ +/*! + * Matomo - free/libre analytics platform + * + * @link https://matomo.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +export { default as ShowRecoveryCodes } from './ShowRecoveryCodes/ShowRecoveryCodes.vue'; +export { default as SetupTwoFactorAuth } from './SetupTwoFactorAuth/SetupTwoFactorAuth.vue'; diff --git a/plugins/TwoFactorAuth/vue/src/types.ts b/plugins/TwoFactorAuth/vue/src/types.ts new file mode 100644 index 0000000000..1242d2cc16 --- /dev/null +++ b/plugins/TwoFactorAuth/vue/src/types.ts @@ -0,0 +1,17 @@ +/*! + * Matomo - free/libre analytics platform + * + * @link https://matomo.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +export interface QRCodeConstructor { + new (e: HTMLElement, options: unknown): unknown; +} + +declare global { + interface Window { + QRCode: QRCodeConstructor; + twoFaBarCodeSetupUrl: string; + } +} |