Skip to content

Commit b7c436b

Browse files
OXDEV-9927 Generate and send OTP to correct mail
1 parent 4381bca commit b7c436b

25 files changed

Lines changed: 141 additions & 88 deletions
Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,49 @@
11
<?php
22

3+
/**
4+
* Copyright © OXID eSales AG. All rights reserved.
5+
* See LICENSE file for license details.
6+
*/
7+
38
namespace OxidEsales\SecurityModule\Authentication\TwoFactorAuth\Controller;
49

510
use OxidEsales\Eshop\Application\Controller\FrontendController;
11+
use OxidEsales\Eshop\Core\Registry;
612
use OxidEsales\SecurityModule\Authentication\TwoFactorAuth\Service\AuthorizeServiceInterface;
7-
use OxidEsales\SecurityModule\Authentication\TwoFactorAuth\Transput\OTPRequestInterface;
13+
use OxidEsales\SecurityModule\Authentication\TwoFactorAuth\Transput\AuthCodeRequestInterface;
814

915
class TwoFactorAuthController extends FrontendController
1016
{
1117
protected $_sThisTemplate = '@oe_security_module/templates/two_factor_auth';
1218

1319
public function handleOTP(): void
1420
{
15-
$OTPRequest = $this->getService(OTPRequestInterface::class);
21+
$OTPRequest = $this->getService(AuthCodeRequestInterface::class);
1622

1723
//todo: catch only OTP exception that will be shown to user, maybe some abstract OTP exception?
1824
try {
1925
$authorizeService = $this->getService(AuthorizeServiceInterface::class);
2026
$authorizeService->validate(
21-
$OTPRequest->getOTPCode()
27+
$OTPRequest->getCode()
2228
);
29+
30+
//todo: redirect to originally requested page after successful OTP validation
31+
//todo: create correct session for logged in user (from service, handleLogin?)
2332
} catch (\Exception $e) {
24-
//todo: display error message to user
33+
//todo: display translated error message to user
34+
Registry::getUtilsView()->addErrorToDisplay($e->getMessage());
2535
}
2636
}
2737

2838
public function generate(): void
2939
{
40+
//todo: use session to get email/username
41+
$username = uniqid();
42+
3043
//todo: stop execution if not ajax
3144
//todo: prevent spam by rate limiting
3245
//todo: should return json response with success or error message
33-
3446
$authorizeService = $this->getService(AuthorizeServiceInterface::class);
35-
$authorizeService->generate();
47+
$authorizeService->generate($username);
3648
}
3749
}

src/Authentication/TwoFactorAuth/DTO/User.php

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,15 @@
99

1010
namespace OxidEsales\SecurityModule\Authentication\TwoFactorAuth\DTO;
1111

12-
use DateTimeImmutable;
12+
use DateTime;
1313

1414
class User implements UserInterface
1515
{
1616
public function __construct(
1717
private readonly string $userId,
1818
private readonly ?string $code,
1919
private readonly ?int $attempts,
20-
private readonly ?int $expiresAt,
20+
private readonly ?DateTime $expiresAt,
2121
) {
2222
}
2323

@@ -36,13 +36,9 @@ public function getAttempts(): ?int
3636
return $this->attempts;
3737
}
3838

39-
public function getExpiresAt(): ?DateTimeImmutable
39+
public function getExpiresAt(): ?DateTime
4040
{
4141
//todo: possible bug - should be null if not set
42-
$expireAt = $this->expiresAt ?? time();
43-
44-
$dateTime = new DateTimeImmutable();
45-
46-
return $dateTime->setTimestamp($expireAt);
42+
return $this->expiresAt;
4743
}
4844
}

src/Authentication/TwoFactorAuth/DTO/UserInterface.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
namespace OxidEsales\SecurityModule\Authentication\TwoFactorAuth\DTO;
99

10-
use DateTimeImmutable;
10+
use DateTime;
1111

1212
interface UserInterface
1313
{
@@ -17,5 +17,5 @@ public function getCode(): ?string;
1717

1818
public function getAttempts(): ?int;
1919

20-
public function getExpiresAt(): ?DateTimeImmutable;
20+
public function getExpiresAt(): ?DateTime;
2121
}

src/Authentication/TwoFactorAuth/Exception/AttemptLimitExceededException.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
<?php
22

3+
/**
4+
* Copyright © OXID eSales AG. All rights reserved.
5+
* See LICENSE file for license details.
6+
*/
7+
38
namespace OxidEsales\SecurityModule\Authentication\TwoFactorAuth\Exception;
49

510
class AttemptLimitExceededException extends \Exception

src/Authentication/TwoFactorAuth/Infrastructure/Provider/Email/EmailAdapter.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ public function getName(): string
2626

