Skip to content

Commit 6b8f5c2

Browse files
committed
PHPC-2097: Enable ClientEncryption constructor
1 parent 1cdfd0d commit 6b8f5c2

File tree

7 files changed

+162
-32
lines changed

7 files changed

+162
-32
lines changed

src/MongoDB/ClientEncryption.c

Lines changed: 45 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,20 @@ static void phongo_clientencryption_create_datakey(php_phongo_clientencryption_t
3636
static void phongo_clientencryption_encrypt(php_phongo_clientencryption_t* clientencryption, zval* zvalue, zval* zciphertext, zval* options);
3737
static void phongo_clientencryption_decrypt(php_phongo_clientencryption_t* clientencryption, zval* zciphertext, zval* zvalue);
3838

39+
/* {{{ proto void MongoDB\Driver\ClientEncryption::__construct(array $options)
40+
Constructs a new ClientEncryption */
41+
static PHP_METHOD(ClientEncryption, __construct)
42+
{
43+
zval* options;
44+
45+
PHONGO_PARSE_PARAMETERS_START(1, 1)
46+
Z_PARAM_ARRAY(options)
47+
PHONGO_PARSE_PARAMETERS_END();
48+
49+
/* An exception will be thrown on error. */
50+
phongo_clientencryption_init(Z_CLIENTENCRYPTION_OBJ_P(getThis()), options, NULL);
51+
} /* }}} */
52+
3953
/* {{{ proto MongoDB\BSON\Binary MongoDB\Driver\ClientEncryption::createDataKey(string $kmsProvider[, array $options])
4054
Creates a new key document and inserts into the key vault collection. */
4155
static PHP_METHOD(ClientEncryption, createDataKey)
@@ -99,6 +113,10 @@ static PHP_METHOD(ClientEncryption, decrypt)
99113
phongo_clientencryption_decrypt(intern, ciphertext, return_value);
100114
} /* }}} */
101115

116+
ZEND_BEGIN_ARG_INFO_EX(ai_ClientEncryption___construct, 0, 0, 0)
117+
ZEND_ARG_ARRAY_INFO(0, options, 1)
118+
ZEND_END_ARG_INFO()
119+
102120
ZEND_BEGIN_ARG_INFO_EX(ai_ClientEncryption_createDataKey, 0, 0, 1)
103121
ZEND_ARG_INFO(0, kmsProvider)
104122
ZEND_ARG_ARRAY_INFO(0, options, 1)
@@ -118,10 +136,10 @@ ZEND_END_ARG_INFO()
118136

119137
static zend_function_entry php_phongo_clientencryption_me[] = {
120138
/* clang-format off */
139+
PHP_ME(ClientEncryption, __construct, ai_ClientEncryption___construct, ZEND_ACC_PUBLIC | ZEND_ACC_FINAL)
121140
PHP_ME(ClientEncryption, createDataKey, ai_ClientEncryption_createDataKey, ZEND_ACC_PUBLIC | ZEND_ACC_FINAL)
122141
PHP_ME(ClientEncryption, encrypt, ai_ClientEncryption_encrypt, ZEND_ACC_PUBLIC | ZEND_ACC_FINAL)
123142
PHP_ME(ClientEncryption, decrypt, ai_ClientEncryption_decrypt, ZEND_ACC_PUBLIC | ZEND_ACC_FINAL)
124-
ZEND_NAMED_ME(__construct, PHP_FN(MongoDB_disabled___construct), ai_ClientEncryption_void, ZEND_ACC_PRIVATE | ZEND_ACC_FINAL)
125143
ZEND_NAMED_ME(__wakeup, PHP_FN(MongoDB_disabled___wakeup), ai_ClientEncryption_void, ZEND_ACC_PUBLIC | ZEND_ACC_FINAL)
126144
PHP_FE_END
127145
/* clang-format on */
@@ -197,14 +215,13 @@ void php_phongo_clientencryption_init_ce(INIT_FUNC_ARGS) /* {{{ */
197215
} /* }}} */
198216

