Merge pull request #7896 from stanley-cheung/php-fix-per-rpc-creds-v1_0

PHP: reject metadata keys that are not legal
pull/7916/head
Stanley Cheung 9 years ago committed by GitHub
commit e1ec9fd347
  1. 4
      package.xml
  2. 3
      src/php/ext/grpc/call.c
  3. 20
      src/php/ext/grpc/call_credentials.c
  4. 37
      src/php/tests/interop/interop_client.php
  5. 65
      src/php/tests/unit_tests/CallCredentials2Test.php
  6. 135
      src/php/tests/unit_tests/CallCredentials3Test.php
  7. 24
      src/php/tests/unit_tests/CallTest.php
  8. 4
      templates/package.xml.template

@ -22,7 +22,7 @@
</stability> </stability>
<license>BSD</license> <license>BSD</license>
<notes> <notes>
- TBD - Reject metadata keys which are not legal #7881
</notes> </notes>
<contents> <contents>
<dir baseinstalldir="/" name="/"> <dir baseinstalldir="/" name="/">
@ -1173,7 +1173,7 @@ Update to wrap gRPC C Core version 0.10.0
<date>2016-08-22</date> <date>2016-08-22</date>
<license>BSD</license> <license>BSD</license>
<notes> <notes>
- TBD - Reject metadata keys which are not legal #7881
</notes> </notes>
</release> </release>
</changelog> </changelog>

