Skip to content

Commit 2c555d2

Browse files
author
Alex Paliarush
committed
MAGETWO-61867: API token does not expire after a time limit
1 parent 15b3f13 commit 2c555d2

File tree

5 files changed

+329
-5
lines changed

5 files changed

+329
-5
lines changed

app/code/Magento/Integration/Helper/Oauth/Data.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ public function getConsumerPostTimeout()
118118
*/
119119
public function getCustomerTokenLifetime()
120120
{
121-
$hours = (int)$this->_scopeConfig->getValue('oauth/access_token_expiration_period/customer');
121+
$hours = (int)$this->_scopeConfig->getValue('oauth/access_token_lifetime/customer');
122122
return $hours > 0 ? $hours : 0;
123123
}
124124

@@ -129,7 +129,7 @@ public function getCustomerTokenLifetime()
129129
*/
130130
public function getAdminTokenLifetime()
131131
{
132-
$hours = (int)$this->_scopeConfig->getValue('oauth/access_token_expiration_period/admin');
132+
$hours = (int)$this->_scopeConfig->getValue('oauth/access_token_lifetime/admin');
133133
return $hours > 0 ? $hours : 0;
134134
}
135135
}

app/code/Magento/Integration/Test/Unit/Helper/Oauth/DataTest.php

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,4 +81,44 @@ public function testGetConsumerPostTimeoutNonZero()
8181
$this->_scopeConfigMock->expects($this->once())->method('getValue')->will($this->returnValue(10));
8282
$this->assertEquals(10, $this->_dataHelper->getConsumerPostTimeout());
8383
}
84+
85+
public function testGetCustomerTokenLifetimeNotEmpty()
86+
{
87+
$this->_scopeConfigMock
88+
->expects($this->once())
89+
->method('getValue')
90+
->with('oauth/access_token_lifetime/customer')
91+
->will($this->returnValue(10));
92+
$this->assertEquals(10, $this->_dataHelper->getCustomerTokenLifetime());
93+
}
94+
95+
public function testGetCustomerTokenLifetimeEmpty()
96+
{
97+
$this->_scopeConfigMock
98+
->expects($this->once())
99+
->method('getValue')
100+
->with('oauth/access_token_lifetime/customer')
101+
->will($this->returnValue(null));
102+
$this->assertEquals(0, $this->_dataHelper->getCustomerTokenLifetime());
103+
}
104+
105+
public function testGetAdminTokenLifetimeNotEmpty()
106+
{
107+
$this->_scopeConfigMock
108+
->expects($this->once())
109+
->method('getValue')
110+
->with('oauth/access_token_lifetime/admin')
111+
->will($this->returnValue(10));
112+
$this->assertEquals(10, $this->_dataHelper->getAdminTokenLifetime());
113+
}
114+
115+
public function testGetAdminTokenLifetimeEmpty()
116+
{
117+
$this->_scopeConfigMock
118+
->expects($this->once())
119+
->method('getValue')
120+
->with('oauth/access_token_lifetime/admin')
121+
->will($this->returnValue(null));
122+
$this->assertEquals(0, $this->_dataHelper->getAdminTokenLifetime());
123+
}
84124
}

app/code/Magento/Integration/etc/adminhtml/system.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
<label>OAuth</label>
1212
<tab>service</tab>
1313
<resource>Magento_Integration::config_oauth</resource>
14-
<group id="access_token_expiration_period" translate="label" type="text" sortOrder="100" showInDefault="1" showInWebsite="0" showInStore="0">
14+
<group id="access_token_lifetime" translate="label" type="text" sortOrder="100" showInDefault="1" showInWebsite="0" showInStore="0">
1515
<label>Access Token Expiration</label>
1616
<field id="customer" translate="label" type="text" sortOrder="30" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1">
1717
<label>Customer Token Lifetime (hours)</label>