199217
#ifdef MONGOC_ENABLE_CLIENT_SIDE_ENCRYPTION
200-
/* keyVaultClientManager is an output parameter and will be assigned the
201-
* keyVaultNamespace Manager (if any). */
202-
static mongoc_client_encryption_opts_t* phongo_clientencryption_opts_from_zval(zval* defaultKeyVaultClient, zval* options, zval** keyVaultClientManager) /* {{{ */
218+
/* key_vault_client_manager is an output parameter and will be assigned to the
219+
* effective keyVaultClient. */
220+
static mongoc_client_encryption_opts_t* phongo_clientencryption_opts_from_zval(zval* options, zval* default_key_vault_client_manager, zval** key_vault_client_manager) /* {{{ */
203221
{
204-
mongoc_client_encryption_opts_t* opts;
222+
mongoc_client_encryption_opts_t* opts = mongoc_client_encryption_opts_new();
205223

206-
opts = mongoc_client_encryption_opts_new();
207-
*keyVaultClientManager = NULL;
224+
*key_vault_client_manager = NULL;
208225

209226
if (!options || Z_TYPE_P(options) != IS_ARRAY) {
210227
/* Returning opts as-is will defer to mongoc_client_encryption_new to
@@ -221,10 +238,15 @@ static mongoc_client_encryption_opts_t* phongo_clientencryption_opts_from_zval(z
221238
}
222239

223240
mongoc_client_encryption_opts_set_keyvault_client(opts, Z_MANAGER_OBJ_P(key_vault_client)->client);
224-
*keyVaultClientManager = key_vault_client;
241+
*key_vault_client_manager = key_vault_client;
242+
} else if (default_key_vault_client_manager) {
243+
mongoc_client_encryption_opts_set_keyvault_client(opts, Z_MANAGER_OBJ_P(default_key_vault_client_manager)->client);
244+
*key_vault_client_manager = default_key_vault_client_manager;
225245
} else {
226-
mongoc_client_encryption_opts_set_keyvault_client(opts, Z_MANAGER_OBJ_P(defaultKeyVaultClient)->client);
227-
*keyVaultClientManager = defaultKeyVaultClient;
246+
/* If the ClientEncryption object is being constructed directly, the
247+
* "keyVaultClient" option must be explicitly provided. */
248+
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT, "The \"keyVaultClient\" option is required when constructing a ClientEncryption object directly");
249+
goto cleanup;
228250
}
229251