@ -164,6 +164,9 @@ bool create_metadata_array(zval *array, grpc_metadata_array *metadata) {
if (key_type1 != HASH_KEY_IS_STRING) { if (key_type1 != HASH_KEY_IS_STRING) {
return false; return false;
} }
if (!grpc_header_key_is_legal(key1, strlen(key1))) {
return false;
}
inner_array_hash = Z_ARRVAL_P(inner_array); inner_array_hash = Z_ARRVAL_P(inner_array);
PHP_GRPC_HASH_FOREACH_VAL_START(inner_array_hash, value) PHP_GRPC_HASH_FOREACH_VAL_START(inner_array_hash, value)
if (Z_TYPE_P(value) != IS_STRING) { if (Z_TYPE_P(value) != IS_STRING) {

@ -192,24 +192,16 @@ void plugin_get_metadata(void *ptr, grpc_auth_metadata_context context,
/* call the user callback function */ /* call the user callback function */
zend_call_function(state->fci, state->fci_cache TSRMLS_CC); zend_call_function(state->fci, state->fci_cache TSRMLS_CC);
if (Z_TYPE_P(retval) != IS_ARRAY) { grpc_status_code code = GRPC_STATUS_OK;
zend_throw_exception(spl_ce_InvalidArgumentException,
"plugin callback must return metadata array",
1 TSRMLS_CC);
return;
}
grpc_metadata_array metadata; grpc_metadata_array metadata;
if (!create_metadata_array(retval, &metadata)) {
zend_throw_exception(spl_ce_InvalidArgumentException, if (Z_TYPE_P(retval) != IS_ARRAY) {
"invalid metadata", 1 TSRMLS_CC); code = GRPC_STATUS_INVALID_ARGUMENT;
} else if (!create_metadata_array(retval, &metadata)) {
grpc_metadata_array_destroy(&metadata); grpc_metadata_array_destroy(&metadata);
return; code = GRPC_STATUS_INVALID_ARGUMENT;
} }
/* TODO: handle error */
grpc_status_code code = GRPC_STATUS_OK;
/* Pass control back to core */ /* Pass control back to core */
cb(user_data, metadata.metadata, metadata.count, code, NULL); cb(user_data, metadata.metadata, metadata.count, code, NULL);
} }

@ -54,6 +54,15 @@ function hardAssert($value, $error_message)
} }
} }
function hardAssertIfStatusOk($status)
{
if ($status->code !== Grpc\STATUS_OK) {
echo "Call did not complete successfully. Status object:\n";
var_dump($status);
exit(1);
}
}
/** /**
* Run the empty_unary test. * Run the empty_unary test.
* *
@ -62,7 +71,7 @@ function hardAssert($value, $error_message)
function emptyUnary($stub) function emptyUnary($stub)
{ {
list($result, $status) = $stub->EmptyCall(new grpc\testing\EmptyMessage())->wait(); list($result, $status) = $stub->EmptyCall(new grpc\testing\EmptyMessage())->wait();
hardAssert($status->code === Grpc\STATUS_OK, 'Call did not complete successfully'); hardAssertIfStatusOk($status);
hardAssert($result !== null, 'Call completed with a null response'); hardAssert($result !== null, 'Call completed with a null response');
} }
@ -105,7 +114,7 @@ function performLargeUnary($stub, $fillUsername = false, $fillOauthScope = false
} }
list($result, $status) = $stub->UnaryCall($request, [], $options)->wait(); list($result, $status) = $stub->UnaryCall($request, [], $options)->wait();
hardAssert($status->code === Grpc\STATUS_OK, 'Call did not complete successfully'); hardAssertIfStatusOk($status);
hardAssert($result !== null, 'Call returned a null response'); hardAssert($result !== null, 'Call returned a null response');
$payload = $result->getPayload(); $payload = $result->getPayload();
hardAssert($payload->getType() === grpc\testing\PayloadType::COMPRESSABLE, hardAssert($payload->getType() === grpc\testing\PayloadType::COMPRESSABLE,
@ -197,7 +206,12 @@ function updateAuthMetadataCallback($context)
$methodName = $context->method_name; $methodName = $context->method_name;
$auth_credentials = ApplicationDefaultCredentials::getCredentials(); $auth_credentials = ApplicationDefaultCredentials::getCredentials();
return $auth_credentials->updateMetadata($metadata = [], $authUri); $metadata = [];
$result = $auth_credentials->updateMetadata([], $authUri);
foreach ($result as $key => $value) {
$metadata[strtolower($key)] = $value;
}
return $metadata;
} }
/** /**
@ -242,7 +256,7 @@ function clientStreaming($stub)
$call->write($request); $call->write($request);
} }
list($result, $status) = $call->wait(); list($result, $status) = $call->wait();
hardAssert($status->code === Grpc\STATUS_OK, 'Call did not complete successfully'); hardAssertIfStatusOk($status);
hardAssert($result->getAggregatedPayloadSize() === 74922, hardAssert($result->getAggregatedPayloadSize() === 74922,
'aggregated_payload_size was incorrect'); 'aggregated_payload_size was incorrect');
} }
@ -275,8 +289,7 @@ function serverStreaming($stub)
'Response '.$i.' had the wrong length'); 'Response '.$i.' had the wrong length');
$i += 1; $i += 1;
} }
hardAssert($call->getStatus()->code === Grpc\STATUS_OK, hardAssertIfStatusOk($call->getStatus());
'Call did not complete successfully');
} }
/** /**
@ -312,8 +325,7 @@ function pingPong($stub)
} }
$call->writesDone(); $call->writesDone();
hardAssert($call->read() === null, 'Server returned too many responses'); hardAssert($call->read() === null, 'Server returned too many responses');
hardAssert($call->getStatus()->code === Grpc\STATUS_OK, hardAssertIfStatusOk($call->getStatus());
'Call did not complete successfully');
} }
/** /**
@ -326,8 +338,7 @@ function emptyStream($stub)
$call = $stub->FullDuplexCall(); $call = $stub->FullDuplexCall();
$call->writesDone(); $call->writesDone();
hardAssert($call->read() === null, 'Server returned too many responses'); hardAssert($call->read() === null, 'Server returned too many responses');
hardAssert($call->getStatus()->code === Grpc\STATUS_OK, hardAssertIfStatusOk($call->getStatus());
'Call did not complete successfully');
} }
/** /**
@ -419,8 +430,7 @@ function customMetadata($stub)
'Incorrect initial metadata value'); 'Incorrect initial metadata value');
list($result, $status) = $call->wait(); list($result, $status) = $call->wait();
hardAssert($status->code === Grpc\STATUS_OK, hardAssertIfStatusOk($status);
'Call did not complete successfully');
$trailing_metadata = $call->getTrailingMetadata(); $trailing_metadata = $call->getTrailingMetadata();
hardAssert(array_key_exists($ECHO_TRAILING_KEY, $trailing_metadata), hardAssert(array_key_exists($ECHO_TRAILING_KEY, $trailing_metadata),
@ -435,8 +445,7 @@ function customMetadata($stub)
$streaming_call->write($streaming_request); $streaming_call->write($streaming_request);
$streaming_call->writesDone(); $streaming_call->writesDone();
hardAssert($streaming_call->getStatus()->code === Grpc\STATUS_OK, hardAssertIfStatusOk($streaming_call->getStatus());
'Call did not complete successfully');
$streaming_trailing_metadata = $streaming_call->getTrailingMetadata(); $streaming_trailing_metadata = $streaming_call->getTrailingMetadata();
hardAssert(array_key_exists($ECHO_TRAILING_KEY, hardAssert(array_key_exists($ECHO_TRAILING_KEY,

@ -132,4 +132,69 @@ class CallCredentials2Test extends PHPUnit_Framework_TestCase
unset($call); unset($call);
unset($server_call); unset($server_call);
} }
public function invalidKeyCallbackFunc($context)
{
$this->assertTrue(is_string($context->service_url));
$this->assertTrue(is_string($context->method_name));
return ['K1' => ['v1']];
}
public function testCallbackWithInvalidKey()
{
$deadline = Grpc\Timeval::infFuture();
$status_text = 'xyz';
$call = new Grpc\Call($this->channel,
'/abc/dummy_method',
$deadline,
$this->host_override);
$call_credentials = Grpc\CallCredentials::createFromPlugin(
array($this, 'invalidKeyCallbackFunc'));
$call->setCredentials($call_credentials);
$event = $call->startBatch([
Grpc\OP_SEND_INITIAL_METADATA => [],
Grpc\OP_SEND_CLOSE_FROM_CLIENT => true,
Grpc\OP_RECV_STATUS_ON_CLIENT => true,
]);
$this->assertTrue($event->send_metadata);
$this->assertTrue($event->send_close);
$this->assertTrue($event->status->code == Grpc\STATUS_UNAUTHENTICATED);
}
public function invalidReturnCallbackFunc($context)
{
$this->assertTrue(is_string($context->service_url));
$this->assertTrue(is_string($context->method_name));
return "a string";
}
public function testCallbackWithInvalidReturnValue()
{
$deadline = Grpc\Timeval::infFuture();
$status_text = 'xyz';
$call = new Grpc\Call($this->channel,
'/abc/dummy_method',
$deadline,
$this->host_override);
$call_credentials = Grpc\CallCredentials::createFromPlugin(
array($this, 'invalidReturnCallbackFunc'));
$call->setCredentials($call_credentials);
$event = $call->startBatch([
Grpc\OP_SEND_INITIAL_METADATA => [],
Grpc\OP_SEND_CLOSE_FROM_CLIENT => true,
Grpc\OP_RECV_STATUS_ON_CLIENT => true,
]);
$this->assertTrue($event->send_metadata);
$this->assertTrue($event->send_close);
$this->assertTrue($event->status->code == Grpc\STATUS_UNAUTHENTICATED);
}
} }

@ -1,135 +0,0 @@
<?php
/*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
class CallCredentials3Test extends PHPUnit_Framework_TestCase
{
public function setUp()
{
$this->credentials = Grpc\ChannelCredentials::createSsl(
file_get_contents(dirname(__FILE__).'/../data/ca.pem'));
$server_credentials = Grpc\ServerCredentials::createSsl(
null,
file_get_contents(dirname(__FILE__).'/../data/server1.key'),
file_get_contents(dirname(__FILE__).'/../data/server1.pem'));
$this->server = new Grpc\Server();
$this->port = $this->server->addSecureHttp2Port('0.0.0.0:0',
$server_credentials);
$this->server->start();
$this->host_override = 'foo.test.google.fr';
$this->channel = new Grpc\Channel(
'localhost:'.$this->port,
[
'grpc.ssl_target_name_override' => $this->host_override,
'grpc.default_authority' => $this->host_override,
'credentials' => $this->credentials,
]
);
}
public function tearDown()
{
unset($this->channel);
unset($this->server);
}
public function callbackFunc($context)
{
$this->assertTrue(is_string($context->service_url));
$this->assertTrue(is_string($context->method_name));
return ['k1' => ['v1'], 'k2' => ['v2']];
}
public function testCreateFromPlugin()
{
$deadline = Grpc\Timeval::infFuture();
$status_text = 'xyz';
$call = new Grpc\Call($this->channel,
'/abc/dummy_method',
$deadline,
$this->host_override);
$call_credentials = Grpc\CallCredentials::createFromPlugin(
[$this, 'callbackFunc']);
$call->setCredentials($call_credentials);
$event = $call->startBatch([
Grpc\OP_SEND_INITIAL_METADATA => [],
Grpc\OP_SEND_CLOSE_FROM_CLIENT => true,
]);
$this->assertTrue($event->send_metadata);
$this->assertTrue($event->send_close);
$event = $this->server->requestCall();
$this->assertTrue(is_array($event->metadata));
$metadata = $event->metadata;
$this->assertTrue(array_key_exists('k1', $metadata));
$this->assertTrue(array_key_exists('k2', $metadata));
$this->assertSame($metadata['k1'], ['v1']);
$this->assertSame($metadata['k2'], ['v2']);
$this->assertSame('/abc/dummy_method', $event->method);
$server_call = $event->call;
$event = $server_call->startBatch([
Grpc\OP_SEND_INITIAL_METADATA => [],
Grpc\OP_SEND_STATUS_FROM_SERVER => [
'metadata' => [],
'code' => Grpc\STATUS_OK,
'details' => $status_text,
],
Grpc\OP_RECV_CLOSE_ON_SERVER => true,
]);
$this->assertTrue($event->send_metadata);
$this->assertTrue($event->send_status);
$this->assertFalse($event->cancelled);
$event = $call->startBatch([
Grpc\OP_RECV_INITIAL_METADATA => true,
Grpc\OP_RECV_STATUS_ON_CLIENT => true,
]);
$this->assertSame([], $event->metadata);
$status = $event->status;
$this->assertSame([], $status->metadata);
$this->assertSame(Grpc\STATUS_OK, $status->code);
$this->assertSame($status_text, $status->details);
unset($call);
unset($server_call);
}
}

@ -113,7 +113,7 @@ class CallTest extends PHPUnit_Framework_TestCase
/** /**
* @expectedException InvalidArgumentException * @expectedException InvalidArgumentException
*/ */
public function testInvalidMetadataKey() public function testInvalidStartBatchKey()
{ {
$batch = [ $batch = [
'invalid' => ['key1' => 'value1'], 'invalid' => ['key1' => 'value1'],
@ -121,6 +121,28 @@ class CallTest extends PHPUnit_Framework_TestCase
$result = $this->call->startBatch($batch); $result = $this->call->startBatch($batch);
} }
/**
* @expectedException InvalidArgumentException
*/
public function testInvalidMetadataStrKey()
{
$batch = [
Grpc\OP_SEND_INITIAL_METADATA => ['Key' => ['value1', 'value2']],
];
$result = $this->call->startBatch($batch);
}
/**
* @expectedException InvalidArgumentException
*/
public function testInvalidMetadataIntKey()
{
$batch = [
Grpc\OP_SEND_INITIAL_METADATA => [1 => ['value1', 'value2']],
];
$result = $this->call->startBatch($batch);
}
/** /**
* @expectedException InvalidArgumentException * @expectedException InvalidArgumentException
*/ */

@ -24,7 +24,7 @@
</stability> </stability>
<license>BSD</license> <license>BSD</license>
<notes> <notes>
- TBD - Reject metadata keys which are not legal #7881
</notes> </notes>
<contents> <contents>
<dir baseinstalldir="/" name="/"> <dir baseinstalldir="/" name="/">
@ -291,7 +291,7 @@
<date>2016-08-22</date> <date>2016-08-22</date>
<license>BSD</license> <license>BSD</license>
<notes> <notes>
- TBD - Reject metadata keys which are not legal #7881
</notes> </notes>
</release> </release>
</changelog> </changelog>

Loading…
Cancel
Save