Skip to content

Commit 8a3f8e6

Browse files
committed
Merge branch 'b-7.4.x-implement-otp-OXDEV-9927' into b-7.4.x-2fa-OXDEV-9078
2 parents d333f35 + d321f63 commit 8a3f8e6

75 files changed

Lines changed: 2164 additions & 17 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

metadata.php

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
* Metadata version
1010
*/
1111

12+
use OxidEsales\SecurityModule\Authentication\OAuth2\Service\ModuleSettingsService;
13+
use OxidEsales\SecurityModule\Authentication\TwoFactorAuth\Service\ModuleSettingsService as TwoFactorAuthModuleSettings;
1214
use OxidEsales\SecurityModule\PasswordPolicy\Service\ModuleSettingsService as PasswordPolicyModuleSettings;
1315
use OxidEsales\SecurityModule\Captcha\Service\ModuleSettingsService as CaptchaModuleSettings;
1416
use OxidEsales\SecurityModule\Authentication\OAuth2\Service\ModuleSettingsService as OAuthModuleSettings;
@@ -41,7 +43,8 @@
4143
'controllers' => [
4244
'captcha' => \OxidEsales\SecurityModule\Captcha\Controller\CaptchaController::class,
4345
'password' => \OxidEsales\SecurityModule\PasswordPolicy\Controller\PasswordAjaxController::class,
44-
'oauth' => \OxidEsales\SecurityModule\Authentication\OAuth2\Controller\OAuthController::class
46+
'oauth' => \OxidEsales\SecurityModule\Authentication\OAuth2\Controller\OAuthController::class,
47+
'twofactorauth' => \OxidEsales\SecurityModule\Authentication\TwoFactorAuth\Controller\TwoFactorAuthController::class,
4548
],
4649
'templates' => [
4750
],
@@ -161,6 +164,21 @@
161164
'name' => OAuthModuleSettings::GOOGLE_REDIRECT_URL,
162165
'type' => 'str',
163166
'value' => ''
164-
]
167+
],
168+
169+
//TwoFactorAuth settings
170+
[
171+
'group' => 'two_factor_auth',
172+
'name' => TwoFactorAuthModuleSettings::ACTIVE,
173+
'type' => 'bool',
174+
'value' => false
175+
],
176+
[
177+
'group' => 'two_factor_auth',
178+
'name' => TwoFactorAuthModuleSettings::TWO_FACTOR_TYPE,
179+
'type' => 'select',
180+
'constraints' => 'otp|totp',
181+
'value' => ''
182+
],
165183
],
166184
];
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* Copyright © OXID eSales AG. All rights reserved.
7+
* See LICENSE file for license details.
8+
*/
9+
10+
namespace OxidEsales\SecurityModule\Migrations;
11+
12+
use Doctrine\DBAL\Schema\Schema;
13+
use Doctrine\Migrations\AbstractMigration;
14+
15+
final class Version20251128093245 extends AbstractMigration
16+
{
17+
public function up(Schema $schema): void
18+
{
19+
$this->addSql('ALTER TABLE `oxuser` ADD column `OESMOTPCODE` VARCHAR(128) default NULL COMMENT "OTP code"');
20+
$this->addSql('ALTER TABLE `oxuser` ADD column `OESMOTPEXPTIME` DATETIME default NULL COMMENT "OTP code expiration time"');
21+
$this->addSql('ALTER TABLE `oxuser` ADD column `OESMOTPATTEMPTS` INT NOT NULL default 0 COMMENT "OTP code attempts"');
22+
}
23+
24+
public function down(Schema $schema): void
25+
{
26+
}
27+
}

migration/migrations.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
table_storage:
2+
table_name: oxmigrations_oe_security_module
3+
migrations_paths:
4+
'OxidEsales\SecurityModule\Migrations': data

src/Authentication/OAuth2/Infrastructure/Factory/OAuth2UserDTOFactory.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ public function createFromFacebookUser(FacebookUser $facebookUser): OAuth2UserDT
2525
);
2626
}
2727

