Skip to content

Commit fa80860

Browse files
Merge branch 'magento-commerce:develop' into graphql-api-enhancements
2 parents 4e54bc0 + d9d1406 commit fa80860

File tree

9 files changed

+123
-10
lines changed

9 files changed

+123
-10
lines changed
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
<?php
2+
/**
3+
* Copyright 2024 Adobe
4+
* All Rights Reserved.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\TwoFactorAuth\Model\Config\Backend;
9+
10+
use Magento\Framework\App\Config\Value;
11+
use Magento\Framework\App\Config\Data\ProcessorInterface;
12+
use Magento\Framework\Exception\ValidatorException;
13+
use OTPHP\TOTPInterface;
14+
15+
class Leeway extends Value implements ProcessorInterface
16+
{
17+
/**
18+
* Fetch Totp default period value
19+
*
20+
* @return int
21+
*/
22+
private function getDefaultPeriod(): int
23+
{
24+
return TOTPInterface::DEFAULT_PERIOD;
25+
}
26+
27+
/**
28+
* Process the value before saving.
29+
*
30+
* @param mixed $value The configuration value.
31+
* @return mixed The processed value.
32+
* @throws ValidatorException If the value is invalid.
33+
*/
34+
public function processValue($value)
35+
{
36+
if (!is_numeric($value)) {
37+
throw new ValidatorException(__('The Leeway must be a numeric value.'));
38+
}
39+
$numericValue = (int) $value;
40+
return $numericValue;
41+
}
42+
43+
/**
44+
* Validates the value before saving.
45+
*
46+
* @throws ValidatorException If the value is invalid.
47+
*/
48+
public function beforeSave()
49+
{
50+
$value = $this->getValue();
51+
$period = $this->getDefaultPeriod();
52+
if (!is_numeric($value) || $value < 1 || $value >= $period) {
53+
throw new ValidatorException(
54+
__(
55+
'Invalid Leeway value. It must be between 1 and %1 as default period is %2',
56+
$period-1,
57+
$period
58+
)
59+
);
60+
}
61+
62+
return parent::beforeSave();
63+
}
64+
}

TwoFactorAuth/Model/Provider/Engine/DuoSecurity.php

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,13 +77,21 @@ class DuoSecurity implements EngineInterface
7777
*/
7878
private $scopeConfig;
7979

80+
/**
81+
* @var string
82+
*/
83+
private $duoSignaturePrefix;
84+
8085
/**
8186
* @param ScopeConfigInterface $scopeConfig
87+
* @param string $duoSignaturePrefix
8288
*/
8389
public function __construct(
84-
ScopeConfigInterface $scopeConfig
90+
ScopeConfigInterface $scopeConfig,
91+
string $duoSignaturePrefix = self::AUTH_PREFIX
8592
) {
8693
$this->scopeConfig = $scopeConfig;
94+
$this->duoSignaturePrefix = $duoSignaturePrefix;
8795
}
8896

8997
/**
@@ -208,7 +216,7 @@ public function getRequestSignature(UserInterface $user): string
208216
$duoSignature = $this->signValues(
209217
$this->getSecretKey(),
210218
$values,
211-
static::DUO_PREFIX,
219+
$this->duoSignaturePrefix,
212220
static::DUO_EXPIRE,
213221
$time
214222
);

TwoFactorAuth/Model/Provider/Engine/Google.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ class Google implements EngineInterface
3535
/**
3636
* Config path for the OTP window
3737
*/
38-
const XML_PATH_OTP_WINDOW = 'twofactorauth/google/otp_window';
38+
public const XML_PATH_LEEWAY = 'twofactorauth/google/leeway';
3939

