Skip to content

Commit 68246b2

Browse files
committed
Cache tokens when using swift's v2 authentication
Signed-off-by: Robin Appelman <[email protected]>
1 parent 6e91329 commit 68246b2

File tree

3 files changed

+94
-17
lines changed

3 files changed

+94
-17
lines changed

lib/private/Files/ObjectStore/SwiftFactory.php

Lines changed: 51 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
use OCP\ILogger;
3434
use OpenStack\Common\Error\BadResponseError;
3535
use OpenStack\Common\Auth\Token;
36+
use OpenStack\Identity\v2\Models\Catalog;
3637
use OpenStack\Identity\v2\Service as IdentityV2Service;
3738
use OpenStack\Identity\v3\Service as IdentityV3Service;
3839
use OpenStack\OpenStack;
@@ -47,6 +48,13 @@ class SwiftFactory {
4748
private $container = null;
4849
private $logger;
4950

51+
const DEFAULT_OPTIONS = [
52+
'autocreate' => false,
53+
'urlType' => 'publicURL',
54+
'catalogName' => 'swift',
55+
'catalogType' => 'object-store'
56+
];
57+
5058
public function __construct(ICache $cache, array $params, ILogger $logger) {
5159
$this->cache = $cache;
5260
$this->params = $params;
@@ -62,11 +70,21 @@ private function getCachedToken(string $cacheKey) {
6270
}
6371
}
6472

65-
private function cacheToken(Token $token, string $cacheKey) {
73+
private function cacheToken(Token $token, string $serviceUrl, string $cacheKey) {
6674
if ($token instanceof \OpenStack\Identity\v3\Models\Token) {
75+
// for v3 the catalog is cached as part of the token, so no need to cache $serviceUrl separately
6776
$value = json_encode($token->export());
6877
} else {
69-
$value = json_encode($token);
78+
/** @var \OpenStack\Identity\v2\Models\Token $token */
79+
$value = json_encode([
80+
'serviceUrl' => $serviceUrl,
81+
'token' => [
82+
'issued_at' => $token->issuedAt->format('c'),
83+
'expires' => $token->expires->format('c'),
84+
'id' => $token->id,
85+
'tenant' => $token->tenant
86+
]
87+
]);
7088
}
7189
$this->cache->set($cacheKey . '/token', $value);
7290
}
@@ -82,10 +100,6 @@ private function getClient() {
82100
if (!isset($this->params['container'])) {
83101
$this->params['container'] = 'nextcloud';
84102
}
85-
if (!isset($this->params['autocreate'])) {
86-
// should only be true for tests
87-
$this->params['autocreate'] = false;
88-
}
89103
if (isset($this->params['user']) && is_array($this->params['user'])) {
90104
$userName = $this->params['user']['name'];
91105
} else {
@@ -97,6 +111,7 @@ private function getClient() {
97111
if (!isset($this->params['tenantName']) && isset($this->params['tenant'])) {
98112
$this->params['tenantName'] = $this->params['tenant'];
99113
}
114+
$this->params = array_merge(self::DEFAULT_OPTIONS, $this->params);
100115

101116
$cacheKey = $userName . '@' . $this->params['url'] . '/' . $this->params['container'];
102117
$token = $this->getCachedToken($cacheKey);
@@ -114,7 +129,7 @@ private function getClient() {
114129

115130
return $this->auth(IdentityV3Service::factory($httpClient), $cacheKey);
116131
} else {
117-
return $this->auth(IdentityV2Service::factory($httpClient), $cacheKey);
132+
return $this->auth(SwiftV2CachingAuthService::factory($httpClient), $cacheKey);
118133
}
119134
}
120135

@@ -127,25 +142,41 @@ private function getClient() {
127142
private function auth($authService, string $cacheKey) {
128143
$this->params['identityService'] = $authService;
129144
$this->params['authUrl'] = $this->params['url'];
130-
$client = new OpenStack($this->params);
131145

132146
$cachedToken = $this->params['cachedToken'];
133147
$hasValidCachedToken = false;
134-
if (\is_array($cachedToken) && ($authService instanceof IdentityV3Service)) {
135-
$token = $authService->generateTokenFromCache($cachedToken);
136-
if (\is_null($token->catalog)) {
137-
$this->logger->warning('Invalid cached token for swift, no catalog set: ' . json_encode($cachedToken));
138-
} else if ($token->hasExpired()) {
139-
$this->logger->debug('Cached token for swift expired');
148+
if (\is_array($cachedToken)) {
149+
if ($authService instanceof IdentityV3Service) {
150+
$token = $authService->generateTokenFromCache($cachedToken);
151+
if (\is_null($token->catalog)) {
152+
$this->logger->warning('Invalid cached token for swift, no catalog set: ' . json_encode($cachedToken));
153+
} else if ($token->hasExpired()) {
154+
$this->logger->debug('Cached token for swift expired');
155+
} else {
156+
$hasValidCachedToken = true;
157+
}
140158
} else {
141-
$hasValidCachedToken = true;
159+
try {
160+
/** @var \OpenStack\Identity\v2\Models\Token $token */
161+
$token = $authService->model(\OpenStack\Identity\v2\Models\Token::class, $cachedToken['token']);
162+
$now = new \DateTimeImmutable("now");
163+
if ($token->expires > $now) {
164+
$hasValidCachedToken = true;
165+
$this->params['v2cachedToken'] = $token;
166+
$this->params['v2serviceUrl'] = $cachedToken['serviceUrl'];
167+
} else {
168+
$this->logger->debug('Cached token for swift expired');
169+
}
170+
} catch (\Exception $e) {
171+
$this->logger->logException($e);
172+
}
142173
}
143174
}
144175

145176
if (!$hasValidCachedToken) {
146177
try {
147-
$token = $authService->generateToken($this->params);
148-
$this->cacheToken($token, $cacheKey);
178+
list($token, $serviceUrl) = $authService->authenticate($this->params);
179+
$this->cacheToken($token, $serviceUrl, $cacheKey);
149180
} catch (ConnectException $e) {
150181
throw new StorageAuthException('Failed to connect to keystone, verify the keystone url', $e);
151182
} catch (ClientException $e) {
@@ -164,6 +195,9 @@ private function auth($authService, string $cacheKey) {
164195
}
165196
}
166197

198+
199+
$client = new OpenStack($this->params);
200+
167201
return $client;
168202
}
169203

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<?php declare(strict_types=1);
2+
/**
3+
* @copyright Copyright (c) 2018 Robin Appelman <[email protected]>
4+
*
5+
* @license GNU AGPL version 3 or any later version
6+
*
7+
* This program is free software: you can redistribute it and/or modify
8+
* it under the terms of the GNU Affero General Public License as
9+
* published by the Free Software Foundation, either version 3 of the
10+
* License, or (at your option) any later version.
11+
*
12+
* This program is distributed in the hope that it will be useful,
13+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+
* GNU Affero General Public License for more details.
16+
*
17+
* You should have received a copy of the GNU Affero General Public License
18+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
19+
*
20+
*/
21+
22+
namespace OC\Files\ObjectStore;
23+
24+
25+
use OpenStack\Identity\v2\Service;
26+
27+
class SwiftV2CachingAuthService extends Service {
28+
public function authenticate(array $options = []): array {
29+
if (!empty($options['v2cachedToken'])) {
30+
return [$options['v2cachedToken'], $options['v2serviceUrl']];
31+
} else {
32+
return parent::authenticate($options);
33+
}
34+
}
35+
}

tests/preseed-config.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,14 @@
6363
'name' => 'default',
6464
]
6565
],
66+
'scope' => [
67+
'project' => [
68+
'name' => 'service',
69+
'domain' => [
70+
'name' => 'default',
71+
],
72+
],
73+
],
6674
'tenantName' => 'service',
6775
'serviceName' => 'swift',
6876
'region' => 'regionOne',

0 commit comments

Comments
 (0)