28-
//TODO: use one factory for all providers
2928
public function createFromGoogleUser(GoogleUser $googleUser): OAuth2UserDTOInterface
3029
{
3130
return new OAuth2UserDTO(

src/Authentication/OAuth2/Service/ProviderCollector.php

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,8 @@
99

1010
namespace OxidEsales\SecurityModule\Authentication\OAuth2\Service;
1111

12-
use OxidEsales\SecurityModule\Authentication\OAuth2\Exception\ProviderNotActiveException;
1312
use OxidEsales\SecurityModule\Authentication\OAuth2\Exception\ProviderNotFoundException;
1413
use OxidEsales\SecurityModule\Authentication\OAuth2\Infrastructure\Provider\ProviderAdapterInterface;
15-
use OxidEsales\SecurityModule\Authentication\OAuth2\Service\Exception\ProviderNotActive;
1614
use Symfony\Component\Translation\Provider\ProviderInterface;
1715

1816
class ProviderCollector implements ProviderCollectorInterface
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
<?php
2+
3+
/**
4+
* Copyright © OXID eSales AG. All rights reserved.
5+
* See LICENSE file for license details.
6+
*/
7+
8+
declare(strict_types=1);
9+
10+
namespace OxidEsales\SecurityModule\Authentication\TwoFactorAuth\Controller;
11+
12+
use OxidEsales\Eshop\Application\Controller\FrontendController;
13+
use OxidEsales\Eshop\Core\Registry;
14+
use OxidEsales\SecurityModule\Authentication\TwoFactorAuth\Service\AuthorizeServiceInterface;
15+
use OxidEsales\SecurityModule\Authentication\TwoFactorAuth\Transput\AuthCodeRequestInterface;
16+
17+
class TwoFactorAuthController extends FrontendController
18+
{
19+
/**
20+
* Current view template
21+
*
22+
* @var string
23+
* @SuppressWarnings("PHPMD.CamelCasePropertyName")
24+
*/
25+
protected $_sThisTemplate = '@oe_security_module/templates/two_factor_auth';
26+
27+
public function handleOTP(): void
28+
{
29+
$OTPRequest = $this->getService(AuthCodeRequestInterface::class);
30+
31+
//todo: catch only OTP exception that will be shown to user, maybe some abstract OTP exception?
32+
try {
33+
$authorizeService = $this->getService(AuthorizeServiceInterface::class);
34+
$authorizeService->validate(
35+
$OTPRequest->getCode()
36+
);
37+
38+
//todo: redirect to originally requested page after successful OTP validation
39+
//todo: create correct session for logged in user (from service, handleLogin?)
40+
} catch (\Exception $e) {
41+
//todo: display translated error message to user
42+
Registry::getUtilsView()->addErrorToDisplay($e->getMessage());
43+
}
44+
}
45+
46+
public function generate(): void
47+
{
48+
//todo: stop execution if not ajax
49+
//todo: prevent spam by rate limiting
50+
//todo: should return json response with success or error message
51+
$authorizeService = $this->getService(AuthorizeServiceInterface::class);
52+
$authorizeService->generate();
53+
}
54+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?php
2+
3+
/**
4+
* Copyright © OXID eSales AG. All rights reserved.
5+
* See LICENSE file for license details.
6+
*/
7+
8+
declare(strict_types=1);
9+
10+
namespace OxidEsales\SecurityModule\Authentication\TwoFactorAuth\DTO;
11+
12+
use DateTimeInterface;
13+
14+
class User implements UserInterface
15+
{
16+
public function __construct(
17+
private readonly string $userId,
18+
private readonly int $attempts,
19+
private readonly ?string $code,
20+
private readonly ?DateTimeInterface $expiresAt,
21+
) {
22+
}
23+
24+
public function getId(): string
25+
{
26+
return $this->userId;
27+
}
28+
29+
public function getCode(): ?string
30+
{
31+
return $this->code;
32+
}
33+
34+
public function getAttempts(): int
35+
{
36+
return $this->attempts;
37+
}
38+
39+
public function getExpiresAt(): ?DateTimeInterface
40+
{
41+
return $this->expiresAt;
42+
}
43+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
3+
/**
4+
* Copyright © OXID eSales AG. All rights reserved.
5+
* See LICENSE file for license details.
6+
*/
7+
8+
namespace OxidEsales\SecurityModule\Authentication\TwoFactorAuth\DTO;
9+
10+
use DateTimeInterface;
11+
12+
interface UserInterface
13+
{
14+
public function getId(): string;
15+
16+
public function getCode(): ?string;
17+
18+
public function getAttempts(): int;
19+
20+
public function getExpiresAt(): ?DateTimeInterface;
21+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php
2+
3+
/**
4+
* Copyright © OXID eSales AG. All rights reserved.
5+
* See LICENSE file for license details.
6+
*/
7+
8+
declare(strict_types=1);
9+
10+
namespace OxidEsales\SecurityModule\Authentication\TwoFactorAuth\Exception;
11+
12+
class AttemptLimitExceededException extends \Exception
13+
{
14+
public function __construct()
15+
{
16+
parent::__construct('ERROR_ATTEMPT_LIMIT_EXCEEDED');
17+
}
18+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php
2+
3+
/**
4+
* Copyright © OXID eSales AG. All rights reserved.
5+
* See LICENSE file for license details.
6+
*/
7+
8+
declare(strict_types=1);
9+
10+
namespace OxidEsales\SecurityModule\Authentication\TwoFactorAuth\Exception;
11+
12+
class InvalidCodeException extends \Exception
13+
{
14+
public function __construct()
15+
{
16+
parent::__construct('ERROR_INVALID_CODE');
17+
}
18+
}

0 commit comments

Comments
 (0)