app/code/Magento/Integration/etc/config.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,10 @@
2121
<max_failures_count>6</max_failures_count>
2222
<timeout>1800</timeout>
2323
</authentication_lock>
24-
<access_token_expiration_period>
24+
<access_token_lifetime>
2525
<customer>1</customer>
2626
<admin>4</admin>
27-
</access_token_expiration_period>
27+
</access_token_lifetime>
2828
</oauth>
2929
</default>
3030
</config>
Lines changed: 284 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,284 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
7+
namespace Magento\Integration\Model\ResourceModel\Oauth;
8+
9+
use Magento\Authorization\Model\UserContextInterface;
10+
use Magento\Integration\Model\Oauth\Token;
11+
12+
/**
13+
* Integration test for @see \Magento\Integration\Model\ResourceModel\Oauth\Token
14+
*
15+
* Also tests @see \Magento\Integration\Cron\CleanExpiredTokens
16+
*/
17+
class TokenTest extends \PHPUnit_Framework_TestCase
18+
{
19+
const TOKEN_LIFETIME = 1; // in hours
20+
21+
const BASE_CREATED_AT_TIMESTAMP = 100000;
22+
23+
/**
24+
* @var array
25+
*/
26+
private $generatedTokens;
27+
28+
/**
29+
* @var \Magento\Framework\Stdlib\DateTime\DateTime | \PHPUnit_Framework_MockObject_MockObject
30+
*/
31+
private $dateTimeMock;
32+
33+
/**
34+
* @var \Magento\Integration\Model\ResourceModel\Oauth\Token
35+
*/
36+
private $tokenResourceModel;
37+
38+
/**
39+
* @var \Magento\Framework\ObjectManagerInterface
40+
*/
41+
private $objectManager;
42+
43+
/**
44+
* @var \Magento\Integration\Model\Oauth\TokenFactory
45+
*/
46+
private $tokenFactory;
47+
48+
protected function setUp()
49+
{
50+
$this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
51+
$this->tokenFactory = $this->objectManager->create(\Magento\Integration\Model\Oauth\TokenFactory::class);
52+
53+
/** Mock date model to be able to specify "current timestamp" and avoid dependency on real timestamp */
54+
$this->dateTimeMock = $this->getMockBuilder(\Magento\Framework\Stdlib\DateTime\DateTime::class)
55+
->disableOriginalConstructor()
56+
->getMock();
57+
/** @var \Magento\Integration\Model\ResourceModel\Oauth\Token $tokenResourceModel */
58+
$this->tokenResourceModel = $this->objectManager->create(
59+
\Magento\Integration\Model\ResourceModel\Oauth\Token::class,
60+
['date' => $this->dateTimeMock]
61+
);
62+
63+
$this->generatedTokens = $this->generateTokens();
64+
65+
parent::setUp();
66+
}
67+
68+
/**
69+
* @return array
70+
*/
71+
private function generateTokens()
72+
{
73+
/** Generate several tokens with different user types and created at combinations */
74+
$tokensToBeGenerated = [
75+
'#1' => [
76+
'userType' => UserContextInterface::USER_TYPE_ADMIN,
77+
'createdAt' => self::BASE_CREATED_AT_TIMESTAMP
78+
],
79+
'#2' => [
80+
'userType' => UserContextInterface::USER_TYPE_ADMIN,
81+
'createdAt' => self::BASE_CREATED_AT_TIMESTAMP + 5
82+
],
83+
'#3' => [
84+
'userType' => UserContextInterface::USER_TYPE_CUSTOMER,
85+
'createdAt' => self::BASE_CREATED_AT_TIMESTAMP
86+
],
87+
'#4' => [
88+
'userType' => UserContextInterface::USER_TYPE_CUSTOMER,
89+
'createdAt' => self::BASE_CREATED_AT_TIMESTAMP - 5
90+
],
91+
'#5' => [
92+
'userType' => UserContextInterface::USER_TYPE_INTEGRATION,
93+
'createdAt' => self::BASE_CREATED_AT_TIMESTAMP
94+
],
95+
'#6' => [
96+
'userType' => UserContextInterface::USER_TYPE_INTEGRATION,
97+
'createdAt' => self::BASE_CREATED_AT_TIMESTAMP + 5
98+
],
99+
];
100+
/** @var \Magento\Framework\Stdlib\DateTime $dateTimeUtils */
101+
$dateTimeUtils = $this->objectManager->get(\Magento\Framework\Stdlib\DateTime::class);
102+
foreach ($tokensToBeGenerated as &$tokenData) {
103+
$token = $this->tokenFactory->create();
104+
$token->setType(Token::TYPE_ACCESS)
105+
->setUserType($tokenData['userType'])
106+
->setToken(rand(1, PHP_INT_MAX))
107+
->setCreatedAt($dateTimeUtils->formatDate($tokenData['createdAt']));
108+
$this->tokenResourceModel->save($token);
109+
$tokenData['tokenId'] = $token->getId();
110+
}
111+
return $tokensToBeGenerated;
112+
}
113+
114+
/**
115+
* Make sure that @see \Magento\Integration\Cron\CleanExpiredTokens cleans tokens correctly per configuration
116+
*
117+
* 1. Generate several tokens with different user type and creation time
118+
* 2. Emulate current time stamp to be equal to (expiration period + some adjustment)
119+
* 3. Run clean up
120+
* 4. Make sure that clean up removed tokens that were expected to be removed,
121+
* and those tokens which were not expected to be removed are still there
122+
*
123+
* @param int $secondsAfterBaseCreatedTimestamp
124+
* @param array $expectedRemovedTokenNumbers
125+
* @param array $expectedPreservedTokenNumbers
126+
*
127+
* @dataProvider deleteExpiredTokenUsingObserverDataProvider
128+
* @covers \Magento\Integration\Cron\CleanExpiredTokens::execute
129+
*/
130+
public function testDeleteExpiredTokenUsingObserver(
131+
$secondsAfterBaseCreatedTimestamp,
132+
$expectedRemovedTokenNumbers,
133+
$expectedPreservedTokenNumbers
134+
) {
135+
/** @var \Magento\Integration\Cron\CleanExpiredTokens $cleanExpiredTokensModel */
136+
$cleanExpiredTokensModel = $this->objectManager->create(
137+
\Magento\Integration\Cron\CleanExpiredTokens::class,
138+
['tokenResourceModel' => $this->tokenResourceModel]
139+
);
140+
141+
$emulatedCurrentTimestamp = self::BASE_CREATED_AT_TIMESTAMP + $secondsAfterBaseCreatedTimestamp;
142+
$this->dateTimeMock->method('gmtTimestamp')->willReturn($emulatedCurrentTimestamp);
143+
$cleanExpiredTokensModel->execute();
144+
$this->assertTokenCleanUp(
145+
$expectedRemovedTokenNumbers,
146+
$expectedPreservedTokenNumbers,
147+
$this->generatedTokens
148+
);
149+
}
150+
151+
public function deleteExpiredTokenUsingObserverDataProvider()
152+
{
153+
return [
154+
"Clean up long before default admin and default customer token life time" => [
155+
3600 - 6, // time passed after base creation time
156+
[], // expected to be removed
157+
['#1', '#2', '#3', '#4', '#5', '#6'], // expected to exist
158+
],
159+
"Clean up just before default admin and default customer token life time" => [
160+
3600 - 1, // time passed after base creation time
161+
['#4'], // expected to be removed
162+
['#1', '#2', '#3', '#5', '#6'], // expected to exist
163+
],
164+
"Clean up after default admin token life time, but before default customer token life time" => [
165+
3600 + 1, // time passed after base creation time
166+
['#3', '#4'], // expected to be removed
167+
['#1', '#2', '#5', '#6'], // expected to exist
168+
],
169+
"Clean up after default customer and default admin token life time" => [
170+
14400 + 1, // time passed after base creation time
171+
['#1', '#3', '#4'], // expected to be removed
172+
['#2', '#5', '#6'], // expected to exist
173+
],
174+
];
175+
}
176+
177+
/**
178+
* Verify that expired tokens removal works as expected, @see \Magento\Integration\Model\ResourceModel\Oauth\Token
179+
*
180+
* 1. Generate several tokens with different user type and creation time
181+
* 2. Emulate current time stamp to be equal to (expiration period + some adjustment)
182+
* 3. Run clean up for some token types
183+
* 4. Make sure that clean up removed tokens that were expected to be removed,
184+
* and those tokens which were not expected to be removed are still there
185+
*
186+
* @param $secondsAfterBaseCreatedTimestamp
187+
* @param $tokenTypesToClean
188+
* @param $expectedRemovedTokenNumbers
189+
* @param $expectedPreservedTokenNumbers
190+
*
191+
* @magentoDbIsolation enabled
192+
* @dataProvider deleteExpiredTokensDataProvider
193+
* @covers \Magento\Integration\Model\ResourceModel\Oauth\Token::deleteExpiredTokens
194+
*/
195+
public function testDeleteExpiredTokens(
196+
$secondsAfterBaseCreatedTimestamp,
197+
$tokenTypesToClean,
198+
$expectedRemovedTokenNumbers,
199+
$expectedPreservedTokenNumbers
200+
) {
201+
/** Run clean up for tokens of {$tokenTypesToClean} type, created {$secondsAfterBaseCreatedTimestamp} ago */
202+
$emulatedCurrentTimestamp = self::BASE_CREATED_AT_TIMESTAMP + $secondsAfterBaseCreatedTimestamp;
203+
$this->dateTimeMock->method('gmtTimestamp')->willReturn($emulatedCurrentTimestamp);
204+
$this->tokenResourceModel->deleteExpiredTokens(self::TOKEN_LIFETIME, $tokenTypesToClean);
205+
$this->assertTokenCleanUp(
206+
$expectedRemovedTokenNumbers,
207+
$expectedPreservedTokenNumbers,
208+
$this->generatedTokens
209+
);
210+
}
211+
212+
public function deleteExpiredTokensDataProvider()
213+
{
214+
return [
215+
"Clean up for admin tokens which were created ('token_lifetime' + 1 second) ago" => [
216+
self::TOKEN_LIFETIME * 60 * 60 + 1, // time passed after base creation time
217+
[UserContextInterface::USER_TYPE_ADMIN], // token types to clean up
218+
['#1'], // expected to be removed
219+
['#2', '#3', '#4', '#5', '#6'], // expected to exist
220+
],
221+
"Clean up for admin, integration, guest tokens which were created ('token_lifetime' + 6 second) ago" => [
222+
self::TOKEN_LIFETIME * 60 * 60 + 6, // time passed after base creation time
223+
[ // token types to clean up
224+
UserContextInterface::USER_TYPE_ADMIN,
225+
UserContextInterface::USER_TYPE_INTEGRATION,
226+
UserContextInterface::USER_TYPE_GUEST
227+
],
228+
['#1', '#2', '#5', '#6'], // expected to be removed
229+
['#3', '#4'], // expected to exist
230+
],
231+
"Clean up for admin, integration, customer tokens which were created ('token_lifetime' + 6 second) ago" => [
232+
self::TOKEN_LIFETIME * 60 * 60 + 6, // time passed after base creation time
233+
[ // token types to clean up
234+
UserContextInterface::USER_TYPE_ADMIN,
235+
UserContextInterface::USER_TYPE_INTEGRATION,
236+
UserContextInterface::USER_TYPE_CUSTOMER
237+
],
238+
['#1', '#2', '#3', '#4', '#5', '#6'], // expected to be removed
239+
[], // expected to exist
240+
],
241+
"Clean up for admin, integration, customer tokens which were created ('token_lifetime' + 1 second) ago" => [
242+
self::TOKEN_LIFETIME * 60 * 60 + 1, // time passed after base creation time
243+
[ // token types to clean up
244+
UserContextInterface::USER_TYPE_ADMIN,
245+
UserContextInterface::USER_TYPE_INTEGRATION,
246+
UserContextInterface::USER_TYPE_CUSTOMER
247+
],
248+
['#1', '#3', '#4', '#5'], // expected to be removed
249+
['#2', '#6'], // expected to exist
250+
],
251+
];
252+
}
253+
254+
/**
255+
* Make that only exired tokens were cleaned up
256+
*
257+
* @param array $expectedRemovedTokenNumbers
258+
* @param array $expectedPreservedTokenNumbers
259+
* @param array $generatedTokens
260+
*/
261+
private function assertTokenCleanUp(
262+
$expectedRemovedTokenNumbers,
263+
$expectedPreservedTokenNumbers,
264+
$generatedTokens
265+
) {
266+
foreach ($expectedRemovedTokenNumbers as $tokenNumber) {
267+
$token = $this->tokenFactory->create();
268+
$this->tokenResourceModel->load($token, $generatedTokens[$tokenNumber]['tokenId']);
269+
$this->assertEmpty(
270+
$token->getId(),
271+
"Token {$tokenNumber} was expected to be deleted after clean up"
272+
);
273+
}
274+
foreach ($expectedPreservedTokenNumbers as $tokenNumber) {
275+
$token = $this->tokenFactory->create();
276+
$this->tokenResourceModel->load($token, $generatedTokens[$tokenNumber]['tokenId']);
277+
$this->assertEquals(
278+
$generatedTokens[$tokenNumber]['tokenId'],
279+
$token->getId(),
280+
"Token {$tokenNumber} was NOT expected to be deleted after clean up"
281+
);
282+
}
283+
}
284+
}

0 commit comments

Comments
 (0)