Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/matomo-org/matomo.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordizzy <diosmosis@users.noreply.github.com>2022-04-06 16:42:49 +0300
committerGitHub <noreply@github.com>2022-04-06 16:42:49 +0300
commit698c83bb1e411b18c6ea7c4d1df8668f9f432855 (patch)
tree947d5becafe4844bce9fdbae3c799b027ea4114f
parent59f26bc0f5436426d5b42876a4c535973b519c2f (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>
-rw-r--r--plugins/TwoFactorAuth/TwoFactorAuth.php29
-rw-r--r--plugins/TwoFactorAuth/angularjs/setuptwofactor/setuptwofactor.controller.js60
-rw-r--r--plugins/TwoFactorAuth/templates/_setupTwoFactorAuth.twig76
-rw-r--r--plugins/TwoFactorAuth/templates/_showRecoveryCodes.twig38
-rw-r--r--plugins/TwoFactorAuth/templates/showRecoveryCodes.twig4
-rw-r--r--plugins/TwoFactorAuth/tests/UI/TwoFactorAuth_spec.js2
-rw-r--r--plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_show_recovery_codes_step2.png4
-rw-r--r--plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_twofa_forced_step1.png4
-rw-r--r--plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_twofa_forced_step2.png4
-rw-r--r--plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_twofa_forced_step3.png4
-rw-r--r--plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_twofa_setup_step1.png4
-rw-r--r--plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_twofa_setup_step2.png4
-rw-r--r--plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_twofa_setup_step3.png4
-rw-r--r--plugins/TwoFactorAuth/vue/dist/TwoFactorAuth.umd.js565
-rw-r--r--plugins/TwoFactorAuth/vue/dist/TwoFactorAuth.umd.min.js14
-rw-r--r--plugins/TwoFactorAuth/vue/dist/umd.metadata.json6
-rw-r--r--plugins/TwoFactorAuth/vue/src/SetupTwoFactorAuth/SetupTwoFactorAuth.vue254
-rw-r--r--plugins/TwoFactorAuth/vue/src/ShowRecoveryCodes/ShowRecoveryCodes.vue82
-rw-r--r--plugins/TwoFactorAuth/vue/src/index.ts9
-rw-r--r--plugins/TwoFactorAuth/vue/src/types.ts17
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;
+ }
+}