Skip to content

Commit 83b0925

Browse files
authored
Merge pull request #58 from ydb-platform/fix-token-refresh
Fixed refresh token when it expired
2 parents 318310f + 9583942 commit 83b0925

File tree

14 files changed

+186
-12
lines changed

14 files changed

+186
-12
lines changed

.github/workflows/tests.yml

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
name: CI test connection
1+
name: CI tests
22

33
on:
44
pull_request:
@@ -9,7 +9,7 @@ permissions:
99
contents: read
1010

1111
jobs:
12-
build:
12+
tests:
1313
services:
1414
ydb:
1515
image: cr.yandex/yc/yandex-docker-local-ydb:stable-22-5
@@ -23,7 +23,9 @@ jobs:
2323
YDB_USE_IN_MEMORY_PDISKS: true
2424
options: '-h localhost'
2525
runs-on: ubuntu-latest
26-
26+
strategy:
27+
matrix:
28+
php-versions: [ '7.4' ]
2729
steps:
2830
- uses: actions/checkout@v3
2931
name: Checkout
@@ -38,7 +40,7 @@ jobs:
3840
id: php
3941
with:
4042
extensions: grpc
41-
php-version: 7.4
43+
php-version: ${{ matrix.php-versions }}
4244

4345
- run: composer validate --strict
4446
name: Validate composer.json and composer.lock

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
* fixed refresh token when it expired
12
* fixed retry at BAD_SESSION
23
* added credentials authentication
34
* added CI test

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
"psr/log": "^1|^2|^3"
2121
},
2222
"require-dev": {
23-
"phpunit/phpunit": "9.*"
23+
"phpunit/phpunit": ">= 6.0, <10.0"
2424
},
2525
"autoload": {
2626
"psr-4": {

src/Auth/TokenInfo.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
class TokenInfo
66
{
7+
const _PRIVATE_REFRESH_RATIO = 0.1;
78
/**
89
* @var string
910
*/
@@ -13,10 +14,13 @@ class TokenInfo
1314
*/
1415
protected $expiresAt;
1516

17+
private $refreshAt;
18+
1619
public function __construct(string $token, int $expiresAt)
1720
{
1821
$this->token = $token;
1922
$this->expiresAt = $expiresAt;
23+
$this->refreshAt = time() + round(TokenInfo::_PRIVATE_REFRESH_RATIO*($this->expiresAt-time()),0);
2024
}
2125

2226
/**
@@ -34,4 +38,9 @@ public function getToken(): string
3438
{
3539
return $this->token;
3640
}
41+
42+
public function getRefreshAt(): int
43+
{
44+
return $this->refreshAt;
45+
}
3746
}

src/Discovery.php

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ class Discovery
3030
* @var LoggerInterface
3131
*/
3232
protected $logger;
33+
/**
34+
* @var Iam
35+
*/
36+
protected $credentials;
3337

3438
/**
3539
* @param Ydb $ydb
@@ -43,6 +47,8 @@ public function __construct(Ydb $ydb, LoggerInterface $logger = null)
4347

4448
$this->meta = $ydb->meta();
4549

50+
$this->credentials = $ydb->iam();
51+
4652
$this->database = $ydb->database();
4753

4854
$this->logger = $logger;
@@ -82,4 +88,4 @@ protected function request($method, array $data = [])
8288
{
8389
return $this->doRequest('Discovery', $method, $data);
8490
}
85-
}
91+
}

src/Iam.php

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,11 @@ class Iam implements IamTokenContract
4545
*/
4646
protected $logger;
4747

48+
/**
49+
* @var int
50+
*/
51+
protected $refresh_at;
52+
4853
/**
4954
* @param array $config
5055
* @param LoggerInterface|null $logger
@@ -96,9 +101,11 @@ public function newToken()
96101
$tokenInfo = $this->config('credentials')->getTokenInfo();
97102
$this->iam_token = $tokenInfo->getToken();
98103
$this->expires_at = $tokenInfo->getExpiresAt();
104+
$this->refresh_at = $tokenInfo->getRefreshAt();
99105
$this->saveToken((object)[
100106
"iamToken" => $tokenInfo->getToken(),
101107
"expiresAt" => $tokenInfo->getExpiresAt(),
108+
"refreshAt" => $tokenInfo->getRefreshAt()
102109
]);
103110
return $tokenInfo->getToken();
104111
}
@@ -347,11 +354,14 @@ protected function loadToken()
347354
{
348355
if ($this->iam_token)
349356
{
350-
if ($this->expires_at > time())
351-
{
352-
return $this->iam_token;
357+
if ($this->refresh_at <= time()){
358+
try {
359+
return $this->newToken();
360+
} catch (\Exception $e){
361+
return $this->iam_token;
362+
}
353363
}
354-
return $this->newToken();
364+
return $this->iam_token;
355365
}
356366

357367
return $this->loadTokenFromFile();
@@ -372,6 +382,7 @@ protected function loadTokenFromFile()
372382
{
373383
$this->iam_token = $token->iamToken;
374384
$this->expires_at = $token->expiresAt;
385+
$this->refresh_at = $token->refreshAt ?? time();
375386
$this->logger()->info('YDB: Reused IAM token [...' . substr($this->iam_token, -6) . '].');
376387
return $token->iamToken;
377388
}
@@ -390,10 +401,12 @@ protected function saveToken($token)
390401

391402
$this->iam_token = $token->iamToken;
392403
$this->expires_at = $this->convertExpiresAt($token->expiresAt ?? '');
404+
$this->refresh_at = $token->refreshAt;
393405

394406
file_put_contents($tokenFile, json_encode([
395407
'iamToken' => $this->iam_token,
396408
'expiresAt' => $this->expires_at,
409+
'refreshAt' => $this->refresh_at
397410
]));
398411
}
399412

src/Operations.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ class Operations
2525
* @var LoggerInterface
2626
*/
2727
protected $logger;
28+
/**
29+
* @var Iam
30+
*/
31+
protected $credentials;
2832

2933
/**
3034
* @param Ydb $ydb
@@ -38,6 +42,8 @@ public function __construct(Ydb $ydb, LoggerInterface $logger = null)
3842

3943
$this->meta = $ydb->meta();
4044

45+
$this->credentials = $ydb->iam();
46+
4147
$this->logger = $logger;
4248
}
4349

src/Scheme.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ public function __construct(Ydb $ydb, LoggerInterface $logger = null)
4545

4646
$this->meta = $ydb->meta();
4747

48+
$this->credentials = $ydb->iam();
49+
4850
$this->database = $ydb->database();
4951

5052
$this->logger = $logger;

src/Scripting.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ class Scripting
2525
* @var LoggerInterface
2626
*/
2727
protected $logger;
28+
/**
29+
* @var Iam
30+
*/
31+
protected $credentials;
2832

2933
/**
3034
* @param Ydb $ydb
@@ -38,6 +42,8 @@ public function __construct(Ydb $ydb, LoggerInterface $logger = null)
3842

3943
$this->meta = $ydb->meta();
4044

45+
$this->credentials = $ydb->iam();
46+
4147
$this->logger = $logger;
4248
}
4349

src/Session.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ public function __construct(Table $table, $session_id)
8282

8383
$this->client = $table->client();
8484
$this->meta = $table->meta();
85+
$this->credentials = $table->credentials();
8586
$this->path = $table->path();
8687
$this->logger = $table->getLogger();
8788

src/Table.php

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@ class Table
4141
* @var LoggerInterface
4242
*/
4343
protected $logger;
44+
/**
45+
* @var Iam
46+
*/
47+
protected $credentials;
4448

4549
/**
4650
* @param Ydb $ydb
@@ -54,6 +58,8 @@ public function __construct(Ydb $ydb, LoggerInterface $logger = null)
5458

5559
$this->meta = $ydb->meta();
5660

61+
$this->credentials = $ydb->iam();
62+
5763
$this->path = $ydb->database();
5864

5965
$this->logger = $logger;
@@ -284,6 +290,14 @@ public function createTable($table, $columns, $primary_key = 'id', $indexes = []
284290
return $this->session()->createTable($table, $columns, $primary_key, $indexes);
285291
}
286292

293+
/**
294+
* @return Iam
295+
*/
296+
public function credentials(): Iam
297+
{
298+
return $this->credentials;
299+
}
300+
287301
/**
288302
* Proxy to Session::copyTable.
289303
*
@@ -407,4 +421,4 @@ protected function streamRequest($method, array $data = [])
407421
return $this->doStreamRequest('Table', $method, $data);
408422
}
409423

410-
}
424+
}

src/Traits/RequestTrait.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ trait RequestTrait
4141
*/
4242
protected function doRequest($service, $method, array $data = [])
4343
{
44+
$this->meta['x-ydb-auth-ticket'] = [$this->credentials->token()];
45+
4446
$this->saveLastRequest($service, $method, $data);
4547

4648
$requestClass = '\\Ydb\\' . $service . '\\' . $method . 'Request';

tests/RefreshTokenTest.php

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
<?php
2+
3+
namespace YdbPlatform\Ydb\Test;
4+
5+
use PHPUnit\Framework\TestCase;
6+
use YdbPlatform\Ydb\Auth\Auth;
7+
use YdbPlatform\Ydb\Auth\TokenInfo;
8+
use YdbPlatform\Ydb\Ydb;
9+
10+
class FakeCredentials extends Auth {
11+
12+
/**
13+
* @var int
14+
*/
15+
protected $counter;
16+
17+
protected $tokenLiveTime;
18+
19+
public function __construct(&$counter, &$tokenLiveTime)
20+
{
21+
$this->counter = &$counter;
22+
$this->tokenLiveTime = &$tokenLiveTime;
23+
}
24+
25+
public function getTokenInfo(): TokenInfo
26+
{
27+
$this->counter++;
28+
if ($this->counter==2){
29+
throw new \Exception("Some error");
30+
}
31+
return new TokenInfo(time()+$this->tokenLiveTime,time()+$this->tokenLiveTime);
32+
}
33+
34+
public function getName(): string
35+
{
36+
return "FakeCredentials";
37+
}
38+
}
39+
40+
class MetaGetter extends \YdbPlatform\Ydb\Session{
41+
public static function getMeta(\YdbPlatform\Ydb\Session $session){
42+
return $session->meta;
43+
}
44+
}
45+
46+
class RefreshTokenTest extends TestCase
47+
{
48+
public function test(){
49+
50+
$counter = 0;
51+
52+
$TOKEN_LIVE_TIME = 10;
53+
54+
$config = [
55+
56+
// Database path
57+
'database' => '/local',
58+
59+
// Database endpoint
60+
'endpoint' => 'localhost:2136',
61+
62+
// Auto discovery (dedicated server only)
63+
'discovery' => false,
64+
65+
// IAM config
66+
'iam_config' => [
67+
'insecure' => true,
68+
],
69+
'credentials' => new FakeCredentials($counter, $TOKEN_LIVE_TIME)
70+
];
71+
$ydb = new Ydb($config);
72+
$table = $ydb->table();
73+
$session = $table->session();
74+
$token = MetaGetter::getMeta($session)["x-ydb-auth-ticket"][0];
75+
self::assertEquals(
76+
1,
77+
$counter
78+
);
79+
80+
// Check that the token will not be updated until a refresh time
81+
$session->query('select 1 as res');
82+
self::assertEquals(
83+
1,
84+
$counter
85+
);
86+
self::assertEquals(
87+
$token,
88+
MetaGetter::getMeta($session)["x-ydb-auth-ticket"][0]
89+
);
90+
// Check that sdk used old token when failed refreshing
91+
usleep(TokenInfo::_PRIVATE_REFRESH_RATIO*$TOKEN_LIVE_TIME*1000*1000); // waiting 10% from token live time
92+
$session->query('select 1 as res');
93+
self::assertEquals(
94+
2,
95+
$counter
96+
);
97+
self::assertEquals(
98+
$token,
99+
MetaGetter::getMeta($session)["x-ydb-auth-ticket"][0]
100+
);
101+
102+
// Check that token refreshed
103+
$session->query('select 1 as res');
104+
self::assertEquals(
105+
3,
106+
$counter
107+
);
108+
self::assertNotEquals(
109+
$token,
110+
MetaGetter::getMeta($session)["x-ydb-auth-ticket"][0]
111+
);
112+
}
113+
}

0 commit comments

Comments
 (0)