Skip to content

Commit

Permalink
fix requests for container with "tokens" name in it
Browse files Browse the repository at this point in the history
  • Loading branch information
k0ka committed Jan 31, 2024
1 parent 9f817df commit f3541e1
Show file tree
Hide file tree
Showing 13 changed files with 70 additions and 50 deletions.
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
"justinrainbow/json-schema": "^5.2"
},
"require-dev": {
"ext-json": "*",
"ergebnis/composer-normalize": "^2.0",
"friendsofphp/php-cs-fixer": "^3",
"php-coveralls/php-coveralls": "^2.0",
Expand Down
14 changes: 13 additions & 1 deletion src/Common/Api/Operation.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ class Operation
/** @var []Parameter The parameters of this operation */
private $params;

/** @var bool Whether this operation should skip authentication */
private $skipAuth;

/**
* @param array $definition The data definition (in array form) that will populate this
* operation. Usually this is retrieved from an {@see ApiInterface}
Expand All @@ -42,6 +45,7 @@ public function __construct(array $definition)
}

$this->params = self::toParamArray($definition['params']);
$this->skipAuth = $definition['skipAuth'] ?? false;
}

public function getPath(): string
Expand All @@ -54,10 +58,18 @@ public function getMethod(): string
return $this->method;
}

/**
* Indicates if operation must be run without authentication. This is useful for getting authentication tokens.
*/
public function getSkipAuth(): bool
{
return $this->skipAuth;
}

/**
* Indicates whether this operation supports a parameter.
*
* @param $key The name of a parameter
* @param string $key The name of a parameter
*/
public function hasParam(string $key): bool
{
Expand Down
17 changes: 9 additions & 8 deletions src/Common/Api/OperatorTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ trait OperatorTrait
public function __construct(ClientInterface $client, ApiInterface $api)
{
$this->client = $client;
$this->api = $api;
$this->api = $api;
}

/**
Expand Down Expand Up @@ -55,8 +55,8 @@ public function __debugInfo()
* {@see Promise} object. In order for this to happen, the called methods need to be in the
* following format: `createAsync`, where `create` is the sequential method being wrapped.
*
* @param $methodName the name of the method being invoked
* @param $args the arguments to be passed to the sequential method
* @param string $methodName the name of the method being invoked
* @param array $args the arguments to be passed to the sequential method
*
* @return Promise
*
Expand Down Expand Up @@ -92,22 +92,23 @@ public function getOperation(array $definition): Operation
return new Operation($definition);
}

/**
* @throws \Exception
*/
protected function sendRequest(Operation $operation, array $userValues = [], bool $async = false)
{
$operation->validate($userValues);

$options = (new RequestSerializer())->serializeOptions($operation, $userValues);
$method = $async ? 'requestAsync' : 'request';
$method = $async ? 'requestAsync' : 'request';

$uri = Utils::uri_template($operation->getPath(), $userValues);
$uri = Utils::uri_template($operation->getPath(), $userValues);

if (array_key_exists('requestOptions', $userValues)) {
$options += $userValues['requestOptions'];
}

if ($operation->getSkipAuth()) {
$options['openstack.skip_auth'] = true;
}

return $this->client->$method($operation->getMethod(), $uri, $options);
}

Expand Down
11 changes: 1 addition & 10 deletions src/Common/Auth/AuthHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public function __invoke(RequestInterface $request, array $options)
{
$fn = $this->nextHandler;

if ($this->shouldIgnore($request)) {
if (isset($options['openstack.skip_auth']) && $options['openstack.skip_auth']) {
return $fn($request, $options);
}

Expand All @@ -55,13 +55,4 @@ public function __invoke(RequestInterface $request, array $options)

return $fn(Utils::modifyRequest($request, $modify), $options);
}

/**
* Internal method which prevents infinite recursion. For certain requests, like the initial
* auth call itself, we do NOT want to send a token.
*/
private function shouldIgnore(RequestInterface $request): bool
{
return false !== strpos((string) $request->getUri(), 'tokens') && 'POST' == $request->getMethod();
}
}
6 changes: 3 additions & 3 deletions src/Common/Service/Builder.php
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ public function createService(string $namespace, array $serviceOptions = []): Se
return new $serviceClass($options['httpClient'], new $apiClass());
}

private function stockHttpClient(array &$options, string $serviceName)
private function stockHttpClient(array &$options, string $serviceName): void
{
if (!isset($options['httpClient']) || !($options['httpClient'] instanceof ClientInterface)) {
if (false !== stripos($serviceName, 'identity')) {
Expand All @@ -105,7 +105,7 @@ private function stockHttpClient(array &$options, string $serviceName)
/**
* @codeCoverageIgnore
*/
private function addDebugMiddleware(array $options, HandlerStack &$stack)
private function addDebugMiddleware(array $options, HandlerStack &$stack): void
{
if (!empty($options['debugLog'])
&& !empty($options['logger'])
Expand All @@ -118,7 +118,7 @@ private function addDebugMiddleware(array $options, HandlerStack &$stack)
/**
* @codeCoverageIgnore
*/
private function stockAuthHandler(array &$options)
private function stockAuthHandler(array &$options): void
{
if (!isset($options['authHandler'])) {
$options['authHandler'] = function () use ($options) {
Expand Down
13 changes: 7 additions & 6 deletions src/Identity/v2/Api.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,21 @@ class Api implements ApiInterface
public function postToken(): array
{
return [
'method' => 'POST',
'path' => 'tokens',
'params' => [
'username' => [
'method' => 'POST',
'path' => 'tokens',
'skipAuth' => true,
'params' => [
'username' => [
'type' => 'string',
'required' => true,
'path' => 'auth.passwordCredentials',
],
'password' => [
'password' => [
'type' => 'string',
'required' => true,
'path' => 'auth.passwordCredentials',
],
'tenantId' => [
'tenantId' => [
'type' => 'string',
'path' => 'auth',
],
Expand Down
7 changes: 4 additions & 3 deletions src/Identity/v3/Api.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,10 @@ public function __construct()
public function postTokens(): array
{
return [
'method' => 'POST',
'path' => 'auth/tokens',
'params' => [
'method' => 'POST',
'path' => 'auth/tokens',
'skipAuth' => true,
'params' => [
'methods' => $this->params->methods(),
'user' => $this->params->user(),
'application_credential' => $this->params->applicationCredential(),
Expand Down
22 changes: 11 additions & 11 deletions src/Identity/v3/Models/Token.php
Original file line number Diff line number Diff line change
Expand Up @@ -89,27 +89,27 @@ public function retrieve()
}

/**
* @param array $data {@see \OpenStack\Identity\v3\Api::postTokens}
* @param array $userOptions {@see \OpenStack\Identity\v3\Api::postTokens}
*/
public function create(array $data): Creatable
public function create(array $userOptions): Creatable
{
if (isset($data['user'])) {
$data['methods'] = ['password'];
if (!isset($data['user']['id']) && empty($data['user']['domain'])) {
if (isset($userOptions['user'])) {
$userOptions['methods'] = ['password'];
if (!isset($userOptions['user']['id']) && empty($userOptions['user']['domain'])) {
throw new InvalidArgumentException('When authenticating with a username, you must also provide either the domain name '.'or domain ID to which the user belongs to. Alternatively, if you provide a user ID instead, '.'you do not need to provide domain information.');
}
} elseif (isset($data['application_credential'])) {
$data['methods'] = ['application_credential'];
if (!isset($data['application_credential']['id']) || !isset($data['application_credential']['secret'])) {
} elseif (isset($userOptions['application_credential'])) {
$userOptions['methods'] = ['application_credential'];
if (!isset($userOptions['application_credential']['id']) || !isset($userOptions['application_credential']['secret'])) {
throw new InvalidArgumentException('When authenticating with a application_credential, you must provide application credential ID '.' and application credential secret.');
}
} elseif (isset($data['tokenId'])) {
$data['methods'] = ['token'];
} elseif (isset($userOptions['tokenId'])) {
$userOptions['methods'] = ['token'];
} else {
throw new InvalidArgumentException('Either a user, tokenId or application_credential must be provided.');
}

$response = $this->execute($this->api->postTokens(), $data);
$response = $this->execute($this->api->postTokens(), $userOptions);
$token = $this->populateFromResponse($response);

// Cache response as an array to export if needed.
Expand Down
9 changes: 9 additions & 0 deletions tests/sample/ObjectStore/v1/ContainerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -132,4 +132,13 @@ public function testDelete(Container $createdContainer)
$this->expectException(BadResponseError::class);
$createdContainer->retrieve();
}

public function testTokensContainer()
{
$container = $this->getService()->createContainer(['name' => $this->randomStr() . '_tokens']);
$this->assertNotNull($container->name);

// this would send POST request with 'tokens' in the URL
$container->resetMetadata([]);
}
}
2 changes: 1 addition & 1 deletion tests/unit/Common/Auth/AuthHandlerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public function test_it_should_bypass_auth_http_requests()
// Fake a Keystone request
$request = new Request('POST', 'https://my-openstack.org:5000/v2.0/tokens');

self::assertEquals($request, call_user_func_array($this->handler, [$request, []]));
self::assertEquals($request, call_user_func_array($this->handler, [$request, ['openstack.skip_auth' => true]]));
}

public function test_it_should_generate_a_new_token_if_the_current_token_is_either_expired_or_not_set()
Expand Down
4 changes: 2 additions & 2 deletions tests/unit/Identity/v2/ServiceTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public function test_it_authenticates()
'tenantId' => $options['tenantId'],
]];

$this->setupMock('POST', 'tokens', $expectedJson, [], 'token-post');
$this->setupMock('POST', 'tokens', $expectedJson, [], 'token-post', true);

list($token, $baseUrl) = $this->service->authenticate($options);

Expand All @@ -64,7 +64,7 @@ public function test_it_generates_tokens()
'tenantId' => $options['tenantId'],
]];

$this->setupMock('POST', 'tokens', $expectedJson, [], 'token-post');
$this->setupMock('POST', 'tokens', $expectedJson, [], 'token-post', true);

self::assertInstanceOf(Token::class, $this->service->generateToken($options));
}
Expand Down
8 changes: 4 additions & 4 deletions tests/unit/Identity/v3/ServiceTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ public function test_it_authenticates()
]
];

$this->setupMock('POST', 'auth/tokens', ['auth' => $expectedJson], [], 'token');
$this->setupMock('POST', 'auth/tokens', ['auth' => $expectedJson], [], 'token', true);

list($token, $url) = $this->service->authenticate($userOptions);

Expand Down Expand Up @@ -231,7 +231,7 @@ public function test_it_throws_exception_if_no_endpoint_found()
]
];

$this->setupMock('POST', 'auth/tokens', ['auth' => $expectedJson], [], 'token');
$this->setupMock('POST', 'auth/tokens', ['auth' => $expectedJson], [], 'token', true);
$this->expectException(\RuntimeException::class);

$this->service->authenticate([
Expand Down Expand Up @@ -629,7 +629,7 @@ public function test_it_generates_tokens_with_user_creds()
]
];

$this->setupMock('POST', 'auth/tokens', ['auth' => $expectedJson], [], 'token');
$this->setupMock('POST', 'auth/tokens', ['auth' => $expectedJson], [], 'token', true);

$token = $this->service->generateToken($userOptions);
self::assertInstanceOf(Models\Token::class, $token);
Expand All @@ -654,7 +654,7 @@ public function test_it_generates_token_with_token_id()
]
];

$this->setupMock('POST', 'auth/tokens', ['auth' => $expectedJson], [], 'token');
$this->setupMock('POST', 'auth/tokens', ['auth' => $expectedJson], [], 'token', true);

$token = $this->service->generateToken($userOptions);
self::assertInstanceOf(Models\Token::class, $token);
Expand Down
6 changes: 5 additions & 1 deletion tests/unit/TestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ protected function getFixture($file)
return Message::parseResponse(file_get_contents($path));
}

protected function setupMock($method, $path, $body = null, array $headers = [], $response = null)
protected function setupMock($method, $path, $body = null, array $headers = [], $response = null, $skipAuth = false)
{
$options = ['headers' => $headers];

Expand All @@ -54,6 +54,10 @@ protected function setupMock($method, $path, $body = null, array $headers = [],
$response = $this->getFixture($response);
}

if ($skipAuth) {
$options['openstack.skip_auth'] = true;
}

$this->client
->request($method, $path, $options)
->shouldBeCalled()
Expand Down

0 comments on commit f3541e1

Please sign in to comment.