4040
/**
4141
* Engine code
@@ -199,7 +199,7 @@ public function verify(UserInterface $user, DataObject $request): bool
199199
return $totp->verify(
200200
$token,
201201
null,
202-
$config['window'] ?? (int)$this->scopeConfig->getValue(self::XML_PATH_OTP_WINDOW) ?: null
202+
$config['window'] ?? (int)$this->scopeConfig->getValue(self::XML_PATH_LEEWAY) ?: null
203203
);
204204
}
205205

TwoFactorAuth/Test/Api/GoogleActivateTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ public function testAlreadyActivatedProvider()
129129
/**
130130
* @magentoConfigFixture twofactorauth/general/force_providers google
131131
* @magentoApiDataFixture Magento/User/_files/user_with_custom_role.php
132-
* @magentoConfigFixture twofactorauth/google/otp_window 20
132+
* @magentoConfigFixture twofactorauth/google/leeway 29
133133
*/
134134
public function testActivate()
135135
{

TwoFactorAuth/Test/Api/GoogleAuthenticateTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,7 @@ public function testNotConfiguredProvider(): void
223223
/**
224224
* @magentoConfigFixture twofactorauth/general/force_providers google
225225
* @magentoApiDataFixture Magento/User/_files/user_with_custom_role.php
226-
* @magentoConfigFixture twofactorauth/google/otp_window 20
226+
* @magentoConfigFixture twofactorauth/google/leeway 29
227227
*
228228
* @return void
229229
*/

TwoFactorAuth/Test/Unit/Model/Provider/Engine/DuoSecurityTest.php

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
namespace Magento\TwoFactorAuth\Test\Unit\Model\Provider\Engine;
1010

11+
use Magento\User\Api\Data\UserInterface;
1112
use Magento\Framework\App\Config\ScopeConfigInterface;
1213
use Magento\TwoFactorAuth\Model\Provider\Engine\DuoSecurity;
1314
use PHPUnit\Framework\MockObject\MockObject;
@@ -21,20 +22,32 @@ class DuoSecurityTest extends TestCase
2122
*/
2223
private $model;
2324

25+
/**
26+
* @var DuoSecurity
27+
*/
28+
private $modelWithForcedDuoAuth;
29+
2430
/**
2531
* @var ScopeConfigInterface|MockObject
2632
*/
2733
private $configMock;
2834

35+
/**
36+
* @var UserInterface|MockObject
37+
*/
38+
private $user;
39+
2940
/**
3041
* @inheritDoc
3142
*/
3243
protected function setUp(): void
3344
{
3445
$objectManager = new ObjectManager($this);
3546
$this->configMock = $this->getMockBuilder(ScopeConfigInterface::class)->disableOriginalConstructor()->getMock();
47+
$this->user = $this->getMockBuilder(UserInterface::class)->disableOriginalConstructor()->getMock();
3648

3749
$this->model = $objectManager->getObject(DuoSecurity::class, ['scopeConfig' => $this->configMock]);
50+
$this->modelWithForcedDuoAuth = new DuoSecurity($this->configMock, $this->model::DUO_PREFIX);
3851
}
3952

4053
/**
@@ -119,4 +132,26 @@ public function testIsEnabled(
119132

120133
$this->assertEquals($expected, $this->model->isEnabled());
121134
}
135+
136+
public function testGetRequestSignature() : void
137+
{
138+
$this->user->expects($this->any())
139+
->method('getUserName')
140+
->willReturn('admin');
141+
$this->configMock->expects($this->any())
142+
->method('getValue')
143+
->willReturn('SECRET');
144+
145+
$this->assertStringContainsString($this->model::AUTH_PREFIX, $this->model->getRequestSignature($this->user));
146+
$this->assertStringNotContainsString($this->model::DUO_PREFIX, $this->model->getRequestSignature($this->user));
147+
148+
$this->assertStringContainsString(
149+
$this->model::DUO_PREFIX,
150+
$this->modelWithForcedDuoAuth->getRequestSignature($this->user)
151+
);
152+
$this->assertStringNotContainsString(
153+
$this->model::AUTH_PREFIX,
154+
$this->modelWithForcedDuoAuth->getRequestSignature($this->user)
155+
);
156+
}
122157
}

TwoFactorAuth/etc/adminhtml/di.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,9 @@
2121
<type name="Magento\Backend\Model\Auth">
2222
<plugin name="delete_tfat_cookie" type="Magento\TwoFactorAuth\Plugin\DeleteCookieOnLogout"/>
2323
</type>
24+
<type name="Magento\TwoFactorAuth\Model\Provider\Engine\DuoSecurity">
25+
<arguments>
26+
<argument name="duoSignaturePrefix" xsi:type="const">Magento\TwoFactorAuth\Model\Provider\Engine\DuoSecurity::DUO_PREFIX</argument>
27+
</arguments>
28+
</type>
2429
</config>

TwoFactorAuth/etc/adminhtml/system.xml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,11 @@
3939
<group id="google" translate="label" type="text" sortOrder="30" showInDefault="1" showInWebsite="0"
4040
showInStore="0">
4141
<label>Google</label>
42-
<field id="otp_window" translate="label comment" type="text" sortOrder="10" showInDefault="1"
42+
<field id="leeway" translate="label comment" type="text" sortOrder="10" showInDefault="1"
4343
showInWebsite="0" showInStore="0" canRestore="1">
44-
<label>OTP Window</label>
45-
<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>
44+
<label>Leeway</label>
45+
<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>
46+
<backend_model>Magento\TwoFactorAuth\Model\Config\Backend\Leeway</backend_model>
4647
</field>
4748
</group>
4849
<group id="duo" translate="label" type="text" sortOrder="40" showInDefault="1" showInWebsite="0"

TwoFactorAuth/etc/config.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
<application_key backend_model="Magento\Config\Model\Config\Backend\Encrypted"/>
2222
</duo>
2323
<google>
24-
<otp_window>1</otp_window>
24+
<leeway backend_model="Magento\TwoFactorAuth\Model\Config\Backend\Leeway">29</leeway>
2525
</google>
2626
</twofactorauth>
2727
</default>

0 commit comments

Comments
 (0)