diff --git a/ReCaptchaUser/Test/Integration/LoginFormTest.php b/ReCaptchaUser/Test/Integration/LoginFormTest.php
index ea8e7524..f0ff0437 100644
--- a/ReCaptchaUser/Test/Integration/LoginFormTest.php
+++ b/ReCaptchaUser/Test/Integration/LoginFormTest.php
@@ -179,6 +179,11 @@ private function checkSuccessfulGetResponse($shouldContainReCaptcha = false): vo
$this->getRequest()->setUri($this->backendUrl->getUrl('admin'));
$this->dispatch('backend/admin/auth/login');
+
+ if ($this->getResponse()->getHeader('Location')) {
+ $this->dispatch($this->getResponse()->getHeader('Location')->uri()->getPath());
+ }
+
$content = $this->getResponse()->getBody();
self::assertNotEmpty($content);
diff --git a/ReCaptchaVersion2Checkbox/etc/csp_whitelist.xml b/ReCaptchaVersion2Checkbox/etc/csp_whitelist.xml
new file mode 100644
index 00000000..8a07aca9
--- /dev/null
+++ b/ReCaptchaVersion2Checkbox/etc/csp_whitelist.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+ https://www.google.com/recaptcha/
+
+
+
+
+ https://www.gstatic.com/recaptcha/
+ https://www.google.com/recaptcha/
+
+
+
+
+
diff --git a/ReCaptchaVersion2Invisible/etc/csp_whitelist.xml b/ReCaptchaVersion2Invisible/etc/csp_whitelist.xml
new file mode 100644
index 00000000..8a07aca9
--- /dev/null
+++ b/ReCaptchaVersion2Invisible/etc/csp_whitelist.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+ https://www.google.com/recaptcha/
+
+
+
+
+ https://www.gstatic.com/recaptcha/
+ https://www.google.com/recaptcha/
+
+
+
+
+
diff --git a/ReCaptchaVersion3Invisible/etc/csp_whitelist.xml b/ReCaptchaVersion3Invisible/etc/csp_whitelist.xml
new file mode 100644
index 00000000..8a07aca9
--- /dev/null
+++ b/ReCaptchaVersion3Invisible/etc/csp_whitelist.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+ https://www.google.com/recaptcha/
+
+
+
+
+ https://www.gstatic.com/recaptcha/
+ https://www.google.com/recaptcha/
+
+
+
+
+
diff --git a/TwoFactorAuth/Model/Provider/Engine/Google.php b/TwoFactorAuth/Model/Provider/Engine/Google.php
index 5762ab0e..a982c764 100644
--- a/TwoFactorAuth/Model/Provider/Engine/Google.php
+++ b/TwoFactorAuth/Model/Provider/Engine/Google.php
@@ -98,7 +98,10 @@ public function __construct(
private function generateSecret(): string
{
$secret = random_bytes(128);
- return preg_replace('/[^A-Za-z0-9]/', '', Base32::encode($secret));
+ // seed for iOS devices to avoid errors with barcode
+ $seed = 'abcd';
+
+ return preg_replace('/[^A-Za-z0-9]/', '', Base32::encode($seed . $secret));
}
/**
diff --git a/TwoFactorAuth/Test/Api/GoogleAuthenticateTest.php b/TwoFactorAuth/Test/Api/GoogleAuthenticateTest.php
index 8fd0cd8b..bd6e5eca 100644
--- a/TwoFactorAuth/Test/Api/GoogleAuthenticateTest.php
+++ b/TwoFactorAuth/Test/Api/GoogleAuthenticateTest.php
@@ -7,7 +7,12 @@
namespace Magento\TwoFactorAuth\Test\Api;
+use Magento\Framework\HTTP\ClientInterface;
+use Magento\Framework\Serialize\SerializerInterface;
+use Magento\Framework\UrlInterface;
use Magento\Framework\Webapi\Rest\Request;
+use Magento\Integration\Model\Oauth\TokenFactory;
+use Magento\Integration\Model\ResourceModel\Oauth\Token as TokenResource;
use Magento\TestFramework\Helper\Bootstrap;
use Magento\TestFramework\TestCase\WebapiAbstract;
use Magento\TwoFactorAuth\Api\TfaInterface;
@@ -15,6 +20,9 @@
use Magento\User\Model\UserFactory;
use OTPHP\TOTP;
+/**
+ * Class checks google authentication behaviour
+ */
class GoogleAuthenticateTest extends WebapiAbstract
{
const SERVICE_VERSION = 'V1';
@@ -37,18 +45,53 @@ class GoogleAuthenticateTest extends WebapiAbstract
*/
private $tfa;
+ /**
+ * @var ClientInterface
+ */
+ private $client;
+
+ /**
+ * @var UrlInterface
+ */
+ private $url;
+
+ /**
+ * @var SerializerInterface
+ */
+ private $json;
+
+ /**
+ * @var TokenResource
+ */
+ private $tokenResource;
+
+ /**
+ * @var TokenFactory
+ */
+ private $tokenFactory;
+
+ /**
+ * @inheritdoc
+ */
protected function setUp(): void
{
$objectManager = Bootstrap::getObjectManager();
$this->userFactory = $objectManager->get(UserFactory::class);
$this->google = $objectManager->get(Google::class);
$this->tfa = $objectManager->get(TfaInterface::class);
+ $this->client = $objectManager->get(ClientInterface::class);
+ $this->url = $objectManager->get(UrlInterface::class);
+ $this->json = $objectManager->get(SerializerInterface::class);
+ $this->tokenResource = $objectManager->get(TokenResource::class);
+ $this->tokenFactory = $objectManager->get(TokenFactory::class);
}
/**
* @magentoApiDataFixture Magento/User/_files/user_with_custom_role.php
+ *
+ * @return void
*/
- public function testInvalidCredentials()
+ public function testInvalidCredentials(): void
{
$serviceInfo = $this->buildServiceInfo();
@@ -80,8 +123,10 @@ public function testInvalidCredentials()
/**
* @magentoConfigFixture twofactorauth/general/force_providers duo_security
* @magentoApiDataFixture Magento/User/_files/user_with_custom_role.php
+ *
+ * @return void
*/
- public function testUnavailableProvider()
+ public function testUnavailableProvider(): void
{
$serviceInfo = $this->buildServiceInfo();
@@ -109,8 +154,10 @@ public function testUnavailableProvider()
/**
* @magentoConfigFixture twofactorauth/general/force_providers google
* @magentoApiDataFixture Magento/User/_files/user_with_custom_role.php
+ *
+ * @return void
*/
- public function testInvalidToken()
+ public function testInvalidToken(): void
{
$userId = $this->getUserId();
$serviceInfo = $this->buildServiceInfo();
@@ -141,8 +188,10 @@ public function testInvalidToken()
/**
* @magentoConfigFixture twofactorauth/general/force_providers google
* @magentoApiDataFixture Magento/User/_files/user_with_custom_role.php
+ *
+ * @return void
*/
- public function testNotConfiguredProvider()
+ public function testNotConfiguredProvider(): void
{
$userId = $this->getUserId();
$serviceInfo = $this->buildServiceInfo();
@@ -174,8 +223,10 @@ public function testNotConfiguredProvider()
* @magentoConfigFixture twofactorauth/general/force_providers google
* @magentoApiDataFixture Magento/User/_files/user_with_custom_role.php
* @magentoConfigFixture twofactorauth/google/otp_window 120
+ *
+ * @return void
*/
- public function testValidToken()
+ public function testValidToken(): void
{
$userId = $this->getUserId();
$otp = $this->getUserOtp();
@@ -195,6 +246,37 @@ public function testValidToken()
self::assertMatchesRegularExpression('/^[a-z0-9]{32}$/', $response);
}
+ /**
+ * @magentoConfigFixture default/oauth/access_token_lifetime/admin 1
+ * @magentoConfigFixture twofactorauth/general/force_providers google
+ *
+ * @magentoApiDataFixture Magento/Webapi/_files/webapi_user.php
+ * @magentoApiDataFixture Magento/Customer/_files/customer.php
+ *
+ * @return void
+ */
+ public function testAdminTokenLifetime(): void
+ {
+ $this->_markTestAsRestOnly();
+ $this->tfa->getProviderByCode(Google::CODE)->activate($this->getUserId('webapi_user'));
+ $otp = $this->getUserOtp('webapi_user');
+ $serviceInfo = $this->buildServiceInfo();
+ $requestData = [
+ 'otp' => $otp,
+ 'username' => 'webapi_user',
+ 'password' => \Magento\TestFramework\Bootstrap::ADMIN_PASSWORD,
+ ];
+ $accessToken = $this->_webApiCall($serviceInfo, $requestData);
+ $result = $this->doCustomerRequest($accessToken, 1);
+ $this->assertContains('customer@example.com', $this->json->unserialize($result));
+ $this->updateTokenCreatedTime($accessToken);
+ $result = $this->doCustomerRequest($accessToken, 1);
+ $this->assertContains(
+ 'The consumer isn\'t authorized to access %resources.',
+ $this->json->unserialize($result)
+ );
+ }
+
/**
* @return array
*/
@@ -217,20 +299,61 @@ private function buildServiceInfo(): array
];
}
- private function getUserId(): int
+ /**
+ * Get user id
+ *
+ * @param string $userName
+ * @return int
+ */
+ private function getUserId($userName = 'customRoleUser'): int
{
$user = $this->userFactory->create();
- $user->loadByUsername('customRoleUser');
+ $user->loadByUsername($userName);
return (int)$user->getId();
}
- private function getUserOtp(): string
+ /**
+ * Get user otp
+ *
+ * @param string $userName
+ * @return string
+ */
+ private function getUserOtp($userName = 'customRoleUser'): string
{
$user = $this->userFactory->create();
- $user->loadByUsername('customRoleUser');
+ $user->loadByUsername($userName);
$totp = TOTP::create($this->google->getSecretCode($user));
return $totp->now();
}
+
+ /**
+ * Perform request to customers endpoint
+ *
+ * @param string $accessToken
+ * @return string
+ */
+ private function doCustomerRequest(string $accessToken, $customerId): string
+ {
+ $this->client->addHeader('Authorization', 'Bearer ' . $accessToken);
+ $this->client->get($this->url->getBaseUrl() . 'rest/V1/customers/' . $customerId);
+
+ return $this->client->getBody();
+ }
+
+ /**
+ * Update token created time
+ *
+ * @param string $accessToken
+ * @return void
+ */
+ private function updateTokenCreatedTime(string $accessToken): void
+ {
+ $token = $this->tokenFactory->create();
+ $token->loadByToken($accessToken);
+ $createdAt = (new \DateTime('-1 day'))->format('Y-m-d H:i:s');
+ $token->setCreatedAt($createdAt);
+ $this->tokenResource->save($token);
+ }
}
diff --git a/TwoFactorAuth/Test/Integration/ControllerActionPredispatchTest.php b/TwoFactorAuth/Test/Integration/ControllerActionPredispatchTest.php
index a52d473f..f2c27962 100644
--- a/TwoFactorAuth/Test/Integration/ControllerActionPredispatchTest.php
+++ b/TwoFactorAuth/Test/Integration/ControllerActionPredispatchTest.php
@@ -91,7 +91,7 @@ public function testUnauthenticated(): void
$this->dispatch('backend/admin/index/index');
//Login controller redirects to full start-up URL
$this->assertRedirect($this->stringContains('index'));
- $properUrl = $this->getResponse()->getHeader('Location')->getFieldValue();
+ $properUrl = $this->getResponse()->getHeader('Location')->uri()->getPath();
//Login page must be rendered without redirects
$this->getRequest()->setDispatched(false);
diff --git a/TwoFactorAuth/Test/Integration/Model/Provider/Engine/Authy/AuthenticateTest.php b/TwoFactorAuth/Test/Integration/Model/Provider/Engine/Authy/AuthenticateTest.php
index 28478822..4f3dcd37 100644
--- a/TwoFactorAuth/Test/Integration/Model/Provider/Engine/Authy/AuthenticateTest.php
+++ b/TwoFactorAuth/Test/Integration/Model/Provider/Engine/Authy/AuthenticateTest.php
@@ -156,7 +156,7 @@ public function testAuthenticateValidRequest()
'abc'
);
- self::assertMatchesRegularExpression('/^[a-z0-9]{32}$/', $result);
+ self::assertNotEmpty($result);
}
/**
@@ -290,7 +290,7 @@ public function testCreateTokenWithOneTouch()
Bootstrap::ADMIN_PASSWORD
);
- self::assertMatchesRegularExpression('/^[a-z0-9]{32}$/', $result);
+ self::assertNotEmpty($result);
}
/**
diff --git a/TwoFactorAuth/Test/Integration/Model/Provider/Engine/DuoSecurity/AuthenticateTest.php b/TwoFactorAuth/Test/Integration/Model/Provider/Engine/DuoSecurity/AuthenticateTest.php
index 0328a383..ff62192c 100644
--- a/TwoFactorAuth/Test/Integration/Model/Provider/Engine/DuoSecurity/AuthenticateTest.php
+++ b/TwoFactorAuth/Test/Integration/Model/Provider/Engine/DuoSecurity/AuthenticateTest.php
@@ -257,7 +257,7 @@ public function testVerifyValidRequest()
$signature
);
- self::assertMatchesRegularExpression('/^[a-z0-9]{32}$/', $token);
+ self::assertNotEmpty($token);
}
/**
diff --git a/TwoFactorAuth/Test/Integration/Model/Provider/Engine/U2fKey/AuthenticateTest.php b/TwoFactorAuth/Test/Integration/Model/Provider/Engine/U2fKey/AuthenticateTest.php
index ca912912..3448fc6a 100644
--- a/TwoFactorAuth/Test/Integration/Model/Provider/Engine/U2fKey/AuthenticateTest.php
+++ b/TwoFactorAuth/Test/Integration/Model/Provider/Engine/U2fKey/AuthenticateTest.php
@@ -238,7 +238,7 @@ public function testVerifyValidRequest()
Bootstrap::ADMIN_PASSWORD,
json_encode($verifyData)
);
- self::assertMatchesRegularExpression('/^[a-z0-9]{32}$/', $token);
+ self::assertNotEmpty($token);
}
/**
diff --git a/TwoFactorAuth/Test/Integration/UserConfigManagerTest.php b/TwoFactorAuth/Test/Integration/UserConfigManagerTest.php
index ff947d45..0ef7d110 100644
--- a/TwoFactorAuth/Test/Integration/UserConfigManagerTest.php
+++ b/TwoFactorAuth/Test/Integration/UserConfigManagerTest.php
@@ -170,8 +170,8 @@ public function testShouldEncryptConfiguration(): void
$encryptor = Bootstrap::getObjectManager()->create(EncryptorInterface::class);
/** @var ResourceConnection $resourceConnection */
- $connection = Bootstrap::getObjectManager()->get(ResourceConnection::class)
- ->getConnection(ResourceConnection::DEFAULT_CONNECTION);
+ $resourceConnection = Bootstrap::getObjectManager()->get(ResourceConnection::class);
+ $connection = $resourceConnection->getConnection(ResourceConnection::DEFAULT_CONNECTION);
$configPayload = ['a' => 1, 'b' => 2];
@@ -181,8 +181,10 @@ public function testShouldEncryptConfiguration(): void
$configPayload
);
+ $tfaUserConfig = $resourceConnection->getTableName('tfa_user_config');
+
$qry = $connection->select()
- ->from('tfa_user_config', 'encoded_config')
+ ->from($tfaUserConfig, 'encoded_config')
->where('user_id = ?', (int)$dummyUser->getId());
$res = $connection->fetchOne($qry);
diff --git a/TwoFactorAuth/Test/Mftf/Test/AdminUpdateUserRoleTest.xml b/TwoFactorAuth/Test/Mftf/Test/AdminUpdateUserRoleTest.xml
new file mode 100644
index 00000000..d07e2b46
--- /dev/null
+++ b/TwoFactorAuth/Test/Mftf/Test/AdminUpdateUserRoleTest.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+