diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 9718d21c..9bf275de 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -2,7 +2,7 @@ Contributions to the Magento 2 codebase are done using the fork & pull model. This contribution model has contributors maintaining their own fork of the Magento 2 repository. -The forked repository is then used to submit a request to the base repository to “pull” a set of changes. +The forked repository is then used to submit a request to the base repository to "pull" a set of changes. For more information on pull requests please refer to [GitHub Help](https://help.github.com/articles/about-pull-requests/). Contributions can take the form of new components or features, changes to existing features, tests, documentation (such as developer guides, user guides, examples, or specifications), bug fixes or optimizations. @@ -15,15 +15,15 @@ For more detailed information on contribution please read our [beginners guide]( ## Contribution requirements -1. Contributions must adhere to the [Magento coding standards](https://devdocs.magento.com/guides/v2.3/coding-standards/bk-coding-standards.html). +1. Contributions must adhere to the [Magento coding standards](https://developer.adobe.com/commerce/php/coding-standards/). 2. Pull requests (PRs) must be accompanied by a meaningful description of their purpose. Comprehensive descriptions increase the chances of a pull request being merged quickly and without additional clarification requests. 3. Commits must be accompanied by meaningful commit messages. Please see the [Magento Pull Request Template](https://github.com/magento/magento2/blob/2.3-develop/.github/PULL_REQUEST_TEMPLATE.md) for more information. 4. PRs which include bug fixes must be accompanied with a step-by-step description of how to reproduce the bug. -3. PRs which include new logic or new features must be submitted along with: -* Unit/integration test coverage -* Proposed [documentation](https://devdocs.magento.com) updates. Documentation contributions can be submitted via the [devdocs GitHub](https://github.com/magento/devdocs). -4. For larger features or changes, please [open an issue](https://github.com/magento/magento2/issues) to discuss the proposed changes prior to development. This may prevent duplicate or unnecessary effort and allow other contributors to provide input. -5. All automated tests must pass (all builds on [Travis CI](https://travis-ci.org/magento/magento2) must be green). +5. PRs which include new logic or new features must be submitted along with: + * Unit/integration test coverage + * Proposed [documentation](https://developer.adobe.com/commerce) updates. Use feedback buttons __Edit in GitHub__ and __Log an issue__ at the top of a relevant topic. +6. For larger features or changes, please [open an issue](https://github.com/magento/magento2/issues) to discuss the proposed changes prior to development. This may prevent duplicate or unnecessary effort and allow other contributors to provide input. +7. All automated tests must pass (all builds on [Travis CI](https://travis-ci.org/magento/magento2) must be green). ## Contribution process @@ -33,7 +33,7 @@ This will allow you to collaborate with the Magento 2 development team, fork the 1. Search current [listed issues](https://github.com/magento/magento2/issues) (open or closed) for similar proposals of intended contribution before starting work on a new contribution. 2. Sign the [Adobe Contributor License Agreement](https://opensource.adobe.com/cla.html) if you have not signed it before. 3. Create and test your work. -4. Fork the Magento 2 repository according to the [Fork A Repository instructions](https://devdocs.magento.com/guides/v2.3/contributor-guide/contributing.html#fork) and when you are ready to send us a pull request – follow the [Create A Pull Request instructions](https://devdocs.magento.com/guides/v2.3/contributor-guide/contributing.html#pull_request). +4. Fork the Magento 2 repository according to the [Fork A Repository instructions](https://developer.adobe.com/commerce/contributor/guides/code-contributions/) and when you are ready to send us a pull request – follow the [Create A Pull Request instructions](https://developer.adobe.com/commerce/contributor/guides/code-contributions/). 5. Once your contribution is received the Magento 2 development team will review the contribution and collaborate with you as needed. ## Code of Conduct diff --git a/README.md b/README.md index d63f6b68..a7b97f94 100644 --- a/README.md +++ b/README.md @@ -5,15 +5,15 @@ Welcome to the Magento Security Package community project! ## Overview Magento security package provides a set of security-related features including two-factor authentication for admins, Google ReCAPTCHA support for various forms, and Security.txt to support vulnerability -disclosure practices. +disclosure practices. ## Documentation - Complete documentation located on the project [wiki pages](https://github.com/magento/security-package/wiki): - How to start local development described in the [installation guide](https://github.com/magento/security-package/wiki/Metapackage-Installation-Guide) - - Two-factor authentication [user guide](https://docs.magento.com/user-guide/stores/security-two-factor-authentication.html) and [technical guide](https://devdocs.magento.com/guides/v2.4/security/two-factor-authentication.html). - - Google ReCAPTCHA [user guide](https://docs.magento.com/user-guide/stores/security-google-recaptcha.html) and [technical guide](https://devdocs.magento.com/guides/v2.4/security/google-recaptcha.html). - - Security.txt [user guide](https://docs.magento.com/user-guide/configuration/security/security-txt.html) and [technical guide](https://devdocs.magento.com/guides/v2.4/security/security-txt.html). + - Two-factor authentication [user guide](https://experienceleague.adobe.com/en/docs/commerce-admin/systems/security/2fa/security-two-factor-authentication) and [technical guide](https://developer.adobe.com/commerce/testing/functional-testing-framework/two-factor-authentication/). + - Google ReCAPTCHA [user guide](https://experienceleague.adobe.com/en/docs/commerce-admin/systems/security/captcha/security-google-recaptcha) and [technical guide](https://experienceleague.adobe.com/docs/commerce-admin/systems/security/captcha/security-google-recaptcha.html). + - Security.txt [user guide](https://experienceleague.adobe.com/en/docs/commerce-admin/config/security/security-txt) and [technical guide](https://experienceleague.adobe.com/docs/commerce-operations/configuration-guide/security/security-txt.html). ## Community Engineering Slack diff --git a/ReCaptchaAdminUi/Block/Adminhtml/System/Config/Form/Field/Notice.php b/ReCaptchaAdminUi/Block/Adminhtml/System/Config/Form/Field/Notice.php index 9a3b6032..05a38077 100644 --- a/ReCaptchaAdminUi/Block/Adminhtml/System/Config/Form/Field/Notice.php +++ b/ReCaptchaAdminUi/Block/Adminhtml/System/Config/Form/Field/Notice.php @@ -1,8 +1,9 @@ +--> +--> diff --git a/ReCaptchaAdminUi/etc/config.xml b/ReCaptchaAdminUi/etc/config.xml index 8f371674..18dd3c5a 100644 --- a/ReCaptchaAdminUi/etc/config.xml +++ b/ReCaptchaAdminUi/etc/config.xml @@ -1,10 +1,10 @@ +--> diff --git a/ReCaptchaAdminUi/etc/di.xml b/ReCaptchaAdminUi/etc/di.xml index ce8edb48..e987bfe2 100644 --- a/ReCaptchaAdminUi/etc/di.xml +++ b/ReCaptchaAdminUi/etc/di.xml @@ -1,10 +1,10 @@ +--> +--> diff --git a/ReCaptchaAdminUi/registration.php b/ReCaptchaAdminUi/registration.php index a6e545b4..fb9b66db 100644 --- a/ReCaptchaAdminUi/registration.php +++ b/ReCaptchaAdminUi/registration.php @@ -1,8 +1,9 @@ isCaptchaEnabled->isCaptchaEnabledFor($key)) { $jsLayout['components']['checkout']['children']['steps']['children']['billing-step']['children'] - ['payment']['children']['beforeMethods']['children']['place-order-recaptcha-container']['children'] + ['payment']['children']['payments-list']['children']['before-place-order']['children'] ['place-order-recaptcha']['settings'] = $this->captchaUiConfigResolver->get($key); } else { if (isset($jsLayout['components']['checkout']['children']['steps']['children']['billing-step']['children'] - ['payment']['children']['beforeMethods']['children']['place-order-recaptcha-container']['children'] + ['payment']['children']['payments-list']['children']['before-place-order']['children'] ['place-order-recaptcha'])) { unset($jsLayout['components']['checkout']['children']['steps']['children']['billing-step']['children'] - ['payment']['children']['beforeMethods']['children']['place-order-recaptcha-container'] + ['payment']['children']['payments-list']['children']['before-place-order'] ['children']['place-order-recaptcha']); } } diff --git a/ReCaptchaCheckout/Model/WebapiConfigProvider.php b/ReCaptchaCheckout/Model/WebapiConfigProvider.php index 34c353ef..8b5dc4b0 100644 --- a/ReCaptchaCheckout/Model/WebapiConfigProvider.php +++ b/ReCaptchaCheckout/Model/WebapiConfigProvider.php @@ -1,8 +1,9 @@ [ + 'checkout' => [ + 'children' => [ + 'steps' => [ + 'children' => [ + 'shipping-step' => [ + 'children' => [ + 'shippingAddress' => [ + 'children' => [ + 'customer-email' => [ + 'children' => [ + 'recaptcha' => [] + ] + ] + ] + ] + ] + ], + 'billing-step' => [ + 'children' => [ + 'payment' => [ + 'children' => [ + 'customer-email' => [ + 'children' => [ + 'recaptcha' => [] + ] + ], + 'payments-list' => [ + 'children' => [ + 'before-place-order' => [ + 'children' => [ + 'place-order-recaptcha' => [] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ], + 'authentication' => [ + 'children' => [ + 'recaptcha' => [] + ] + ] + ] + ] + ] + ]; + + /** + * @inheritdoc + */ + protected function setUp(): void + { + parent::setUp(); + $this->uiConfigResolver = $this->getMockForAbstractClass(UiConfigResolverInterface::class); + $this->isCaptchEnabled = $this->getMockForAbstractClass(IsCaptchaEnabledInterface::class); + $this->model = new Onepage( + $this->uiConfigResolver, + $this->isCaptchEnabled + ); + } + + /** + * @dataProvider processDataProvider + */ + public function testProcess(array $mocks, array $expected): void + { + $this->uiConfigResolver->method('get') + ->willReturnMap($mocks['uiConfigResolver']); + $this->isCaptchEnabled->method('isCaptchaEnabledFor') + ->willReturnMap($mocks['isCaptchaEnabled']); + $prefix = 'components/checkout/children/'; + $config = new DataObject($this->model->process($this->jsLayout)); + $actual = []; + foreach (array_keys($expected) as $path) { + $actual[$path] = $config->getDataByPath($prefix.$path); + } + $this->assertSame($expected, $actual); + } + + public static function processDataProvider(): array + { + return [ + [ + [ + 'isCaptchaEnabled' => [ + ['customer_login', false], + ['place_order', false], + ], + 'uiConfigResolver' => [ + ['customer_login', ['type' => 'invisible']], + ['place_order', ['type' => 'robot']], + ], + ], + [ + 'steps/children/shipping-step/children/shippingAddress/children/customer-email/children' => [], + 'steps/children/billing-step/children/payment/children/customer-email/children' => [], + 'authentication/children' => [], + 'steps/children/billing-step/children/payment/children/payments-list/children/before-place-order/' . + 'children' => [], + ] + ], + [ + [ + 'isCaptchaEnabled' => [ + ['customer_login', true], + ['place_order', true], + ], + 'uiConfigResolver' => [ + ['customer_login', ['type' => 'invisible']], + ['place_order', ['type' => 'robot']], + ], + ], + [ + 'steps/children/shipping-step/children/shippingAddress/children/' . + 'customer-email/children' => ['recaptcha' => ['settings' => ['type' => 'invisible']]], + 'steps/children/billing-step/children/payment/children/' . + 'customer-email/children' => ['recaptcha' => ['settings' => ['type' => 'invisible']]], + 'authentication/children' => ['recaptcha' => ['settings' => ['type' => 'invisible']]], + 'steps/children/billing-step/children/payment/children/payments-list/children/before-place-order/' . + 'children' => ['place-order-recaptcha' => ['settings' => ['type' => 'robot']]], + ] + ] + ]; + } +} diff --git a/ReCaptchaCheckout/composer.json b/ReCaptchaCheckout/composer.json index 5222f4dd..b30e0cb0 100644 --- a/ReCaptchaCheckout/composer.json +++ b/ReCaptchaCheckout/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-re-captcha-checkout", "description": "Google reCAPTCHA integration for Magento2", "require": { - "php": "~7.4.0||~8.0.0||~8.1.0", + "php": "~8.2.0||~8.3.0||~8.4.0", "magento/framework": "*", "magento/module-checkout": "*", "magento/module-re-captcha-ui": "*", diff --git a/ReCaptchaCheckout/etc/adminhtml/system.xml b/ReCaptchaCheckout/etc/adminhtml/system.xml index 6be7fbe7..cb760012 100644 --- a/ReCaptchaCheckout/etc/adminhtml/system.xml +++ b/ReCaptchaCheckout/etc/adminhtml/system.xml @@ -1,10 +1,10 @@ +--> diff --git a/ReCaptchaCheckout/etc/config.xml b/ReCaptchaCheckout/etc/config.xml index 8a1ce1fc..a60bd8ee 100644 --- a/ReCaptchaCheckout/etc/config.xml +++ b/ReCaptchaCheckout/etc/config.xml @@ -1,10 +1,10 @@ +--> diff --git a/ReCaptchaCheckout/etc/di.xml b/ReCaptchaCheckout/etc/di.xml index 401b976b..b4010553 100644 --- a/ReCaptchaCheckout/etc/di.xml +++ b/ReCaptchaCheckout/etc/di.xml @@ -1,10 +1,10 @@ +--> diff --git a/ReCaptchaCheckout/etc/frontend/di.xml b/ReCaptchaCheckout/etc/frontend/di.xml index 1d10cbea..3689d325 100644 --- a/ReCaptchaCheckout/etc/frontend/di.xml +++ b/ReCaptchaCheckout/etc/frontend/di.xml @@ -1,10 +1,10 @@ +--> diff --git a/ReCaptchaCheckout/etc/module.xml b/ReCaptchaCheckout/etc/module.xml index b260cb48..7550e9e7 100644 --- a/ReCaptchaCheckout/etc/module.xml +++ b/ReCaptchaCheckout/etc/module.xml @@ -1,10 +1,10 @@ +--> diff --git a/ReCaptchaCheckout/registration.php b/ReCaptchaCheckout/registration.php index 10e710d0..80dffeac 100644 --- a/ReCaptchaCheckout/registration.php +++ b/ReCaptchaCheckout/registration.php @@ -1,8 +1,9 @@ +--> @@ -49,18 +49,18 @@ - + - - uiComponent - Magento_ReCaptchaCheckout/payment-recaptcha-container - beforeMethods + - Magento_ReCaptchaWebapiUi/js/webapiReCaptcha - place-order-recaptcha + Magento_ReCaptchaCheckout/js/reCaptchaCheckout + before-place-order checkoutConfig recaptcha-checkout-place-order + + + diff --git a/ReCaptchaCheckout/view/frontend/requirejs-config.js b/ReCaptchaCheckout/view/frontend/requirejs-config.js index 2c6f3490..e112d147 100644 --- a/ReCaptchaCheckout/view/frontend/requirejs-config.js +++ b/ReCaptchaCheckout/view/frontend/requirejs-config.js @@ -1,6 +1,6 @@ /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ // eslint-disable-next-line no-unused-vars @@ -9,7 +9,11 @@ var config = { mixins: { 'Magento_Checkout/js/model/place-order': { 'Magento_ReCaptchaCheckout/js/model/place-order-mixin': true + }, + 'Magento_ReCaptchaWebapiUi/js/webapiReCaptchaRegistry': { + 'Magento_ReCaptchaCheckout/js/webapiReCaptchaRegistry-mixin': true } } } }; + diff --git a/ReCaptchaCheckout/view/frontend/web/js/model/place-order-mixin.js b/ReCaptchaCheckout/view/frontend/web/js/model/place-order-mixin.js index 1cb64d19..32b558a0 100644 --- a/ReCaptchaCheckout/view/frontend/web/js/model/place-order-mixin.js +++ b/ReCaptchaCheckout/view/frontend/web/js/model/place-order-mixin.js @@ -1,6 +1,6 @@ /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ /* eslint-disable max-nested-callbacks */ @@ -10,16 +10,24 @@ define([ 'mage/utils/wrapper', 'Magento_ReCaptchaWebapiUi/js/webapiReCaptchaRegistry' ], function ($, wrapper, recaptchaRegistry) { - 'use strict'; + 'use strict'; // eslint-disable-line return function (placeOrder) { return wrapper.wrap(placeOrder, function (originalAction, serviceUrl, payload, messageContainer) { - var recaptchaDeferred; + var recaptchaDeferred, + reCaptchaId, + $activeReCaptcha; - if (recaptchaRegistry.triggers.hasOwnProperty('recaptcha-checkout-place-order')) { + $activeReCaptcha = $('.recaptcha-checkout-place-order:visible .g-recaptcha'); + + if ($activeReCaptcha.length > 0) { + reCaptchaId = $activeReCaptcha.last().attr('id'); + } + + if (reCaptchaId !== undefined && recaptchaRegistry.triggers.hasOwnProperty(reCaptchaId)) { //ReCaptcha is present for checkout recaptchaDeferred = $.Deferred(); - recaptchaRegistry.addListener('recaptcha-checkout-place-order', function (token) { + recaptchaRegistry.addListener(reCaptchaId, function (token) { //Add reCaptcha value to place-order request and resolve deferred with the API call results payload.xReCaptchaValue = token; originalAction(serviceUrl, payload, messageContainer).done(function () { @@ -29,7 +37,15 @@ define([ }); }); //Trigger ReCaptcha validation - recaptchaRegistry.triggers['recaptcha-checkout-place-order'](); + recaptchaRegistry.triggers[reCaptchaId](); + + if ( + !recaptchaRegistry._isInvisibleType.hasOwnProperty(reCaptchaId) || + recaptchaRegistry._isInvisibleType[reCaptchaId] === false + ) { + //remove listener so that place order action is only triggered by the 'Place Order' button + recaptchaRegistry.removeListener(reCaptchaId); + } return recaptchaDeferred; } diff --git a/ReCaptchaCheckout/view/frontend/web/js/reCaptchaCheckout.js b/ReCaptchaCheckout/view/frontend/web/js/reCaptchaCheckout.js new file mode 100644 index 00000000..60740c3b --- /dev/null +++ b/ReCaptchaCheckout/view/frontend/web/js/reCaptchaCheckout.js @@ -0,0 +1,80 @@ +/** + * Copyright 2022 Adobe + * All Rights Reserved. + */ + +define( + [ + 'Magento_ReCaptchaWebapiUi/js/webapiReCaptcha', + 'jquery' + ], + function (Component, $) { + 'use strict'; // eslint-disable-line + + var reCaptchaIds = new WeakMap(), + uuid = 0; + + return Component.extend({ + defaults: { + template: 'Magento_ReCaptchaCheckout/reCaptcha', + skipPayments: [] // List of payment methods that do not require this reCaptcha + }, + + /** + * Render reCAPTCHA for payment method + * + * @param {Object} method + */ + renderReCaptchaFor: function (method) { + var reCaptcha; + + if (this.isCheckoutReCaptchaRequiredFor(method)) { + reCaptcha = $.extend(true, {}, this, {reCaptchaId: this.getReCaptchaIdFor(method)}); + reCaptcha.renderReCaptcha(); + } + }, + + /** + * Get reCAPTCHA ID for payment method + * + * @param {Object} method + * @returns {String} + */ + getReCaptchaIdFor: function (method) { + if (!reCaptchaIds.has(method)) { + reCaptchaIds.set(method, this.getReCaptchaId() + '-' + uuid++); + } + return reCaptchaIds.get(method); + }, + + /** + * Check whether checkout reCAPTCHA is required for payment method + * + * @param {Object} method + * @returns {Boolean} + */ + isCheckoutReCaptchaRequiredFor: function (method) { + return !this.skipPayments || !this.skipPayments.hasOwnProperty(method.getCode()); + }, + + /** + * @inheritdoc + */ + initCaptcha: function () { + var $wrapper, + $recaptchaResponseInput; + + this._super(); + // Since there will be multiple reCaptcha in the payment form, + // they may override each other if the form data is serialized and submitted. + // Instead, the reCaptcha response will be collected in the callback: reCaptchaCallback() + // and sent in the request header X-ReCaptcha + $wrapper = $('#' + this.getReCaptchaId() + '-wrapper'); + $recaptchaResponseInput = $wrapper.find('[name=g-recaptcha-response]'); + if ($recaptchaResponseInput.length) { + $recaptchaResponseInput.prop('disabled', true); + } + } + }); + } +); diff --git a/ReCaptchaCheckout/view/frontend/web/js/webapiReCaptchaRegistry-mixin.js b/ReCaptchaCheckout/view/frontend/web/js/webapiReCaptchaRegistry-mixin.js new file mode 100644 index 00000000..9a3723dc --- /dev/null +++ b/ReCaptchaCheckout/view/frontend/web/js/webapiReCaptchaRegistry-mixin.js @@ -0,0 +1,20 @@ +/** + * Copyright 2022 Adobe + * All Rights Reserved. + */ + +define([], function () { + 'use strict'; // eslint-disable-line + + return function (originalFunction) { + /** + * {@inheritDoc} + */ + originalFunction.addListener = function (id , func) { + this._listeners[id] = func; + }; + + return originalFunction; + }; + +}); diff --git a/ReCaptchaCheckout/view/frontend/web/template/payment-recaptcha-container.html b/ReCaptchaCheckout/view/frontend/web/template/payment-recaptcha-container.html index 932ecd74..711c5080 100644 --- a/ReCaptchaCheckout/view/frontend/web/template/payment-recaptcha-container.html +++ b/ReCaptchaCheckout/view/frontend/web/template/payment-recaptcha-container.html @@ -1,10 +1,10 @@
-
+
diff --git a/ReCaptchaCheckout/view/frontend/web/template/reCaptcha.html b/ReCaptchaCheckout/view/frontend/web/template/reCaptcha.html new file mode 100644 index 00000000..c2e4f37f --- /dev/null +++ b/ReCaptchaCheckout/view/frontend/web/template/reCaptcha.html @@ -0,0 +1,28 @@ + + +
+
+ +
+
+ +
+
+ +
+ diff --git a/ReCaptchaCheckoutSalesRule/Block/LayoutProcessor/Checkout/Onepage.php b/ReCaptchaCheckoutSalesRule/Block/LayoutProcessor/Checkout/Onepage.php new file mode 100644 index 00000000..7c09a820 --- /dev/null +++ b/ReCaptchaCheckoutSalesRule/Block/LayoutProcessor/Checkout/Onepage.php @@ -0,0 +1,63 @@ +captchaUiConfigResolver = $captchaUiConfigResolver; + $this->isCaptchaEnabled = $isCaptchaEnabled; + } + + /** + * @inheritDoc + */ + public function process($jsLayout) + { + $key = 'coupon_code'; + if ($this->isCaptchaEnabled->isCaptchaEnabledFor($key)) { + $jsLayout['components']['checkout']['children']['steps']['children']['billing-step']['children'] + ['payment']['children']['afterMethods']['children']['discount']['children'] + ['checkout_sales_rule']['settings'] = $this->captchaUiConfigResolver->get($key); + } else { + if (isset($jsLayout['components']['checkout']['children']['steps']['children']['billing-step']['children'] + ['payment']['children']['afterMethods']['children']['discount']['children']['checkout_sales_rule'])) { + unset($jsLayout['components']['checkout']['children']['steps']['children']['billing-step']['children'] + ['payment']['children']['afterMethods']['children']['discount']['children']['checkout_sales_rule']); + } + } + + return $jsLayout; + } +} diff --git a/ReCaptchaCheckoutSalesRule/Model/WebapiConfigProvider.php b/ReCaptchaCheckoutSalesRule/Model/WebapiConfigProvider.php new file mode 100644 index 00000000..a72eb04e --- /dev/null +++ b/ReCaptchaCheckoutSalesRule/Model/WebapiConfigProvider.php @@ -0,0 +1,64 @@ +isEnabled = $isEnabled; + $this->configResolver = $configResolver; + } + + /** + * @inheritDoc + */ + public function getConfigFor(EndpointInterface $endpoint): ?ValidationConfigInterface + { + //phpcs:disable Magento2.PHP.LiteralNamespaces + if ((($endpoint->getServiceClass() === 'Magento\Quote\Api\CouponManagementInterface' || + $endpoint->getServiceClass() === 'Magento\Quote\Api\GuestCouponManagementInterface') && + $endpoint->getServiceMethod() === 'set') + || ($endpoint->getServiceClass() === 'Magento\QuoteGraphQl\Model\Resolver\ApplyCouponToCart' + || $endpoint->getServiceMethod() === 'ApplyCouponToCart') + ) { + if ($this->isEnabled->isCaptchaEnabledFor(self::CAPTCHA_ID)) { + return $this->configResolver->get(self::CAPTCHA_ID); + } + } + //phpcs:enable Magento2.PHP.LiteralNamespaces + + return null; + } +} diff --git a/ReCaptchaCheckoutSalesRule/Observer/CouponCodeObserver.php b/ReCaptchaCheckoutSalesRule/Observer/CouponCodeObserver.php new file mode 100644 index 00000000..5d7c0e19 --- /dev/null +++ b/ReCaptchaCheckoutSalesRule/Observer/CouponCodeObserver.php @@ -0,0 +1,74 @@ +redirect = $redirect; + $this->isCaptchaEnabled = $isCaptchaEnabled; + $this->requestHandler = $requestHandler; + } + + /** + * @inheritdoc + * @param Observer $observer + * @return void + * @throws InputException + */ + public function execute(Observer $observer): void + { + /** @var Action $controller */ + $controller = $observer->getControllerAction(); + $request_param = $controller->getRequest()->getParams(); + if (!isset($request_param['remove']) && $this->isCaptchaEnabled->isCaptchaEnabledFor(self::CAPTCHA_KEY)) { + $request = $controller->getRequest(); + $response = $controller->getResponse(); + $redirectOnFailureUrl = $this->redirect->getRefererUrl(); + $this->requestHandler->execute(self::CAPTCHA_KEY, $request, $response, $redirectOnFailureUrl); + } + } +} diff --git a/ReCaptchaCheckoutSalesRule/Plugin/CouponSetLayoutPlugin.php b/ReCaptchaCheckoutSalesRule/Plugin/CouponSetLayoutPlugin.php new file mode 100644 index 00000000..5240be4b --- /dev/null +++ b/ReCaptchaCheckoutSalesRule/Plugin/CouponSetLayoutPlugin.php @@ -0,0 +1,41 @@ +getChildBlock('captcha')) { + $subject->addChild( + 'captcha', + ReCaptcha::class, + [ + 'jsLayout' => [ + 'components' => [ + 'captcha' => ['component' => 'Magento_ReCaptchaFrontendUi/js/reCaptcha'] + ] + ] + ] + ); + } + return $subject; + } +} diff --git a/ReCaptchaCheckoutSalesRule/README.md b/ReCaptchaCheckoutSalesRule/README.md new file mode 100644 index 00000000..450653a1 --- /dev/null +++ b/ReCaptchaCheckoutSalesRule/README.md @@ -0,0 +1,7 @@ +# Magento_ReCaptchaCheckoutSalesRule module + +Google reCAPTCHA ensures that a human being, rather than a computer (or "bot"), is interacting with your website. Unlike the standard Magento CAPTCHA, Google reCAPTCHA provides enhanced security with a selection of different display options and methods. Additional website traffic information is available in the dashboard of your Google reCAPTCHA account. + +This module provides the reCAPTCHA implementations related to coupon code apply action on checkout cart & payment. + +For more information, see the Magento document for reCAPTCHA. diff --git a/ReCaptchaCheckoutSalesRule/Test/Api/CouponApplyFormRecaptchaTest.php b/ReCaptchaCheckoutSalesRule/Test/Api/CouponApplyFormRecaptchaTest.php new file mode 100644 index 00000000..200f0e44 --- /dev/null +++ b/ReCaptchaCheckoutSalesRule/Test/Api/CouponApplyFormRecaptchaTest.php @@ -0,0 +1,174 @@ +_markTestAsRestOnly(); + $this->quoteFactory = Bootstrap::getObjectManager()->get(QuoteFactory::class); + $this->fixtures = Bootstrap::getObjectManager()->get(DataFixtureStorageManager::class)->getStorage(); + $this->customerTokenService = Bootstrap::getObjectManager()->get(CustomerTokenServiceInterface::class); + } + + #[ + ConfigFixture('recaptcha_frontend/type_invisible/public_key', 'test_public_key'), + ConfigFixture('recaptcha_frontend/type_invisible/private_key', 'test_private_key'), + ConfigFixture('recaptcha_frontend/type_for/coupon_code', 'invisible'), + DataFixture(ProductFixture::class, as: 'product'), + DataFixture(Customer::class, as: 'customer'), + DataFixture(CustomerCart::class, ['customer_id' => '$customer.id$'], as: 'cart'), + DataFixture( + AddProductToCart::class, + ['cart_id' => '$cart.id$', 'product_id' => '$product.id$', 'qty' => 5], + 'cart_item' + ), + DataFixture(SetShippingAddress::class, ['cart_id' => '$cart.id$']), + DataFixture( + SalesRuleFixture::class, + [ + 'coupon_code' => 'coupon%uniqid%', + 'discount_amount' => 5.00, + 'coupon_type' => 2, + 'simple_action' => 'by_fixed' + ], + 'sales_rule' + ) + ] + public function testRequired(): void + { + $this->expectException(Throwable::class); + $this->expectExceptionCode(400); + $this->expectExceptionMessageMatches('/.*ReCaptcha validation failed, please try again.*/'); + + $this->_webApiCall( + [ + 'rest' => [ + 'resourcePath' => sprintf( + self::API_ROUTE, + $this->fixtures->get('sales_rule')->getCouponCode() + ), + 'httpMethod' => Request::HTTP_METHOD_PUT, + 'token' => $this->getCustomerAuthToken( + $this->fixtures->get('customer')->getEmail() + ), + ], + ], + [ + 'cart_id' => $this->fixtures->get('cart')->getId() + ] + ); + } + + #[ + ConfigFixture('recaptcha_frontend/type_invisible/public_key', 'test_public_key'), + ConfigFixture('recaptcha_frontend/type_invisible/private_key', 'test_private_key'), + ConfigFixture('recaptcha_frontend/type_for/coupon_code', 'invisible'), + DataFixture(ProductFixture::class, as: 'product'), + DataFixture(GuestCart::class, as: 'quote'), + DataFixture(QuoteIdMask::class, ['cart_id' => '$quote.id$'], 'cart_mask'), + DataFixture(SetGuestEmailFixture::class, ['cart_id' => '$quote.id$']), + DataFixture( + AddProductToCart::class, + ['cart_id' => '$quote.id$', 'product_id' => '$product.id$', 'qty' => 5], + 'cart_item' + ), + DataFixture(SetShippingAddress::class, ['cart_id' => '$quote.id$']), + DataFixture( + SalesRuleFixture::class, + [ + 'coupon_code' => 'coupon%uniqid%', + 'discount_amount' => 5.00, + 'coupon_type' => 2, + 'simple_action' => 'by_fixed' + ], + 'sales_rule' + ) + ] + public function testGuestCartTest(): void + { + $this->expectException(Throwable::class); + $this->expectExceptionCode(400); + $this->expectExceptionMessageMatches('/.*ReCaptcha validation failed, please try again.*/'); + + $cartId = $this->fixtures->get('cart_mask')->getMaskedId(); + $couponCode = $this->fixtures->get('sales_rule')->getCouponCode(); + $this->_webApiCall( + [ + 'rest' => [ + 'resourcePath' => "/V1/guest-carts/$cartId/coupons/" . $couponCode, + 'httpMethod' => Request::HTTP_METHOD_PUT, + 'token' => null + ], + ], + [] + ); + } + + /** + * Get customer authentication token + * + * @param string $email + * @return string + * @throws AuthenticationException + * @throws EmailNotConfirmedException + */ + private function getCustomerAuthToken(string $email): string + { + return $this->customerTokenService->createCustomerAccessToken($email, 'password'); + } +} diff --git a/ReCaptchaCheckoutSalesRule/Test/Api/CouponApplyGraphQLTest.php b/ReCaptchaCheckoutSalesRule/Test/Api/CouponApplyGraphQLTest.php new file mode 100644 index 00000000..4653f817 --- /dev/null +++ b/ReCaptchaCheckoutSalesRule/Test/Api/CouponApplyGraphQLTest.php @@ -0,0 +1,138 @@ +fixtures = Bootstrap::getObjectManager()->get(DataFixtureStorageManager::class)->getStorage(); + $this->customerTokenService = Bootstrap::getObjectManager()->get(CustomerTokenServiceInterface::class); + } + + #[ + DataFixture(ProductFixture::class, as: 'product'), + DataFixture(Customer::class, as: 'customer'), + DataFixture(CustomerCart::class, ['customer_id' => '$customer.id$'], as: 'cart'), + DataFixture( + AddProductToCart::class, + ['cart_id' => '$cart.id$', 'product_id' => '$product.id$', 'qty' => 5], + 'cart_item' + ), + DataFixture(SetShippingAddress::class, ['cart_id' => '$cart.id$']), + DataFixture(QuoteIdMask::class, ['cart_id' => '$cart.id$'], 'cart_mask'), + DataFixture( + SalesRuleFixture::class, + [ + 'coupon_code' => 'coupon%uniqid%', + 'discount_amount' => 5.00, + 'coupon_type' => 2, + 'simple_action' => 'by_fixed' + ], + 'sales_rule' + ), + ConfigFixture('customer/captcha/enable', '0'), + ConfigFixture('recaptcha_frontend/type_invisible/public_key', 'test_public_key'), + ConfigFixture('recaptcha_frontend/type_invisible/private_key', 'test_private_key'), + ConfigFixture('recaptcha_frontend/type_for/coupon_code', 'invisible') + ] + public function testCreateCouponApply(): void + { + $this->expectException(\Throwable::class); + $this->expectExceptionMessage('ReCaptcha validation failed, please try again'); + + $this->graphQlMutation( + $this->getApplyCouponToCartMutation( + $this->fixtures->get('cart_mask')->getMaskedId(), + $this->fixtures->get('sales_rule')->getCouponCode() + ), + [], + '', + $this->getCustomerAuthHeaders($this->fixtures->get('customer')->getEmail()) + ); + } + + /** + * Prepare mutation for applying coupon to cart + * + * @param string $cartId + * @return string + */ + private function getApplyCouponToCartMutation(string $cartId, string $couponCode): string + { + return <<customerTokenService->createCustomerAccessToken($email, 'password'); + + return ['Authorization' => 'Bearer ' . $token]; + } +} diff --git a/ReCaptchaCheckoutSalesRule/Test/Integration/CouponApplyPostTest.php b/ReCaptchaCheckoutSalesRule/Test/Integration/CouponApplyPostTest.php new file mode 100644 index 00000000..c36e5b03 --- /dev/null +++ b/ReCaptchaCheckoutSalesRule/Test/Integration/CouponApplyPostTest.php @@ -0,0 +1,172 @@ +customerSession = $this->_objectManager->get(Session::class); + $this->customerSession->setCustomerId(self::CUSTOMER_ID); + $this->checkoutSession = $this->_objectManager->create(CheckoutSession::class); + } + + /** + * Verifying that recaptcha is present on the CouponPost form/page and keys are configured + * + * @magentoDataFixture Magento/Checkout/_files/quote_with_virtual_product_and_address.php + * @magentoDataFixture Magento/Usps/Fixtures/cart_rule_coupon_free_shipping.php + * + * @magentoConfigFixture base_website recaptcha_frontend/type_invisible/public_key test_public_key + * @magentoConfigFixture base_website recaptcha_frontend/type_invisible/private_key test_private_key + * @magentoConfigFixture base_website recaptcha_frontend/type_for/coupon_code invisible + * @magentoConfigFixture default_store recaptcha_frontend/type_for/coupon_code invisible + */ + public function testGetRequestIfReCaptchaIsEnabled(): void + { + $quote = $this->checkoutSession->getQuote(); + $quote->setData('trigger_recollect', 1)->setTotalsCollectedFlag(true); + $this->checkSuccessfulGetResponse(true); + } + + /** + * Checks the coupon post with ReCaptcha validation when `g-recaptcha-response` missed + * + * @magentoConfigFixture base_website recaptcha_frontend/type_invisible/public_key test_public_key + * @magentoConfigFixture base_website recaptcha_frontend/type_invisible/private_key test_private_key + * @magentoConfigFixture base_website recaptcha_frontend/type_for/coupon_code invisible + * + * It's needed for proper work of "ifconfig" in layout during tests running + * @magentoConfigFixture default_store recaptcha_frontend/type_for/coupon_code invisible + * @magentoDataFixture Magento/Checkout/_files/quote_with_virtual_product_and_address.php + */ + public function testPostRequestIfReCaptchaParameterIsMissed(): void + { + $this->checkFailedPostRequest(); + $this->assertSessionMessages( + $this->equalTo(['The coupon code "test" is not valid.']), + MessageInterface::TYPE_ERROR + ); + } + + /** + * Checks the failed coupon post with ReCaptcha validation + * + * @magentoConfigFixture base_website recaptcha_frontend/type_invisible/public_key test_public_key + * @magentoConfigFixture base_website recaptcha_frontend/type_invisible/private_key test_private_key + * @magentoConfigFixture base_website recaptcha_frontend/type_for/coupon_code invisible + * + * @magentoConfigFixture default_store recaptcha_frontend/type_for/coupon_code invisible + * @magentoDataFixture Magento/Checkout/_files/quote_with_virtual_product_and_address.php + */ + public function testPostRequestWithFailedReCaptchaValidation(): void + { + $this->checkFailedPostRequest(true); + $this->assertSessionMessages( + $this->equalTo(['The coupon code "test" is not valid.']), + MessageInterface::TYPE_ERROR + ); + } + + /** + * Checks GET response + * + * @param bool $shouldContainReCaptcha + * @return void + */ + private function checkSuccessfulGetResponse(bool $shouldContainReCaptcha = false): void + { + $this->getRequest()->setMethod(Http::METHOD_GET); + $this->dispatch('checkout/cart/'); + $response = $this->getResponse(); + $content = $response->getContent(); + + $this->assertNotEmpty($content); + $shouldContainReCaptcha + ? $this->assertStringContainsString('field-recaptcha', $content) + : $this->assertStringNotContainsString('field-recaptcha', $content); + + $this->assertEmpty($this->getSessionMessages(\Magento\Framework\Message\MessageInterface::TYPE_ERROR)); + } + + /** + * Checks failed sharing process + * + * @param bool $withParamReCaptcha + */ + private function checkFailedPostRequest(bool $withParamReCaptcha = false): void + { + $this->makePostRequest($withParamReCaptcha); + } + + /** + * Makes post request + * + * @param bool $withParamReCaptcha + * @return void + */ + private function makePostRequest(bool $withParamReCaptcha = false): void + { + $postValue = [ + 'remove' => 0, + 'coupon_code' => 'test' + ]; + + if ($withParamReCaptcha) { + $postValue[CaptchaResponseResolverInterface::PARAM_RECAPTCHA] = 'test'; + } + + $this->getRequest() + ->setMethod(Http::METHOD_POST) + ->setPostValue($postValue); + $this->dispatch('checkout/cart/couponPost/'); + } + + /** + * @inheritDoc + */ + public function tearDown(): void + { + $this->customerSession->setCustomerId(null); + parent::tearDown(); + } +} diff --git a/ReCaptchaCheckoutSalesRule/composer.json b/ReCaptchaCheckoutSalesRule/composer.json new file mode 100644 index 00000000..2d4005d7 --- /dev/null +++ b/ReCaptchaCheckoutSalesRule/composer.json @@ -0,0 +1,26 @@ +{ + "name": "magento/module-re-captcha-checkout-sales-rule", + "description": "Google ReCaptcha integration for Magento2 coupons", + "require": { + "php": "~8.2.0||~8.3.0||~8.4.0", + "magento/framework": "*", + "magento/module-checkout": "*", + "magento/module-sales-rule": "*", + "magento/module-re-captcha-ui": "*", + "magento/module-re-captcha-validation-api": "*", + "magento/module-re-captcha-admin-ui": "*", + "magento/module-re-captcha-frontend-ui": "*", + "magento/module-re-captcha-webapi-api": "*", + "magento/module-re-captcha-webapi-ui": "*" + }, + "type": "magento2-module", + "license": "OSL-3.0", + "autoload": { + "files": [ + "registration.php" + ], + "psr-4": { + "Magento\\ReCaptchaCheckoutSalesRule\\": "" + } + } +} diff --git a/ReCaptchaCheckoutSalesRule/etc/adminhtml/system.xml b/ReCaptchaCheckoutSalesRule/etc/adminhtml/system.xml new file mode 100644 index 00000000..f8219494 --- /dev/null +++ b/ReCaptchaCheckoutSalesRule/etc/adminhtml/system.xml @@ -0,0 +1,21 @@ + + + + +
+ + + + Magento\ReCaptchaAdminUi\Model\OptionSource\Type + + +
+
+
diff --git a/ReCaptchaCheckoutSalesRule/etc/config.xml b/ReCaptchaCheckoutSalesRule/etc/config.xml new file mode 100644 index 00000000..065a7ff3 --- /dev/null +++ b/ReCaptchaCheckoutSalesRule/etc/config.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + diff --git a/ReCaptchaCheckoutSalesRule/etc/di.xml b/ReCaptchaCheckoutSalesRule/etc/di.xml new file mode 100644 index 00000000..c123061e --- /dev/null +++ b/ReCaptchaCheckoutSalesRule/etc/di.xml @@ -0,0 +1,17 @@ + + + + + + + Magento\ReCaptchaCheckoutSalesRule\Model\WebapiConfigProvider + + + + \ No newline at end of file diff --git a/ReCaptchaCheckoutSalesRule/etc/frontend/di.xml b/ReCaptchaCheckoutSalesRule/etc/frontend/di.xml new file mode 100644 index 00000000..ad5d551e --- /dev/null +++ b/ReCaptchaCheckoutSalesRule/etc/frontend/di.xml @@ -0,0 +1,20 @@ + + + + + + + + + + Magento\ReCaptchaCheckoutSalesRule\Block\LayoutProcessor\Checkout\Onepage + + + + diff --git a/ReCaptchaCheckoutSalesRule/etc/frontend/events.xml b/ReCaptchaCheckoutSalesRule/etc/frontend/events.xml new file mode 100644 index 00000000..501baa46 --- /dev/null +++ b/ReCaptchaCheckoutSalesRule/etc/frontend/events.xml @@ -0,0 +1,13 @@ + + + + + + + diff --git a/ReCaptchaCheckoutSalesRule/etc/module.xml b/ReCaptchaCheckoutSalesRule/etc/module.xml new file mode 100644 index 00000000..47b46b1c --- /dev/null +++ b/ReCaptchaCheckoutSalesRule/etc/module.xml @@ -0,0 +1,15 @@ + + + + + + + + + diff --git a/ReCaptchaCheckoutSalesRule/registration.php b/ReCaptchaCheckoutSalesRule/registration.php new file mode 100644 index 00000000..022d1c62 --- /dev/null +++ b/ReCaptchaCheckoutSalesRule/registration.php @@ -0,0 +1,13 @@ + + + + + + + + coupon_code + + + + Magento_ReCaptchaFrontendUi/js/reCaptcha + + + + + + + + diff --git a/ReCaptchaCheckoutSalesRule/view/frontend/layout/checkout_index_index.xml b/ReCaptchaCheckoutSalesRule/view/frontend/layout/checkout_index_index.xml new file mode 100644 index 00000000..bee81f33 --- /dev/null +++ b/ReCaptchaCheckoutSalesRule/view/frontend/layout/checkout_index_index.xml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + uiComponent + + + + + + + Magento_SalesRule/js/view/payment/discount + + + Magento_ReCaptchaCheckoutSalesRule/js/checkout-sales-rule + captcha + checkout-sales-rule-request + checkoutConfig + recaptcha-checkout-coupon-apply + + + + + + + + + + + + + + + + + + + diff --git a/ReCaptchaCheckoutSalesRule/view/frontend/web/css/source/_module.less b/ReCaptchaCheckoutSalesRule/view/frontend/web/css/source/_module.less new file mode 100644 index 00000000..2f816705 --- /dev/null +++ b/ReCaptchaCheckoutSalesRule/view/frontend/web/css/source/_module.less @@ -0,0 +1,10 @@ +/** + * Copyright 2021 Adobe + * All Rights Reserved. + */ + +.form-discount { + .g-recaptcha { + margin-top: 50px !important; + } +} diff --git a/ReCaptchaCheckoutSalesRule/view/frontend/web/js/checkout-sales-rule.js b/ReCaptchaCheckoutSalesRule/view/frontend/web/js/checkout-sales-rule.js new file mode 100644 index 00000000..b6a5ead3 --- /dev/null +++ b/ReCaptchaCheckoutSalesRule/view/frontend/web/js/checkout-sales-rule.js @@ -0,0 +1,91 @@ +/** + * Copyright 2021 Adobe + * All Rights Reserved. + */ + +/* global grecaptcha */ +define( + [ + 'Magento_ReCaptchaWebapiUi/js/webapiReCaptcha', + 'Magento_ReCaptchaWebapiUi/js/webapiReCaptchaRegistry', + 'jquery', + 'Magento_SalesRule/js/action/set-coupon-code', + 'Magento_SalesRule/js/action/cancel-coupon', + 'Magento_Checkout/js/model/quote', + 'ko' + ], function (Component, recaptchaRegistry, $, setCouponCodeAction, cancelCouponAction, quote, ko) { + 'use strict'; // eslint-disable-line + + var totals = quote.getTotals(), + couponCode = ko.observable(null), + isApplied; + + if (totals()) { + couponCode(totals()['coupon_code']); + } + //Captcha can only be required for adding a coupon so we need to know if one was added already. + isApplied = ko.observable(couponCode() != null); + + return Component.extend({ + + /** + * Initialize parent form. + * + * @param {Object} parentForm + * @param {String} widgetId + */ + initParentForm: function (parentForm, widgetId) { + var self = this, + xRecaptchaValue, + captchaId = this.getReCaptchaId(); + + this._super(); + + if (couponCode() != null) { + if (isApplied) { + self.validateReCaptcha(true); + $('#' + captchaId).hide(); + } + } + + if (recaptchaRegistry.triggers.hasOwnProperty('recaptcha-checkout-coupon-apply')) { + recaptchaRegistry.addListener('recaptcha-checkout-coupon-apply', function (token) { + //Add reCaptcha value to coupon request + xRecaptchaValue = token; + }); + } + + setCouponCodeAction.registerDataModifier(function (headers) { + headers['X-ReCaptcha'] = xRecaptchaValue; + }); + + if (self.getIsInvisibleRecaptcha()) { + grecaptcha.execute(widgetId); + self.validateReCaptcha(true); + } + //Refresh reCaptcha after failed request. + setCouponCodeAction.registerFailCallback(function () { + if (self.getIsInvisibleRecaptcha()) { + grecaptcha.execute(widgetId); + self.validateReCaptcha(true); + } else { + self.validateReCaptcha(false); + grecaptcha.reset(widgetId); + $('#' + captchaId).show(); + } + }); + //Hide captcha when a coupon has been applied. + setCouponCodeAction.registerSuccessCallback(function () { + self.validateReCaptcha(true); + $('#' + captchaId).hide(); + }); + //Show captcha again if it was canceled. + cancelCouponAction.registerSuccessCallback(function () { + self.validateReCaptcha(false); + grecaptcha.reset(widgetId); + $('#' + captchaId).show(); + }); + } + }); + } +); diff --git a/ReCaptchaContact/Model/WebapiConfigProvider.php b/ReCaptchaContact/Model/WebapiConfigProvider.php new file mode 100644 index 00000000..6c33ad4d --- /dev/null +++ b/ReCaptchaContact/Model/WebapiConfigProvider.php @@ -0,0 +1,47 @@ +getServiceMethod() === 'resolve' + && $endpoint->getServiceClass() === ContactUs::class + && $this->isEnabled->isCaptchaEnabledFor(self::CAPTCHA_ID) + ) { + return $this->configResolver->get(self::CAPTCHA_ID); + } + + return null; + } +} diff --git a/ReCaptchaContact/Observer/ContactFormObserver.php b/ReCaptchaContact/Observer/ContactFormObserver.php index 4ff8d9e0..c83c340d 100644 --- a/ReCaptchaContact/Observer/ContactFormObserver.php +++ b/ReCaptchaContact/Observer/ContactFormObserver.php @@ -1,8 +1,9 @@ expectExceptionMessage('ReCaptcha validation failed, please try again'); + $this->graphQlMutation($this->getContactUsMutation()); + } + + /** + * Get contact us graphql mutation query + * + * @return string + */ + private function getContactUsMutation(): string + { + return << +--> diff --git a/ReCaptchaContact/etc/config.xml b/ReCaptchaContact/etc/config.xml index a7060fc1..4f7ae61d 100644 --- a/ReCaptchaContact/etc/config.xml +++ b/ReCaptchaContact/etc/config.xml @@ -1,10 +1,10 @@ +--> diff --git a/ReCaptchaContact/etc/di.xml b/ReCaptchaContact/etc/di.xml new file mode 100644 index 00000000..8b1bba05 --- /dev/null +++ b/ReCaptchaContact/etc/di.xml @@ -0,0 +1,17 @@ + + + + + + + Magento\ReCaptchaContact\Model\WebapiConfigProvider + + + + diff --git a/ReCaptchaContact/etc/frontend/di.xml b/ReCaptchaContact/etc/frontend/di.xml new file mode 100644 index 00000000..da2ef61e --- /dev/null +++ b/ReCaptchaContact/etc/frontend/di.xml @@ -0,0 +1,25 @@ + + + + + + + + Magento\ReCaptchaContact\Model\ButtonLock\ContactUsFormSubmit + + + + + + + contact_us_form_submit + contact + + + diff --git a/ReCaptchaContact/etc/frontend/events.xml b/ReCaptchaContact/etc/frontend/events.xml index 45a325a0..8e56819e 100644 --- a/ReCaptchaContact/etc/frontend/events.xml +++ b/ReCaptchaContact/etc/frontend/events.xml @@ -1,10 +1,10 @@ +--> diff --git a/ReCaptchaContact/etc/module.xml b/ReCaptchaContact/etc/module.xml index 10f3f1ed..2c7573c7 100644 --- a/ReCaptchaContact/etc/module.xml +++ b/ReCaptchaContact/etc/module.xml @@ -1,10 +1,10 @@ +--> diff --git a/ReCaptchaContact/registration.php b/ReCaptchaContact/registration.php index aac21c51..eb473733 100644 --- a/ReCaptchaContact/registration.php +++ b/ReCaptchaContact/registration.php @@ -1,8 +1,9 @@ +--> diff --git a/ReCaptchaCustomer/Model/AjaxLogin/CaptchaResponseResolver.php b/ReCaptchaCustomer/Model/AjaxLogin/CaptchaResponseResolver.php index dacd2c8f..bc3ba267 100644 --- a/ReCaptchaCustomer/Model/AjaxLogin/CaptchaResponseResolver.php +++ b/ReCaptchaCustomer/Model/AjaxLogin/CaptchaResponseResolver.php @@ -1,8 +1,9 @@ configResolver = $configResolver; } + /** + * Validates ifChangedPasswordCaptchaEnabled using captcha_id + * + * @param string $serviceMethod + * @param string $serviceClass + * @return ValidationConfigInterface|null + * @throws InputException + */ + private function validateChangePasswordCaptcha($serviceMethod, $serviceClass): ?ValidationConfigInterface + { + if ($this->isResetPasswordCase($serviceMethod, $serviceClass)) { + $captchaId = self::RESET_PASSWORD_CAPTCHA_ID; + } elseif ($this->isChangePasswordCase($serviceMethod, $serviceClass)) { + $captchaId = self::CUSTOMER_EDIT_CAPTCHA_ID; + } + + if (isset($captchaId) && $this->isEnabled->isCaptchaEnabledFor($captchaId)) { + return $this->configResolver->get($captchaId); + } + + return null; + } + + /** + * Check if the request is related to reset password + * + * @param string $serviceMethod + * @param string $serviceClass + * @return bool + */ + private function isResetPasswordCase(string $serviceMethod, string $serviceClass): bool + { + return in_array($serviceMethod, ['resetPassword', 'initiatePasswordReset'], true) + || in_array($serviceClass, [ResetPassword::class, RequestPasswordResetEmail::class], true); + } + + /** + * Check if the request is related to change password + * + * @param string $serviceMethod + * @param string $serviceClass + * @return bool + */ + private function isChangePasswordCase(string $serviceMethod, string $serviceClass): bool + { + return $serviceMethod === 'changePasswordById' + || in_array($serviceClass, [ChangePassword::class, UpdateCustomer::class], true); + } + + /** + * Validates ifLoginCaptchaEnabled using captcha_id + * + * @return ValidationConfigInterface|null + */ + private function validateLoginCaptcha(): ?ValidationConfigInterface + { + return $this->isEnabled->isCaptchaEnabledFor(self::LOGIN_CAPTCHA_ID) ? + $this->configResolver->get(self::LOGIN_CAPTCHA_ID) : null; + } + + /** + * Validates ifCreateCustomerCaptchaEnabled using captcha_id + * + * @return ValidationConfigInterface|null + */ + private function validateCreateCustomerCaptcha(): ?ValidationConfigInterface + { + return $this->isEnabled->isCaptchaEnabledFor(self::CREATE_CAPTCHA_ID) ? + $this->configResolver->get(self::CREATE_CAPTCHA_ID) : null; + } + /** * @inheritDoc */ @@ -55,38 +132,19 @@ public function getConfigFor(EndpointInterface $endpoint): ?ValidationConfigInte $serviceMethod = $endpoint->getServiceMethod(); //phpcs:disable Magento2.PHP.LiteralNamespaces - if ($serviceMethod === 'resetPassword' - || $serviceMethod === 'initiatePasswordReset' - || $serviceClass === 'Magento\CustomerGraphQl\Model\Resolver\ResetPassword') { - if ($this->isEnabled->isCaptchaEnabledFor(self::RESET_PASSWORD_CAPTCHA_ID)) { - return $this->configResolver->get(self::RESET_PASSWORD_CAPTCHA_ID); - } - } elseif ($serviceMethod === 'changePasswordById' - || $serviceClass === 'Magento\CustomerGraphQl\Model\Resolver\ChangePassword') { - if ($this->isEnabled->isCaptchaEnabledFor(self::CHANGE_PASSWORD_CAPTCHA_ID)) { - return $this->configResolver->get(self::CHANGE_PASSWORD_CAPTCHA_ID); - } - } elseif ( - ($serviceClass === 'Magento\Integration\Api\CustomerTokenServiceInterface' - && $serviceMethod === 'createCustomerAccessToken' - ) + if (($serviceClass === 'Magento\Integration\Api\CustomerTokenServiceInterface' + && $serviceMethod === 'createCustomerAccessToken') || $serviceClass === 'Magento\CustomerGraphQl\Model\Resolver\GenerateCustomerToken' ) { - if ($this->isEnabled->isCaptchaEnabledFor(self::LOGIN_CAPTCHA_ID)) { - return $this->configResolver->get(self::LOGIN_CAPTCHA_ID); - } - } elseif ( - ($serviceClass === 'Magento\Customer\Api\AccountManagementInterface' - && $serviceMethod === 'createAccount' - ) + return $this->validateLoginCaptcha(); + } elseif (($serviceClass === 'Magento\Customer\Api\AccountManagementInterface' + && $serviceMethod === 'createAccount') || $serviceClass === 'Magento\CustomerGraphQl\Model\Resolver\CreateCustomer' ) { - if ($this->isEnabled->isCaptchaEnabledFor(self::CREATE_CAPTCHA_ID)) { - return $this->configResolver->get(self::CREATE_CAPTCHA_ID); - } + return $this->validateCreateCustomerCaptcha(); } //phpcs:enable Magento2.PHP.LiteralNamespaces - return null; + return $this->validateChangePasswordCaptcha($serviceMethod, $serviceClass); } } diff --git a/ReCaptchaCustomer/Observer/AjaxLoginObserver.php b/ReCaptchaCustomer/Observer/AjaxLoginObserver.php index 10b54cce..38de61da 100644 --- a/ReCaptchaCustomer/Observer/AjaxLoginObserver.php +++ b/ReCaptchaCustomer/Observer/AjaxLoginObserver.php @@ -1,8 +1,9 @@ _webApiCall($serviceInfo, $requestData); } - } diff --git a/ReCaptchaCustomer/Test/Api/GraphQl/Customer/UpdateCustomerTest.php b/ReCaptchaCustomer/Test/Api/GraphQl/Customer/UpdateCustomerTest.php new file mode 100644 index 00000000..e2ff2cbb --- /dev/null +++ b/ReCaptchaCustomer/Test/Api/GraphQl/Customer/UpdateCustomerTest.php @@ -0,0 +1,131 @@ +customerTokenService = Bootstrap::getObjectManager()->get(CustomerTokenServiceInterface::class); + $this->customer = DataFixtureStorageManager::getStorage()->get('customer'); + } + + #[ + DataFixture(Customer::class, as: 'customer'), + ConfigFixture('recaptcha_frontend/type_invisible/public_key', 'test_public_key'), + ConfigFixture('recaptcha_frontend/type_invisible/private_key', 'test_private_key'), + ConfigFixture('recaptcha_frontend/type_for/customer_edit', 'invisible') + ] + public function testUpdateCustomerV2ReCaptchaValidationFailed(): void + { + $this->expectExceptionMessage('ReCaptcha validation failed, please try again'); + $this->graphQlMutation( + $this->getUpdateCustomerV2Mutation(), + [], + '', + $this->getCustomerAuthHeaders($this->customer->getEmail()) + ); + } + + #[ + DataFixture(Customer::class, as: 'customer'), + ConfigFixture('recaptcha_frontend/type_invisible/public_key', 'test_public_key'), + ConfigFixture('recaptcha_frontend/type_invisible/private_key', 'test_private_key'), + ConfigFixture('recaptcha_frontend/type_for/customer_edit', 'invisible') + ] + public function testUpdateCustomerReCaptchaValidationFailed(): void + { + $this->expectExceptionMessage('ReCaptcha validation failed, please try again'); + $this->graphQlMutation( + $this->getUpdateCustomerMutation(), + [], + '', + $this->getCustomerAuthHeaders($this->customer->getEmail()) + ); + } + + /** + * Get update customer graphql mutation + * + * @return string + */ + private function getUpdateCustomerMutation(): string + { + return <<customerTokenService->createCustomerAccessToken($email, 'password'); + return ['Authorization' => 'Bearer ' . $customerToken]; + } +} diff --git a/ReCaptchaCustomer/Test/Api/LoginGraphQlTest.php b/ReCaptchaCustomer/Test/Api/LoginGraphQlTest.php index 5893b33c..e72754cc 100644 --- a/ReCaptchaCustomer/Test/Api/LoginGraphQlTest.php +++ b/ReCaptchaCustomer/Test/Api/LoginGraphQlTest.php @@ -1,7 +1,7 @@ +--> diff --git a/ReCaptchaCustomer/etc/config.xml b/ReCaptchaCustomer/etc/config.xml index 11ee5351..239f5cde 100644 --- a/ReCaptchaCustomer/etc/config.xml +++ b/ReCaptchaCustomer/etc/config.xml @@ -1,10 +1,10 @@ +--> diff --git a/ReCaptchaCustomer/etc/di.xml b/ReCaptchaCustomer/etc/di.xml index 7751d8cd..40c37502 100644 --- a/ReCaptchaCustomer/etc/di.xml +++ b/ReCaptchaCustomer/etc/di.xml @@ -1,10 +1,10 @@ +--> diff --git a/ReCaptchaCustomer/etc/frontend/di.xml b/ReCaptchaCustomer/etc/frontend/di.xml index 1d090238..7572ae1b 100644 --- a/ReCaptchaCustomer/etc/frontend/di.xml +++ b/ReCaptchaCustomer/etc/frontend/di.xml @@ -1,10 +1,10 @@ +--> @@ -20,5 +20,38 @@ - + + + + Magento\ReCaptchaCustomer\Model\ButtonLock\CustomerCreateFormSubmit + Magento\ReCaptchaCustomer\Model\ButtonLock\CustomerEditFormSubmit + Magento\ReCaptchaCustomer\Model\ButtonLock\CustomerForgotPasswordFormSubmit + Magento\ReCaptchaCustomer\Model\ButtonLock\CustomerLoginFormSubmit + + + + + + customer_create_form_submit + customer_create + + + + + customer_edit_form_submit + customer_edit + + + + + customer_forgot_password_form_submit + customer_forgot_password + + + + + customer_login_form_submit + customer_login + + diff --git a/ReCaptchaCustomer/etc/frontend/events.xml b/ReCaptchaCustomer/etc/frontend/events.xml index 8166d9b6..212948ef 100644 --- a/ReCaptchaCustomer/etc/frontend/events.xml +++ b/ReCaptchaCustomer/etc/frontend/events.xml @@ -1,10 +1,10 @@ +--> diff --git a/ReCaptchaCustomer/etc/module.xml b/ReCaptchaCustomer/etc/module.xml index 0d20ca70..ec3d50d8 100644 --- a/ReCaptchaCustomer/etc/module.xml +++ b/ReCaptchaCustomer/etc/module.xml @@ -1,10 +1,10 @@ +--> diff --git a/ReCaptchaCustomer/registration.php b/ReCaptchaCustomer/registration.php index 50426e2d..bd7b486d 100644 --- a/ReCaptchaCustomer/registration.php +++ b/ReCaptchaCustomer/registration.php @@ -1,8 +1,9 @@ +--> diff --git a/ReCaptchaCustomer/view/frontend/layout/customer_account_edit.xml b/ReCaptchaCustomer/view/frontend/layout/customer_account_edit.xml index 11a1de9a..ffd21e84 100644 --- a/ReCaptchaCustomer/view/frontend/layout/customer_account_edit.xml +++ b/ReCaptchaCustomer/view/frontend/layout/customer_account_edit.xml @@ -1,8 +1,8 @@ diff --git a/ReCaptchaCustomer/view/frontend/layout/customer_account_forgotpassword.xml b/ReCaptchaCustomer/view/frontend/layout/customer_account_forgotpassword.xml index 77127af3..fd10a737 100644 --- a/ReCaptchaCustomer/view/frontend/layout/customer_account_forgotpassword.xml +++ b/ReCaptchaCustomer/view/frontend/layout/customer_account_forgotpassword.xml @@ -1,10 +1,10 @@ +--> diff --git a/ReCaptchaCustomer/view/frontend/layout/customer_account_login.xml b/ReCaptchaCustomer/view/frontend/layout/customer_account_login.xml index 70c005bf..6fbd5bdd 100644 --- a/ReCaptchaCustomer/view/frontend/layout/customer_account_login.xml +++ b/ReCaptchaCustomer/view/frontend/layout/customer_account_login.xml @@ -1,10 +1,10 @@ +--> diff --git a/ReCaptchaCustomer/view/frontend/layout/default.xml b/ReCaptchaCustomer/view/frontend/layout/default.xml index 5a1e04d0..74abc79a 100644 --- a/ReCaptchaCustomer/view/frontend/layout/default.xml +++ b/ReCaptchaCustomer/view/frontend/layout/default.xml @@ -1,10 +1,10 @@ +--> diff --git a/ReCaptchaCustomer/view/frontend/web/css/source/_module.less b/ReCaptchaCustomer/view/frontend/web/css/source/_module.less index f2958ae5..e96a4f9c 100644 --- a/ReCaptchaCustomer/view/frontend/web/css/source/_module.less +++ b/ReCaptchaCustomer/view/frontend/web/css/source/_module.less @@ -1,7 +1,8 @@ /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + .login-container, .form-login, .form-edit-account { diff --git a/ReCaptchaFrontendUi/Model/CaptchaTypeResolver.php b/ReCaptchaFrontendUi/Model/CaptchaTypeResolver.php index 56ce5019..6e68e443 100644 --- a/ReCaptchaFrontendUi/Model/CaptchaTypeResolver.php +++ b/ReCaptchaFrontendUi/Model/CaptchaTypeResolver.php @@ -1,8 +1,9 @@ +--> diff --git a/ReCaptchaFrontendUi/etc/config.xml b/ReCaptchaFrontendUi/etc/config.xml index 81154a13..23155a04 100644 --- a/ReCaptchaFrontendUi/etc/config.xml +++ b/ReCaptchaFrontendUi/etc/config.xml @@ -1,10 +1,10 @@ +--> diff --git a/ReCaptchaFrontendUi/etc/di.xml b/ReCaptchaFrontendUi/etc/di.xml index a8a4fa8e..64f067ec 100644 --- a/ReCaptchaFrontendUi/etc/di.xml +++ b/ReCaptchaFrontendUi/etc/di.xml @@ -1,10 +1,10 @@ +--> diff --git a/ReCaptchaFrontendUi/etc/frontend/di.xml b/ReCaptchaFrontendUi/etc/frontend/di.xml index 56f11f03..6bde9e80 100644 --- a/ReCaptchaFrontendUi/etc/frontend/di.xml +++ b/ReCaptchaFrontendUi/etc/frontend/di.xml @@ -1,10 +1,10 @@ +--> +--> diff --git a/ReCaptchaFrontendUi/registration.php b/ReCaptchaFrontendUi/registration.php index ba5e8ba4..ade81895 100644 --- a/ReCaptchaFrontendUi/registration.php +++ b/ReCaptchaFrontendUi/registration.php @@ -1,8 +1,9 @@ 0) { parentForm.submit(function (event) { + var submitButton; + if (!this.tokenField.value) { + submitButton = this.$parentForm.find('button:not([type]), [type=submit]'); + if (submitButton.length) { //eslint-disable-line max-depth + submitButton.attr('disabled', true); + } // eslint-disable-next-line no-undef grecaptcha.execute(widgetId); event.preventDefault(event); @@ -170,6 +186,11 @@ define( } else { this.tokenField = null; } + let submitButton = parentForm.find('button:not([type]), [type=submit]'); + + if (submitButton.length) { + submitButton.prop('disabled', false); + } }, /** diff --git a/ReCaptchaFrontendUi/view/frontend/web/js/reCaptchaScriptLoader.js b/ReCaptchaFrontendUi/view/frontend/web/js/reCaptchaScriptLoader.js index 94746c33..43ff3087 100644 --- a/ReCaptchaFrontendUi/view/frontend/web/js/reCaptchaScriptLoader.js +++ b/ReCaptchaFrontendUi/view/frontend/web/js/reCaptchaScriptLoader.js @@ -1,10 +1,10 @@ /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ define([], function () { - 'use strict'; + 'use strict'; // eslint-disable-line var scriptTagAdded = false; diff --git a/ReCaptchaFrontendUi/view/frontend/web/js/registry.js b/ReCaptchaFrontendUi/view/frontend/web/js/registry.js index 2ff47a0c..1da0c183 100644 --- a/ReCaptchaFrontendUi/view/frontend/web/js/registry.js +++ b/ReCaptchaFrontendUi/view/frontend/web/js/registry.js @@ -1,10 +1,10 @@ /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ define(['ko'], function (ko) { - 'use strict'; + 'use strict'; // eslint-disable-line return { ids: ko.observableArray([]), diff --git a/ReCaptchaFrontendUi/view/frontend/web/js/ui-messages-mixin.js b/ReCaptchaFrontendUi/view/frontend/web/js/ui-messages-mixin.js index 86713ef4..e142ee70 100644 --- a/ReCaptchaFrontendUi/view/frontend/web/js/ui-messages-mixin.js +++ b/ReCaptchaFrontendUi/view/frontend/web/js/ui-messages-mixin.js @@ -1,10 +1,10 @@ /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ define(['Magento_ReCaptchaFrontendUi/js/registry'], function (registry) { - 'use strict'; + 'use strict'; // eslint-disable-line return function (originalComponent) { return originalComponent.extend({ diff --git a/ReCaptchaFrontendUi/view/frontend/web/template/reCaptcha.html b/ReCaptchaFrontendUi/view/frontend/web/template/reCaptcha.html index 9f8eac95..351302e0 100644 --- a/ReCaptchaFrontendUi/view/frontend/web/template/reCaptcha.html +++ b/ReCaptchaFrontendUi/view/frontend/web/template/reCaptcha.html @@ -1,7 +1,7 @@ @@ -20,7 +20,7 @@ class="required-captcha checkbox" name="recaptcha-validate-" data-validate="{required:true}" - /> + tabindex="-1"> diff --git a/ReCaptchaMigration/README.md b/ReCaptchaMigration/README.md index eb1189ad..371dcf61 100644 --- a/ReCaptchaMigration/README.md +++ b/ReCaptchaMigration/README.md @@ -1,7 +1,7 @@ -# Magento reCAPTCHA +# Magento_ReCaptchaMigration module -Google reCAPTCHA ensures that a human being, rather than a computer (or “bot”), is interacting with your website. Unlike the standard Magento CAPTCHA, Google reCAPTCHA provides enhanced security with a selection of different display options and methods. Additional website traffic information is available in the dashboard of your Google reCAPTCHA account. +Google reCAPTCHA ensures that a human being, rather than a computer (or "bot"), is interacting with your website. Unlike the standard Magento CAPTCHA, Google reCAPTCHA provides enhanced security with a selection of different display options and methods. Additional website traffic information is available in the dashboard of your Google reCAPTCHA account. This module helps migrate data from the old reCAPTCHA implementation to the new one. -For more information please visit the [Magento document for reCAPTCHA](https://docs.magento.com/user-guide/stores/security-google-recaptcha.html). +For more information, see the [reCAPTCHA documentation](https://experienceleague.adobe.com/en/docs/commerce-admin/systems/security/captcha/security-google-recaptcha). diff --git a/ReCaptchaMigration/Setup/Patch/Data/MigrateConfigToRecaptchaModules.php b/ReCaptchaMigration/Setup/Patch/Data/MigrateConfigToRecaptchaModules.php index 1c131d6a..c64736df 100644 --- a/ReCaptchaMigration/Setup/Patch/Data/MigrateConfigToRecaptchaModules.php +++ b/ReCaptchaMigration/Setup/Patch/Data/MigrateConfigToRecaptchaModules.php @@ -1,8 +1,9 @@ copyEnabledRecaptcha($scope); $this->disableLegacyRecaptcha($scope); } + + return $this; } /** diff --git a/ReCaptchaMigration/composer.json b/ReCaptchaMigration/composer.json index f72df210..f89e9fda 100644 --- a/ReCaptchaMigration/composer.json +++ b/ReCaptchaMigration/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-re-captcha-migration", "description": "Google reCAPTCHA config migration for Magento2", "require": { - "php": "~7.4.0||~8.0.0||~8.1.0", + "php": "~8.2.0||~8.3.0||~8.4.0", "magento/framework": "*", "magento/module-config": "*" }, diff --git a/ReCaptchaMigration/etc/module.xml b/ReCaptchaMigration/etc/module.xml index be65c266..fc23b4d5 100644 --- a/ReCaptchaMigration/etc/module.xml +++ b/ReCaptchaMigration/etc/module.xml @@ -1,10 +1,10 @@ +--> diff --git a/ReCaptchaMigration/registration.php b/ReCaptchaMigration/registration.php index d47e5be1..ae586447 100644 --- a/ReCaptchaMigration/registration.php +++ b/ReCaptchaMigration/registration.php @@ -1,8 +1,9 @@ +--> diff --git a/ReCaptchaNewsletter/etc/config.xml b/ReCaptchaNewsletter/etc/config.xml index 56acb6bd..c2d01b98 100644 --- a/ReCaptchaNewsletter/etc/config.xml +++ b/ReCaptchaNewsletter/etc/config.xml @@ -1,10 +1,10 @@ +--> diff --git a/ReCaptchaNewsletter/etc/di.xml b/ReCaptchaNewsletter/etc/di.xml index cd496bad..fa8247b8 100644 --- a/ReCaptchaNewsletter/etc/di.xml +++ b/ReCaptchaNewsletter/etc/di.xml @@ -1,10 +1,10 @@ +--> diff --git a/ReCaptchaNewsletter/etc/frontend/di.xml b/ReCaptchaNewsletter/etc/frontend/di.xml new file mode 100644 index 00000000..1a46121e --- /dev/null +++ b/ReCaptchaNewsletter/etc/frontend/di.xml @@ -0,0 +1,24 @@ + + + + + + + + Magento\ReCaptchaNewsletter\Model\ButtonLock\NewsletterFormSubmit + + + + + + newsletter_form_submit + newsletter + + + diff --git a/ReCaptchaNewsletter/etc/frontend/events.xml b/ReCaptchaNewsletter/etc/frontend/events.xml index 9cc0e365..da816118 100644 --- a/ReCaptchaNewsletter/etc/frontend/events.xml +++ b/ReCaptchaNewsletter/etc/frontend/events.xml @@ -1,10 +1,10 @@ +--> diff --git a/ReCaptchaNewsletter/etc/module.xml b/ReCaptchaNewsletter/etc/module.xml index f684e0f8..088bd1f6 100644 --- a/ReCaptchaNewsletter/etc/module.xml +++ b/ReCaptchaNewsletter/etc/module.xml @@ -1,10 +1,10 @@ +--> diff --git a/ReCaptchaNewsletter/registration.php b/ReCaptchaNewsletter/registration.php index e5c20536..6011ad42 100644 --- a/ReCaptchaNewsletter/registration.php +++ b/ReCaptchaNewsletter/registration.php @@ -1,8 +1,9 @@ +--> diff --git a/ReCaptchaNewsletter/view/frontend/templates/recaptcha_newsletter.phtml b/ReCaptchaNewsletter/view/frontend/templates/recaptcha_newsletter.phtml index d53a4062..9cc06440 100644 --- a/ReCaptchaNewsletter/view/frontend/templates/recaptcha_newsletter.phtml +++ b/ReCaptchaNewsletter/view/frontend/templates/recaptcha_newsletter.phtml @@ -1,8 +1,9 @@ true, + Config::METHOD_WPP_PE_EXPRESS => true, + Config::METHOD_WPP_PE_BML => true, + ]; if ($this->isCaptchaEnabled->isCaptchaEnabledFor($key)) { $jsLayout['components']['checkout']['children']['steps']['children']['billing-step']['children'] ['payment']['children']['payments-list']['children']['paypal-captcha']['children'] ['recaptcha']['settings'] = $this->captchaUiConfigResolver->get($key); + if ($this->isCaptchaEnabled->isCaptchaEnabledFor('place_order')) { + $skipCheckoutRecaptchaForPayments[Config::METHOD_PAYFLOWPRO] = true; + } } else { if (isset($jsLayout['components']['checkout']['children']['steps']['children']['billing-step']['children'] ['payment']['children']['payments-list']['children']['paypal-captcha']['children']['recaptcha'])) { @@ -60,6 +70,11 @@ public function process($jsLayout) ['payment']['children']['payments-list']['children']['paypal-captcha']['children']['recaptcha']); } } + if ($this->isCaptchaEnabled->isCaptchaEnabledFor('place_order')) { + $jsLayout['components']['checkout']['children']['steps']['children']['billing-step']['children'] + ['payment']['children']['payments-list']['children']['before-place-order']['children'] + ['place-order-recaptcha']['skipPayments'] += $skipCheckoutRecaptchaForPayments; + } return $jsLayout; } diff --git a/ReCaptchaPaypal/Model/CheckoutConfigProvider.php b/ReCaptchaPaypal/Model/CheckoutConfigProvider.php index ce2a338c..56327218 100644 --- a/ReCaptchaPaypal/Model/CheckoutConfigProvider.php +++ b/ReCaptchaPaypal/Model/CheckoutConfigProvider.php @@ -1,8 +1,9 @@ timezone = $timezone; + $this->transparentSession = $transparentSession; + $this->checkoutSession = $checkoutSession; + } + + /** + * Saves quote_id and datetime the reCaptcha was verified in session + * + * @return bool + */ + public function save(): bool + { + $result = false; + if ($this->checkoutSession->getQuote()) { + $this->transparentSession->setData( + self::PAYPAL_PAYFLOWPRO_RECAPTCHA, + [ + 'quote_id' => $this->checkoutSession->getQuote()->getId(), + 'verified_at' => $this->timezone->date()->getTimestamp(), + ] + ); + $result = true; + } + return $result; + } + + /** + * Checks whether the time since reCaptcha was verified is not more than the timeout + * + * @param int $quoteId + * @return bool + */ + public function isValid(int $quoteId): bool + { + $result = false; + $data = $this->transparentSession->getData(self::PAYPAL_PAYFLOWPRO_RECAPTCHA) ?? []; + if (isset($data['quote_id']) + && (int) $data['quote_id'] === $quoteId + && ($data['verified_at'] + self::TIMEOUT) >= $this->timezone->date()->getTimestamp() + ) { + $this->transparentSession->unsetData(self::PAYPAL_PAYFLOWPRO_RECAPTCHA); + $result = true; + } + return $result; + } +} diff --git a/ReCaptchaPaypal/Model/WebapiConfigProvider.php b/ReCaptchaPaypal/Model/WebapiConfigProvider.php index 752579b5..868069bc 100644 --- a/ReCaptchaPaypal/Model/WebapiConfigProvider.php +++ b/ReCaptchaPaypal/Model/WebapiConfigProvider.php @@ -1,8 +1,9 @@ captchaResponseResolver = $captchaResponseResolver; $this->validationConfigResolver = $validationConfigResolver; @@ -109,6 +119,8 @@ public function __construct( ?? ObjectManager::getInstance()->get(ErrorMessageConfigInterface::class); $this->validationErrorMessagesProvider = $validationErrorMessagesProvider ?? ObjectManager::getInstance()->get(ValidationErrorMessagesProvider::class); + $this->reCaptchaSession = $reCaptchaSession + ?? ObjectManager::getInstance()->get(ReCaptchaSession::class); } /** @@ -148,6 +160,9 @@ public function execute(Observer $observer): void $validationResult->getErrors(), $key ); + } elseif ($this->isCaptchaEnabled->isCaptchaEnabledFor('place_order')) { + // Extend reCaptcha verification to place order + $this->reCaptchaSession->save(); } } } diff --git a/ReCaptchaPaypal/Plugin/ReplayPayflowReCaptchaForPlaceOrder.php b/ReCaptchaPaypal/Plugin/ReplayPayflowReCaptchaForPlaceOrder.php new file mode 100644 index 00000000..20809d42 --- /dev/null +++ b/ReCaptchaPaypal/Plugin/ReplayPayflowReCaptchaForPlaceOrder.php @@ -0,0 +1,97 @@ +isCaptchaEnabled = $isCaptchaEnabled; + $this->request = $request; + $this->reCaptchaSession = $reCaptchaSession; + $this->quoteIdMaskFactory = $quoteIdMaskFactory; + } + + /** + * Skip reCaptcha validation for "place order" button if captcha is enabled for payflowpro + * + * @param WebapiConfigProvider $subject + * @param ValidationConfigInterface $result + * @param EndpointInterface $endpoint + * @return ValidationConfigInterface + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function afterGetConfigFor( + WebapiConfigProvider $subject, + ?ValidationConfigInterface $result, + EndpointInterface $endpoint + ): ?ValidationConfigInterface { + + if ($result && $this->isCaptchaEnabled->isCaptchaEnabledFor(self::PAYPAL_PAYFLOWPRO_CAPTCHA_ID)) { + $bodyParams = $this->request->getBodyParams(); + $paymentMethod = $bodyParams['paymentMethod'] ?? $bodyParams['payment_method'] ?? []; + $cartId = $bodyParams['cartId'] ?? $bodyParams['cart_id'] ?? null; + if (isset($paymentMethod['method']) + && $paymentMethod['method'] === Config::METHOD_PAYFLOWPRO + && $cartId + ) { + // check if it is guest cart, then resolve cart id by mask ID + if (!is_numeric($cartId)) { + $cartId = $this->quoteIdMaskFactory->create()->load($cartId, 'masked_id')->getQuoteId(); + } + if ($this->reCaptchaSession->isValid((int) $cartId)) { + return null; + } + } + } + + return $result; + } +} diff --git a/ReCaptchaPaypal/README.md b/ReCaptchaPaypal/README.md index 55504ff0..61065443 100644 --- a/ReCaptchaPaypal/README.md +++ b/ReCaptchaPaypal/README.md @@ -1,7 +1,7 @@ -# Magento reCAPTCHA +# Magento_ReCaptchaPaypal module -Google reCAPTCHA ensures that a human being, rather than a computer (or “bot”), is interacting with your website. Unlike the standard Magento CAPTCHA, Google reCAPTCHA provides enhanced security with a selection of different display options and methods. Additional website traffic information is available in the dashboard of your Google reCAPTCHA account. +Google reCAPTCHA ensures that a human being, rather than a computer (or "bot"), is interacting with your website. Unlike the standard Magento CAPTCHA, Google reCAPTCHA provides enhanced security with a selection of different display options and methods. Additional website traffic information is available in the dashboard of your Google reCAPTCHA account. This module provides the reCAPTCHA implementations related to PayPal payments. -For more information please visit the [Magento document for reCAPTCHA](https://docs.magento.com/user-guide/stores/security-google-recaptcha.html). +For more information, see the [reCAPTCHA documentation](https://experienceleague.adobe.com/en/docs/commerce-admin/systems/security/captcha/security-google-recaptcha). diff --git a/ReCaptchaPaypal/Test/Api/PayflowCaptchaGlaphQLTest.php b/ReCaptchaPaypal/Test/Api/PayflowCaptchaGlaphQLTest.php index 58994f3c..de7c4cc3 100644 --- a/ReCaptchaPaypal/Test/Api/PayflowCaptchaGlaphQLTest.php +++ b/ReCaptchaPaypal/Test/Api/PayflowCaptchaGlaphQLTest.php @@ -1,8 +1,9 @@ graphQlMutation($query); } - } diff --git a/ReCaptchaPaypal/Test/Unit/Block/LayoutProcessor/Checkout/OnepageTest.php b/ReCaptchaPaypal/Test/Unit/Block/LayoutProcessor/Checkout/OnepageTest.php new file mode 100644 index 00000000..30de658a --- /dev/null +++ b/ReCaptchaPaypal/Test/Unit/Block/LayoutProcessor/Checkout/OnepageTest.php @@ -0,0 +1,213 @@ + [ + 'checkout' => [ + 'children' => [ + 'steps' => [ + 'children' => [ + 'billing-step' => [ + 'children' => [ + 'payment' => [ + 'children' => [ + 'payments-list' => [ + 'children' => [ + 'before-place-order' => [ + 'children' => [ + 'place-order-recaptcha' => [ + 'skipPayments' => [] + ] + ] + ], + 'paypal-captcha' => [ + 'children' => [ + 'recaptcha' => [] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ]; + + /** + * @inheritdoc + */ + protected function setUp(): void + { + parent::setUp(); + $this->uiConfigResolver = $this->getMockForAbstractClass(UiConfigResolverInterface::class); + $this->isCaptchEnabled = $this->getMockForAbstractClass(IsCaptchaEnabledInterface::class); + $this->model = new Onepage( + $this->uiConfigResolver, + $this->isCaptchEnabled + ); + } + + /** + * @dataProvider processDataProvider + */ + public function testProcess(array $mocks, array $expected): void + { + $this->uiConfigResolver->method('get') + ->willReturnMap($mocks['uiConfigResolver']); + $this->isCaptchEnabled->method('isCaptchaEnabledFor') + ->willReturnMap($mocks['isCaptchaEnabled']); + $prefix = 'components/checkout/children/'; + $config = new DataObject($this->model->process($this->jsLayout)); + $actual = []; + foreach (array_keys($expected) as $path) { + $actual[$path] = $config->getDataByPath($prefix . $path); + } + $this->assertSame($expected, $actual); + } + + /** + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ + public static function processDataProvider(): array + { + return [ + [ + [ + 'isCaptchaEnabled' => [ + ['paypal_payflowpro', false], + ['place_order', false], + ], + 'uiConfigResolver' => [ + ['paypal_payflowpro', ['type' => 'invisible']], + ['place_order', ['type' => 'robot']], + ], + ], + [ + 'steps/children/billing-step/children/payment/children/payments-list/children/paypal-captcha/' . + 'children' => [], + 'steps/children/billing-step/children/payment/children/payments-list/children/before-place-order/' . + 'children' => [ + 'place-order-recaptcha' => [ + 'skipPayments' => [] + ] + ], + ] + ], + [ + [ + 'isCaptchaEnabled' => [ + ['paypal_payflowpro', false], + ['place_order', true], + ], + 'uiConfigResolver' => [ + ['paypal_payflowpro', ['type' => 'invisible']], + ['place_order', ['type' => 'robot']], + ], + ], + [ + 'steps/children/billing-step/children/payment/children/payments-list/children/paypal-captcha/' . + 'children' => [], + 'steps/children/billing-step/children/payment/children/payments-list/children/before-place-order/' . + 'children' => [ + 'place-order-recaptcha' => [ + 'skipPayments' => [ + Config::METHOD_EXPRESS => true, + Config::METHOD_WPP_PE_EXPRESS => true, + Config::METHOD_WPP_PE_BML => true, + ] + ] + ], + ] + ], + [ + [ + 'isCaptchaEnabled' => [ + ['paypal_payflowpro', true], + ['place_order', false], + ], + 'uiConfigResolver' => [ + ['paypal_payflowpro', ['type' => 'invisible']], + ['place_order', ['type' => 'robot']], + ], + ], + [ + 'steps/children/billing-step/children/payment/children/payments-list/children/paypal-captcha/' . + 'children' => ['recaptcha' => ['settings' => ['type' => 'invisible']]], + 'steps/children/billing-step/children/payment/children/payments-list/children/before-place-order/' . + 'children' => [ + 'place-order-recaptcha' => [ + 'skipPayments' => [] + ] + ], + ] + ], + [ + [ + 'isCaptchaEnabled' => [ + ['paypal_payflowpro', true], + ['place_order', true], + ], + 'uiConfigResolver' => [ + ['paypal_payflowpro', ['type' => 'invisible']], + ['place_order', ['type' => 'robot']], + ], + ], + [ + 'steps/children/billing-step/children/payment/children/payments-list/children/paypal-captcha/' . + 'children' => ['recaptcha' => ['settings' => ['type' => 'invisible']]], + 'steps/children/billing-step/children/payment/children/payments-list/children/before-place-order/' . + 'children' => [ + 'place-order-recaptcha' => [ + 'skipPayments' => [ + Config::METHOD_EXPRESS => true, + Config::METHOD_WPP_PE_EXPRESS => true, + Config::METHOD_WPP_PE_BML => true, + Config::METHOD_PAYFLOWPRO => true + ] + ] + ], + ] + ] + ]; + } +} diff --git a/ReCaptchaPaypal/Test/Unit/Model/ReCaptchaSessionTest.php b/ReCaptchaPaypal/Test/Unit/Model/ReCaptchaSessionTest.php new file mode 100644 index 00000000..990f7883 --- /dev/null +++ b/ReCaptchaPaypal/Test/Unit/Model/ReCaptchaSessionTest.php @@ -0,0 +1,133 @@ +timezone = $this->getMockForAbstractClass(TimezoneInterface::class); + $this->transparentSession = $this->getMockBuilder(SessionManager::class) + ->disableOriginalConstructor() + ->onlyMethods(['getData']) + ->addMethods(['setData', 'unsetData']) + ->getMock(); + $this->checkoutSession = $this->getMockBuilder(SessionManager::class) + ->disableOriginalConstructor() + ->addMethods(['getQuote']) + ->getMock(); + $this->model = new ReCaptchaSession( + $this->timezone, + $this->transparentSession, + $this->checkoutSession + ); + } + + public function testSaveIfThereIsNoActiveQuote(): void + { + $this->checkoutSession->expects($this->once()) + ->method('getQuote') + ->willReturn(null); + $this->assertFalse($this->model->save()); + } + + public function testSaveIfThereIsActiveQuote(): void + { + $quote = $this->getMockForAbstractClass(CartInterface::class); + $quote->expects($this->once()) + ->method('getId') + ->willReturn(1); + $this->checkoutSession->expects($this->exactly(2)) + ->method('getQuote') + ->willReturn($quote); + $this->timezone->expects($this->once()) + ->method('date') + ->willReturn(new \Datetime('@1670607221')); + $this->transparentSession->expects($this->once()) + ->method('setData') + ->with('paypal_payflowpro_recaptcha', ['quote_id' => 1, 'verified_at' => 1670607221]); + $this->assertTrue($this->model->save()); + } + + public function testIsInvalidIfQuoteIdIsMissing(): void + { + $this->transparentSession->expects($this->once()) + ->method('getData') + ->with('paypal_payflowpro_recaptcha') + ->willReturn(null); + $this->assertFalse($this->model->isValid(1)); + } + + public function testIsInvalidIfQuoteIdDoesNotMatch(): void + { + $this->transparentSession->expects($this->once()) + ->method('getData') + ->with('paypal_payflowpro_recaptcha') + ->willReturn(['quote_id' => 2, 'verified_at' => 1670607221]); + $this->assertFalse($this->model->isValid(1)); + } + + public function testIsInvalidIfExpired(): void + { + $this->timezone->expects($this->once()) + ->method('date') + ->willReturn(new \Datetime('@1670607342')); + $this->transparentSession->expects($this->once()) + ->method('getData') + ->with('paypal_payflowpro_recaptcha') + ->willReturn(['quote_id' => 1, 'verified_at' => 1670607221]); + $this->assertFalse($this->model->isValid(1)); + } + + public function testIsInvalidIfNotExpired(): void + { + $this->timezone->expects($this->once()) + ->method('date') + ->willReturn(new \Datetime('@1670607340')); + $this->transparentSession->expects($this->once()) + ->method('getData') + ->with('paypal_payflowpro_recaptcha') + ->willReturn(['quote_id' => 1, 'verified_at' => 1670607221]); + $this->transparentSession->expects($this->once()) + ->method('unsetData') + ->with('paypal_payflowpro_recaptcha'); + $this->assertTrue($this->model->isValid(1)); + } +} diff --git a/ReCaptchaPaypal/Test/Unit/Observer/PayPalObserverTest.php b/ReCaptchaPaypal/Test/Unit/Observer/PayPalObserverTest.php new file mode 100644 index 00000000..aad0e7cb --- /dev/null +++ b/ReCaptchaPaypal/Test/Unit/Observer/PayPalObserverTest.php @@ -0,0 +1,225 @@ +getMockForAbstractClass(CaptchaResponseResolverInterface::class); + $validationConfigResolver = $this->getMockForAbstractClass(ValidationConfigResolverInterface::class); + $this->captchaValidator = $this->getMockForAbstractClass(ValidatorInterface::class); + $actionFlag = $this->createMock(ActionFlag::class); + $serializer = $this->getMockForAbstractClass(SerializerInterface::class); + $this->isCaptchaEnabled = $this->getMockForAbstractClass(IsCaptchaEnabledInterface::class); + $logger = $this->getMockForAbstractClass(LoggerInterface::class); + $errorMessageConfig = $this->getMockForAbstractClass(ErrorMessageConfigInterface::class); + $validationErrorMessagesProvider = $this->createMock(ValidationErrorMessagesProvider::class); + $this->reCaptchaSession = $this->createMock(ReCaptchaSession::class); + $this->model = new PayPalObserver( + $captchaResponseResolver, + $validationConfigResolver, + $this->captchaValidator, + $actionFlag, + $serializer, + $this->isCaptchaEnabled, + $logger, + $errorMessageConfig, + $validationErrorMessagesProvider, + $this->reCaptchaSession + ); + $controller = $this->getMockBuilder(AbstractAction::class) + ->disableOriginalConstructor() + ->onlyMethods(['getRequest', 'getResponse']) + ->getMockForAbstractClass(); + $request = $this->getMockForAbstractClass(RequestInterface::class); + $response = $this->getMockBuilder(ResponseInterface::class) + ->disableOriginalConstructor() + ->addMethods(['representJson']) + ->getMockForAbstractClass(); + $controller->method('getRequest')->willReturn($request); + $controller->method('getResponse')->willReturn($response); + $this->observer = new Observer(['controller_action' => $controller]); + $this->validationResult = $this->createMock(ValidationResult::class); + } + + /** + * @param array $mocks + * @dataProvider executeDataProvider + */ + public function testExecute(array $mocks): void + { + $this->configureMock($mocks); + $this->model->execute($this->observer); + } + + public static function executeDataProvider(): array + { + return [ + [ + [ + 'isCaptchaEnabled' => [ + [ + 'method' => 'isCaptchaEnabledFor', + 'willReturnMap' => [ + ['paypal_payflowpro', false], + ['place_order', false], + ] + ] + ], + 'reCaptchaSession' => [ + [ + 'method' => 'save', + 'expects' => self::never(), + ] + ] + ] + ], + [ + [ + 'isCaptchaEnabled' => [ + [ + 'method' => 'isCaptchaEnabledFor', + 'willReturnMap' => [ + ['paypal_payflowpro', true], + ['place_order', false], + ] + ] + ], + 'reCaptchaSession' => [ + [ + 'method' => 'save', + 'expects' => self::never(), + ] + ], + 'captchaValidator' => [ + [ + 'method' => 'isValid', + 'expects' => self::once(), + 'willReturnProperty' => 'validationResult' + ] + ], + 'validationResult' => [ + [ + 'method' => 'isValid', + 'expects' => self::once(), + 'willReturn' => true, + ] + ] + ] + ], + [ + [ + 'isCaptchaEnabled' => [ + [ + 'method' => 'isCaptchaEnabledFor', + 'willReturnMap' => [ + ['paypal_payflowpro', true], + ['place_order', true], + ] + ] + ], + 'reCaptchaSession' => [ + [ + 'method' => 'save', + 'expects' => self::once(), + ] + ], + 'captchaValidator' => [ + [ + 'method' => 'isValid', + 'expects' => self::once(), + 'willReturnProperty' => 'validationResult' + ] + ], + 'validationResult' => [ + [ + 'method' => 'isValid', + 'expects' => self::once(), + 'willReturn' => true, + ] + ] + ] + ] + ]; + } + + private function configureMock(array $mocks): void + { + foreach ($mocks as $prop => $propMocks) { + foreach ($propMocks as $mock) { + $builder = $this->$prop->expects($mock['expects'] ?? $this->any()); + unset($mock['expects']); + foreach ($mock as $method => $args) { + if ($method === 'willReturnProperty') { + $method = 'willReturn'; + $args = $this->$args; + } + $builder->$method(...[$args]); + } + } + } + } +} diff --git a/ReCaptchaPaypal/Test/Unit/Plugin/ReplayPayflowReCaptchaForPlaceOrderTest.php b/ReCaptchaPaypal/Test/Unit/Plugin/ReplayPayflowReCaptchaForPlaceOrderTest.php new file mode 100644 index 00000000..3203cd9e --- /dev/null +++ b/ReCaptchaPaypal/Test/Unit/Plugin/ReplayPayflowReCaptchaForPlaceOrderTest.php @@ -0,0 +1,278 @@ +isCaptchaEnabled = $this->getMockForAbstractClass(IsCaptchaEnabledInterface::class); + $this->request = $this->createMock(Request::class); + $this->reCaptchaSession = $this->createMock(ReCaptchaSession::class); + $this->quoteIdMaskFactory = $this->createMock(QuoteIdMaskFactory::class); + $this->quoteIdMask = $this->getMockBuilder(QuoteIdMask::class) + ->onlyMethods(['load']) + ->addMethods(['getQuoteId']) + ->disableOriginalConstructor() + ->getMock(); + $this->model = new ReplayPayflowReCaptchaForPlaceOrder( + $this->isCaptchaEnabled, + $this->request, + $this->reCaptchaSession, + $this->quoteIdMaskFactory + ); + } + + /** + * @param array $mocks + * @param bool $isResultNull + * @param bool $isReturnNull + * @dataProvider afterGetConfigForDataProvider + */ + public function testAfterGetConfigFor(array $mocks, bool $isResultNull, bool $isReturnNull): void + { + $this->configureMock($mocks); + $subject = $this->createMock(WebapiConfigProvider::class); + $result = $this->getMockForAbstractClass(ValidationConfigInterface::class); + $endpoint = $this->getMockForAbstractClass(EndpointInterface::class); + $this->assertSame( + $isReturnNull ? null : $result, + $this->model->afterGetConfigFor($subject, $isResultNull ? null : $result, $endpoint) + ); + } + + /** + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ + public static function afterGetConfigForDataProvider(): array + { + return [ + [ + [ + 'reCaptchaSession' => [ + ['method' => 'isValid', 'expects' => self::never()] + ] + ], + true, + true + ], + [ + [ + 'isCaptchaEnabled' => [ + ['method' => 'isCaptchaEnabledFor', 'with' => 'paypal_payflowpro', 'willReturn' => false] + ], + 'reCaptchaSession' => [ + ['method' => 'isValid', 'expects' => self::never(),] + ] + ], + false, + false + ], + [ + [ + 'isCaptchaEnabled' => [ + ['method' => 'isCaptchaEnabledFor', 'with' => 'paypal_payflowpro', 'willReturn' => true] + ], + 'request' => [ + ['method' => 'getBodyParams', 'expects' => self::once(), 'willReturn' => []] + ], + 'reCaptchaSession' => [ + ['method' => 'isValid', 'expects' => self::never(),] + ] + ], + false, + false + ], + [ + [ + 'isCaptchaEnabled' => [ + ['method' => 'isCaptchaEnabledFor', 'with' => 'paypal_payflowpro', 'willReturn' => true] + ], + 'request' => [ + [ + 'method' => 'getBodyParams', + 'expects' => self::once(), + 'willReturn' => ['cartId' => 1, 'paymentMethod' => ['method' => 'checkmo']] + ] + ], + 'reCaptchaSession' => [ + ['method' => 'isValid', 'expects' => self::never(), 'willReturn' => false] + ] + ], + false, + false + ], + [ + [ + 'isCaptchaEnabled' => [ + ['method' => 'isCaptchaEnabledFor', 'with' => 'paypal_payflowpro', 'willReturn' => true] + ], + 'request' => [ + [ + 'method' => 'getBodyParams', + 'expects' => self::once(), + 'willReturn' => ['cartId' => 1, 'paymentMethod' => ['method' => Config::METHOD_PAYFLOWPRO]] + ] + ], + 'reCaptchaSession' => [ + ['method' => 'isValid', 'expects' => self::once(), 'with' => 1, 'willReturn' => false] + ] + ], + false, + false + ], + [ + [ + 'isCaptchaEnabled' => [ + ['method' => 'isCaptchaEnabledFor', 'with' => 'paypal_payflowpro', 'willReturn' => true] + ], + 'request' => [ + [ + 'method' => 'getBodyParams', + 'expects' => self::once(), + 'willReturn' => ['cartId' => 1, 'paymentMethod' => ['method' => Config::METHOD_PAYFLOWPRO]] + ] + ], + 'reCaptchaSession' => [ + ['method' => 'isValid', 'expects' => self::once(), 'with' => 1, 'willReturn' => true] + ] + ], + false, + true + ], + [ + [ + 'isCaptchaEnabled' => [ + ['method' => 'isCaptchaEnabledFor', 'with' => 'paypal_payflowpro', 'willReturn' => true] + ], + 'request' => [ + [ + 'method' => 'getBodyParams', + 'expects' => self::once(), + 'willReturn' => [ + 'cart_id' => 1, + 'payment_method' => ['method' => Config::METHOD_PAYFLOWPRO] + ] + ] + ], + 'reCaptchaSession' => [ + ['method' => 'isValid', 'expects' => self::once(), 'with' => 1, 'willReturn' => true] + ] + ], + false, + true + ], + [ + [ + 'isCaptchaEnabled' => [ + ['method' => 'isCaptchaEnabledFor', 'with' => 'paypal_payflowpro', 'willReturn' => true] + ], + 'request' => [ + [ + 'method' => 'getBodyParams', + 'expects' => self::once(), + 'willReturn' => [ + 'cartId' => '17uc43rge98nc92', + 'paymentMethod' => ['method' => Config::METHOD_PAYFLOWPRO] + ] + ] + ], + 'quoteIdMaskFactory' => [ + [ + 'method' => 'create', + 'expects' => self::once(), + 'willReturnProperty' => 'quoteIdMask' + ] + ], + 'quoteIdMask' => [ + [ + 'method' => 'load', + 'expects' => self::once(), + 'willReturnSelf' => null + ], + [ + 'method' => 'getQuoteId', + 'expects' => self::once(), + 'willReturn' => 2 + ] + ], + 'reCaptchaSession' => [ + ['method' => 'isValid', 'expects' => self::once(), 'with' => 2, 'willReturn' => true] + ] + ], + false, + true + ], + ]; + } + + private function configureMock(array $mocks): void + { + foreach ($mocks as $prop => $propMocks) { + foreach ($propMocks as $mock) { + $builder = $this->$prop->expects($mock['expects'] ?? $this->any()); + unset($mock['expects']); + foreach ($mock as $method => $args) { + if ($method === 'willReturnProperty') { + $method = 'willReturn'; + $args = $this->$args; + } + $builder->$method(...[$args]); + } + } + } + } +} diff --git a/ReCaptchaPaypal/composer.json b/ReCaptchaPaypal/composer.json index 9ac1f26a..8da56051 100644 --- a/ReCaptchaPaypal/composer.json +++ b/ReCaptchaPaypal/composer.json @@ -2,12 +2,15 @@ "name": "magento/module-re-captcha-paypal", "description": "Google reCaptcha integration for Magento2 PayPal PayflowPro payment form", "require": { - "php": "~7.4.0||~8.0.0||~8.1.0", + "php": "~8.2.0||~8.3.0||~8.4.0", "magento/framework": "*", "magento/module-re-captcha-ui": "*", "magento/module-re-captcha-validation-api": "*", "magento/module-checkout": "*", - "magento/module-re-captcha-webapi-api": "*" + "magento/module-re-captcha-webapi-api": "*", + "magento/module-quote": "*", + "magento/module-paypal": "*", + "magento/module-re-captcha-checkout": "*" }, "type": "magento2-module", "license": "OSL-3.0", diff --git a/ReCaptchaPaypal/etc/adminhtml/system.xml b/ReCaptchaPaypal/etc/adminhtml/system.xml index 358278cc..629ca6a5 100644 --- a/ReCaptchaPaypal/etc/adminhtml/system.xml +++ b/ReCaptchaPaypal/etc/adminhtml/system.xml @@ -1,8 +1,8 @@ +--> + + @@ -14,4 +15,10 @@ + + + Magento\Framework\Session\Generic\Proxy + Magento\Checkout\Model\Session\Proxy + + diff --git a/ReCaptchaPaypal/etc/frontend/di.xml b/ReCaptchaPaypal/etc/frontend/di.xml index d63b8637..3dd88e60 100644 --- a/ReCaptchaPaypal/etc/frontend/di.xml +++ b/ReCaptchaPaypal/etc/frontend/di.xml @@ -1,8 +1,8 @@ + + + + + + diff --git a/ReCaptchaPaypal/registration.php b/ReCaptchaPaypal/registration.php index 2e3ebb0e..6d1c2237 100644 --- a/ReCaptchaPaypal/registration.php +++ b/ReCaptchaPaypal/registration.php @@ -1,8 +1,9 @@ getServiceMethod() === 'resolve' + && $endpoint->getServiceClass() === ResendConfirmationEmail::class + ) { + if ($this->isEnabled->isCaptchaEnabledFor(self::CAPTCHA_ID)) { + return $this->configResolver->get(self::CAPTCHA_ID); + } + } + + return null; + } +} diff --git a/ReCaptchaResendConfirmationEmail/Observer/ResendConfirmationEmailObserver.php b/ReCaptchaResendConfirmationEmail/Observer/ResendConfirmationEmailObserver.php new file mode 100644 index 00000000..7bda396c --- /dev/null +++ b/ReCaptchaResendConfirmationEmail/Observer/ResendConfirmationEmailObserver.php @@ -0,0 +1,54 @@ +isCaptchaEnabled->isCaptchaEnabledFor(self::KEY)) { + /** @var Action $controller */ + $controller = $observer->getControllerAction(); + $request = $controller->getRequest(); + $response = $controller->getResponse(); + $redirectOnFailureUrl = $this->redirect->getRefererUrl(); + + $this->requestHandler->execute(self::KEY, $request, $response, $redirectOnFailureUrl); + } + } +} diff --git a/ReCaptchaResendConfirmationEmail/README.md b/ReCaptchaResendConfirmationEmail/README.md new file mode 100644 index 00000000..0b06d107 --- /dev/null +++ b/ReCaptchaResendConfirmationEmail/README.md @@ -0,0 +1,7 @@ +# Magento_ReCaptchaResendConfirmationEmail module + +Google reCAPTCHA ensures that a human being, rather than a computer (or "bot"), is interacting with your website. Unlike the standard Magento CAPTCHA, Google reCAPTCHA provides enhanced security with a selection of different display options and methods. Additional website traffic information is available in the dashboard of your Google reCAPTCHA account. + +This module provides the reCAPTCHA implementations related to resend confirmation email action. + +For more information, see the [document for reCAPTCHA](https://experienceleague.adobe.com/en/docs/commerce-admin/systems/security/captcha/security-google-recaptcha). diff --git a/ReCaptchaResendConfirmationEmail/Test/Api/GraphQl/ResendConfirmationEmail/ResendConfirmationEmailTest.php b/ReCaptchaResendConfirmationEmail/Test/Api/GraphQl/ResendConfirmationEmail/ResendConfirmationEmailTest.php new file mode 100644 index 00000000..586e206e --- /dev/null +++ b/ReCaptchaResendConfirmationEmail/Test/Api/GraphQl/ResendConfirmationEmail/ResendConfirmationEmailTest.php @@ -0,0 +1,47 @@ +getQuery("test@example.com"); + + $this->expectExceptionMessage('ReCaptcha validation failed, please try again'); + $this->graphQlMutation($query); + } + + /** + * @param string $email + * @return string + */ + private function getQuery(string $email): string + { + return << + + + +
+ + + + Magento\ReCaptchaAdminUi\Model\OptionSource\Type + + +
+
+
diff --git a/ReCaptchaResendConfirmationEmail/etc/config.xml b/ReCaptchaResendConfirmationEmail/etc/config.xml new file mode 100644 index 00000000..8033a1cc --- /dev/null +++ b/ReCaptchaResendConfirmationEmail/etc/config.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + diff --git a/ReCaptchaResendConfirmationEmail/etc/di.xml b/ReCaptchaResendConfirmationEmail/etc/di.xml new file mode 100644 index 00000000..51f3ec2d --- /dev/null +++ b/ReCaptchaResendConfirmationEmail/etc/di.xml @@ -0,0 +1,17 @@ + + + + + + + Magento\ReCaptchaResendConfirmationEmail\Model\WebapiConfigProvider + + + + diff --git a/ReCaptchaResendConfirmationEmail/etc/module.xml b/ReCaptchaResendConfirmationEmail/etc/module.xml new file mode 100644 index 00000000..5535b69b --- /dev/null +++ b/ReCaptchaResendConfirmationEmail/etc/module.xml @@ -0,0 +1,11 @@ + + + + + diff --git a/ReCaptchaResendConfirmationEmail/registration.php b/ReCaptchaResendConfirmationEmail/registration.php new file mode 100644 index 00000000..5c9e78d4 --- /dev/null +++ b/ReCaptchaResendConfirmationEmail/registration.php @@ -0,0 +1,13 @@ + ['John Doe', true], diff --git a/ReCaptchaReview/Test/Integration/ReviewFormTest.php b/ReCaptchaReview/Test/Integration/ReviewFormTest.php index 1047fc50..c97fbe61 100644 --- a/ReCaptchaReview/Test/Integration/ReviewFormTest.php +++ b/ReCaptchaReview/Test/Integration/ReviewFormTest.php @@ -1,8 +1,9 @@ +--> diff --git a/ReCaptchaReview/etc/config.xml b/ReCaptchaReview/etc/config.xml index b8fc1dac..d437d560 100644 --- a/ReCaptchaReview/etc/config.xml +++ b/ReCaptchaReview/etc/config.xml @@ -1,10 +1,10 @@ +--> diff --git a/ReCaptchaReview/etc/di.xml b/ReCaptchaReview/etc/di.xml index bbc6d320..f237bd9d 100644 --- a/ReCaptchaReview/etc/di.xml +++ b/ReCaptchaReview/etc/di.xml @@ -1,10 +1,10 @@ +--> diff --git a/ReCaptchaReview/etc/frontend/di.xml b/ReCaptchaReview/etc/frontend/di.xml new file mode 100644 index 00000000..c45d4584 --- /dev/null +++ b/ReCaptchaReview/etc/frontend/di.xml @@ -0,0 +1,24 @@ + + + + + + + + Magento\ReCaptchaReview\Model\ButtonLock\ReviewFormSubmit + + + + + + review_form_submit + product_review + + + diff --git a/ReCaptchaReview/etc/frontend/events.xml b/ReCaptchaReview/etc/frontend/events.xml index 6cc89dfd..541c69c0 100644 --- a/ReCaptchaReview/etc/frontend/events.xml +++ b/ReCaptchaReview/etc/frontend/events.xml @@ -1,10 +1,10 @@ +--> diff --git a/ReCaptchaReview/etc/module.xml b/ReCaptchaReview/etc/module.xml index 9691a735..3f18bca9 100644 --- a/ReCaptchaReview/etc/module.xml +++ b/ReCaptchaReview/etc/module.xml @@ -1,10 +1,10 @@ +--> diff --git a/ReCaptchaReview/registration.php b/ReCaptchaReview/registration.php index 8b2cae1b..21509455 100644 --- a/ReCaptchaReview/registration.php +++ b/ReCaptchaReview/registration.php @@ -1,8 +1,9 @@ +--> - + +--> diff --git a/ReCaptchaSendFriend/etc/config.xml b/ReCaptchaSendFriend/etc/config.xml index a23d1a11..ece17d23 100644 --- a/ReCaptchaSendFriend/etc/config.xml +++ b/ReCaptchaSendFriend/etc/config.xml @@ -1,10 +1,10 @@ +--> diff --git a/ReCaptchaSendFriend/etc/di.xml b/ReCaptchaSendFriend/etc/di.xml index 01d9075e..1deab30e 100644 --- a/ReCaptchaSendFriend/etc/di.xml +++ b/ReCaptchaSendFriend/etc/di.xml @@ -1,10 +1,10 @@ +--> diff --git a/ReCaptchaSendFriend/etc/frontend/di.xml b/ReCaptchaSendFriend/etc/frontend/di.xml new file mode 100644 index 00000000..15ec5b30 --- /dev/null +++ b/ReCaptchaSendFriend/etc/frontend/di.xml @@ -0,0 +1,24 @@ + + + + + + + + Magento\ReCaptchaSendFriend\Model\ButtonLock\SendFriendFormSubmit + + + + + + sendfriend_form_submit + sendfriend + + + diff --git a/ReCaptchaSendFriend/etc/frontend/events.xml b/ReCaptchaSendFriend/etc/frontend/events.xml index d28d8ba7..a1631a06 100644 --- a/ReCaptchaSendFriend/etc/frontend/events.xml +++ b/ReCaptchaSendFriend/etc/frontend/events.xml @@ -1,10 +1,10 @@ +--> diff --git a/ReCaptchaSendFriend/etc/module.xml b/ReCaptchaSendFriend/etc/module.xml index 9a3e3a08..0c063e3f 100644 --- a/ReCaptchaSendFriend/etc/module.xml +++ b/ReCaptchaSendFriend/etc/module.xml @@ -1,10 +1,10 @@ +--> diff --git a/ReCaptchaSendFriend/registration.php b/ReCaptchaSendFriend/registration.php index 9f72af49..357fe232 100644 --- a/ReCaptchaSendFriend/registration.php +++ b/ReCaptchaSendFriend/registration.php @@ -1,8 +1,9 @@ +--> diff --git a/ReCaptchaSendFriend/view/frontend/web/css/source/_module.less b/ReCaptchaSendFriend/view/frontend/web/css/source/_module.less index 495f4b46..2d787978 100644 --- a/ReCaptchaSendFriend/view/frontend/web/css/source/_module.less +++ b/ReCaptchaSendFriend/view/frontend/web/css/source/_module.less @@ -1,7 +1,8 @@ /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + .form.send.friend .g-recaptcha { margin-top: 40px; } diff --git a/ReCaptchaStorePickup/Block/LayoutProcessor/Checkout/Onepage.php b/ReCaptchaStorePickup/Block/LayoutProcessor/Checkout/Onepage.php index 339db3a7..3b8c48bb 100644 --- a/ReCaptchaStorePickup/Block/LayoutProcessor/Checkout/Onepage.php +++ b/ReCaptchaStorePickup/Block/LayoutProcessor/Checkout/Onepage.php @@ -1,8 +1,9 @@ +--> diff --git a/ReCaptchaStorePickup/view/frontend/web/js/reCaptchaStorePickup.js b/ReCaptchaStorePickup/view/frontend/web/js/reCaptchaStorePickup.js index 5fe3878d..bc42bd5a 100644 --- a/ReCaptchaStorePickup/view/frontend/web/js/reCaptchaStorePickup.js +++ b/ReCaptchaStorePickup/view/frontend/web/js/reCaptchaStorePickup.js @@ -1,10 +1,10 @@ /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2021 Adobe + * All Rights Reserved. */ define(['Magento_ReCaptchaFrontendUi/js/reCaptcha'], function (reCaptcha) { - 'use strict'; + 'use strict'; // eslint-disable-line return reCaptcha.extend({ diff --git a/ReCaptchaUi/Block/ReCaptcha.php b/ReCaptchaUi/Block/ReCaptcha.php index cca673d3..7c1f2290 100644 --- a/ReCaptchaUi/Block/ReCaptcha.php +++ b/ReCaptchaUi/Block/ReCaptcha.php @@ -1,8 +1,9 @@ isCaptchaEnabled = $isCaptchaEnabled; + $this->reCaptchaId = $reCaptchaId; + $this->buttonCode = $buttonCode; + } + + /** + * @inheritDoc + */ + public function getCode(): string + { + return $this->buttonCode; + } + + /** + * @inheritDoc + */ + public function isDisabled(): bool + { + return $this->isCaptchaEnabled->isCaptchaEnabledFor($this->reCaptchaId); + } +} diff --git a/ReCaptchaUi/Model/CaptchaResponseResolver.php b/ReCaptchaUi/Model/CaptchaResponseResolver.php index 53c92d86..befad472 100644 --- a/ReCaptchaUi/Model/CaptchaResponseResolver.php +++ b/ReCaptchaUi/Model/CaptchaResponseResolver.php @@ -1,8 +1,9 @@ captchaResponseResolver->resolve($request); } catch (InputException $e) { - $this->logger->error($e); - $this->processError($response, [], $redirectOnFailureUrl, $key); + $errorMessages['missing-input-response'] = $e->getMessage(); + $this->processError($response, $errorMessages, $redirectOnFailureUrl, $key); return; } diff --git a/ReCaptchaUi/Model/RequestHandlerInterface.php b/ReCaptchaUi/Model/RequestHandlerInterface.php index 3efc05f4..c8ab62f2 100644 --- a/ReCaptchaUi/Model/RequestHandlerInterface.php +++ b/ReCaptchaUi/Model/RequestHandlerInterface.php @@ -1,8 +1,9 @@ captchaResponseResolverMock = $this->createMock(CaptchaResponseResolverInterface::class); + $this->validationConfigResolverMock = $this->createMock(ValidationConfigResolverInterface::class); + $this->captchaValidatorMock = $this->createMock(ValidatorInterface::class); + $this->messageManagerMock = $this->createMock(MessageManagerInterface::class); + $this->actionFlagMock = $this->createMock(ActionFlag::class); + $this->loggerMock = $this->createMock(LoggerInterface::class); + $this->errorMessageConfigMock = $this->createMock(ErrorMessageConfigInterface::class); + $this->validationErrorMessagesProviderMock = $this->createMock(ValidationErrorMessagesProvider::class); + $this->requestMock = $this->createMock(RequestInterface::class); + $this->responseMock = $this->createMock(HttpResponseInterface::class); + $this->validationConfigMock = $this->createMock(ApiValidationConfigInterface::class); + $this->validationResultMock = $this->createMock(ValidationResult::class); + + $this->requestHandler = new RequestHandler( + $this->captchaResponseResolverMock, + $this->validationConfigResolverMock, + $this->captchaValidatorMock, + $this->messageManagerMock, + $this->actionFlagMock, + $this->loggerMock, + $this->errorMessageConfigMock, + $this->validationErrorMessagesProviderMock + ); + } + + /** + * Test successful reCAPTCHA validation + */ + public function testExecuteWithValidCaptchaResponse() + { + $key = 'customer_login'; + $redirectOnFailureUrl = '/customer/account/login'; + $reCaptchaResponse = 'valid-captcha-response'; + + $this->validationConfigResolverMock->expects($this->once()) + ->method('get') + ->with($key) + ->willReturn($this->validationConfigMock); + + $this->captchaResponseResolverMock->expects($this->once()) + ->method('resolve') + ->with($this->requestMock) + ->willReturn($reCaptchaResponse); + + $this->validationResultMock->expects($this->once()) + ->method('isValid') + ->willReturn(true); + + $this->captchaValidatorMock->expects($this->once()) + ->method('isValid') + ->with($reCaptchaResponse, $this->validationConfigMock) + ->willReturn($this->validationResultMock); + + // These should not be called for successful validation + $this->messageManagerMock->expects($this->never())->method('addErrorMessage'); + $this->actionFlagMock->expects($this->never())->method('set'); + $this->responseMock->expects($this->never())->method('setRedirect'); + + $this->requestHandler->execute($key, $this->requestMock, $this->responseMock, $redirectOnFailureUrl); + } + + /** + * Test reCAPTCHA validation failure + */ + public function testExecuteWithInvalidCaptchaResponse() + { + $key = 'customer_login'; + $redirectOnFailureUrl = '/customer/account/login'; + $reCaptchaResponse = 'invalid-captcha-response'; + $errorMessages = ['invalid-input-response' => 'Invalid reCAPTCHA response']; + $validationErrorText = 'reCAPTCHA validation failed'; + + $this->validationConfigResolverMock->expects($this->once()) + ->method('get') + ->with($key) + ->willReturn($this->validationConfigMock); + + $this->captchaResponseResolverMock->expects($this->once()) + ->method('resolve') + ->with($this->requestMock) + ->willReturn($reCaptchaResponse); + + $this->validationResultMock->expects($this->once()) + ->method('isValid') + ->willReturn(false); + + $this->validationResultMock->expects($this->once()) + ->method('getErrors') + ->willReturn($errorMessages); + + $this->captchaValidatorMock->expects($this->once()) + ->method('isValid') + ->with($reCaptchaResponse, $this->validationConfigMock) + ->willReturn($this->validationResultMock); + + $this->errorMessageConfigMock->expects($this->once()) + ->method('getValidationFailureMessage') + ->willReturn($validationErrorText); + + $this->validationErrorMessagesProviderMock->expects($this->once()) + ->method('getErrorMessage') + ->with('invalid-input-response') + ->willReturn('Invalid reCAPTCHA response'); + + $this->messageManagerMock->expects($this->once()) + ->method('addErrorMessage') + ->with($validationErrorText); + + $this->actionFlagMock->expects($this->once()) + ->method('set') + ->with('', Action::FLAG_NO_DISPATCH, true); + + $this->responseMock->expects($this->once()) + ->method('setRedirect') + ->with($redirectOnFailureUrl); + + $this->requestHandler->execute($key, $this->requestMock, $this->responseMock, $redirectOnFailureUrl); + } + + /** + * Test InputException handling - this covers the specific change made + */ + public function testExecuteWithInputException() + { + $key = 'customer_login'; + $redirectOnFailureUrl = '/customer/account/login'; + $errorMessage = 'Missing reCAPTCHA response'; + $validationErrorText = 'reCAPTCHA validation failed'; + + $inputException = new InputException(__($errorMessage)); + + $this->validationConfigResolverMock->expects($this->once()) + ->method('get') + ->with($key) + ->willReturn($this->validationConfigMock); + + $this->captchaResponseResolverMock->expects($this->once()) + ->method('resolve') + ->with($this->requestMock) + ->willThrowException($inputException); + + // The validator should not be called when InputException is thrown + $this->captchaValidatorMock->expects($this->never())->method('isValid'); + + $this->errorMessageConfigMock->expects($this->once()) + ->method('getValidationFailureMessage') + ->willReturn($validationErrorText); + + $this->validationErrorMessagesProviderMock->expects($this->once()) + ->method('getErrorMessage') + ->with('missing-input-response') + ->willReturn('Missing reCAPTCHA response'); + + $this->messageManagerMock->expects($this->once()) + ->method('addErrorMessage') + ->with($validationErrorText); + + $this->actionFlagMock->expects($this->once()) + ->method('set') + ->with('', Action::FLAG_NO_DISPATCH, true); + + $this->responseMock->expects($this->once()) + ->method('setRedirect') + ->with($redirectOnFailureUrl); + + $this->requestHandler->execute($key, $this->requestMock, $this->responseMock, $redirectOnFailureUrl); + } + + /** + * Test technical error handling + */ + public function testExecuteWithTechnicalError() + { + $key = 'customer_login'; + $redirectOnFailureUrl = '/customer/account/login'; + $reCaptchaResponse = 'captcha-response'; + $errorMessages = ['unknown-error' => 'Unknown technical error']; + $technicalErrorText = 'Technical error occurred'; + + $this->validationConfigResolverMock->expects($this->once()) + ->method('get') + ->with($key) + ->willReturn($this->validationConfigMock); + + $this->captchaResponseResolverMock->expects($this->once()) + ->method('resolve') + ->with($this->requestMock) + ->willReturn($reCaptchaResponse); + + $this->validationResultMock->expects($this->once()) + ->method('isValid') + ->willReturn(false); + + $this->validationResultMock->expects($this->once()) + ->method('getErrors') + ->willReturn($errorMessages); + + $this->captchaValidatorMock->expects($this->once()) + ->method('isValid') + ->with($reCaptchaResponse, $this->validationConfigMock) + ->willReturn($this->validationResultMock); + + $this->errorMessageConfigMock->expects($this->once()) + ->method('getTechnicalFailureMessage') + ->willReturn($technicalErrorText); + + $this->validationErrorMessagesProviderMock->expects($this->once()) + ->method('getErrorMessage') + ->with('unknown-error') + ->willReturn('unknown-error'); + + $this->loggerMock->expects($this->once()) + ->method('error') + ->with($this->callback(function ($phrase) { + // The logger receives a Magento\Framework\Phrase object + return (string)$phrase === (string)__( + "reCAPTCHA '%1' form error: %2", + 'customer_login', + 'Unknown technical error' + ); + })); + + $this->messageManagerMock->expects($this->once()) + ->method('addErrorMessage') + ->with($technicalErrorText); + + $this->actionFlagMock->expects($this->once()) + ->method('set') + ->with('', Action::FLAG_NO_DISPATCH, true); + + $this->responseMock->expects($this->once()) + ->method('setRedirect') + ->with($redirectOnFailureUrl); + + $this->requestHandler->execute($key, $this->requestMock, $this->responseMock, $redirectOnFailureUrl); + } + + /** + * Test empty error messages handling + */ + public function testExecuteWithEmptyErrorMessages() + { + $key = 'customer_login'; + $redirectOnFailureUrl = '/customer/account/login'; + $reCaptchaResponse = 'captcha-response'; + $errorMessages = []; + $technicalErrorText = 'Technical error occurred'; + + $this->validationConfigResolverMock->expects($this->once()) + ->method('get') + ->with($key) + ->willReturn($this->validationConfigMock); + + $this->captchaResponseResolverMock->expects($this->once()) + ->method('resolve') + ->with($this->requestMock) + ->willReturn($reCaptchaResponse); + + $this->validationResultMock->expects($this->once()) + ->method('isValid') + ->willReturn(false); + + $this->validationResultMock->expects($this->once()) + ->method('getErrors') + ->willReturn($errorMessages); + + $this->captchaValidatorMock->expects($this->once()) + ->method('isValid') + ->with($reCaptchaResponse, $this->validationConfigMock) + ->willReturn($this->validationResultMock); + + $this->errorMessageConfigMock->expects($this->once()) + ->method('getTechnicalFailureMessage') + ->willReturn($technicalErrorText); + + $this->messageManagerMock->expects($this->once()) + ->method('addErrorMessage') + ->with($technicalErrorText); + + $this->actionFlagMock->expects($this->once()) + ->method('set') + ->with('', Action::FLAG_NO_DISPATCH, true); + + $this->responseMock->expects($this->once()) + ->method('setRedirect') + ->with($redirectOnFailureUrl); + + $this->requestHandler->execute($key, $this->requestMock, $this->responseMock, $redirectOnFailureUrl); + } +} diff --git a/ReCaptchaUi/composer.json b/ReCaptchaUi/composer.json index 208ac3e9..045822ae 100644 --- a/ReCaptchaUi/composer.json +++ b/ReCaptchaUi/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-re-captcha-ui", "description": "Google reCAPTCHA integration for Magento2", "require": { - "php": "~7.4.0||~8.0.0||~8.1.0", + "php": "~8.2.0||~8.3.0||~8.4.0", "magento/framework": "*", "magento/module-re-captcha-validation-api": "*" }, diff --git a/ReCaptchaUi/etc/acl.xml b/ReCaptchaUi/etc/acl.xml index c40f317c..64606489 100644 --- a/ReCaptchaUi/etc/acl.xml +++ b/ReCaptchaUi/etc/acl.xml @@ -1,10 +1,10 @@ +--> diff --git a/ReCaptchaUi/etc/di.xml b/ReCaptchaUi/etc/di.xml index 260929bf..26fd38b9 100644 --- a/ReCaptchaUi/etc/di.xml +++ b/ReCaptchaUi/etc/di.xml @@ -1,10 +1,10 @@ +--> diff --git a/ReCaptchaUi/etc/module.xml b/ReCaptchaUi/etc/module.xml index d1125400..d6287047 100644 --- a/ReCaptchaUi/etc/module.xml +++ b/ReCaptchaUi/etc/module.xml @@ -1,10 +1,10 @@ +--> diff --git a/ReCaptchaUi/registration.php b/ReCaptchaUi/registration.php index 8308dbd7..202ac5b2 100644 --- a/ReCaptchaUi/registration.php +++ b/ReCaptchaUi/registration.php @@ -1,8 +1,9 @@ disableReCaptchaForUserForgotPassword->execute(); + + return Cli::RETURN_SUCCESS; } } diff --git a/ReCaptchaUser/Command/DisableReCaptchaForUserLoginCommand.php b/ReCaptchaUser/Command/DisableReCaptchaForUserLoginCommand.php index 461067e6..2c989932 100644 --- a/ReCaptchaUser/Command/DisableReCaptchaForUserLoginCommand.php +++ b/ReCaptchaUser/Command/DisableReCaptchaForUserLoginCommand.php @@ -1,12 +1,14 @@ disableReCaptchaForUserLogin->execute(); + + return Cli::RETURN_SUCCESS; } } diff --git a/ReCaptchaUser/Model/DisableReCaptchaForUserForgotPassword.php b/ReCaptchaUser/Model/DisableReCaptchaForUserForgotPassword.php index 4db2e4bf..b4ac4345 100644 --- a/ReCaptchaUser/Model/DisableReCaptchaForUserForgotPassword.php +++ b/ReCaptchaUser/Model/DisableReCaptchaForUserForgotPassword.php @@ -1,8 +1,9 @@ isCaptchaEnabled->isCaptchaEnabledFor($key) - && $this->request->getFullActionName() === $this->loginActionName - ) { + if ($this->isCaptchaEnabled->isCaptchaEnabledFor($key)) { $validationConfig = $this->validationConfigResolver->get($key); try { $reCaptchaResponse = $this->captchaResponseResolver->resolve($this->request); diff --git a/ReCaptchaUser/README.md b/ReCaptchaUser/README.md index f3319e7b..e73178b0 100644 --- a/ReCaptchaUser/README.md +++ b/ReCaptchaUser/README.md @@ -1,24 +1,27 @@ -# Magento reCAPTCHA +# Magento_ReCaptchaUser module -Google reCAPTCHA ensures that a human being, rather than a computer (or “bot”), is interacting with your website. Unlike the standard Magento CAPTCHA, Google reCAPTCHA provides enhanced security with a selection of different display options and methods. Additional website traffic information is available in the dashboard of your Google reCAPTCHA account. +Google reCAPTCHA ensures that a human being, rather than a computer (or "bot"), is interacting with your website. Unlike the standard Magento CAPTCHA, Google reCAPTCHA provides enhanced security with a selection of different display options and methods. Additional website traffic information is available in the dashboard of your Google reCAPTCHA account. This module provides the reCAPTCHA implementations related to user actions. -For more information please visit the [Magento document for reCAPTCHA](https://docs.magento.com/user-guide/stores/security-google-recaptcha.html). +For more information, see the [reCAPTCHA documentation](https://experienceleague.adobe.com/en/docs/commerce-admin/systems/security/captcha/security-google-recaptcha). +## Emergency commandline disable for Admin panel Login page -## Emergency commandline disable for Admin panel Login page: +To disable Google reCAPTCHA for Admin Panel Login page from command-line: -Can disable Google reCAPTCHA for Admin Panel Login page from command-line: +```bash +php bin/magento security:recaptcha:disable-for-user-login +``` -`php bin/magento security:recaptcha:disable-for-user-login` +This disables Google reCAPTCHA for Admin Panel Login page form. -This will disable Google reCAPTCHA for Admin Panel Login page form. +## Emergency commandline disable for Admin panel Reset Password page -## Emergency commandline disable for Admin panel Reset Password page: +To disable Google reCAPTCHA for Admin panel Reset Password page from command-line: -Can disable Google reCAPTCHA for Admin panel Reset Password page from command-line: +```bash +php bin/magento security:recaptcha:disable-for-user-forgot-password +``` -`php bin/magento security:recaptcha:disable-for-user-forgot-password` - -This will disable Google reCAPTCHA for Admin panel Reset Password page form. +This disables Google reCAPTCHA for Admin panel Reset Password page form. diff --git a/ReCaptchaUser/Test/Integration/ForgotPasswordFormTest.php b/ReCaptchaUser/Test/Integration/ForgotPasswordFormTest.php index 465cfbd8..1324adba 100644 --- a/ReCaptchaUser/Test/Integration/ForgotPasswordFormTest.php +++ b/ReCaptchaUser/Test/Integration/ForgotPasswordFormTest.php @@ -1,8 +1,9 @@ checkFailedPostResponse(); } + /** + * @magentoAdminConfigFixture admin/security/use_form_key 0 + * @magentoAdminConfigFixture admin/captcha/enable 0 + * @magentoAdminConfigFixture recaptcha_backend/type_invisible/public_key test_public_key + * @magentoAdminConfigFixture recaptcha_backend/type_invisible/private_key test_private_key + * @magentoAdminConfigFixture recaptcha_backend/type_for/user_login invisible + */ + public function testPostRequestIfReCaptchaParameterIsMissedWithRef(): void + { + $postValues = []; + $this->getRequest() + ->setMethod(Http::METHOD_POST) + ->setPostValue(array_replace_recursive( + [ + 'form_key' => $this->formKey->getFormKey(), + 'login' => [ + 'username' => Bootstrap::ADMIN_NAME, + 'password' => Bootstrap::ADMIN_PASSWORD, + ], + ], + $postValues + )); + $this->dispatch('backend/admin/dashboard/index'); + $this->assertSessionMessages( + self::equalTo(['Something went wrong with reCAPTCHA. Please contact the store owner.']), + MessageInterface::TYPE_ERROR + ); + self::assertFalse($this->auth->isLoggedIn()); + } + /** * @magentoAdminConfigFixture admin/security/use_form_key 0 * @magentoAdminConfigFixture admin/captcha/enable 0 diff --git a/ReCaptchaUser/Test/Mftf/ActionGroup/RecaptchaDisabledActionGroup.xml b/ReCaptchaUser/Test/Mftf/ActionGroup/RecaptchaDisabledActionGroup.xml new file mode 100644 index 00000000..020dd755 --- /dev/null +++ b/ReCaptchaUser/Test/Mftf/ActionGroup/RecaptchaDisabledActionGroup.xml @@ -0,0 +1,24 @@ + + + + + + Disable admin login recaptcha + + + + + + + + + + + + diff --git a/ReCaptchaUser/Test/Mftf/ActionGroup/RecaptchaEnabledActionGroup.xml b/ReCaptchaUser/Test/Mftf/ActionGroup/RecaptchaEnabledActionGroup.xml new file mode 100644 index 00000000..5c1a34e1 --- /dev/null +++ b/ReCaptchaUser/Test/Mftf/ActionGroup/RecaptchaEnabledActionGroup.xml @@ -0,0 +1,28 @@ + + + + + + Enable admin login recaptcha + + + + + + + + + + + + + + + + diff --git a/ReCaptchaUser/Test/Mftf/Data/RecaptchaConfigPageData.xml b/ReCaptchaUser/Test/Mftf/Data/RecaptchaConfigPageData.xml new file mode 100644 index 00000000..0925f55b --- /dev/null +++ b/ReCaptchaUser/Test/Mftf/Data/RecaptchaConfigPageData.xml @@ -0,0 +1,16 @@ + + + + + recaptcha_v3 + + + + + diff --git a/ReCaptchaUser/Test/Mftf/Page/AdminStoreConfigurationPage.xml b/ReCaptchaUser/Test/Mftf/Page/AdminStoreConfigurationPage.xml new file mode 100644 index 00000000..02ae5a68 --- /dev/null +++ b/ReCaptchaUser/Test/Mftf/Page/AdminStoreConfigurationPage.xml @@ -0,0 +1,13 @@ + + + + +
+ + diff --git a/ReCaptchaUser/Test/Mftf/Section/RecaptchaFormSection.xml b/ReCaptchaUser/Test/Mftf/Section/RecaptchaFormSection.xml new file mode 100644 index 00000000..e3a4678f --- /dev/null +++ b/ReCaptchaUser/Test/Mftf/Section/RecaptchaFormSection.xml @@ -0,0 +1,17 @@ + + + +
+ + + + + +
+
diff --git a/ReCaptchaUser/Test/Mftf/Test/AdminLoginReCaptchaFunctionalityTest.xml b/ReCaptchaUser/Test/Mftf/Test/AdminLoginReCaptchaFunctionalityTest.xml new file mode 100644 index 00000000..ab973577 --- /dev/null +++ b/ReCaptchaUser/Test/Mftf/Test/AdminLoginReCaptchaFunctionalityTest.xml @@ -0,0 +1,30 @@ + + + + + + + + + <description value="Admin should be able to login with enabled recaptcha option"/> + <severity value="AVERAGE"/> + <group value="login"/> + <testCaseId value="AC-3179"/> + </annotations> + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> + <actionGroup ref="RecaptchaEnabledActionGroup" stepKey="recaptchaEnabled"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAgain11"/> + <actionGroup ref="RecaptchaDisabledActionGroup" stepKey="recaptchaDisabled"/> + </before> + <after> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + </test> +</tests> diff --git a/ReCaptchaUser/Test/Mftf/test-dependency-allowlist b/ReCaptchaUser/Test/Mftf/test-dependency-allowlist new file mode 100644 index 00000000..22d50ad2 --- /dev/null +++ b/ReCaptchaUser/Test/Mftf/test-dependency-allowlist @@ -0,0 +1,2 @@ +AdminLoginActionGroup +AdminLogoutActionGroup diff --git a/ReCaptchaUser/Test/Mftf/test-dependency-errors-detailed b/ReCaptchaUser/Test/Mftf/test-dependency-errors-detailed new file mode 100644 index 00000000..e078c2bc --- /dev/null +++ b/ReCaptchaUser/Test/Mftf/test-dependency-errors-detailed @@ -0,0 +1,6 @@ + +File "/var/www/html/app/code/Magento/ReCaptchaUser/Test/Mftf/Test/AdminLoginReCaptchaFunctionalityTest.xml" +contains entity references that violate dependency constraints: + + AdminLoginActionGroup from module(s): magento/module-admin-analytics, magento/module-backend, magento/module-two-factor-auth + AdminLogoutActionGroup from module(s): magento/module-backend diff --git a/ReCaptchaUser/composer.json b/ReCaptchaUser/composer.json index ddb60c77..b19869e7 100644 --- a/ReCaptchaUser/composer.json +++ b/ReCaptchaUser/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-re-captcha-user", "description": "Google reCAPTCHA integration for Magento2", "require": { - "php": "~7.4.0||~8.0.0||~8.1.0", + "php": "~8.2.0||~8.3.0||~8.4.0", "magento/framework": "*", "magento/module-re-captcha-ui": "*", "magento/module-re-captcha-validation-api": "*" diff --git a/ReCaptchaUser/etc/adminhtml/events.xml b/ReCaptchaUser/etc/adminhtml/events.xml index c0e9a2c5..f2bf172b 100644 --- a/ReCaptchaUser/etc/adminhtml/events.xml +++ b/ReCaptchaUser/etc/adminhtml/events.xml @@ -1,10 +1,10 @@ <?xml version="1.0"?> <!-- /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ - --> +--> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd"> <event name="controller_action_predispatch_adminhtml_auth_forgotpassword"> diff --git a/ReCaptchaUser/etc/adminhtml/system.xml b/ReCaptchaUser/etc/adminhtml/system.xml index 8bb4d234..5b38eb54 100644 --- a/ReCaptchaUser/etc/adminhtml/system.xml +++ b/ReCaptchaUser/etc/adminhtml/system.xml @@ -1,10 +1,10 @@ <?xml version="1.0"?> <!-- /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ - --> +--> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd"> <system> diff --git a/ReCaptchaUser/etc/config.xml b/ReCaptchaUser/etc/config.xml index 0bd2fe9f..cc2dc64e 100644 --- a/ReCaptchaUser/etc/config.xml +++ b/ReCaptchaUser/etc/config.xml @@ -1,10 +1,10 @@ <?xml version="1.0"?> <!-- /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ - --> +--> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Store:etc/config.xsd"> <default> diff --git a/ReCaptchaUser/etc/di.xml b/ReCaptchaUser/etc/di.xml index 8d033b5f..639fc74c 100644 --- a/ReCaptchaUser/etc/di.xml +++ b/ReCaptchaUser/etc/di.xml @@ -1,10 +1,10 @@ <?xml version="1.0"?> <!-- /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ - --> +--> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> <type name="Magento\Framework\Console\CommandListInterface"> diff --git a/ReCaptchaUser/etc/module.xml b/ReCaptchaUser/etc/module.xml index 533c7a8d..501b02bd 100644 --- a/ReCaptchaUser/etc/module.xml +++ b/ReCaptchaUser/etc/module.xml @@ -1,10 +1,10 @@ <?xml version="1.0"?> <!-- /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ - --> +--> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd"> <module name="Magento_ReCaptchaUser"/> diff --git a/ReCaptchaUser/registration.php b/ReCaptchaUser/registration.php index 5fabe668..75f065ac 100644 --- a/ReCaptchaUser/registration.php +++ b/ReCaptchaUser/registration.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); \Magento\Framework\Component\ComponentRegistrar::register( diff --git a/ReCaptchaUser/view/adminhtml/layout/adminhtml_auth_forgotpassword.xml b/ReCaptchaUser/view/adminhtml/layout/adminhtml_auth_forgotpassword.xml index aa14ebae..8df07538 100644 --- a/ReCaptchaUser/view/adminhtml/layout/adminhtml_auth_forgotpassword.xml +++ b/ReCaptchaUser/view/adminhtml/layout/adminhtml_auth_forgotpassword.xml @@ -1,10 +1,10 @@ <?xml version="1.0"?> <!-- /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ - --> +--> <page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> diff --git a/ReCaptchaUser/view/adminhtml/layout/adminhtml_auth_login.xml b/ReCaptchaUser/view/adminhtml/layout/adminhtml_auth_login.xml index 4bc51e0e..1ea612af 100644 --- a/ReCaptchaUser/view/adminhtml/layout/adminhtml_auth_login.xml +++ b/ReCaptchaUser/view/adminhtml/layout/adminhtml_auth_login.xml @@ -1,10 +1,10 @@ <?xml version="1.0"?> <!-- /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ - --> +--> <page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> diff --git a/ReCaptchaUser/view/adminhtml/layout/recaptcha.xml b/ReCaptchaUser/view/adminhtml/layout/recaptcha.xml index 1a55a978..f516c166 100644 --- a/ReCaptchaUser/view/adminhtml/layout/recaptcha.xml +++ b/ReCaptchaUser/view/adminhtml/layout/recaptcha.xml @@ -1,10 +1,10 @@ <?xml version="1.0"?> <!-- /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ - --> +--> <page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> <head> diff --git a/ReCaptchaUser/view/adminhtml/templates/recaptcha.phtml b/ReCaptchaUser/view/adminhtml/templates/recaptcha.phtml index 35e287b3..cae29d35 100644 --- a/ReCaptchaUser/view/adminhtml/templates/recaptcha.phtml +++ b/ReCaptchaUser/view/adminhtml/templates/recaptcha.phtml @@ -1,9 +1,11 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + /** @var $block Magento\ReCaptchaUi\Block\ReCaptcha */ +/** @var $escaper \Magento\Framework\Escaper */ $config = $block->getCaptchaUiConfig(); $renderingOptions = $config['rendering'] ?? []; $isInvisible = !empty($config['invisible']); @@ -13,7 +15,6 @@ $isInvisible = !empty($config['invisible']); class="admin-recaptcha-content<?= /* @noEscape */ !empty($renderingOptions['size']) ? ' size-' . $renderingOptions['size'] : '' ?>"></div> </div> - <script> require([ 'jquery' @@ -27,36 +28,57 @@ $isInvisible = !empty($config['invisible']); element.src = 'https://www.google.com/recaptcha/api.js' + '?onload=globalOnRecaptchaOnLoadCallback&render=explicit'; - window.globalOnRecaptchaOnLoadCallback = function () { - let token = ''; + let isRecaptchaLoaded = false; + let token = ''; + let maxRetryAttempts = 5; + let attempts = 0; + let widgetId = 0; + <?php if ($isInvisible): ?> + $('#login-form').submit(function (event) { + if (!token) { + event.preventDefault(event); + event.stopImmediatePropagation(); + event.stopPropagation(); - this.widgetId = grecaptcha.render('admin-recaptcha', { - <?php foreach ($renderingOptions as $key => $value): ?> - '<?= $block->escapeJs($key) ?>': '<?= $block->escapeJs($value) ?>', - <?php endforeach; ?> - 'callback': function (token) { // jscs:ignore jsDoc - <?php if ($isInvisible): ?> - this.token = token; - $('#login-form').submit(); - <?php endif; ?> - }.bind(this) - }); + let attemptRecaptcha = () => { + attempts++; + if (attempts > maxRetryAttempts){ + console.error("Could not fetch invisible ReCaptcha token. Please refresh the page."); + return; + } + if (!isRecaptchaLoaded) { - <?php if ($isInvisible): ?> - $('#login-form').submit(function (event) { - if (!this.token) { - grecaptcha.execute(this.widgetId).then( - function() { + setTimeout(() => { + attemptRecaptcha() + }, 1000); + return; + } + grecaptcha.execute(widgetId) + .then( () => { event.preventDefault(event); event.stopImmediatePropagation(); - }, function(reason) { - }); + event.stopPropagation(); + }, (reason) => { }) + .catch(err => { console.error(err); }); } - }.bind(this)); - <?php endif; ?> - - }.bind(this); + attemptRecaptcha(); + } + }); + <?php endif; ?> + window.globalOnRecaptchaOnLoadCallback = function () { + widgetId = grecaptcha.render('admin-recaptcha', { + <?php foreach ($renderingOptions as $key => $value): ?> + '<?= $escaper->escapeJs($key) ?>': '<?= $escaper->escapeJs($value) ?>', + <?php endforeach; ?> 'callback': function (_token) { + <?php if ($isInvisible): ?> + token = _token; + $('#login-form').unbind('submit'); + $('#login-form').submit(); + <?php endif; ?> } + }); + isRecaptchaLoaded = true; + } scriptTag.parentNode.insertBefore(element, scriptTag); }); </script> diff --git a/ReCaptchaUser/view/adminhtml/web/css/recaptcha.less b/ReCaptchaUser/view/adminhtml/web/css/recaptcha.less index d30a444d..13ee3f86 100644 --- a/ReCaptchaUser/view/adminhtml/web/css/recaptcha.less +++ b/ReCaptchaUser/view/adminhtml/web/css/recaptcha.less @@ -1,15 +1,16 @@ /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + .login-content { .field-recaptcha { margin-left: auto; } .field-invisible-recaptcha { - padding-left: 30px !important; - margin-top: -10px; margin-bottom: 35px; + margin-top: -10px; + padding-left: 30px !important; } } diff --git a/ReCaptchaValidation/Model/ReCaptcha.php b/ReCaptchaValidation/Model/ReCaptcha.php new file mode 100644 index 00000000..07c073d5 --- /dev/null +++ b/ReCaptchaValidation/Model/ReCaptcha.php @@ -0,0 +1,24 @@ +<?php +/** + * Copyright 2023 Adobe + * All Rights Reserved. + */ + +declare(strict_types=1); + +namespace Magento\ReCaptchaValidation\Model; + +use ReCaptcha\ReCaptcha as GoogleReCaptcha; + +/** + * Wrapper Class for Google Recaptcha + * Used to fix dynamic property deprecation error + */ +class ReCaptcha extends GoogleReCaptcha +{ + + /** + * @var float + */ + protected float $threshold = 0.0; +} diff --git a/ReCaptchaValidation/Model/ValidationConfig.php b/ReCaptchaValidation/Model/ValidationConfig.php index f6bd5674..b73a68ce 100644 --- a/ReCaptchaValidation/Model/ValidationConfig.php +++ b/ReCaptchaValidation/Model/ValidationConfig.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\ReCaptchaValidation\Model; @@ -45,7 +46,7 @@ public function __construct( string $privateKey, string $remoteIp, string $validationFailureMessage, - ValidationConfigExtensionInterface $extensionAttributes = null + ?ValidationConfigExtensionInterface $extensionAttributes = null ) { $this->privateKey = $privateKey; $this->remoteIp = $remoteIp; diff --git a/ReCaptchaValidation/Model/Validator.php b/ReCaptchaValidation/Model/Validator.php index a8f544d9..c6d0887f 100644 --- a/ReCaptchaValidation/Model/Validator.php +++ b/ReCaptchaValidation/Model/Validator.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\ReCaptchaValidation\Model; @@ -13,7 +14,7 @@ use Magento\ReCaptchaValidationApi\Api\ValidatorInterface; use Magento\ReCaptchaValidationApi\Model\ErrorMessagesProvider; use ReCaptcha\ReCaptcha; -use ReCaptcha\ReCaptchaFactory; +use Magento\ReCaptchaValidation\Model\ReCaptchaFactory; /** * @inheritdoc @@ -31,7 +32,7 @@ class Validator implements ValidatorInterface private $errorMessagesProvider; /** - * @var ReCaptchaFactory + * @var ReCaptchaFactory\ */ private $reCaptchaFactory; diff --git a/ReCaptchaValidation/README.md b/ReCaptchaValidation/README.md index a5f0548c..5cdd4095 100644 --- a/ReCaptchaValidation/README.md +++ b/ReCaptchaValidation/README.md @@ -1,7 +1,7 @@ -# Magento reCAPTCHA +# Magento_ReCaptchaValidation module -Google reCAPTCHA ensures that a human being, rather than a computer (or “bot”), is interacting with your website. Unlike the standard Magento CAPTCHA, Google reCAPTCHA provides enhanced security with a selection of different display options and methods. Additional website traffic information is available in the dashboard of your Google reCAPTCHA account. +Google reCAPTCHA ensures that a human being, rather than a computer (or "bot"), is interacting with your website. Unlike the standard Magento CAPTCHA, Google reCAPTCHA provides enhanced security with a selection of different display options and methods. Additional website traffic information is available in the dashboard of your Google reCAPTCHA account. This module provides the base implementation for reCAPTCHA. -For more information please visit the [Magento document for reCAPTCHA](https://docs.magento.com/user-guide/stores/security-google-recaptcha.html). +For more information, see the [reCAPTCHA documentation](https://experienceleague.adobe.com/en/docs/commerce-admin/systems/security/captcha/security-google-recaptcha). diff --git a/ReCaptchaValidation/composer.json b/ReCaptchaValidation/composer.json index afe236b8..d47fedcd 100644 --- a/ReCaptchaValidation/composer.json +++ b/ReCaptchaValidation/composer.json @@ -2,10 +2,10 @@ "name": "magento/module-re-captcha-validation", "description": "Google reCAPTCHA integration for Magento2", "require": { - "php": "~7.4.0||~8.0.0||~8.1.0", + "php": "~8.2.0||~8.3.0||~8.4.0", "magento/framework": "*", "magento/module-re-captcha-validation-api": "*", - "google/recaptcha": "^1.2" + "phpfui/recaptcha": "^2.0.0" }, "type": "magento2-module", "license": "OSL-3.0", diff --git a/ReCaptchaValidation/etc/di.xml b/ReCaptchaValidation/etc/di.xml index 9cddc231..a6bfe189 100644 --- a/ReCaptchaValidation/etc/di.xml +++ b/ReCaptchaValidation/etc/di.xml @@ -1,10 +1,10 @@ <?xml version="1.0"?> <!-- /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ - --> +--> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> <preference for="Magento\ReCaptchaValidationApi\Api\Data\ValidationConfigInterface" type="Magento\ReCaptchaValidation\Model\ValidationConfig"/> diff --git a/ReCaptchaValidation/etc/module.xml b/ReCaptchaValidation/etc/module.xml index bd577e5d..5f5b6242 100644 --- a/ReCaptchaValidation/etc/module.xml +++ b/ReCaptchaValidation/etc/module.xml @@ -1,10 +1,10 @@ <?xml version="1.0"?> <!-- /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ - --> +--> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd"> <module name="Magento_ReCaptchaValidation"/> diff --git a/ReCaptchaValidation/registration.php b/ReCaptchaValidation/registration.php index 9c83d4c3..8eb6b3f4 100644 --- a/ReCaptchaValidation/registration.php +++ b/ReCaptchaValidation/registration.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); \Magento\Framework\Component\ComponentRegistrar::register( diff --git a/ReCaptchaValidationApi/Api/Data/ValidationConfigInterface.php b/ReCaptchaValidationApi/Api/Data/ValidationConfigInterface.php index 824012af..94d37af3 100644 --- a/ReCaptchaValidationApi/Api/Data/ValidationConfigInterface.php +++ b/ReCaptchaValidationApi/Api/Data/ValidationConfigInterface.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\ReCaptchaValidationApi\Api\Data; @@ -32,6 +33,7 @@ public function getRemoteIp(): string; * Get validation failure message TODO * * @deprecated use TODO + * @see not used anymore * @return string */ public function getValidationFailureMessage(): string; diff --git a/ReCaptchaValidationApi/Api/ValidatorInterface.php b/ReCaptchaValidationApi/Api/ValidatorInterface.php index 6e580935..28af5fc5 100644 --- a/ReCaptchaValidationApi/Api/ValidatorInterface.php +++ b/ReCaptchaValidationApi/Api/ValidatorInterface.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\ReCaptchaValidationApi\Api; diff --git a/ReCaptchaValidationApi/Model/ErrorMessagesProvider.php b/ReCaptchaValidationApi/Model/ErrorMessagesProvider.php index ec83f9af..eb64381f 100644 --- a/ReCaptchaValidationApi/Model/ErrorMessagesProvider.php +++ b/ReCaptchaValidationApi/Model/ErrorMessagesProvider.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\ReCaptchaValidationApi\Model; diff --git a/ReCaptchaValidationApi/Model/ValidationErrorMessagesProvider.php b/ReCaptchaValidationApi/Model/ValidationErrorMessagesProvider.php index 0b3df540..ee34de99 100644 --- a/ReCaptchaValidationApi/Model/ValidationErrorMessagesProvider.php +++ b/ReCaptchaValidationApi/Model/ValidationErrorMessagesProvider.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\ReCaptchaValidationApi\Model; diff --git a/ReCaptchaValidationApi/README.md b/ReCaptchaValidationApi/README.md index 6da66c85..55fbfe35 100644 --- a/ReCaptchaValidationApi/README.md +++ b/ReCaptchaValidationApi/README.md @@ -1,7 +1,7 @@ -# Magento reCAPTCHA +# Magento_ReCaptchaValidationApi module -Google reCAPTCHA ensures that a human being, rather than a computer (or “bot”), is interacting with your website. Unlike the standard Magento CAPTCHA, Google reCAPTCHA provides enhanced security with a selection of different display options and methods. Additional website traffic information is available in the dashboard of your Google reCAPTCHA account. +Google reCAPTCHA ensures that a human being, rather than a computer (or "bot"), is interacting with your website. Unlike the standard Magento CAPTCHA, Google reCAPTCHA provides enhanced security with a selection of different display options and methods. Additional website traffic information is available in the dashboard of your Google reCAPTCHA account. This module provides the service contracts for the base reCAPTCHA implementation. -For more information please visit the [Magento document for reCAPTCHA](https://docs.magento.com/user-guide/stores/security-google-recaptcha.html). +For more information, see the [reCAPTCHA documentation](https://experienceleague.adobe.com/en/docs/commerce-admin/systems/security/captcha/security-google-recaptcha). diff --git a/ReCaptchaValidationApi/composer.json b/ReCaptchaValidationApi/composer.json index 97d9dab4..a497dd9b 100644 --- a/ReCaptchaValidationApi/composer.json +++ b/ReCaptchaValidationApi/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-re-captcha-validation-api", "description": "Google reCAPTCHA integration for Magento2", "require": { - "php": "~7.4.0||~8.0.0||~8.1.0", + "php": "~8.2.0||~8.3.0||~8.4.0", "magento/framework": "*" }, "type": "magento2-module", diff --git a/ReCaptchaValidationApi/etc/di.xml b/ReCaptchaValidationApi/etc/di.xml index a70521e1..dcac396b 100644 --- a/ReCaptchaValidationApi/etc/di.xml +++ b/ReCaptchaValidationApi/etc/di.xml @@ -1,10 +1,10 @@ <?xml version="1.0"?> <!-- /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ - --> +--> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> <type name="Magento\ReCaptchaValidationApi\Model\ErrorMessagesProvider"> diff --git a/ReCaptchaValidationApi/etc/module.xml b/ReCaptchaValidationApi/etc/module.xml index 7a24451e..3987fd41 100644 --- a/ReCaptchaValidationApi/etc/module.xml +++ b/ReCaptchaValidationApi/etc/module.xml @@ -1,10 +1,10 @@ <?xml version="1.0"?> <!-- /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ - --> +--> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd"> <module name="Magento_ReCaptchaValidationApi"/> diff --git a/ReCaptchaValidationApi/registration.php b/ReCaptchaValidationApi/registration.php index bed7ab66..64434fe0 100644 --- a/ReCaptchaValidationApi/registration.php +++ b/ReCaptchaValidationApi/registration.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); \Magento\Framework\Component\ComponentRegistrar::register( diff --git a/ReCaptchaVersion2Checkbox/Model/Adminhtml/UiConfigProvider.php b/ReCaptchaVersion2Checkbox/Model/Adminhtml/UiConfigProvider.php index feb2c014..afa64e6f 100644 --- a/ReCaptchaVersion2Checkbox/Model/Adminhtml/UiConfigProvider.php +++ b/ReCaptchaVersion2Checkbox/Model/Adminhtml/UiConfigProvider.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\ReCaptchaVersion2Checkbox\Model\Adminhtml; diff --git a/ReCaptchaVersion2Checkbox/Model/Adminhtml/ValidationConfigProvider.php b/ReCaptchaVersion2Checkbox/Model/Adminhtml/ValidationConfigProvider.php index 96495fe6..9b3b1689 100644 --- a/ReCaptchaVersion2Checkbox/Model/Adminhtml/ValidationConfigProvider.php +++ b/ReCaptchaVersion2Checkbox/Model/Adminhtml/ValidationConfigProvider.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\ReCaptchaVersion2Checkbox\Model\Adminhtml; diff --git a/ReCaptchaVersion2Checkbox/Model/Config.php b/ReCaptchaVersion2Checkbox/Model/Config.php new file mode 100644 index 00000000..ac6bf759 --- /dev/null +++ b/ReCaptchaVersion2Checkbox/Model/Config.php @@ -0,0 +1,100 @@ +<?php +/** + * Copyright 2024 Adobe + * All Rights Reserved. + */ + +declare(strict_types=1); + +namespace Magento\ReCaptchaVersion2Checkbox\Model; + +use Magento\Framework\ObjectManager\ResetAfterRequestInterface; +use Magento\ReCaptchaVersion2Checkbox\Model\Frontend\UiConfigProvider; +use Magento\ReCaptchaWebapiGraphQl\Model\Adapter\ReCaptchaConfigInterface; + +class Config implements ReCaptchaConfigInterface, ResetAfterRequestInterface +{ + /** + * @var array + */ + private array $uiConfig = []; + + /** + * @param UiConfigProvider $uiConfigProvider + */ + public function __construct( + private readonly UiConfigProvider $uiConfigProvider, + ) { + } + + /** + * Get front-end's UI configurations + * + * @return array + */ + private function getUiConfig(): array + { + if (empty($this->uiConfig)) { + $this->uiConfig = $this->uiConfigProvider->get() ?? []; + } + return $this->uiConfig; + } + + /** + * Get website's Google API public key + * + * @return string + */ + public function getWebsiteKey(): string + { + return $this->getUiConfig()['rendering']['sitekey'] ?? ''; + } + + /** + * Get configured captcha's theme + * + * @return string + */ + public function getTheme(): string + { + return $this->getUiConfig()['rendering']['theme'] ?? ''; + } + + /** + * Get code of language to send notifications + * + * @return string + */ + public function getLanguageCode(): string + { + return $this->getUiConfig()['rendering']['hl'] ?? ''; + } + + /** + * "I am not a robot" captcha does not provide configurable minimum score setting + * + * @return null|float + */ + public function getMinimumScore(): ?float + { + return null; + } + + /** + * ReCaptcha V2 does not provide configurable badge_position setting + * + * @return string + */ + public function getBadgePosition(): string + { + return ''; + } + + /** + * @inheritDoc + */ + public function _resetState(): void + { + $this->uiConfig = []; + } +} diff --git a/ReCaptchaVersion2Checkbox/Model/Frontend/UiConfigProvider.php b/ReCaptchaVersion2Checkbox/Model/Frontend/UiConfigProvider.php index bdc4b7f5..5600ed83 100644 --- a/ReCaptchaVersion2Checkbox/Model/Frontend/UiConfigProvider.php +++ b/ReCaptchaVersion2Checkbox/Model/Frontend/UiConfigProvider.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\ReCaptchaVersion2Checkbox\Model\Frontend; diff --git a/ReCaptchaVersion2Checkbox/Model/Frontend/ValidationConfigProvider.php b/ReCaptchaVersion2Checkbox/Model/Frontend/ValidationConfigProvider.php index 136a7dc3..274309c7 100644 --- a/ReCaptchaVersion2Checkbox/Model/Frontend/ValidationConfigProvider.php +++ b/ReCaptchaVersion2Checkbox/Model/Frontend/ValidationConfigProvider.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\ReCaptchaVersion2Checkbox\Model\Frontend; diff --git a/ReCaptchaVersion2Checkbox/README.md b/ReCaptchaVersion2Checkbox/README.md index e5b30cf6..63aa5d5a 100644 --- a/ReCaptchaVersion2Checkbox/README.md +++ b/ReCaptchaVersion2Checkbox/README.md @@ -1,7 +1,7 @@ -# Magento reCAPTCHA +# Magento_ReCaptchaVersion2Checkbox module -Google reCAPTCHA ensures that a human being, rather than a computer (or “bot”), is interacting with your website. Unlike the standard Magento CAPTCHA, Google reCAPTCHA provides enhanced security with a selection of different display options and methods. Additional website traffic information is available in the dashboard of your Google reCAPTCHA account. +Google reCAPTCHA ensures that a human being, rather than a computer (or "bot"), is interacting with your website. Unlike the standard Magento CAPTCHA, Google reCAPTCHA provides enhanced security with a selection of different display options and methods. Additional website traffic information is available in the dashboard of your Google reCAPTCHA account. This module provides the reCAPTCHA implementation for the V2 Checkbox variation. -For more information please visit the [Magento document for reCAPTCHA](https://docs.magento.com/user-guide/stores/security-google-recaptcha.html). +For more information, see the [reCAPTCHA documentation](https://experienceleague.adobe.com/en/docs/commerce-admin/systems/security/captcha/security-google-recaptcha). diff --git a/ReCaptchaVersion2Checkbox/composer.json b/ReCaptchaVersion2Checkbox/composer.json index e325cddb..f659a22f 100644 --- a/ReCaptchaVersion2Checkbox/composer.json +++ b/ReCaptchaVersion2Checkbox/composer.json @@ -2,11 +2,12 @@ "name": "magento/module-re-captcha-version-2-checkbox", "description": "Google reCAPTCHA integration for Magento2", "require": { - "php": "~7.4.0||~8.0.0||~8.1.0", + "php": "~8.2.0||~8.3.0||~8.4.0", "magento/framework": "*", "magento/module-store": "*", "magento/module-re-captcha-ui": "*", - "magento/module-re-captcha-validation-api": "*" + "magento/module-re-captcha-validation-api": "*", + "magento/module-re-captcha-webapi-graph-ql": "*" }, "suggest": { "magento/module-config": "*", diff --git a/ReCaptchaVersion2Checkbox/etc/adminhtml/di.xml b/ReCaptchaVersion2Checkbox/etc/adminhtml/di.xml index 10efe821..005db252 100644 --- a/ReCaptchaVersion2Checkbox/etc/adminhtml/di.xml +++ b/ReCaptchaVersion2Checkbox/etc/adminhtml/di.xml @@ -1,10 +1,10 @@ <?xml version="1.0"?> <!-- /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ - --> +--> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> <type name="Magento\ReCaptchaUi\Model\UiConfigResolver"> diff --git a/ReCaptchaVersion2Checkbox/etc/adminhtml/system.xml b/ReCaptchaVersion2Checkbox/etc/adminhtml/system.xml index 935c813d..ec62ddd9 100644 --- a/ReCaptchaVersion2Checkbox/etc/adminhtml/system.xml +++ b/ReCaptchaVersion2Checkbox/etc/adminhtml/system.xml @@ -1,10 +1,10 @@ <?xml version="1.0"?> <!-- /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ - --> +--> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd"> <system> diff --git a/ReCaptchaVersion2Checkbox/etc/config.xml b/ReCaptchaVersion2Checkbox/etc/config.xml index 5fe67d28..b68a9de7 100644 --- a/ReCaptchaVersion2Checkbox/etc/config.xml +++ b/ReCaptchaVersion2Checkbox/etc/config.xml @@ -1,10 +1,10 @@ <?xml version="1.0"?> <!-- /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ - --> +--> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Store:etc/config.xsd"> <default> diff --git a/ReCaptchaVersion2Checkbox/etc/csp_whitelist.xml b/ReCaptchaVersion2Checkbox/etc/csp_whitelist.xml index 8a07aca9..ba72b07e 100644 --- a/ReCaptchaVersion2Checkbox/etc/csp_whitelist.xml +++ b/ReCaptchaVersion2Checkbox/etc/csp_whitelist.xml @@ -1,8 +1,8 @@ <?xml version="1.0" encoding="UTF-8"?> <!-- /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ --> <csp_whitelist xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Csp:etc/csp_whitelist.xsd"> diff --git a/ReCaptchaVersion2Checkbox/etc/di.xml b/ReCaptchaVersion2Checkbox/etc/di.xml index 02876b29..9231152e 100644 --- a/ReCaptchaVersion2Checkbox/etc/di.xml +++ b/ReCaptchaVersion2Checkbox/etc/di.xml @@ -1,10 +1,10 @@ <?xml version="1.0"?> <!-- /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ - --> +--> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> <type name="Magento\Config\Model\Config\TypePool"> diff --git a/ReCaptchaVersion2Checkbox/etc/frontend/di.xml b/ReCaptchaVersion2Checkbox/etc/frontend/di.xml index 249dc802..7c73f41b 100644 --- a/ReCaptchaVersion2Checkbox/etc/frontend/di.xml +++ b/ReCaptchaVersion2Checkbox/etc/frontend/di.xml @@ -1,10 +1,10 @@ <?xml version="1.0"?> <!-- /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ - --> +--> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> <type name="Magento\ReCaptchaUi\Model\UiConfigResolver"> diff --git a/ReCaptchaVersion2Checkbox/etc/module.xml b/ReCaptchaVersion2Checkbox/etc/module.xml index 19a79a04..d48881a0 100644 --- a/ReCaptchaVersion2Checkbox/etc/module.xml +++ b/ReCaptchaVersion2Checkbox/etc/module.xml @@ -1,10 +1,10 @@ <?xml version="1.0"?> <!-- /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ - --> +--> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd"> <module name="Magento_ReCaptchaVersion2Checkbox"/> diff --git a/ReCaptchaVersion2Checkbox/registration.php b/ReCaptchaVersion2Checkbox/registration.php index 725347e0..4ac8b220 100644 --- a/ReCaptchaVersion2Checkbox/registration.php +++ b/ReCaptchaVersion2Checkbox/registration.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); \Magento\Framework\Component\ComponentRegistrar::register( diff --git a/ReCaptchaVersion2Invisible/Model/Adminhtml/UiConfigProvider.php b/ReCaptchaVersion2Invisible/Model/Adminhtml/UiConfigProvider.php index c592e70d..b9b9f562 100644 --- a/ReCaptchaVersion2Invisible/Model/Adminhtml/UiConfigProvider.php +++ b/ReCaptchaVersion2Invisible/Model/Adminhtml/UiConfigProvider.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\ReCaptchaVersion2Invisible\Model\Adminhtml; diff --git a/ReCaptchaVersion2Invisible/Model/Adminhtml/ValidationConfigProvider.php b/ReCaptchaVersion2Invisible/Model/Adminhtml/ValidationConfigProvider.php index c6184081..c5b50568 100644 --- a/ReCaptchaVersion2Invisible/Model/Adminhtml/ValidationConfigProvider.php +++ b/ReCaptchaVersion2Invisible/Model/Adminhtml/ValidationConfigProvider.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\ReCaptchaVersion2Invisible\Model\Adminhtml; diff --git a/ReCaptchaVersion2Invisible/Model/Config.php b/ReCaptchaVersion2Invisible/Model/Config.php new file mode 100644 index 00000000..d5ae6535 --- /dev/null +++ b/ReCaptchaVersion2Invisible/Model/Config.php @@ -0,0 +1,100 @@ +<?php +/** + * Copyright 2024 Adobe + * All Rights Reserved. + */ + +declare(strict_types=1); + +namespace Magento\ReCaptchaVersion2Invisible\Model; + +use Magento\Framework\ObjectManager\ResetAfterRequestInterface; +use Magento\ReCaptchaVersion2Invisible\Model\Frontend\UiConfigProvider; +use Magento\ReCaptchaWebapiGraphQl\Model\Adapter\ReCaptchaConfigInterface; + +class Config implements ReCaptchaConfigInterface, ResetAfterRequestInterface +{ + /** + * @var array + */ + private array $uiConfig = []; + + /** + * @param UiConfigProvider $uiConfigProvider + */ + public function __construct( + private readonly UiConfigProvider $uiConfigProvider, + ) { + } + + /** + * Get website's Google API public key + * + * @return string + */ + public function getWebsiteKey(): string + { + return $this->getUiConfig()['rendering']['sitekey'] ?? ''; + } + + /** + * ReCaptcha v2 Invisible does not provide configuration for minimum score + * + * @return null|float + */ + public function getMinimumScore(): ?float + { + return null; + } + + /** + * Get configured captcha's badge position + * + * @return string + */ + public function getBadgePosition(): string + { + return $this->getUiConfig()['rendering']['badge'] ?? ''; + } + + /** + * Get configured captcha's theme + * + * @return string + */ + public function getTheme(): string + { + return $this->getUiConfig()['rendering']['theme'] ?? ''; + } + + /** + * Get code of language to send notifications + * + * @return string + */ + public function getLanguageCode(): string + { + return $this->getUiConfig()['rendering']['hl'] ?? ''; + } + + /** + * Get front-end's UI configurations + * + * @return array + */ + private function getUiConfig(): array + { + if (empty($this->uiConfig)) { + $this->uiConfig = $this->uiConfigProvider->get() ?? []; + } + return $this->uiConfig; + } + + /** + * @inheritDoc + */ + public function _resetState(): void + { + $this->uiConfig = []; + } +} diff --git a/ReCaptchaVersion2Invisible/Model/Frontend/UiConfigProvider.php b/ReCaptchaVersion2Invisible/Model/Frontend/UiConfigProvider.php index 4e84c44f..d9b648b2 100644 --- a/ReCaptchaVersion2Invisible/Model/Frontend/UiConfigProvider.php +++ b/ReCaptchaVersion2Invisible/Model/Frontend/UiConfigProvider.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\ReCaptchaVersion2Invisible\Model\Frontend; diff --git a/ReCaptchaVersion2Invisible/Model/Frontend/ValidationConfigProvider.php b/ReCaptchaVersion2Invisible/Model/Frontend/ValidationConfigProvider.php index f1208ba0..b07710a0 100644 --- a/ReCaptchaVersion2Invisible/Model/Frontend/ValidationConfigProvider.php +++ b/ReCaptchaVersion2Invisible/Model/Frontend/ValidationConfigProvider.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\ReCaptchaVersion2Invisible\Model\Frontend; diff --git a/ReCaptchaVersion2Invisible/README.md b/ReCaptchaVersion2Invisible/README.md index 76cdb3b6..5ba66a6a 100644 --- a/ReCaptchaVersion2Invisible/README.md +++ b/ReCaptchaVersion2Invisible/README.md @@ -1,7 +1,7 @@ -# Magento reCAPTCHA +# Magento_ReCaptchaVersion2Invisible module -Google reCAPTCHA ensures that a human being, rather than a computer (or “bot”), is interacting with your website. Unlike the standard Magento CAPTCHA, Google reCAPTCHA provides enhanced security with a selection of different display options and methods. Additional website traffic information is available in the dashboard of your Google reCAPTCHA account. +Google reCAPTCHA ensures that a human being, rather than a computer (or "bot"), is interacting with your website. Unlike the standard Magento CAPTCHA, Google reCAPTCHA provides enhanced security with a selection of different display options and methods. Additional website traffic information is available in the dashboard of your Google reCAPTCHA account. This module provides the reCAPTCHA implementation for the V2 Invisible variation. -For more information please visit the [Magento document for reCAPTCHA](https://docs.magento.com/user-guide/stores/security-google-recaptcha.html). +For more information, see the [reCAPTCHA documentation](https://experienceleague.adobe.com/en/docs/commerce-admin/systems/security/captcha/security-google-recaptcha). diff --git a/ReCaptchaVersion2Invisible/composer.json b/ReCaptchaVersion2Invisible/composer.json index e42e7b25..cdd6a94b 100644 --- a/ReCaptchaVersion2Invisible/composer.json +++ b/ReCaptchaVersion2Invisible/composer.json @@ -2,11 +2,12 @@ "name": "magento/module-re-captcha-version-2-invisible", "description": "Google reCAPTCHA integration for Magento2", "require": { - "php": "~7.4.0||~8.0.0||~8.1.0", + "php": "~8.2.0||~8.3.0||~8.4.0", "magento/framework": "*", "magento/module-store": "*", "magento/module-re-captcha-ui": "*", - "magento/module-re-captcha-validation-api": "*" + "magento/module-re-captcha-validation-api": "*", + "magento/module-re-captcha-webapi-graph-ql": "*" }, "suggest": { "magento/module-config": "*", diff --git a/ReCaptchaVersion2Invisible/etc/adminhtml/di.xml b/ReCaptchaVersion2Invisible/etc/adminhtml/di.xml index 74ec785d..71cd727e 100644 --- a/ReCaptchaVersion2Invisible/etc/adminhtml/di.xml +++ b/ReCaptchaVersion2Invisible/etc/adminhtml/di.xml @@ -1,10 +1,10 @@ <?xml version="1.0"?> <!-- /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ - --> +--> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> <type name="Magento\ReCaptchaUi\Model\UiConfigResolver"> diff --git a/ReCaptchaVersion2Invisible/etc/adminhtml/system.xml b/ReCaptchaVersion2Invisible/etc/adminhtml/system.xml index 7a36dac7..26afe29d 100644 --- a/ReCaptchaVersion2Invisible/etc/adminhtml/system.xml +++ b/ReCaptchaVersion2Invisible/etc/adminhtml/system.xml @@ -1,10 +1,10 @@ <?xml version="1.0"?> <!-- /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ - --> +--> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd"> <system> diff --git a/ReCaptchaVersion2Invisible/etc/config.xml b/ReCaptchaVersion2Invisible/etc/config.xml index 6d768a18..d0f2deef 100644 --- a/ReCaptchaVersion2Invisible/etc/config.xml +++ b/ReCaptchaVersion2Invisible/etc/config.xml @@ -1,10 +1,10 @@ <?xml version="1.0"?> <!-- /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ - --> +--> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Store:etc/config.xsd"> <default> diff --git a/ReCaptchaVersion2Invisible/etc/csp_whitelist.xml b/ReCaptchaVersion2Invisible/etc/csp_whitelist.xml index 8a07aca9..ba72b07e 100644 --- a/ReCaptchaVersion2Invisible/etc/csp_whitelist.xml +++ b/ReCaptchaVersion2Invisible/etc/csp_whitelist.xml @@ -1,8 +1,8 @@ <?xml version="1.0" encoding="UTF-8"?> <!-- /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ --> <csp_whitelist xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Csp:etc/csp_whitelist.xsd"> diff --git a/ReCaptchaVersion2Invisible/etc/di.xml b/ReCaptchaVersion2Invisible/etc/di.xml index 83aed2c3..f9e6a92b 100644 --- a/ReCaptchaVersion2Invisible/etc/di.xml +++ b/ReCaptchaVersion2Invisible/etc/di.xml @@ -1,10 +1,10 @@ <?xml version="1.0"?> <!-- /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ - --> +--> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> <type name="Magento\Config\Model\Config\TypePool"> diff --git a/ReCaptchaVersion2Invisible/etc/frontend/di.xml b/ReCaptchaVersion2Invisible/etc/frontend/di.xml index 451a98eb..c2dd8bed 100644 --- a/ReCaptchaVersion2Invisible/etc/frontend/di.xml +++ b/ReCaptchaVersion2Invisible/etc/frontend/di.xml @@ -1,10 +1,10 @@ <?xml version="1.0"?> <!-- /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ - --> +--> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> <type name="Magento\ReCaptchaUi\Model\UiConfigResolver"> diff --git a/ReCaptchaVersion2Invisible/etc/module.xml b/ReCaptchaVersion2Invisible/etc/module.xml index 348088be..0f1ef47e 100644 --- a/ReCaptchaVersion2Invisible/etc/module.xml +++ b/ReCaptchaVersion2Invisible/etc/module.xml @@ -1,10 +1,10 @@ <?xml version="1.0"?> <!-- /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ - --> +--> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd"> <module name="Magento_ReCaptchaVersion2Invisible"/> diff --git a/ReCaptchaVersion2Invisible/registration.php b/ReCaptchaVersion2Invisible/registration.php index 69a4a5c8..f6eac07d 100644 --- a/ReCaptchaVersion2Invisible/registration.php +++ b/ReCaptchaVersion2Invisible/registration.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); \Magento\Framework\Component\ComponentRegistrar::register( diff --git a/ReCaptchaVersion3Invisible/Model/Adminhtml/UiConfigProvider.php b/ReCaptchaVersion3Invisible/Model/Adminhtml/UiConfigProvider.php index 565b84df..6635d360 100644 --- a/ReCaptchaVersion3Invisible/Model/Adminhtml/UiConfigProvider.php +++ b/ReCaptchaVersion3Invisible/Model/Adminhtml/UiConfigProvider.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\ReCaptchaVersion3Invisible\Model\Adminhtml; diff --git a/ReCaptchaVersion3Invisible/Model/Adminhtml/ValidationConfigProvider.php b/ReCaptchaVersion3Invisible/Model/Adminhtml/ValidationConfigProvider.php index b93bc4f0..03ae4874 100644 --- a/ReCaptchaVersion3Invisible/Model/Adminhtml/ValidationConfigProvider.php +++ b/ReCaptchaVersion3Invisible/Model/Adminhtml/ValidationConfigProvider.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\ReCaptchaVersion3Invisible\Model\Adminhtml; diff --git a/ReCaptchaVersion3Invisible/Model/Config.php b/ReCaptchaVersion3Invisible/Model/Config.php new file mode 100644 index 00000000..7f86bfc8 --- /dev/null +++ b/ReCaptchaVersion3Invisible/Model/Config.php @@ -0,0 +1,148 @@ +<?php +/** + * Copyright 2023 Adobe + * All Rights Reserved. + */ + +declare(strict_types=1); + +namespace Magento\ReCaptchaVersion3Invisible\Model; + +use Magento\Framework\ObjectManager\ResetAfterRequestInterface; +use Magento\ReCaptchaValidationApi\Api\Data\ValidationConfigInterface; +use Magento\ReCaptchaVersion3Invisible\Model\Frontend\UiConfigProvider; +use Magento\ReCaptchaVersion3Invisible\Model\Frontend\ValidationConfigProvider; +use Magento\ReCaptchaWebapiGraphQl\Model\Adapter\ReCaptchaConfigInterface; + +class Config implements ReCaptchaConfigInterface, ResetAfterRequestInterface +{ + /** + * @var float|null + */ + private ?float $minimumScore = null; + + /** + * @var array + */ + private array $uiConfig = []; + + /** + * @var ValidationConfigInterface|null + */ + private ?ValidationConfigInterface $validationConfig = null; + + /** + * @param UiConfigProvider $uiConfigProvider + * @param ValidationConfigProvider $validationConfigProvider + * @param array $formTypes + */ + public function __construct( + private readonly UiConfigProvider $uiConfigProvider, + private readonly ValidationConfigProvider $validationConfigProvider, + private readonly array $formTypes + ) { + } + + /** + * Get website's Google API public key + * + * @return string + */ + public function getWebsiteKey(): string + { + return $this->getUiConfig()['rendering']['sitekey'] ?? ''; + } + + /** + * Get configured minimum score value + * + * @return float|null + */ + public function getMinimumScore(): ?float + { + if (!$this->minimumScore) { + $validationProvider = $this->validationConfigProvider->get(); + if ($validationProvider->getExtensionAttributes() === null) { + return $this->minimumScore; + } + $this->minimumScore = $validationProvider->getExtensionAttributes()->getScoreThreshold(); + } + return $this->minimumScore; + } + + /** + * Get configured captcha's badge position + * + * @return string + */ + public function getBadgePosition(): string + { + return $this->getUiConfig()['rendering']['badge'] ?? ''; + } + + /** + * Get code of language to send notifications + * + * @return string + */ + public function getLanguageCode(): string + { + return $this->getUiConfig()['rendering']['hl'] ?? ''; + } + + /** + * Get configured captcha's theme + * + * @return string + */ + public function getTheme(): string + { + return $this->getUiConfig()['rendering']['theme'] ?? ''; + } + + /** + * Get ReCaptchaV3's available form types + * + * @return array + */ + public function getFormTypes(): array + { + return $this->formTypes; + } + + /** + * Get front-end's validation configurations + * + * @return ValidationConfigInterface + */ + public function getValidationConfig(): ValidationConfigInterface + { + if (!$this->validationConfig) { + $this->validationConfig = $this->validationConfigProvider->get(); + } + return $this->validationConfig; + } + + /** + * Get front-end's UI configurations + * + * @return array + */ + private function getUiConfig(): array + { + if (empty($this->uiConfig)) { + $this->uiConfig = $this->uiConfigProvider->get() ?? []; + } + return $this->uiConfig; + } + + /** + * @inheritDoc + */ + public function _resetState(): void + { + $this->uiConfig = []; + $this->minimumScore = null; + $this->validationConfig = null; + } +} diff --git a/ReCaptchaVersion3Invisible/Model/Frontend/UiConfigProvider.php b/ReCaptchaVersion3Invisible/Model/Frontend/UiConfigProvider.php index 9b10ab97..4d56c407 100644 --- a/ReCaptchaVersion3Invisible/Model/Frontend/UiConfigProvider.php +++ b/ReCaptchaVersion3Invisible/Model/Frontend/UiConfigProvider.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\ReCaptchaVersion3Invisible\Model\Frontend; diff --git a/ReCaptchaVersion3Invisible/Model/Frontend/ValidationConfigProvider.php b/ReCaptchaVersion3Invisible/Model/Frontend/ValidationConfigProvider.php index 3ff0fdf5..915ab195 100644 --- a/ReCaptchaVersion3Invisible/Model/Frontend/ValidationConfigProvider.php +++ b/ReCaptchaVersion3Invisible/Model/Frontend/ValidationConfigProvider.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\ReCaptchaVersion3Invisible\Model\Frontend; diff --git a/ReCaptchaVersion3Invisible/README.md b/ReCaptchaVersion3Invisible/README.md index f1500134..dc429f38 100644 --- a/ReCaptchaVersion3Invisible/README.md +++ b/ReCaptchaVersion3Invisible/README.md @@ -1,7 +1,7 @@ -# Magento reCAPTCHA +# Magento_ReCaptchaVersion3Invisible module -Google reCAPTCHA ensures that a human being, rather than a computer (or “bot”), is interacting with your website. Unlike the standard Magento CAPTCHA, Google reCAPTCHA provides enhanced security with a selection of different display options and methods. Additional website traffic information is available in the dashboard of your Google reCAPTCHA account. +Google reCAPTCHA ensures that a human being, rather than a computer (or "bot"), is interacting with your website. Unlike the standard Magento CAPTCHA, Google reCAPTCHA provides enhanced security with a selection of different display options and methods. Additional website traffic information is available in the dashboard of your Google reCAPTCHA account. This module provides the reCAPTCHA implementation for the V3 Invisible variation. -For more information please visit the [Magento document for reCAPTCHA](https://docs.magento.com/user-guide/stores/security-google-recaptcha.html). +For more information, see the [reCAPTCHA documentation](https://experienceleague.adobe.com/en/docs/commerce-admin/systems/security/captcha/security-google-recaptcha). diff --git a/ReCaptchaVersion3Invisible/composer.json b/ReCaptchaVersion3Invisible/composer.json index 84917125..518af1c1 100644 --- a/ReCaptchaVersion3Invisible/composer.json +++ b/ReCaptchaVersion3Invisible/composer.json @@ -2,11 +2,12 @@ "name": "magento/module-re-captcha-version-3-invisible", "description": "Google reCAPTCHA integration for Magento2", "require": { - "php": "~7.4.0||~8.0.0||~8.1.0", + "php": "~8.2.0||~8.3.0||~8.4.0", "magento/framework": "*", "magento/module-store": "*", "magento/module-re-captcha-ui": "*", - "magento/module-re-captcha-validation-api": "*" + "magento/module-re-captcha-validation-api": "*", + "magento/module-re-captcha-webapi-graph-ql": "*" }, "suggest": { "magento/module-config": "*", diff --git a/ReCaptchaVersion3Invisible/etc/adminhtml/di.xml b/ReCaptchaVersion3Invisible/etc/adminhtml/di.xml index 66bd1dee..48373702 100644 --- a/ReCaptchaVersion3Invisible/etc/adminhtml/di.xml +++ b/ReCaptchaVersion3Invisible/etc/adminhtml/di.xml @@ -1,10 +1,10 @@ <?xml version="1.0"?> <!-- /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ - --> +--> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> <type name="Magento\ReCaptchaUi\Model\UiConfigResolver"> diff --git a/ReCaptchaVersion3Invisible/etc/adminhtml/system.xml b/ReCaptchaVersion3Invisible/etc/adminhtml/system.xml index b2c48c9d..a3c02dfa 100644 --- a/ReCaptchaVersion3Invisible/etc/adminhtml/system.xml +++ b/ReCaptchaVersion3Invisible/etc/adminhtml/system.xml @@ -1,10 +1,10 @@ <?xml version="1.0"?> <!-- /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ - --> +--> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd"> <system> diff --git a/ReCaptchaVersion3Invisible/etc/config.xml b/ReCaptchaVersion3Invisible/etc/config.xml index 7bf2f131..22fda62c 100644 --- a/ReCaptchaVersion3Invisible/etc/config.xml +++ b/ReCaptchaVersion3Invisible/etc/config.xml @@ -1,10 +1,10 @@ <?xml version="1.0"?> <!-- /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ - --> +--> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Store:etc/config.xsd"> <default> diff --git a/ReCaptchaVersion3Invisible/etc/csp_whitelist.xml b/ReCaptchaVersion3Invisible/etc/csp_whitelist.xml index 8a07aca9..ba72b07e 100644 --- a/ReCaptchaVersion3Invisible/etc/csp_whitelist.xml +++ b/ReCaptchaVersion3Invisible/etc/csp_whitelist.xml @@ -1,8 +1,8 @@ <?xml version="1.0" encoding="UTF-8"?> <!-- /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ --> <csp_whitelist xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Csp:etc/csp_whitelist.xsd"> diff --git a/ReCaptchaVersion3Invisible/etc/di.xml b/ReCaptchaVersion3Invisible/etc/di.xml index d63385c4..43cbc98b 100644 --- a/ReCaptchaVersion3Invisible/etc/di.xml +++ b/ReCaptchaVersion3Invisible/etc/di.xml @@ -1,10 +1,10 @@ <?xml version="1.0"?> <!-- /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ - --> +--> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> <type name="Magento\Config\Model\Config\TypePool"> @@ -84,4 +84,20 @@ </argument> </arguments> </type> + <type name="Magento\ReCaptchaVersion3Invisible\Model\Config"> + <arguments> + <argument name="formTypes" xsi:type="array"> + <item name="place_order" xsi:type="string">place_order</item> + <item name="contact" xsi:type="string">contact</item> + <item name="customer_forgot_password" xsi:type="string">customer_forgot_password</item> + <item name="customer_edit" xsi:type="string">customer_edit</item> + <item name="customer_login" xsi:type="string">customer_login</item> + <item name="customer_create" xsi:type="string">customer_create</item> + <item name="newsletter" xsi:type="string">newsletter</item> + <item name="product_review" xsi:type="string">product_review</item> + <item name="sendfriend" xsi:type="string">sendfriend</item> + <item name="braintree" xsi:type="string">braintree</item> + </argument> + </arguments> + </type> </config> diff --git a/ReCaptchaVersion3Invisible/etc/extension_attributes.xml b/ReCaptchaVersion3Invisible/etc/extension_attributes.xml index b947b525..135b8768 100644 --- a/ReCaptchaVersion3Invisible/etc/extension_attributes.xml +++ b/ReCaptchaVersion3Invisible/etc/extension_attributes.xml @@ -1,8 +1,8 @@ <?xml version="1.0"?> <!-- /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Api/etc/extension_attributes.xsd"> diff --git a/ReCaptchaVersion3Invisible/etc/frontend/di.xml b/ReCaptchaVersion3Invisible/etc/frontend/di.xml index 9f2d94b8..42ca15d8 100644 --- a/ReCaptchaVersion3Invisible/etc/frontend/di.xml +++ b/ReCaptchaVersion3Invisible/etc/frontend/di.xml @@ -1,10 +1,10 @@ <?xml version="1.0"?> <!-- /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ - --> +--> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> <type name="Magento\ReCaptchaUi\Model\UiConfigResolver"> diff --git a/ReCaptchaVersion3Invisible/etc/module.xml b/ReCaptchaVersion3Invisible/etc/module.xml index 84efbd88..c59e7134 100644 --- a/ReCaptchaVersion3Invisible/etc/module.xml +++ b/ReCaptchaVersion3Invisible/etc/module.xml @@ -1,10 +1,10 @@ <?xml version="1.0"?> <!-- /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ - --> +--> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd"> <module name="Magento_ReCaptchaVersion3Invisible"/> diff --git a/ReCaptchaVersion3Invisible/registration.php b/ReCaptchaVersion3Invisible/registration.php index a69eb3ad..b4e05642 100644 --- a/ReCaptchaVersion3Invisible/registration.php +++ b/ReCaptchaVersion3Invisible/registration.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); \Magento\Framework\Component\ComponentRegistrar::register( diff --git a/ReCaptchaWebapiApi/Api/Data/EndpointInterface.php b/ReCaptchaWebapiApi/Api/Data/EndpointInterface.php index af8f9894..8458ee2d 100644 --- a/ReCaptchaWebapiApi/Api/Data/EndpointInterface.php +++ b/ReCaptchaWebapiApi/Api/Data/EndpointInterface.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\ReCaptchaWebapiApi\Api\Data; diff --git a/ReCaptchaWebapiApi/Api/WebapiValidationConfigProviderInterface.php b/ReCaptchaWebapiApi/Api/WebapiValidationConfigProviderInterface.php index ff4ce5eb..32b2209e 100644 --- a/ReCaptchaWebapiApi/Api/WebapiValidationConfigProviderInterface.php +++ b/ReCaptchaWebapiApi/Api/WebapiValidationConfigProviderInterface.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\ReCaptchaWebapiApi\Api; diff --git a/ReCaptchaWebapiApi/Model/CompositeWebapiValidationConfigProvider.php b/ReCaptchaWebapiApi/Model/CompositeWebapiValidationConfigProvider.php index 9f56a67d..02d24886 100644 --- a/ReCaptchaWebapiApi/Model/CompositeWebapiValidationConfigProvider.php +++ b/ReCaptchaWebapiApi/Model/CompositeWebapiValidationConfigProvider.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\ReCaptchaWebapiApi\Model; diff --git a/ReCaptchaWebapiApi/Model/Data/Endpoint.php b/ReCaptchaWebapiApi/Model/Data/Endpoint.php index dcc8d0d7..9063d733 100644 --- a/ReCaptchaWebapiApi/Model/Data/Endpoint.php +++ b/ReCaptchaWebapiApi/Model/Data/Endpoint.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\ReCaptchaWebapiApi\Model\Data; diff --git a/ReCaptchaWebapiApi/README.md b/ReCaptchaWebapiApi/README.md index 13640a31..526047f8 100644 --- a/ReCaptchaWebapiApi/README.md +++ b/ReCaptchaWebapiApi/README.md @@ -1,7 +1,7 @@ -# Magento reCAPTCHA +# Magento_ReCaptchaWebapiApi module -Google reCAPTCHA ensures that a human being, rather than a computer (or “bot”), is interacting with your website. Unlike the standard Magento CAPTCHA, Google reCAPTCHA provides enhanced security with a selection of different display options and methods. Additional website traffic information is available in the dashboard of your Google reCAPTCHA account. +Google reCAPTCHA ensures that a human being, rather than a computer (or "bot"), is interacting with your website. Unlike the standard Magento CAPTCHA, Google reCAPTCHA provides enhanced security with a selection of different display options and methods. Additional website traffic information is available in the dashboard of your Google reCAPTCHA account. This module provides the service contracts related to the base reCAPTCHA implementation. -For more information please visit the [Magento document for reCAPTCHA](https://docs.magento.com/user-guide/stores/security-google-recaptcha.html). +For more information, see the [reCAPTCHA documentation](https://experienceleague.adobe.com/en/docs/commerce-admin/systems/security/captcha/security-google-recaptcha). diff --git a/ReCaptchaWebapiApi/composer.json b/ReCaptchaWebapiApi/composer.json index 8f8ead52..619cde30 100644 --- a/ReCaptchaWebapiApi/composer.json +++ b/ReCaptchaWebapiApi/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-re-captcha-webapi-api", "description": "Google reCAPTCHA integration for Magento2", "require": { - "php": "~7.4.0||~8.0.0||~8.1.0", + "php": "~8.2.0||~8.3.0||~8.4.0", "magento/framework": "*", "magento/module-re-captcha-validation-api": "*" }, diff --git a/ReCaptchaWebapiApi/etc/di.xml b/ReCaptchaWebapiApi/etc/di.xml index 998403b1..387c63e6 100644 --- a/ReCaptchaWebapiApi/etc/di.xml +++ b/ReCaptchaWebapiApi/etc/di.xml @@ -1,11 +1,10 @@ <?xml version="1.0"?> <!-- /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ --> - <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> <preference for="Magento\ReCaptchaWebapiApi\Api\WebapiValidationConfigProviderInterface" type="Magento\ReCaptchaWebapiApi\Model\CompositeWebapiValidationConfigProvider" /> </config> diff --git a/ReCaptchaWebapiApi/etc/module.xml b/ReCaptchaWebapiApi/etc/module.xml index 18ead477..2e2381f0 100644 --- a/ReCaptchaWebapiApi/etc/module.xml +++ b/ReCaptchaWebapiApi/etc/module.xml @@ -1,10 +1,10 @@ <?xml version="1.0"?> <!-- /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ - --> +--> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd"> <module name="Magento_ReCaptchaWebapiApi"/> diff --git a/ReCaptchaWebapiApi/registration.php b/ReCaptchaWebapiApi/registration.php index 5fb987de..2e4296a5 100644 --- a/ReCaptchaWebapiApi/registration.php +++ b/ReCaptchaWebapiApi/registration.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); \Magento\Framework\Component\ComponentRegistrar::register( diff --git a/ReCaptchaWebapiGraphQl/Model/Adapter/ReCaptchaConfigInterface.php b/ReCaptchaWebapiGraphQl/Model/Adapter/ReCaptchaConfigInterface.php new file mode 100644 index 00000000..ed51c5ac --- /dev/null +++ b/ReCaptchaWebapiGraphQl/Model/Adapter/ReCaptchaConfigInterface.php @@ -0,0 +1,51 @@ +<?php +/** + * Copyright 2024 Adobe + * All Rights Reserved. + */ + +declare(strict_types=1); + +namespace Magento\ReCaptchaWebapiGraphQl\Model\Adapter; + +/** + * Interface for ReCaptcha config adapters. Used in Config adapters which retrieve + * configuration settings for different ReCaptcha types. + */ +interface ReCaptchaConfigInterface +{ + /** + * Get website's Google API public key + * + * @return string + */ + public function getWebsiteKey(): string; + + /** + * Get configured captcha's theme + * + * @return string + */ + public function getTheme(): string; + + /** + * Get code of language to send notifications + * + * @return string + */ + public function getLanguageCode(): string; + + /** + * Returns minimum score setting + * + * @return float|null + */ + public function getMinimumScore(): ?float; + + /** + * Returns badge_position setting + * + * @return string + */ + public function getBadgePosition(): string; +} diff --git a/ReCaptchaWebapiGraphQl/Model/Resolver/ReCaptchaFormConfig.php b/ReCaptchaWebapiGraphQl/Model/Resolver/ReCaptchaFormConfig.php new file mode 100644 index 00000000..d7d68b48 --- /dev/null +++ b/ReCaptchaWebapiGraphQl/Model/Resolver/ReCaptchaFormConfig.php @@ -0,0 +1,89 @@ +<?php +/** + * Copyright 2024 Adobe + * All Rights Reserved. + */ + +declare(strict_types=1); + +namespace Magento\ReCaptchaWebapiGraphQl\Model\Resolver; + +use Magento\Framework\GraphQl\Config\Element\Field; +use Magento\Framework\GraphQl\Query\ResolverInterface; +use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; +use Magento\Framework\GraphQl\Exception\GraphQlInputException; +use Magento\ReCaptchaFrontendUi\Model\CaptchaTypeResolver; +use Magento\ReCaptchaFrontendUi\Model\ErrorMessageConfig; + +/** + * Query returning reCaptcha configuration details for selected form type + */ +class ReCaptchaFormConfig implements ResolverInterface +{ + /** + * @var array + */ + private array $reCaptchaConfigProviders; + + /** + * @var array + */ + private array $formTypes; + + /** + * @param CaptchaTypeResolver $captchaTypeResolver + * @param ErrorMessageConfig $errorMessageConfig + * @param array $providers + * @param array $formTypes + */ + public function __construct( + private readonly CaptchaTypeResolver $captchaTypeResolver, + private readonly ErrorMessageConfig $errorMessageConfig, + array $providers = [], + array $formTypes = [] + ) { + $this->reCaptchaConfigProviders = $providers; + $this->formTypes = $formTypes; + } + + /** + * @inheritDoc + */ + public function resolve( + Field $field, + $context, + ResolveInfo $info, + ?array $value = null, + ?array $args = null, + ) { + try { + $captchaType = $this->captchaTypeResolver->getCaptchaTypeFor($this->formTypes[$args['formType']]); + + if (!$captchaType) { + return [ + 'is_enabled' => false, + 'configurations' => null + ]; + } + + $reCaptchaConfigProvider = $this->reCaptchaConfigProviders[$captchaType]; + return [ + 'is_enabled' => true, + 'configurations' => [ + 're_captcha_type' => mb_strtoupper($captchaType), + 'website_key' => $reCaptchaConfigProvider->getWebsiteKey(), + 'minimum_score' => $reCaptchaConfigProvider->getMinimumScore(), + 'badge_position' => $reCaptchaConfigProvider->getBadgePosition(), + 'theme' => $reCaptchaConfigProvider->getTheme(), + 'language_code' => $reCaptchaConfigProvider->getLanguageCode(), + 'validation_failure_message' => $this->errorMessageConfig->getValidationFailureMessage(), + 'technical_failure_message' => $this->errorMessageConfig->getTechnicalFailureMessage() + ] + ]; + } catch (\Exception $e) { + throw new GraphQlInputException( + __('Configuration for provided captcha type can not be retrieved.') + ); + } + } +} diff --git a/ReCaptchaWebapiGraphQl/Model/Resolver/ReCaptchaV3.php b/ReCaptchaWebapiGraphQl/Model/Resolver/ReCaptchaV3.php new file mode 100644 index 00000000..1503a252 --- /dev/null +++ b/ReCaptchaWebapiGraphQl/Model/Resolver/ReCaptchaV3.php @@ -0,0 +1,147 @@ +<?php +/** + * Copyright 2023 Adobe + * All Rights Reserved. + */ + +declare(strict_types=1); + +namespace Magento\ReCaptchaWebapiGraphQl\Model\Resolver; + +use Magento\Framework\Exception\InputException; +use Magento\Framework\GraphQl\Config\Element\Field; +use Magento\Framework\GraphQl\Query\ResolverInterface; +use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; +use Magento\Framework\ObjectManager\ResetAfterRequestInterface; +use Magento\ReCaptchaFrontendUi\Model\CaptchaTypeResolver; +use Magento\ReCaptchaFrontendUi\Model\ErrorMessageConfig; +use Magento\ReCaptchaVersion3Invisible\Model\Config; + +class ReCaptchaV3 implements ResolverInterface, ResetAfterRequestInterface +{ + private const RECAPTCHA_TYPE = 'recaptcha_v3'; + + /** + * @var bool|null + */ + private ?bool $isEnabled = null; + + /** + * @var array + */ + private array $forms = []; + + /** + * @var Config + */ + private Config $reCaptchaV3Config; + + /** + * @var CaptchaTypeResolver + */ + private CaptchaTypeResolver $captchaTypeResolver; + + /** + * @var string|null + */ + private ?string $failureMessage = null; + + /** + * @var ErrorMessageConfig $errorMessageConfig + */ + private ErrorMessageConfig $errorMessageConfig; + + /** + * @param Config $reCaptchaV3Config + * @param CaptchaTypeResolver $captchaTypeResolver + * @param ErrorMessageConfig $errorMessageConfig + */ + public function __construct( + Config $reCaptchaV3Config, + CaptchaTypeResolver $captchaTypeResolver, + ErrorMessageConfig $errorMessageConfig + ) { + $this->reCaptchaV3Config = $reCaptchaV3Config; + $this->captchaTypeResolver = $captchaTypeResolver; + $this->errorMessageConfig = $errorMessageConfig; + } + + /** + * @inheritDoc + */ + public function resolve( + Field $field, + $context, + ResolveInfo $info, + ?array $value = null, + ?array $args = null + ) { + return [ + 'is_enabled' => $this->isEnabled(), + 'website_key' => $this->reCaptchaV3Config->getWebsiteKey(), + 'minimum_score' => $this->reCaptchaV3Config->getMinimumScore(), + 'badge_position' => $this->reCaptchaV3Config->getBadgePosition(), + 'language_code' => $this->reCaptchaV3Config->getLanguageCode(), + 'failure_message' => $this->getFailureMessage(), + 'forms' => $this->getEnumFormTypes(), + 'theme' => $this->reCaptchaV3Config->getTheme() + ]; + } + + /** + * Get whether service has all the required settings set up to be enabled or not + * + * @return bool + * @throws InputException + */ + public function isEnabled(): bool + { + if ($this->isEnabled === null) { + $this->isEnabled = $this->reCaptchaV3Config->getValidationConfig()->getPrivateKey() && + !empty($this->reCaptchaV3Config->getWebsiteKey()) && + !empty($this->getEnumFormTypes()); + } + return $this->isEnabled; + } + + /** + * Get form keys that are configured to ReCaptcha V3 + * + * @return array + * @throws InputException + */ + private function getEnumFormTypes(): array + { + $forms = []; + if (empty($this->forms)) { + foreach ($this->reCaptchaV3Config->getFormTypes() as $formType) { + if ($this->captchaTypeResolver->getCaptchaTypeFor($formType) === self::RECAPTCHA_TYPE) { + $forms[] = $formType; + } + } + } + return array_map('strtoupper', $forms); + } + + /** + * Get configured message sent in case of failure + * + * @return string + */ + public function getFailureMessage(): string + { + if (!$this->failureMessage) { + $this->failureMessage = $this->errorMessageConfig->getValidationFailureMessage(); + } + return $this->failureMessage; + } + + /** + * @inheritDoc + */ + public function _resetState(): void + { + $this->isEnabled = null; + $this->failureMessage = null; + } +} diff --git a/ReCaptchaWebapiGraphQl/Plugin/GraphQlValidator.php b/ReCaptchaWebapiGraphQl/Plugin/GraphQlValidator.php index 60ec2e2e..12b79669 100644 --- a/ReCaptchaWebapiGraphQl/Plugin/GraphQlValidator.php +++ b/ReCaptchaWebapiGraphQl/Plugin/GraphQlValidator.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\ReCaptchaWebapiGraphQl\Plugin; diff --git a/ReCaptchaWebapiGraphQl/Plugin/ValidationOverrider.php b/ReCaptchaWebapiGraphQl/Plugin/ValidationOverrider.php index cb27ad14..70779960 100644 --- a/ReCaptchaWebapiGraphQl/Plugin/ValidationOverrider.php +++ b/ReCaptchaWebapiGraphQl/Plugin/ValidationOverrider.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\ReCaptchaWebapiGraphQl\Plugin; diff --git a/ReCaptchaWebapiGraphQl/README.md b/ReCaptchaWebapiGraphQl/README.md index 81fd5d70..0ec9a66b 100644 --- a/ReCaptchaWebapiGraphQl/README.md +++ b/ReCaptchaWebapiGraphQl/README.md @@ -1,7 +1,7 @@ -# Magento reCAPTCHA +# Magento_ReCaptchaWebapiGraphQl module -Google reCAPTCHA ensures that a human being, rather than a computer (or “bot”), is interacting with your website. Unlike the standard Magento CAPTCHA, Google reCAPTCHA provides enhanced security with a selection of different display options and methods. Additional website traffic information is available in the dashboard of your Google reCAPTCHA account. +Google reCAPTCHA ensures that a human being, rather than a computer (or "bot"), is interacting with your website. Unlike the standard Magento CAPTCHA, Google reCAPTCHA provides enhanced security with a selection of different display options and methods. Additional website traffic information is available in the dashboard of your Google reCAPTCHA account. This module provides the GraphQl implementation of reCAPTCHA. -For more information please visit the [Magento document for reCAPTCHA](https://docs.magento.com/user-guide/stores/security-google-recaptcha.html). +For more information, see the [reCAPTCHA documentation](https://experienceleague.adobe.com/en/docs/commerce-admin/systems/security/captcha/security-google-recaptcha). diff --git a/ReCaptchaWebapiGraphQl/Test/Api/ReCaptchaFormConfigTest.php b/ReCaptchaWebapiGraphQl/Test/Api/ReCaptchaFormConfigTest.php new file mode 100644 index 00000000..82857968 --- /dev/null +++ b/ReCaptchaWebapiGraphQl/Test/Api/ReCaptchaFormConfigTest.php @@ -0,0 +1,202 @@ +<?php +/** + * Copyright 2024 Adobe + * All Rights Reserved. + */ + +declare(strict_types=1); + +namespace Magento\ReCaptchaWebapiGraphQl\Test\Api; + +use Magento\Framework\Encryption\EncryptorInterface; +use Magento\Framework\App\ResourceConnection; +use Magento\Framework\DB\Adapter\AdapterInterface; +use Magento\TestFramework\Fixture\Config; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\TestCase\GraphQlAbstract; + +/** + * Test recaptcha config query + */ +class ReCaptchaFormConfigTest extends GraphQlAbstract +{ + /** + * @var EncryptorInterface $encryptor + */ + private $encryptor; + + /** + * @var \Magento\Config\Model\Config $config + */ + private $config; + + public function setUp(): void + { + $this->encryptor = Bootstrap::getObjectManager()->get(EncryptorInterface::class); + $this->config = Bootstrap::getObjectManager()->get(\Magento\Config\Model\Config::class); + } + + #[ + Config('recaptcha_frontend/type_recaptcha_v3/score_threshold', 0.5), + Config('recaptcha_frontend/type_recaptcha_v3/position', 'inline'), + Config('recaptcha_frontend/type_recaptcha_v3/lang', 'en'), + Config('recaptcha_frontend/failure_messages/validation_failure_message', 'Test validation failure message'), + Config('recaptcha_frontend/failure_messages/technical_failure_message', 'Test technical failure message'), + Config('recaptcha_frontend/type_for/customer_login', 'recaptcha_v3') + ] + public function testRecaptchaFormConfigQueryForReCaptchaV3(): void + { + $this->setWebapiKeys('type_recaptcha_v3'); + + $result = $this->graphQlQuery($this->getQueryForForm()); + $response = [ + "recaptchaFormConfig" => [ + "is_enabled" => true, + "configurations" => [ + "re_captcha_type" => "RECAPTCHA_V3", + "badge_position" => "inline", + "theme" => "light", + "website_key" => "test_public_key", + "language_code" => "en", + "minimum_score" => 0.5, + "validation_failure_message" => "Test validation failure message", + "technical_failure_message" => "Test technical failure message" + ] + ] + ]; + + $this->assertEquals($response, $result, "reCaptcha config contains errors"); + } + + #[ + Config('recaptcha_frontend/type_recaptcha/position', 'bottomright'), + Config('recaptcha_frontend/type_recaptcha/lang', 'en'), + Config('recaptcha_frontend/failure_messages/validation_failure_message', 'Test validation failure message'), + Config('recaptcha_frontend/failure_messages/technical_failure_message', 'Test technical failure message'), + Config('recaptcha_frontend/type_for/customer_login', 'recaptcha') + ] + public function testRecaptchaFormConfigQueryForReCaptchaV2(): void + { + $this->setWebapiKeys('type_recaptcha'); + + $result = $this->graphQlQuery($this->getQueryForForm()); + $response = [ + "recaptchaFormConfig" => [ + "is_enabled" => true, + "configurations" => [ + "re_captcha_type" => "RECAPTCHA", + "badge_position" => '', + "theme" => "light", + "website_key" => "test_public_key", + "minimum_score" => null, + "language_code" => "en", + "validation_failure_message" => "Test validation failure message", + "technical_failure_message" => "Test technical failure message" + ] + ] + ]; + + $this->assertEquals($response, $result, "reCaptcha config contains errors"); + } + + #[ + Config('recaptcha_frontend/type_invisible/position', 'bottomright'), + Config('recaptcha_frontend/type_invisible/lang', 'en'), + Config('recaptcha_frontend/failure_messages/validation_failure_message', 'Test validation failure message'), + Config('recaptcha_frontend/failure_messages/technical_failure_message', 'Test technical failure message'), + Config('recaptcha_frontend/type_for/customer_login', 'invisible') + ] + public function testRecaptchaFormConfigQueryForReCaptchaV2Invisible(): void + { + $this->setWebapiKeys('type_invisible'); + + $result = $this->graphQlQuery($this->getQueryForForm()); + $response = [ + "recaptchaFormConfig" => [ + "is_enabled" => true, + "configurations" => [ + "re_captcha_type" => "INVISIBLE", + "badge_position" => "bottomright", + "theme" => "light", + "website_key" => "test_public_key", + "minimum_score" => null, + "language_code" => "en", + "validation_failure_message" => "Test validation failure message", + "technical_failure_message" => "Test technical failure message" + ] + ] + ]; + + $this->assertEquals($response, $result, "reCaptcha config contains errors"); + } + + public function testRecaptchaFormConfigQueryForNotConfiguredForm(): void + { + $result = $this->graphQlQuery($this->getQueryForForm()); + $response = [ + "recaptchaFormConfig" => [ + "is_enabled" => false, + "configurations" => null + ] + ]; + $this->assertEquals($response, $result, "reCaptcha config contains errors"); + } + + /** + * Generates wepapi private/public key for reCaptcha config + * + * @param string $captchaType + */ + private function setWebapiKeys(string $captchaType) + { + $this->config->setDataByPath( + "recaptcha_frontend/{$captchaType}/public_key", + $this->encryptor->encrypt('test_public_key') + ); + $this->config->setDataByPath( + "recaptcha_frontend/{$captchaType}/private_key", + $this->encryptor->encrypt('test_private_key') + ); + $this->config->save(); + } + + /** + * Returns formatted query for reCaptcha configuration + */ + private function getQueryForForm(): string + { + $query = <<<QUERY + query + { + recaptchaFormConfig(formType: CUSTOMER_LOGIN){ + is_enabled + configurations{ + re_captcha_type + badge_position + theme + website_key + language_code + minimum_score + validation_failure_message + technical_failure_message + } + } + } +QUERY; + + return $query; + } + + public function tearDown(): void + { + /** @var ResourceConnection $resource */ + $resource = Bootstrap::getObjectManager()->get(ResourceConnection::class); + /** @var AdapterInterface $connection */ + $connection = $resource->getConnection(ResourceConnection::DEFAULT_CONNECTION); + + $connection->delete( + $resource->getTableName('core_config_data') + ); + parent::tearDown(); + } +} diff --git a/ReCaptchaWebapiGraphQl/Test/Api/ReCaptchaV3ConfigTest.php b/ReCaptchaWebapiGraphQl/Test/Api/ReCaptchaV3ConfigTest.php new file mode 100644 index 00000000..192476f7 --- /dev/null +++ b/ReCaptchaWebapiGraphQl/Test/Api/ReCaptchaV3ConfigTest.php @@ -0,0 +1,169 @@ +<?php +/** + * Copyright 2024 Adobe + * All Rights Reserved. + */ + +declare(strict_types=1); + +namespace Magento\ReCaptchaWebapiGraphQl\Test\Api; + +use Exception; +use Magento\Framework\Encryption\EncryptorInterface; +use Magento\TestFramework\Fixture\Config; +use Magento\Framework\App\ResourceConnection; +use Magento\Framework\DB\Adapter\AdapterInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\TestCase\GraphQlAbstract; + +/** + * Test recaptcha config query + */ +class ReCaptchaV3ConfigTest extends GraphQlAbstract +{ + private const QUERY = <<<QUERY +query { + recaptchaV3Config { + is_enabled + website_key + minimum_score + badge_position + language_code + failure_message + forms + theme + } +} +QUERY; + + /** @var EncryptorInterface $encryptor */ + private $encryptor; + + /** @var \Magento\Config\Model\Config $config */ + private $config; + + public function setUp(): void + { + $this->encryptor = Bootstrap::getObjectManager()->get(EncryptorInterface::class); + $this->config = Bootstrap::getObjectManager()->get(\Magento\Config\Model\Config::class); + } + + #[ + Config('recaptcha_frontend/type_recaptcha_v3/score_threshold', 0.75), + Config('recaptcha_frontend/type_recaptcha_v3/position', 'bottomright'), + Config('recaptcha_frontend/type_recaptcha_v3/lang', 'en'), + Config('recaptcha_frontend/type_recaptcha_v3/theme', 'light'), + Config('recaptcha_frontend/failure_messages/validation_failure_message', 'Test failure message'), + Config('recaptcha_frontend/type_for/customer_login', 'recaptcha_v3') + ] + public function testQueryRecaptchaNoPublicKeyConfigured(): void + { + $this->assertEquals( + [ + 'recaptchaV3Config' => [ + 'is_enabled' => false, + 'website_key' => '', + 'minimum_score' => 0.75, + 'badge_position' => 'bottomright', + 'language_code' => 'en', + 'failure_message' => 'Test failure message', + 'forms' => [ + 'CUSTOMER_LOGIN' + ], + 'theme' => 'light' + ] + ], + $this->graphQlQuery(self::QUERY) + ); + } + + #[ + Config('recaptcha_frontend/type_recaptcha_v3/score_threshold', 0.75), + Config('recaptcha_frontend/type_recaptcha_v3/position', 'bottomright'), + Config('recaptcha_frontend/type_recaptcha_v3/lang', 'en'), + Config('recaptcha_frontend/type_recaptcha_v3/theme', 'light'), + Config('recaptcha_frontend/failure_messages/validation_failure_message', 'Test failure message') + ] + public function testQueryRecaptchaNoFormsConfigured(): void + { + $this->config->setDataByPath( + 'recaptcha_frontend/type_recaptcha_v3/public_key', + $this->encryptor->encrypt('test_public_key') + ); + $this->config->setDataByPath( + 'recaptcha_frontend/type_recaptcha_v3/private_key', + $this->encryptor->encrypt('test_private_key') + ); + + $this->config->save(); + + $this->assertEquals( + [ + 'recaptchaV3Config' => [ + 'is_enabled' => false, + 'website_key' => 'test_public_key', + 'minimum_score' => 0.75, + 'badge_position' => 'bottomright', + 'language_code' => 'en', + 'failure_message' => 'Test failure message', + 'forms' => [], + 'theme' => 'light' + ] + ], + $this->graphQlQuery(self::QUERY) + ); + } + + #[ + Config('recaptcha_frontend/type_recaptcha_v3/score_threshold', 0.75), + Config('recaptcha_frontend/type_recaptcha_v3/position', 'bottomright'), + Config('recaptcha_frontend/type_recaptcha_v3/lang', 'en'), + Config('recaptcha_frontend/type_recaptcha_v3/theme', 'dark'), + Config('recaptcha_frontend/failure_messages/validation_failure_message', 'Test failure message'), + Config('recaptcha_frontend/type_for/customer_login', 'recaptcha_v3') + ] + public function testQueryRecaptchaConfigured(): void + { + $this->config->setDataByPath( + 'recaptcha_frontend/type_recaptcha_v3/public_key', + $this->encryptor->encrypt('test_public_key') + ); + $this->config->setDataByPath( + 'recaptcha_frontend/type_recaptcha_v3/private_key', + $this->encryptor->encrypt('test_private_key') + ); + + $this->config->save(); + + $this->assertEquals( + [ + 'recaptchaV3Config' => [ + 'is_enabled' => true, + 'website_key' => 'test_public_key', + 'minimum_score' => 0.75, + 'badge_position' => 'bottomright', + 'language_code' => 'en', + 'failure_message' => 'Test failure message', + 'forms' => [ + 'CUSTOMER_LOGIN' + ], + 'theme' => 'dark' + ] + ], + $this->graphQlQuery(self::QUERY) + ); + } + + public function tearDown(): void + { + /** @var ResourceConnection $resource */ + $resource = Bootstrap::getObjectManager()->get(ResourceConnection::class); + /** @var AdapterInterface $connection */ + $connection = $resource->getConnection(ResourceConnection::DEFAULT_CONNECTION); + + $connection->delete( + $resource->getTableName('core_config_data') + ); + parent::tearDown(); + } +} diff --git a/ReCaptchaWebapiGraphQl/Test/Unit/Plugin/GraphQlValidatorTest.php b/ReCaptchaWebapiGraphQl/Test/Unit/Plugin/GraphQlValidatorTest.php index 3583aa32..84d141ea 100644 --- a/ReCaptchaWebapiGraphQl/Test/Unit/Plugin/GraphQlValidatorTest.php +++ b/ReCaptchaWebapiGraphQl/Test/Unit/Plugin/GraphQlValidatorTest.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2024 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\ReCaptchaWebapiGraphQl\Test\Unit\Plugin; @@ -23,6 +24,9 @@ use PHPUnit\Framework\TestCase; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; +/** + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ class GraphQlValidatorTest extends TestCase { /** @@ -60,7 +64,7 @@ protected function setUp(): void ); } - public function getPluginCases(): array + public static function getPluginCases(): array { return [ 'not-mutation' => [false, true, false, false], diff --git a/ReCaptchaWebapiGraphQl/Test/Unit/Plugin/ValidationOverriderTest.php b/ReCaptchaWebapiGraphQl/Test/Unit/Plugin/ValidationOverriderTest.php index 8d1baeaa..5b798674 100644 --- a/ReCaptchaWebapiGraphQl/Test/Unit/Plugin/ValidationOverriderTest.php +++ b/ReCaptchaWebapiGraphQl/Test/Unit/Plugin/ValidationOverriderTest.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2024 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\ReCaptchaWebapiGraphQl\Test\Unit\Plugin; @@ -35,7 +36,7 @@ protected function setUp(): void $this->model = new ValidationOverrider($this->userContextMock); } - public function getUserContextData(): array + public static function getUserContextData(): array { return [ 'customer' => [UserContextInterface::USER_TYPE_CUSTOMER, 1, true], diff --git a/ReCaptchaWebapiGraphQl/composer.json b/ReCaptchaWebapiGraphQl/composer.json index 5b099fbc..f581895c 100644 --- a/ReCaptchaWebapiGraphQl/composer.json +++ b/ReCaptchaWebapiGraphQl/composer.json @@ -2,9 +2,11 @@ "name": "magento/module-re-captcha-webapi-graph-ql", "description": "Google reCAPTCHA integration for Magento2", "require": { - "php": "~7.4.0||~8.0.0||~8.1.0", + "php": "~8.2.0||~8.3.0||~8.4.0", "magento/framework": "*", "magento/module-authorization": "*", + "magento/module-re-captcha-frontend-ui": "*", + "magento/module-re-captcha-version-3-invisible": "*", "magento/module-re-captcha-validation-api": "*", "magento/module-re-captcha-webapi-api": "*" }, diff --git a/ReCaptchaWebapiGraphQl/etc/di.xml b/ReCaptchaWebapiGraphQl/etc/di.xml index cef82784..2a8ad994 100644 --- a/ReCaptchaWebapiGraphQl/etc/di.xml +++ b/ReCaptchaWebapiGraphQl/etc/di.xml @@ -1,11 +1,10 @@ <?xml version="1.0"?> <!-- /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ --> - <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> <type name="Magento\Framework\GraphQl\Query\ResolverInterface"> <plugin name="graphql_recaptcha_validation" type="Magento\ReCaptchaWebapiGraphQl\Plugin\GraphQlValidator" /> diff --git a/ReCaptchaWebapiGraphQl/etc/graphql/di.xml b/ReCaptchaWebapiGraphQl/etc/graphql/di.xml index 5e9e9b79..453da0b0 100644 --- a/ReCaptchaWebapiGraphQl/etc/graphql/di.xml +++ b/ReCaptchaWebapiGraphQl/etc/graphql/di.xml @@ -1,13 +1,35 @@ <?xml version="1.0"?> <!-- /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ --> - <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> <type name="Magento\ReCaptchaValidationApi\Api\ValidatorInterface"> <plugin name="graphql_recaptcha_validation_override" type="Magento\ReCaptchaWebapiGraphQl\Plugin\ValidationOverrider" /> </type> + + <type name="Magento\ReCaptchaWebapiGraphQl\Model\Resolver\ReCaptchaFormConfig"> + <arguments> + <argument name="providers" xsi:type="array"> + <item name="recaptcha" xsi:type="object">Magento\ReCaptchaVersion2Checkbox\Model\Config</item> + <item name="invisible" xsi:type="object">Magento\ReCaptchaVersion2Invisible\Model\Config</item> + <item name="recaptcha_v3" xsi:type="object">Magento\ReCaptchaVersion3Invisible\Model\Config</item> + </argument> + + <argument name="formTypes" xsi:type="array"> + <item name="PLACE_ORDER" xsi:type="string">place_order</item> + <item name="CONTACT" xsi:type="string">contact</item> + <item name="CUSTOMER_LOGIN" xsi:type="string">customer_login</item> + <item name="CUSTOMER_FORGOT_PASSWORD" xsi:type="string">customer_forgot_password</item> + <item name="CUSTOMER_CREATE" xsi:type="string">customer_create</item> + <item name="CUSTOMER_EDIT" xsi:type="string">customer_edit</item> + <item name="NEWSLETTER" xsi:type="string">newsletter</item> + <item name="PRODUCT_REVIEW" xsi:type="string">product_review</item> + <item name="SENDFRIEND" xsi:type="string">sendfriend</item> + <item name="BRAINTREE" xsi:type="string">braintree</item> + </argument> + </arguments> + </type> </config> diff --git a/ReCaptchaWebapiGraphQl/etc/module.xml b/ReCaptchaWebapiGraphQl/etc/module.xml index 93a2834f..791e4a73 100644 --- a/ReCaptchaWebapiGraphQl/etc/module.xml +++ b/ReCaptchaWebapiGraphQl/etc/module.xml @@ -1,10 +1,10 @@ <?xml version="1.0"?> <!-- /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ - --> +--> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd"> <module name="Magento_ReCaptchaWebapiGraphQl"/> diff --git a/ReCaptchaWebapiGraphQl/etc/schema.graphqls b/ReCaptchaWebapiGraphQl/etc/schema.graphqls new file mode 100644 index 00000000..caf82f99 --- /dev/null +++ b/ReCaptchaWebapiGraphQl/etc/schema.graphqls @@ -0,0 +1,58 @@ +# +# +# Copyright 2023 Adobe +# All Rights Reserved. +# +# +# +type Query { + recaptchaV3Config: ReCaptchaConfigurationV3 @resolver(class: "Magento\\ReCaptchaWebapiGraphQl\\Model\\Resolver\\ReCaptchaV3") @doc(description: "Returns details about Google reCAPTCHA V3-Invisible configuration.") + recaptchaFormConfig(formType: ReCaptchaFormEnum!): ReCaptchaConfigOutput @resolver(class: "Magento\\ReCaptchaWebapiGraphQl\\Model\\Resolver\\ReCaptchaFormConfig") +} + +enum ReCaptchaFormEnum { + PLACE_ORDER + CONTACT + CUSTOMER_LOGIN + CUSTOMER_FORGOT_PASSWORD + CUSTOMER_CREATE + CUSTOMER_EDIT + NEWSLETTER + PRODUCT_REVIEW + SENDFRIEND + BRAINTREE + RESEND_CONFIRMATION_EMAIL +} + +enum ReCaptchaTypeEmum { + INVISIBLE + RECAPTCHA + RECAPTCHA_V3 +} + +type ReCaptchaConfigOutput { + is_enabled: Boolean! @doc(description: "Indicates whether reCaptcha type is enabled") + configurations: ReCaptchaConfiguration @doc(description: "Configuration details for reCaptcha type") +} + +type ReCaptchaConfiguration @doc(description: "Contains reCAPTCHA form configuration details.") { + re_captcha_type: ReCaptchaTypeEmum! + website_key: String! @doc(description: "The website key generated when the Google reCAPTCHA account was registered.") + minimum_score: Float @doc(description: "The minimum score that identifies a user interaction as a potential risk.") + badge_position: String @doc(description: "The position of the invisible reCAPTCHA badge on each page.") + theme: String! @doc(description: "Theme to be used to render reCaptcha.") + language_code: String @doc(description: "A two-character code that specifies the language that is used for Google reCAPTCHA text and messaging.") + validation_failure_message: String! @doc(description: "The message that appears to the user if validation fails.") + technical_failure_message: String! @doc(description: "The message that appears when reCaptcha fails.") +} + +type ReCaptchaConfigurationV3 @doc(description: "Contains reCAPTCHA V3-Invisible configuration details.") { + is_enabled: Boolean! @doc(description: "Return whether recaptcha is enabled or not") + website_key: String! @doc(description: "The website key generated when the Google reCAPTCHA account was registered.") + minimum_score: Float! @doc(description: "The minimum score that identifies a user interaction as a potential risk.") + badge_position: String! @doc(description: "The position of the invisible reCAPTCHA badge on each page.") + language_code: String @doc(description: "A two-character code that specifies the language that is used for Google reCAPTCHA text and messaging.") + failure_message: String! @doc(description: "The message that appears to the user if validation fails.") + forms: [ReCaptchaFormEnum!]! @doc(description: "A list of forms on the storefront that have been configured to use reCAPTCHA V3.") + theme: String! @doc(description: "Theme to be used to render reCaptcha.") +} diff --git a/ReCaptchaWebapiGraphQl/registration.php b/ReCaptchaWebapiGraphQl/registration.php index 9d99e132..c74add3b 100644 --- a/ReCaptchaWebapiGraphQl/registration.php +++ b/ReCaptchaWebapiGraphQl/registration.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); \Magento\Framework\Component\ComponentRegistrar::register( diff --git a/ReCaptchaWebapiRest/Plugin/RestValidationPlugin.php b/ReCaptchaWebapiRest/Plugin/RestValidationPlugin.php index 3e85ef0a..e641401f 100644 --- a/ReCaptchaWebapiRest/Plugin/RestValidationPlugin.php +++ b/ReCaptchaWebapiRest/Plugin/RestValidationPlugin.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\ReCaptchaWebapiRest\Plugin; diff --git a/ReCaptchaWebapiRest/Plugin/SoapValidationPlugin.php b/ReCaptchaWebapiRest/Plugin/SoapValidationPlugin.php index ad53b684..702a2e05 100644 --- a/ReCaptchaWebapiRest/Plugin/SoapValidationPlugin.php +++ b/ReCaptchaWebapiRest/Plugin/SoapValidationPlugin.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\ReCaptchaWebapiRest\Plugin; diff --git a/ReCaptchaWebapiRest/Plugin/ValidationOverrider.php b/ReCaptchaWebapiRest/Plugin/ValidationOverrider.php index 06be9eee..c649d897 100644 --- a/ReCaptchaWebapiRest/Plugin/ValidationOverrider.php +++ b/ReCaptchaWebapiRest/Plugin/ValidationOverrider.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\ReCaptchaWebapiRest\Plugin; diff --git a/ReCaptchaWebapiRest/README.md b/ReCaptchaWebapiRest/README.md index cb9f3abe..d3454253 100644 --- a/ReCaptchaWebapiRest/README.md +++ b/ReCaptchaWebapiRest/README.md @@ -1,7 +1,7 @@ -# Magento reCAPTCHA +# Magento_ReCaptchaWebapiRest module -Google reCAPTCHA ensures that a human being, rather than a computer (or “bot”), is interacting with your website. Unlike the standard Magento CAPTCHA, Google reCAPTCHA provides enhanced security with a selection of different display options and methods. Additional website traffic information is available in the dashboard of your Google reCAPTCHA account. +Google reCAPTCHA ensures that a human being, rather than a computer (or "bot"), is interacting with your website. Unlike the standard Magento CAPTCHA, Google reCAPTCHA provides enhanced security with a selection of different display options and methods. Additional website traffic information is available in the dashboard of your Google reCAPTCHA account. This module provides the WebAPI REST implementation of reCAPTCHA. -For more information please visit the [Magento document for reCAPTCHA](https://docs.magento.com/user-guide/stores/security-google-recaptcha.html). +For more information, see the [reCAPTCHA documentation](https://experienceleague.adobe.com/en/docs/commerce-admin/systems/security/captcha/security-google-recaptcha). diff --git a/ReCaptchaWebapiRest/Test/Unit/Plugin/RestValidationPluginTest.php b/ReCaptchaWebapiRest/Test/Unit/Plugin/RestValidationPluginTest.php index 893d3f85..c706d9e7 100644 --- a/ReCaptchaWebapiRest/Test/Unit/Plugin/RestValidationPluginTest.php +++ b/ReCaptchaWebapiRest/Test/Unit/Plugin/RestValidationPluginTest.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2024 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\ReCaptchaWebapiRest\Test\Unit\Plugin; @@ -71,7 +72,7 @@ protected function setUp(): void ); } - public function getPluginCases(): array + public static function getPluginCases(): array { return [ 'unprotected-endpoint' => [false, false, false], diff --git a/ReCaptchaWebapiRest/Test/Unit/Plugin/SoapValidationPluginTest.php b/ReCaptchaWebapiRest/Test/Unit/Plugin/SoapValidationPluginTest.php index fba005ce..c39fdd00 100644 --- a/ReCaptchaWebapiRest/Test/Unit/Plugin/SoapValidationPluginTest.php +++ b/ReCaptchaWebapiRest/Test/Unit/Plugin/SoapValidationPluginTest.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2024 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\ReCaptchaWebapiRest\Test\Unit\Plugin; @@ -64,7 +65,7 @@ protected function setUp(): void ); } - public function getPluginCases(): array + public static function getPluginCases(): array { return [ 'not-protected' => [false, UserContextInterface::USER_TYPE_GUEST, null, false], diff --git a/ReCaptchaWebapiRest/Test/Unit/Plugin/ValidationOverriderTest.php b/ReCaptchaWebapiRest/Test/Unit/Plugin/ValidationOverriderTest.php index e3d97216..1ec0546c 100644 --- a/ReCaptchaWebapiRest/Test/Unit/Plugin/ValidationOverriderTest.php +++ b/ReCaptchaWebapiRest/Test/Unit/Plugin/ValidationOverriderTest.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2024 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\ReCaptchaWebapiRest\Test\Unit\Plugin; @@ -35,7 +36,7 @@ protected function setUp(): void $this->model = new ValidationOverrider($this->userContextMock); } - public function getUserContextData(): array + public static function getUserContextData(): array { return [ 'customer' => [UserContextInterface::USER_TYPE_CUSTOMER, 1, true], diff --git a/ReCaptchaWebapiRest/composer.json b/ReCaptchaWebapiRest/composer.json index f2b545e8..39219e0a 100644 --- a/ReCaptchaWebapiRest/composer.json +++ b/ReCaptchaWebapiRest/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-re-captcha-webapi-rest", "description": "Google reCAPTCHA integration for Magento2", "require": { - "php": "~7.4.0||~8.0.0||~8.1.0", + "php": "~8.2.0||~8.3.0||~8.4.0", "magento/framework": "*", "magento/module-authorization": "*", "magento/module-re-captcha-validation-api": "*", diff --git a/ReCaptchaWebapiRest/etc/di.xml b/ReCaptchaWebapiRest/etc/di.xml index cfaf88b6..5f47ac48 100644 --- a/ReCaptchaWebapiRest/etc/di.xml +++ b/ReCaptchaWebapiRest/etc/di.xml @@ -1,11 +1,10 @@ <?xml version="1.0"?> <!-- /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ --> - <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> <type name="Magento\Webapi\Controller\Rest\RequestValidator"> <plugin name="rest_webapi_recaptcha_validation" type="Magento\ReCaptchaWebapiRest\Plugin\RestValidationPlugin" /> diff --git a/ReCaptchaWebapiRest/etc/module.xml b/ReCaptchaWebapiRest/etc/module.xml index 5ee5170d..c2dbd083 100644 --- a/ReCaptchaWebapiRest/etc/module.xml +++ b/ReCaptchaWebapiRest/etc/module.xml @@ -1,10 +1,10 @@ <?xml version="1.0"?> <!-- /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ - --> +--> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd"> <module name="Magento_ReCaptchaWebapiRest"/> diff --git a/ReCaptchaWebapiRest/etc/webapi_rest/di.xml b/ReCaptchaWebapiRest/etc/webapi_rest/di.xml index b881cd85..2d8ed68c 100644 --- a/ReCaptchaWebapiRest/etc/webapi_rest/di.xml +++ b/ReCaptchaWebapiRest/etc/webapi_rest/di.xml @@ -1,11 +1,10 @@ <?xml version="1.0"?> <!-- /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ --> - <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> <type name="Magento\ReCaptchaValidationApi\Api\ValidatorInterface"> <plugin name="webapi_recaptcha_validation_override" type="Magento\ReCaptchaWebapiRest\Plugin\ValidationOverrider" /> diff --git a/ReCaptchaWebapiRest/etc/webapi_soap/di.xml b/ReCaptchaWebapiRest/etc/webapi_soap/di.xml index b881cd85..2d8ed68c 100644 --- a/ReCaptchaWebapiRest/etc/webapi_soap/di.xml +++ b/ReCaptchaWebapiRest/etc/webapi_soap/di.xml @@ -1,11 +1,10 @@ <?xml version="1.0"?> <!-- /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ --> - <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> <type name="Magento\ReCaptchaValidationApi\Api\ValidatorInterface"> <plugin name="webapi_recaptcha_validation_override" type="Magento\ReCaptchaWebapiRest\Plugin\ValidationOverrider" /> diff --git a/ReCaptchaWebapiRest/registration.php b/ReCaptchaWebapiRest/registration.php index a7239df2..0e78e6bb 100644 --- a/ReCaptchaWebapiRest/registration.php +++ b/ReCaptchaWebapiRest/registration.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); \Magento\Framework\Component\ComponentRegistrar::register( diff --git a/ReCaptchaWebapiUi/README.md b/ReCaptchaWebapiUi/README.md index 68524ec4..6c4b20d0 100644 --- a/ReCaptchaWebapiUi/README.md +++ b/ReCaptchaWebapiUi/README.md @@ -1,7 +1,7 @@ -# Magento reCAPTCHA +# Magento_ReCaptchaWebapiUi module -Google reCAPTCHA ensures that a human being, rather than a computer (or “bot”), is interacting with your website. Unlike the standard Magento CAPTCHA, Google reCAPTCHA provides enhanced security with a selection of different display options and methods. Additional website traffic information is available in the dashboard of your Google reCAPTCHA account. +Google reCAPTCHA ensures that a human being, rather than a computer (or "bot"), is interacting with your website. Unlike the standard Magento CAPTCHA, Google reCAPTCHA provides enhanced security with a selection of different display options and methods. Additional website traffic information is available in the dashboard of your Google reCAPTCHA account. This module provides the UI files related to the WebAPI implementation of reCAPTCHA. -For more information please visit the [Magento document for reCAPTCHA](https://docs.magento.com/user-guide/stores/security-google-recaptcha.html). +For more information, see the [reCAPTCHA documentation](https://experienceleague.adobe.com/en/docs/commerce-admin/systems/security/captcha/security-google-recaptcha). diff --git a/ReCaptchaWebapiUi/composer.json b/ReCaptchaWebapiUi/composer.json index 3dbd5a1b..3e4e03e2 100644 --- a/ReCaptchaWebapiUi/composer.json +++ b/ReCaptchaWebapiUi/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-re-captcha-webapi-ui", "description": "Google reCAPTCHA integration for Magento2", "require": { - "php": "~7.4.0||~8.0.0||~8.1.0", + "php": "~8.2.0||~8.3.0||~8.4.0", "magento/framework": "*", "magento/module-re-captcha-frontend-ui": "*" }, diff --git a/ReCaptchaWebapiUi/etc/module.xml b/ReCaptchaWebapiUi/etc/module.xml index a26f71c5..da240653 100644 --- a/ReCaptchaWebapiUi/etc/module.xml +++ b/ReCaptchaWebapiUi/etc/module.xml @@ -1,10 +1,10 @@ <?xml version="1.0"?> <!-- /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ - --> +--> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd"> <module name="Magento_ReCaptchaWebapiUi"/> diff --git a/ReCaptchaWebapiUi/registration.php b/ReCaptchaWebapiUi/registration.php index fcf8ab63..9b5eb62d 100644 --- a/ReCaptchaWebapiUi/registration.php +++ b/ReCaptchaWebapiUi/registration.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); \Magento\Framework\Component\ComponentRegistrar::register( diff --git a/ReCaptchaWebapiUi/view/frontend/requirejs-config.js b/ReCaptchaWebapiUi/view/frontend/requirejs-config.js index 6b787ba5..f173cd81 100644 --- a/ReCaptchaWebapiUi/view/frontend/requirejs-config.js +++ b/ReCaptchaWebapiUi/view/frontend/requirejs-config.js @@ -1,6 +1,6 @@ /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ // eslint-disable-next-line no-unused-vars diff --git a/ReCaptchaWebapiUi/view/frontend/web/js/jquery-mixin.js b/ReCaptchaWebapiUi/view/frontend/web/js/jquery-mixin.js index e6f13966..8b93fcad 100644 --- a/ReCaptchaWebapiUi/view/frontend/web/js/jquery-mixin.js +++ b/ReCaptchaWebapiUi/view/frontend/web/js/jquery-mixin.js @@ -1,6 +1,6 @@ /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ // jscs:disable requireDotNotation diff --git a/ReCaptchaWebapiUi/view/frontend/web/js/webapiReCaptcha.js b/ReCaptchaWebapiUi/view/frontend/web/js/webapiReCaptcha.js index c7c7c15d..e2979d0d 100644 --- a/ReCaptchaWebapiUi/view/frontend/web/js/webapiReCaptcha.js +++ b/ReCaptchaWebapiUi/view/frontend/web/js/webapiReCaptcha.js @@ -1,6 +1,6 @@ /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ // jscs:disable jsDoc @@ -43,14 +43,16 @@ define( var self = this, trigger; + trigger = function () { + self.reCaptchaCallback(grecaptcha.getResponse(widgetId)); + }; + registry._isInvisibleType[this.getReCaptchaId()] = false; + if (this.getIsInvisibleRecaptcha()) { trigger = function () { grecaptcha.execute(widgetId); }; - } else { - trigger = function () { - self.reCaptchaCallback(grecaptcha.getResponse(widgetId)); - }; + registry._isInvisibleType[this.getReCaptchaId()] = true; } if (this.autoTrigger) { diff --git a/ReCaptchaWebapiUi/view/frontend/web/js/webapiReCaptchaRegistry.js b/ReCaptchaWebapiUi/view/frontend/web/js/webapiReCaptchaRegistry.js index ba2f786d..b5d47cc7 100644 --- a/ReCaptchaWebapiUi/view/frontend/web/js/webapiReCaptchaRegistry.js +++ b/ReCaptchaWebapiUi/view/frontend/web/js/webapiReCaptchaRegistry.js @@ -1,6 +1,6 @@ /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ define([], function () { @@ -26,6 +26,11 @@ define([], function () { */ _listeners: {}, + /** + * recaptchaId: bool map + */ + _isInvisibleType: {}, + /** * Add a listener to when the ReCaptcha finishes verification * @param {String} id - ReCaptchaId @@ -37,6 +42,15 @@ define([], function () { } else { this._listeners[id] = func; } + }, + + /** + * Remove a listener + * + * @param id + */ + removeListener: function (id) { + this._listeners[id] = undefined; } }; }); diff --git a/ReCaptchaWishlist/Observer/ShareWishlistObserver.php b/ReCaptchaWishlist/Observer/ShareWishlistObserver.php new file mode 100644 index 00000000..39e0a2f6 --- /dev/null +++ b/ReCaptchaWishlist/Observer/ShareWishlistObserver.php @@ -0,0 +1,77 @@ +<?php +/** + * Copyright 2024 Adobe + * All Rights Reserved. + */ + +declare(strict_types=1); + +namespace Magento\ReCaptchaWishlist\Observer; + +use Magento\Framework\App\Action\Action; +use Magento\Framework\App\Response\RedirectInterface; +use Magento\Framework\Event\Observer; +use Magento\Framework\Event\ObserverInterface; +use Magento\Framework\Exception\InputException; +use Magento\ReCaptchaUi\Model\IsCaptchaEnabledInterface; +use Magento\ReCaptchaUi\Model\RequestHandlerInterface; + +/** + * Adds Captcha support for share wishlist + */ +class ShareWishlistObserver implements ObserverInterface +{ + /** + * @var string Captcha key + */ + private const CAPTCHA_KEY = 'wishlist'; + + /** + * @var RedirectInterface + */ + private $redirect; + + /** + * @var IsCaptchaEnabledInterface + */ + private $isCaptchaEnabled; + + /** + * @var RequestHandlerInterface + */ + private $requestHandler; + + /** + * @param RedirectInterface $redirect + * @param IsCaptchaEnabledInterface $isCaptchaEnabled + * @param RequestHandlerInterface $requestHandler + */ + public function __construct( + RedirectInterface $redirect, + IsCaptchaEnabledInterface $isCaptchaEnabled, + RequestHandlerInterface $requestHandler + ) { + $this->redirect = $redirect; + $this->isCaptchaEnabled = $isCaptchaEnabled; + $this->requestHandler = $requestHandler; + } + + /** + * @inheritdoc + * @param Observer $observer + * @return void + * @throws InputException + */ + public function execute(Observer $observer): void + { + if ($this->isCaptchaEnabled->isCaptchaEnabledFor(self::CAPTCHA_KEY)) { + /** @var Action $controller */ + $controller = $observer->getControllerAction(); + $request = $controller->getRequest(); + $response = $controller->getResponse(); + $redirectOnFailureUrl = $this->redirect->getRefererUrl(); + + $this->requestHandler->execute(self::CAPTCHA_KEY, $request, $response, $redirectOnFailureUrl); + } + } +} diff --git a/ReCaptchaWishlist/README.md b/ReCaptchaWishlist/README.md new file mode 100644 index 00000000..6c36ee1d --- /dev/null +++ b/ReCaptchaWishlist/README.md @@ -0,0 +1,7 @@ +# Magento_ReCaptchaWishlist module + +Google reCAPTCHA ensures that a human being, rather than a computer (or "bot"), is interacting with your website. Unlike the standard Magento CAPTCHA, Google reCAPTCHA provides enhanced security with a selection of different display options and methods. Additional website traffic information is available in the dashboard of your Google reCAPTCHA account. + +This module provides the reCAPTCHA implementations related to wishlist. + +For more information, see the [reCAPTCHA documentation](https://experienceleague.adobe.com/en/docs/commerce-admin/systems/security/captcha/security-google-recaptcha). diff --git a/ReCaptchaWishlist/Test/Integration/ShareWishlistFormTest.php b/ReCaptchaWishlist/Test/Integration/ShareWishlistFormTest.php new file mode 100644 index 00000000..ed43dfa2 --- /dev/null +++ b/ReCaptchaWishlist/Test/Integration/ShareWishlistFormTest.php @@ -0,0 +1,272 @@ +<?php +/** + * Copyright 2024 Adobe + * All Rights Reserved. + */ + +declare(strict_types=1); + +namespace Magento\ReCaptchaWishlist\Test\Integration; + +use Magento\Customer\Model\Session; +use Magento\Framework\App\Request\Http; +use Magento\Framework\Data\Form\FormKey; +use Magento\Framework\Message\MessageInterface; +use Magento\Framework\Validation\ValidationResult; +use Magento\ReCaptchaValidation\Model\Validator; +use Magento\TestFramework\TestCase\AbstractController; +use Magento\TestFramework\Wishlist\Model\GetWishlistByCustomerId; +use Magento\ReCaptchaUi\Model\CaptchaResponseResolverInterface; +use Magento\Framework\UrlInterface; +use PHPUnit\Framework\MockObject\MockObject; + +/** + * Tests for create wish list + * + * @magentoDataFixture Magento/Customer/_files/customer.php + * @magentoDbIsolation enabled + * @magentoAppArea frontend + */ +class ShareWishlistFormTest extends AbstractController +{ + /** + * @var string Customer ID + */ + private const CUSTOMER_ID = 1; + + /** + * @var Session + */ + private $customerSession; + + /** + * @var string + */ + private $formKey; + + /** + * @var UrlInterface + */ + private $url; + + /** + * @var int + */ + private $wishlistId; + + /** + * @var ValidationResult|MockObject + */ + private $captchaValidationResultMock; + + /** + * @inheritdoc + */ + protected function setUp(): void + { + parent::setUp(); + $this->formKey = $this->_objectManager->get(FormKey::class)->getFormKey(); + $this->customerSession = $this->_objectManager->get(Session::class); + $this->customerSession->setCustomerId(self::CUSTOMER_ID); + $this->wishlistId = $this->_objectManager->get(GetWishlistByCustomerId::class) + ->execute(self::CUSTOMER_ID) + ->getId(); + $this->url = $this->_objectManager->get(UrlInterface::class); + $this->captchaValidationResultMock = $this->createMock(ValidationResult::class); + $captchaValidatorMock = $this->createMock(Validator::class); + $captchaValidatorMock->expects($this->any()) + ->method('isValid') + ->willReturn($this->captchaValidationResultMock); + $this->_objectManager->addSharedInstance($captchaValidatorMock, Validator::class); + } + + /** + * Checks the content of the 'Wish List Sharing' page when ReCaptcha is disabled + */ + public function testGetRequestIfReCaptchaIsDisabled(): void + { + $this->checkSuccessfulGetResponse(); + } + + /** + * Checks the content of the 'Wish List Sharing' page when ReCaptcha is enabled + * but keys are not configured + * + * @magentoConfigFixture base_website recaptcha_frontend/type_for/wishlist invisible + * + * It's needed for proper work of "ifconfig" in layout during tests running + * @magentoConfigFixture default_store recaptcha_frontend/type_for/wishlist invisible + */ + public function testGetRequestIfReCaptchaKeysAreNotConfigured(): void + { + $this->checkSuccessfulGetResponse(); + } + + /** + * Checks the content of the 'Wish List Sharing' page when ReCaptcha is enabled + * and keys are configured + * + * @magentoConfigFixture base_website recaptcha_frontend/type_invisible/public_key test_public_key + * @magentoConfigFixture base_website recaptcha_frontend/type_invisible/private_key test_private_key + * @magentoConfigFixture base_website recaptcha_frontend/type_for/wishlist invisible + * + * It's needed for proper work of "ifconfig" in layout during tests running + * @magentoConfigFixture default_store recaptcha_frontend/type_for/wishlist invisible + */ + public function testGetRequestIfReCaptchaIsEnabled(): void + { + $this->checkSuccessfulGetResponse(true); + } + + /** + * Checks GET response + * + * @param bool $shouldContainReCaptcha + * @return void + */ + private function checkSuccessfulGetResponse(bool $shouldContainReCaptcha = false): void + { + $this->dispatch('wishlist/index/share/wishlist_id/' . $this->wishlistId); + $content = $this->getResponse()->getBody(); + + $this->assertNotEmpty($content); + + $shouldContainReCaptcha + ? $this->assertStringContainsString('field-recaptcha', $content) + : $this->assertStringNotContainsString('field-recaptcha', $content); + + $this->assertEmpty($this->getSessionMessages(MessageInterface::TYPE_ERROR)); + } + + /** + * Checks the sharing process without ReCaptcha validation + */ + public function testPostRequestWithoutReCaptchaValidation(): void + { + $this->checkSuccessfulPostRequest(); + } + + /** + * Checks the sharing process if ReCaptcha is enabled but keys are not configured + * + * @magentoConfigFixture base_website recaptcha_frontend/type_for/wishlist invisible + * + * It's needed for proper work of "ifconfig" in layout during tests running + * @magentoConfigFixture default_store recaptcha_frontend/type_for/wishlist invisible + */ + public function testPostRequestIfReCaptchaKeysAreNotConfigured(): void + { + $this->checkSuccessfulPostRequest(); + } + + /** + * Checks the successful sharing process with ReCaptcha validation + * + * @magentoConfigFixture base_website recaptcha_frontend/type_invisible/public_key test_public_key + * @magentoConfigFixture base_website recaptcha_frontend/type_invisible/private_key test_private_key + * @magentoConfigFixture base_website recaptcha_frontend/type_for/wishlist invisible + * + * It's needed for proper work of "ifconfig" in layout during tests running + * @magentoConfigFixture default_store recaptcha_frontend/type_for/wishlist invisible + */ + public function testPostRequestWithSuccessfulReCaptchaValidation(): void + { + $this->captchaValidationResultMock->expects($this->once()) + ->method('isValid') + ->willReturn(true); + $this->checkSuccessfulPostRequest(true); + } + + /** + * Checks successful sharing process + * + * @param bool $withParamReCaptcha + */ + private function checkSuccessfulPostRequest(bool $withParamReCaptcha = false):void + { + $this->makePostRequest($withParamReCaptcha); + $url = $this->url->getRouteUrl('wishlist/index/index/wishlist_id/' . $this->wishlistId . '/'); + $this->assertRedirect(self::equalTo($url)); + $this->assertEmpty($this->getSessionMessages(MessageInterface::TYPE_ERROR)); + } + + /** + * Checks the sharing process with ReCaptcha validation when `g-recaptcha-response` missed + * + * @magentoConfigFixture base_website recaptcha_frontend/type_invisible/public_key test_public_key + * @magentoConfigFixture base_website recaptcha_frontend/type_invisible/private_key test_private_key + * @magentoConfigFixture base_website recaptcha_frontend/type_for/wishlist invisible + * + * It's needed for proper work of "ifconfig" in layout during tests running + * @magentoConfigFixture default_store recaptcha_frontend/type_for/wishlist invisible + */ + public function testPostRequestIfReCaptchaParameterIsMissed(): void + { + $this->checkFailedPostRequest(); + } + + /** + * Checks the failed sharing process with ReCaptcha validation + * + * @magentoConfigFixture base_website recaptcha_frontend/type_invisible/public_key test_public_key + * @magentoConfigFixture base_website recaptcha_frontend/type_invisible/private_key test_private_key + * @magentoConfigFixture base_website recaptcha_frontend/type_for/wishlist invisible + * + * It's needed for proper work of "ifconfig" in layout during tests running + * @magentoConfigFixture default_store recaptcha_frontend/type_for/wishlist invisible + */ + public function testPostRequestWithFailedReCaptchaValidation(): void + { + $this->captchaValidationResultMock->expects($this->once()) + ->method('isValid') + ->willReturn(false); + $this->checkFailedPostRequest(true); + } + + /** + * Checks failed sharing process + * + * @param bool $withParamReCaptcha + */ + private function checkFailedPostRequest(bool $withParamReCaptcha = false): void + { + $this->makePostRequest($withParamReCaptcha); + $this->assertSessionMessages( + $this->equalTo(['Something went wrong with reCAPTCHA. Please contact the store owner.']), + MessageInterface::TYPE_ERROR + ); + } + + /** + * Makes post request + * + * @param bool $withParamReCaptcha + * @return void + */ + private function makePostRequest(bool $withParamReCaptcha = false): void + { + $postValue = [ + 'form_key' => $this->formKey, + 'emails' => 'example1@gmail.com, example2@gmail.com, example3@gmail.com', + ]; + + if ($withParamReCaptcha) { + $postValue[CaptchaResponseResolverInterface::PARAM_RECAPTCHA] = 'test'; + } + + $this->getRequest() + ->setMethod(Http::METHOD_POST) + ->setPostValue($postValue); + + $this->dispatch('wishlist/index/send/wishlist_id/' . $this->wishlistId); + } + + /** + * @inheritDoc + */ + public function tearDown(): void + { + $this->customerSession->setCustomerId(null); + parent::tearDown(); + } +} diff --git a/ReCaptchaWishlist/composer.json b/ReCaptchaWishlist/composer.json new file mode 100644 index 00000000..20186ae6 --- /dev/null +++ b/ReCaptchaWishlist/composer.json @@ -0,0 +1,19 @@ +{ + "name": "magento/module-re-captcha-wishlist", + "description": "Google reCAPTCHA integration for Magento2", + "type": "magento2-module", + "license": "OSL-3.0", + "require": { + "php": "~8.2.0||~8.3.0||~8.4.0", + "magento/framework": "*", + "magento/module-re-captcha-ui": "*" + }, + "autoload": { + "files": [ + "registration.php" + ], + "psr-4": { + "Magento\\ReCaptchaWishlist\\": "" + } + } +} diff --git a/ReCaptchaWishlist/etc/adminhtml/system.xml b/ReCaptchaWishlist/etc/adminhtml/system.xml new file mode 100644 index 00000000..cf2fff86 --- /dev/null +++ b/ReCaptchaWishlist/etc/adminhtml/system.xml @@ -0,0 +1,21 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright 2024 Adobe + * All Rights Reserved. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd"> + <system> + <section id="recaptcha_frontend"> + <group id="type_for"> + <field id="wishlist" translate="label" type="select" sortOrder="180" showInDefault="1" + showInWebsite="1" showInStore="0" canRestore="1"> + <label>Enable for Wishlist Sharing</label> + <source_model>Magento\ReCaptchaAdminUi\Model\OptionSource\Type</source_model> + </field> + </group> + </section> + </system> +</config> diff --git a/ReCaptchaWishlist/etc/config.xml b/ReCaptchaWishlist/etc/config.xml new file mode 100644 index 00000000..e0438db5 --- /dev/null +++ b/ReCaptchaWishlist/etc/config.xml @@ -0,0 +1,17 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright 2024 Adobe + * All Rights Reserved. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Store:etc/config.xsd"> + <default> + <recaptcha_frontend> + <type_for> + <wishlist/> + </type_for> + </recaptcha_frontend> + </default> +</config> diff --git a/ReCaptchaWishlist/etc/frontend/events.xml b/ReCaptchaWishlist/etc/frontend/events.xml new file mode 100644 index 00000000..2a4a19d2 --- /dev/null +++ b/ReCaptchaWishlist/etc/frontend/events.xml @@ -0,0 +1,13 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright 2024 Adobe + * All Rights Reserved. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd"> + <event name="controller_action_predispatch_wishlist_index_send"> + <observer name="recaptcha_on_share_wish" instance="Magento\ReCaptchaWishlist\Observer\ShareWishlistObserver"/> + </event> +</config> diff --git a/ReCaptchaWishlist/etc/module.xml b/ReCaptchaWishlist/etc/module.xml new file mode 100644 index 00000000..dddb7ad5 --- /dev/null +++ b/ReCaptchaWishlist/etc/module.xml @@ -0,0 +1,11 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright 2024 Adobe + * All Rights Reserved. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd"> + <module name="Magento_ReCaptchaWishlist"/> +</config> diff --git a/ReCaptchaWishlist/registration.php b/ReCaptchaWishlist/registration.php new file mode 100644 index 00000000..999ad303 --- /dev/null +++ b/ReCaptchaWishlist/registration.php @@ -0,0 +1,13 @@ +<?php +/** + * Copyright 2024 Adobe + * All Rights Reserved. + */ + +declare(strict_types=1); + +\Magento\Framework\Component\ComponentRegistrar::register( + \Magento\Framework\Component\ComponentRegistrar::MODULE, + 'Magento_ReCaptchaWishlist', + __DIR__ +); diff --git a/ReCaptchaWishlist/view/frontend/layout/wishlist_index_share.xml b/ReCaptchaWishlist/view/frontend/layout/wishlist_index_share.xml new file mode 100644 index 00000000..619957ff --- /dev/null +++ b/ReCaptchaWishlist/view/frontend/layout/wishlist_index_share.xml @@ -0,0 +1,30 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright 2024 Adobe + * All Rights Reserved. + */ +--> +<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> + <body> + <referenceBlock name="wishlist.sharing"> + <block class="Magento\ReCaptchaUi\Block\ReCaptcha" + name="captcha" + after="-" + template="Magento_ReCaptchaFrontendUi::recaptcha.phtml" + ifconfig="recaptcha_frontend/type_for/wishlist"> + <arguments> + <argument name="recaptcha_for" xsi:type="string">wishlist</argument> + <argument name="jsLayout" xsi:type="array"> + <item name="components" xsi:type="array"> + <item name="recaptcha" xsi:type="array"> + <item name="component" xsi:type="string">Magento_ReCaptchaFrontendUi/js/reCaptcha</item> + </item> + </item> + </argument> + </arguments> + </block> + </referenceBlock> + </body> +</page> diff --git a/ReCaptchaWishlist/view/frontend/web/css/source/_module.less b/ReCaptchaWishlist/view/frontend/web/css/source/_module.less new file mode 100644 index 00000000..c80e1934 --- /dev/null +++ b/ReCaptchaWishlist/view/frontend/web/css/source/_module.less @@ -0,0 +1,8 @@ +/** + * Copyright 2024 Adobe + * All Rights Reserved. + */ + +.form.wishlist.share .g-recaptcha { + margin-bottom: 40px; +} diff --git a/Securitytxt/Controller/Index/Securitytxt.php b/Securitytxt/Controller/Index/Securitytxt.php index 685384a2..5e5e4e4a 100644 --- a/Securitytxt/Controller/Index/Securitytxt.php +++ b/Securitytxt/Controller/Index/Securitytxt.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2019 Adobe + * All Rights Reserved. */ declare(strict_types=1); diff --git a/Securitytxt/Controller/Index/Securitytxtsig.php b/Securitytxt/Controller/Index/Securitytxtsig.php index ddc828f2..e17919bf 100644 --- a/Securitytxt/Controller/Index/Securitytxtsig.php +++ b/Securitytxt/Controller/Index/Securitytxtsig.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2019 Adobe + * All Rights Reserved. */ declare(strict_types=1); diff --git a/Securitytxt/Controller/Router.php b/Securitytxt/Controller/Router.php index 3ed0cb7b..c806ee36 100644 --- a/Securitytxt/Controller/Router.php +++ b/Securitytxt/Controller/Router.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2019 Adobe + * All Rights Reserved. */ declare(strict_types=1); diff --git a/Securitytxt/Model/Config.php b/Securitytxt/Model/Config.php index 6af98e08..bdba190f 100644 --- a/Securitytxt/Model/Config.php +++ b/Securitytxt/Model/Config.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2019 Adobe + * All Rights Reserved. */ declare(strict_types=1); diff --git a/Securitytxt/Model/Config/Backend/SecureUrl.php b/Securitytxt/Model/Config/Backend/SecureUrl.php index 50e6bd71..00d0f67b 100644 --- a/Securitytxt/Model/Config/Backend/SecureUrl.php +++ b/Securitytxt/Model/Config/Backend/SecureUrl.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\Securitytxt\Model\Config\Backend; diff --git a/Securitytxt/Model/Config/Signature.php b/Securitytxt/Model/Config/Signature.php index a9d62d31..3ab20f02 100644 --- a/Securitytxt/Model/Config/Signature.php +++ b/Securitytxt/Model/Config/Signature.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2019 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\Securitytxt\Model\Config; diff --git a/Securitytxt/Model/Securitytxt.php b/Securitytxt/Model/Securitytxt.php index e23329bf..32dfcc8f 100644 --- a/Securitytxt/Model/Securitytxt.php +++ b/Securitytxt/Model/Securitytxt.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2019 Adobe + * All Rights Reserved. */ declare(strict_types=1); diff --git a/Securitytxt/README.md b/Securitytxt/README.md index c4fd8c2d..ab77ca8d 100644 --- a/Securitytxt/README.md +++ b/Securitytxt/README.md @@ -1,18 +1,18 @@ -# Security.txt +# Magento_Securitytxt module -### Summary -> When security vulnerabilities are discovered by researchers, proper reporting channels are often lacking. As a result, vulnerabilities may be left unreported. This document defines a format ("security.txt") to help organizations describe their vulnerability disclosure practices to make it easier for researchers to report vulnerabilities. +> When security vulnerabilities are discovered by researchers, proper reporting channels are often lacking. As a result, vulnerabilities may be left unreported. This document defines a format ("security.txt") to help organizations describe their vulnerability disclosure practices to make it easier for researchers to report vulnerabilities. -Source: https://tools.ietf.org/html/draft-foudil-securitytxt-09 +_Source: <https://datatracker.ietf.org/doc/html/draft-foudil-securitytxt-09>_ + +The Magento_Securitytxt module provides the following functionality: -The Magento_Securitytxt module provides the following functionality: * allows to save the security configurations in the admin panel -* contains a router to match application action class for requests to the `.well-known/security.txt` and `.well-known/security.txt.sig` files. -* serves the content of the `.well-known/security.txt` and `.well-known/security.txt.sig` files. +* contains a router to match application action class for requests to the `.well-known/security.txt` and `.well-known/security.txt.sig` files +* serves the content of the `.well-known/security.txt` and `.well-known/security.txt.sig` files -A valid security.txt file could look like the following example: +A valid _security.txt_ file could look like the following example: -``` +```txt Contact: mailto:security@example.com Contact: tel:+1-201-555-0123 Encryption: https://example.com/pgp.asc @@ -20,13 +20,18 @@ Acknowledgement: https://example.com/security/hall-of-fame Policy: https://example.com/security-policy.html Signature: https://example.com/.well-known/security.txt.sig ``` -Security.txt can be accessed at below location: + +_Security.txt_ can be accessed at the location of the following format: `https://example.com/.well-known/security.txt` -To create security.txt signature (security.txt.sig) file: +To create _security.txt_ signature (_security.txt.sig_) file, run the following command: -`gpg -u KEYID --output security.txt.sig --armor --detach-sig security.txt` +```bash +gpg -u KEYID --output security.txt.sig --armor --detach-sig security.txt +``` -To verify the security.txt file's signature: +To verify the _security.txt_ file's signature, run the following command: -`gpg --verify security.txt.sig security.txt` \ No newline at end of file +```bash +gpg --verify security.txt.sig security.txt +``` diff --git a/Securitytxt/Test/Mftf/ActionGroup/AssertFullSecurityTxtConfigurationActionGroup.xml b/Securitytxt/Test/Mftf/ActionGroup/AssertFullSecurityTxtConfigurationActionGroup.xml index 12fb8218..35351812 100644 --- a/Securitytxt/Test/Mftf/ActionGroup/AssertFullSecurityTxtConfigurationActionGroup.xml +++ b/Securitytxt/Test/Mftf/ActionGroup/AssertFullSecurityTxtConfigurationActionGroup.xml @@ -1,11 +1,10 @@ <?xml version="1.0" encoding="UTF-8"?> <!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ +/** + * Copyright 2019 Adobe + * All Rights Reserved. + */ --> - <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="AssertFullSecurityTxtConfiguration"> diff --git a/Securitytxt/Test/Mftf/ActionGroup/FillSecurityTxtConfigurationActionGroup.xml b/Securitytxt/Test/Mftf/ActionGroup/FillSecurityTxtConfigurationActionGroup.xml index 6b59f3ff..9c0fd7c7 100644 --- a/Securitytxt/Test/Mftf/ActionGroup/FillSecurityTxtConfigurationActionGroup.xml +++ b/Securitytxt/Test/Mftf/ActionGroup/FillSecurityTxtConfigurationActionGroup.xml @@ -1,11 +1,10 @@ <?xml version="1.0" encoding="UTF-8"?> <!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ +/** + * Copyright 2019 Adobe + * All Rights Reserved. + */ --> - <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="FillSecurityTxtConfigurationActionGroup"> diff --git a/Securitytxt/Test/Mftf/ActionGroup/OpenSecurityTxtPageActionGroup.xml b/Securitytxt/Test/Mftf/ActionGroup/OpenSecurityTxtPageActionGroup.xml index 90c44c5f..c2884ac5 100644 --- a/Securitytxt/Test/Mftf/ActionGroup/OpenSecurityTxtPageActionGroup.xml +++ b/Securitytxt/Test/Mftf/ActionGroup/OpenSecurityTxtPageActionGroup.xml @@ -1,11 +1,10 @@ <?xml version="1.0" encoding="UTF-8"?> <!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ +/** + * Copyright 2019 Adobe + * All Rights Reserved. + */ --> - <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="OpenSecurityTxtPage"> diff --git a/Securitytxt/Test/Mftf/ActionGroup/OpenSecurityTxtSignaturePageActionGroup.xml b/Securitytxt/Test/Mftf/ActionGroup/OpenSecurityTxtSignaturePageActionGroup.xml index bb017897..6b04a50a 100644 --- a/Securitytxt/Test/Mftf/ActionGroup/OpenSecurityTxtSignaturePageActionGroup.xml +++ b/Securitytxt/Test/Mftf/ActionGroup/OpenSecurityTxtSignaturePageActionGroup.xml @@ -1,11 +1,10 @@ <?xml version="1.0" encoding="UTF-8"?> <!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ +/** + * Copyright 2019 Adobe + * All Rights Reserved. + */ --> - <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="OpenSecurityTxtSignaturePage"> diff --git a/Securitytxt/Test/Mftf/Data/SecuritytxtConfigData.xml b/Securitytxt/Test/Mftf/Data/SecuritytxtConfigData.xml index 3804a106..e74c18fd 100644 --- a/Securitytxt/Test/Mftf/Data/SecuritytxtConfigData.xml +++ b/Securitytxt/Test/Mftf/Data/SecuritytxtConfigData.xml @@ -1,11 +1,10 @@ <?xml version="1.0" encoding="UTF-8"?> <!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ +/** + * Copyright 2019 Adobe + * All Rights Reserved. + */ --> - <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <entity name="EnableSecurityTxt"> diff --git a/Securitytxt/Test/Mftf/Data/SecuritytxtPageData.xml b/Securitytxt/Test/Mftf/Data/SecuritytxtPageData.xml index be1f0f06..48c65c7c 100644 --- a/Securitytxt/Test/Mftf/Data/SecuritytxtPageData.xml +++ b/Securitytxt/Test/Mftf/Data/SecuritytxtPageData.xml @@ -1,11 +1,10 @@ <?xml version="1.0" encoding="UTF-8"?> <!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ +/** + * Copyright 2019 Adobe + * All Rights Reserved. + */ --> - <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <entity name="SecurityTxtPageData"> diff --git a/Securitytxt/Test/Mftf/Page/AdminSecurityTxtConfigurationPage.xml b/Securitytxt/Test/Mftf/Page/AdminSecurityTxtConfigurationPage.xml index cd5462b0..8430bbb6 100644 --- a/Securitytxt/Test/Mftf/Page/AdminSecurityTxtConfigurationPage.xml +++ b/Securitytxt/Test/Mftf/Page/AdminSecurityTxtConfigurationPage.xml @@ -1,11 +1,10 @@ <?xml version="1.0" encoding="UTF-8"?> <!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ +/** + * Copyright 2019 Adobe + * All Rights Reserved. + */ --> - <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="AdminSecurityTxtConfigurationPage" url="admin/system_config/edit/section/magento_securitytxt_securitytxt/" area="admin" module="Magento_Securitytxt"> diff --git a/Securitytxt/Test/Mftf/Section/AdminConfigureSecurityTxtSection.xml b/Securitytxt/Test/Mftf/Section/AdminConfigureSecurityTxtSection.xml index 723fb3b8..e5f1a5d4 100644 --- a/Securitytxt/Test/Mftf/Section/AdminConfigureSecurityTxtSection.xml +++ b/Securitytxt/Test/Mftf/Section/AdminConfigureSecurityTxtSection.xml @@ -1,9 +1,9 @@ <?xml version="1.0" encoding="UTF-8"?> <!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ +/** + * Copyright 2019 Adobe + * All Rights Reserved. + */ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> diff --git a/Securitytxt/Test/Mftf/Test/RedirectToNotFoundWhenSecurityTxtIsDisabled.xml b/Securitytxt/Test/Mftf/Test/RedirectToNotFoundWhenSecurityTxtIsDisabled.xml index 5bb2e5fd..0b86f872 100644 --- a/Securitytxt/Test/Mftf/Test/RedirectToNotFoundWhenSecurityTxtIsDisabled.xml +++ b/Securitytxt/Test/Mftf/Test/RedirectToNotFoundWhenSecurityTxtIsDisabled.xml @@ -1,9 +1,9 @@ <?xml version="1.0" encoding="UTF-8"?> <!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ +/** + * Copyright 2019 Adobe + * All Rights Reserved. + */ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> diff --git a/Securitytxt/Test/Mftf/Test/SecurityTxtWithCorrectConfiguration.xml b/Securitytxt/Test/Mftf/Test/SecurityTxtWithCorrectConfiguration.xml index f25ed1ab..2a196fbb 100644 --- a/Securitytxt/Test/Mftf/Test/SecurityTxtWithCorrectConfiguration.xml +++ b/Securitytxt/Test/Mftf/Test/SecurityTxtWithCorrectConfiguration.xml @@ -1,9 +1,9 @@ <?xml version="1.0" encoding="UTF-8"?> <!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ +/** + * Copyright 2019 Adobe + * All Rights Reserved. + */ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> diff --git a/Securitytxt/Test/Mftf/class-file-naming-allowlist b/Securitytxt/Test/Mftf/class-file-naming-allowlist new file mode 100644 index 00000000..4f3447f8 --- /dev/null +++ b/Securitytxt/Test/Mftf/class-file-naming-allowlist @@ -0,0 +1,7 @@ +AssertFullSecurityTxtConfiguration +OpenSecurityTxtPage +OpenSecurityTxtSignaturePage + + + + diff --git a/Securitytxt/composer.json b/Securitytxt/composer.json index 667c555c..327970e6 100644 --- a/Securitytxt/composer.json +++ b/Securitytxt/composer.json @@ -3,7 +3,7 @@ "description": "Security.txt file for Magento 2 websites", "type": "magento2-module", "require": { - "php": "~7.4.0||~8.0.0||~8.1.0", + "php": "~8.2.0||~8.3.0||~8.4.0", "magento/framework": "*", "magento/module-config": "*", "magento/module-store": "*" diff --git a/Securitytxt/etc/acl.xml b/Securitytxt/etc/acl.xml index e72ef9c4..ea4bb036 100644 --- a/Securitytxt/etc/acl.xml +++ b/Securitytxt/etc/acl.xml @@ -1,8 +1,8 @@ <?xml version="1.0"?> <!-- /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2019 Adobe + * All Rights Reserved. */ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" diff --git a/Securitytxt/etc/adminhtml/system.xml b/Securitytxt/etc/adminhtml/system.xml index 08474520..8f112806 100644 --- a/Securitytxt/etc/adminhtml/system.xml +++ b/Securitytxt/etc/adminhtml/system.xml @@ -1,8 +1,8 @@ <?xml version="1.0"?> <!-- /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2019 Adobe + * All Rights Reserved. */ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" diff --git a/Securitytxt/etc/config.xml b/Securitytxt/etc/config.xml index c2b3a47a..60b2f2d5 100644 --- a/Securitytxt/etc/config.xml +++ b/Securitytxt/etc/config.xml @@ -1,8 +1,8 @@ <?xml version="1.0"?> <!-- /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" diff --git a/Securitytxt/etc/di.xml b/Securitytxt/etc/di.xml index 4c44f1e2..f33a244e 100644 --- a/Securitytxt/etc/di.xml +++ b/Securitytxt/etc/di.xml @@ -1,8 +1,8 @@ <?xml version="1.0"?> <!-- /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2019 Adobe + * All Rights Reserved. */ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" diff --git a/Securitytxt/etc/frontend/di.xml b/Securitytxt/etc/frontend/di.xml index 36ca7d97..fdc675df 100644 --- a/Securitytxt/etc/frontend/di.xml +++ b/Securitytxt/etc/frontend/di.xml @@ -1,8 +1,8 @@ <?xml version="1.0"?> <!-- /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2019 Adobe + * All Rights Reserved. */ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" diff --git a/Securitytxt/etc/frontend/routes.xml b/Securitytxt/etc/frontend/routes.xml index e2cfd519..5832bf3c 100644 --- a/Securitytxt/etc/frontend/routes.xml +++ b/Securitytxt/etc/frontend/routes.xml @@ -1,8 +1,8 @@ <?xml version="1.0"?> <!-- /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2019 Adobe + * All Rights Reserved. */ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" diff --git a/Securitytxt/etc/module.xml b/Securitytxt/etc/module.xml index 80561d37..0bcf6b65 100644 --- a/Securitytxt/etc/module.xml +++ b/Securitytxt/etc/module.xml @@ -1,8 +1,8 @@ <?xml version="1.0"?> <!-- /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2019 Adobe + * All Rights Reserved. */ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" diff --git a/Securitytxt/registration.php b/Securitytxt/registration.php index 8ed7af62..7f72224c 100644 --- a/Securitytxt/registration.php +++ b/Securitytxt/registration.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2019 Adobe + * All Rights Reserved. */ use \Magento\Framework\Component\ComponentRegistrar; diff --git a/TwoFactorAuth/Api/AdminTokenServiceInterface.php b/TwoFactorAuth/Api/AdminTokenServiceInterface.php index 519b568f..e7708138 100644 --- a/TwoFactorAuth/Api/AdminTokenServiceInterface.php +++ b/TwoFactorAuth/Api/AdminTokenServiceInterface.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ declare(strict_types=1); diff --git a/TwoFactorAuth/Api/AuthyAuthenticateInterface.php b/TwoFactorAuth/Api/AuthyAuthenticateInterface.php index f6642550..ca713c23 100644 --- a/TwoFactorAuth/Api/AuthyAuthenticateInterface.php +++ b/TwoFactorAuth/Api/AuthyAuthenticateInterface.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ declare(strict_types=1); diff --git a/TwoFactorAuth/Api/AuthyConfigureInterface.php b/TwoFactorAuth/Api/AuthyConfigureInterface.php index 22afeecb..027d3171 100644 --- a/TwoFactorAuth/Api/AuthyConfigureInterface.php +++ b/TwoFactorAuth/Api/AuthyConfigureInterface.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ declare(strict_types=1); diff --git a/TwoFactorAuth/Api/CountryRepositoryInterface.php b/TwoFactorAuth/Api/CountryRepositoryInterface.php index efe695c5..7b242784 100644 --- a/TwoFactorAuth/Api/CountryRepositoryInterface.php +++ b/TwoFactorAuth/Api/CountryRepositoryInterface.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Api; diff --git a/TwoFactorAuth/Api/Data/AdminTokenResponseInterface.php b/TwoFactorAuth/Api/Data/AdminTokenResponseInterface.php index 067d1cb3..1c928f4f 100644 --- a/TwoFactorAuth/Api/Data/AdminTokenResponseInterface.php +++ b/TwoFactorAuth/Api/Data/AdminTokenResponseInterface.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ declare(strict_types=1); diff --git a/TwoFactorAuth/Api/Data/AuthyDeviceInterface.php b/TwoFactorAuth/Api/Data/AuthyDeviceInterface.php index e961410a..8f3ac6c3 100644 --- a/TwoFactorAuth/Api/Data/AuthyDeviceInterface.php +++ b/TwoFactorAuth/Api/Data/AuthyDeviceInterface.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Api\Data; diff --git a/TwoFactorAuth/Api/Data/AuthyRegistrationPromptResponseInterface.php b/TwoFactorAuth/Api/Data/AuthyRegistrationPromptResponseInterface.php index 993270da..5a21c99b 100644 --- a/TwoFactorAuth/Api/Data/AuthyRegistrationPromptResponseInterface.php +++ b/TwoFactorAuth/Api/Data/AuthyRegistrationPromptResponseInterface.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ declare(strict_types=1); diff --git a/TwoFactorAuth/Api/Data/CountryInterface.php b/TwoFactorAuth/Api/Data/CountryInterface.php index 1a7c42fb..1797726c 100644 --- a/TwoFactorAuth/Api/Data/CountryInterface.php +++ b/TwoFactorAuth/Api/Data/CountryInterface.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Api\Data; diff --git a/TwoFactorAuth/Api/Data/CountrySearchResultsInterface.php b/TwoFactorAuth/Api/Data/CountrySearchResultsInterface.php index 18c87a21..1fefa000 100644 --- a/TwoFactorAuth/Api/Data/CountrySearchResultsInterface.php +++ b/TwoFactorAuth/Api/Data/CountrySearchResultsInterface.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Api\Data; diff --git a/TwoFactorAuth/Api/Data/DuoDataInterface.php b/TwoFactorAuth/Api/Data/DuoDataInterface.php index ae57ce22..00630e3e 100644 --- a/TwoFactorAuth/Api/Data/DuoDataInterface.php +++ b/TwoFactorAuth/Api/Data/DuoDataInterface.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Api\Data; @@ -12,23 +13,33 @@ /** * Represents the data needed to use duo * + * @deprecated This interface is no longer used. + * @see none * @api */ interface DuoDataInterface extends ExtensibleDataInterface { /** * Signature field name + * + * @deprecated + * @see none */ public const SIGNATURE = 'signature'; /** * Api host field name + * + * @deprecated + * @see none */ public const API_HOSTNAME = 'api_hostname'; /** * Get the signature * + * @deprecated + * @see none * @return string */ public function getSignature(): string; @@ -36,31 +47,39 @@ public function getSignature(): string; /** * Set the signature * + * @deprecated + * @see none * @param string $value * @return void */ public function setSignature(string $value): void; - /** - * Get the api hostname - * - * @return string - */ - public function getApiHostname(): string; - /** * Set the api hostname * + * @deprecated + * @see none * @param string $value * @return void */ public function setApiHostname(string $value): void; + /** + * Get the api hostname + * + * @deprecated + * @see none + * @return string + */ + public function getApiHostname(): string; + /** * Retrieve existing extension attributes object or create a new one * * Used fully qualified namespaces in annotations for proper work of extension interface/class code generation * + * @deprecated + * @see none * @return \Magento\TwoFactorAuth\Api\Data\DuoDataExtensionInterface|null */ public function getExtensionAttributes(): ?DuoDataExtensionInterface; @@ -68,6 +87,8 @@ public function getExtensionAttributes(): ?DuoDataExtensionInterface; /** * Set an extension attributes object * + * @deprecated + * @see none * @param \Magento\TwoFactorAuth\Api\Data\DuoDataExtensionInterface $extensionAttributes * @return void */ diff --git a/TwoFactorAuth/Api/Data/GoogleAuthenticateInterface.php b/TwoFactorAuth/Api/Data/GoogleAuthenticateInterface.php index fcdfc267..b40b4092 100644 --- a/TwoFactorAuth/Api/Data/GoogleAuthenticateInterface.php +++ b/TwoFactorAuth/Api/Data/GoogleAuthenticateInterface.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ declare(strict_types=1); diff --git a/TwoFactorAuth/Api/Data/GoogleConfigureInterface.php b/TwoFactorAuth/Api/Data/GoogleConfigureInterface.php index d401a7ae..68c9ec13 100644 --- a/TwoFactorAuth/Api/Data/GoogleConfigureInterface.php +++ b/TwoFactorAuth/Api/Data/GoogleConfigureInterface.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Api\Data; diff --git a/TwoFactorAuth/Api/Data/U2fWebAuthnRequestInterface.php b/TwoFactorAuth/Api/Data/U2fWebAuthnRequestInterface.php index 7a47b686..c4fc165d 100644 --- a/TwoFactorAuth/Api/Data/U2fWebAuthnRequestInterface.php +++ b/TwoFactorAuth/Api/Data/U2fWebAuthnRequestInterface.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ declare(strict_types=1); diff --git a/TwoFactorAuth/Api/Data/UserConfigInterface.php b/TwoFactorAuth/Api/Data/UserConfigInterface.php index 17b23510..6105467f 100644 --- a/TwoFactorAuth/Api/Data/UserConfigInterface.php +++ b/TwoFactorAuth/Api/Data/UserConfigInterface.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Api\Data; diff --git a/TwoFactorAuth/Api/Data/UserConfigSearchResultsInterface.php b/TwoFactorAuth/Api/Data/UserConfigSearchResultsInterface.php index 140fea79..b0af4b82 100644 --- a/TwoFactorAuth/Api/Data/UserConfigSearchResultsInterface.php +++ b/TwoFactorAuth/Api/Data/UserConfigSearchResultsInterface.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Api\Data; diff --git a/TwoFactorAuth/Api/DuoAuthenticateInterface.php b/TwoFactorAuth/Api/DuoAuthenticateInterface.php index 221b539d..2ff76031 100644 --- a/TwoFactorAuth/Api/DuoAuthenticateInterface.php +++ b/TwoFactorAuth/Api/DuoAuthenticateInterface.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ declare(strict_types=1); @@ -20,6 +20,8 @@ interface DuoAuthenticateInterface /** * Get the information required to configure duo * + * @deprecated this method is deprecated and will be removed in a future release. + * @see none * @param string $username * @param string $password * @return \Magento\TwoFactorAuth\Api\Data\DuoDataInterface @@ -32,6 +34,9 @@ public function getAuthenticateData( /** * Authenticate and get an admin token * + * @deprecated this method is deprecated and will be removed in a future release. + * @see createAdminAccessTokenWithCredentialsAndPasscode + * * @param string $username * @param string $password * @param string $signatureResponse @@ -42,4 +47,18 @@ public function createAdminAccessTokenWithCredentials( string $password, string $signatureResponse ): string; + + /** + * Authenticate and get an admin token with passcode + * + * @param string $username + * @param string $password + * @param string $passcode + * @return string + */ + public function createAdminAccessTokenWithCredentialsAndPasscode( + string $username, + string $password, + string $passcode + ): string; } diff --git a/TwoFactorAuth/Api/DuoConfigureInterface.php b/TwoFactorAuth/Api/DuoConfigureInterface.php index c1c6e664..d31a49ea 100644 --- a/TwoFactorAuth/Api/DuoConfigureInterface.php +++ b/TwoFactorAuth/Api/DuoConfigureInterface.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ declare(strict_types=1); @@ -20,6 +20,9 @@ interface DuoConfigureInterface /** * Get the information required to configure duo * + * @deprecated this method is deprecated and will be removed in a future release. + * @see getDuoConfigurationData + * * @param string $tfaToken * @return \Magento\TwoFactorAuth\Api\Data\DuoDataInterface */ @@ -30,9 +33,29 @@ public function getConfigurationData( /** * Activate the provider and get an admin token * + * @deprecated this method is deprecated and will be removed in a future release. + * @see duoActivate * @param string $tfaToken * @param string $signatureResponse * @return void */ public function activate(string $tfaToken, string $signatureResponse): void; + + /** + * Configure duo for first time user + * + * @param string $tfaToken + * @return void + */ + public function getDuoConfigurationData( + string $tfaToken + ); + + /** + * Activate the provider and get an admin token + * + * @param string $tfaToken + * @return void + */ + public function duoActivate(string $tfaToken): void; } diff --git a/TwoFactorAuth/Api/EngineInterface.php b/TwoFactorAuth/Api/EngineInterface.php index 432b1680..dfd6229e 100644 --- a/TwoFactorAuth/Api/EngineInterface.php +++ b/TwoFactorAuth/Api/EngineInterface.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Api; diff --git a/TwoFactorAuth/Api/Exception/NotificationExceptionInterface.php b/TwoFactorAuth/Api/Exception/NotificationExceptionInterface.php index c80f4471..844198a6 100644 --- a/TwoFactorAuth/Api/Exception/NotificationExceptionInterface.php +++ b/TwoFactorAuth/Api/Exception/NotificationExceptionInterface.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ declare(strict_types=1); diff --git a/TwoFactorAuth/Api/GoogleAuthenticateInterface.php b/TwoFactorAuth/Api/GoogleAuthenticateInterface.php index f5cccd39..8feda581 100644 --- a/TwoFactorAuth/Api/GoogleAuthenticateInterface.php +++ b/TwoFactorAuth/Api/GoogleAuthenticateInterface.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ declare(strict_types=1); diff --git a/TwoFactorAuth/Api/GoogleConfigureInterface.php b/TwoFactorAuth/Api/GoogleConfigureInterface.php index 8c5f809a..0ae42240 100644 --- a/TwoFactorAuth/Api/GoogleConfigureInterface.php +++ b/TwoFactorAuth/Api/GoogleConfigureInterface.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ declare(strict_types=1); diff --git a/TwoFactorAuth/Api/ProviderInterface.php b/TwoFactorAuth/Api/ProviderInterface.php index 652493a6..7dacb982 100644 --- a/TwoFactorAuth/Api/ProviderInterface.php +++ b/TwoFactorAuth/Api/ProviderInterface.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Api; diff --git a/TwoFactorAuth/Api/ProviderPoolInterface.php b/TwoFactorAuth/Api/ProviderPoolInterface.php index 2806bcfe..498f84ce 100644 --- a/TwoFactorAuth/Api/ProviderPoolInterface.php +++ b/TwoFactorAuth/Api/ProviderPoolInterface.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Api; diff --git a/TwoFactorAuth/Api/TfaInterface.php b/TwoFactorAuth/Api/TfaInterface.php index a6c122d4..4db6101e 100644 --- a/TwoFactorAuth/Api/TfaInterface.php +++ b/TwoFactorAuth/Api/TfaInterface.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Api; diff --git a/TwoFactorAuth/Api/TfaSessionInterface.php b/TwoFactorAuth/Api/TfaSessionInterface.php index 4875bd17..2668dabb 100644 --- a/TwoFactorAuth/Api/TfaSessionInterface.php +++ b/TwoFactorAuth/Api/TfaSessionInterface.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Api; diff --git a/TwoFactorAuth/Api/TfatActionsInterface.php b/TwoFactorAuth/Api/TfatActionsInterface.php index f8cb2b40..b41335dd 100644 --- a/TwoFactorAuth/Api/TfatActionsInterface.php +++ b/TwoFactorAuth/Api/TfatActionsInterface.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ declare(strict_types=1); diff --git a/TwoFactorAuth/Api/U2fKeyAuthenticateInterface.php b/TwoFactorAuth/Api/U2fKeyAuthenticateInterface.php index 29ff7b15..f692e271 100644 --- a/TwoFactorAuth/Api/U2fKeyAuthenticateInterface.php +++ b/TwoFactorAuth/Api/U2fKeyAuthenticateInterface.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ declare(strict_types=1); diff --git a/TwoFactorAuth/Api/U2fKeyConfigReaderInterface.php b/TwoFactorAuth/Api/U2fKeyConfigReaderInterface.php index f6d0595d..169b498a 100644 --- a/TwoFactorAuth/Api/U2fKeyConfigReaderInterface.php +++ b/TwoFactorAuth/Api/U2fKeyConfigReaderInterface.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ declare(strict_types=1); diff --git a/TwoFactorAuth/Api/U2fKeyConfigureInterface.php b/TwoFactorAuth/Api/U2fKeyConfigureInterface.php index 19483ef4..e01b5649 100644 --- a/TwoFactorAuth/Api/U2fKeyConfigureInterface.php +++ b/TwoFactorAuth/Api/U2fKeyConfigureInterface.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ declare(strict_types=1); diff --git a/TwoFactorAuth/Api/UserConfigManagerInterface.php b/TwoFactorAuth/Api/UserConfigManagerInterface.php index 521d8c17..e4c0a8b1 100644 --- a/TwoFactorAuth/Api/UserConfigManagerInterface.php +++ b/TwoFactorAuth/Api/UserConfigManagerInterface.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Api; diff --git a/TwoFactorAuth/Api/UserConfigRepositoryInterface.php b/TwoFactorAuth/Api/UserConfigRepositoryInterface.php index d5d2dbfe..ae6c2cb9 100644 --- a/TwoFactorAuth/Api/UserConfigRepositoryInterface.php +++ b/TwoFactorAuth/Api/UserConfigRepositoryInterface.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Api; diff --git a/TwoFactorAuth/Api/UserConfigRequestManagerInterface.php b/TwoFactorAuth/Api/UserConfigRequestManagerInterface.php index a574d8e7..26b9a777 100644 --- a/TwoFactorAuth/Api/UserConfigRequestManagerInterface.php +++ b/TwoFactorAuth/Api/UserConfigRequestManagerInterface.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ declare(strict_types=1); diff --git a/TwoFactorAuth/Api/UserConfigTokenManagerInterface.php b/TwoFactorAuth/Api/UserConfigTokenManagerInterface.php index fa7ddc22..d428e704 100644 --- a/TwoFactorAuth/Api/UserConfigTokenManagerInterface.php +++ b/TwoFactorAuth/Api/UserConfigTokenManagerInterface.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ declare(strict_types=1); diff --git a/TwoFactorAuth/Api/UserNotifierInterface.php b/TwoFactorAuth/Api/UserNotifierInterface.php index 100d7636..76e9619b 100644 --- a/TwoFactorAuth/Api/UserNotifierInterface.php +++ b/TwoFactorAuth/Api/UserNotifierInterface.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ declare(strict_types=1); diff --git a/TwoFactorAuth/Block/Adminhtml/System/Config/Providers.php b/TwoFactorAuth/Block/Adminhtml/System/Config/Providers.php index caa0d6ac..d3fe95de 100644 --- a/TwoFactorAuth/Block/Adminhtml/System/Config/Providers.php +++ b/TwoFactorAuth/Block/Adminhtml/System/Config/Providers.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Block\Adminhtml\System\Config; @@ -46,7 +47,15 @@ protected function _getElementHtml(AbstractElement $element) '#twofactorauth_general_force_providers' => [ 'Magento_TwoFactorAuth/js/system/config/providers' => [ 'modalTitleText' => $this->getModalTitleText(), - 'modalContentBody' => $this->getModalContentBody() + 'modalContentBody' => $this->getModalContentBody(), + 'duoProviderValue' => 'duo_security', + 'duoFields' => [ + 'twofactorauth_duo_client_id', + 'twofactorauth_duo_client_secret', + 'twofactorauth_duo_api_hostname', + 'twofactorauth_duo_integration_key', + 'twofactorauth_duo_secret_key', + ] ] ] ]; diff --git a/TwoFactorAuth/Block/ChangeProvider.php b/TwoFactorAuth/Block/ChangeProvider.php index 6b251cc8..3084d9db 100644 --- a/TwoFactorAuth/Block/ChangeProvider.php +++ b/TwoFactorAuth/Block/ChangeProvider.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Block; diff --git a/TwoFactorAuth/Block/Configure.php b/TwoFactorAuth/Block/Configure.php index 2032cfa9..fe187d26 100644 --- a/TwoFactorAuth/Block/Configure.php +++ b/TwoFactorAuth/Block/Configure.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ declare(strict_types=1); diff --git a/TwoFactorAuth/Block/ConfigureLater.php b/TwoFactorAuth/Block/ConfigureLater.php index 446caca8..de139213 100644 --- a/TwoFactorAuth/Block/ConfigureLater.php +++ b/TwoFactorAuth/Block/ConfigureLater.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Block; diff --git a/TwoFactorAuth/Block/Provider/Authy/Auth.php b/TwoFactorAuth/Block/Provider/Authy/Auth.php index 18182240..30baba74 100644 --- a/TwoFactorAuth/Block/Provider/Authy/Auth.php +++ b/TwoFactorAuth/Block/Provider/Authy/Auth.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Block\Provider\Authy; diff --git a/TwoFactorAuth/Block/Provider/Authy/Configure.php b/TwoFactorAuth/Block/Provider/Authy/Configure.php index 40e97ae8..2796d4a5 100644 --- a/TwoFactorAuth/Block/Provider/Authy/Configure.php +++ b/TwoFactorAuth/Block/Provider/Authy/Configure.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Block\Provider\Authy; diff --git a/TwoFactorAuth/Block/Provider/Duo/Auth.php b/TwoFactorAuth/Block/Provider/Duo/Auth.php index 4f07843a..234e104c 100644 --- a/TwoFactorAuth/Block/Provider/Duo/Auth.php +++ b/TwoFactorAuth/Block/Provider/Duo/Auth.php @@ -1,14 +1,16 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Block\Provider\Duo; use Magento\Backend\Block\Template; use Magento\Backend\Model\Auth\Session; +use Magento\Framework\Exception\LocalizedException; use Magento\TwoFactorAuth\Model\Provider\Engine\DuoSecurity; /** @@ -48,15 +50,14 @@ public function __construct( */ public function getJsLayout() { - $this->jsLayout['components']['tfa-auth']['postUrl'] = - $this->getUrl('*/*/authpost', ['form_key' => $this->getFormKey()]); - - $this->jsLayout['components']['tfa-auth']['signature'] = - $this->duoSecurity->getRequestSignature($this->session->getUser()); - - $this->jsLayout['components']['tfa-auth']['apiHost'] = - $this->duoSecurity->getApiHostname(); - + $user = $this->session->getUser(); + if (!$user) { + throw new LocalizedException(__('User session not found.')); + } + $authUrl = $this->getData('auth_url'); + if ($authUrl) { + $this->jsLayout['components']['tfa-auth']['authUrl'] = $authUrl; + } return parent::getJsLayout(); } } diff --git a/TwoFactorAuth/Block/Provider/Google/Auth.php b/TwoFactorAuth/Block/Provider/Google/Auth.php index 221c44ca..93af80e1 100644 --- a/TwoFactorAuth/Block/Provider/Google/Auth.php +++ b/TwoFactorAuth/Block/Provider/Google/Auth.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Block\Provider\Google; diff --git a/TwoFactorAuth/Block/Provider/Google/Configure.php b/TwoFactorAuth/Block/Provider/Google/Configure.php index 1d1227d3..747927e6 100644 --- a/TwoFactorAuth/Block/Provider/Google/Configure.php +++ b/TwoFactorAuth/Block/Provider/Google/Configure.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Block\Provider\Google; diff --git a/TwoFactorAuth/Block/Provider/U2fKey/Auth.php b/TwoFactorAuth/Block/Provider/U2fKey/Auth.php index b884c75a..4542702a 100644 --- a/TwoFactorAuth/Block/Provider/U2fKey/Auth.php +++ b/TwoFactorAuth/Block/Provider/U2fKey/Auth.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Block\Provider\U2fKey; diff --git a/TwoFactorAuth/Block/Provider/U2fKey/Configure.php b/TwoFactorAuth/Block/Provider/U2fKey/Configure.php index f11b2351..489ae575 100644 --- a/TwoFactorAuth/Block/Provider/U2fKey/Configure.php +++ b/TwoFactorAuth/Block/Provider/U2fKey/Configure.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Block\Provider\U2fKey; diff --git a/TwoFactorAuth/Command/GoogleSecret.php b/TwoFactorAuth/Command/GoogleSecret.php index ba48fbab..0e7392c5 100644 --- a/TwoFactorAuth/Command/GoogleSecret.php +++ b/TwoFactorAuth/Command/GoogleSecret.php @@ -1,12 +1,14 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Command; +use Magento\Framework\Console\Cli; use Magento\Framework\Exception\LocalizedException; use Magento\TwoFactorAuth\Api\UserConfigManagerInterface; use Magento\TwoFactorAuth\Model\Provider\Engine\Google; @@ -68,8 +70,8 @@ protected function configure() $this->setName('security:tfa:google:set-secret'); $this->setDescription('Set the secret used for Google OTP generation.'); - $this->addArgument('user', InputArgument::REQUIRED, __('Username')); - $this->addArgument('secret', InputArgument::REQUIRED, __('Secret')); + $this->addArgument('user', InputArgument::REQUIRED, __('Username')->render()); + $this->addArgument('secret', InputArgument::REQUIRED, __('Secret')->render()); parent::configure(); } @@ -104,5 +106,7 @@ protected function execute(InputInterface $input, OutputInterface $output) ); $output->writeln((string)__('Google OTP secret has been set')); + + return Cli::RETURN_SUCCESS; } } diff --git a/TwoFactorAuth/Command/TfaProviders.php b/TwoFactorAuth/Command/TfaProviders.php index a2aeebe7..6810ee18 100644 --- a/TwoFactorAuth/Command/TfaProviders.php +++ b/TwoFactorAuth/Command/TfaProviders.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Command; diff --git a/TwoFactorAuth/Command/TfaReset.php b/TwoFactorAuth/Command/TfaReset.php index c3020ee0..eb297a73 100644 --- a/TwoFactorAuth/Command/TfaReset.php +++ b/TwoFactorAuth/Command/TfaReset.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Command; @@ -69,8 +70,8 @@ protected function configure() $this->setName('security:tfa:reset'); $this->setDescription('Reset configuration for one user'); - $this->addArgument('user', InputArgument::REQUIRED, __('Username')); - $this->addArgument('provider', InputArgument::REQUIRED, __('Provider code')); + $this->addArgument('user', InputArgument::REQUIRED, __('Username')->render()); + $this->addArgument('provider', InputArgument::REQUIRED, __('Provider code')->render()); parent::configure(); } diff --git a/TwoFactorAuth/Controller/Adminhtml/AbstractAction.php b/TwoFactorAuth/Controller/Adminhtml/AbstractAction.php index f3a3868f..9076fbb0 100644 --- a/TwoFactorAuth/Controller/Adminhtml/AbstractAction.php +++ b/TwoFactorAuth/Controller/Adminhtml/AbstractAction.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Controller\Adminhtml; diff --git a/TwoFactorAuth/Controller/Adminhtml/AbstractConfigureAction.php b/TwoFactorAuth/Controller/Adminhtml/AbstractConfigureAction.php index e7892d60..3efb73fe 100644 --- a/TwoFactorAuth/Controller/Adminhtml/AbstractConfigureAction.php +++ b/TwoFactorAuth/Controller/Adminhtml/AbstractConfigureAction.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ declare(strict_types=1); diff --git a/TwoFactorAuth/Controller/Adminhtml/Authy/Auth.php b/TwoFactorAuth/Controller/Adminhtml/Authy/Auth.php index e4f25cd8..1cf83c45 100644 --- a/TwoFactorAuth/Controller/Adminhtml/Authy/Auth.php +++ b/TwoFactorAuth/Controller/Adminhtml/Authy/Auth.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Controller\Adminhtml\Authy; diff --git a/TwoFactorAuth/Controller/Adminhtml/Authy/Authpost.php b/TwoFactorAuth/Controller/Adminhtml/Authy/Authpost.php old mode 100644 new mode 100755 index 1fd18f93..20cc20f0 --- a/TwoFactorAuth/Controller/Adminhtml/Authy/Authpost.php +++ b/TwoFactorAuth/Controller/Adminhtml/Authy/Authpost.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Controller\Adminhtml\Authy; @@ -19,9 +20,13 @@ use Magento\TwoFactorAuth\Controller\Adminhtml\AbstractAction; use Magento\TwoFactorAuth\Model\Provider\Engine\Authy; use Magento\User\Model\User; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\User\Model\ResourceModel\User as UserResource; +use Magento\Framework\App\ObjectManager; /** * @SuppressWarnings(PHPMD.CamelCaseMethodName) + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class Authpost extends AbstractAction implements HttpPostActionInterface { @@ -60,6 +65,26 @@ class Authpost extends AbstractAction implements HttpPostActionInterface */ private $alert; + /** + * Config path for the 2FA Attempts + */ + private const XML_PATH_2FA_RETRY_ATTEMPTS = 'twofactorauth/general/twofactorauth_retry'; + + /** + * Config path for the 2FA Attempts + */ + private const XML_PATH_2FA_LOCK_EXPIRE = 'twofactorauth/general/auth_lock_expire'; + + /** + * @var ScopeConfigInterface + */ + private $scopeConfig; + + /** + * @var UserResource + */ + private $userResource; + /** * @param Action\Context $context * @param Session $session @@ -69,6 +94,9 @@ class Authpost extends AbstractAction implements HttpPostActionInterface * @param TfaInterface $tfa * @param AlertInterface $alert * @param DataObjectFactory $dataObjectFactory + * @param UserResource|null $userResource + * @param ScopeConfigInterface|null $scopeConfig + * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( Action\Context $context, @@ -78,7 +106,9 @@ public function __construct( TfaSessionInterface $tfaSession, TfaInterface $tfa, AlertInterface $alert, - DataObjectFactory $dataObjectFactory + DataObjectFactory $dataObjectFactory, + ?UserResource $userResource = null, + ?ScopeConfigInterface $scopeConfig = null ) { parent::__construct($context); $this->tfa = $tfa; @@ -88,6 +118,8 @@ public function __construct( $this->authy = $authy; $this->dataObjectFactory = $dataObjectFactory; $this->alert = $alert; + $this->scopeConfig = $scopeConfig ?? ObjectManager::getInstance()->get(ScopeConfigInterface::class); + $this->userResource = $userResource ?? ObjectManager::getInstance()->get(UserResource::class); } /** @@ -109,6 +141,13 @@ public function execute() $result = $this->jsonFactory->create(); try { + if (!$this->allowApiRetries()) { //locked the user + $lockThreshold = $this->scopeConfig->getValue(self::XML_PATH_2FA_LOCK_EXPIRE); + if ($this->userResource->lock((int)$user->getId(), 0, $lockThreshold)) { + $result->setData(['success' => false, 'message' => "Your account is temporarily disabled."]); + return $result; + } + } $this->authy->verify($user, $this->dataObjectFactory->create([ 'data' => $this->getRequest()->getParams(), ])); @@ -141,4 +180,18 @@ protected function _isAllowed() $this->tfa->getProviderIsAllowed((int) $user->getId(), Authy::CODE) && $this->tfa->getProvider(Authy::CODE)->isActive((int) $user->getId()); } + + /** + * Check if retry attempt above threshold value + * + * @return bool + */ + private function allowApiRetries() : bool + { + $maxRetries = $this->scopeConfig->getValue(self::XML_PATH_2FA_RETRY_ATTEMPTS); + $verifyAttempts = $this->session->getOtpAttempt(); + $verifyAttempts = $verifyAttempts === null ? 1 : $verifyAttempts+1; + $this->session->setOtpAttempt($verifyAttempts); + return $maxRetries >= $verifyAttempts; + } } diff --git a/TwoFactorAuth/Controller/Adminhtml/Authy/Configure.php b/TwoFactorAuth/Controller/Adminhtml/Authy/Configure.php index 07356384..31ebfdb9 100644 --- a/TwoFactorAuth/Controller/Adminhtml/Authy/Configure.php +++ b/TwoFactorAuth/Controller/Adminhtml/Authy/Configure.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Controller\Adminhtml\Authy; diff --git a/TwoFactorAuth/Controller/Adminhtml/Authy/Configurepost.php b/TwoFactorAuth/Controller/Adminhtml/Authy/Configurepost.php index aa0bab38..b8ac3897 100644 --- a/TwoFactorAuth/Controller/Adminhtml/Authy/Configurepost.php +++ b/TwoFactorAuth/Controller/Adminhtml/Authy/Configurepost.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Controller\Adminhtml\Authy; diff --git a/TwoFactorAuth/Controller/Adminhtml/Authy/Configureverifypost.php b/TwoFactorAuth/Controller/Adminhtml/Authy/Configureverifypost.php index c94fffd9..b3e03f7f 100644 --- a/TwoFactorAuth/Controller/Adminhtml/Authy/Configureverifypost.php +++ b/TwoFactorAuth/Controller/Adminhtml/Authy/Configureverifypost.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Controller\Adminhtml\Authy; diff --git a/TwoFactorAuth/Controller/Adminhtml/Authy/Onetouch.php b/TwoFactorAuth/Controller/Adminhtml/Authy/Onetouch.php index 52da562d..f2b80cb7 100644 --- a/TwoFactorAuth/Controller/Adminhtml/Authy/Onetouch.php +++ b/TwoFactorAuth/Controller/Adminhtml/Authy/Onetouch.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Controller\Adminhtml\Authy; diff --git a/TwoFactorAuth/Controller/Adminhtml/Authy/Token.php b/TwoFactorAuth/Controller/Adminhtml/Authy/Token.php index f4fca06f..0de89748 100644 --- a/TwoFactorAuth/Controller/Adminhtml/Authy/Token.php +++ b/TwoFactorAuth/Controller/Adminhtml/Authy/Token.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Controller\Adminhtml\Authy; diff --git a/TwoFactorAuth/Controller/Adminhtml/Authy/Verify.php b/TwoFactorAuth/Controller/Adminhtml/Authy/Verify.php index badb4e45..56f3b636 100644 --- a/TwoFactorAuth/Controller/Adminhtml/Authy/Verify.php +++ b/TwoFactorAuth/Controller/Adminhtml/Authy/Verify.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Controller\Adminhtml\Authy; diff --git a/TwoFactorAuth/Controller/Adminhtml/Authy/Verifyonetouch.php b/TwoFactorAuth/Controller/Adminhtml/Authy/Verifyonetouch.php index 1812c1e6..28402fe8 100644 --- a/TwoFactorAuth/Controller/Adminhtml/Authy/Verifyonetouch.php +++ b/TwoFactorAuth/Controller/Adminhtml/Authy/Verifyonetouch.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Controller\Adminhtml\Authy; diff --git a/TwoFactorAuth/Controller/Adminhtml/Duo/Auth.php b/TwoFactorAuth/Controller/Adminhtml/Duo/Auth.php index e5fbfbaa..84009c82 100644 --- a/TwoFactorAuth/Controller/Adminhtml/Duo/Auth.php +++ b/TwoFactorAuth/Controller/Adminhtml/Duo/Auth.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Controller\Adminhtml\Duo; @@ -10,6 +11,8 @@ use Magento\Backend\Model\Auth\Session; use Magento\Backend\App\Action; use Magento\Framework\App\Action\HttpGetActionInterface; +use Magento\Framework\Controller\Result\RedirectFactory; +use Magento\Framework\Message\ManagerInterface; use Magento\Framework\View\Result\PageFactory; use Magento\TwoFactorAuth\Api\TfaInterface; use Magento\TwoFactorAuth\Api\UserConfigManagerInterface; @@ -48,6 +51,19 @@ class Auth extends AbstractAction implements HttpGetActionInterface */ private $tokenVerifier; + /** + * @var DuoSecurity + */ + private $duoSecurity; + /** + * @var ManagerInterface + */ + protected $messageManager; + /** + * @var RedirectFactory + */ + protected $resultRedirectFactory; + /** * @param Action\Context $context * @param Session $session @@ -55,6 +71,7 @@ class Auth extends AbstractAction implements HttpGetActionInterface * @param UserConfigManagerInterface $userConfigManager * @param TfaInterface $tfa * @param HtmlAreaTokenVerifier $tokenVerifier + * @param DuoSecurity $duoSecurity */ public function __construct( Action\Context $context, @@ -62,7 +79,8 @@ public function __construct( PageFactory $pageFactory, UserConfigManagerInterface $userConfigManager, TfaInterface $tfa, - HtmlAreaTokenVerifier $tokenVerifier + HtmlAreaTokenVerifier $tokenVerifier, + DuoSecurity $duoSecurity ) { parent::__construct($context); $this->tfa = $tfa; @@ -70,6 +88,9 @@ public function __construct( $this->pageFactory = $pageFactory; $this->userConfigManager = $userConfigManager; $this->tokenVerifier = $tokenVerifier; + $this->duoSecurity = $duoSecurity; + $this->messageManager = $context->getMessageManager(); + $this->resultRedirectFactory = $context->getResultRedirectFactory(); } /** @@ -87,8 +108,27 @@ private function getUser() */ public function execute() { + $user = $this->getUser(); + if (!$user) { + $this->messageManager->addErrorMessage(__('User session not found.')); + } $this->userConfigManager->setDefaultProvider((int)$this->getUser()->getId(), DuoSecurity::CODE); - return $this->pageFactory->create(); + + $username = $this->getUser()->getUserName(); + $state = $this->duoSecurity->generateDuoState(); + $this->session->setDuoState($state); + $response = $this->duoSecurity->initiateAuth($username, $state); + if ($response['status'] === 'failure') { + // if health check fails, skip the Duo prompt and choose different 2FA. + $this->messageManager->addErrorMessage($response['message']); + } + + $resultPage = $this->pageFactory->create(); + $block = $resultPage->getLayout()->getBlock('content'); + if ($block) { + $block->setData('auth_url', $response['redirect_url']); + } + return $resultPage; } /** diff --git a/TwoFactorAuth/Controller/Adminhtml/Duo/Authpost.php b/TwoFactorAuth/Controller/Adminhtml/Duo/Authpost.php index 1928332d..31e9fc84 100644 --- a/TwoFactorAuth/Controller/Adminhtml/Duo/Authpost.php +++ b/TwoFactorAuth/Controller/Adminhtml/Duo/Authpost.php @@ -1,15 +1,16 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Controller\Adminhtml\Duo; use Magento\Backend\Model\Auth\Session; use Magento\Backend\App\Action; -use Magento\Framework\App\Action\HttpPostActionInterface; +use Magento\Framework\App\Action\HttpGetActionInterface; use Magento\Framework\DataObjectFactory; use Magento\TwoFactorAuth\Model\AlertInterface; use Magento\TwoFactorAuth\Api\TfaInterface; @@ -24,7 +25,7 @@ * * @SuppressWarnings(PHPMD.CamelCaseMethodName) */ -class Authpost extends AbstractAction implements HttpPostActionInterface +class Authpost extends AbstractAction implements HttpGetActionInterface { /** * @var TfaInterface @@ -122,13 +123,17 @@ private function getUser() public function execute() { $user = $this->getUser(); - - if ($this->duoSecurity->verify($user, $this->dataObjectFactory->create([ - 'data' => $this->getRequest()->getParams(), - ]))) { - $this->tfa->getProvider(DuoSecurity::CODE)->activate((int) $user->getId()); - $this->tfaSession->grantAccess(); - return $this->_redirect($this->context->getBackendUrl()->getStartupPageUrl()); + $username = $user->getUserName(); + $savedState = $this->session->getDuoState(); + + if (!empty($savedState) && !empty($username) && ($this->getRequest()->getParam('state') == $savedState)) { + if ($this->duoSecurity->verify($user, $this->dataObjectFactory->create([ + 'data' => $this->getRequest()->getParams(), + ]))) { + $this->tfa->getProvider(DuoSecurity::CODE)->activate((int) $user->getId()); + $this->tfaSession->grantAccess(); + return $this->_redirect($this->context->getBackendUrl()->getStartupPageUrl()); + } } else { $this->alert->event( 'Magento_TwoFactorAuth', @@ -139,6 +144,8 @@ public function execute() return $this->_redirect('*/*/auth'); } + + return $this->_redirect('*/*/auth'); } /** diff --git a/TwoFactorAuth/Controller/Adminhtml/Duo/Configure.php b/TwoFactorAuth/Controller/Adminhtml/Duo/Configure.php index aa1c690f..5cefec63 100644 --- a/TwoFactorAuth/Controller/Adminhtml/Duo/Configure.php +++ b/TwoFactorAuth/Controller/Adminhtml/Duo/Configure.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Controller\Adminhtml\Duo; diff --git a/TwoFactorAuth/Controller/Adminhtml/Google/Auth.php b/TwoFactorAuth/Controller/Adminhtml/Google/Auth.php index df652239..96493a31 100644 --- a/TwoFactorAuth/Controller/Adminhtml/Google/Auth.php +++ b/TwoFactorAuth/Controller/Adminhtml/Google/Auth.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Controller\Adminhtml\Google; diff --git a/TwoFactorAuth/Controller/Adminhtml/Google/Authpost.php b/TwoFactorAuth/Controller/Adminhtml/Google/Authpost.php old mode 100644 new mode 100755 index f0437f1d..266b3f8c --- a/TwoFactorAuth/Controller/Adminhtml/Google/Authpost.php +++ b/TwoFactorAuth/Controller/Adminhtml/Google/Authpost.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Controller\Adminhtml\Google; @@ -19,6 +20,9 @@ use Magento\TwoFactorAuth\Controller\Adminhtml\AbstractAction; use Magento\TwoFactorAuth\Model\Provider\Engine\Google; use Magento\User\Model\User; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\User\Model\ResourceModel\User as UserResource; +use Magento\Framework\App\ObjectManager; /** * Google authenticator post controller @@ -61,6 +65,26 @@ class Authpost extends AbstractAction implements HttpPostActionInterface */ private $alert; + /** + * Config path for the 2FA Attempts + */ + private const XML_PATH_2FA_RETRY_ATTEMPTS = 'twofactorauth/general/twofactorauth_retry'; + + /** + * Config path for the 2FA Attempts + */ + private const XML_PATH_2FA_LOCK_EXPIRE = 'twofactorauth/general/auth_lock_expire'; + + /** + * @var ScopeConfigInterface + */ + private $scopeConfig; + + /** + * @var UserResource + */ + private $userResource; + /** * @param Action\Context $context * @param Session $session @@ -70,6 +94,9 @@ class Authpost extends AbstractAction implements HttpPostActionInterface * @param TfaInterface $tfa * @param AlertInterface $alert * @param DataObjectFactory $dataObjectFactory + * @param UserResource|null $userResource + * @param ScopeConfigInterface|null $scopeConfig + * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( Action\Context $context, @@ -79,7 +106,9 @@ public function __construct( TfaSessionInterface $tfaSession, TfaInterface $tfa, AlertInterface $alert, - DataObjectFactory $dataObjectFactory + DataObjectFactory $dataObjectFactory, + ?UserResource $userResource = null, + ?ScopeConfigInterface $scopeConfig = null ) { parent::__construct($context); $this->tfa = $tfa; @@ -89,6 +118,8 @@ public function __construct( $this->tfaSession = $tfaSession; $this->dataObjectFactory = $dataObjectFactory; $this->alert = $alert; + $this->scopeConfig = $scopeConfig ?? ObjectManager::getInstance()->get(ScopeConfigInterface::class); + $this->userResource = $userResource ?? ObjectManager::getInstance()->get(UserResource::class); } /** @@ -103,6 +134,14 @@ public function execute() /** @var \Magento\Framework\DataObject $request */ $request = $this->dataObjectFactory->create(['data' => $this->getRequest()->getParams()]); + if (!$this->allowApiRetries()) { //locked the user + $lockThreshold = $this->scopeConfig->getValue(self::XML_PATH_2FA_LOCK_EXPIRE); + if ($this->userResource->lock((int)$user->getId(), 0, $lockThreshold)) { + $response->setData(['success' => false, 'message' => "Your account is temporarily disabled."]); + return $response; + } + } + if ($this->google->verify($user, $request)) { $this->tfaSession->grantAccess(); $response->setData(['success' => true]); @@ -133,4 +172,18 @@ protected function _isAllowed() && $this->tfa->getProviderIsAllowed((int)$user->getId(), Google::CODE) && $this->tfa->getProvider(Google::CODE)->isActive((int)$user->getId()); } + + /** + * Check if retry attempt above threshold value + * + * @return bool + */ + private function allowApiRetries() : bool + { + $maxRetries = $this->scopeConfig->getValue(self::XML_PATH_2FA_RETRY_ATTEMPTS); + $verifyAttempts = $this->session->getOtpAttempt(); + $verifyAttempts = $verifyAttempts === null ? 1 : $verifyAttempts+1; + $this->session->setOtpAttempt($verifyAttempts); + return $maxRetries >= $verifyAttempts; + } } diff --git a/TwoFactorAuth/Controller/Adminhtml/Google/Configure.php b/TwoFactorAuth/Controller/Adminhtml/Google/Configure.php index 1b3e6743..9e6b21f0 100644 --- a/TwoFactorAuth/Controller/Adminhtml/Google/Configure.php +++ b/TwoFactorAuth/Controller/Adminhtml/Google/Configure.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Controller\Adminhtml\Google; diff --git a/TwoFactorAuth/Controller/Adminhtml/Google/Configurepost.php b/TwoFactorAuth/Controller/Adminhtml/Google/Configurepost.php index e4d823d3..b7e944e8 100644 --- a/TwoFactorAuth/Controller/Adminhtml/Google/Configurepost.php +++ b/TwoFactorAuth/Controller/Adminhtml/Google/Configurepost.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Controller\Adminhtml\Google; diff --git a/TwoFactorAuth/Controller/Adminhtml/Google/Qr.php b/TwoFactorAuth/Controller/Adminhtml/Google/Qr.php index f5eb03e6..e29304c5 100644 --- a/TwoFactorAuth/Controller/Adminhtml/Google/Qr.php +++ b/TwoFactorAuth/Controller/Adminhtml/Google/Qr.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Controller\Adminhtml\Google; diff --git a/TwoFactorAuth/Controller/Adminhtml/Tfa/AccessDenied.php b/TwoFactorAuth/Controller/Adminhtml/Tfa/AccessDenied.php index bce6590a..d42aee34 100644 --- a/TwoFactorAuth/Controller/Adminhtml/Tfa/AccessDenied.php +++ b/TwoFactorAuth/Controller/Adminhtml/Tfa/AccessDenied.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Controller\Adminhtml\Tfa; diff --git a/TwoFactorAuth/Controller/Adminhtml/Tfa/Configure.php b/TwoFactorAuth/Controller/Adminhtml/Tfa/Configure.php index 6643e85c..ad3cc1b4 100644 --- a/TwoFactorAuth/Controller/Adminhtml/Tfa/Configure.php +++ b/TwoFactorAuth/Controller/Adminhtml/Tfa/Configure.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ declare(strict_types=1); diff --git a/TwoFactorAuth/Controller/Adminhtml/Tfa/ConfigureLater.php b/TwoFactorAuth/Controller/Adminhtml/Tfa/ConfigureLater.php index dc07f8d6..12fd010a 100644 --- a/TwoFactorAuth/Controller/Adminhtml/Tfa/ConfigureLater.php +++ b/TwoFactorAuth/Controller/Adminhtml/Tfa/ConfigureLater.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ declare(strict_types=1); diff --git a/TwoFactorAuth/Controller/Adminhtml/Tfa/Configurepost.php b/TwoFactorAuth/Controller/Adminhtml/Tfa/Configurepost.php index e47ef0ea..f3c31abd 100644 --- a/TwoFactorAuth/Controller/Adminhtml/Tfa/Configurepost.php +++ b/TwoFactorAuth/Controller/Adminhtml/Tfa/Configurepost.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ declare(strict_types=1); diff --git a/TwoFactorAuth/Controller/Adminhtml/Tfa/Index.php b/TwoFactorAuth/Controller/Adminhtml/Tfa/Index.php index f428d6c4..3ea932d6 100644 --- a/TwoFactorAuth/Controller/Adminhtml/Tfa/Index.php +++ b/TwoFactorAuth/Controller/Adminhtml/Tfa/Index.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Controller\Adminhtml\Tfa; diff --git a/TwoFactorAuth/Controller/Adminhtml/Tfa/Requestconfig.php b/TwoFactorAuth/Controller/Adminhtml/Tfa/Requestconfig.php index 535026a3..ddb63cf5 100644 --- a/TwoFactorAuth/Controller/Adminhtml/Tfa/Requestconfig.php +++ b/TwoFactorAuth/Controller/Adminhtml/Tfa/Requestconfig.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ declare(strict_types=1); diff --git a/TwoFactorAuth/Controller/Adminhtml/Tfa/Reset.php b/TwoFactorAuth/Controller/Adminhtml/Tfa/Reset.php index 6f2d3a9d..35dc7e89 100644 --- a/TwoFactorAuth/Controller/Adminhtml/Tfa/Reset.php +++ b/TwoFactorAuth/Controller/Adminhtml/Tfa/Reset.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Controller\Adminhtml\Tfa; @@ -89,6 +90,7 @@ public function execute() */ protected function _isAllowed() { - return parent::_isAllowed() && $this->_authorization->isAllowed('Magento_TwoFactorAuth::tfa'); + return parent::_isAllowed() && $this->_authorization->isAllowed('Magento_TwoFactorAuth::tfa') + && $this->_authorization->isAllowed('Magento_User::acl_users'); } } diff --git a/TwoFactorAuth/Controller/Adminhtml/U2f/Auth.php b/TwoFactorAuth/Controller/Adminhtml/U2f/Auth.php index 613dcfa6..9ae692d5 100644 --- a/TwoFactorAuth/Controller/Adminhtml/U2f/Auth.php +++ b/TwoFactorAuth/Controller/Adminhtml/U2f/Auth.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Controller\Adminhtml\U2f; diff --git a/TwoFactorAuth/Controller/Adminhtml/U2f/Authpost.php b/TwoFactorAuth/Controller/Adminhtml/U2f/Authpost.php index 70905c05..9aae812d 100644 --- a/TwoFactorAuth/Controller/Adminhtml/U2f/Authpost.php +++ b/TwoFactorAuth/Controller/Adminhtml/U2f/Authpost.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Controller\Adminhtml\U2f; diff --git a/TwoFactorAuth/Controller/Adminhtml/U2f/Configure.php b/TwoFactorAuth/Controller/Adminhtml/U2f/Configure.php index 7c33c2c6..8ff2ea55 100644 --- a/TwoFactorAuth/Controller/Adminhtml/U2f/Configure.php +++ b/TwoFactorAuth/Controller/Adminhtml/U2f/Configure.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Controller\Adminhtml\U2f; diff --git a/TwoFactorAuth/Controller/Adminhtml/U2f/Configurepost.php b/TwoFactorAuth/Controller/Adminhtml/U2f/Configurepost.php index 91978915..2023cfd2 100644 --- a/TwoFactorAuth/Controller/Adminhtml/U2f/Configurepost.php +++ b/TwoFactorAuth/Controller/Adminhtml/U2f/Configurepost.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Controller\Adminhtml\U2f; diff --git a/TwoFactorAuth/Model/AdminAccessTokenService.php b/TwoFactorAuth/Model/AdminAccessTokenService.php index 007878a0..9cdc0b1c 100644 --- a/TwoFactorAuth/Model/AdminAccessTokenService.php +++ b/TwoFactorAuth/Model/AdminAccessTokenService.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ declare(strict_types=1); diff --git a/TwoFactorAuth/Model/Alert.php b/TwoFactorAuth/Model/Alert.php index f6d28044..eab11460 100644 --- a/TwoFactorAuth/Model/Alert.php +++ b/TwoFactorAuth/Model/Alert.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Model; diff --git a/TwoFactorAuth/Model/AlertInterface.php b/TwoFactorAuth/Model/AlertInterface.php index ce07377d..85e020f1 100644 --- a/TwoFactorAuth/Model/AlertInterface.php +++ b/TwoFactorAuth/Model/AlertInterface.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Model; diff --git a/TwoFactorAuth/Model/Config/Backend/Duo/ApiHostname.php b/TwoFactorAuth/Model/Config/Backend/Duo/ApiHostname.php index fcc9cc6a..24a250f2 100644 --- a/TwoFactorAuth/Model/Config/Backend/Duo/ApiHostname.php +++ b/TwoFactorAuth/Model/Config/Backend/Duo/ApiHostname.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ declare(strict_types=1); diff --git a/TwoFactorAuth/Model/Config/Backend/ForceProviders.php b/TwoFactorAuth/Model/Config/Backend/ForceProviders.php index dc093b52..220535ab 100644 --- a/TwoFactorAuth/Model/Config/Backend/ForceProviders.php +++ b/TwoFactorAuth/Model/Config/Backend/ForceProviders.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ declare(strict_types=1); @@ -44,8 +44,8 @@ public function __construct( ScopeConfigInterface $config, TypeListInterface $cacheTypeList, TfaInterface $tfa, - AbstractResource $resource = null, - AbstractDb $resourceCollection = null, + ?AbstractResource $resource = null, + ?AbstractDb $resourceCollection = null, array $data = [] ) { parent::__construct( @@ -76,7 +76,7 @@ public function beforeSave() $value = explode(',', $value); } $validValues = is_array($value) ? array_intersect($codes, $value) : []; - if (empty($value) || !$validValues) { + if (count($value) === 0 || count($validValues) === 0) { throw new ValidatorException(__('You have to select at least one Two-Factor Authorization provider')); } diff --git a/TwoFactorAuth/Model/Config/Backend/Leeway.php b/TwoFactorAuth/Model/Config/Backend/Leeway.php new file mode 100644 index 00000000..2909c95e --- /dev/null +++ b/TwoFactorAuth/Model/Config/Backend/Leeway.php @@ -0,0 +1,65 @@ +<?php +/** + * Copyright 2024 Adobe + * All Rights Reserved. + */ + +declare(strict_types=1); + +namespace Magento\TwoFactorAuth\Model\Config\Backend; + +use Magento\Framework\App\Config\Value; +use Magento\Framework\App\Config\Data\ProcessorInterface; +use Magento\Framework\Exception\ValidatorException; +use OTPHP\TOTPInterface; + +class Leeway extends Value implements ProcessorInterface +{ + /** + * Fetch Totp default period value + * + * @return int + */ + private function getDefaultPeriod(): int + { + return TOTPInterface::DEFAULT_PERIOD; + } + + /** + * Process the value before saving. + * + * @param mixed $value The configuration value. + * @return mixed The processed value. + * @throws ValidatorException If the value is invalid. + */ + public function processValue($value) + { + if (!is_numeric($value)) { + throw new ValidatorException(__('The Leeway must be a numeric value.')); + } + $numericValue = (int) $value; + return $numericValue; + } + + /** + * Validates the value before saving. + * + * @throws ValidatorException If the value is invalid. + */ + public function beforeSave() + { + $value = $this->getValue(); + $period = $this->getDefaultPeriod(); + if (!is_numeric($value) || $value < 1 || $value >= $period) { + throw new ValidatorException( + __( + 'Invalid Leeway value. It must be between 1 and %1 as default period is %2', + $period-1, + $period + ) + ); + } + + return parent::beforeSave(); + } +} diff --git a/TwoFactorAuth/Model/Config/Source/EnabledProvider.php b/TwoFactorAuth/Model/Config/Source/EnabledProvider.php index 3b85fae2..86abd910 100644 --- a/TwoFactorAuth/Model/Config/Source/EnabledProvider.php +++ b/TwoFactorAuth/Model/Config/Source/EnabledProvider.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Model\Config\Source; diff --git a/TwoFactorAuth/Model/Config/Source/Provider.php b/TwoFactorAuth/Model/Config/Source/Provider.php index 3dc1e6eb..55f7e3a7 100644 --- a/TwoFactorAuth/Model/Config/Source/Provider.php +++ b/TwoFactorAuth/Model/Config/Source/Provider.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Model\Config\Source; diff --git a/TwoFactorAuth/Model/Config/UserNotifier.php b/TwoFactorAuth/Model/Config/UserNotifier.php index 9ad5c61a..ded21dfa 100644 --- a/TwoFactorAuth/Model/Config/UserNotifier.php +++ b/TwoFactorAuth/Model/Config/UserNotifier.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ declare(strict_types=1); diff --git a/TwoFactorAuth/Model/Config/WebApiUserNotifier.php b/TwoFactorAuth/Model/Config/WebApiUserNotifier.php index 95fe91cf..3d5f9667 100644 --- a/TwoFactorAuth/Model/Config/WebApiUserNotifier.php +++ b/TwoFactorAuth/Model/Config/WebApiUserNotifier.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ declare(strict_types=1); diff --git a/TwoFactorAuth/Model/Country.php b/TwoFactorAuth/Model/Country.php index b7716965..6cdc3384 100644 --- a/TwoFactorAuth/Model/Country.php +++ b/TwoFactorAuth/Model/Country.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Model; @@ -48,8 +49,8 @@ public function __construct( Registry $registry, DataObjectHelper $dataObjectHelper, CountryInterfaceFactory $countryDataFactory, - AbstractResource $resource = null, - AbstractDb $resourceCollection = null, + ?AbstractResource $resource = null, + ?AbstractDb $resourceCollection = null, array $data = [] ) { parent::__construct($context, $registry, $resource, $resourceCollection, $data); diff --git a/TwoFactorAuth/Model/CountryRegistry.php b/TwoFactorAuth/Model/CountryRegistry.php index d45f11cd..edb3ff0e 100644 --- a/TwoFactorAuth/Model/CountryRegistry.php +++ b/TwoFactorAuth/Model/CountryRegistry.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Model; diff --git a/TwoFactorAuth/Model/Data/AdminTokenResponse.php b/TwoFactorAuth/Model/Data/AdminTokenResponse.php index 956ca842..bcc239ab 100644 --- a/TwoFactorAuth/Model/Data/AdminTokenResponse.php +++ b/TwoFactorAuth/Model/Data/AdminTokenResponse.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Model\Data; diff --git a/TwoFactorAuth/Model/Data/Country.php b/TwoFactorAuth/Model/Data/Country.php index 35c43b79..ea880df6 100644 --- a/TwoFactorAuth/Model/Data/Country.php +++ b/TwoFactorAuth/Model/Data/Country.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Model\Data; diff --git a/TwoFactorAuth/Model/Data/Provider/Engine/Authy/Device.php b/TwoFactorAuth/Model/Data/Provider/Engine/Authy/Device.php index 68eddac3..1f1e93d1 100644 --- a/TwoFactorAuth/Model/Data/Provider/Engine/Authy/Device.php +++ b/TwoFactorAuth/Model/Data/Provider/Engine/Authy/Device.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ declare(strict_types=1); diff --git a/TwoFactorAuth/Model/Data/Provider/Engine/Authy/RegistrationResponse.php b/TwoFactorAuth/Model/Data/Provider/Engine/Authy/RegistrationResponse.php index 99b7f5a6..677e928a 100644 --- a/TwoFactorAuth/Model/Data/Provider/Engine/Authy/RegistrationResponse.php +++ b/TwoFactorAuth/Model/Data/Provider/Engine/Authy/RegistrationResponse.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ declare(strict_types=1); diff --git a/TwoFactorAuth/Model/Data/Provider/Engine/DuoSecurity/Data.php b/TwoFactorAuth/Model/Data/Provider/Engine/DuoSecurity/Data.php index a2bcda14..a73d3062 100644 --- a/TwoFactorAuth/Model/Data/Provider/Engine/DuoSecurity/Data.php +++ b/TwoFactorAuth/Model/Data/Provider/Engine/DuoSecurity/Data.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ declare(strict_types=1); @@ -10,13 +10,24 @@ use Magento\Framework\Model\AbstractExtensibleModel; use Magento\TwoFactorAuth\Api\Data\DuoDataExtensionInterface; -use Magento\TwoFactorAuth\Api\Data\DuoDataInterface; /** + * This class was originally implementing Magento\TwoFactorAuth\Api\Data\DuoDataInterface. + * It no longer implements the interface but maintains backward compatibility. * Represents the data needed to authenticate with duo */ -class Data extends AbstractExtensibleModel implements DuoDataInterface +class Data extends AbstractExtensibleModel { + /** + * Signature field name + */ + public const SIGNATURE = 'signature'; + + /** + * Api host field name + */ + public const API_HOSTNAME = 'api_hostname'; + /** * @inheritDoc */ diff --git a/TwoFactorAuth/Model/Data/Provider/Engine/Google/AuthenticateData.php b/TwoFactorAuth/Model/Data/Provider/Engine/Google/AuthenticateData.php index e01cade1..25adcbd0 100644 --- a/TwoFactorAuth/Model/Data/Provider/Engine/Google/AuthenticateData.php +++ b/TwoFactorAuth/Model/Data/Provider/Engine/Google/AuthenticateData.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ declare(strict_types=1); diff --git a/TwoFactorAuth/Model/Data/Provider/Engine/Google/ConfigurationData.php b/TwoFactorAuth/Model/Data/Provider/Engine/Google/ConfigurationData.php index 44378481..8018054a 100644 --- a/TwoFactorAuth/Model/Data/Provider/Engine/Google/ConfigurationData.php +++ b/TwoFactorAuth/Model/Data/Provider/Engine/Google/ConfigurationData.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ declare(strict_types=1); diff --git a/TwoFactorAuth/Model/Data/Provider/Engine/U2fkey/WebAuthnRequest.php b/TwoFactorAuth/Model/Data/Provider/Engine/U2fkey/WebAuthnRequest.php index dc5de8a3..942206f8 100644 --- a/TwoFactorAuth/Model/Data/Provider/Engine/U2fkey/WebAuthnRequest.php +++ b/TwoFactorAuth/Model/Data/Provider/Engine/U2fkey/WebAuthnRequest.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ declare(strict_types=1); diff --git a/TwoFactorAuth/Model/Data/UserConfig.php b/TwoFactorAuth/Model/Data/UserConfig.php index f67e25f6..7f1012cc 100644 --- a/TwoFactorAuth/Model/Data/UserConfig.php +++ b/TwoFactorAuth/Model/Data/UserConfig.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Model\Data; diff --git a/TwoFactorAuth/Model/EmailUserNotifier.php b/TwoFactorAuth/Model/EmailUserNotifier.php index 7ab01949..c53ef6a7 100644 --- a/TwoFactorAuth/Model/EmailUserNotifier.php +++ b/TwoFactorAuth/Model/EmailUserNotifier.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ declare(strict_types=1); diff --git a/TwoFactorAuth/Model/Exception/NotificationException.php b/TwoFactorAuth/Model/Exception/NotificationException.php index 679cac51..4b676e8e 100644 --- a/TwoFactorAuth/Model/Exception/NotificationException.php +++ b/TwoFactorAuth/Model/Exception/NotificationException.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ declare(strict_types=1); diff --git a/TwoFactorAuth/Model/Provider.php b/TwoFactorAuth/Model/Provider.php index 90195248..8beca4bc 100644 --- a/TwoFactorAuth/Model/Provider.php +++ b/TwoFactorAuth/Model/Provider.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Model; diff --git a/TwoFactorAuth/Model/Provider/Engine/Authy.php b/TwoFactorAuth/Model/Provider/Engine/Authy.php index efc59c5e..c433c382 100644 --- a/TwoFactorAuth/Model/Provider/Engine/Authy.php +++ b/TwoFactorAuth/Model/Provider/Engine/Authy.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Model\Provider\Engine; diff --git a/TwoFactorAuth/Model/Provider/Engine/Authy/Authenticate.php b/TwoFactorAuth/Model/Provider/Engine/Authy/Authenticate.php index 6318608e..cc6f580b 100644 --- a/TwoFactorAuth/Model/Provider/Engine/Authy/Authenticate.php +++ b/TwoFactorAuth/Model/Provider/Engine/Authy/Authenticate.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ declare(strict_types=1); diff --git a/TwoFactorAuth/Model/Provider/Engine/Authy/Configure.php b/TwoFactorAuth/Model/Provider/Engine/Authy/Configure.php index df68fcf3..4f34c1a0 100644 --- a/TwoFactorAuth/Model/Provider/Engine/Authy/Configure.php +++ b/TwoFactorAuth/Model/Provider/Engine/Authy/Configure.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ declare(strict_types=1); diff --git a/TwoFactorAuth/Model/Provider/Engine/Authy/OneTouch.php b/TwoFactorAuth/Model/Provider/Engine/Authy/OneTouch.php index 84c03d08..5a6be704 100644 --- a/TwoFactorAuth/Model/Provider/Engine/Authy/OneTouch.php +++ b/TwoFactorAuth/Model/Provider/Engine/Authy/OneTouch.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Model\Provider\Engine\Authy; diff --git a/TwoFactorAuth/Model/Provider/Engine/Authy/Service.php b/TwoFactorAuth/Model/Provider/Engine/Authy/Service.php index 8278a02e..fe27d7d8 100644 --- a/TwoFactorAuth/Model/Provider/Engine/Authy/Service.php +++ b/TwoFactorAuth/Model/Provider/Engine/Authy/Service.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Model\Provider\Engine\Authy; diff --git a/TwoFactorAuth/Model/Provider/Engine/Authy/Token.php b/TwoFactorAuth/Model/Provider/Engine/Authy/Token.php index 9dd58cb7..c49fa8b0 100644 --- a/TwoFactorAuth/Model/Provider/Engine/Authy/Token.php +++ b/TwoFactorAuth/Model/Provider/Engine/Authy/Token.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Model\Provider\Engine\Authy; diff --git a/TwoFactorAuth/Model/Provider/Engine/Authy/Verification.php b/TwoFactorAuth/Model/Provider/Engine/Authy/Verification.php index 13730aca..fbdf7acb 100644 --- a/TwoFactorAuth/Model/Provider/Engine/Authy/Verification.php +++ b/TwoFactorAuth/Model/Provider/Engine/Authy/Verification.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Model\Provider\Engine\Authy; diff --git a/TwoFactorAuth/Model/Provider/Engine/DuoSecurity.php b/TwoFactorAuth/Model/Provider/Engine/DuoSecurity.php index 362c444c..5ede11be 100644 --- a/TwoFactorAuth/Model/Provider/Engine/DuoSecurity.php +++ b/TwoFactorAuth/Model/Provider/Engine/DuoSecurity.php @@ -1,16 +1,21 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Model\Provider\Engine; +use Duo\DuoUniversal\DuoException; use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\DataObject; +use Magento\Framework\UrlInterface; use Magento\User\Api\Data\UserInterface; use Magento\TwoFactorAuth\Api\EngineInterface; +use Duo\DuoUniversal\Client; +use DuoAPI\Auth as DuoAuth; /** * Duo Security engine @@ -23,67 +28,83 @@ class DuoSecurity implements EngineInterface public const CODE = 'duo_security'; // Must be the same as defined in di.xml /** - * Duo request prefix + * Configuration XML path for enabled flag */ - public const DUO_PREFIX = 'TX'; + public const XML_PATH_ENABLED = 'twofactorauth/duo/enabled'; /** - * Duo app prefix + * Configuration XML path for Client Id */ - public const APP_PREFIX = 'APP'; + public const XML_PATH_CLIENT_ID = 'twofactorauth/duo/client_id'; /** - * Duo auth prefix + * Configuration XML path for Client secret */ - public const AUTH_PREFIX = 'AUTH'; + public const XML_PATH_CLIENT_SECRET = 'twofactorauth/duo/client_secret'; /** - * Duo expire time + * Configuration XML path for host name */ - public const DUO_EXPIRE = 300; + public const XML_PATH_API_HOSTNAME = 'twofactorauth/duo/api_hostname'; /** - * Application expire time + * Configuration XML path for integration key */ - public const APP_EXPIRE = 3600; + public const XML_PATH_IKEY = 'twofactorauth/duo/integration_key'; /** - * Configuration XML path for enabled flag + * Configuration XML path for secret key */ - public const XML_PATH_ENABLED = 'twofactorauth/duo/enabled'; + public const XML_PATH_SKEY = 'twofactorauth/duo/secret_key'; /** - * Configuration XML path for integration key - */ - public const XML_PATH_INTEGRATION_KEY = 'twofactorauth/duo/integration_key'; - - /** - * Configuration XML path for secret key + * @var ScopeConfigInterface */ - public const XML_PATH_SECRET_KEY = 'twofactorauth/duo/secret_key'; + private $scopeConfig; /** - * Configuration XML path for host name + * @var Client */ - public const XML_PATH_API_HOSTNAME = 'twofactorauth/duo/api_hostname'; + private $client; /** - * Configuration XML path for application key + * @var DuoAuth */ - public const XML_PATH_APPLICATION_KEY = 'twofactorauth/duo/application_key'; + private $duoAuth; /** - * @var ScopeConfigInterface + * @var UrlInterface */ - private $scopeConfig; + private $urlBuilder; /** * @param ScopeConfigInterface $scopeConfig + * @param UrlInterface $urlBuilder + * @param Client|null $client + * @param DuoAuth|null $duoAuth + * @throws \Duo\DuoUniversal\DuoException */ public function __construct( - ScopeConfigInterface $scopeConfig + ScopeConfigInterface $scopeConfig, + UrlInterface $urlBuilder, + ?Client $client = null, + ?DuoAuth $duoAuth = null ) { $this->scopeConfig = $scopeConfig; + $this->urlBuilder = $urlBuilder; + if ($this->isDuoForcedProvider()) { + $this->client = $client ?? new Client( + $this->getClientId(), + $this->getClientSecret(), + $this->getApiHostname(), + $this->getCallbackUrl() + ); + $this->duoAuth = $duoAuth ?? new DuoAuth( + $this->getIkey(), + $this->getSkey(), + $this->getApiHostname() + ); + } } /** @@ -97,163 +118,195 @@ public function getApiHostname(): string } /** - * Get application key + * Get Client Secret * * @return string */ - private function getApplicationKey(): string + private function getClientSecret(): string { - return $this->scopeConfig->getValue(static::XML_PATH_APPLICATION_KEY); + return $this->scopeConfig->getValue(static::XML_PATH_CLIENT_SECRET); } /** - * Get secret key + * Get Client Id * * @return string */ - private function getSecretKey(): string + private function getClientId(): string { - return $this->scopeConfig->getValue(static::XML_PATH_SECRET_KEY); + return $this->scopeConfig->getValue(static::XML_PATH_CLIENT_ID); } /** - * Get integration key + * Get callback URL * * @return string */ - private function getIntegrationKey(): string + private function getCallbackUrl(): string { - return $this->scopeConfig->getValue(static::XML_PATH_INTEGRATION_KEY); + return $this->urlBuilder->getUrl('tfa/duo/authpost'); } /** - * Sign values + * Get Integration Key * - * @param string $key - * @param string $values - * @param string $prefix - * @param int $expire - * @param int $time * @return string */ - private function signValues(string $key, string $values, string $prefix, int $expire, int $time): string + private function getIkey(): string { - $exp = $time + $expire; - $cookie = $prefix . '|' . base64_encode($values . '|' . $exp); - - $sig = hash_hmac('sha1', $cookie, $key); - return $cookie . '|' . $sig; + return $this->scopeConfig->getValue(static::XML_PATH_IKEY); } /** - * Parse signed values and return username + * Get Secret Key * - * @param string $key - * @param string $val - * @param string $prefix - * @param int $time - * @return string|null + * @return string */ - private function parseValues(string $key, string $val, string $prefix, int $time): ?string + private function getSkey(): string { - $integrationKey = $this->getIntegrationKey(); + return $this->scopeConfig->getValue(static::XML_PATH_SKEY); + } - $timestamp = ($time ? $time : time()); + /** + * Verify the user + * + * @param UserInterface $user + * @param DataObject $request + * @return bool + * @throws \Duo\DuoUniversal\DuoException + */ + public function verify(UserInterface $user, DataObject $request): bool + { + $duoCode = $request->getData('duo_code'); + $username = $user->getUserName(); - $parts = explode('|', $val); - if (count($parts) !== 3) { - return null; + try { + // Not saving token as this is for verification purpose + $this->client->exchangeAuthorizationCodeFor2FAResult($duoCode, $username); + } catch (DuoException $e) { + return false; } - [$uPrefix, $uB64, $uSig] = $parts; + # Exchange happened successfully so render success page + return true; + } - $sig = hash_hmac('sha1', $uPrefix . '|' . $uB64, $key); - if (hash_hmac('sha1', $sig, $key) !== hash_hmac('sha1', $uSig, $key)) { - return null; - } + /** + * Check if Duo is selected as forced provider + */ + private function isDuoForcedProvider(): bool + { + $providers = $this->scopeConfig->getValue('twofactorauth/general/force_providers') ?? ''; + $forcedProviders = array_map('trim', explode(',', $providers)); + return in_array(self::CODE, $forcedProviders, true); + } - if ($uPrefix !== $prefix) { - return null; + /** + * @inheritDoc + */ + public function isEnabled(): bool + { + try { + return $this->isDuoForcedProvider() && + !!$this->getApiHostname() && + !!$this->getClientId() && + !!$this->getClientSecret(); + } catch (\TypeError $exception) { + //At least one of the methods returned null instead of a string + return false; } + } - // @codingStandardsIgnoreStart - $cookieParts = explode('|', base64_decode($uB64)); - // @codingStandardsIgnoreEnd - - if (count($cookieParts) !== 3) { - return null; + /** + * Initiate authentication with Duo Universal Prompt + * + * @param string $username + * @param string $state + * @return array + * @throws \Duo\DuoUniversal\DuoException + */ + public function initiateAuth($username, string $state): array + { + try { + $this->healthCheck(); + } catch (DuoException $e) { + return [ + 'status' => 'failure', + 'redirect_url' => '', + 'message' => __("2FA Unavailable. Confirm Duo client/secret/host values are correct") + ]; } - [$user, $uIkey, $exp] = $cookieParts; - if ($uIkey !== $integrationKey) { - return null; - } - if ($timestamp >= (int) $exp) { - return null; - } + return [ + 'status' => 'success', + 'redirect_url' => $this->client->createAuthUrl($username, $state), + 'message' => __('Duo Auth URL created successfully.') + ]; + } - return $user; + /** + * Health check for Duo Universal prompt. + * + * @return void + * @throws \Duo\DuoUniversal\DuoException + */ + public function healthCheck(): void + { + $this->client->healthCheck(); } /** - * Get request signature + * Generate a state for Duo Universal prompt * - * @param UserInterface $user * @return string */ - public function getRequestSignature(UserInterface $user): string + public function generateDuoState() : string { - $time = time(); - - $values = $user->getUserName() . '|' . $this->getIntegrationKey(); - $duoSignature = $this->signValues( - $this->getSecretKey(), - $values, - static::DUO_PREFIX, - static::DUO_EXPIRE, - $time - ); - $appSignature = $this->signValues( - $this->getApplicationKey(), - $values, - static::APP_PREFIX, - static::APP_EXPIRE, - $time - ); - - return $duoSignature . ':' . $appSignature; + return $this->client->generateState(); } /** - * @inheritDoc + * Enroll a new user for Duo Auth API. + * + * @param string|null $username + * @param int|null $validSecs + * @return mixed */ - public function verify(UserInterface $user, DataObject $request): bool + public function enrollNewUser($username = null, $validSecs = null) { - $time = time(); - - $signatures = explode(':', (string)$request->getData('sig_response')); - if (count($signatures) !== 2) { - return false; - } - [$authSig, $appSig] = $signatures; - - $authUser = $this->parseValues($this->getSecretKey(), $authSig, static::AUTH_PREFIX, $time); - $appUser = $this->parseValues($this->getApplicationKey(), $appSig, static::APP_PREFIX, $time); + return $this->duoAuth->enroll($username, $validSecs); + } - return (($authUser === $appUser) && ($appUser === $user->getUserName())); + /** + * Check authentication for Duo Auth API. + * + * @param string $userIdentifier + * @param string|null $ipAddr + * @param string|null $trustedDeviceToken + * @param bool $username + * @return string + */ + public function assertUserIsValid($userIdentifier, $ipAddr = null, $trustedDeviceToken = null, $username = true) + { + $response = $this->duoAuth->preauth($userIdentifier, $ipAddr, $trustedDeviceToken, $username); + return $response['response']['response']['result']; } /** - * @inheritDoc + * Authorize a user with Duo Auth API. + * + * @param string $userIdentifier + * @param string $factor + * @param array $factorParams + * @param string|null $ipAddr + * @param bool $async + * @return array */ - public function isEnabled(): bool + public function authorizeUser($userIdentifier, $factor, $factorParams, $ipAddr = null, $async = false) { - try { - return !!$this->getApiHostname() && - !!$this->getIntegrationKey() && - !!$this->getSecretKey(); - } catch (\TypeError $exception) { - //At least one of the methods returned null instead of a string - return false; - } + $response = $this->duoAuth->auth($userIdentifier, $factor, $factorParams, $ipAddr, $async); + return [ + 'status' => $response['response']['response']['status'], + 'msg' => $response['response']['response']['status_msg'] + ]; } } diff --git a/TwoFactorAuth/Model/Provider/Engine/DuoSecurity/Authenticate.php b/TwoFactorAuth/Model/Provider/Engine/DuoSecurity/Authenticate.php index a12dc487..ae1eec00 100644 --- a/TwoFactorAuth/Model/Provider/Engine/DuoSecurity/Authenticate.php +++ b/TwoFactorAuth/Model/Provider/Engine/DuoSecurity/Authenticate.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ declare(strict_types=1); @@ -129,6 +129,8 @@ public function createAdminAccessTokenWithCredentials( /** * Assert that the given signature is valid for the user * + * @deprecated This method is deprecated and will be removed in a future release. + * @see assertResponseIsValidForPasscode * @param UserInterface $user * @param string $signatureResponse * @throws LocalizedException @@ -154,6 +156,48 @@ public function assertResponseIsValid(UserInterface $user, string $signatureResp } } + /** + * @inheritDoc + */ + public function createAdminAccessTokenWithCredentialsAndPasscode( + string $username, + string $password, + string $passcode + ): string { + $token = $this->adminTokenService->createAdminAccessToken($username, $password); + + $user = $this->getUser($username); + $this->userAuthenticator->assertProviderIsValidForUser((int)$user->getId(), DuoSecurity::CODE); + + $this->assertResponseIsValidForPasscode($user, $username, $passcode); + + return $token; + } + + /** + * Assert that the response from Duo is valid + * + * @param UserInterface $user + * @param string $username + * @param string $passcode + * @return void + * @throws LocalizedException + */ + public function assertResponseIsValidForPasscode(UserInterface $user, $username, string $passcode): void + { + $duoAuthResponse = $this->duo->authorizeUser($username, "passcode", ['passcode' => $passcode]); + if ($duoAuthResponse['status'] !== 'allow') { + $this->alert->event( + 'Magento_TwoFactorAuth', + 'DuoSecurity invalid auth '. $duoAuthResponse['msg'], + AlertInterface::LEVEL_WARNING, + $user->getUserName() + ); + + throw new LocalizedException(__('Invalid response')); + } + } + /** * Retrieve a user using the username * diff --git a/TwoFactorAuth/Model/Provider/Engine/DuoSecurity/Configure.php b/TwoFactorAuth/Model/Provider/Engine/DuoSecurity/Configure.php index f842cb89..0069c404 100644 --- a/TwoFactorAuth/Model/Provider/Engine/DuoSecurity/Configure.php +++ b/TwoFactorAuth/Model/Provider/Engine/DuoSecurity/Configure.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ declare(strict_types=1); @@ -95,4 +95,27 @@ public function activate(string $tfaToken, string $signatureResponse): void $this->tfa->getProviderByCode(DuoSecurity::CODE) ->activate($userId); } + + /** + * @inheritDoc + */ + public function getDuoConfigurationData(string $tfaToken) + { + $user = $this->userAuthenticator->authenticateWithTokenAndProvider($tfaToken, DuoSecurity::CODE); + return $this->duo->enrollNewUser($user->getUserName(), 60); + } + + /** + * @inheritDoc + */ + public function duoActivate(string $tfaToken): void + { + $user = $this->userAuthenticator->authenticateWithTokenAndProvider($tfaToken, DuoSecurity::CODE); + $userId = (int)$user->getId(); + + if ($this->duo->assertUserIsValid($user->getUserName()) == "auth") { + $this->tfa->getProviderByCode(DuoSecurity::CODE) + ->activate($userId); + } + } } diff --git a/TwoFactorAuth/Model/Provider/Engine/Google.php b/TwoFactorAuth/Model/Provider/Engine/Google.php index 9d77ba90..a8b69bdb 100644 --- a/TwoFactorAuth/Model/Provider/Engine/Google.php +++ b/TwoFactorAuth/Model/Provider/Engine/Google.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Model\Provider\Engine; @@ -10,8 +11,10 @@ use Base32\Base32; use Endroid\QrCode\Color\Color; use Endroid\QrCode\Encoding\Encoding; +use Endroid\QrCode\ErrorCorrectionLevel; use Endroid\QrCode\ErrorCorrectionLevel\ErrorCorrectionLevelHigh; use Endroid\QrCode\QrCode; +use Endroid\QrCode\RoundBlockSizeMode; use Endroid\QrCode\Writer\PngWriter; use Exception; use Magento\Framework\App\Config\ScopeConfigInterface; @@ -35,7 +38,7 @@ class Google implements EngineInterface /** * Config path for the OTP window */ - const XML_PATH_OTP_WINDOW = 'twofactorauth/google/otp_window'; + public const XML_PATH_LEEWAY = 'twofactorauth/google/leeway'; /** * Engine code @@ -199,7 +202,7 @@ public function verify(UserInterface $user, DataObject $request): bool return $totp->verify( $token, null, - $config['window'] ?? (int)$this->scopeConfig->getValue(self::XML_PATH_OTP_WINDOW) ?: null + $config['window'] ?? (int)$this->scopeConfig->getValue(self::XML_PATH_LEEWAY) ?: null ); } @@ -214,13 +217,17 @@ public function verify(UserInterface $user, DataObject $request): bool public function getQrCodeAsPng(UserInterface $user): string { // @codingStandardsIgnoreStart - $qrCode = new QrCode($this->getProvisioningUrl($user)); - $qrCode->setSize(400); - $qrCode->setMargin(0); - $qrCode->setErrorCorrectionLevel(new ErrorCorrectionLevelHigh()); - $qrCode->setForegroundColor(new Color(0, 0, 0, 0)); - $qrCode->setBackgroundColor(new Color(255, 255, 255, 0)); - $qrCode->setEncoding(new Encoding('UTF-8')); + $qrCode = new QrCode( + $this->getProvisioningUrl($user), + new Encoding('UTF-8'), + ErrorCorrectionLevel::High, + 400, + 0, + RoundBlockSizeMode::Margin, + new Color(0, 0, 0, 0), + new Color(255, 255, 255, 0) + ); + $writer = new PngWriter(); $pngData = $writer->write($qrCode); diff --git a/TwoFactorAuth/Model/Provider/Engine/Google/Authenticate.php b/TwoFactorAuth/Model/Provider/Engine/Google/Authenticate.php index bdfdc447..75b0781f 100644 --- a/TwoFactorAuth/Model/Provider/Engine/Google/Authenticate.php +++ b/TwoFactorAuth/Model/Provider/Engine/Google/Authenticate.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ declare(strict_types=1); diff --git a/TwoFactorAuth/Model/Provider/Engine/Google/Configure.php b/TwoFactorAuth/Model/Provider/Engine/Google/Configure.php index d1ecad49..d7c0f6b1 100644 --- a/TwoFactorAuth/Model/Provider/Engine/Google/Configure.php +++ b/TwoFactorAuth/Model/Provider/Engine/Google/Configure.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ declare(strict_types=1); diff --git a/TwoFactorAuth/Model/Provider/Engine/Google/TotpFactory.php b/TwoFactorAuth/Model/Provider/Engine/Google/TotpFactory.php index b0a72090..18d78ed9 100644 --- a/TwoFactorAuth/Model/Provider/Engine/Google/TotpFactory.php +++ b/TwoFactorAuth/Model/Provider/Engine/Google/TotpFactory.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ declare(strict_types=1); diff --git a/TwoFactorAuth/Model/Provider/Engine/U2fKey.php b/TwoFactorAuth/Model/Provider/Engine/U2fKey.php index a4a6b313..86ccfb58 100644 --- a/TwoFactorAuth/Model/Provider/Engine/U2fKey.php +++ b/TwoFactorAuth/Model/Provider/Engine/U2fKey.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Model\Provider\Engine; diff --git a/TwoFactorAuth/Model/Provider/Engine/U2fKey/Authenticate.php b/TwoFactorAuth/Model/Provider/Engine/U2fKey/Authenticate.php index 7b6abe21..865f88e1 100644 --- a/TwoFactorAuth/Model/Provider/Engine/U2fKey/Authenticate.php +++ b/TwoFactorAuth/Model/Provider/Engine/U2fKey/Authenticate.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ declare(strict_types=1); diff --git a/TwoFactorAuth/Model/Provider/Engine/U2fKey/ConfigReader.php b/TwoFactorAuth/Model/Provider/Engine/U2fKey/ConfigReader.php index ef4adaa2..ad8f731e 100644 --- a/TwoFactorAuth/Model/Provider/Engine/U2fKey/ConfigReader.php +++ b/TwoFactorAuth/Model/Provider/Engine/U2fKey/ConfigReader.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ declare(strict_types=1); diff --git a/TwoFactorAuth/Model/Provider/Engine/U2fKey/Configure.php b/TwoFactorAuth/Model/Provider/Engine/U2fKey/Configure.php index 1fab3fc1..89c92af5 100644 --- a/TwoFactorAuth/Model/Provider/Engine/U2fKey/Configure.php +++ b/TwoFactorAuth/Model/Provider/Engine/U2fKey/Configure.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ declare(strict_types=1); diff --git a/TwoFactorAuth/Model/Provider/Engine/U2fKey/Session.php b/TwoFactorAuth/Model/Provider/Engine/U2fKey/Session.php index 7bec3c6f..206644d0 100644 --- a/TwoFactorAuth/Model/Provider/Engine/U2fKey/Session.php +++ b/TwoFactorAuth/Model/Provider/Engine/U2fKey/Session.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ declare(strict_types=1); diff --git a/TwoFactorAuth/Model/Provider/Engine/U2fKey/WebApiConfigReader.php b/TwoFactorAuth/Model/Provider/Engine/U2fKey/WebApiConfigReader.php index 1bdc7dda..bc7591dd 100644 --- a/TwoFactorAuth/Model/Provider/Engine/U2fKey/WebApiConfigReader.php +++ b/TwoFactorAuth/Model/Provider/Engine/U2fKey/WebApiConfigReader.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ declare(strict_types=1); diff --git a/TwoFactorAuth/Model/Provider/Engine/U2fKey/WebAuthn.php b/TwoFactorAuth/Model/Provider/Engine/U2fKey/WebAuthn.php index e9e499d2..b77c295e 100644 --- a/TwoFactorAuth/Model/Provider/Engine/U2fKey/WebAuthn.php +++ b/TwoFactorAuth/Model/Provider/Engine/U2fKey/WebAuthn.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ declare(strict_types=1); diff --git a/TwoFactorAuth/Model/ProviderPool.php b/TwoFactorAuth/Model/ProviderPool.php index b8502344..1a6e81f5 100644 --- a/TwoFactorAuth/Model/ProviderPool.php +++ b/TwoFactorAuth/Model/ProviderPool.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Model; diff --git a/TwoFactorAuth/Model/ResourceModel/Country.php b/TwoFactorAuth/Model/ResourceModel/Country.php index ec5bdc26..026b64b8 100644 --- a/TwoFactorAuth/Model/ResourceModel/Country.php +++ b/TwoFactorAuth/Model/ResourceModel/Country.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Model\ResourceModel; diff --git a/TwoFactorAuth/Model/ResourceModel/Country/Collection.php b/TwoFactorAuth/Model/ResourceModel/Country/Collection.php index e0576182..9fd72690 100644 --- a/TwoFactorAuth/Model/ResourceModel/Country/Collection.php +++ b/TwoFactorAuth/Model/ResourceModel/Country/Collection.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Model\ResourceModel\Country; diff --git a/TwoFactorAuth/Model/ResourceModel/CountryRepository.php b/TwoFactorAuth/Model/ResourceModel/CountryRepository.php index 1b4abb0c..c9b01fda 100644 --- a/TwoFactorAuth/Model/ResourceModel/CountryRepository.php +++ b/TwoFactorAuth/Model/ResourceModel/CountryRepository.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Model\ResourceModel; diff --git a/TwoFactorAuth/Model/ResourceModel/UserConfig.php b/TwoFactorAuth/Model/ResourceModel/UserConfig.php index 44da7016..36cbddb6 100644 --- a/TwoFactorAuth/Model/ResourceModel/UserConfig.php +++ b/TwoFactorAuth/Model/ResourceModel/UserConfig.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Model\ResourceModel; @@ -36,8 +37,8 @@ class UserConfig extends AbstractDb public function __construct( Context $context, $connectionName = null, - EncryptorInterface $encryptor = null, - SerializerInterface $serializer = null + ?EncryptorInterface $encryptor = null, + ?SerializerInterface $serializer = null ) { parent::__construct($context, $connectionName); $this->encryptor = $encryptor ?: diff --git a/TwoFactorAuth/Model/ResourceModel/UserConfig/Collection.php b/TwoFactorAuth/Model/ResourceModel/UserConfig/Collection.php index 53f6e0e9..d773c6b6 100644 --- a/TwoFactorAuth/Model/ResourceModel/UserConfig/Collection.php +++ b/TwoFactorAuth/Model/ResourceModel/UserConfig/Collection.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Model\ResourceModel\UserConfig; diff --git a/TwoFactorAuth/Model/ResourceModel/UserConfigRepository.php b/TwoFactorAuth/Model/ResourceModel/UserConfigRepository.php index dd865ec1..2a699c59 100644 --- a/TwoFactorAuth/Model/ResourceModel/UserConfigRepository.php +++ b/TwoFactorAuth/Model/ResourceModel/UserConfigRepository.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Model\ResourceModel; diff --git a/TwoFactorAuth/Model/Tfa.php b/TwoFactorAuth/Model/Tfa.php index cd7c567c..3fa753cb 100644 --- a/TwoFactorAuth/Model/Tfa.php +++ b/TwoFactorAuth/Model/Tfa.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Model; diff --git a/TwoFactorAuth/Model/TfaSession.php b/TwoFactorAuth/Model/TfaSession.php index 67a337eb..b7703e15 100644 --- a/TwoFactorAuth/Model/TfaSession.php +++ b/TwoFactorAuth/Model/TfaSession.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Model; diff --git a/TwoFactorAuth/Model/TfatActions.php b/TwoFactorAuth/Model/TfatActions.php index c9856f0b..97406d69 100644 --- a/TwoFactorAuth/Model/TfatActions.php +++ b/TwoFactorAuth/Model/TfatActions.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ declare(strict_types=1); diff --git a/TwoFactorAuth/Model/UserAuthenticator.php b/TwoFactorAuth/Model/UserAuthenticator.php index 14152961..3c0a6528 100644 --- a/TwoFactorAuth/Model/UserAuthenticator.php +++ b/TwoFactorAuth/Model/UserAuthenticator.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ declare(strict_types=1); diff --git a/TwoFactorAuth/Model/UserConfig.php b/TwoFactorAuth/Model/UserConfig.php index 47a9aef3..a8c91c94 100644 --- a/TwoFactorAuth/Model/UserConfig.php +++ b/TwoFactorAuth/Model/UserConfig.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Model; @@ -48,8 +49,8 @@ public function __construct( Registry $registry, DataObjectHelper $dataObjectHelper, UserConfigInterfaceFactory $userConfigDataFactory, - AbstractResource $resource = null, - AbstractDb $resourceCollection = null, + ?AbstractResource $resource = null, + ?AbstractDb $resourceCollection = null, array $data = [] ) { parent::__construct($context, $registry, $resource, $resourceCollection, $data); diff --git a/TwoFactorAuth/Model/UserConfig/HtmlAreaTokenVerifier.php b/TwoFactorAuth/Model/UserConfig/HtmlAreaTokenVerifier.php index 1192df29..6f6b7966 100644 --- a/TwoFactorAuth/Model/UserConfig/HtmlAreaTokenVerifier.php +++ b/TwoFactorAuth/Model/UserConfig/HtmlAreaTokenVerifier.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ declare(strict_types=1); diff --git a/TwoFactorAuth/Model/UserConfig/SignedTokenManager.php b/TwoFactorAuth/Model/UserConfig/SignedTokenManager.php index 3d742871..1c72aebc 100644 --- a/TwoFactorAuth/Model/UserConfig/SignedTokenManager.php +++ b/TwoFactorAuth/Model/UserConfig/SignedTokenManager.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ declare(strict_types=1); diff --git a/TwoFactorAuth/Model/UserConfig/UserConfigRequestManager.php b/TwoFactorAuth/Model/UserConfig/UserConfigRequestManager.php index 07f0460b..4121113a 100644 --- a/TwoFactorAuth/Model/UserConfig/UserConfigRequestManager.php +++ b/TwoFactorAuth/Model/UserConfig/UserConfigRequestManager.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ declare(strict_types=1); diff --git a/TwoFactorAuth/Model/UserConfigManager.php b/TwoFactorAuth/Model/UserConfigManager.php index 2160d576..85928785 100644 --- a/TwoFactorAuth/Model/UserConfigManager.php +++ b/TwoFactorAuth/Model/UserConfigManager.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Model; diff --git a/TwoFactorAuth/Model/UserConfigRegistry.php b/TwoFactorAuth/Model/UserConfigRegistry.php index 2cfe1e2c..3b46384f 100644 --- a/TwoFactorAuth/Model/UserConfigRegistry.php +++ b/TwoFactorAuth/Model/UserConfigRegistry.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Model; diff --git a/TwoFactorAuth/Observer/AdminUserLoadAfter.php b/TwoFactorAuth/Observer/AdminUserLoadAfter.php index d9c1dbc3..112ac549 100644 --- a/TwoFactorAuth/Observer/AdminUserLoadAfter.php +++ b/TwoFactorAuth/Observer/AdminUserLoadAfter.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Observer; diff --git a/TwoFactorAuth/Observer/AdminUserSaveAfter.php b/TwoFactorAuth/Observer/AdminUserSaveAfter.php index f5777997..c447b951 100644 --- a/TwoFactorAuth/Observer/AdminUserSaveAfter.php +++ b/TwoFactorAuth/Observer/AdminUserSaveAfter.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Observer; diff --git a/TwoFactorAuth/Observer/ControllerActionPredispatch.php b/TwoFactorAuth/Observer/ControllerActionPredispatch.php index 94c3add7..77695d2d 100644 --- a/TwoFactorAuth/Observer/ControllerActionPredispatch.php +++ b/TwoFactorAuth/Observer/ControllerActionPredispatch.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Observer; @@ -119,6 +120,7 @@ private function redirect(string $url): void /** * @inheritDoc + * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ public function execute(Observer $observer) { diff --git a/TwoFactorAuth/Plugin/AddTabToAdminUserEdit.php b/TwoFactorAuth/Plugin/AddTabToAdminUserEdit.php index 99bb9f3d..f563e4a4 100644 --- a/TwoFactorAuth/Plugin/AddTabToAdminUserEdit.php +++ b/TwoFactorAuth/Plugin/AddTabToAdminUserEdit.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Plugin; diff --git a/TwoFactorAuth/Plugin/AvoidRecursionOnPasswordChange.php b/TwoFactorAuth/Plugin/AvoidRecursionOnPasswordChange.php index 0caa8bd9..29bd46b8 100644 --- a/TwoFactorAuth/Plugin/AvoidRecursionOnPasswordChange.php +++ b/TwoFactorAuth/Plugin/AvoidRecursionOnPasswordChange.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Plugin; diff --git a/TwoFactorAuth/Plugin/DeleteCookieOnLogout.php b/TwoFactorAuth/Plugin/DeleteCookieOnLogout.php index cc15737d..f2618494 100644 --- a/TwoFactorAuth/Plugin/DeleteCookieOnLogout.php +++ b/TwoFactorAuth/Plugin/DeleteCookieOnLogout.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ declare(strict_types=1); diff --git a/TwoFactorAuth/Plugin/FirstAvailableMenu.php b/TwoFactorAuth/Plugin/FirstAvailableMenu.php index b2841c9f..caad036e 100644 --- a/TwoFactorAuth/Plugin/FirstAvailableMenu.php +++ b/TwoFactorAuth/Plugin/FirstAvailableMenu.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ declare(strict_types=1); diff --git a/TwoFactorAuth/README.md b/TwoFactorAuth/README.md index b9ba60c4..73629a91 100644 --- a/TwoFactorAuth/README.md +++ b/TwoFactorAuth/README.md @@ -1,5 +1,5 @@ -# Magento Two Factor Authentication +# Magento_TwoFactorAuth module The Magento Admin provides all access to your store, orders, and customer data. To prevent unauthorized access to your data, all users who attempt to sign in to the Admin of your Magento installation must complete a second step to verify their identity. -For more information please view the Magento documentation for [a general guide on 2fa](https://docs.magento.com/user-guide/stores/security-two-factor-authentication.html) as well as a [a more technical guide](https://devdocs.magento.com/guides/v2.4/security/two-factor-authentication.html). +For more information please view the Magento documentation for [a general guide on 2fa](https://experienceleague.adobe.com/en/docs/commerce-admin/systems/security/2fa/security-two-factor-authentication) as well as a [a more technical guide](https://developer.adobe.com/commerce/testing/functional-testing-framework/two-factor-authentication/). diff --git a/TwoFactorAuth/Setup/Patch/Data/CopyConfigFromOldModule.php b/TwoFactorAuth/Setup/Patch/Data/CopyConfigFromOldModule.php index a627574b..67d2d254 100644 --- a/TwoFactorAuth/Setup/Patch/Data/CopyConfigFromOldModule.php +++ b/TwoFactorAuth/Setup/Patch/Data/CopyConfigFromOldModule.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Setup\Patch\Data; diff --git a/TwoFactorAuth/Setup/Patch/Data/EncryptConfiguration.php b/TwoFactorAuth/Setup/Patch/Data/EncryptConfiguration.php index 513b652e..30751571 100644 --- a/TwoFactorAuth/Setup/Patch/Data/EncryptConfiguration.php +++ b/TwoFactorAuth/Setup/Patch/Data/EncryptConfiguration.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Setup\Patch\Data; diff --git a/TwoFactorAuth/Setup/Patch/Data/EncryptGoogleSecrets.php b/TwoFactorAuth/Setup/Patch/Data/EncryptGoogleSecrets.php index 686bdf31..3f8dbd7d 100644 --- a/TwoFactorAuth/Setup/Patch/Data/EncryptGoogleSecrets.php +++ b/TwoFactorAuth/Setup/Patch/Data/EncryptGoogleSecrets.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Setup\Patch\Data; diff --git a/TwoFactorAuth/Setup/Patch/Data/EncryptSecrets.php b/TwoFactorAuth/Setup/Patch/Data/EncryptSecrets.php index e28d4eac..a070ca62 100644 --- a/TwoFactorAuth/Setup/Patch/Data/EncryptSecrets.php +++ b/TwoFactorAuth/Setup/Patch/Data/EncryptSecrets.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Setup\Patch\Data; @@ -55,8 +56,7 @@ public function apply() ->where( 'path in (?)', [ - DuoSecurity::XML_PATH_APPLICATION_KEY, - DuoSecurity::XML_PATH_SECRET_KEY, + DuoSecurity::XML_PATH_CLIENT_SECRET, Service::XML_PATH_API_KEY, ] ); diff --git a/TwoFactorAuth/Setup/Patch/Data/GenerateDuoSecurityKey.php b/TwoFactorAuth/Setup/Patch/Data/GenerateDuoSecurityKey.php deleted file mode 100644 index bb2f4e68..00000000 --- a/TwoFactorAuth/Setup/Patch/Data/GenerateDuoSecurityKey.php +++ /dev/null @@ -1,96 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\TwoFactorAuth\Setup\Patch\Data; - -use Magento\Framework\App\Config\ConfigResource\ConfigInterface; -use Magento\Framework\App\Config\ScopeConfigInterface; -use Magento\Framework\Setup\ModuleDataSetupInterface; -use Magento\Framework\Setup\Patch\DataPatchInterface; -use Magento\TwoFactorAuth\Model\Provider\Engine\DuoSecurity; - -/** - * Generate initial duo security key - */ -class GenerateDuoSecurityKey implements DataPatchInterface -{ - /** - * @var ModuleDataSetupInterface - */ - private $moduleDataSetup; - - /** - * @var ConfigInterface - */ - private $config; - - /** - * @var ScopeConfigInterface - */ - private $scopeConfig; - - /** - * @param ModuleDataSetupInterface $moduleDataSetup - * @param ConfigInterface $config - * @param ScopeConfigInterface $scopeConfig - */ - public function __construct( - ModuleDataSetupInterface $moduleDataSetup, - ConfigInterface $config, - ScopeConfigInterface $scopeConfig - ) { - $this->moduleDataSetup = $moduleDataSetup; - $this->config = $config; - $this->scopeConfig = $scopeConfig; - } - - /** - * Create and set the duo key - * - * @SuppressWarnings(PHPMD.ExcessiveMethodLength) - * @SuppressWarnings(PHPMD.CyclomaticComplexity) - * @SuppressWarnings(PHPMD.NPathComplexity) - */ - public function apply() - { - $this->moduleDataSetup->startSetup(); - - if (!$this->scopeConfig->getValue(DuoSecurity::XML_PATH_APPLICATION_KEY)) { - // Generate random duo security key - $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; - $charactersLength = strlen($characters); - $randomString = ''; - for ($i = 0; $i < 64; $i++) { - $randomString .= $characters[random_int(0, $charactersLength - 1)]; - } - - $this->config->saveConfig(DuoSecurity::XML_PATH_APPLICATION_KEY, $randomString, 'default', 0); - } - - $this->moduleDataSetup->endSetup(); - - return $this; - } - - /** - * @inheritdoc - */ - public static function getDependencies() - { - return [ - CopyConfigFromOldModule::class - ]; - } - - /** - * @inheritdoc - */ - public function getAliases() - { - return []; - } -} diff --git a/TwoFactorAuth/Setup/Patch/Data/PopulateCountryTable.php b/TwoFactorAuth/Setup/Patch/Data/PopulateCountryTable.php index d49855dd..50652e81 100644 --- a/TwoFactorAuth/Setup/Patch/Data/PopulateCountryTable.php +++ b/TwoFactorAuth/Setup/Patch/Data/PopulateCountryTable.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Setup\Patch\Data; diff --git a/TwoFactorAuth/Setup/Patch/Data/ResetU2fConfig.php b/TwoFactorAuth/Setup/Patch/Data/ResetU2fConfig.php index 18986427..8c276fd4 100644 --- a/TwoFactorAuth/Setup/Patch/Data/ResetU2fConfig.php +++ b/TwoFactorAuth/Setup/Patch/Data/ResetU2fConfig.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Setup\Patch\Data; diff --git a/TwoFactorAuth/Setup/Patch/Schema/CopyTablesFromOldModule.php b/TwoFactorAuth/Setup/Patch/Schema/CopyTablesFromOldModule.php index 3392544e..35762a21 100644 --- a/TwoFactorAuth/Setup/Patch/Schema/CopyTablesFromOldModule.php +++ b/TwoFactorAuth/Setup/Patch/Schema/CopyTablesFromOldModule.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Setup\Patch\Schema; diff --git a/TwoFactorAuth/Test/Api/AdminIntegrationTokenTest.php b/TwoFactorAuth/Test/Api/AdminIntegrationTokenTest.php index 801d4a1f..c19ffe69 100644 --- a/TwoFactorAuth/Test/Api/AdminIntegrationTokenTest.php +++ b/TwoFactorAuth/Test/Api/AdminIntegrationTokenTest.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Test\Api; diff --git a/TwoFactorAuth/Test/Api/GoogleActivateTest.php b/TwoFactorAuth/Test/Api/GoogleActivateTest.php index 3253933f..2511baa9 100644 --- a/TwoFactorAuth/Test/Api/GoogleActivateTest.php +++ b/TwoFactorAuth/Test/Api/GoogleActivateTest.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Test\Api; @@ -18,10 +19,10 @@ class GoogleActivateTest extends WebapiAbstract { - const SERVICE_VERSION = 'V1'; - const SERVICE_NAME = 'twoFactorAuthGoogleConfigureV1'; - const OPERATION = 'Activate'; - const RESOURCE_PATH = '/V1/tfa/provider/google/activate'; + public const SERVICE_VERSION = 'V1'; + public const SERVICE_NAME = 'twoFactorAuthGoogleConfigureV1'; + public const OPERATION = 'Activate'; + public const RESOURCE_PATH = '/V1/tfa/provider/google/activate'; /** * @var UserFactory @@ -129,7 +130,7 @@ public function testAlreadyActivatedProvider() /** * @magentoConfigFixture twofactorauth/general/force_providers google * @magentoApiDataFixture Magento/User/_files/user_with_custom_role.php - * @magentoConfigFixture twofactorauth/google/otp_window 120 + * @magentoConfigFixture twofactorauth/google/leeway 29 */ public function testActivate() { diff --git a/TwoFactorAuth/Test/Api/GoogleAuthenticateTest.php b/TwoFactorAuth/Test/Api/GoogleAuthenticateTest.php index bd6e5eca..0222bf74 100644 --- a/TwoFactorAuth/Test/Api/GoogleAuthenticateTest.php +++ b/TwoFactorAuth/Test/Api/GoogleAuthenticateTest.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Test\Api; @@ -22,13 +23,14 @@ /** * Class checks google authentication behaviour + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class GoogleAuthenticateTest extends WebapiAbstract { - const SERVICE_VERSION = 'V1'; - const SERVICE_NAME = 'twoFactorAuthGoogleAuthenticateV1'; - const OPERATION = 'CreateAdminAccessToken'; - const RESOURCE_PATH = '/V1/tfa/provider/google/authenticate'; + public const SERVICE_VERSION = 'V1'; + public const SERVICE_NAME = 'twoFactorAuthGoogleAuthenticateV1'; + public const OPERATION = 'CreateAdminAccessToken'; + public const RESOURCE_PATH = '/V1/tfa/provider/google/authenticate'; /** * @var UserFactory @@ -222,7 +224,7 @@ public function testNotConfiguredProvider(): void /** * @magentoConfigFixture twofactorauth/general/force_providers google * @magentoApiDataFixture Magento/User/_files/user_with_custom_role.php - * @magentoConfigFixture twofactorauth/google/otp_window 120 + * @magentoConfigFixture twofactorauth/google/leeway 29 * * @return void */ diff --git a/TwoFactorAuth/Test/Api/GoogleConfigureTest.php b/TwoFactorAuth/Test/Api/GoogleConfigureTest.php index e92c856c..da46a0d7 100644 --- a/TwoFactorAuth/Test/Api/GoogleConfigureTest.php +++ b/TwoFactorAuth/Test/Api/GoogleConfigureTest.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Test\Api; diff --git a/TwoFactorAuth/Test/Integration/Block/ChangeProviderTest.php b/TwoFactorAuth/Test/Integration/Block/ChangeProviderTest.php index 95507988..d578c6b1 100644 --- a/TwoFactorAuth/Test/Integration/Block/ChangeProviderTest.php +++ b/TwoFactorAuth/Test/Integration/Block/ChangeProviderTest.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ declare(strict_types=1); @@ -72,8 +72,10 @@ protected function setUp(): void /** * @magentoConfigFixture default/twofactorauth/general/force_providers authy,duo_security * @magentoConfigFixture default/twofactorauth/authy/api_key abc123 + * @magentoConfigFixture default/twofactorauth/duo/client_id ABCDEFGHIJKLMNOPQRST + * @magentoConfigFixture default/twofactorauth/duo/client_secret abcdefghijklmnopqrstuvwxyz0123456789abcd * @magentoConfigFixture default/twofactorauth/duo/integration_key abc123 - * @magentoConfigFixture default/twofactorauth/duo/api_hostname abc123 + * @magentoConfigFixture default/twofactorauth/duo/api_hostname test.duosecurity.com * @magentoConfigFixture default/twofactorauth/duo/secret_key abc123 */ public function testBlockRendersWithActiveProviders(): void @@ -94,8 +96,10 @@ function ($item) { /** * @magentoConfigFixture default/twofactorauth/general/force_providers authy,duo_security * @magentoConfigFixture default/twofactorauth/authy/api_key abc123 + * @magentoConfigFixture default/twofactorauth/duo/client_id ABCDEFGHIJKLMNOPQRST + * @magentoConfigFixture default/twofactorauth/duo/client_secret abcdefghijklmnopqrstuvwxyz0123456789abcd * @magentoConfigFixture default/twofactorauth/duo/integration_key abc123 - * @magentoConfigFixture default/twofactorauth/duo/api_hostname abc123 + * @magentoConfigFixture default/twofactorauth/duo/api_hostname test.duosecurity.com * @magentoConfigFixture default/twofactorauth/duo/secret_key abc123 */ public function testBlockRendersWhenCurrentProviderIsActivated(): void @@ -111,8 +115,10 @@ public function testBlockRendersWhenCurrentProviderIsActivated(): void /** * @magentoConfigFixture default/twofactorauth/general/force_providers authy,duo_security * @magentoConfigFixture default/twofactorauth/authy/api_key abc123 + * @magentoConfigFixture default/twofactorauth/duo/client_id ABCDEFGHIJKLMNOPQRST + * @magentoConfigFixture default/twofactorauth/duo/client_secret abcdefghijklmnopqrstuvwxyz0123456789abcd * @magentoConfigFixture default/twofactorauth/duo/integration_key abc123 - * @magentoConfigFixture default/twofactorauth/duo/api_hostname abc123 + * @magentoConfigFixture default/twofactorauth/duo/api_hostname test.duosecurity.com * @magentoConfigFixture default/twofactorauth/duo/secret_key abc123 */ public function testBlockRendersWhenCurrentProviderIsNotActivated(): void diff --git a/TwoFactorAuth/Test/Integration/Block/ConfigureLaterTest.php b/TwoFactorAuth/Test/Integration/Block/ConfigureLaterTest.php index be1ea6c8..519f9315 100644 --- a/TwoFactorAuth/Test/Integration/Block/ConfigureLaterTest.php +++ b/TwoFactorAuth/Test/Integration/Block/ConfigureLaterTest.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ declare(strict_types=1); @@ -83,8 +83,10 @@ public function testGetPostData(): void /** * @magentoConfigFixture default/twofactorauth/general/force_providers authy,duo_security * @magentoConfigFixture default/twofactorauth/authy/api_key abc123 + * @magentoConfigFixture default/twofactorauth/duo/client_id ABCDEFGHIJKLMNOPQRST + * @magentoConfigFixture default/twofactorauth/duo/client_secret abcdefghijklmnopqrstuvwxyz0123456789abcd * @magentoConfigFixture default/twofactorauth/duo/integration_key abc123 - * @magentoConfigFixture default/twofactorauth/duo/api_hostname abc123 + * @magentoConfigFixture default/twofactorauth/duo/api_hostname test.duosecurity.com * @magentoConfigFixture default/twofactorauth/duo/secret_key abc123 */ public function testBlockRendersWithCurrentInactiveAndOneOtherActive(): void @@ -99,8 +101,10 @@ public function testBlockRendersWithCurrentInactiveAndOneOtherActive(): void /** * @magentoConfigFixture default/twofactorauth/general/force_providers duo_security + * @magentoConfigFixture default/twofactorauth/duo/client_id ABCDEFGHIJKLMNOPQRST + * @magentoConfigFixture default/twofactorauth/duo/client_secret abcdefghijklmnopqrstuvwxyz0123456789abcd * @magentoConfigFixture default/twofactorauth/duo/integration_key abc123 - * @magentoConfigFixture default/twofactorauth/duo/api_hostname abc123 + * @magentoConfigFixture default/twofactorauth/duo/api_hostname test.duosecurity.com * @magentoConfigFixture default/twofactorauth/duo/secret_key abc123 */ public function testBlockDoesntRenderWithCurrentInactiveAndNoOtherActive(): void diff --git a/TwoFactorAuth/Test/Integration/Command/GoogleSecretTest.php b/TwoFactorAuth/Test/Integration/Command/GoogleSecretTest.php index d3d9daa9..0343816a 100644 --- a/TwoFactorAuth/Test/Integration/Command/GoogleSecretTest.php +++ b/TwoFactorAuth/Test/Integration/Command/GoogleSecretTest.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ declare(strict_types=1); diff --git a/TwoFactorAuth/Test/Integration/Controller/Adminhtml/Authy/ConfigureTest.php b/TwoFactorAuth/Test/Integration/Controller/Adminhtml/Authy/ConfigureTest.php index c22245b8..2576c35d 100644 --- a/TwoFactorAuth/Test/Integration/Controller/Adminhtml/Authy/ConfigureTest.php +++ b/TwoFactorAuth/Test/Integration/Controller/Adminhtml/Authy/ConfigureTest.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ declare(strict_types=1); diff --git a/TwoFactorAuth/Test/Integration/Controller/Adminhtml/Authy/ConfigurepostTest.php b/TwoFactorAuth/Test/Integration/Controller/Adminhtml/Authy/ConfigurepostTest.php index e70afaa3..462a0413 100644 --- a/TwoFactorAuth/Test/Integration/Controller/Adminhtml/Authy/ConfigurepostTest.php +++ b/TwoFactorAuth/Test/Integration/Controller/Adminhtml/Authy/ConfigurepostTest.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ declare(strict_types=1); diff --git a/TwoFactorAuth/Test/Integration/Controller/Adminhtml/Authy/ConfigureverifypostTest.php b/TwoFactorAuth/Test/Integration/Controller/Adminhtml/Authy/ConfigureverifypostTest.php index b1517110..615d6a14 100644 --- a/TwoFactorAuth/Test/Integration/Controller/Adminhtml/Authy/ConfigureverifypostTest.php +++ b/TwoFactorAuth/Test/Integration/Controller/Adminhtml/Authy/ConfigureverifypostTest.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ declare(strict_types=1); diff --git a/TwoFactorAuth/Test/Integration/Controller/Adminhtml/Duo/AuthTest.php b/TwoFactorAuth/Test/Integration/Controller/Adminhtml/Duo/AuthTest.php index c537c451..02586a6a 100644 --- a/TwoFactorAuth/Test/Integration/Controller/Adminhtml/Duo/AuthTest.php +++ b/TwoFactorAuth/Test/Integration/Controller/Adminhtml/Duo/AuthTest.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ declare(strict_types=1); @@ -20,22 +20,23 @@ class AuthTest extends AbstractConfigureBackendController { /** - * @inheritDoc + * @var string */ protected $uri = 'backend/tfa/duo/auth'; /** - * @inheritDoc + * @var string */ protected $httpMethod = Request::METHOD_GET; /** * @inheritDoc * @magentoConfigFixture default/twofactorauth/general/force_providers duo_security - * @magentoConfigFixture default/twofactorauth/duo/integration_key duo_security - * @magentoConfigFixture default/twofactorauth/duo/secret_key duo_security - * @magentoConfigFixture default/twofactorauth/duo/api_hostname duo_security - * @magentoConfigFixture default/twofactorauth/duo/application_key duo_security + * @magentoConfigFixture default/twofactorauth/duo/client_id ABCDEFGHIJKLMNOPQRST + * @magentoConfigFixture default/twofactorauth/duo/client_secret abcdefghijklmnopqrstuvwxyz0123456789abcd + * @magentoConfigFixture default/twofactorauth/duo/integration_key abc123 + * @magentoConfigFixture default/twofactorauth/duo/api_hostname test.duosecurity.com + * @magentoConfigFixture default/twofactorauth/duo/secret_key abc123 * phpcs:disable Generic.CodeAnalysis.UselessOverridingMethod */ public function testTokenAccess(): void @@ -46,10 +47,11 @@ public function testTokenAccess(): void /** * @inheritDoc * @magentoConfigFixture default/twofactorauth/general/force_providers duo_security - * @magentoConfigFixture default/twofactorauth/duo/integration_key duo_security - * @magentoConfigFixture default/twofactorauth/duo/secret_key duo_security - * @magentoConfigFixture default/twofactorauth/duo/api_hostname duo_security - * @magentoConfigFixture default/twofactorauth/duo/application_key duo_security + * @magentoConfigFixture default/twofactorauth/duo/client_id ABCDEFGHIJKLMNOPQRST + * @magentoConfigFixture default/twofactorauth/duo/client_secret abcdefghijklmnopqrstuvwxyz0123456789abcd + * @magentoConfigFixture default/twofactorauth/duo/integration_key abc123 + * @magentoConfigFixture default/twofactorauth/duo/api_hostname test.duosecurity.com + * @magentoConfigFixture default/twofactorauth/duo/secret_key abc123 * phpcs:disable Generic.CodeAnalysis.UselessOverridingMethod */ public function testAclHasAccess() @@ -60,10 +62,11 @@ public function testAclHasAccess() /** * @inheritDoc * @magentoConfigFixture default/twofactorauth/general/force_providers duo_security - * @magentoConfigFixture default/twofactorauth/duo/integration_key duo_security - * @magentoConfigFixture default/twofactorauth/duo/secret_key duo_security - * @magentoConfigFixture default/twofactorauth/duo/api_hostname duo_security - * @magentoConfigFixture default/twofactorauth/duo/application_key duo_security + * @magentoConfigFixture default/twofactorauth/duo/client_id ABCDEFGHIJKLMNOPQRST + * @magentoConfigFixture default/twofactorauth/duo/client_secret abcdefghijklmnopqrstuvwxyz0123456789abcd + * @magentoConfigFixture default/twofactorauth/duo/integration_key abc123 + * @magentoConfigFixture default/twofactorauth/duo/api_hostname test.duosecurity.com + * @magentoConfigFixture default/twofactorauth/duo/secret_key abc123 * phpcs:disable Generic.CodeAnalysis.UselessOverridingMethod */ public function testAclNoAccess() diff --git a/TwoFactorAuth/Test/Integration/Controller/Adminhtml/Duo/AuthpostTest.php b/TwoFactorAuth/Test/Integration/Controller/Adminhtml/Duo/AuthpostTest.php index 435bef93..8d89085b 100644 --- a/TwoFactorAuth/Test/Integration/Controller/Adminhtml/Duo/AuthpostTest.php +++ b/TwoFactorAuth/Test/Integration/Controller/Adminhtml/Duo/AuthpostTest.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ declare(strict_types=1); @@ -20,14 +20,14 @@ class AuthpostTest extends AbstractConfigureBackendController { /** - * @inheritDoc + * @var string */ protected $uri = 'backend/tfa/duo/authpost'; /** - * @inheritDoc + * @var string */ - protected $httpMethod = Request::METHOD_POST; + protected $httpMethod = Request::METHOD_GET; /** * @inheritDoc @@ -41,10 +41,11 @@ protected function setUp(): void /** * @inheritDoc * @magentoConfigFixture default/twofactorauth/general/force_providers duo_security - * @magentoConfigFixture default/twofactorauth/duo/integration_key duo_security - * @magentoConfigFixture default/twofactorauth/duo/secret_key duo_security - * @magentoConfigFixture default/twofactorauth/duo/api_hostname duo_security - * @magentoConfigFixture default/twofactorauth/duo/application_key duo_security + * @magentoConfigFixture default/twofactorauth/duo/client_id ABCDEFGHIJKLMNOPQRST + * @magentoConfigFixture default/twofactorauth/duo/client_secret abcdefghijklmnopqrstuvwxyz0123456789abcd + * @magentoConfigFixture default/twofactorauth/duo/integration_key abc123 + * @magentoConfigFixture default/twofactorauth/duo/api_hostname test.duosecurity.com + * @magentoConfigFixture default/twofactorauth/duo/secret_key abc123 */ public function testTokenAccess(): void { @@ -56,10 +57,11 @@ public function testTokenAccess(): void /** * @inheritDoc * @magentoConfigFixture default/twofactorauth/general/force_providers duo_security - * @magentoConfigFixture default/twofactorauth/duo/integration_key duo_security - * @magentoConfigFixture default/twofactorauth/duo/secret_key duo_security - * @magentoConfigFixture default/twofactorauth/duo/api_hostname duo_security - * @magentoConfigFixture default/twofactorauth/duo/application_key duo_security + * @magentoConfigFixture default/twofactorauth/duo/client_id ABCDEFGHIJKLMNOPQRST + * @magentoConfigFixture default/twofactorauth/duo/client_secret abcdefghijklmnopqrstuvwxyz0123456789abcd + * @magentoConfigFixture default/twofactorauth/duo/integration_key abc123 + * @magentoConfigFixture default/twofactorauth/duo/api_hostname test.duosecurity.com + * @magentoConfigFixture default/twofactorauth/duo/secret_key abc123 */ public function testAclHasAccess() { @@ -72,10 +74,11 @@ public function testAclHasAccess() /** * @inheritDoc * @magentoConfigFixture default/twofactorauth/general/force_providers duo_security - * @magentoConfigFixture default/twofactorauth/duo/integration_key duo_security - * @magentoConfigFixture default/twofactorauth/duo/secret_key duo_security - * @magentoConfigFixture default/twofactorauth/duo/api_hostname duo_security - * @magentoConfigFixture default/twofactorauth/duo/application_key duo_security + * @magentoConfigFixture default/twofactorauth/duo/client_id ABCDEFGHIJKLMNOPQRST + * @magentoConfigFixture default/twofactorauth/duo/client_secret abcdefghijklmnopqrstuvwxyz0123456789abcd + * @magentoConfigFixture default/twofactorauth/duo/integration_key abc123 + * @magentoConfigFixture default/twofactorauth/duo/api_hostname test.duosecurity.com + * @magentoConfigFixture default/twofactorauth/duo/secret_key abc123 */ public function testAclNoAccess() { diff --git a/TwoFactorAuth/Test/Integration/Controller/Adminhtml/Google/ConfigureTest.php b/TwoFactorAuth/Test/Integration/Controller/Adminhtml/Google/ConfigureTest.php index a82c6348..5a7c45d3 100644 --- a/TwoFactorAuth/Test/Integration/Controller/Adminhtml/Google/ConfigureTest.php +++ b/TwoFactorAuth/Test/Integration/Controller/Adminhtml/Google/ConfigureTest.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ declare(strict_types=1); diff --git a/TwoFactorAuth/Test/Integration/Controller/Adminhtml/Google/ConfigurepostTest.php b/TwoFactorAuth/Test/Integration/Controller/Adminhtml/Google/ConfigurepostTest.php index 7c96f339..fd8887ca 100644 --- a/TwoFactorAuth/Test/Integration/Controller/Adminhtml/Google/ConfigurepostTest.php +++ b/TwoFactorAuth/Test/Integration/Controller/Adminhtml/Google/ConfigurepostTest.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ declare(strict_types=1); diff --git a/TwoFactorAuth/Test/Integration/Controller/Adminhtml/Tfa/ConfigureLaterTest.php b/TwoFactorAuth/Test/Integration/Controller/Adminhtml/Tfa/ConfigureLaterTest.php index 5add6179..40a27379 100644 --- a/TwoFactorAuth/Test/Integration/Controller/Adminhtml/Tfa/ConfigureLaterTest.php +++ b/TwoFactorAuth/Test/Integration/Controller/Adminhtml/Tfa/ConfigureLaterTest.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ declare(strict_types=1); @@ -21,12 +21,12 @@ class ConfigureLaterTest extends AbstractBackendController { /** - * @inheritDoc + * @var string */ protected $uri = 'backend/tfa/tfa/configurelater'; /** - * @inheritDoc + * @var string */ protected $resource = 'Magento_TwoFactorAuth::tfa'; @@ -55,8 +55,10 @@ protected function setUp(): void /** * @magentoConfigFixture default/twofactorauth/general/force_providers duo_security + * @magentoConfigFixture default/twofactorauth/duo/client_id ABCDEFGHIJKLMNOPQRST + * @magentoConfigFixture default/twofactorauth/duo/client_secret abcdefghijklmnopqrstuvwxyz0123456789abcd * @magentoConfigFixture default/twofactorauth/duo/integration_key abc123 - * @magentoConfigFixture default/twofactorauth/duo/api_hostname abc123 + * @magentoConfigFixture default/twofactorauth/duo/api_hostname test.duosecurity.com * @magentoConfigFixture default/twofactorauth/duo/secret_key abc123 */ public function testNotAllowedWhenProviderAlreadyActivated(): void @@ -73,8 +75,10 @@ public function testNotAllowedWhenProviderAlreadyActivated(): void /** * @magentoConfigFixture default/twofactorauth/general/force_providers duo_security + * @magentoConfigFixture default/twofactorauth/duo/client_id ABCDEFGHIJKLMNOPQRST + * @magentoConfigFixture default/twofactorauth/duo/client_secret abcdefghijklmnopqrstuvwxyz0123456789abcd * @magentoConfigFixture default/twofactorauth/duo/integration_key abc123 - * @magentoConfigFixture default/twofactorauth/duo/api_hostname abc123 + * @magentoConfigFixture default/twofactorauth/duo/api_hostname test.duosecurity.com * @magentoConfigFixture default/twofactorauth/duo/secret_key abc123 */ public function testNotAllowedWhenProviderNotActivatedButIsTheOnlyProvider(): void @@ -91,8 +95,10 @@ public function testNotAllowedWhenProviderNotActivatedButIsTheOnlyProvider(): vo /** * @magentoConfigFixture default/twofactorauth/general/force_providers google,duo_security,authy * @magentoConfigFixture default/twofactorauth/authy/api_key abc123 + * @magentoConfigFixture default/twofactorauth/duo/client_id ABCDEFGHIJKLMNOPQRST + * @magentoConfigFixture default/twofactorauth/duo/client_secret abcdefghijklmnopqrstuvwxyz0123456789abcd * @magentoConfigFixture default/twofactorauth/duo/integration_key abc123 - * @magentoConfigFixture default/twofactorauth/duo/api_hostname abc123 + * @magentoConfigFixture default/twofactorauth/duo/api_hostname test.duosecurity.com * @magentoConfigFixture default/twofactorauth/duo/secret_key abc123 */ public function testSkippingAProvider(): void @@ -110,8 +116,10 @@ public function testSkippingAProvider(): void /** * @magentoConfigFixture default/twofactorauth/general/force_providers duo_security,authy * @magentoConfigFixture default/twofactorauth/authy/api_key abc123 + * @magentoConfigFixture default/twofactorauth/duo/client_id ABCDEFGHIJKLMNOPQRST + * @magentoConfigFixture default/twofactorauth/duo/client_secret abcdefghijklmnopqrstuvwxyz0123456789abcd * @magentoConfigFixture default/twofactorauth/duo/integration_key abc123 - * @magentoConfigFixture default/twofactorauth/duo/api_hostname abc123 + * @magentoConfigFixture default/twofactorauth/duo/api_hostname test.duosecurity.com * @magentoConfigFixture default/twofactorauth/duo/secret_key abc123 */ public function testSkippingAllProvidersWhenThereAreNoneConfigured(): void diff --git a/TwoFactorAuth/Test/Integration/Controller/Adminhtml/Tfa/ConfigureTest.php b/TwoFactorAuth/Test/Integration/Controller/Adminhtml/Tfa/ConfigureTest.php index 3331daec..d4f09587 100644 --- a/TwoFactorAuth/Test/Integration/Controller/Adminhtml/Tfa/ConfigureTest.php +++ b/TwoFactorAuth/Test/Integration/Controller/Adminhtml/Tfa/ConfigureTest.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ declare(strict_types=1); diff --git a/TwoFactorAuth/Test/Integration/Controller/Adminhtml/Tfa/ConfigurepostTest.php b/TwoFactorAuth/Test/Integration/Controller/Adminhtml/Tfa/ConfigurepostTest.php index bcd9cdee..aa9c68e6 100644 --- a/TwoFactorAuth/Test/Integration/Controller/Adminhtml/Tfa/ConfigurepostTest.php +++ b/TwoFactorAuth/Test/Integration/Controller/Adminhtml/Tfa/ConfigurepostTest.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ declare(strict_types=1); diff --git a/TwoFactorAuth/Test/Integration/Controller/Adminhtml/Tfa/IndexTest.php b/TwoFactorAuth/Test/Integration/Controller/Adminhtml/Tfa/IndexTest.php index 7cba3279..ae0d5aa1 100644 --- a/TwoFactorAuth/Test/Integration/Controller/Adminhtml/Tfa/IndexTest.php +++ b/TwoFactorAuth/Test/Integration/Controller/Adminhtml/Tfa/IndexTest.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ declare(strict_types=1); @@ -26,7 +26,7 @@ class IndexTest extends AbstractBackendController { /** - * @inheritDoc + * @var string */ protected $uri = 'backend/tfa/tfa/index'; @@ -115,8 +115,10 @@ public function testNotConfiguredWithSkipped(): void /** * @magentoConfigFixture default/twofactorauth/general/force_providers google,authy,duo_security * @magentoConfigFixture default/twofactorauth/authy/api_key abc123 + * @magentoConfigFixture default/twofactorauth/duo/client_id ABCDEFGHIJKLMNOPQRST + * @magentoConfigFixture default/twofactorauth/duo/client_secret abcdefghijklmnopqrstuvwxyz0123456789abcd * @magentoConfigFixture default/twofactorauth/duo/integration_key abc123 - * @magentoConfigFixture default/twofactorauth/duo/api_hostname abc123 + * @magentoConfigFixture default/twofactorauth/duo/api_hostname test.duosecurity.com * @magentoConfigFixture default/twofactorauth/duo/secret_key abc123 * @magentoDbIsolation enabled */ @@ -134,8 +136,10 @@ public function testDefaultProviderIsUsedForAuth(): void /** * @magentoConfigFixture default/twofactorauth/general/force_providers google,authy,duo_security * @magentoConfigFixture default/twofactorauth/authy/api_key abc123 + * @magentoConfigFixture default/twofactorauth/duo/client_id ABCDEFGHIJKLMNOPQRST + * @magentoConfigFixture default/twofactorauth/duo/client_secret abcdefghijklmnopqrstuvwxyz0123456789abcd * @magentoConfigFixture default/twofactorauth/duo/integration_key abc123 - * @magentoConfigFixture default/twofactorauth/duo/api_hostname abc123 + * @magentoConfigFixture default/twofactorauth/duo/api_hostname test.duosecurity.com * @magentoConfigFixture default/twofactorauth/duo/secret_key abc123 * @magentoDbIsolation enabled */ @@ -153,8 +157,10 @@ public function testFirstProviderIsUsedForAuthWithoutADefault(): void /** * @magentoConfigFixture default/twofactorauth/general/force_providers google,authy,duo_security * @magentoConfigFixture default/twofactorauth/authy/api_key abc123 + * @magentoConfigFixture default/twofactorauth/duo/client_id ABCDEFGHIJKLMNOPQRST + * @magentoConfigFixture default/twofactorauth/duo/client_secret abcdefghijklmnopqrstuvwxyz0123456789abcd * @magentoConfigFixture default/twofactorauth/duo/integration_key abc123 - * @magentoConfigFixture default/twofactorauth/duo/api_hostname abc123 + * @magentoConfigFixture default/twofactorauth/duo/api_hostname test.duosecurity.com * @magentoConfigFixture default/twofactorauth/duo/secret_key abc123 * @magentoDbIsolation enabled */ diff --git a/TwoFactorAuth/Test/Integration/Controller/Adminhtml/Tfa/RequestconfigTest.php b/TwoFactorAuth/Test/Integration/Controller/Adminhtml/Tfa/RequestconfigTest.php index 35d488b8..fbd378d4 100644 --- a/TwoFactorAuth/Test/Integration/Controller/Adminhtml/Tfa/RequestconfigTest.php +++ b/TwoFactorAuth/Test/Integration/Controller/Adminhtml/Tfa/RequestconfigTest.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ declare(strict_types=1); diff --git a/TwoFactorAuth/Test/Integration/Controller/Adminhtml/U2f/ConfigureTest.php b/TwoFactorAuth/Test/Integration/Controller/Adminhtml/U2f/ConfigureTest.php index 2dbe3e89..699d3355 100644 --- a/TwoFactorAuth/Test/Integration/Controller/Adminhtml/U2f/ConfigureTest.php +++ b/TwoFactorAuth/Test/Integration/Controller/Adminhtml/U2f/ConfigureTest.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ declare(strict_types=1); diff --git a/TwoFactorAuth/Test/Integration/Controller/Adminhtml/U2f/ConfigurepostTest.php b/TwoFactorAuth/Test/Integration/Controller/Adminhtml/U2f/ConfigurepostTest.php index 473fb882..ddc6b548 100644 --- a/TwoFactorAuth/Test/Integration/Controller/Adminhtml/U2f/ConfigurepostTest.php +++ b/TwoFactorAuth/Test/Integration/Controller/Adminhtml/U2f/ConfigurepostTest.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ declare(strict_types=1); diff --git a/TwoFactorAuth/Test/Integration/Controller/SoapTest.php b/TwoFactorAuth/Test/Integration/Controller/SoapTest.php index 50bf11f0..6c921e4a 100644 --- a/TwoFactorAuth/Test/Integration/Controller/SoapTest.php +++ b/TwoFactorAuth/Test/Integration/Controller/SoapTest.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ namespace Magento\TwoFactorAuth\Test\Integration\Controller; diff --git a/TwoFactorAuth/Test/Integration/ControllerActionPredispatchTest.php b/TwoFactorAuth/Test/Integration/ControllerActionPredispatchTest.php index f2c27962..f7cc5d14 100644 --- a/TwoFactorAuth/Test/Integration/ControllerActionPredispatchTest.php +++ b/TwoFactorAuth/Test/Integration/ControllerActionPredispatchTest.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ declare(strict_types=1); @@ -104,6 +104,11 @@ public function testUnauthenticated(): void * Verify that users would be redirected to "2FA Config Request" page when 2FA is not configured for the app. * * @magentoConfigFixture default/twofactorauth/general/force_providers google,duo_security + * @magentoConfigFixture default/twofactorauth/duo/client_id ABCDEFGHIJKLMNOPQRST + * @magentoConfigFixture default/twofactorauth/duo/client_secret abcdefghijklmnopqrstuvwxyz0123456789abcd + * @magentoConfigFixture default/twofactorauth/duo/integration_key abc123 + * @magentoConfigFixture default/twofactorauth/duo/api_hostname test.duosecurity.com + * @magentoConfigFixture default/twofactorauth/duo/secret_key abc123 * @return void */ public function testConfigRequested(): void @@ -134,6 +139,11 @@ public function testUserConfigRequested(): void * Verify that users returning with a token from the E-mail get a new cookie with it. * * @magentoConfigFixture default/twofactorauth/general/force_providers google,duo_security + * @magentoConfigFixture default/twofactorauth/duo/client_id ABCDEFGHIJKLMNOPQRST + * @magentoConfigFixture default/twofactorauth/duo/client_secret abcdefghijklmnopqrstuvwxyz0123456789abcd + * @magentoConfigFixture default/twofactorauth/duo/integration_key abc123 + * @magentoConfigFixture default/twofactorauth/duo/api_hostname test.duosecurity.com + * @magentoConfigFixture default/twofactorauth/duo/secret_key abc123 * @return void */ public function testCookieSet(): void diff --git a/TwoFactorAuth/Test/Integration/Model/Config/UserNotifierTest.php b/TwoFactorAuth/Test/Integration/Model/Config/UserNotifierTest.php index 48a3af34..56eaae95 100644 --- a/TwoFactorAuth/Test/Integration/Model/Config/UserNotifierTest.php +++ b/TwoFactorAuth/Test/Integration/Model/Config/UserNotifierTest.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2021 Adobe + * All Rights Reserved. */ declare(strict_types=1); diff --git a/TwoFactorAuth/Test/Integration/Model/Config/WebApiUserNotifierTest.php b/TwoFactorAuth/Test/Integration/Model/Config/WebApiUserNotifierTest.php index bf6a6344..a7e245c5 100644 --- a/TwoFactorAuth/Test/Integration/Model/Config/WebApiUserNotifierTest.php +++ b/TwoFactorAuth/Test/Integration/Model/Config/WebApiUserNotifierTest.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2021 Adobe + * All Rights Reserved. */ declare(strict_types=1); diff --git a/TwoFactorAuth/Test/Integration/Model/Provider/Engine/Authy/AuthenticateTest.php b/TwoFactorAuth/Test/Integration/Model/Provider/Engine/Authy/AuthenticateTest.php index 4f3dcd37..02c68351 100644 --- a/TwoFactorAuth/Test/Integration/Model/Provider/Engine/Authy/AuthenticateTest.php +++ b/TwoFactorAuth/Test/Integration/Model/Provider/Engine/Authy/AuthenticateTest.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ declare(strict_types=1); diff --git a/TwoFactorAuth/Test/Integration/Model/Provider/Engine/Authy/ConfigureTest.php b/TwoFactorAuth/Test/Integration/Model/Provider/Engine/Authy/ConfigureTest.php index a4f1ff69..609dde3e 100644 --- a/TwoFactorAuth/Test/Integration/Model/Provider/Engine/Authy/ConfigureTest.php +++ b/TwoFactorAuth/Test/Integration/Model/Provider/Engine/Authy/ConfigureTest.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ declare(strict_types=1); diff --git a/TwoFactorAuth/Test/Integration/Model/Provider/Engine/DuoSecurity/AuthenticateTest.php b/TwoFactorAuth/Test/Integration/Model/Provider/Engine/DuoSecurity/AuthenticateTest.php index ff62192c..76b28512 100644 --- a/TwoFactorAuth/Test/Integration/Model/Provider/Engine/DuoSecurity/AuthenticateTest.php +++ b/TwoFactorAuth/Test/Integration/Model/Provider/Engine/DuoSecurity/AuthenticateTest.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ declare(strict_types=1); @@ -10,7 +10,6 @@ use Magento\Framework\App\ObjectManager; use Magento\TestFramework\Bootstrap; -use Magento\TwoFactorAuth\Api\Data\DuoDataInterface; use Magento\TwoFactorAuth\Api\TfaInterface; use Magento\TwoFactorAuth\Api\UserConfigTokenManagerInterface; use Magento\TwoFactorAuth\Model\Provider\Engine\DuoSecurity; @@ -52,10 +51,10 @@ class AuthenticateTest extends TestCase protected function setUp(): void { $objectManager = ObjectManager::getInstance(); - $this->userFactory = $objectManager->get(UserFactory::class); $this->tokenManager = $objectManager->get(UserConfigTokenManagerInterface::class); $this->tfa = $objectManager->get(TfaInterface::class); $this->duo = $this->createMock(DuoSecurity::class); + $this->userFactory = $objectManager->get(UserFactory::class); $this->model = $objectManager->create( Authenticate::class, [ @@ -66,69 +65,10 @@ protected function setUp(): void /** * @magentoConfigFixture default/twofactorauth/general/force_providers duo_security + * @magentoConfigFixture default/twofactorauth/duo/client_id ABCDEFGHIJKLMNOPQRST + * @magentoConfigFixture default/twofactorauth/duo/client_secret abcdefghijklmnopqrstuvwxyz0123456789abcd * @magentoConfigFixture default/twofactorauth/duo/integration_key abc123 - * @magentoConfigFixture default/twofactorauth/duo/api_hostname abc123 - * @magentoConfigFixture default/twofactorauth/duo/secret_key abc123 - * @magentoDataFixture Magento/User/_files/user_with_role.php - */ - public function testGetAuthenticateDataInvalidCredentials() - { - $this->expectException(\Magento\Framework\Exception\AuthenticationException::class); - $this->tfa->getProviderByCode(DuoSecurity::CODE) - ->activate($this->getUserId()); - $this->duo - ->expects($this->never()) - ->method('getRequestSignature'); - $this->model->getAuthenticateData( - 'adminUser', - 'abc' - ); - } - - /** - * @magentoConfigFixture default/twofactorauth/general/force_providers duo_security - * @magentoConfigFixture default/twofactorauth/duo/integration_key abc123 - * @magentoConfigFixture default/twofactorauth/duo/api_hostname abc123 - * @magentoConfigFixture default/twofactorauth/duo/secret_key abc123 - * @magentoDataFixture Magento/User/_files/user_with_role.php - */ - public function testGetAuthenticateDataNotConfiguredProvider() - { - $this->expectException(\Magento\Framework\Exception\LocalizedException::class); - $this->expectExceptionMessage('Provider is not configured.'); - $userId = $this->getUserId(); - $this->tfa->getProviderByCode(DuoSecurity::CODE) - ->resetConfiguration($userId); - - $this->duo - ->expects($this->never()) - ->method('getRequestSignature'); - $this->model->getAuthenticateData( - 'adminUser', - Bootstrap::ADMIN_PASSWORD - ); - } - - /** - * @magentoConfigFixture default/twofactorauth/general/force_providers authy - * @magentoDataFixture Magento/User/_files/user_with_role.php - */ - public function testGetAuthenticateDataUnavailableProvider() - { - $this->expectException(\Magento\Framework\Exception\LocalizedException::class); - $this->expectExceptionMessage('Provider is not allowed.'); - $this->duo - ->expects($this->never()) - ->method('getRequestSignature'); - $this->model->getAuthenticateData( - 'adminUser', - Bootstrap::ADMIN_PASSWORD - ); - } - /** - * @magentoConfigFixture default/twofactorauth/general/force_providers duo_security - * @magentoConfigFixture default/twofactorauth/duo/integration_key abc123 - * @magentoConfigFixture default/twofactorauth/duo/api_hostname abc123 + * @magentoConfigFixture default/twofactorauth/duo/api_hostname test.duosecurity.com * @magentoConfigFixture default/twofactorauth/duo/secret_key abc123 * @magentoDataFixture Magento/User/_files/user_with_role.php */ @@ -139,18 +79,20 @@ public function testVerifyInvalidCredentials() ->activate($this->getUserId()); $this->duo ->expects($this->never()) - ->method('getRequestSignature'); - $this->model->createAdminAccessTokenWithCredentials( + ->method('authorizeUser'); + $this->model->createAdminAccessTokenWithCredentialsAndPasscode( 'adminUser', 'abc', - 'signature' + '123456' ); } /** * @magentoConfigFixture default/twofactorauth/general/force_providers duo_security + * @magentoConfigFixture default/twofactorauth/duo/client_id ABCDEFGHIJKLMNOPQRST + * @magentoConfigFixture default/twofactorauth/duo/client_secret abcdefghijklmnopqrstuvwxyz0123456789abcd * @magentoConfigFixture default/twofactorauth/duo/integration_key abc123 - * @magentoConfigFixture default/twofactorauth/duo/api_hostname abc123 + * @magentoConfigFixture default/twofactorauth/duo/api_hostname test.duosecurity.com * @magentoConfigFixture default/twofactorauth/duo/secret_key abc123 * @magentoDataFixture Magento/User/_files/user_with_role.php */ @@ -164,11 +106,11 @@ public function testVerifyNotConfiguredProvider() $this->duo ->expects($this->never()) - ->method('getRequestSignature'); - $this->model->createAdminAccessTokenWithCredentials( + ->method('authorizeUser'); + $this->model->createAdminAccessTokenWithCredentialsAndPasscode( 'adminUser', Bootstrap::ADMIN_PASSWORD, - 'signature' + '123456' ); } @@ -182,88 +124,61 @@ public function testVerifyUnavailableProvider() $this->expectExceptionMessage('Provider is not allowed.'); $this->duo ->expects($this->never()) - ->method('getRequestSignature'); - $this->model->createAdminAccessTokenWithCredentials( + ->method('authorizeUser'); + $this->model->createAdminAccessTokenWithCredentialsAndPasscode( 'adminUser', Bootstrap::ADMIN_PASSWORD, - 'signature' + '123456' ); } /** * @magentoConfigFixture default/twofactorauth/general/force_providers duo_security + * @magentoConfigFixture default/twofactorauth/duo/client_id ABCDEFGHIJKLMNOPQRST + * @magentoConfigFixture default/twofactorauth/duo/client_secret abcdefghijklmnopqrstuvwxyz0123456789abcd * @magentoConfigFixture default/twofactorauth/duo/integration_key abc123 - * @magentoConfigFixture default/twofactorauth/duo/api_hostname abc123 + * @magentoConfigFixture default/twofactorauth/duo/api_hostname test.duosecurity.com * @magentoConfigFixture default/twofactorauth/duo/secret_key abc123 * @magentoDataFixture Magento/User/_files/user_with_role.php */ - public function testGetAuthenticateDataValidRequest() + public function testVerifyValidRequest() { $userId = $this->getUserId(); + // Activate the Two-Factor Authentication provider for DuoSecurity $this->tfa->getProviderByCode(DuoSecurity::CODE) ->activate($userId); - $this->duo - ->method('getApiHostname') - ->willReturn('abc'); - $this->duo - ->method('getRequestSignature') - ->with( - $this->callback(function ($value) use ($userId) { - return (int)$value->getId() === $userId; - }) - ) - ->willReturn('cba'); - - $result = $this->model->getAuthenticateData( - 'adminUser', - Bootstrap::ADMIN_PASSWORD - ); - - self::assertInstanceOf(DuoDataInterface::class, $result); - self::assertSame('abc', $result->getApiHostname()); - self::assertSame('cba', $result->getSignature()); - } - - /** - * @magentoConfigFixture default/twofactorauth/general/force_providers duo_security - * @magentoConfigFixture default/twofactorauth/duo/integration_key abc123 - * @magentoConfigFixture default/twofactorauth/duo/api_hostname abc123 - * @magentoConfigFixture default/twofactorauth/duo/secret_key abc123 - * @magentoDataFixture Magento/User/_files/user_with_role.php - */ - public function testVerifyValidRequest() - { - $userId = $this->getUserId(); - $this->tfa->getProviderByCode(DuoSecurity::CODE) - ->activate($userId); + $username = 'adminUser'; + $password = Bootstrap::ADMIN_PASSWORD; + $passcode = '123456'; // Example passcode for the test - $signature = 'a signature'; - $this->duo->method('verify') + // Mock the DuoSecurity `authorizeUser` method to simulate valid Duo response + $this->duo->method('authorizeUser') ->with( - $this->callback(function ($value) use ($userId) { - return (int)$value->getId() === $userId; - }), - $this->callback(function ($value) use ($signature) { - return $value->getData('sig_response') === $signature; - }) + $this->equalTo($username), + $this->equalTo("passcode"), + $this->equalTo(['passcode' => $passcode]) ) - ->willReturn(true); + ->willReturn(['status' => 'allow']); - $token = $this->model->createAdminAccessTokenWithCredentials( - 'adminUser', - Bootstrap::ADMIN_PASSWORD, - $signature + // Attempt to create the access token + $token = $this->model->createAdminAccessTokenWithCredentialsAndPasscode( + $username, + $password, + $passcode ); + // Assert that a token was generated successfully self::assertNotEmpty($token); } /** * @magentoConfigFixture default/twofactorauth/general/force_providers duo_security + * @magentoConfigFixture default/twofactorauth/duo/client_id ABCDEFGHIJKLMNOPQRST + * @magentoConfigFixture default/twofactorauth/duo/client_secret abcdefghijklmnopqrstuvwxyz0123456789abcd * @magentoConfigFixture default/twofactorauth/duo/integration_key abc123 - * @magentoConfigFixture default/twofactorauth/duo/api_hostname abc123 + * @magentoConfigFixture default/twofactorauth/duo/api_hostname test.duosecurity.com * @magentoConfigFixture default/twofactorauth/duo/secret_key abc123 * @magentoDataFixture Magento/User/_files/user_with_role.php */ @@ -271,21 +186,32 @@ public function testVerifyInvalidRequest() { $this->expectException(\Magento\Framework\Exception\LocalizedException::class); $this->expectExceptionMessage('Invalid response'); + $userId = $this->getUserId(); + + // Activate the Two-Factor Authentication provider for DuoSecurity $this->tfa->getProviderByCode(DuoSecurity::CODE) ->activate($userId); - $signature = 'a signature'; - $this->duo->method('verify') - ->willReturn(false); + $username = 'adminUser'; + $password = Bootstrap::ADMIN_PASSWORD; + $passcode = '123456'; // Example passcode used for testing - $token = $this->model->createAdminAccessTokenWithCredentials( - 'adminUser', - Bootstrap::ADMIN_PASSWORD, - $signature - ); + // Mock the DuoSecurity `authorizeUser` method to simulate an invalid response + $this->duo->method('authorizeUser') + ->with( + $this->equalTo($username), + $this->equalTo("passcode"), + $this->equalTo(['passcode' => $passcode]) + ) + ->willReturn(['status' => 'deny', 'msg' => 'Authentication denied']); // Simulate invalid response - self::assertEmpty($token); + // Attempt to create the access token, expecting an exception due to the invalid response + $this->model->createAdminAccessTokenWithCredentialsAndPasscode( + $username, + $password, + $passcode + ); } private function getUserId(): int diff --git a/TwoFactorAuth/Test/Integration/Model/Provider/Engine/DuoSecurity/ConfigureTest.php b/TwoFactorAuth/Test/Integration/Model/Provider/Engine/DuoSecurity/ConfigureTest.php index 4a306f70..4a58ff57 100644 --- a/TwoFactorAuth/Test/Integration/Model/Provider/Engine/DuoSecurity/ConfigureTest.php +++ b/TwoFactorAuth/Test/Integration/Model/Provider/Engine/DuoSecurity/ConfigureTest.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ declare(strict_types=1); @@ -9,7 +9,6 @@ namespace Magento\TwoFactorAuth\Test\Integration\Model\Provider\Engine\DuoSecurity; use Magento\Framework\App\ObjectManager; -use Magento\TwoFactorAuth\Api\Data\DuoDataInterface; use Magento\TwoFactorAuth\Api\TfaInterface; use Magento\TwoFactorAuth\Api\UserConfigTokenManagerInterface; use Magento\TwoFactorAuth\Model\Provider\Engine\DuoSecurity; @@ -57,10 +56,10 @@ class ConfigureTest extends TestCase protected function setUp(): void { $objectManager = ObjectManager::getInstance(); - $this->userFactory = $objectManager->get(UserFactory::class); $this->tokenManager = $objectManager->get(UserConfigTokenManagerInterface::class); $this->tfa = $objectManager->get(TfaInterface::class); $this->duo = $this->createMock(DuoSecurity::class); + $this->userFactory = $objectManager->get(UserFactory::class); $this->authenticate = $this->createMock(Authenticate::class); $this->model = $objectManager->create( Configure::class, @@ -73,31 +72,35 @@ protected function setUp(): void /** * @magentoConfigFixture default/twofactorauth/general/force_providers duo_security + * @magentoConfigFixture default/twofactorauth/duo/client_id ABCDEFGHIJKLMNOPQRST + * @magentoConfigFixture default/twofactorauth/duo/client_secret abcdefghijklmnopqrstuvwxyz0123456789abcd * @magentoConfigFixture default/twofactorauth/duo/integration_key abc123 - * @magentoConfigFixture default/twofactorauth/duo/api_hostname abc123 + * @magentoConfigFixture default/twofactorauth/duo/api_hostname test.duosecurity.com * @magentoConfigFixture default/twofactorauth/duo/secret_key abc123 * @magentoDataFixture Magento/User/_files/user_with_role.php */ - public function testGetConfigurationDataInvalidTfat() + public function testGetDuoConfigurationDataInvalidTfat() { $this->expectException(\Magento\Framework\Exception\AuthorizationException::class); $this->expectExceptionMessage('Invalid two-factor authorization token'); $this->duo ->expects($this->never()) - ->method('getRequestSignature'); - $this->model->getConfigurationData( + ->method('enrollNewUser'); + $this->model->getDuoConfigurationData( 'abc' ); } /** * @magentoConfigFixture default/twofactorauth/general/force_providers duo_security + * @magentoConfigFixture default/twofactorauth/duo/client_id ABCDEFGHIJKLMNOPQRST + * @magentoConfigFixture default/twofactorauth/duo/client_secret abcdefghijklmnopqrstuvwxyz0123456789abcd * @magentoConfigFixture default/twofactorauth/duo/integration_key abc123 - * @magentoConfigFixture default/twofactorauth/duo/api_hostname abc123 + * @magentoConfigFixture default/twofactorauth/duo/api_hostname test.duosecurity.com * @magentoConfigFixture default/twofactorauth/duo/secret_key abc123 * @magentoDataFixture Magento/User/_files/user_with_role.php */ - public function testGetConfigurationDataAlreadyConfiguredProvider() + public function testGetDuoConfigurationDataAlreadyConfiguredProvider() { $this->expectException(\Magento\Framework\Exception\LocalizedException::class); $this->expectExceptionMessage('Provider is already configured.'); @@ -107,8 +110,8 @@ public function testGetConfigurationDataAlreadyConfiguredProvider() $this->duo ->expects($this->never()) - ->method('getRequestSignature'); - $this->model->getConfigurationData( + ->method('enrollNewUser'); + $this->model->getDuoConfigurationData( $this->tokenManager->issueFor($userId) ); } @@ -117,33 +120,35 @@ public function testGetConfigurationDataAlreadyConfiguredProvider() * @magentoConfigFixture default/twofactorauth/general/force_providers authy * @magentoDataFixture Magento/User/_files/user_with_role.php */ - public function testGetConfigurationDataUnavailableProvider() + public function testGetDuoConfigurationDataUnavailableProvider() { $this->expectException(\Magento\Framework\Exception\LocalizedException::class); $this->expectExceptionMessage('Provider is not allowed.'); $this->duo ->expects($this->never()) - ->method('getRequestSignature'); - $this->model->getConfigurationData( + ->method('enrollNewUser'); + $this->model->getDuoConfigurationData( $this->tokenManager->issueFor($this->getUserId()) ); } /** * @magentoConfigFixture default/twofactorauth/general/force_providers duo_security + * @magentoConfigFixture default/twofactorauth/duo/client_id ABCDEFGHIJKLMNOPQRST + * @magentoConfigFixture default/twofactorauth/duo/client_secret abcdefghijklmnopqrstuvwxyz0123456789abcd * @magentoConfigFixture default/twofactorauth/duo/integration_key abc123 - * @magentoConfigFixture default/twofactorauth/duo/api_hostname abc123 + * @magentoConfigFixture default/twofactorauth/duo/api_hostname test.duosecurity.com * @magentoConfigFixture default/twofactorauth/duo/secret_key abc123 * @magentoDataFixture Magento/User/_files/user_with_role.php */ - public function testActivateInvalidTfat() + public function testDuoActivateInvalidTfat() { $this->expectException(\Magento\Framework\Exception\AuthorizationException::class); $this->expectExceptionMessage('Invalid two-factor authorization token'); $this->duo ->expects($this->never()) - ->method('getRequestSignature'); - $this->model->activate( + ->method('assertUserIsValid'); + $this->model->duoActivate( 'abc', 'something' ); @@ -151,12 +156,14 @@ public function testActivateInvalidTfat() /** * @magentoConfigFixture default/twofactorauth/general/force_providers duo_security + * @magentoConfigFixture default/twofactorauth/duo/client_id ABCDEFGHIJKLMNOPQRST + * @magentoConfigFixture default/twofactorauth/duo/client_secret abcdefghijklmnopqrstuvwxyz0123456789abcd * @magentoConfigFixture default/twofactorauth/duo/integration_key abc123 - * @magentoConfigFixture default/twofactorauth/duo/api_hostname abc123 + * @magentoConfigFixture default/twofactorauth/duo/api_hostname test.duosecurity.com * @magentoConfigFixture default/twofactorauth/duo/secret_key abc123 * @magentoDataFixture Magento/User/_files/user_with_role.php */ - public function testActivateAlreadyConfiguredProvider() + public function testDuoActivateAlreadyConfiguredProvider() { $this->expectException(\Magento\Framework\Exception\LocalizedException::class); $this->expectExceptionMessage('Provider is already configured.'); @@ -165,8 +172,8 @@ public function testActivateAlreadyConfiguredProvider() ->activate($userId); $this->duo ->expects($this->never()) - ->method('getRequestSignature'); - $this->model->activate( + ->method('assertUserIsValid'); + $this->model->duoActivate( $this->tokenManager->issueFor($userId), 'something' ); @@ -176,15 +183,15 @@ public function testActivateAlreadyConfiguredProvider() * @magentoConfigFixture default/twofactorauth/general/force_providers authy * @magentoDataFixture Magento/User/_files/user_with_role.php */ - public function testActivateUnavailableProvider() + public function testDuoActivateUnavailableProvider() { $this->expectException(\Magento\Framework\Exception\LocalizedException::class); $this->expectExceptionMessage('Provider is not allowed.'); $userId = $this->getUserId(); $this->duo ->expects($this->never()) - ->method('getRequestSignature'); - $this->model->activate( + ->method('assertUserIsValid'); + $this->model->duoActivate( $this->tokenManager->issueFor($userId), 'something' ); @@ -192,87 +199,85 @@ public function testActivateUnavailableProvider() /** * @magentoConfigFixture default/twofactorauth/general/force_providers duo_security + * @magentoConfigFixture default/twofactorauth/duo/client_id ABCDEFGHIJKLMNOPQRST + * @magentoConfigFixture default/twofactorauth/duo/client_secret abcdefghijklmnopqrstuvwxyz0123456789abcd * @magentoConfigFixture default/twofactorauth/duo/integration_key abc123 - * @magentoConfigFixture default/twofactorauth/duo/api_hostname abc123 + * @magentoConfigFixture default/twofactorauth/duo/api_hostname test.duosecurity.com * @magentoConfigFixture default/twofactorauth/duo/secret_key abc123 * @magentoDataFixture Magento/User/_files/user_with_role.php */ - public function testGetConfigurationDataValidRequest() + public function testGetDuoConfigurationDataValidRequest() { $userId = $this->getUserId(); + $userName = 'adminUser'; $this->duo - ->method('getApiHostname') - ->willReturn('abc'); - $this->duo - ->method('getRequestSignature') - ->with( - $this->callback(function ($value) use ($userId) { - return (int)$value->getId() === $userId; - }) - ) - ->willReturn('cba'); - - $result = $this->model->getConfigurationData( + ->expects($this->once()) + ->method('enrollNewUser') + ->with($userName, 60) + ->willReturn('enrollment_data'); + + $result = $this->model->getDuoConfigurationData( $this->tokenManager->issueFor($userId) ); - self::assertInstanceOf(DuoDataInterface::class, $result); - self::assertSame('abc', $result->getApiHostname()); - self::assertSame('cba', $result->getSignature()); + self::assertSame('enrollment_data', $result); } /** * @magentoConfigFixture default/twofactorauth/general/force_providers duo_security + * @magentoConfigFixture default/twofactorauth/duo/client_id ABCDEFGHIJKLMNOPQRST + * @magentoConfigFixture default/twofactorauth/duo/client_secret abcdefghijklmnopqrstuvwxyz0123456789abcd * @magentoConfigFixture default/twofactorauth/duo/integration_key abc123 - * @magentoConfigFixture default/twofactorauth/duo/api_hostname abc123 + * @magentoConfigFixture default/twofactorauth/duo/api_hostname test.duosecurity.com * @magentoConfigFixture default/twofactorauth/duo/secret_key abc123 * @magentoDataFixture Magento/User/_files/user_with_role.php */ - public function testActivateValidRequest() + public function testDuoActivateValidRequest() { $userId = $this->getUserId(); - $tfat = $this->tokenManager->issueFor($userId); + $userName = 'adminUser'; - $signature = 'a signature'; - $this->authenticate->method('assertResponseIsValid') - ->with( - $this->callback(function ($value) use ($userId) { - return (int)$value->getId() === $userId; - }), - $signature - ); + $this->duo + ->expects($this->once()) + ->method('assertUserIsValid') + ->with($userName) + ->willReturn('auth'); - $this->model->activate($tfat, $signature); + $this->model->duoActivate( + $this->tokenManager->issueFor($userId) + ); self::assertTrue($this->tfa->getProviderByCode(DuoSecurity::CODE)->isActive($userId)); } /** * @magentoConfigFixture default/twofactorauth/general/force_providers duo_security + * @magentoConfigFixture default/twofactorauth/duo/client_id ABCDEFGHIJKLMNOPQRST + * @magentoConfigFixture default/twofactorauth/duo/client_secret abcdefghijklmnopqrstuvwxyz0123456789abcd * @magentoConfigFixture default/twofactorauth/duo/integration_key abc123 - * @magentoConfigFixture default/twofactorauth/duo/api_hostname abc123 + * @magentoConfigFixture default/twofactorauth/duo/api_hostname test.duosecurity.com * @magentoConfigFixture default/twofactorauth/duo/secret_key abc123 * @magentoDataFixture Magento/User/_files/user_with_role.php */ - public function testActivateInvalidDataThrowsException() + public function testDuoActivateInvalidDataThrowsException() { $this->expectException(\InvalidArgumentException::class); $this->expectExceptionMessage('Something'); + $userId = $this->getUserId(); $tfat = $this->tokenManager->issueFor($userId); - $signature = 'a signature'; - $this->authenticate->method('assertResponseIsValid') - ->with( - $this->callback(function ($value) use ($userId) { - return (int)$value->getId() === $userId; - }), - $signature - ) + $userName = 'adminUser'; + + $this->duo + ->expects($this->once()) + ->method('assertUserIsValid') + ->with($userName) ->willThrowException(new \InvalidArgumentException('Something')); - $result = $this->model->activate($tfat, $signature); + // Call activate without a signature, as per your updated logic + $result = $this->model->duoActivate($tfat); self::assertEmpty($result); } diff --git a/TwoFactorAuth/Test/Integration/Model/Provider/Engine/U2fKey/AuthenticateTest.php b/TwoFactorAuth/Test/Integration/Model/Provider/Engine/U2fKey/AuthenticateTest.php index 3448fc6a..f668eadc 100644 --- a/TwoFactorAuth/Test/Integration/Model/Provider/Engine/U2fKey/AuthenticateTest.php +++ b/TwoFactorAuth/Test/Integration/Model/Provider/Engine/U2fKey/AuthenticateTest.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ declare(strict_types=1); diff --git a/TwoFactorAuth/Test/Integration/Model/Provider/Engine/U2fKey/ConfigureTest.php b/TwoFactorAuth/Test/Integration/Model/Provider/Engine/U2fKey/ConfigureTest.php index f9f952a7..cd410ca9 100644 --- a/TwoFactorAuth/Test/Integration/Model/Provider/Engine/U2fKey/ConfigureTest.php +++ b/TwoFactorAuth/Test/Integration/Model/Provider/Engine/U2fKey/ConfigureTest.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ declare(strict_types=1); diff --git a/TwoFactorAuth/Test/Integration/Model/Provider/Engine/U2fKey/SessionTest.php b/TwoFactorAuth/Test/Integration/Model/Provider/Engine/U2fKey/SessionTest.php index 56039e51..1f1cbfc4 100644 --- a/TwoFactorAuth/Test/Integration/Model/Provider/Engine/U2fKey/SessionTest.php +++ b/TwoFactorAuth/Test/Integration/Model/Provider/Engine/U2fKey/SessionTest.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ declare(strict_types=1); diff --git a/TwoFactorAuth/Test/Integration/Model/TfaSessionTest.php b/TwoFactorAuth/Test/Integration/Model/TfaSessionTest.php index 73195242..897d50e3 100644 --- a/TwoFactorAuth/Test/Integration/Model/TfaSessionTest.php +++ b/TwoFactorAuth/Test/Integration/Model/TfaSessionTest.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ declare(strict_types=1); diff --git a/TwoFactorAuth/Test/Integration/UserConfigManagerTest.php b/TwoFactorAuth/Test/Integration/UserConfigManagerTest.php index 0ef7d110..50d8b00f 100644 --- a/TwoFactorAuth/Test/Integration/UserConfigManagerTest.php +++ b/TwoFactorAuth/Test/Integration/UserConfigManagerTest.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Test\Integration; diff --git a/TwoFactorAuth/Test/Integration/UserConfigRequestManagerTest.php b/TwoFactorAuth/Test/Integration/UserConfigRequestManagerTest.php index bc081fa0..c980ee66 100644 --- a/TwoFactorAuth/Test/Integration/UserConfigRequestManagerTest.php +++ b/TwoFactorAuth/Test/Integration/UserConfigRequestManagerTest.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2015 Adobe + * All Rights Reserved. */ declare(strict_types=1); @@ -9,7 +9,9 @@ namespace Magento\TwoFactorAuth\Test\Integration; use Magento\Framework\Acl\Builder; -use Magento\TestFramework\Helper\Bootstrap; +use Magento\Framework\Exception\AuthorizationException; +use Magento\TestFramework\Bootstrap; +use Magento\TestFramework\Helper\Bootstrap as BootstrapHelper; use Magento\TestFramework\Mail\Template\TransportBuilderMock; use Magento\User\Model\User; use Magento\TwoFactorAuth\Api\TfaInterface; @@ -17,6 +19,7 @@ use Magento\TwoFactorAuth\Api\UserConfigTokenManagerInterface; use Magento\TwoFactorAuth\Model\Provider\Engine\Google; use PHPUnit\Framework\TestCase; +use Throwable; /** * @magentoDbIsolation enabled @@ -58,15 +61,15 @@ class UserConfigRequestManagerTest extends TestCase protected function setUp(): void { /** @var User $user */ - $user = Bootstrap::getObjectManager()->create(User::class); - $user->loadByUsername(\Magento\TestFramework\Bootstrap::ADMIN_NAME); + $user = BootstrapHelper::getObjectManager()->create(User::class); + $user->loadByUsername(Bootstrap::ADMIN_NAME); $this->user = $user; - $this->tfa = Bootstrap::getObjectManager()->get(TfaInterface::class); - $this->transportBuilderMock = Bootstrap::getObjectManager()->get(TransportBuilderMock::class); - $this->tokenManager = Bootstrap::getObjectManager()->get(UserConfigTokenManagerInterface::class); - $this->aclBuilder = Bootstrap::getObjectManager()->get(Builder::class); + $this->tfa = BootstrapHelper::getObjectManager()->get(TfaInterface::class); + $this->transportBuilderMock = BootstrapHelper::getObjectManager()->get(TransportBuilderMock::class); + $this->tokenManager = BootstrapHelper::getObjectManager()->get(UserConfigTokenManagerInterface::class); + $this->aclBuilder = BootstrapHelper::getObjectManager()->get(Builder::class); - $this->manager = Bootstrap::getObjectManager()->get(UserConfigRequestManagerInterface::class); + $this->manager = BootstrapHelper::getObjectManager()->get(UserConfigRequestManagerInterface::class); } /** @@ -104,17 +107,20 @@ public function testIsRequiredWithConfig(): void } /** - * Check that app config request E-mail is NOT sent for a user that does not posses proper rights. + * Check that app config request E-mail is NOT sent for a user that does not possess proper rights. * * @return void - * @throws \Throwable + * @throws Throwable * @magentoAppArea adminhtml * @magentoAppIsolation enabled */ public function testFailAppConfigRequest(): void { - $this->expectException(\Magento\Framework\Exception\AuthorizationException::class); - $this->aclBuilder->getAcl()->deny(null, 'Magento_TwoFactorAuth::config'); + $this->expectException(AuthorizationException::class); + $this->aclBuilder->getAcl()->deny( + Bootstrap::ADMIN_ROLE_ID, + 'Magento_TwoFactorAuth::config' + ); $this->manager->sendConfigRequestTo($this->user); } @@ -122,7 +128,7 @@ public function testFailAppConfigRequest(): void * Check that app config request E-mail is sent for a user that posseses proper rights. * * @return void - * @throws \Throwable + * @throws Throwable * @magentoAppArea adminhtml */ public function testSendAppConfigRequest(): void @@ -130,7 +136,7 @@ public function testSendAppConfigRequest(): void $this->manager->sendConfigRequestTo($this->user); $this->assertNotEmpty($message = $this->transportBuilderMock->getSentMessage()); - $messageHtml = $message->getBody()->getParts()[0]->getRawContent(); + $messageHtml = quoted_printable_decode($message->getBody()->bodyToString()); $this->assertStringContainsString( 'You are required to configure website-wide and personal Two-Factor Authorization in order to login to', $messageHtml @@ -151,7 +157,7 @@ public function testSendAppConfigRequest(): void * Check that personal 2FA config request E-mail is sent for users. * * @return void - * @throws \Throwable + * @throws Throwable * @magentoAppArea adminhtml * @magentoConfigFixture default/twofactorauth/general/force_providers google */ @@ -160,7 +166,7 @@ public function testSendUserConfigRequest(): void $this->manager->sendConfigRequestTo($this->user); $this->assertNotEmpty($message = $this->transportBuilderMock->getSentMessage()); - $messageHtml = $message->getBody()->getParts()[0]->getRawContent(); + $messageHtml = quoted_printable_decode($message->getBody()->bodyToString()); $this->assertStringContainsString( 'You are required to configure personal Two-Factor Authorization in order to login to', $messageHtml diff --git a/TwoFactorAuth/Test/Integration/UserConfigTokenManagerTest.php b/TwoFactorAuth/Test/Integration/UserConfigTokenManagerTest.php index c98419d6..49376665 100644 --- a/TwoFactorAuth/Test/Integration/UserConfigTokenManagerTest.php +++ b/TwoFactorAuth/Test/Integration/UserConfigTokenManagerTest.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ declare(strict_types=1); diff --git a/TwoFactorAuth/Test/Integration/_files/overrides.xml b/TwoFactorAuth/Test/Integration/_files/overrides.xml index c6109823..5ab2c0be 100644 --- a/TwoFactorAuth/Test/Integration/_files/overrides.xml +++ b/TwoFactorAuth/Test/Integration/_files/overrides.xml @@ -1,10 +1,10 @@ <?xml version="1.0"?> <!-- /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ - --> +--> <overrides> <test class="\Magento\Webapi\Controller\SoapTest" skip="true"/> <test class="\Magento\Webapi\Controller\SoapEnterpriseTest" skip="true"/> diff --git a/TwoFactorAuth/Test/Mftf/ActionGroup/AdminCreateRoleActionGroup.xml b/TwoFactorAuth/Test/Mftf/ActionGroup/AdminCreateRoleActionGroup.xml index c235e474..b7cb2bd9 100644 --- a/TwoFactorAuth/Test/Mftf/ActionGroup/AdminCreateRoleActionGroup.xml +++ b/TwoFactorAuth/Test/Mftf/ActionGroup/AdminCreateRoleActionGroup.xml @@ -1,9 +1,9 @@ <?xml version="1.0" encoding="UTF-8"?> <!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ +/** + * Copyright 2020 Adobe + * All Rights Reserved. + */ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> diff --git a/TwoFactorAuth/Test/Mftf/ActionGroup/AdminCreateUserRoleWithReportsActionGroup.xml b/TwoFactorAuth/Test/Mftf/ActionGroup/AdminCreateUserRoleWithReportsActionGroup.xml index b0fa4280..ad84b55c 100644 --- a/TwoFactorAuth/Test/Mftf/ActionGroup/AdminCreateUserRoleWithReportsActionGroup.xml +++ b/TwoFactorAuth/Test/Mftf/ActionGroup/AdminCreateUserRoleWithReportsActionGroup.xml @@ -1,9 +1,9 @@ <?xml version="1.0" encoding="UTF-8"?> <!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ +/** + * Copyright 2020 Adobe + * All Rights Reserved. + */ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> diff --git a/TwoFactorAuth/Test/Mftf/ActionGroup/AdminFillUserRoleRequiredDataActionGroup.xml b/TwoFactorAuth/Test/Mftf/ActionGroup/AdminFillUserRoleRequiredDataActionGroup.xml index c756a363..7a8c5663 100644 --- a/TwoFactorAuth/Test/Mftf/ActionGroup/AdminFillUserRoleRequiredDataActionGroup.xml +++ b/TwoFactorAuth/Test/Mftf/ActionGroup/AdminFillUserRoleRequiredDataActionGroup.xml @@ -1,9 +1,9 @@ <?xml version="1.0" encoding="UTF-8"?> <!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ +/** + * Copyright 2020 Adobe + * All Rights Reserved. + */ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> diff --git a/TwoFactorAuth/Test/Mftf/ActionGroup/AdminLoginActionGroup.xml b/TwoFactorAuth/Test/Mftf/ActionGroup/AdminLoginActionGroup.xml index 97bd9001..34d315be 100644 --- a/TwoFactorAuth/Test/Mftf/ActionGroup/AdminLoginActionGroup.xml +++ b/TwoFactorAuth/Test/Mftf/ActionGroup/AdminLoginActionGroup.xml @@ -1,9 +1,9 @@ <?xml version="1.0" encoding="UTF-8"?> <!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ +/** + * Copyright 2020 Adobe + * All Rights Reserved. + */ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> diff --git a/TwoFactorAuth/Test/Mftf/ActionGroup/AdminSaveUserRoleActionGroup.xml b/TwoFactorAuth/Test/Mftf/ActionGroup/AdminSaveUserRoleActionGroup.xml new file mode 100644 index 00000000..0c9ae61b --- /dev/null +++ b/TwoFactorAuth/Test/Mftf/ActionGroup/AdminSaveUserRoleActionGroup.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright 2023 Adobe + * All Rights Reserved. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminSaveUserRoleActionGroup"> + <annotations> + <description>Merges with CE actionGroup. Adds steps to enable the 2FA ACL rule when creating a User Role.</description> + </annotations> + <waitForElementVisible selector="{{AdminEditRoleResourcesSection.resourceCheckboxLink('Magento_TwoFactorAuth::tfa', 'Two Factor Auth')}}" stepKey="waitFor2FACheckboxVisible" before="check2FAIfNotAlreadyChecked"/> + <conditionalClick selector="{{AdminEditRoleResourcesSection.resourceCheckboxLink('Magento_TwoFactorAuth::tfa', 'Two Factor Auth')}}" dependentSelector="{{AdminEditRoleResourcesSection.resourceCheckbox('Magento_TwoFactorAuth::tfa')}}" visible="false" stepKey="check2FAIfNotAlreadyChecked" before="see2FAChecked"/> + <seeElement selector="{{AdminEditRoleResourcesSection.resourceCheckbox('Magento_TwoFactorAuth::tfa')}}" stepKey="see2FAChecked" before="clickSaveRoleButton"/> + </actionGroup> +</actionGroups> diff --git a/TwoFactorAuth/Test/Mftf/Data/TwoFactorAuthData.xml b/TwoFactorAuth/Test/Mftf/Data/TwoFactorAuthData.xml index dc75c491..bd3fed54 100644 --- a/TwoFactorAuth/Test/Mftf/Data/TwoFactorAuthData.xml +++ b/TwoFactorAuth/Test/Mftf/Data/TwoFactorAuthData.xml @@ -1,11 +1,10 @@ <?xml version="1.0" encoding="UTF-8"?> <!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ +/** + * Copyright 2021 Adobe + * All Rights Reserved. + */ --> - <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <entity name="adminApiToken" type="adminToken"> <data key="username">{{_ENV.MAGENTO_ADMIN_USERNAME}}</data> diff --git a/TwoFactorAuth/Test/Mftf/Data/UserRoleData.xml b/TwoFactorAuth/Test/Mftf/Data/UserRoleData.xml index 16a4bca3..014f660a 100644 --- a/TwoFactorAuth/Test/Mftf/Data/UserRoleData.xml +++ b/TwoFactorAuth/Test/Mftf/Data/UserRoleData.xml @@ -1,11 +1,10 @@ <?xml version="1.0" encoding="UTF-8"?> <!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ +/** + * Copyright 2020 Adobe + * All Rights Reserved. + */ --> - <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <entity name="adminRestrictedProductRole" type="user_role"> <array key="resource"> diff --git a/TwoFactorAuth/Test/Mftf/Helper/FillOtp.php b/TwoFactorAuth/Test/Mftf/Helper/FillOtp.php index 3135b8fe..94b6331c 100644 --- a/TwoFactorAuth/Test/Mftf/Helper/FillOtp.php +++ b/TwoFactorAuth/Test/Mftf/Helper/FillOtp.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ declare(strict_types=1); diff --git a/TwoFactorAuth/Test/Mftf/Helper/SetSharedSecret.php b/TwoFactorAuth/Test/Mftf/Helper/SetSharedSecret.php index 4d03d013..70487e5c 100644 --- a/TwoFactorAuth/Test/Mftf/Helper/SetSharedSecret.php +++ b/TwoFactorAuth/Test/Mftf/Helper/SetSharedSecret.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ declare(strict_types=1); diff --git a/TwoFactorAuth/Test/Mftf/Metadata/TwoFactorAuthMeta.xml b/TwoFactorAuth/Test/Mftf/Metadata/TwoFactorAuthMeta.xml index 81d2d1af..81b458ad 100644 --- a/TwoFactorAuth/Test/Mftf/Metadata/TwoFactorAuthMeta.xml +++ b/TwoFactorAuth/Test/Mftf/Metadata/TwoFactorAuthMeta.xml @@ -1,9 +1,9 @@ <?xml version="1.0" encoding="UTF-8"?> <!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ +/** + * Copyright 2021 Adobe + * All Rights Reserved. + */ --> <operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> diff --git a/TwoFactorAuth/Test/Mftf/Section/AdminEditRoleInfoSection.xml b/TwoFactorAuth/Test/Mftf/Section/AdminEditRoleInfoSection.xml index 27240767..d50200c7 100644 --- a/TwoFactorAuth/Test/Mftf/Section/AdminEditRoleInfoSection.xml +++ b/TwoFactorAuth/Test/Mftf/Section/AdminEditRoleInfoSection.xml @@ -1,9 +1,9 @@ <?xml version="1.0" encoding="utf-8"?> <!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ +/** + * Copyright 2020 Adobe + * All Rights Reserved. + */ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminEditRoleInfoSection"> diff --git a/TwoFactorAuth/Test/Mftf/Section/AdminGoogleTfaSection.xml b/TwoFactorAuth/Test/Mftf/Section/AdminGoogleTfaSection.xml index 9297b203..788f4636 100644 --- a/TwoFactorAuth/Test/Mftf/Section/AdminGoogleTfaSection.xml +++ b/TwoFactorAuth/Test/Mftf/Section/AdminGoogleTfaSection.xml @@ -1,11 +1,10 @@ <?xml version="1.0" encoding="UTF-8"?> <!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ +/** + * Copyright 2020 Adobe + * All Rights Reserved. + */ --> - <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminGoogleTfaSection"> diff --git a/TwoFactorAuth/Test/Mftf/Test/AdminBulkOperationsLogIsNotAccessibleForAdminUserWithLimitedAccessTest.xml b/TwoFactorAuth/Test/Mftf/Test/AdminBulkOperationsLogIsNotAccessibleForAdminUserWithLimitedAccessTest.xml index 691b1750..739dc420 100644 --- a/TwoFactorAuth/Test/Mftf/Test/AdminBulkOperationsLogIsNotAccessibleForAdminUserWithLimitedAccessTest.xml +++ b/TwoFactorAuth/Test/Mftf/Test/AdminBulkOperationsLogIsNotAccessibleForAdminUserWithLimitedAccessTest.xml @@ -1,9 +1,9 @@ <?xml version="1.0" encoding="UTF-8"?> <!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ +/** + * Copyright 2020 Adobe + * All Rights Reserved. + */ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminBulkOperationsLogIsNotAccessibleForAdminUserWithLimitedAccessTest"> diff --git a/TwoFactorAuth/Test/Mftf/Test/AdminConfigurationPermissionTest.xml b/TwoFactorAuth/Test/Mftf/Test/AdminConfigurationPermissionTest.xml index 560f6986..b421cae0 100644 --- a/TwoFactorAuth/Test/Mftf/Test/AdminConfigurationPermissionTest.xml +++ b/TwoFactorAuth/Test/Mftf/Test/AdminConfigurationPermissionTest.xml @@ -1,9 +1,9 @@ <?xml version="1.0" encoding="UTF-8"?> <!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ +/** + * Copyright 2020 Adobe + * All Rights Reserved. + */ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminConfigurationPermissionTest"> diff --git a/TwoFactorAuth/Test/Mftf/Test/AdminReviewOrderWithReportsPermissionTest.xml b/TwoFactorAuth/Test/Mftf/Test/AdminReviewOrderWithReportsPermissionTest.xml index 0b1740d0..c35c251a 100644 --- a/TwoFactorAuth/Test/Mftf/Test/AdminReviewOrderWithReportsPermissionTest.xml +++ b/TwoFactorAuth/Test/Mftf/Test/AdminReviewOrderWithReportsPermissionTest.xml @@ -1,17 +1,14 @@ <?xml version="1.0" encoding="UTF-8"?> <!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ +/** + * Copyright 2020 Adobe + * All Rights Reserved. + */ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminReviewOrderWithReportsPermissionTest"> <before> - <actionGroup ref="AdminChooseUserRoleResourceActionGroup" before="saveRole" stepKey="enableTfa"> - <argument name="resourceId" value="Magento_TwoFactorAuth::tfa"/> - <argument name="resourceName" value="Two Factor Auth"/> - </actionGroup> + <comment userInput="BIC workaround" before="saveRole" stepKey="enableTfa"/> </before> </test> </tests> diff --git a/TwoFactorAuth/Test/Mftf/Test/AdminUpdateUserRoleTest.xml b/TwoFactorAuth/Test/Mftf/Test/AdminUpdateUserRoleTest.xml index d07e2b46..67bda6ac 100644 --- a/TwoFactorAuth/Test/Mftf/Test/AdminUpdateUserRoleTest.xml +++ b/TwoFactorAuth/Test/Mftf/Test/AdminUpdateUserRoleTest.xml @@ -1,9 +1,9 @@ <?xml version="1.0" encoding="UTF-8"?> <!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ +/** + * Copyright 2021 Adobe + * All Rights Reserved. + */ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> @@ -15,10 +15,7 @@ <severity value="AVERAGE"/> </annotations> <before> - <actionGroup ref="AdminChooseUserRoleResourceActionGroup" before="saveNewRole" stepKey="enableTfa"> - <argument name="resourceId" value="Magento_TwoFactorAuth::tfa"/> - <argument name="resourceName" value="Two Factor Auth"/> - </actionGroup> + <comment userInput="BIC workaround" before="saveNewRole" stepKey="enableTfa"/> </before> </test> </tests> diff --git a/TwoFactorAuth/Test/Mftf/Test/RestrictedAdminCatalogMassActionPermissionTest.xml b/TwoFactorAuth/Test/Mftf/Test/RestrictedAdminCatalogMassActionPermissionTest.xml index 708291a0..646a546e 100644 --- a/TwoFactorAuth/Test/Mftf/Test/RestrictedAdminCatalogMassActionPermissionTest.xml +++ b/TwoFactorAuth/Test/Mftf/Test/RestrictedAdminCatalogMassActionPermissionTest.xml @@ -1,11 +1,10 @@ <?xml version="1.0" encoding="UTF-8"?> <!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ +/** + * Copyright 2020 Adobe + * All Rights Reserved. + */ --> - <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="RestrictedAdminCatalogMassActionPermissionTest"> diff --git a/TwoFactorAuth/Test/Mftf/Test/RestrictedUserRoleForProductRemovalTest.xml b/TwoFactorAuth/Test/Mftf/Test/RestrictedUserRoleForProductRemovalTest.xml index 22b21c7e..fe9aa36d 100644 --- a/TwoFactorAuth/Test/Mftf/Test/RestrictedUserRoleForProductRemovalTest.xml +++ b/TwoFactorAuth/Test/Mftf/Test/RestrictedUserRoleForProductRemovalTest.xml @@ -1,9 +1,9 @@ <?xml version="1.0" encoding="UTF-8"?> <!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ +/** + * Copyright 2020 Adobe + * All Rights Reserved. + */ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="RestrictedUserRoleForProductRemovalTest"> diff --git a/TwoFactorAuth/Test/Mftf/Test/RestrictedUserRoleProductAttributeTest.xml b/TwoFactorAuth/Test/Mftf/Test/RestrictedUserRoleProductAttributeTest.xml index 49ecd5ba..83582449 100644 --- a/TwoFactorAuth/Test/Mftf/Test/RestrictedUserRoleProductAttributeTest.xml +++ b/TwoFactorAuth/Test/Mftf/Test/RestrictedUserRoleProductAttributeTest.xml @@ -1,9 +1,9 @@ <?xml version="1.0" encoding="UTF-8"?> <!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ +/** + * Copyright 2020 Adobe + * All Rights Reserved. + */ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="RestrictedUserRoleProductAttributeTest"> diff --git a/TwoFactorAuth/Test/Mftf/Test/StorefrontGiftWrappingCanBeAppliedOnOrderLevelAndOrderItemForAdditionalWebsiteTest.xml b/TwoFactorAuth/Test/Mftf/Test/StorefrontGiftWrappingCanBeAppliedOnOrderLevelAndOrderItemForAdditionalWebsiteTest.xml index a62c2d48..97be46a3 100644 --- a/TwoFactorAuth/Test/Mftf/Test/StorefrontGiftWrappingCanBeAppliedOnOrderLevelAndOrderItemForAdditionalWebsiteTest.xml +++ b/TwoFactorAuth/Test/Mftf/Test/StorefrontGiftWrappingCanBeAppliedOnOrderLevelAndOrderItemForAdditionalWebsiteTest.xml @@ -1,9 +1,9 @@ <?xml version="1.0" encoding="UTF-8"?> <!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ +/** + * Copyright 2020 Adobe + * All Rights Reserved. + */ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="StorefrontGiftWrappingCanBeAppliedOnOrderLevelAndOrderItemForAdditionalWebsiteTest"> diff --git a/TwoFactorAuth/Test/Mftf/Test/StorefrontInvoiceFilterTest.xml b/TwoFactorAuth/Test/Mftf/Test/StorefrontInvoiceFilterTest.xml index 2ce65495..c1925049 100644 --- a/TwoFactorAuth/Test/Mftf/Test/StorefrontInvoiceFilterTest.xml +++ b/TwoFactorAuth/Test/Mftf/Test/StorefrontInvoiceFilterTest.xml @@ -1,9 +1,9 @@ <?xml version="1.0" encoding="UTF-8"?> <!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ +/** + * Copyright 2020 Adobe + * All Rights Reserved. + */ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="StorefrontInvoiceFilterTest"> diff --git a/TwoFactorAuth/Test/Unit/Model/Config/Backend/Duo/ApiHostnameTest.php b/TwoFactorAuth/Test/Unit/Model/Config/Backend/Duo/ApiHostnameTest.php index 5a670567..12b755de 100644 --- a/TwoFactorAuth/Test/Unit/Model/Config/Backend/Duo/ApiHostnameTest.php +++ b/TwoFactorAuth/Test/Unit/Model/Config/Backend/Duo/ApiHostnameTest.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2024 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Test\Unit\Model\Config\Backend\Duo; @@ -40,7 +41,7 @@ public function testBefore($value, $isValid): void $this->model->beforeSave(); } - public function valuesDataProvider() + public static function valuesDataProvider() { return [ ['', true], diff --git a/TwoFactorAuth/Test/Unit/Model/Config/Backend/ForceProvidersTest.php b/TwoFactorAuth/Test/Unit/Model/Config/Backend/ForceProvidersTest.php index 248f8a19..51d36eea 100644 --- a/TwoFactorAuth/Test/Unit/Model/Config/Backend/ForceProvidersTest.php +++ b/TwoFactorAuth/Test/Unit/Model/Config/Backend/ForceProvidersTest.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ declare(strict_types=1); diff --git a/TwoFactorAuth/Test/Unit/Model/Provider/Engine/AuthyTest.php b/TwoFactorAuth/Test/Unit/Model/Provider/Engine/AuthyTest.php index f76871e3..4b1aae19 100644 --- a/TwoFactorAuth/Test/Unit/Model/Provider/Engine/AuthyTest.php +++ b/TwoFactorAuth/Test/Unit/Model/Provider/Engine/AuthyTest.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2024 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Test\Unit\Model\Provider\Engine; @@ -40,7 +41,7 @@ protected function setUp(): void * * @return array */ - public function getIsEnabledTestDataSet(): array + public static function getIsEnabledTestDataSet(): array { return [ 'api key present' => [ diff --git a/TwoFactorAuth/Test/Unit/Model/Provider/Engine/DuoSecurityTest.php b/TwoFactorAuth/Test/Unit/Model/Provider/Engine/DuoSecurityTest.php index cb6d2aa0..062564f4 100644 --- a/TwoFactorAuth/Test/Unit/Model/Provider/Engine/DuoSecurityTest.php +++ b/TwoFactorAuth/Test/Unit/Model/Provider/Engine/DuoSecurityTest.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ declare(strict_types=1); @@ -9,32 +9,53 @@ namespace Magento\TwoFactorAuth\Test\Unit\Model\Provider\Engine; use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\DataObject; +use Magento\Framework\UrlInterface; use Magento\TwoFactorAuth\Model\Provider\Engine\DuoSecurity; +use Magento\User\Api\Data\UserInterface; +use Duo\DuoUniversal\Client; +use DuoAPI\Auth as DuoAuth; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; class DuoSecurityTest extends TestCase { - /** - * @var DuoSecurity - */ - private $model; - - /** - * @var ScopeConfigInterface|MockObject - */ + /** @var MockObject|ScopeConfigInterface */ private $configMock; + /** @var MockObject|UrlInterface */ + private $urlMock; + + /** @var MockObject|Client */ + private $clientMock; + /** - * @inheritDoc + * @var DuoAuth|MockObject */ + private $duoAuthMock; + + /** @var DuoSecurity */ + private $model; + protected function setUp(): void { - $objectManager = new ObjectManager($this); - $this->configMock = $this->getMockBuilder(ScopeConfigInterface::class)->disableOriginalConstructor()->getMock(); + $this->configMock = $this->getMockBuilder(ScopeConfigInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->urlMock = $this->getMockBuilder(UrlInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->clientMock = $this->createMock(Client::class); + $this->duoAuthMock = $this->createMock(DuoAuth::class); - $this->model = $objectManager->getObject(DuoSecurity::class, ['scopeConfig' => $this->configMock]); + $this->model = new DuoSecurity( + $this->configMock, + $this->urlMock, + $this->clientMock, + $this->duoAuthMock + ); } /** @@ -42,50 +63,15 @@ protected function setUp(): void * * @return array */ - public function getIsEnabledTestDataSet(): array + public static function getIsEnabledTestDataSet(): array { return [ [ - 'value', - 'value', - 'value', - 'value', + 'test.duosecurity.com', + 'ABCDEFGHIJKLMNOPQRST', + 'abcdefghijklmnopqrstuvwxyz0123456789abcd', + 'google,duo_security,authy', true - ], - [ - null, - null, - null, - null, - false - ], - [ - 'value', - null, - null, - null, - false - ], - [ - null, - 'value', - null, - null, - false - ], - [ - null, - null, - 'value', - null, - false - ], - [ - null, - null, - null, - 'value', - false ] ]; } @@ -94,26 +80,25 @@ public function getIsEnabledTestDataSet(): array * Check that the provider is available based on configuration. * * @param string|null $apiHostname - * @param string|null $appKey - * @param string|null $secretKey - * @param string|null $integrationKey + * @param string|null $clientId + * @param string|null $clientSecret * @param bool $expected * @return void * @dataProvider getIsEnabledTestDataSet */ public function testIsEnabled( ?string $apiHostname, - ?string $appKey, - ?string $secretKey, - ?string $integrationKey, + ?string $clientId, + ?string $clientSecret, + string $forceProviders, bool $expected ): void { $this->configMock->method('getValue')->willReturnMap( [ [DuoSecurity::XML_PATH_API_HOSTNAME, 'default', null, $apiHostname], - [DuoSecurity::XML_PATH_APPLICATION_KEY, 'default', null, $appKey], - [DuoSecurity::XML_PATH_SECRET_KEY, 'default', null, $secretKey], - [DuoSecurity::XML_PATH_INTEGRATION_KEY, 'default', null, $integrationKey] + [DuoSecurity::XML_PATH_CLIENT_ID, 'default', null, $clientId], + [DuoSecurity::XML_PATH_CLIENT_SECRET, 'default', null, $clientSecret], + ['twofactorauth/general/force_providers', 'default', null, $forceProviders] ] ); diff --git a/TwoFactorAuth/Test/Unit/Model/Provider/Engine/GoogleTest.php b/TwoFactorAuth/Test/Unit/Model/Provider/Engine/GoogleTest.php index ed6ba5b9..41eaa72d 100644 --- a/TwoFactorAuth/Test/Unit/Model/Provider/Engine/GoogleTest.php +++ b/TwoFactorAuth/Test/Unit/Model/Provider/Engine/GoogleTest.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ declare(strict_types=1); diff --git a/TwoFactorAuth/Test/Unit/Model/Provider/Engine/U2fKey/ConfigReaderTest.php b/TwoFactorAuth/Test/Unit/Model/Provider/Engine/U2fKey/ConfigReaderTest.php index 198d82d5..7d985860 100644 --- a/TwoFactorAuth/Test/Unit/Model/Provider/Engine/U2fKey/ConfigReaderTest.php +++ b/TwoFactorAuth/Test/Unit/Model/Provider/Engine/U2fKey/ConfigReaderTest.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ declare(strict_types=1); diff --git a/TwoFactorAuth/Test/Unit/Model/Provider/Engine/U2fKey/WebApiConfigReaderTest.php b/TwoFactorAuth/Test/Unit/Model/Provider/Engine/U2fKey/WebApiConfigReaderTest.php index a2c13442..32ee9c20 100644 --- a/TwoFactorAuth/Test/Unit/Model/Provider/Engine/U2fKey/WebApiConfigReaderTest.php +++ b/TwoFactorAuth/Test/Unit/Model/Provider/Engine/U2fKey/WebApiConfigReaderTest.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ declare(strict_types=1); diff --git a/TwoFactorAuth/Test/Unit/Model/Provider/Engine/U2fKeyTest.php b/TwoFactorAuth/Test/Unit/Model/Provider/Engine/U2fKeyTest.php index c30e3657..cacc3e90 100644 --- a/TwoFactorAuth/Test/Unit/Model/Provider/Engine/U2fKeyTest.php +++ b/TwoFactorAuth/Test/Unit/Model/Provider/Engine/U2fKeyTest.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ declare(strict_types=1); diff --git a/TwoFactorAuth/Test/Unit/Model/TfaTest.php b/TwoFactorAuth/Test/Unit/Model/TfaTest.php index 394f6613..fc6c7ddf 100644 --- a/TwoFactorAuth/Test/Unit/Model/TfaTest.php +++ b/TwoFactorAuth/Test/Unit/Model/TfaTest.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2024 Adobe + * All Rights Reserved. */ declare(strict_types=1); @@ -142,7 +142,7 @@ public function testAllEnabledProvidersUpdates(): void * * @return array */ - public function getForcedProvidersDataSet(): array + public static function getForcedProvidersDataSet(): array { return [ 'not defined' => [ diff --git a/TwoFactorAuth/Test/Unit/Model/UserConfig/HtmlAreaTokenVerifierTest.php b/TwoFactorAuth/Test/Unit/Model/UserConfig/HtmlAreaTokenVerifierTest.php index bee182f0..a85941d2 100644 --- a/TwoFactorAuth/Test/Unit/Model/UserConfig/HtmlAreaTokenVerifierTest.php +++ b/TwoFactorAuth/Test/Unit/Model/UserConfig/HtmlAreaTokenVerifierTest.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2024 Adobe + * All Rights Reserved. */ declare(strict_types=1); @@ -95,7 +95,7 @@ protected function setUp(): void * * @return array */ - public function getTokenRequestData(): array + public static function getTokenRequestData(): array { return [ 'token in query' => [ diff --git a/TwoFactorAuth/TestFramework/Plugin/BypassTwoFactorAuth.php b/TwoFactorAuth/TestFramework/Plugin/BypassTwoFactorAuth.php index ae084bff..d9b01fee 100644 --- a/TwoFactorAuth/TestFramework/Plugin/BypassTwoFactorAuth.php +++ b/TwoFactorAuth/TestFramework/Plugin/BypassTwoFactorAuth.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2021 Adobe + * All Rights Reserved. */ declare(strict_types=1); diff --git a/TwoFactorAuth/TestFramework/TestCase/AbstractBackendController.php b/TwoFactorAuth/TestFramework/TestCase/AbstractBackendController.php index fc6d4155..19230e71 100644 --- a/TwoFactorAuth/TestFramework/TestCase/AbstractBackendController.php +++ b/TwoFactorAuth/TestFramework/TestCase/AbstractBackendController.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ declare(strict_types=1); diff --git a/TwoFactorAuth/TestFramework/TestCase/AbstractConfigureBackendController.php b/TwoFactorAuth/TestFramework/TestCase/AbstractConfigureBackendController.php index 50063cad..ec49b2fc 100644 --- a/TwoFactorAuth/TestFramework/TestCase/AbstractConfigureBackendController.php +++ b/TwoFactorAuth/TestFramework/TestCase/AbstractConfigureBackendController.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ declare(strict_types=1); diff --git a/TwoFactorAuth/Ui/Component/Form/User/DataProvider.php b/TwoFactorAuth/Ui/Component/Form/User/DataProvider.php index ee3a5dbc..c36ad129 100644 --- a/TwoFactorAuth/Ui/Component/Form/User/DataProvider.php +++ b/TwoFactorAuth/Ui/Component/Form/User/DataProvider.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); namespace Magento\TwoFactorAuth\Ui\Component\Form\User; diff --git a/TwoFactorAuth/composer.json b/TwoFactorAuth/composer.json index 42bb5f09..89e154fe 100644 --- a/TwoFactorAuth/composer.json +++ b/TwoFactorAuth/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-two-factor-auth", "description": "Two Factor Authentication module for Magento2", "require": { - "php": "~7.4.0||~8.0.0||~8.1.0", + "php": "~8.2.0||~8.3.0||~8.4.0", "magento/framework": "*", "magento/magento-composer-installer": "*", "magento/module-backend": "*", @@ -13,9 +13,11 @@ "magento/module-user": "*", "magento/module-integration": "*", "christian-riesen/base32": "^1.3", - "spomky-labs/otphp": "^10.0", - "endroid/qr-code": "^4.3.5", - "2tvenom/cborencode": "^1.0" + "spomky-labs/otphp": "^11.2", + "endroid/qr-code": "^6.0.3", + "2tvenom/cborencode": "^1.0", + "duosecurity/duo_api_php": "^1.1", + "duosecurity/duo_universal_php": "^1.0" }, "type": "magento2-module", "license": "OSL-3.0", diff --git a/TwoFactorAuth/etc/acl.xml b/TwoFactorAuth/etc/acl.xml index 0ff1cdeb..a754fac2 100644 --- a/TwoFactorAuth/etc/acl.xml +++ b/TwoFactorAuth/etc/acl.xml @@ -1,10 +1,10 @@ <?xml version="1.0"?> <!-- /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ - --> +--> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Acl/etc/acl.xsd"> <acl> diff --git a/TwoFactorAuth/etc/adminhtml/di.xml b/TwoFactorAuth/etc/adminhtml/di.xml index bcb1a8dc..6a59a48c 100644 --- a/TwoFactorAuth/etc/adminhtml/di.xml +++ b/TwoFactorAuth/etc/adminhtml/di.xml @@ -1,8 +1,8 @@ <?xml version="1.0"?> <!-- /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" diff --git a/TwoFactorAuth/etc/adminhtml/events.xml b/TwoFactorAuth/etc/adminhtml/events.xml index 0d5c1f23..801ccb77 100644 --- a/TwoFactorAuth/etc/adminhtml/events.xml +++ b/TwoFactorAuth/etc/adminhtml/events.xml @@ -1,8 +1,8 @@ <?xml version="1.0"?> <!-- /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" diff --git a/TwoFactorAuth/etc/adminhtml/routes.xml b/TwoFactorAuth/etc/adminhtml/routes.xml index bbc7e5c6..9fc65704 100644 --- a/TwoFactorAuth/etc/adminhtml/routes.xml +++ b/TwoFactorAuth/etc/adminhtml/routes.xml @@ -1,8 +1,8 @@ <?xml version="1.0"?> <!-- /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" diff --git a/TwoFactorAuth/etc/adminhtml/system.xml b/TwoFactorAuth/etc/adminhtml/system.xml old mode 100644 new mode 100755 index 7086746b..7ac2da7d --- a/TwoFactorAuth/etc/adminhtml/system.xml +++ b/TwoFactorAuth/etc/adminhtml/system.xml @@ -1,8 +1,8 @@ <?xml version="1.0"?> <!-- /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" @@ -35,26 +35,42 @@ <label>Configuration Email URL for Web API</label> <comment>This can be used to override the default email configuration link that is sent when using the Magento Web API's to authenticate. Use the placeholder :tfat to indicate where the token should be injected</comment> </field> + <field canRestore="1" id="twofactorauth_retry" translate="label" type="text" sortOrder="40" + showInDefault="1" showInWebsite="0" showInStore="0"> + <label>Retry attempt limit for Two-Factor Authentication</label> + <comment> + Determines how many times a user can try to complete the 2FA process before being temporarily locked out. + </comment> + </field> + <field canRestore="1" id="auth_lock_expire" translate="label" type="text" sortOrder="40" + showInDefault="1" showInWebsite="0" showInStore="0"> + <label>Two-Factor Authentication lockout time (seconds)</label> + <comment> + Determines how long a user is locked out if they reach the threshold defined above. + Please enter at least 120 and at most 1800 (30 minutes). + </comment> + </field> </group> <group id="google" translate="label" type="text" sortOrder="30" showInDefault="1" showInWebsite="0" showInStore="0"> <label>Google</label> - <field id="otp_window" translate="label comment" type="text" sortOrder="10" showInDefault="1" + <field id="leeway" translate="label comment" type="text" sortOrder="10" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> - <label>OTP Window</label> - <comment>This determines how long the one-time-passwords are valid for. An OTP Window of 1 will result in the current OTP value plus 1 code in the past and 1 code in the future to be valid at any given point in time.</comment> + <label>Leeway</label> + <comment>This sets the time drift leeway for OTPs. A leeway of 29 with a period of 30 means OTPs are valid within ±29 seconds from the current time. The leeway must be smaller than the period</comment> + <backend_model>Magento\TwoFactorAuth\Model\Config\Backend\Leeway</backend_model> </field> </group> <group id="duo" translate="label" type="text" sortOrder="40" showInDefault="1" showInWebsite="0" showInStore="0"> - <label>Duo Security</label> - <field id="integration_key" translate="label comment" type="text" sortOrder="20" showInDefault="1" + <label>Duo Security Universal Prompt</label> + <field id="client_id" translate="label comment" type="text" sortOrder="20" showInDefault="1" showInWebsite="0" showInStore="0"> - <label>Integration key</label> + <label>Client Id</label> </field> - <field id="secret_key" translate="label comment" type="obscure" sortOrder="30" showInDefault="1" + <field id="client_secret" translate="label comment" type="obscure" sortOrder="30" showInDefault="1" showInWebsite="0" showInStore="0"> - <label>Secret key</label> + <label>Client Secret</label> <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model> </field> <field id="api_hostname" translate="label comment" type="text" sortOrder="40" showInDefault="1" @@ -62,6 +78,15 @@ <label>API hostname</label> <backend_model>Magento\TwoFactorAuth\Model\Config\Backend\Duo\ApiHostname</backend_model> </field> + <field id="integration_key" translate="label comment" type="text" sortOrder="60" showInDefault="1" + showInWebsite="0" showInStore="0"> + <label>Integration Key</label> + </field> + <field id="secret_key" translate="label comment" type="obscure" sortOrder="70" showInDefault="1" + showInWebsite="0" showInStore="0"> + <label>Secret Key</label> + <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model> + </field> </group> <group id="authy" translate="label" type="text" sortOrder="50" showInDefault="1" showInWebsite="0" diff --git a/TwoFactorAuth/etc/config.xml b/TwoFactorAuth/etc/config.xml index 515a4ae9..e9afc9f0 100644 --- a/TwoFactorAuth/etc/config.xml +++ b/TwoFactorAuth/etc/config.xml @@ -1,8 +1,8 @@ <?xml version="1.0"?> <!-- /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" @@ -11,17 +11,19 @@ <twofactorauth> <general> <force_providers></force_providers> + <twofactorauth_retry>10</twofactorauth_retry> + <auth_lock_expire>300</auth_lock_expire> </general> <authy> <onetouch_message>Login request to your Magento Admin</onetouch_message> <api_key backend_model="Magento\Config\Model\Config\Backend\Encrypted"/> </authy> <duo> + <client_secret backend_model="Magento\Config\Model\Config\Backend\Encrypted"/> <secret_key backend_model="Magento\Config\Model\Config\Backend\Encrypted"/> - <application_key backend_model="Magento\Config\Model\Config\Backend\Encrypted"/> </duo> <google> - <otp_window>1</otp_window> + <leeway backend_model="Magento\TwoFactorAuth\Model\Config\Backend\Leeway">29</leeway> </google> </twofactorauth> </default> diff --git a/TwoFactorAuth/etc/db_schema.xml b/TwoFactorAuth/etc/db_schema.xml index 711a24c2..f4fe7cb3 100644 --- a/TwoFactorAuth/etc/db_schema.xml +++ b/TwoFactorAuth/etc/db_schema.xml @@ -1,8 +1,8 @@ <?xml version="1.0"?> <!-- /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ --> <schema xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" diff --git a/TwoFactorAuth/etc/di.xml b/TwoFactorAuth/etc/di.xml index d58a4a55..5e04d31b 100644 --- a/TwoFactorAuth/etc/di.xml +++ b/TwoFactorAuth/etc/di.xml @@ -1,8 +1,8 @@ <?xml version="1.0"?> <!-- /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" @@ -38,7 +38,6 @@ <preference for="Magento\TwoFactorAuth\Api\U2fKeyAuthenticateInterface" type="Magento\TwoFactorAuth\Model\Provider\Engine\U2fKey\Authenticate"/> <preference for="Magento\TwoFactorAuth\Api\DuoConfigureInterface" type="Magento\TwoFactorAuth\Model\Provider\Engine\DuoSecurity\Configure"/> <preference for="Magento\TwoFactorAuth\Api\DuoAuthenticateInterface" type="Magento\TwoFactorAuth\Model\Provider\Engine\DuoSecurity\Authenticate"/> - <preference for="Magento\TwoFactorAuth\Api\Data\DuoDataInterface" type="Magento\TwoFactorAuth\Model\Data\Provider\Engine\DuoSecurity\Data"/> <preference for="Magento\TwoFactorAuth\Api\U2fKeyConfigReaderInterface" type="Magento\TwoFactorAuth\Model\Provider\Engine\U2fKey\ConfigReader"/> <type name="Magento\Framework\Console\CommandListInterface"> @@ -57,12 +56,16 @@ <argument name="sensitive" xsi:type="array"> <item name="twofactorauth/duo/integration_key" xsi:type="string">1</item> <item name="twofactorauth/duo/secret_key" xsi:type="string">1</item> + <item name="twofactorauth/duo/client_id" xsi:type="string">1</item> + <item name="twofactorauth/duo/client_secret" xsi:type="string">1</item> <item name="twofactorauth/duo/api_hostname" xsi:type="string">1</item> <item name="twofactorauth/authy/api_key" xsi:type="string">1</item> </argument> <argument name="environment" xsi:type="array"> <item name="twofactorauth/duo/integration_key" xsi:type="string">1</item> <item name="twofactorauth/duo/secret_key" xsi:type="string">1</item> + <item name="twofactorauth/duo/client_id" xsi:type="string">1</item> + <item name="twofactorauth/duo/client_secret" xsi:type="string">1</item> <item name="twofactorauth/duo/api_hostname" xsi:type="string">1</item> <item name="twofactorauth/authy/api_key" xsi:type="string">1</item> </argument> diff --git a/TwoFactorAuth/etc/email_templates.xml b/TwoFactorAuth/etc/email_templates.xml index addfd7bd..3bc386fe 100644 --- a/TwoFactorAuth/etc/email_templates.xml +++ b/TwoFactorAuth/etc/email_templates.xml @@ -1,8 +1,8 @@ <?xml version="1.0"?> <!-- /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Email:etc/email_templates.xsd"> diff --git a/TwoFactorAuth/etc/module.xml b/TwoFactorAuth/etc/module.xml index 9f24769d..8a808c8c 100644 --- a/TwoFactorAuth/etc/module.xml +++ b/TwoFactorAuth/etc/module.xml @@ -1,8 +1,8 @@ <?xml version="1.0"?> <!-- /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" diff --git a/TwoFactorAuth/etc/webapi.xml b/TwoFactorAuth/etc/webapi.xml index 2c7cd443..e947faca 100644 --- a/TwoFactorAuth/etc/webapi.xml +++ b/TwoFactorAuth/etc/webapi.xml @@ -1,10 +1,10 @@ <?xml version="1.0"?> <!-- /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ - --> +--> <routes xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Webapi:etc/webapi.xsd"> @@ -163,28 +163,21 @@ </route> <route url="/V1/tfa/provider/duo_security/configure" method="POST"> - <service class="Magento\TwoFactorAuth\Api\DuoConfigureInterface" method="getConfigurationData"/> + <service class="Magento\TwoFactorAuth\Api\DuoConfigureInterface" method="getDuoConfigurationData"/> <resources> <resource ref="anonymous" /> </resources> </route> <route url="/V1/tfa/provider/duo_security/activate" method="POST"> - <service class="Magento\TwoFactorAuth\Api\DuoConfigureInterface" method="activate"/> - <resources> - <resource ref="anonymous" /> - </resources> - </route> - - <route url="/V1/tfa/provider/duo_security/get-authentication-data" method="POST"> - <service class="Magento\TwoFactorAuth\Api\DuoAuthenticateInterface" method="getAuthenticateData"/> + <service class="Magento\TwoFactorAuth\Api\DuoConfigureInterface" method="duoActivate"/> <resources> <resource ref="anonymous" /> </resources> </route> <route url="/V1/tfa/provider/duo_security/authenticate" method="POST"> - <service class="Magento\TwoFactorAuth\Api\DuoAuthenticateInterface" method="createAdminAccessTokenWithCredentials"/> + <service class="Magento\TwoFactorAuth\Api\DuoAuthenticateInterface" method="createAdminAccessTokenWithCredentialsAndPasscode"/> <resources> <resource ref="anonymous" /> </resources> diff --git a/TwoFactorAuth/etc/webapi_rest/di.xml b/TwoFactorAuth/etc/webapi_rest/di.xml index 1f87ae33..4645f1c3 100644 --- a/TwoFactorAuth/etc/webapi_rest/di.xml +++ b/TwoFactorAuth/etc/webapi_rest/di.xml @@ -1,8 +1,8 @@ <?xml version="1.0"?> <!-- /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" diff --git a/TwoFactorAuth/etc/webapi_soap/di.xml b/TwoFactorAuth/etc/webapi_soap/di.xml index 1f87ae33..4645f1c3 100644 --- a/TwoFactorAuth/etc/webapi_soap/di.xml +++ b/TwoFactorAuth/etc/webapi_soap/di.xml @@ -1,8 +1,8 @@ <?xml version="1.0"?> <!-- /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" diff --git a/TwoFactorAuth/i18n/en_US.csv b/TwoFactorAuth/i18n/en_US.csv new file mode 100755 index 00000000..667ec371 --- /dev/null +++ b/TwoFactorAuth/i18n/en_US.csv @@ -0,0 +1,5 @@ +"Configuration for TwoFactorAuth retry attempts","Configuration for TwoFactorAuth retry attempts" +"Configuration for TwoFactorAuth lock expire time","Configuration for TwoFactorAuth lock expire time" +"TwoFactorAuth Configuration","TwoFactorAuth Configuration" +"Security configurations for TwoFactorAuth page","Security configurations for TwoFactorAuth page" +"Your account is temporarily disabled.","Your account is temporarily disabled." diff --git a/TwoFactorAuth/registration.php b/TwoFactorAuth/registration.php index 3048cf3a..94a265b9 100644 --- a/TwoFactorAuth/registration.php +++ b/TwoFactorAuth/registration.php @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + declare(strict_types=1); \Magento\Framework\Component\ComponentRegistrar::register( diff --git a/TwoFactorAuth/view/adminhtml/email/app_config_required.html b/TwoFactorAuth/view/adminhtml/email/app_config_required.html index 6bf3684d..1c3a3ffb 100644 --- a/TwoFactorAuth/view/adminhtml/email/app_config_required.html +++ b/TwoFactorAuth/view/adminhtml/email/app_config_required.html @@ -1,7 +1,7 @@ <!-- /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ --> <!--@subject {{trans "2FA configuration required for %name" name=$store_name}} @--> diff --git a/TwoFactorAuth/view/adminhtml/email/user_config_required.html b/TwoFactorAuth/view/adminhtml/email/user_config_required.html index efffe609..ffc8d9ad 100644 --- a/TwoFactorAuth/view/adminhtml/email/user_config_required.html +++ b/TwoFactorAuth/view/adminhtml/email/user_config_required.html @@ -1,7 +1,7 @@ <!-- /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ --> <!--@subject {{trans "2FA configuration required for %name" name=$username}} @--> diff --git a/TwoFactorAuth/view/adminhtml/layout/adminhtml_user_edit.xml b/TwoFactorAuth/view/adminhtml/layout/adminhtml_user_edit.xml index c9771d6c..c4160316 100644 --- a/TwoFactorAuth/view/adminhtml/layout/adminhtml_user_edit.xml +++ b/TwoFactorAuth/view/adminhtml/layout/adminhtml_user_edit.xml @@ -1,8 +1,8 @@ <?xml version="1.0"?> <!-- /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ --> <page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" diff --git a/TwoFactorAuth/view/adminhtml/layout/tfa_authy_auth.xml b/TwoFactorAuth/view/adminhtml/layout/tfa_authy_auth.xml index 2d8327df..207bc585 100644 --- a/TwoFactorAuth/view/adminhtml/layout/tfa_authy_auth.xml +++ b/TwoFactorAuth/view/adminhtml/layout/tfa_authy_auth.xml @@ -1,8 +1,8 @@ <?xml version="1.0"?> <!-- /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ --> <page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" diff --git a/TwoFactorAuth/view/adminhtml/layout/tfa_authy_configure.xml b/TwoFactorAuth/view/adminhtml/layout/tfa_authy_configure.xml index 1a79da42..f3e0f3a0 100644 --- a/TwoFactorAuth/view/adminhtml/layout/tfa_authy_configure.xml +++ b/TwoFactorAuth/view/adminhtml/layout/tfa_authy_configure.xml @@ -1,8 +1,8 @@ <?xml version="1.0"?> <!-- /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ --> <page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" diff --git a/TwoFactorAuth/view/adminhtml/layout/tfa_duo_auth.xml b/TwoFactorAuth/view/adminhtml/layout/tfa_duo_auth.xml index 273a7cdf..a74818ec 100644 --- a/TwoFactorAuth/view/adminhtml/layout/tfa_duo_auth.xml +++ b/TwoFactorAuth/view/adminhtml/layout/tfa_duo_auth.xml @@ -1,8 +1,8 @@ <?xml version="1.0"?> <!-- /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ --> <page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" diff --git a/TwoFactorAuth/view/adminhtml/layout/tfa_google_auth.xml b/TwoFactorAuth/view/adminhtml/layout/tfa_google_auth.xml index 8288e39e..dbd087fb 100644 --- a/TwoFactorAuth/view/adminhtml/layout/tfa_google_auth.xml +++ b/TwoFactorAuth/view/adminhtml/layout/tfa_google_auth.xml @@ -1,8 +1,8 @@ <?xml version="1.0"?> <!-- /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ --> <page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" diff --git a/TwoFactorAuth/view/adminhtml/layout/tfa_google_configure.xml b/TwoFactorAuth/view/adminhtml/layout/tfa_google_configure.xml index a571f628..5214002b 100644 --- a/TwoFactorAuth/view/adminhtml/layout/tfa_google_configure.xml +++ b/TwoFactorAuth/view/adminhtml/layout/tfa_google_configure.xml @@ -1,8 +1,8 @@ <?xml version="1.0"?> <!-- /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ --> <page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" diff --git a/TwoFactorAuth/view/adminhtml/layout/tfa_screen.xml b/TwoFactorAuth/view/adminhtml/layout/tfa_screen.xml index 419048cc..a9106150 100644 --- a/TwoFactorAuth/view/adminhtml/layout/tfa_screen.xml +++ b/TwoFactorAuth/view/adminhtml/layout/tfa_screen.xml @@ -1,8 +1,8 @@ <?xml version="1.0"?> <!-- /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ --> <page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" diff --git a/TwoFactorAuth/view/adminhtml/layout/tfa_tfa_accessdenied.xml b/TwoFactorAuth/view/adminhtml/layout/tfa_tfa_accessdenied.xml index 956b8550..9e72f173 100644 --- a/TwoFactorAuth/view/adminhtml/layout/tfa_tfa_accessdenied.xml +++ b/TwoFactorAuth/view/adminhtml/layout/tfa_tfa_accessdenied.xml @@ -1,8 +1,8 @@ <?xml version="1.0"?> <!-- /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ --> <page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" diff --git a/TwoFactorAuth/view/adminhtml/layout/tfa_tfa_configure.xml b/TwoFactorAuth/view/adminhtml/layout/tfa_tfa_configure.xml index bb56d4a7..b4e4a97e 100644 --- a/TwoFactorAuth/view/adminhtml/layout/tfa_tfa_configure.xml +++ b/TwoFactorAuth/view/adminhtml/layout/tfa_tfa_configure.xml @@ -1,8 +1,8 @@ <?xml version="1.0"?> <!-- /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ --> <page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" diff --git a/TwoFactorAuth/view/adminhtml/layout/tfa_tfa_requestconfig.xml b/TwoFactorAuth/view/adminhtml/layout/tfa_tfa_requestconfig.xml index 43643774..82b07e11 100644 --- a/TwoFactorAuth/view/adminhtml/layout/tfa_tfa_requestconfig.xml +++ b/TwoFactorAuth/view/adminhtml/layout/tfa_tfa_requestconfig.xml @@ -1,8 +1,8 @@ <?xml version="1.0"?> <!-- /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ --> <page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" diff --git a/TwoFactorAuth/view/adminhtml/layout/tfa_u2f_auth.xml b/TwoFactorAuth/view/adminhtml/layout/tfa_u2f_auth.xml index 5aa91757..99454163 100644 --- a/TwoFactorAuth/view/adminhtml/layout/tfa_u2f_auth.xml +++ b/TwoFactorAuth/view/adminhtml/layout/tfa_u2f_auth.xml @@ -1,8 +1,8 @@ <?xml version="1.0"?> <!-- /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ --> <page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" diff --git a/TwoFactorAuth/view/adminhtml/layout/tfa_u2f_configure.xml b/TwoFactorAuth/view/adminhtml/layout/tfa_u2f_configure.xml index 8a7d202d..3bf808bd 100644 --- a/TwoFactorAuth/view/adminhtml/layout/tfa_u2f_configure.xml +++ b/TwoFactorAuth/view/adminhtml/layout/tfa_u2f_configure.xml @@ -1,8 +1,8 @@ <?xml version="1.0"?> <!-- /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ --> <page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" diff --git a/TwoFactorAuth/view/adminhtml/templates/system/config/providers/modal_content_body.phtml b/TwoFactorAuth/view/adminhtml/templates/system/config/providers/modal_content_body.phtml index 91fc1a52..89888e33 100644 --- a/TwoFactorAuth/view/adminhtml/templates/system/config/providers/modal_content_body.phtml +++ b/TwoFactorAuth/view/adminhtml/templates/system/config/providers/modal_content_body.phtml @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ ?> diff --git a/TwoFactorAuth/view/adminhtml/templates/tfa/change_provider.phtml b/TwoFactorAuth/view/adminhtml/templates/tfa/change_provider.phtml index c9fda998..19241e05 100644 --- a/TwoFactorAuth/view/adminhtml/templates/tfa/change_provider.phtml +++ b/TwoFactorAuth/view/adminhtml/templates/tfa/change_provider.phtml @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + ?> <div id="tfa-change-provider-container" data-bind="scope:'tfa-change-provider'"> <!-- ko template: getTemplate() --><!-- /ko --> diff --git a/TwoFactorAuth/view/adminhtml/templates/tfa/configure.phtml b/TwoFactorAuth/view/adminhtml/templates/tfa/configure.phtml index 2c6fc570..1e3e6795 100644 --- a/TwoFactorAuth/view/adminhtml/templates/tfa/configure.phtml +++ b/TwoFactorAuth/view/adminhtml/templates/tfa/configure.phtml @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ /** @var \Magento\TwoFactorAuth\Block\Configure $block */ diff --git a/TwoFactorAuth/view/adminhtml/templates/tfa/configure_later.phtml b/TwoFactorAuth/view/adminhtml/templates/tfa/configure_later.phtml index d46edab2..975b5d17 100644 --- a/TwoFactorAuth/view/adminhtml/templates/tfa/configure_later.phtml +++ b/TwoFactorAuth/view/adminhtml/templates/tfa/configure_later.phtml @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + ?> <?php /** @var $block Magento\TwoFactorAuth\Block\ConfigureLater */ diff --git a/TwoFactorAuth/view/adminhtml/templates/tfa/provider/auth.phtml b/TwoFactorAuth/view/adminhtml/templates/tfa/provider/auth.phtml index 71a1d935..6530c810 100644 --- a/TwoFactorAuth/view/adminhtml/templates/tfa/provider/auth.phtml +++ b/TwoFactorAuth/view/adminhtml/templates/tfa/provider/auth.phtml @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + ?> <div id="tfa-auth-container" data-bind="scope:'tfa-auth'"> <!-- ko template: getTemplate() --><!-- /ko --> diff --git a/TwoFactorAuth/view/adminhtml/templates/tfa/provider/configure.phtml b/TwoFactorAuth/view/adminhtml/templates/tfa/provider/configure.phtml index 5a891c33..6984dd26 100644 --- a/TwoFactorAuth/view/adminhtml/templates/tfa/provider/configure.phtml +++ b/TwoFactorAuth/view/adminhtml/templates/tfa/provider/configure.phtml @@ -1,8 +1,9 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ + ?> <div id="tfa-configure-container" data-bind="scope:'tfa-configure'"> <!-- ko template: getTemplate() --><!-- /ko --> diff --git a/TwoFactorAuth/view/adminhtml/templates/tfa/request_config.phtml b/TwoFactorAuth/view/adminhtml/templates/tfa/request_config.phtml index e78affe8..c4c52d53 100644 --- a/TwoFactorAuth/view/adminhtml/templates/tfa/request_config.phtml +++ b/TwoFactorAuth/view/adminhtml/templates/tfa/request_config.phtml @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ /** @var \Magento\Backend\Block\Template $block */ diff --git a/TwoFactorAuth/view/adminhtml/ui_component/tfa_edit_user_form.xml b/TwoFactorAuth/view/adminhtml/ui_component/tfa_edit_user_form.xml index 72e9597f..4d625e74 100644 --- a/TwoFactorAuth/view/adminhtml/ui_component/tfa_edit_user_form.xml +++ b/TwoFactorAuth/view/adminhtml/ui_component/tfa_edit_user_form.xml @@ -1,8 +1,8 @@ <?xml version="1.0"?> <!-- /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ --> <form xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" diff --git a/TwoFactorAuth/view/adminhtml/web/css/auth.css b/TwoFactorAuth/view/adminhtml/web/css/auth.css index 77e18ee8..cdc3d78c 100644 --- a/TwoFactorAuth/view/adminhtml/web/css/auth.css +++ b/TwoFactorAuth/view/adminhtml/web/css/auth.css @@ -1,6 +1,6 @@ /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ .page-wrapper { diff --git a/TwoFactorAuth/view/adminhtml/web/css/authy.css b/TwoFactorAuth/view/adminhtml/web/css/authy.css index 22f3cf12..abcc43ba 100644 --- a/TwoFactorAuth/view/adminhtml/web/css/authy.css +++ b/TwoFactorAuth/view/adminhtml/web/css/authy.css @@ -1,6 +1,6 @@ /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ .tfa-authy-actions-list button span { diff --git a/TwoFactorAuth/view/adminhtml/web/css/duo.css b/TwoFactorAuth/view/adminhtml/web/css/duo.css index e59eac41..db8c4af6 100644 --- a/TwoFactorAuth/view/adminhtml/web/css/duo.css +++ b/TwoFactorAuth/view/adminhtml/web/css/duo.css @@ -1,6 +1,6 @@ /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ #duo_iframe { diff --git a/TwoFactorAuth/view/adminhtml/web/css/google.css b/TwoFactorAuth/view/adminhtml/web/css/google.css index 8dcfc278..9d15ae21 100644 --- a/TwoFactorAuth/view/adminhtml/web/css/google.css +++ b/TwoFactorAuth/view/adminhtml/web/css/google.css @@ -1,6 +1,6 @@ /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ .tfa-google-qr { diff --git a/TwoFactorAuth/view/adminhtml/web/css/tfa-screen.css b/TwoFactorAuth/view/adminhtml/web/css/tfa-screen.css index f2372469..d0a1f13a 100644 --- a/TwoFactorAuth/view/adminhtml/web/css/tfa-screen.css +++ b/TwoFactorAuth/view/adminhtml/web/css/tfa-screen.css @@ -1,6 +1,6 @@ /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ .login-content > li { diff --git a/TwoFactorAuth/view/adminhtml/web/css/tfa.css b/TwoFactorAuth/view/adminhtml/web/css/tfa.css index b2070e76..4235f652 100644 --- a/TwoFactorAuth/view/adminhtml/web/css/tfa.css +++ b/TwoFactorAuth/view/adminhtml/web/css/tfa.css @@ -1,6 +1,6 @@ /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ .tfa-forced-providers ul { diff --git a/TwoFactorAuth/view/adminhtml/web/css/u2f.css b/TwoFactorAuth/view/adminhtml/web/css/u2f.css index 97eee42d..f3cad401 100644 --- a/TwoFactorAuth/view/adminhtml/web/css/u2f.css +++ b/TwoFactorAuth/view/adminhtml/web/css/u2f.css @@ -1,6 +1,6 @@ /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ fieldset { diff --git a/TwoFactorAuth/view/adminhtml/web/js/authy/auth.js b/TwoFactorAuth/view/adminhtml/web/js/authy/auth.js index 5048adc0..c7ae8cb1 100644 --- a/TwoFactorAuth/view/adminhtml/web/js/authy/auth.js +++ b/TwoFactorAuth/view/adminhtml/web/js/authy/auth.js @@ -1,6 +1,6 @@ /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ define([ diff --git a/TwoFactorAuth/view/adminhtml/web/js/authy/configure.js b/TwoFactorAuth/view/adminhtml/web/js/authy/configure.js index 04fe4c9b..8dce65a5 100644 --- a/TwoFactorAuth/view/adminhtml/web/js/authy/configure.js +++ b/TwoFactorAuth/view/adminhtml/web/js/authy/configure.js @@ -1,6 +1,6 @@ /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ define([ diff --git a/TwoFactorAuth/view/adminhtml/web/js/authy/configure/register.js b/TwoFactorAuth/view/adminhtml/web/js/authy/configure/register.js index a40f6142..59bc2103 100644 --- a/TwoFactorAuth/view/adminhtml/web/js/authy/configure/register.js +++ b/TwoFactorAuth/view/adminhtml/web/js/authy/configure/register.js @@ -1,6 +1,6 @@ /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ define([ diff --git a/TwoFactorAuth/view/adminhtml/web/js/authy/configure/registry.js b/TwoFactorAuth/view/adminhtml/web/js/authy/configure/registry.js index dcf101a8..55bc3cb4 100644 --- a/TwoFactorAuth/view/adminhtml/web/js/authy/configure/registry.js +++ b/TwoFactorAuth/view/adminhtml/web/js/authy/configure/registry.js @@ -1,6 +1,6 @@ /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ define([ diff --git a/TwoFactorAuth/view/adminhtml/web/js/authy/configure/verify.js b/TwoFactorAuth/view/adminhtml/web/js/authy/configure/verify.js index 95be978d..6082d5b0 100644 --- a/TwoFactorAuth/view/adminhtml/web/js/authy/configure/verify.js +++ b/TwoFactorAuth/view/adminhtml/web/js/authy/configure/verify.js @@ -1,6 +1,6 @@ /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ define([ diff --git a/TwoFactorAuth/view/adminhtml/web/js/change_provider.js b/TwoFactorAuth/view/adminhtml/web/js/change_provider.js index 431ecc47..67e3f39e 100644 --- a/TwoFactorAuth/view/adminhtml/web/js/change_provider.js +++ b/TwoFactorAuth/view/adminhtml/web/js/change_provider.js @@ -1,6 +1,6 @@ /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ define([ diff --git a/TwoFactorAuth/view/adminhtml/web/js/duo/api.js b/TwoFactorAuth/view/adminhtml/web/js/duo/api.js deleted file mode 100644 index 27cf3d0f..00000000 --- a/TwoFactorAuth/view/adminhtml/web/js/duo/api.js +++ /dev/null @@ -1,439 +0,0 @@ -/** - * Duo Web SDK v2 - * Copyright 2017, Duo Security - */ - -/* eslint-disable */ -// jscs:disable - -(function (root, factory) { -/* eslint-disable */ -if (typeof define === 'function' && define.amd) { - // AMD. Register as an anonymous module. - define([], factory); -} else if (typeof module === 'object' && module.exports) { - // Node. Does not work with strict CommonJS, but - // only CommonJS-like environments that support module.exports, - // like Node. - module.exports = factory(); -} else { - // Browser globals (root is window) - var Duo = factory(); - // If the Javascript was loaded via a script tag, attempt to autoload - // the frame. - - Duo._onReady(Duo.init); - - // Attach Duo to the `window` object - root.Duo = Duo; -} -}(this, function () { - var DUO_MESSAGE_FORMAT = /^(?:AUTH|ENROLL)+\|[A-Za-z0-9\+\/=]+\|[A-Za-z0-9\+\/=]+$/; - var DUO_ERROR_FORMAT = /^ERR\|[\w\s\.\(\)]+$/; - var DUO_OPEN_WINDOW_FORMAT = /^DUO_OPEN_WINDOW\|/; - var VALID_OPEN_WINDOW_DOMAINS = [ - 'duo.com', - 'duosecurity.com', - 'duomobile.s3-us-west-1.amazonaws.com' - ]; - - var iframeId = 'duo_iframe', - postAction = '', - postArgument = 'sig_response', - host, - sigRequest, - duoSig, - appSig, - iframe, - submitCallback; - - function throwError(message, url) { - throw new Error( - 'Duo Web SDK error: ' + message + - (url ? '\n' + 'See ' + url + ' for more information' : '') - ); - } - - function hyphenize(str) { - return str.replace(/([a-z])([A-Z])/, '$1-$2').toLowerCase(); - } - - // cross-browser data attributes - function getDataAttribute(element, name) { - if ('dataset' in element) { - return element.dataset[name]; - } - - return element.getAttribute('data-' + hyphenize(name)); - - } - - // cross-browser event binding/unbinding - function on(context, event, fallbackEvent, callback) { - if ('addEventListener' in window) { - context.addEventListener(event, callback, false); - } else { - context.attachEvent(fallbackEvent, callback); - } - } - - function off(context, event, fallbackEvent, callback) { - if ('removeEventListener' in window) { - context.removeEventListener(event, callback, false); - } else { - context.detachEvent(fallbackEvent, callback); - } - } - - function onReady(callback) { - on(document, 'DOMContentLoaded', 'onreadystatechange', callback); - } - - function offReady(callback) { - off(document, 'DOMContentLoaded', 'onreadystatechange', callback); - } - - function onMessage(callback) { - on(window, 'message', 'onmessage', callback); - } - - function offMessage(callback) { - off(window, 'message', 'onmessage', callback); - } - - /** - * Parse the sig_request parameter, throwing errors if the token contains - * a server error or if the token is invalid. - * - * @param {String} sig Request token - */ - function parseSigRequest(sig) { - if (!sig) { - // nothing to do - return; - } - - // see if the token contains an error, throwing it if it does - if (sig.indexOf('ERR|') === 0) { - throwError(sig.split('|')[1]); - } - - // validate the token - if (sig.indexOf(':') === -1 || sig.split(':').length !== 2) { - throwError( - 'Duo was given a bad token. This might indicate a configuration ' + - 'problem with one of Duo\'s client libraries.', - 'https://www.duosecurity.com/docs/duoweb#first-steps' - ); - } - - var sigParts = sig.split(':'); - - // hang on to the token, and the parsed duo and app sigs - sigRequest = sig; - duoSig = sigParts[0]; - appSig = sigParts[1]; - - return { - sigRequest: sig, - duoSig: sigParts[0], - appSig: sigParts[1] - }; - } - - /** - * This function is set up to run when the DOM is ready, if the iframe was - * not available during `init`. - */ - function onDOMReady() { - iframe = document.getElementById(iframeId); - - if (!iframe) { - throw new Error( - 'This page does not contain an iframe for Duo to use.' + - 'Add an element like <iframe id="duo_iframe"></iframe> ' + - 'to this page. ' + - 'See https://www.duosecurity.com/docs/duoweb#3.-show-the-iframe ' + - 'for more information.' - ); - } - - // we've got an iframe, away we go! - ready(); - - // always clean up after yourself - offReady(onDOMReady); - } - - /** - * Validate that a MessageEvent came from the Duo service, and that it - * is a properly formatted payload. - * - * The Google Chrome sign-in page injects some JS into pages that also - * make use of postMessage, so we need to do additional validation above - * and beyond the origin. - * - * @param {MessageEvent} event Message received via postMessage - */ - function isDuoMessage(event) { - return Boolean( - event.origin === 'https://' + host && - typeof event.data === 'string' && - ( - event.data.match(DUO_MESSAGE_FORMAT) || - event.data.match(DUO_ERROR_FORMAT) || - event.data.match(DUO_OPEN_WINDOW_FORMAT) - ) - ); - } - - /** - * Validate the request token and prepare for the iframe to become ready. - * - * All options below can be passed into an options hash to `Duo.init`, or - * specified on the iframe using `data-` attributes. - * - * Options specified using the options hash will take precedence over - * `data-` attributes. - * - * Example using options hash: - * ```javascript - * Duo.init({ - * iframe: "some_other_id", - * host: "api-main.duo.test", - * sig_request: "...", - * post_action: "/auth", - * post_argument: "resp" - * }); - * ``` - * - * Example using `data-` attributes: - * ``` - * <iframe id="duo_iframe" - * data-host="api-main.duo.test" - * data-sig-request="..." - * data-post-action="https://github.com/auth" - * data-post-argument="resp" - * > - * </iframe> - * ``` - * - * @param {Object} options - * @param {String} options.iframe The iframe, or id of an iframe to set up - * @param {String} options.host Hostname - * @param {String} options.sig_request Request token - * @param {String} [options.post_action=''] URL to POST back to after successful auth - * @param {String} [options.post_argument='sig_response'] Parameter name to use for response token - * @param {Function} [options.submit_callback] If provided, duo will not submit the form instead execute - * the callback function with reference to the "duo_form" form object - * submit_callback can be used to prevent the webpage from reloading. - */ - function init(options) { - if (options) { - if (options.host) { - host = options.host; - } - - if (options.sig_request) { - parseSigRequest(options.sig_request); - } - - if (options.post_action) { - postAction = options.post_action; - } - - if (options.post_argument) { - postArgument = options.post_argument; - } - - if (options.iframe) { - if (options.iframe.tagName) { - iframe = options.iframe; - } else if (typeof options.iframe === 'string') { - iframeId = options.iframe; - } - } - - if (typeof options.submit_callback === 'function') { - submitCallback = options.submit_callback; - } - } - - // if we were given an iframe, no need to wait for the rest of the DOM - if (iframe) { - ready(); - } else { - // try to find the iframe in the DOM - iframe = document.getElementById(iframeId); - - // iframe is in the DOM, away we go! - if (iframe) { - ready(); - } else { - // wait until the DOM is ready, then try again - onReady(onDOMReady); - } - } - - // always clean up after yourself! - offReady(init); - } - - /** - * This function is called when a message was received from another domain - * using the `postMessage` API. Check that the event came from the Duo - * service domain, and that the message is a properly formatted payload, - * then perform the post back to the primary service. - * - * @param event Event object (contains origin and data) - */ - function onReceivedMessage(event) { - if (isDuoMessage(event)) { - if (event.data.match(DUO_OPEN_WINDOW_FORMAT)) { - var url = event.data.substring('DUO_OPEN_WINDOW|'.length); - - if (isValidUrlToOpen(url)) { - // Open the URL that comes after the DUO_WINDOW_OPEN token. - window.open(url, '_self'); - } - } else { - // the event came from duo, do the post back - doPostBack(event.data); - - // always clean up after yourself! - offMessage(onReceivedMessage); - } - } - } - - /** - * Validate that this passed in URL is one that we will actually allow to - * be opened. - * @param url String URL that the message poster wants to open - * @returns {boolean} true if we allow this url to be opened in the window - */ - function isValidUrlToOpen(url) { - if (!url) { - return false; - } - - var parser = document.createElement('a'); - - parser.href = url; - - if (parser.protocol === 'duotrustedendpoints:') { - return true; - } else if (parser.protocol !== 'https:') { - return false; - } - - for (var i = 0; i < VALID_OPEN_WINDOW_DOMAINS.length; i++) { - if (parser.hostname.endsWith('.' + VALID_OPEN_WINDOW_DOMAINS[i]) || - parser.hostname === VALID_OPEN_WINDOW_DOMAINS[i]) { - return true; - } - } - - return false; - } - - /** - * Point the iframe at Duo, then wait for it to postMessage back to us. - */ - function ready() { - if (!host) { - host = getDataAttribute(iframe, 'host'); - - if (!host) { - throwError( - 'No API hostname is given for Duo to use. Be sure to pass ' + - 'a `host` parameter to Duo.init, or through the `data-host` ' + - 'attribute on the iframe element.', - 'https://www.duosecurity.com/docs/duoweb#3.-show-the-iframe' - ); - } - } - - if (!duoSig || !appSig) { - parseSigRequest(getDataAttribute(iframe, 'sigRequest')); - - if (!duoSig || !appSig) { - throwError( - 'No valid signed request is given. Be sure to give the ' + - '`sig_request` parameter to Duo.init, or use the ' + - '`data-sig-request` attribute on the iframe element.', - 'https://www.duosecurity.com/docs/duoweb#3.-show-the-iframe' - ); - } - } - - // if postAction/Argument are defaults, see if they are specified - // as data attributes on the iframe - if (postAction === '') { - postAction = getDataAttribute(iframe, 'postAction') || postAction; - } - - if (postArgument === 'sig_response') { - postArgument = getDataAttribute(iframe, 'postArgument') || postArgument; - } - - // point the iframe at Duo - iframe.src = [ - 'https://', host, '/frame/web/v1/auth?tx=', duoSig, - '&parent=', encodeURIComponent(document.location.href), - '&v=2.6' - ].join(''); - - // listen for the 'message' event - onMessage(onReceivedMessage); - } - - /** - * We received a postMessage from Duo. POST back to the primary service - * with the response token, and any additional user-supplied parameters - * given in form#duo_form. - */ - function doPostBack(response) { - // create a hidden input to contain the response token - var input = document.createElement('input'); - - input.type = 'hidden'; - input.name = postArgument; - input.value = response + ':' + appSig; - - // user may supply their own form with additional inputs - var form = document.getElementById('duo_form'); - - // if the form doesn't exist, create one - if (!form) { - form = document.createElement('form'); - - // insert the new form after the iframe - iframe.parentElement.insertBefore(form, iframe.nextSibling); - } - - // make sure we are actually posting to the right place - form.method = 'POST'; - form.action = postAction; - - // add the response token input to the form - form.appendChild(input); - - // away we go! - if (typeof submitCallback === 'function') { - submitCallback.call(null, form); - } else { - form.submit(); - } - } - - return { - init: init, - _onReady: onReady, - _parseSigRequest: parseSigRequest, - _isDuoMessage: isDuoMessage, - _doPostBack: doPostBack - }; -})); - -/* eslint-enable */ diff --git a/TwoFactorAuth/view/adminhtml/web/js/duo/auth.js b/TwoFactorAuth/view/adminhtml/web/js/duo/auth.js index c94de8b9..1b68df1c 100644 --- a/TwoFactorAuth/view/adminhtml/web/js/duo/auth.js +++ b/TwoFactorAuth/view/adminhtml/web/js/duo/auth.js @@ -1,13 +1,12 @@ /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ define([ 'ko', - 'uiComponent', - 'Magento_TwoFactorAuth/js/duo/api' -], function (ko, Component, duo) { + 'uiComponent' +], function (ko, Component) { 'use strict'; return Component.extend({ @@ -17,42 +16,25 @@ define([ template: 'Magento_TwoFactorAuth/duo/auth' }, - signature: '', - apiHost: '', - postUrl: '', - authenticateData: {}, + authUrl: '', - /** - * Start waiting loop - */ - onAfterRender: function () { - window.setTimeout(function () { - duo.init(); - }, 100); + getAuthUrl: function () { + return this.authUrl; }, - /** - * Get POST URL - * @returns {String} - */ - getPostUrl: function () { - return this.postUrl; - }, + redirectToAuthUrl: function () { + var redirectUrl = this.getAuthUrl(); - /** - * Get signature - * @returns {String} - */ - getSignature: function () { - return this.signature; + if (redirectUrl) { + window.location.href = redirectUrl; + } }, /** - * Get API host - * @returns {String} + * After the element is rendered, bind the authUrl (optional) */ - getApiHost: function () { - return this.apiHost; + onAfterRender: function () { + // Not Required } }); }); diff --git a/TwoFactorAuth/view/adminhtml/web/js/error.js b/TwoFactorAuth/view/adminhtml/web/js/error.js index 3708cf6f..5ceef8b9 100644 --- a/TwoFactorAuth/view/adminhtml/web/js/error.js +++ b/TwoFactorAuth/view/adminhtml/web/js/error.js @@ -1,6 +1,6 @@ /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ define([ diff --git a/TwoFactorAuth/view/adminhtml/web/js/form/element/providers.js b/TwoFactorAuth/view/adminhtml/web/js/form/element/providers.js index 207c3fd1..0ab67fda 100644 --- a/TwoFactorAuth/view/adminhtml/web/js/form/element/providers.js +++ b/TwoFactorAuth/view/adminhtml/web/js/form/element/providers.js @@ -1,6 +1,6 @@ /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ define(['Magento_Ui/js/form/element/abstract'], function (Abstract) { diff --git a/TwoFactorAuth/view/adminhtml/web/js/form/element/reset_providers.js b/TwoFactorAuth/view/adminhtml/web/js/form/element/reset_providers.js index 0feacc76..0161e412 100644 --- a/TwoFactorAuth/view/adminhtml/web/js/form/element/reset_providers.js +++ b/TwoFactorAuth/view/adminhtml/web/js/form/element/reset_providers.js @@ -1,6 +1,6 @@ /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ define([ diff --git a/TwoFactorAuth/view/adminhtml/web/js/form/provider.js b/TwoFactorAuth/view/adminhtml/web/js/form/provider.js index ad74c487..e1b8f170 100644 --- a/TwoFactorAuth/view/adminhtml/web/js/form/provider.js +++ b/TwoFactorAuth/view/adminhtml/web/js/form/provider.js @@ -1,6 +1,6 @@ /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ define([ diff --git a/TwoFactorAuth/view/adminhtml/web/js/google/auth.js b/TwoFactorAuth/view/adminhtml/web/js/google/auth.js index 02f57d3d..5169412a 100644 --- a/TwoFactorAuth/view/adminhtml/web/js/google/auth.js +++ b/TwoFactorAuth/view/adminhtml/web/js/google/auth.js @@ -1,6 +1,6 @@ /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ define([ diff --git a/TwoFactorAuth/view/adminhtml/web/js/system/config/providers.js b/TwoFactorAuth/view/adminhtml/web/js/system/config/providers.js index de9a4246..aa10cd58 100644 --- a/TwoFactorAuth/view/adminhtml/web/js/system/config/providers.js +++ b/TwoFactorAuth/view/adminhtml/web/js/system/config/providers.js @@ -1,6 +1,6 @@ /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ define([ @@ -14,7 +14,51 @@ define([ return function (config, element) { var $element = $(element), - initialValue = $element.val(); + initialValue = $element.val(), + duoProviderValue = config.duoProviderValue, + duoFields = config.duoFields; + + /** + * Adds the "required" attribute to each Duo field + * + * @param {Array} fields - List of field IDs to mark as required + */ + function addRequiredAttributes(fields) { + fields.forEach(function (fieldId) { + var $field = $('#' + fieldId); + + if ($field.length) { + $field.attr('required', 'required'); + $field.addClass('required-entry'); + } + }); + } + + /** + * Removes the "required" attribute from each Duo field + * + * @param {Array} fields - List of field IDs to unmark as required + */ + function removeRequiredAttributes(fields) { + fields.forEach(function (fieldId) { + var $field = $('#' + fieldId); + + if ($field.length) { + $field.removeAttr('required'); + $field.removeClass('required-entry'); + } + }); + } + + $element.on('change', function () { + var selectedValues = $element.val() || []; + + if (selectedValues.includes(duoProviderValue)) { + addRequiredAttributes(duoFields); + } else { + removeRequiredAttributes(duoFields); + } + }); element.on('blur', function () { var currentValue = $element.val(); @@ -32,9 +76,6 @@ define([ text: $t('Cancel'), class: 'action-secondary action-dismiss', - /** - * Close modal and trigger 'cancel' action on click - */ click: function (event) { this.closeModal(event); } @@ -42,18 +83,11 @@ define([ text: $t('Confirm'), class: 'action-primary action-accept', - /** - * Close modal and trigger 'confirm' action on click - */ click: function (event) { this.closeModal(event, true); } }], actions: { - - /** - * Revert back to original Enabled setting - */ cancel: function () { $element.val(initialValue); } @@ -62,3 +96,4 @@ define([ }); }; }); + diff --git a/TwoFactorAuth/view/adminhtml/web/js/u2fkey/auth.js b/TwoFactorAuth/view/adminhtml/web/js/u2fkey/auth.js index 6707c2dd..02491489 100644 --- a/TwoFactorAuth/view/adminhtml/web/js/u2fkey/auth.js +++ b/TwoFactorAuth/view/adminhtml/web/js/u2fkey/auth.js @@ -1,6 +1,6 @@ /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ define([ @@ -84,6 +84,10 @@ define([ */ waitForTouch: function () { this.idle(false); + if (!navigator.credentials) { + this.currentStep('no-webauthn'); + return; + } navigator.credentials.get({ publicKey: this.authenticateData.credentialRequestOptions }) @@ -158,8 +162,8 @@ define([ _onCredentialError: function (u2fError) { this.idle(true); - if (['AbortError', 'NS_ERROR_ABORT', 'NotAllowedError'].indexOf(u2fError.name) === -1) { - error.display($t('Unable to register your device')); + if (['AbortError', 'NS_ERROR_ABORT'].indexOf(u2fError.name) === -1) { + error.display($t(u2fError.message)); } } }); diff --git a/TwoFactorAuth/view/adminhtml/web/js/u2fkey/configure.js b/TwoFactorAuth/view/adminhtml/web/js/u2fkey/configure.js index c5b7cc1e..9f732af4 100644 --- a/TwoFactorAuth/view/adminhtml/web/js/u2fkey/configure.js +++ b/TwoFactorAuth/view/adminhtml/web/js/u2fkey/configure.js @@ -1,6 +1,6 @@ /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ define([ @@ -78,6 +78,10 @@ define([ */ waitForTouch: function () { this.idle(false); + if (!navigator.credentials) { + this.currentStep('no-webauthn'); + return; + } navigator.credentials.create({ publicKey: this.registerData.publicKey }) @@ -150,8 +154,8 @@ define([ _onCredentialError: function (u2fError) { this.idle(true); - if (['AbortError', 'NS_ERROR_ABORT', 'NotAllowedError'].indexOf(u2fError.name) === -1) { - error.display($t('Unable to register your device')); + if (['AbortError', 'NS_ERROR_ABORT'].indexOf(u2fError.name) === -1) { + error.display($t(u2fError.message)); } } }); diff --git a/TwoFactorAuth/view/adminhtml/web/js/u2fkey/utils.js b/TwoFactorAuth/view/adminhtml/web/js/u2fkey/utils.js index 0052ca9d..993c4f40 100644 --- a/TwoFactorAuth/view/adminhtml/web/js/u2fkey/utils.js +++ b/TwoFactorAuth/view/adminhtml/web/js/u2fkey/utils.js @@ -1,6 +1,6 @@ /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ define([], function () { diff --git a/TwoFactorAuth/view/adminhtml/web/template/authy/auth.html b/TwoFactorAuth/view/adminhtml/web/template/authy/auth.html index 6c1ade31..239ba37f 100644 --- a/TwoFactorAuth/view/adminhtml/web/template/authy/auth.html +++ b/TwoFactorAuth/view/adminhtml/web/template/authy/auth.html @@ -1,7 +1,7 @@ <!-- /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ --> <form autocomplete="off"> diff --git a/TwoFactorAuth/view/adminhtml/web/template/authy/configure.html b/TwoFactorAuth/view/adminhtml/web/template/authy/configure.html index 2ae8f030..6c0905a9 100644 --- a/TwoFactorAuth/view/adminhtml/web/template/authy/configure.html +++ b/TwoFactorAuth/view/adminhtml/web/template/authy/configure.html @@ -1,7 +1,7 @@ <!-- /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ --> <div> diff --git a/TwoFactorAuth/view/adminhtml/web/template/authy/configure/register.html b/TwoFactorAuth/view/adminhtml/web/template/authy/configure/register.html index aaa9bd81..878f8082 100644 --- a/TwoFactorAuth/view/adminhtml/web/template/authy/configure/register.html +++ b/TwoFactorAuth/view/adminhtml/web/template/authy/configure/register.html @@ -1,7 +1,7 @@ <!-- /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ --> <div> diff --git a/TwoFactorAuth/view/adminhtml/web/template/authy/configure/verify.html b/TwoFactorAuth/view/adminhtml/web/template/authy/configure/verify.html index a4a7a71d..a410985b 100644 --- a/TwoFactorAuth/view/adminhtml/web/template/authy/configure/verify.html +++ b/TwoFactorAuth/view/adminhtml/web/template/authy/configure/verify.html @@ -1,7 +1,7 @@ <!-- /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ --> <form autocomplete="off"> diff --git a/TwoFactorAuth/view/adminhtml/web/template/change_provider.html b/TwoFactorAuth/view/adminhtml/web/template/change_provider.html index 27ef96af..46ce1f61 100644 --- a/TwoFactorAuth/view/adminhtml/web/template/change_provider.html +++ b/TwoFactorAuth/view/adminhtml/web/template/change_provider.html @@ -1,7 +1,7 @@ <!-- /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ --> <div visible="getProviders().length > 0"> diff --git a/TwoFactorAuth/view/adminhtml/web/template/duo/auth.html b/TwoFactorAuth/view/adminhtml/web/template/duo/auth.html index b9a529bf..b8203420 100644 --- a/TwoFactorAuth/view/adminhtml/web/template/duo/auth.html +++ b/TwoFactorAuth/view/adminhtml/web/template/duo/auth.html @@ -1,23 +1,18 @@ <!-- /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ - --> +--> <div> <form> <fieldset class="admin__fieldset"> <legend class="admin__legend"><span translate="'2FA - Duo Security'"></span></legend> - <br/> - - <iframe id="duo_iframe" - data-bind="afterRender: onAfterRender" - attr="{ - 'data-host': getApiHost(), - 'data-sig-request': getSignature(), - 'data-post-action': getPostUrl() - }" - ></iframe> + <br> + <button type="button" data-bind="click: redirectToAuthUrl, afterRender: onAfterRender"> + Go to Duo Universal Prompt + </button> </fieldset> </form> </div> + diff --git a/TwoFactorAuth/view/adminhtml/web/template/form/element/providers.html b/TwoFactorAuth/view/adminhtml/web/template/form/element/providers.html index 180e14c5..57de3214 100644 --- a/TwoFactorAuth/view/adminhtml/web/template/form/element/providers.html +++ b/TwoFactorAuth/view/adminhtml/web/template/form/element/providers.html @@ -1,7 +1,7 @@ <!-- /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ --> <!-- ko if: getForcedProviders().length --> diff --git a/TwoFactorAuth/view/adminhtml/web/template/form/element/reset_providers.html b/TwoFactorAuth/view/adminhtml/web/template/form/element/reset_providers.html index 94bbe574..b5976d42 100644 --- a/TwoFactorAuth/view/adminhtml/web/template/form/element/reset_providers.html +++ b/TwoFactorAuth/view/adminhtml/web/template/form/element/reset_providers.html @@ -1,7 +1,7 @@ <!-- /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ --> <!-- ko if: getResetProviders().length --> diff --git a/TwoFactorAuth/view/adminhtml/web/template/google/auth.html b/TwoFactorAuth/view/adminhtml/web/template/google/auth.html index 8fec6136..cd9cb6a5 100644 --- a/TwoFactorAuth/view/adminhtml/web/template/google/auth.html +++ b/TwoFactorAuth/view/adminhtml/web/template/google/auth.html @@ -1,7 +1,7 @@ <!-- /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ --> <div> diff --git a/TwoFactorAuth/view/adminhtml/web/template/google/configure.html b/TwoFactorAuth/view/adminhtml/web/template/google/configure.html index 2e7b2c16..f4f2df0e 100644 --- a/TwoFactorAuth/view/adminhtml/web/template/google/configure.html +++ b/TwoFactorAuth/view/adminhtml/web/template/google/configure.html @@ -1,7 +1,7 @@ <!-- /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ --> <div> diff --git a/TwoFactorAuth/view/adminhtml/web/template/u2fkey/auth.html b/TwoFactorAuth/view/adminhtml/web/template/u2fkey/auth.html index c43ea6a7..6c87d5d3 100644 --- a/TwoFactorAuth/view/adminhtml/web/template/u2fkey/auth.html +++ b/TwoFactorAuth/view/adminhtml/web/template/u2fkey/auth.html @@ -1,7 +1,7 @@ <!-- /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ --> <div afterRender="onAfterRender"> @@ -9,7 +9,7 @@ <fieldset class="admin__fieldset"> <legend class="admin__legend"> <span translate="'2FA - U2F key verification'"></span> - </legend><br/> + </legend><br> <div class="tfa-u2f-touch-key"> <h3 translate="'Plug in your U2F key and follow instructions'"></h3> <div visible="$data.idle" class="tfa-u2f-try-again"> @@ -27,6 +27,10 @@ <h3 translate="'Plug in your U2F key and follow instructions'"></h3> </div> <div translate="'Redirecting to Magento Admin Panel...'"></div> </div> + <div visible='currentStep() === "no-webauthn"' + translate="'Error! Your browser does not support WebAuthn or you are not using a secure connection'" + > + </div> <div visible='$data.loading' class="tfa-waitbox"> <div data-role="spinner"> <div class="spinner"> diff --git a/TwoFactorAuth/view/adminhtml/web/template/u2fkey/configure.html b/TwoFactorAuth/view/adminhtml/web/template/u2fkey/configure.html index d5edcdd5..0a0655cc 100644 --- a/TwoFactorAuth/view/adminhtml/web/template/u2fkey/configure.html +++ b/TwoFactorAuth/view/adminhtml/web/template/u2fkey/configure.html @@ -1,7 +1,7 @@ <!-- /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright 2020 Adobe + * All Rights Reserved. */ --> <div afterRender="onAfterRender"> @@ -9,7 +9,7 @@ <fieldset class="admin__fieldset"> <legend class="admin__legend"> <span translate="'2FA - U2F key device registration'"></span> - </legend><br/> + </legend><br> <div id="u2f-touch-key"> <h3 translate="'Plug in your U2F key and follow instructions'"></h3> <div visible="$data.idle" class="tfa-u2f-try-again"> @@ -27,6 +27,10 @@ <h3 translate="'Plug in your U2F key and follow instructions'"></h3> </div> <div translate="'Redirecting to Magento Admin Panel...'"></div> </div> + <div visible='currentStep() === "no-webauthn"' + translate="'Error! Your browser does not support WebAuthn or you are not using a secure connection'" + > + </div> <div visible='$data.loading' class="tfa-waitbox"> <div data-role="spinner"> <div class="spinner"> diff --git a/_metapackage/README.md b/_metapackage/README.md index 23b0bbfe..0b4fb3bd 100644 --- a/_metapackage/README.md +++ b/_metapackage/README.md @@ -1,2 +1 @@ # Magento_SecurityPackage - diff --git a/_metapackage/composer.json b/_metapackage/composer.json index bf5fe0b7..79cf1a37 100644 --- a/_metapackage/composer.json +++ b/_metapackage/composer.json @@ -11,6 +11,7 @@ "magento/module-re-captcha-migration": "*", "magento/module-re-captcha-newsletter": "*", "magento/module-re-captcha-paypal": "*", + "magento/module-re-captcha-resend-confirmation-email": "*", "magento/module-re-captcha-review": "*", "magento/module-re-captcha-send-friend": "*", "magento/module-re-captcha-store-pickup": "*", @@ -25,8 +26,10 @@ "magento/module-re-captcha-webapi-rest": "*", "magento/module-re-captcha-webapi-graph-ql": "*", "magento/module-re-captcha-webapi-ui": "*", + "magento/module-re-captcha-wishlist": "*", "magento/module-securitytxt": "*", "magento/module-two-factor-auth": "*", - "google/recaptcha": "^1.2" + "phpfui/recaptcha": "^2.0.0", + "magento/module-re-captcha-checkout-sales-rule": "*" } } diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/ReCaptchaCheckout/frontend/js/model/place-order-mixin.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/ReCaptchaCheckout/frontend/js/model/place-order-mixin.test.js new file mode 100644 index 00000000..b89706ef --- /dev/null +++ b/dev/tests/js/jasmine/tests/app/code/Magento/ReCaptchaCheckout/frontend/js/model/place-order-mixin.test.js @@ -0,0 +1,133 @@ +/** + * Copyright 2022 Adobe + * All Rights Reserved. + */ + +define(['squire' +], function (Squire) { + 'use strict'; + + var injector = new Squire(), + + defaultContext = require.s.contexts._, + mixin, + registry; + + beforeEach(function (done) { + window.checkoutConfig = { + defaultSuccessPageUrl: '' + }; + + injector.require([ + 'Magento_ReCaptchaCheckout/js/model/place-order-mixin', + 'Magento_ReCaptchaWebapiUi/js/webapiReCaptchaRegistry' + ], function (Mixin, Registry) { + mixin = Mixin; + registry = Registry; + done(); + }); + }); + + afterEach(function () { + try { + injector.clean(); + injector.remove(); + } catch (e) {} + }); + + describe('Magento_ReCaptchaCheckout/js/model/place-order-mixin', function () { + it('mixin is applied to Magento_Checkout/js/model/place-order', function () { + var placeOrderMixins = defaultContext.config.config.mixins['Magento_Checkout/js/model/place-order']; + + expect(placeOrderMixins['Magento_ReCaptchaCheckout/js/model/place-order-mixin']).toBe(true); + }); + }); + + describe('Magento_Checkout/js/action/redirect-on-success is called', function () { + var recaptchaId = 'recaptcha-checkout-place-order', + messageContainer = jasmine.createSpy('messageContainer'), + payload = {}, + serviceUrl = 'test', + + /** + * Order place action mock + * + * @returns {{fail: fail, done: (function(Function): *)}} + */ + action = function () { + return { + /** + * Success result for request + * + * @param {Function} handler + * @returns {*} + */ + done: function (handler) { + handler(); + return this; + }, + + /** + * Fail result for request + */ + fail: function () {} + }; + }; + + it('Only PlaceOrder button triggers place order action', function () { + /** + * Triggers declared listener + * + * @returns {*} + */ + registry.triggers[recaptchaId] = function () { + if (registry._listeners[recaptchaId] !== undefined) { + return registry._listeners[recaptchaId]('token'); + } + }; + + /** + * Registers a listener + * + * @param id + * @param func + */ + registry.addListener = function (id, func) { + registry._listeners[id] = func; + }; + + registry.removeListener = jasmine.createSpy(); + mixin()(action, serviceUrl, payload, messageContainer); + expect(registry.removeListener).toHaveBeenCalledWith(recaptchaId); + }); + + it('PlaceOrder Listener is called for invisible google recaptcha', function () { + /** + * Triggers declared listener + * + * @returns {*} + */ + registry.triggers[recaptchaId] = function () { + if (registry._listeners[recaptchaId] !== undefined) { + return registry._listeners[recaptchaId]('token'); + } + }; + + /** + * Registers a listener + * + * @param id + * @param func + */ + registry.addListener = function (id, func) { + registry._listeners[id] = func; + }; + + registry._isInvisibleType[recaptchaId] = true; + registry.removeListener = jasmine.createSpy(); + mixin()(action, serviceUrl, payload, messageContainer); + + expect(registry.removeListener).not.toHaveBeenCalled(); + }); + }); +}); diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/ReCaptchaFrontendUi/frontend/js/reCaptcha.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/ReCaptchaFrontendUi/frontend/js/reCaptcha.test.js new file mode 100644 index 00000000..0900150b --- /dev/null +++ b/dev/tests/js/jasmine/tests/app/code/Magento/ReCaptchaFrontendUi/frontend/js/reCaptcha.test.js @@ -0,0 +1,123 @@ +/** + * Copyright 2023 Adobe + * All Rights Reserved. + */ + +/* eslint-disable max-nested-callbacks */ +define([ + 'squire' +], function (Squire) { + 'use strict'; + + var injector = new Squire(), + mocks = { + 'Magento_ReCaptchaFrontendUi/js/registry': { + ds: [], + captchaList: [], + tokenFields: [] + }, + 'Magento_ReCaptchaFrontendUi/js/reCaptchaScriptLoader': { + addReCaptchaScriptTag: jasmine.createSpy('reCaptchaScriptLoader.addReCaptchaScriptTag') + }, + 'Magento_ReCaptchaFrontendUi/js/nonInlineReCaptchaRenderer': { + add: jasmine.createSpy('nonInlineReCaptchaRenderer.add') + } + }, + reCaptchaModel, + formElement, + submitButtonElement, + $; + + beforeEach(function (done) { + injector.mock(mocks); + injector.require(['jquery', 'Magento_ReCaptchaFrontendUi/js/reCaptcha'], function (jq, reCaptchaUiComponent) { + reCaptchaModel = new reCaptchaUiComponent(); + $ = jq; + done(); + }); + formElement = document.createElement('form'); + submitButtonElement = document.createElement('button'); + formElement.appendChild(submitButtonElement); + window.document.body.appendChild(formElement); + window.grecaptcha = { + render: jasmine.createSpy('window.grecaptcha.render'), + execute: jasmine.createSpy('window.grecaptcha.execute') + }; + }); + + afterEach(function () { + try { + injector.clean(); + injector.remove(); + } catch (e) { + } + formElement.remove(); + formElement = undefined; + submitButtonElement = undefined; + }); + + describe('Magento_ReCaptchaFrontendUi/js/reCaptcha', function () { + describe('Invisible ReCaptcha', function () { + beforeEach(function () { + reCaptchaModel.getIsInvisibleRecaptcha = jasmine.createSpy().and.returnValue(true); + }); + + describe('"initParentForm" method', function () { + it( + 'should disable submit button, prevent submit handlers from executing and execute recaptcha' + + ' on submit', + function () { + var request = { + send: jasmine.createSpy('request.send') + }; + + // check that submit button is enabled + expect(submitButtonElement.disabled).toBeFalse(); + $(formElement).on('submit', function (event) { + event.preventDefault(); + request.send(); + }); + reCaptchaModel.initParentForm($(formElement), 'test'); + $(formElement).submit(); + // check that submit button is disabled + expect(submitButtonElement.disabled).toBeTrue(); + // check that grecaptcha is executed + expect(window.grecaptcha.execute).toHaveBeenCalledOnceWith('test'); + // check that other submit handlers are not executed + expect(request.send).not.toHaveBeenCalled(); + }); + + it('should add a token input field to the form', function () { + submitButtonElement.disabled = true; + reCaptchaModel.initParentForm($(formElement), 'test'); + expect(submitButtonElement.disabled).toBeFalse(); + expect(reCaptchaModel.tokenField).not.toBeNull(); + expect($(reCaptchaModel.tokenField).parents('form').is($(formElement))).toBeTrue(); + }); + }); + describe('"reCaptchaCallback" method', function () { + it('should enable submit button, set token input value and submit the form', function () { + var request = { + send: jasmine.createSpy('request.send') + }; + + submitButtonElement.disabled = true; + reCaptchaModel.$parentForm = $(formElement); + reCaptchaModel.tokenField = $('<input type="text" name="token" style="display: none" />')[0]; + + $(formElement).on('submit', function (event) { + event.preventDefault(); + request.send(); + }); + reCaptchaModel.reCaptchaCallback('testtoken'); + // check that submit button is enabled + expect(submitButtonElement.disabled).toBeFalse(); + // check that token input value is set + expect(reCaptchaModel.tokenField.value).toEqual('testtoken'); + // check that form is submitted + expect(request.send).toHaveBeenCalled(); + }); + }); + }); + }); +});