230252
if (php_array_existsc(options, "keyVaultNamespace")) {
@@ -301,32 +323,35 @@ static mongoc_client_encryption_opts_t* phongo_clientencryption_opts_from_zval(z
301323
return NULL;
302324
} /* }}} */
303325

304-
void phongo_clientencryption_init(zval* return_value, zval* manager, zval* options) /* {{{ */
326+
void phongo_clientencryption_init(php_phongo_clientencryption_t* intern, zval* options, zval* default_key_vault_client_manager) /* {{{ */
305327
{
306-
php_phongo_clientencryption_t* intern;
307328
mongoc_client_encryption_t* client_encryption;
308329
mongoc_client_encryption_opts_t* opts;
309-
zval* key_vault_client_manager = manager;
330+
zval* key_vault_client_manager = NULL;
310331
bson_error_t error = { 0 };
311332

312-
opts = phongo_clientencryption_opts_from_zval(manager, options, &key_vault_client_manager);
333+
opts = phongo_clientencryption_opts_from_zval(options, default_key_vault_client_manager, &key_vault_client_manager);
334+
313335
if (!opts) {
314336
/* Exception already thrown */
315337
goto cleanup;
316338
}
317339

318340
client_encryption = mongoc_client_encryption_new(opts, &error);
341+
319342
if (!client_encryption) {
320343
phongo_throw_exception_from_bson_error_t(&error);
321-
322344
goto cleanup;
323345
}
324346

325-
object_init_ex(return_value, php_phongo_clientencryption_ce);
326-
327-
intern = Z_CLIENTENCRYPTION_OBJ_P(return_value);
328347
intern->client_encryption = client_encryption;
329-
ZVAL_ZVAL(&intern->key_vault_client_manager, key_vault_client_manager, 1, 0);
348+
349+
/* Note: key_vault_client_manager should always be assigned if options were
350+
* successfully parsed by phongo_clientencryption_opts_from_zval, but let's
351+
* be defensive. */
352+
if (key_vault_client_manager) {
353+
ZVAL_ZVAL(&intern->key_vault_client_manager, key_vault_client_manager, 1, 0);
354+
}
330355

331356
cleanup:
332357
if (opts) {
@@ -584,7 +609,7 @@ static void phongo_clientencryption_decrypt(php_phongo_clientencryption_t* clien
584609
bson_value_destroy(&value);
585610
} /* }}} */
586611
#else /* MONGOC_ENABLE_CLIENT_SIDE_ENCRYPTION */
587-
void phongo_clientencryption_init(zval* return_value, zval* manager, zval* options) /* {{{ */
612+
void phongo_clientencryption_init(php_phongo_clientencryption_t* intern, zval* options, zval* default_key_vault_client_manager) /* {{{ */
588613
{
589614
phongo_throw_exception_no_cse(PHONGO_ERROR_RUNTIME, "Cannot configure clientEncryption object.");
590615
}

src/MongoDB/ClientEncryption.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,6 @@
1919

2020
#include <php.h>
2121

22-
void phongo_clientencryption_init(zval* return_value, zval* manager, zval* options);
22+
void phongo_clientencryption_init(php_phongo_clientencryption_t* intern, zval* options, zval* default_key_vault_client_manager);
2323

2424
#endif /* PHONGO_CLIENTENCRYPTION_H */

src/MongoDB/Manager.c

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -311,18 +311,16 @@ static PHP_METHOD(Manager, addSubscriber)
311311
Return a ClientEncryption instance */
312312
static PHP_METHOD(Manager, createClientEncryption)
313313
{
314-
zend_error_handling error_handling;
315-
zval* options;
314+
zval* options;
316315

317-
zend_replace_error_handling(EH_THROW, phongo_exception_from_phongo_domain(PHONGO_ERROR_INVALID_ARGUMENT), &error_handling);
318-
if (zend_parse_parameters(ZEND_NUM_ARGS(), "a", &options) == FAILURE) {
319-
zend_restore_error_handling(&error_handling);
320-
return;
321-
}
322-
zend_restore_error_handling(&error_handling);
316+
PHONGO_PARSE_PARAMETERS_START(1, 1)
317+
Z_PARAM_ARRAY(options)
318+
PHONGO_PARSE_PARAMETERS_END();
319+
320+
object_init_ex(return_value, php_phongo_clientencryption_ce);
323321

324322
/* An exception will be thrown on error. */
325-
phongo_clientencryption_init(return_value, getThis(), options);
323+
phongo_clientencryption_init(Z_CLIENTENCRYPTION_OBJ_P(return_value), options, getThis());
326324
} /* }}} */
327325

328326
/* {{{ proto MongoDB\Driver\Cursor MongoDB\Driver\Manager::executeCommand(string $db, MongoDB\Driver\Command $command[, array $options = null])
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
--TEST--
2+
MongoDB\Driver\ClientEncryption::__construct()
3+
--SKIPIF--
4+
<?php require __DIR__ . "/../utils/basic-skipif.inc"; ?>
5+
<?php skip_if_not_libmongocrypt(); ?>
6+
--FILE--
7+
<?php
8+
9+
$key = base64_decode('Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk');
10+
11+
$clientEncryption = new MongoDB\Driver\ClientEncryption([
12+
'keyVaultClient' => new MongoDB\Driver\Manager(),
13+
'keyVaultNamespace' => 'default.keys',
14+
'kmsProviders' => ['local' => ['key' => new MongoDB\BSON\Binary($key, 0)]]
15+
]);
16+
17+
var_dump($clientEncryption);
18+
19+
?>
20+
===DONE===
21+
<?php exit(0); ?>
22+
--EXPECTF--
23+
object(MongoDB\Driver\ClientEncryption)#%d (%d) {
24+
}
25+
===DONE===
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
--TEST--
2+
MongoDB\Driver\ClientEncryption::__construct() fails if compiled without FLE
3+
--SKIPIF--
4+
<?php require __DIR__ . "/../utils/basic-skipif.inc"; ?>
5+
<?php skip_if_libmongocrypt(); ?>
6+
--FILE--
7+
<?php
8+
9+
require_once __DIR__ . '/../utils/basic.inc';
10+
11+
echo throws(function () {
12+
new MongoDB\Driver\ClientEncryption([]);
13+
}, MongoDB\Driver\Exception\RuntimeException::class), "\n";
14+
15+
?>
16+
===DONE===
17+
<?php exit(0); ?>
18+
--EXPECT--
19+
OK: Got MongoDB\Driver\Exception\RuntimeException
20+
Cannot configure clientEncryption object. Please recompile with support for libmongocrypt using the with-mongodb-client-side-encryption configure switch.
21+
===DONE===
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
--TEST--
2+
MongoDB\Driver\ClientEncryption::__construct() with invalid option types
3+
--SKIPIF--
4+
<?php require __DIR__ . "/../utils/basic-skipif.inc"; ?>
5+
<?php skip_if_not_libmongocrypt(); ?>
6+
--FILE--
7+
<?php
8+
9+
require_once __DIR__ . '/../utils/basic.inc';
10+
11+
/* phongo_clientencryption_opts_from_zval always requires a keyVaultClient
12+
* option when constructing ClientEncryption directly, so this will be used to
13+
* test other options. */
14+
$baseOptions = ['keyVaultClient' => create_test_manager()];
15+
16+
$tests = [
17+
[],
18+
['keyVaultClient' => 'not_an_array_or_object'],
19+
[
20+
'keyVaultNamespace' => 'not_a_namespace',
21+
// keyVaultNamespace requires a valid kmsProviders option
22+
'kmsProviders' => ['local' => ['key' => new MongoDB\BSON\Binary('', 0)]],
23+
] + $baseOptions,
24+
['kmsProviders' => 'not_an_array_or_object'] + $baseOptions,
25+
['tlsOptions' => 'not_an_array_or_object'] + $baseOptions,
26+
];
27+
28+
foreach ($tests as $test) {
29+
echo throws(function () use ($test) {
30+
new MongoDB\Driver\ClientEncryption($test);
31+
}, MongoDB\Driver\Exception\InvalidArgumentException::class), "\n\n";
32+
}
33+
34+
?>
35+
===DONE===
36+
<?php exit(0); ?>
37+
--EXPECT--
38+
OK: Got MongoDB\Driver\Exception\InvalidArgumentException
39+
The "keyVaultClient" option is required when constructing a ClientEncryption object directly
40+
41+
OK: Got MongoDB\Driver\Exception\InvalidArgumentException
42+
Expected "keyVaultClient" option to be MongoDB\Driver\Manager, string given
43+
44+
OK: Got MongoDB\Driver\Exception\InvalidArgumentException
45+
Expected "keyVaultNamespace" option to contain a full collection namespace
46+
47+
OK: Got MongoDB\Driver\Exception\InvalidArgumentException
48+
Expected "kmsProviders" option to be an array or object, string given
49+
50+
OK: Got MongoDB\Driver\Exception\InvalidArgumentException
51+
Expected "tlsOptions" option to be an array or object, string given
52+
53+
===DONE===

tests/manager/manager-createClientEncryption-001.phpt

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,18 @@ MongoDB\Driver\Manager::createClientEncryption()
99
$key = base64_decode('Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk');
1010

1111
$manager = new MongoDB\Driver\Manager();
12-
$clientEncryption = $manager->createClientEncryption(['keyVaultNamespace' => 'default.keys', 'kmsProviders' => ['local' => ['key' => new MongoDB\BSON\Binary($key, 0)]]]);
12+
13+
$clientEncryption = $manager->createClientEncryption([
14+
'keyVaultNamespace' => 'default.keys',
15+
'kmsProviders' => ['local' => ['key' => new MongoDB\BSON\Binary($key, 0)]]
16+
]);
17+
18+
var_dump($clientEncryption);
1319

1420
?>
1521
===DONE===
1622
<?php exit(0); ?>
17-
--EXPECT--
23+
--EXPECTF--
24+
object(MongoDB\Driver\ClientEncryption)#%d (%d) {
25+
}
1826
===DONE===

0 commit comments

Comments
 (0)