Skip to content

Commit 685414a

Browse files
author
Hayder Sharhan
committed
Merge remote-tracking branch 'api/bug-fixes' into bug-fixes
2 parents 2754427 + 30d1f56 commit 685414a

File tree

23 files changed

+874
-44
lines changed

23 files changed

+874
-44
lines changed
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<?php
2+
/**
3+
* Copyright © 2016 Magento. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
namespace Magento\Integration\Cron;
7+
8+
use Magento\Integration\Model\Oauth\Token\RequestLog\WriterInterface as RequestLogWriter;
9+
10+
/**
11+
* Cron class for clearing log of outdated token request authentication failures.
12+
*/
13+
class CleanExpiredAuthenticationFailures
14+
{
15+
/**
16+
* @var RequestLogWriter
17+
*/
18+
private $requestLogWriter;
19+
20+
/**
21+
* Initialize dependencies.
22+
*
23+
* @param RequestLogWriter $requestLogWriter
24+
*/
25+
public function __construct(
26+
RequestLogWriter $requestLogWriter
27+
) {
28+
$this->requestLogWriter = $requestLogWriter;
29+
}
30+
31+
/**
32+
* Clearing log of outdated token request authentication failures.
33+
*
34+
* @return void
35+
*/
36+
public function execute()
37+
{
38+
$this->requestLogWriter->clearExpiredFailures();
39+
}
40+
}

app/code/Magento/Integration/Model/AdminTokenService.php

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
use Magento\Integration\Model\Oauth\TokenFactory as TokenModelFactory;
1414
use Magento\Integration\Model\ResourceModel\Oauth\Token\CollectionFactory as TokenCollectionFactory;
1515
use Magento\User\Model\User as UserModel;
16+
use Magento\Integration\Model\Oauth\Token\RequestThrottler;
1617

1718
/**
1819
* Class to handle token generation for Admins
@@ -46,6 +47,11 @@ class AdminTokenService implements \Magento\Integration\Api\AdminTokenServiceInt
4647
*/
4748
private $tokenModelCollectionFactory;
4849

50+
/**
51+
* @var RequestThrottler
52+
*/
53+
private $requestThrottler;
54+
4955
/**
5056
* Initialize service
5157
*
@@ -72,8 +78,10 @@ public function __construct(
7278
public function createAdminAccessToken($username, $password)
7379
{
7480
$this->validatorHelper->validate($username, $password);
81+
$this->getRequestThrottler()->throttle($username, RequestThrottler::USER_TYPE_ADMIN);
7582
$this->userModel->login($username, $password);
7683
if (!$this->userModel->getId()) {
84+
$this->getRequestThrottler()->logAuthenticationFailure($username, RequestThrottler::USER_TYPE_ADMIN);
7785
/*
7886
* This message is same as one thrown in \Magento\Backend\Model\Auth to keep the behavior consistent.
7987
* Constant cannot be created in Auth Model since it uses legacy translation that doesn't support it.
@@ -83,6 +91,7 @@ public function createAdminAccessToken($username, $password)
8391
__('You did not sign in correctly or your account is temporarily disabled.')
8492
);
8593
}
94+
$this->getRequestThrottler()->resetAuthenticationFailuresCount($username, RequestThrottler::USER_TYPE_ADMIN);
8695
return $this->tokenModelFactory->create()->createAdminToken($this->userModel->getId())->getToken();
8796
}
8897

@@ -104,4 +113,18 @@ public function revokeAdminAccessToken($adminId)
104113
}
105114
return true;
106115
}
116+
117+
/**
118+
* Get request throttler instance
119+
*
120+
* @return RequestThrottler
121+
* @deprecated
122+
*/
123+
private function getRequestThrottler()
124+
{
125+
if (!$this->requestThrottler instanceof RequestThrottler) {
126+
return \Magento\Framework\App\ObjectManager::getInstance()->get(RequestThrottler::class);
127+
}
128+
return $this->requestThrottler;
129+
}
107130
}

app/code/Magento/Integration/Model/CustomerTokenService.php

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
use Magento\Integration\Model\Oauth\Token as Token;
1313
use Magento\Integration\Model\Oauth\TokenFactory as TokenModelFactory;
1414
use Magento\Integration\Model\ResourceModel\Oauth\Token\CollectionFactory as TokenCollectionFactory;
15+
use Magento\Integration\Model\Oauth\Token\RequestThrottler;
16+
use Magento\Framework\Exception\AuthenticationException;
1517