2727
public function notify(string $recipient, string $code): void
2828
{
29+
//todo: replayto is same as recipient
2930
$emailModel = $this->emailFactory->create();
3031
$emailModel->sendEmail(
3132
$recipient,

src/Authentication/TwoFactorAuth/Infrastructure/Provider/NotifierAdapterInterface.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
<?php
22

3+
/**
4+
* Copyright © OXID eSales AG. All rights reserved.
5+
* See LICENSE file for license details.
6+
*/
7+
38
namespace OxidEsales\SecurityModule\Authentication\TwoFactorAuth\Infrastructure\Provider;
49

510
interface NotifierAdapterInterface

src/Authentication/TwoFactorAuth/Infrastructure/Repository/UserRepository.php

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,28 +22,43 @@ public function __construct(
2222
) {
2323
}
2424

25-
public function getUserOTPData(string $userId): UserDTO
25+
public function getUserOTPData(string $userName): UserDTO
2626
{
2727
//todo: exception if not found
28-
//todo: use query builder
29-
$userModel = $this->userFactory->create();
30-
$userModel->load($userId);
28+
//todo: separate method for id and username?
29+
$builder = $this->queryBuilderFactory->create();
30+
$builder->select([
31+
'OXID',
32+
'OESMOTPCODE',
33+
'OESMOTPATTEMPTS',
34+
'OESMOTPEXPTIME'
35+
])
36+
->from('oxuser')
37+
->where('oxusername = :userName')
38+
->orWhere('oxid = :userName')
39+
->setParameter('userName', $userName);
40+
41+
$userData = $builder->execute()->fetchAssociative();
42+
if (!$userData) {
43+
//todo: throw correct exception
44+
throw new \RuntimeException('User not found');
45+
}
3146

3247
return new UserDTO(
33-
$userModel->getId(),
34-
$userModel->getFieldData('OESMOTPCODE'),
35-
(int) $userModel->getFieldData('OESMOTPATTEMPTS'),
36-
(int) $userModel->getFieldData('OESMOTPEXPTIME')
48+
$userData['OXID'],
49+
$userData['OESMOTPCODE'],
50+
$userData['OESMOTPATTEMPTS'],
51+
new DateTime($userData['OESMOTPEXPTIME'])
3752
);
3853
}
3954

40-
public function addOTPtoUser(string $userId, string $otp, int $expiresAt): bool
55+
public function addOTPtoUser(string $userId, string $otp, DateTime $expiresAt): bool
4156
{
4257
$userModel = $this->userFactory->create();
4358
$userModel->load($userId);
4459
$userModel->assign([
4560
'OESMOTPCODE' => $otp,
46-
'OESMOTPEXPTIME' => $expiresAt,
61+
'OESMOTPEXPTIME' => $expiresAt->format('Y-m-d H:i:s'),
4762
'OESMOTPATTEMPTS' => 0,
4863
]);
4964
$userModel->save();

src/Authentication/TwoFactorAuth/Infrastructure/Repository/UserRepositoryInterface.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,18 @@
77

88
namespace OxidEsales\SecurityModule\Authentication\TwoFactorAuth\Infrastructure\Repository;
99

10+
use DateTime;
1011
use OxidEsales\SecurityModule\Authentication\TwoFactorAuth\DTO\User as UserDTO;
1112

1213
interface UserRepositoryInterface
1314
{
14-
public function getUserOTPData(string $userId): UserDTO;
15+
public function getUserOTPData(string $userName): UserDTO;
1516

1617
public function updateAttempts(string $userId, int $attempts): void;
1718

1819
public function resetCodeFields(string $userId): void;
1920

20-
public function addOTPtoUser(string $userId, string $otp, int $expiresAt): bool;
21+
public function addOTPtoUser(string $userId, string $otp, DateTime $expiresAt): bool;
2122

2223
public function getUserPasswordHash(string $userId): string;
2324
}

src/Authentication/TwoFactorAuth/Service/AuthorizeService.php

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,15 @@
99

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

12+
use OxidEsales\EshopCommunity\Internal\Framework\Session\SessionInterface;
13+
1214
class AuthorizeService implements AuthorizeServiceInterface
1315
{
1416
public function __construct(
1517
private ModuleSettingsServiceInterface $moduleSettings,
1618
private VerificationCollectorServiceInterface $verificationCollectorService,
1719
private NotifierCollectorInterface $notifierCollectorService,
20+
private SessionInterface $session
1821
) {
1922
}
2023

@@ -26,22 +29,35 @@ public function validate(string $inputCode): void
2629
$activeVerificator
2730
);
2831

29-
//todo: use session to get user id
30-
$verificator->validateCode('7b4dfcca4669a8bbfcbd29c77cbc82f3', $inputCode);
32+
//todo: use session to get email/username or userId
33+
$userName = $this->session->get('pending_otp_user');
34+
35+
$verificator->validateCode($userName, $inputCode);
3136
}
3237

33-
public function generate(): void
38+
public function generate(string $userName): void
3439
{
3540
$activeVerificator = $this->moduleSettings->getTwoFactorAuthType();
3641

3742
$verificator = $this->verificationCollectorService->getVerificator(
3843
$activeVerificator
3944
);
40-
//todo: use session to get user id
41-
$OTPCode = $verificator->generate(uniqid());
45+
46+
$OTPCode = $verificator->generate($userName);
4247

4348
//todo: module setting?
4449
$notifier = $this->notifierCollectorService->getNotifier('email');
45-
$notifier->notify('localhost@localhost.local', $OTPCode);
50+
$notifier->notify($userName, $OTPCode);
51+
}
52+
53+
public function getVerificationUrl(): string
54+
{
55+
$activeVerificator = $this->moduleSettings->getTwoFactorAuthType();
56+
57+
$verificator = $this->verificationCollectorService->getVerificator(
58+
$activeVerificator
59+
);
60+
61+
return $verificator->getVerificationUrl();
4662
}
4763
}

src/Authentication/TwoFactorAuth/Service/AuthorizeServiceInterface.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,7 @@ interface AuthorizeServiceInterface
1111
{
1212
public function validate(string $inputCode): void;
1313

14-
public function generate(): void;
14+
public function generate(string $userName): void;
15+
16+
public function getVerificationUrl(): string;
1517
}

0 commit comments

Comments
 (0)