Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Cache tokens when using swift's v2 authentication
Signed-off-by: Robin Appelman <[email protected]>
  • Loading branch information
icewind1991 authored and Backportbot committed Jan 22, 2019
commit a5c5a77322906c184bf00e2ce629112e6172b997
68 changes: 51 additions & 17 deletions lib/private/Files/ObjectStore/SwiftFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
use OCP\ILogger;
use OpenStack\Common\Error\BadResponseError;
use OpenStack\Common\Auth\Token;
use OpenStack\Identity\v2\Models\Catalog;
use OpenStack\Identity\v2\Service as IdentityV2Service;
use OpenStack\Identity\v3\Service as IdentityV3Service;
use OpenStack\OpenStack;
Expand All @@ -47,6 +48,13 @@ class SwiftFactory {
private $container = null;
private $logger;

const DEFAULT_OPTIONS = [
'autocreate' => false,
'urlType' => 'publicURL',
'catalogName' => 'swift',
'catalogType' => 'object-store'
];

public function __construct(ICache $cache, array $params, ILogger $logger) {
$this->cache = $cache;
$this->params = $params;
Expand All @@ -62,11 +70,21 @@ private function getCachedToken(string $cacheKey) {
}
}

private function cacheToken(Token $token, string $cacheKey) {
private function cacheToken(Token $token, string $serviceUrl, string $cacheKey) {
if ($token instanceof \OpenStack\Identity\v3\Models\Token) {
// for v3 the catalog is cached as part of the token, so no need to cache $serviceUrl separately
$value = json_encode($token->export());
} else {
$value = json_encode($token);
/** @var \OpenStack\Identity\v2\Models\Token $token */
$value = json_encode([
'serviceUrl' => $serviceUrl,
'token' => [
'issued_at' => $token->issuedAt->format('c'),
'expires' => $token->expires->format('c'),
'id' => $token->id,
'tenant' => $token->tenant
]
]);
}
$this->cache->set($cacheKey . '/token', $value);
}
Expand All @@ -82,10 +100,6 @@ private function getClient() {
if (!isset($this->params['container'])) {
$this->params['container'] = 'nextcloud';
}
if (!isset($this->params['autocreate'])) {
// should only be true for tests
$this->params['autocreate'] = false;
}
if (isset($this->params['user']) && is_array($this->params['user'])) {
$userName = $this->params['user']['name'];
} else {
Expand All @@ -97,6 +111,7 @@ private function getClient() {
if (!isset($this->params['tenantName']) && isset($this->params['tenant'])) {
$this->params['tenantName'] = $this->params['tenant'];
}
$this->params = array_merge(self::DEFAULT_OPTIONS, $this->params);

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

return $this->auth(IdentityV3Service::factory($httpClient), $cacheKey);
} else {
return $this->auth(IdentityV2Service::factory($httpClient), $cacheKey);
return $this->auth(SwiftV2CachingAuthService::factory($httpClient), $cacheKey);
}
}

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

$cachedToken = $this->params['cachedToken'];
$hasValidCachedToken = false;
if (\is_array($cachedToken) && ($authService instanceof IdentityV3Service)) {
$token = $authService->generateTokenFromCache($cachedToken);
if (\is_null($token->catalog)) {
$this->logger->warning('Invalid cached token for swift, no catalog set: ' . json_encode($cachedToken));
} else if ($token->hasExpired()) {
$this->logger->debug('Cached token for swift expired');
if (\is_array($cachedToken)) {
if ($authService instanceof IdentityV3Service) {
$token = $authService->generateTokenFromCache($cachedToken);
if (\is_null($token->catalog)) {
$this->logger->warning('Invalid cached token for swift, no catalog set: ' . json_encode($cachedToken));
} else if ($token->hasExpired()) {
$this->logger->debug('Cached token for swift expired');
} else {
$hasValidCachedToken = true;
}
} else {
$hasValidCachedToken = true;
try {
/** @var \OpenStack\Identity\v2\Models\Token $token */
$token = $authService->model(\OpenStack\Identity\v2\Models\Token::class, $cachedToken['token']);
$now = new \DateTimeImmutable("now");
if ($token->expires > $now) {
$hasValidCachedToken = true;
$this->params['v2cachedToken'] = $token;
$this->params['v2serviceUrl'] = $cachedToken['serviceUrl'];
} else {
$this->logger->debug('Cached token for swift expired');
}
} catch (\Exception $e) {
$this->logger->logException($e);
}
}
}

if (!$hasValidCachedToken) {
try {
$token = $authService->generateToken($this->params);
$this->cacheToken($token, $cacheKey);
list($token, $serviceUrl) = $authService->authenticate($this->params);
$this->cacheToken($token, $serviceUrl, $cacheKey);
} catch (ConnectException $e) {
throw new StorageAuthException('Failed to connect to keystone, verify the keystone url', $e);
} catch (ClientException $e) {
Expand All @@ -164,6 +195,9 @@ private function auth($authService, string $cacheKey) {
}
}


$client = new OpenStack($this->params);

return $client;
}

Expand Down
35 changes: 35 additions & 0 deletions lib/private/Files/ObjectStore/SwiftV2CachingAuthService.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php declare(strict_types=1);
/**
* @copyright Copyright (c) 2018 Robin Appelman <[email protected]>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/

namespace OC\Files\ObjectStore;


use OpenStack\Identity\v2\Service;

class SwiftV2CachingAuthService extends Service {
public function authenticate(array $options = []): array {
if (!empty($options['v2cachedToken'])) {
return [$options['v2cachedToken'], $options['v2serviceUrl']];
} else {
return parent::authenticate($options);
}
}
}
8 changes: 8 additions & 0 deletions tests/preseed-config.php
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,14 @@
'name' => 'default',
]
],
'scope' => [
'project' => [
'name' => 'service',
'domain' => [
'name' => 'default',
],
],
],
'tenantName' => 'service',
'serviceName' => 'swift',
'region' => 'regionOne',
Expand Down