1618
class CustomerTokenService implements \Magento\Integration\Api\CustomerTokenServiceInterface
1719
{
@@ -41,6 +43,11 @@ class CustomerTokenService implements \Magento\Integration\Api\CustomerTokenServ
4143
*/
4244
private $tokenModelCollectionFactory;
4345

46+
/**
47+
* @var RequestThrottler
48+
*/
49+
private $requestThrottler;
50+
4451
/**
4552
* Initialize service
4653
*
@@ -67,7 +74,16 @@ public function __construct(
6774
public function createCustomerAccessToken($username, $password)
6875
{
6976
$this->validatorHelper->validate($username, $password);
70-
$customerDataObject = $this->accountManagement->authenticate($username, $password);
77+
$this->getRequestThrottler()->throttle($username, RequestThrottler::USER_TYPE_CUSTOMER);
78+
try {
79+
$customerDataObject = $this->accountManagement->authenticate($username, $password);
80+
} catch (\Exception $e) {
81+
$this->getRequestThrottler()->logAuthenticationFailure($username, RequestThrottler::USER_TYPE_CUSTOMER);
82+
throw new AuthenticationException(
83+
__('You did not sign in correctly or your account is temporarily disabled.')
84+
);
85+
}
86+
$this->getRequestThrottler()->resetAuthenticationFailuresCount($username, RequestThrottler::USER_TYPE_CUSTOMER);
7187
return $this->tokenModelFactory->create()->createCustomerToken($customerDataObject->getId())->getToken();
7288
}
7389

@@ -89,4 +105,18 @@ public function revokeCustomerAccessToken($customerId)
89105
}
90106
return true;
91107
}
108+
109+
/**
110+
* Get request throttler instance
111+
*
112+
* @return RequestThrottler
113+
* @deprecated
114+
*/
115+
private function getRequestThrottler()
116+
{
117+
if (!$this->requestThrottler instanceof RequestThrottler) {
118+
return \Magento\Framework\App\ObjectManager::getInstance()->get(RequestThrottler::class);
119+
}
120+
return $this->requestThrottler;
121+
}
92122
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<?php
2+
/**
3+
* Copyright © 2016 Magento. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
namespace Magento\Integration\Model\Oauth\Token\RequestLog;
7+
8+
use Magento\Framework\App\Config\ReinitableConfigInterface;
9+
10+
/**
11+
* Token request log config.
12+
*/
13+
class Config
14+
{
15+
/**
16+
* @var ReinitableConfigInterface
17+
*/
18+
private $storeConfig;
19+
20+
/**
21+
* Initialize dependencies.
22+
*
23+
* @param ReinitableConfigInterface $storeConfig
24+
*/
25+
public function __construct(ReinitableConfigInterface $storeConfig)
26+
{
27+
$this->storeConfig = $storeConfig;
28+
}
29+
30+
/**
31+
* Get maximum allowed authentication failures count before account is locked.
32+
*
33+
* @return int
34+
*/
35+
public function getMaxFailuresCount()
36+
{
37+
return (int)$this->storeConfig->getValue('oauth/authentication_lock/max_failures_count');
38+
}
39+
40+
/**
41+
* Get period of time in seconds after which account will be unlocked.
42+
*
43+
* @return int
44+
*/
45+
public function getLockTimeout()
46+
{
47+
return (int)$this->storeConfig->getValue('oauth/authentication_lock/timeout');
48+
}
49+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
/**
3+
*
4+
* Copyright © 2016 Magento. All rights reserved.
5+
* See COPYING.txt for license details.
6+
*/
7+
namespace Magento\Integration\Model\Oauth\Token\RequestLog;
8+
9+
/**
10+
* OAuth token request log reader interface.
11+
*/
12+
interface ReaderInterface
13+
{
14+
/**
15+
* Get number of authentication failures for the specified user account.
16+
*
17+
* @param string $userName
18+
* @param int $userType
19+
* @return int
20+
*/
21+
public function getFailuresCount($userName, $userType);
22+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<?php
2+
/**
3+
*
4+
* Copyright © 2016 Magento. All rights reserved.
5+
* See COPYING.txt for license details.
6+
*/
7+
namespace Magento\Integration\Model\Oauth\Token\RequestLog;
8+
9+
/**
10+
* OAuth token request log writer interface.
11+
*/
12+
interface WriterInterface
13+
{
14+
/**
15+
* Reset number of authentication failures for the specified user account.
16+
*
17+
* @param string $userName
18+
* @param int $userType
19+
* @param return void
20+
*/
21+
public function resetFailuresCount($userName, $userType);
22+
23+
/**
24+
* Increment number of authentication failures for the specified user account.
25+
*
26+
* @param string $userName
27+
* @param int $userType
28+
* @param return void
29+
*/
30+
public function incrementFailuresCount($userName, $userType);
31+
32+
/**
33+
* Clear expired authentication failure logs.
34+
*
35+
* @return void
36+
*/
37+
public function clearExpiredFailures();
38+
}
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
<?php
2+
/**
3+
* Copyright © 2016 Magento. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
namespace Magento\Integration\Model\Oauth\Token;
7+
8+
use Magento\Integration\Model\Oauth\Token\RequestLog\ReaderInterface as RequestLogReader;
9+
use Magento\Integration\Model\Oauth\Token\RequestLog\WriterInterface as RequestLogWriter;
10+
use Magento\Integration\Model\Oauth\Token\RequestLog\Config as RequestLogConfig;
11+
use Magento\Framework\Exception\AuthenticationException;
12+
13+
/**
14+
* Model for OAuth admin/customer token requests throttling.
15+
*/
16+
class RequestThrottler
17+
{
18+
/**#@+
19+
* Web API user type
20+
*/
21+
const USER_TYPE_CUSTOMER = 2;
22+
const USER_TYPE_ADMIN = 3;
23+
/**#@-*/
24+
25+
/**
26+
* @var RequestLogReader
27+
*/
28+
private $requestLogReader;
29+
30+
/**
31+
* @var RequestLogWriter
32+
*/
33+
private $requestLogWriter;
34+
35+
/**
36+
* @var RequestLogConfig
37+
*/
38+
private $requestLogConfig;
39+
40+
/**
41+
* Initialize dependencies.
42+
*
43+
* @param RequestLogReader $requestLogReader
44+
* @param RequestLogWriter $requestLogWriter
45+
* @param RequestLogConfig $requestLogConfig
46+
*/
47+
public function __construct(
48+
RequestLogReader $requestLogReader,
49+
RequestLogWriter $requestLogWriter,
50+
RequestLogConfig $requestLogConfig
51+
) {
52+
$this->requestLogReader = $requestLogReader;
53+
$this->requestLogWriter = $requestLogWriter;
54+
$this->requestLogConfig = $requestLogConfig;
55+
}
56+
57+
/**
58+
* Throw exception if user account is currently locked because of too many failed authentication attempts.
59+
*
60+
* @param string $userName
61+
* @param int $userType
62+
* @return void
63+
* @throws AuthenticationException
64+
*/
65+
public function throttle($userName, $userType)
66+
{
67+
$count = $this->requestLogReader->getFailuresCount($userName, $userType);
68+
if ($count >= $this->requestLogConfig->getMaxFailuresCount()) {
69+
throw new AuthenticationException(
70+
__('You did not sign in correctly or your account is temporarily disabled.')
71+
);
72+
}
73+
}
74+
75+
/**
76+
* Reset count of failed authentication attempts.
77+
*
78+
* Unlock user account and make generation of OAuth tokens possible for this account again.
79+
*
80+
* @param string $userName
81+
* @param int $userType
82+
* @return void
83+
*/
84+
public function resetAuthenticationFailuresCount($userName, $userType)
85+
{
86+
$this->requestLogWriter->resetFailuresCount($userName, $userType);
87+
}
88+
89+
/**
90+
* Increment authentication failures count and lock user account if the limit is reached.
91+
*
92+
* Account will be locked until lock expires.
93+
*
94+
* @param string $userName
95+
* @param int $userType
96+
* @return void
97+
*/
98+
public function logAuthenticationFailure($userName, $userType)
99+
{
100+
$this->requestLogWriter->incrementFailuresCount($userName, $userType);
101+
}
102+
}

0 commit comments

Comments
 (0)