From 84287349d5802b547a234709383c40725cab8877 Mon Sep 17 00:00:00 2001 From: Jeremy Mikola Date: Mon, 1 Aug 2022 14:52:52 -0400 Subject: [PATCH 01/13] Clean keyvault collection before CSFLE tests --- .../clientEncryption/clientEncryption-createDataKey-001.phpt | 1 + tests/clientEncryption/clientEncryption-decrypt-001.phpt | 1 + tests/clientEncryption/clientEncryption-encrypt-001.phpt | 1 + tests/utils/basic.inc | 4 +++- 4 files changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/clientEncryption/clientEncryption-createDataKey-001.phpt b/tests/clientEncryption/clientEncryption-createDataKey-001.phpt index 15539e3ae..4c4e78a6b 100644 --- a/tests/clientEncryption/clientEncryption-createDataKey-001.phpt +++ b/tests/clientEncryption/clientEncryption-createDataKey-001.phpt @@ -4,6 +4,7 @@ MongoDB\Driver\ClientEncryption::createDataKey() + + + Date: Mon, 1 Aug 2022 16:25:39 -0400 Subject: [PATCH 02/13] Test invalid keyMaterial option for ClientEncryption::createDataKey() --- ...entEncryption-createDataKey_error-002.phpt | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 tests/clientEncryption/clientEncryption-createDataKey_error-002.phpt diff --git a/tests/clientEncryption/clientEncryption-createDataKey_error-002.phpt b/tests/clientEncryption/clientEncryption-createDataKey_error-002.phpt new file mode 100644 index 000000000..8d999cdc2 --- /dev/null +++ b/tests/clientEncryption/clientEncryption-createDataKey_error-002.phpt @@ -0,0 +1,37 @@ +--TEST-- +MongoDB\Driver\ClientEncryption::createDataKey() with invalid keyMaterial option +--SKIPIF-- + + + +--FILE-- + 0], + ['keyMaterial' => new stdClass], +]; + +$manager = create_test_manager(); +$clientEncryption = $manager->createClientEncryption([ + 'keyVaultNamespace' => CSFLE_KEY_VAULT_NS, + 'kmsProviders' => ['local' => ['key' => new MongoDB\BSON\Binary(CSFLE_LOCAL_KEY, 0)]], +]); + +foreach ($tests as $opts) { + echo throws(function () use ($clientEncryption, $opts) { + $clientEncryption->createDataKey('local', $opts); + }, MongoDB\Driver\Exception\InvalidArgumentException::class), "\n"; +} + +?> +===DONE=== + +--EXPECT-- +OK: Got MongoDB\Driver\Exception\InvalidArgumentException +Expected "keyMaterial" option to be MongoDB\BSON\Binary, int given +OK: Got MongoDB\Driver\Exception\InvalidArgumentException +Expected "keyMaterial" option to be MongoDB\BSON\Binary, stdClass given +===DONE=== From 15c90bdcefd096a62c1505a43b5c1727e4886cfd Mon Sep 17 00:00:00 2001 From: Jeremy Mikola Date: Wed, 29 Jun 2022 13:28:47 -0400 Subject: [PATCH 03/13] ClientEncryption::createDataKey() "keyMaterial" option --- src/MongoDB/ClientEncryption.c | 11 +++++++++++ .../clientEncryption-createDataKey_error-001.phpt | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/MongoDB/ClientEncryption.c b/src/MongoDB/ClientEncryption.c index e4fe1c089..5df72e833 100644 --- a/src/MongoDB/ClientEncryption.c +++ b/src/MongoDB/ClientEncryption.c @@ -383,6 +383,17 @@ static mongoc_client_encryption_datakey_opts_t* phongo_clientencryption_datakey_ } } + if (php_array_existsc(options, "keyMaterial")) { + zval* keyMaterial = php_array_fetchc(options, "keyMaterial"); + + if (Z_TYPE_P(keyMaterial) != IS_OBJECT || !instanceof_function(Z_OBJCE_P(keyMaterial), php_phongo_binary_ce)) { + phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT, "Expected \"keyMaterial\" option to be %s, %s given", ZSTR_VAL(php_phongo_binary_ce->name), PHONGO_ZVAL_CLASS_OR_TYPE_NAME_P(keyMaterial)); + return false; + } + + mongoc_client_encryption_datakey_opts_set_keymaterial(opts, (uint8_t*) Z_BINARY_OBJ_P(keyMaterial)->data, Z_BINARY_OBJ_P(keyMaterial)->data_len); + } + if (php_array_existsc(options, "masterKey")) { bson_t masterkey = BSON_INITIALIZER; diff --git a/tests/clientEncryption/clientEncryption-createDataKey_error-001.phpt b/tests/clientEncryption/clientEncryption-createDataKey_error-001.phpt index 529f821a7..e824b5244 100644 --- a/tests/clientEncryption/clientEncryption-createDataKey_error-001.phpt +++ b/tests/clientEncryption/clientEncryption-createDataKey_error-001.phpt @@ -1,5 +1,5 @@ --TEST-- -MongoDB\Driver\ClientEncryption::createDataKey() with invalid keyAltNames +MongoDB\Driver\ClientEncryption::createDataKey() with invalid keyAltNames option --SKIPIF-- From 33069e0c8afd28a3ab88712f3a19170f8889300a Mon Sep 17 00:00:00 2001 From: Jeremy Mikola Date: Wed, 29 Jun 2022 13:40:54 -0400 Subject: [PATCH 04/13] ClientEncryption key management methods Use ClientEncryption::getKey in createDataKey tests --- src/BSON/Binary.h | 22 ++ src/MongoDB/ClientEncryption.c | 363 +++++++++++++++++- src/MongoDB/ClientEncryption.stub.php | 14 + src/MongoDB/ClientEncryption_arginfo.h | 43 ++- src/MongoDB/Cursor.c | 75 +++- src/MongoDB/Cursor.h | 5 + src/MongoDB/Query.c | 38 +- src/MongoDB/Query.h | 24 ++ src/phongo_client.c | 52 --- src/phongo_execute.c | 58 +-- src/phongo_structs.h | 1 + src/phongo_util.c | 52 +++ src/phongo_util.h | 4 + .../clientEncryption-addKeyAltName-001.phpt | 40 ++ .../clientEncryption-addKeyAltName-002.phpt | 29 ++ ...entEncryption-addKeyAltName_error-001.phpt | 31 ++ .../clientEncryption-createDataKey-001.phpt | 9 +- .../clientEncryption-createDataKey-002.phpt | 34 ++ .../clientEncryption-deleteKey-001.phpt | 32 ++ .../clientEncryption-deleteKey-002.phpt | 32 ++ .../clientEncryption-deleteKey_error-001.phpt | 32 ++ .../clientEncryption-getKey-001.phpt | 30 ++ .../clientEncryption-getKey-002.phpt | 29 ++ .../clientEncryption-getKeyByAltName-001.phpt | 34 ++ .../clientEncryption-getKeyByAltName-002.phpt | 27 ++ .../clientEncryption-getKey_error-001.phpt | 32 ++ .../clientEncryption-getKeys-001.phpt | 40 ++ .../clientEncryption-getKeys-002.phpt | 27 ++ .../clientEncryption-getKeys-003.phpt | 61 +++ ...clientEncryption-removeKeyAltName-001.phpt | 38 ++ ...clientEncryption-removeKeyAltName-002.phpt | 29 ++ ...clientEncryption-removeKeyAltName-003.phpt | 32 ++ ...Encryption-removeKeyAltName_error-001.phpt | 32 ++ 33 files changed, 1272 insertions(+), 129 deletions(-) create mode 100644 src/BSON/Binary.h create mode 100644 src/MongoDB/Query.h create mode 100644 tests/clientEncryption/clientEncryption-addKeyAltName-001.phpt create mode 100644 tests/clientEncryption/clientEncryption-addKeyAltName-002.phpt create mode 100644 tests/clientEncryption/clientEncryption-addKeyAltName_error-001.phpt create mode 100644 tests/clientEncryption/clientEncryption-createDataKey-002.phpt create mode 100644 tests/clientEncryption/clientEncryption-deleteKey-001.phpt create mode 100644 tests/clientEncryption/clientEncryption-deleteKey-002.phpt create mode 100644 tests/clientEncryption/clientEncryption-deleteKey_error-001.phpt create mode 100644 tests/clientEncryption/clientEncryption-getKey-001.phpt create mode 100644 tests/clientEncryption/clientEncryption-getKey-002.phpt create mode 100644 tests/clientEncryption/clientEncryption-getKeyByAltName-001.phpt create mode 100644 tests/clientEncryption/clientEncryption-getKeyByAltName-002.phpt create mode 100644 tests/clientEncryption/clientEncryption-getKey_error-001.phpt create mode 100644 tests/clientEncryption/clientEncryption-getKeys-001.phpt create mode 100644 tests/clientEncryption/clientEncryption-getKeys-002.phpt create mode 100644 tests/clientEncryption/clientEncryption-getKeys-003.phpt create mode 100644 tests/clientEncryption/clientEncryption-removeKeyAltName-001.phpt create mode 100644 tests/clientEncryption/clientEncryption-removeKeyAltName-002.phpt create mode 100644 tests/clientEncryption/clientEncryption-removeKeyAltName-003.phpt create mode 100644 tests/clientEncryption/clientEncryption-removeKeyAltName_error-001.phpt diff --git a/src/BSON/Binary.h b/src/BSON/Binary.h new file mode 100644 index 000000000..b00fa2441 --- /dev/null +++ b/src/BSON/Binary.h @@ -0,0 +1,22 @@ +/* + * Copyright 2022-present MongoDB, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PHONGO_BSON_BINARY_H +#define PHONGO_BSON_BINARY_H + +#define PHONGO_BINARY_UUID_SIZE 16 + +#endif /* PHONGO_BSON_BINARY_H */ diff --git a/src/MongoDB/ClientEncryption.c b/src/MongoDB/ClientEncryption.c index 5df72e833..76853d318 100644 --- a/src/MongoDB/ClientEncryption.c +++ b/src/MongoDB/ClientEncryption.c @@ -26,9 +26,12 @@ #include "phongo_bson_encode.h" #include "phongo_error.h" #include "phongo_util.h" +#include "ClientEncryption_arginfo.h" +#include "BSON/Binary.h" #include "MongoDB/ClientEncryption.h" -#include "ClientEncryption_arginfo.h" +#include "MongoDB/Cursor.h" +#include "MongoDB/Query.h" zend_class_entry* php_phongo_clientencryption_ce; @@ -37,6 +40,43 @@ static void phongo_clientencryption_create_datakey(php_phongo_clientencryption_t static void phongo_clientencryption_encrypt(php_phongo_clientencryption_t* clientencryption, zval* zvalue, zval* zciphertext, zval* options); static void phongo_clientencryption_decrypt(php_phongo_clientencryption_t* clientencryption, zval* zciphertext, zval* zvalue); +#define RETVAL_OPTIONAL_BSON_T(reply) \ + do { \ + RETVAL_NULL(); \ + if (!bson_empty(&(reply))) { \ + php_phongo_bson_state state = { 0 }; \ + if (!php_phongo_bson_to_zval_ex(bson_get_data(&(reply)), (reply).len, &state)) { \ + zval_ptr_dtor(&state.zchild); \ + goto cleanup; \ + } \ + RETVAL_ZVAL(&state.zchild, 0, 1); \ + } \ + } while (0) + +/* Returns true if keyid is a UUID Binary value with an appropriate data length; + * otherwise, throws an exception and returns false. */ +static bool validate_keyid(bson_value_t* keyid) +{ + if (keyid->value_type != BSON_TYPE_BINARY) { + phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT, "Expected keyid to have Binary BSON type, %s given", php_phongo_bson_type_to_string(keyid->value_type)); + return false; + } + + if (keyid->value.v_binary.subtype != BSON_SUBTYPE_UUID) { + phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT, "Expected keyid to have UUID Binary subtype (%d), %d given", BSON_SUBTYPE_UUID, keyid->value.v_binary.subtype); + return false; + } + + /* php_phongo_binary_init already enforces the data length for Binary objects + * with UUID subtypes so we throw a different exception here. */ + if (keyid->value.v_binary.data_len != PHONGO_BINARY_UUID_SIZE) { + phongo_throw_exception(PHONGO_ERROR_UNEXPECTED_VALUE, "Expected keyid to have data length of %d bytes, %d given", PHONGO_BINARY_UUID_SIZE, keyid->value.v_binary.data_len); + return false; + } + + return true; +} + /* {{{ proto void MongoDB\Driver\ClientEncryption::__construct(array $options) Constructs a new ClientEncryption */ static PHP_METHOD(MongoDB_Driver_ClientEncryption, __construct) @@ -51,6 +91,47 @@ static PHP_METHOD(MongoDB_Driver_ClientEncryption, __construct) phongo_clientencryption_init(Z_CLIENTENCRYPTION_OBJ_P(getThis()), options, NULL); } /* }}} */ +/* {{{ proto object|null MongoDB\Driver\ClientEncryption::addKeyAltName(MongoDB\BSON\Binary $id, string $keyAltName) + Adds a keyAltName to the keyAltNames array of the key document in the key + vault collection with the given UUID (BSON binary subtype 0x04). Returns the + previous version of the key document, or null if no document matched. */ +static PHP_METHOD(MongoDB_Driver_ClientEncryption, addKeyAltName) +{ + zval* zkeyid = NULL; + char* keyaltname = NULL; + size_t keyaltname_len = 0; + bson_value_t keyid = { 0 }; + bson_t key_doc = BSON_INITIALIZER; + bson_error_t error = { 0 }; + + PHONGO_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_OBJECT_OF_CLASS(zkeyid, php_phongo_binary_ce) + Z_PARAM_STRING(keyaltname, keyaltname_len); + PHONGO_PARSE_PARAMETERS_END(); + + php_phongo_zval_to_bson_value(zkeyid, PHONGO_BSON_NONE, &keyid); + + if (EG(exception)) { + goto cleanup; + } + + if (!validate_keyid(&keyid)) { + /* Exception already thrown */ + goto cleanup; + } + + if (!mongoc_client_encryption_add_key_alt_name(Z_CLIENTENCRYPTION_OBJ_P(getThis())->client_encryption, &keyid, keyaltname, &key_doc, &error)) { + phongo_throw_exception_from_bson_error_t(&error); + goto cleanup; + } + + RETVAL_OPTIONAL_BSON_T(key_doc); + +cleanup: + bson_value_destroy(&keyid); + bson_destroy(&key_doc); +} /* }}} */ + /* {{{ proto MongoDB\BSON\Binary MongoDB\Driver\ClientEncryption::createDataKey(string $kmsProvider[, array $options]) Creates a new key document and inserts into the key vault collection. */ static PHP_METHOD(MongoDB_Driver_ClientEncryption, createDataKey) @@ -71,6 +152,44 @@ static PHP_METHOD(MongoDB_Driver_ClientEncryption, createDataKey) phongo_clientencryption_create_datakey(intern, return_value, kms_provider, options); } /* }}} */ +/* {{{ proto object MongoDB\Driver\ClientEncryption::deleteKey(MongoDB\BSON\Binary $id) + Removes the key document with the given UUID (BSON binary subtype 0x04) from + the key vault collection. Returns the result of the internal deleteOne() + operation on the key vault collection. */ +static PHP_METHOD(MongoDB_Driver_ClientEncryption, deleteKey) +{ + zval* zkeyid = NULL; + bson_value_t keyid = { 0 }; + bson_t reply = BSON_INITIALIZER; + bson_error_t error = { 0 }; + + PHONGO_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_OBJECT_OF_CLASS(zkeyid, php_phongo_binary_ce) + PHONGO_PARSE_PARAMETERS_END(); + + php_phongo_zval_to_bson_value(zkeyid, PHONGO_BSON_NONE, &keyid); + + if (EG(exception)) { + goto cleanup; + } + + if (!validate_keyid(&keyid)) { + /* Exception already thrown */ + goto cleanup; + } + + if (!mongoc_client_encryption_delete_key(Z_CLIENTENCRYPTION_OBJ_P(getThis())->client_encryption, &keyid, &reply, &error)) { + phongo_throw_exception_from_bson_error_t(&error); + goto cleanup; + } + + RETVAL_OPTIONAL_BSON_T(reply); + +cleanup: + bson_value_destroy(&keyid); + bson_destroy(&reply); +} /* }}} */ + /* {{{ proto MongoDB\BSON\Binary MongoDB\Driver\ClientEncryption::encrypt(mixed $value[, array $options]) Encrypts a value with a given key and algorithm */ static PHP_METHOD(MongoDB_Driver_ClientEncryption, encrypt) @@ -106,6 +225,215 @@ static PHP_METHOD(MongoDB_Driver_ClientEncryption, decrypt) phongo_clientencryption_decrypt(intern, ciphertext, return_value); } /* }}} */ +/* {{{ proto object|null MongoDB\Driver\ClientEncryption::getKey(MongoDB\BSON\Binary $id) + Finds a single key document with the given UUID (BSON binary subtype 0x04). + Returns the result of the internal find() operation on the key vault + collection, or null if no document matched. */ +static PHP_METHOD(MongoDB_Driver_ClientEncryption, getKey) +{ + zval* zkeyid = NULL; + bson_value_t keyid = { 0 }; + bson_t key_doc = BSON_INITIALIZER; + bson_error_t error = { 0 }; + + PHONGO_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_OBJECT_OF_CLASS(zkeyid, php_phongo_binary_ce) + PHONGO_PARSE_PARAMETERS_END(); + + php_phongo_zval_to_bson_value(zkeyid, PHONGO_BSON_NONE, &keyid); + + if (EG(exception)) { + goto cleanup; + } + + if (!validate_keyid(&keyid)) { + /* Exception already thrown */ + goto cleanup; + } + + if (!mongoc_client_encryption_get_key(Z_CLIENTENCRYPTION_OBJ_P(getThis())->client_encryption, &keyid, &key_doc, &error)) { + phongo_throw_exception_from_bson_error_t(&error); + goto cleanup; + } + + RETVAL_OPTIONAL_BSON_T(key_doc); + +cleanup: + bson_value_destroy(&keyid); + bson_destroy(&key_doc); +} /* }}} */ + +/* {{{ proto object|null MongoDB\Driver\ClientEncryption::getKey(string $keyAltName) + Returns a key document in the key vault collection with the given keyAltName, + or null if no document matched. */ +static PHP_METHOD(MongoDB_Driver_ClientEncryption, getKeyByAltName) +{ + char* keyaltname = NULL; + size_t keyaltname_len = 0; + bson_t key_doc = BSON_INITIALIZER; + bson_error_t error = { 0 }; + + PHONGO_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_STRING(keyaltname, keyaltname_len); + PHONGO_PARSE_PARAMETERS_END(); + + if (!mongoc_client_encryption_get_key_by_alt_name(Z_CLIENTENCRYPTION_OBJ_P(getThis())->client_encryption, keyaltname, &key_doc, &error)) { + phongo_throw_exception_from_bson_error_t(&error); + goto cleanup; + } + + RETVAL_OPTIONAL_BSON_T(key_doc); + +cleanup: + bson_destroy(&key_doc); +} /* }}} */ + +/* {{{ proto MongoDB\Driver\Cursor MongoDB\Driver\ClientEncryption::getKeys() + Finds all documents in the key vault collection. Returns the result of the + internal find() operation on the key vault collection as a cursor. */ +static PHP_METHOD(MongoDB_Driver_ClientEncryption, getKeys) +{ + mongoc_cursor_t* cursor; + bson_error_t error = { 0 }; + php_phongo_clientencryption_t* intern; + zval query = ZVAL_STATIC_INIT; + + intern = Z_CLIENTENCRYPTION_OBJ_P(getThis()); + + PHONGO_PARSE_PARAMETERS_NONE(); + + /* mongoc_client_encryption_get_keys executes a query against its internal + * key vault collection. The collection has a majority read concern, but the + * query itself specifies no filter or options. Create an empty query object + * and attach it to the cursor for the benefit of debugging. */ + if (!phongo_query_init(&query, NULL, NULL)) { + /* Exception already thrown */ + goto cleanup; + } + + cursor = mongoc_client_encryption_get_keys(intern->client_encryption, &error); + + if (!cursor) { + phongo_throw_exception_from_bson_error_t(&error); + goto cleanup; + } + + if (!phongo_cursor_init_for_query(return_value, &intern->key_vault_client_manager, cursor, intern->key_vault_namespace, &query, NULL, NULL)) { + /* Exception already thrown */ + mongoc_cursor_destroy(cursor); + goto cleanup; + } + +cleanup: + zval_ptr_dtor(&query); +} /* }}} */ + +/* {{{ proto object|null MongoDB\Driver\ClientEncryption::removeKeyAltName(MongoDB\BSON\Binary $id, string $keyAltName) + Removes a keyAltName from the keyAltNames array of the key document in the + key vault collection with the given UUID (BSON binary subtype 0x04). Returns + the previous version of the key document, or null if no document matched. */ +static PHP_METHOD(MongoDB_Driver_ClientEncryption, removeKeyAltName) +{ + zval* zkeyid = NULL; + char* keyaltname = NULL; + size_t keyaltname_len = 0; + bson_value_t keyid = { 0 }; + bson_t key_doc = BSON_INITIALIZER; + bson_error_t error = { 0 }; + + PHONGO_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_OBJECT_OF_CLASS(zkeyid, php_phongo_binary_ce) + Z_PARAM_STRING(keyaltname, keyaltname_len); + PHONGO_PARSE_PARAMETERS_END(); + + php_phongo_zval_to_bson_value(zkeyid, PHONGO_BSON_NONE, &keyid); + + if (EG(exception)) { + goto cleanup; + } + + if (!validate_keyid(&keyid)) { + /* Exception already thrown */ + goto cleanup; + } + + if (!mongoc_client_encryption_remove_key_alt_name(Z_CLIENTENCRYPTION_OBJ_P(getThis())->client_encryption, &keyid, keyaltname, &key_doc, &error)) { + phongo_throw_exception_from_bson_error_t(&error); + goto cleanup; + } + + RETVAL_OPTIONAL_BSON_T(key_doc); + +cleanup: + bson_value_destroy(&keyid); + bson_destroy(&key_doc); +} /* }}} */ + +/* {{{ proto object MongoDB\Driver\ClientEncryption::rewrapManyDataKey(array|object $filter[, array $options = array()]) + Decrypts multiple data keys and (re-)encrypts them with a new masterKey, or + with their current masterKey if a new one is not given. Returns an object + corresponding to the internal libmongoc result. */ +static PHP_METHOD(MongoDB_Driver_ClientEncryption, rewrapManyDataKey) +{ + zval* zfilter = NULL; + zval* options = NULL; + bson_t filter = BSON_INITIALIZER; + char* provider = NULL; + zend_bool free_provider = false; + bson_t* masterkey = NULL; + bson_error_t error = { 0 }; + + mongoc_client_encryption_rewrap_many_datakey_result_t* result = NULL; + + PHONGO_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_ARRAY_OR_OBJECT(zfilter) + Z_PARAM_OPTIONAL + Z_PARAM_ARRAY(options) + PHONGO_PARSE_PARAMETERS_END(); + + php_phongo_zval_to_bson(zfilter, PHONGO_BSON_NONE, &filter, NULL); + + if (EG(exception)) { + goto cleanup; + } + + if (options && php_array_existsc(options, "provider")) { + int provider_len; + + provider = php_array_fetchc_string(options, "provider", &provider_len, &free_provider); + } + + if (options && php_array_existsc(options, "masterKey")) { + masterkey = bson_new(); + + php_phongo_zval_to_bson(php_array_fetchc(options, "masterKey"), PHONGO_BSON_NONE, masterkey, NULL); + + if (EG(exception)) { + goto cleanup; + } + } + + result = mongoc_client_encryption_rewrap_many_datakey_result_new(); + + if (!mongoc_client_encryption_rewrap_many_datakey(Z_CLIENTENCRYPTION_OBJ_P(getThis())->client_encryption, &filter, provider, masterkey, result, &error)) { + phongo_throw_exception_from_bson_error_t(&error); + goto cleanup; + } + + RETVAL_NULL(); + + /* TODO: mongoc_client_encryption_rewrap_many_datakey_result_t return value */ + +cleanup: + if (free_provider) { + efree(provider); + } + + bson_destroy(&filter); + bson_destroy(masterkey); + mongoc_client_encryption_rewrap_many_datakey_result_destroy(result); +} /* }}} */ + PHONGO_DISABLED_WAKEUP(MongoDB_Driver_ClientEncryption) /* {{{ MongoDB\Driver\ClientEncryption object handlers */ @@ -126,6 +454,10 @@ static void php_phongo_clientencryption_free_object(zend_object* object) /* {{{ if (!Z_ISUNDEF(intern->key_vault_client_manager)) { zval_ptr_dtor(&intern->key_vault_client_manager); } + + if (intern->key_vault_namespace) { + efree(intern->key_vault_namespace); + } } /* }}} */ static zend_object* php_phongo_clientencryption_create_object(zend_class_entry* class_type) /* {{{ */ @@ -202,19 +534,19 @@ static mongoc_client_encryption_opts_t* phongo_clientencryption_opts_from_zval(z } if (php_array_existsc(options, "keyVaultNamespace")) { - char* keyvault_namespace; + char* key_vault_namespace; char* db_name; char* coll_name; int plen; zend_bool pfree; - keyvault_namespace = php_array_fetchc_string(options, "keyVaultNamespace", &plen, &pfree); + key_vault_namespace = php_array_fetchc_string(options, "keyVaultNamespace", &plen, &pfree); - if (!phongo_split_namespace(keyvault_namespace, &db_name, &coll_name)) { + if (!phongo_split_namespace(key_vault_namespace, &db_name, &coll_name)) { phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT, "Expected \"keyVaultNamespace\" option to contain a full collection namespace"); if (pfree) { - efree(keyvault_namespace); + efree(key_vault_namespace); } goto cleanup; @@ -225,7 +557,7 @@ static mongoc_client_encryption_opts_t* phongo_clientencryption_opts_from_zval(z efree(coll_name); if (pfree) { - efree(keyvault_namespace); + efree(key_vault_namespace); } } @@ -305,6 +637,23 @@ void phongo_clientencryption_init(php_phongo_clientencryption_t* intern, zval* o ZVAL_ZVAL(&intern->key_vault_client_manager, key_vault_client_manager, 1, 0); } + /* Copy the key vault namespace, since it may be referenced later by + * ClientEncryption::getKeys(). The namespace will already have been + * validated by phongo_clientencryption_opts_from_zval. */ + if (php_array_existsc(options, "keyVaultNamespace")) { + char* key_vault_namespace; + int plen; + zend_bool pfree; + + key_vault_namespace = php_array_fetchc_string(options, "keyVaultNamespace", &plen, &pfree); + + intern->key_vault_namespace = estrdup(key_vault_namespace); + + if (pfree) { + efree(key_vault_namespace); + } + } + cleanup: if (opts) { mongoc_client_encryption_opts_destroy(opts); @@ -388,7 +737,7 @@ static mongoc_client_encryption_datakey_opts_t* phongo_clientencryption_datakey_ if (Z_TYPE_P(keyMaterial) != IS_OBJECT || !instanceof_function(Z_OBJCE_P(keyMaterial), php_phongo_binary_ce)) { phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT, "Expected \"keyMaterial\" option to be %s, %s given", ZSTR_VAL(php_phongo_binary_ce->name), PHONGO_ZVAL_CLASS_OR_TYPE_NAME_P(keyMaterial)); - return false; + goto cleanup; } mongoc_client_encryption_datakey_opts_set_keymaterial(opts, (uint8_t*) Z_BINARY_OBJ_P(keyMaterial)->data, Z_BINARY_OBJ_P(keyMaterial)->data_len); diff --git a/src/MongoDB/ClientEncryption.stub.php b/src/MongoDB/ClientEncryption.stub.php index 76d3ca8ed..985737220 100644 --- a/src/MongoDB/ClientEncryption.stub.php +++ b/src/MongoDB/ClientEncryption.stub.php @@ -41,6 +41,8 @@ final class ClientEncryption final public function __construct(array $options) {} + final public function addKeyAltName(\MongoDB\BSON\Binary $keyId, string $keyAltName): ?object {} + final public function createDataKey(string $kmsProvider, ?array $options = null): \MongoDB\BSON\Binary {} #if PHP_VERSION_ID >= 80000 @@ -50,6 +52,8 @@ final public function decrypt(\MongoDB\BSON\Binary $value): mixed {} final public function decrypt(\MongoDB\BSON\Binary $value) {} #endif + final public function deleteKey(\MongoDB\BSON\Binary $keyId): object {} + #if PHP_VERSION_ID >= 80000 final public function encrypt(mixed $value, ?array $options = null): \MongoDB\BSON\Binary {} #else @@ -57,5 +61,15 @@ final public function encrypt(mixed $value, ?array $options = null): \MongoDB\BS final public function encrypt($value, ?array $options = null): \MongoDB\BSON\Binary {} #endif + final public function getKey(\MongoDB\BSON\Binary $keyId): ?object {} + + final public function getKeyByAltName(string $keyAltName): ?object {} + + final public function getKeys(): \MongoDB\Driver\Cursor {} + + final public function removeKeyAltName(\MongoDB\BSON\Binary $keyId, string $keyAltName): ?object {} + + final public function rewrapManyDataKey(array|object $filter, array $options = []): object {} + final public function __wakeup(): void {} } diff --git a/src/MongoDB/ClientEncryption_arginfo.h b/src/MongoDB/ClientEncryption_arginfo.h index f0359a6d3..217c88c14 100644 --- a/src/MongoDB/ClientEncryption_arginfo.h +++ b/src/MongoDB/ClientEncryption_arginfo.h @@ -1,10 +1,15 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 09ec24f8af5aba4c88dc3ee4bcc77022d7cc0b9c */ + * Stub hash: 8a6eece2c09efd48a988f3acee25a1e0e9016298 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_MongoDB_Driver_ClientEncryption___construct, 0, 0, 1) ZEND_ARG_TYPE_INFO(0, options, IS_ARRAY, 0) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_MongoDB_Driver_ClientEncryption_addKeyAltName, 0, 2, IS_OBJECT, 1) + ZEND_ARG_OBJ_INFO(0, keyId, MongoDB\\BSON\\Binary, 0) + ZEND_ARG_TYPE_INFO(0, keyAltName, IS_STRING, 0) +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_MongoDB_Driver_ClientEncryption_createDataKey, 0, 1, MongoDB\\BSON\\Binary, 0) ZEND_ARG_TYPE_INFO(0, kmsProvider, IS_STRING, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null") @@ -22,6 +27,10 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_MongoDB_Driver_ClientEncryption_decrypt, 0, ZEND_END_ARG_INFO() #endif +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_MongoDB_Driver_ClientEncryption_deleteKey, 0, 1, IS_OBJECT, 0) + ZEND_ARG_OBJ_INFO(0, keyId, MongoDB\\BSON\\Binary, 0) +ZEND_END_ARG_INFO() + #if PHP_VERSION_ID >= 80000 ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_MongoDB_Driver_ClientEncryption_encrypt, 0, 1, MongoDB\\BSON\\Binary, 0) ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0) @@ -36,11 +45,30 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_MongoDB_Driver_ClientEncryp ZEND_END_ARG_INFO() #endif +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_MongoDB_Driver_ClientEncryption_getKey, 0, 1, IS_OBJECT, 1) + ZEND_ARG_OBJ_INFO(0, keyId, MongoDB\\BSON\\Binary, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_MongoDB_Driver_ClientEncryption_getKeyByAltName, 0, 1, IS_OBJECT, 1) + ZEND_ARG_TYPE_INFO(0, keyAltName, IS_STRING, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_MongoDB_Driver_ClientEncryption_getKeys, 0, 0, MongoDB\\Driver\\Cursor, 0) +ZEND_END_ARG_INFO() + +#define arginfo_class_MongoDB_Driver_ClientEncryption_removeKeyAltName arginfo_class_MongoDB_Driver_ClientEncryption_addKeyAltName + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_MongoDB_Driver_ClientEncryption_rewrapManyDataKey, 0, 1, IS_OBJECT, 0) + ZEND_ARG_TYPE_MASK(0, filter, MAY_BE_ARRAY|MAY_BE_OBJECT, NULL) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "[]") +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_MongoDB_Driver_ClientEncryption___wakeup, 0, 0, IS_VOID, 0) ZEND_END_ARG_INFO() static ZEND_METHOD(MongoDB_Driver_ClientEncryption, __construct); +static ZEND_METHOD(MongoDB_Driver_ClientEncryption, addKeyAltName); static ZEND_METHOD(MongoDB_Driver_ClientEncryption, createDataKey); #if PHP_VERSION_ID >= 80000 static ZEND_METHOD(MongoDB_Driver_ClientEncryption, decrypt); @@ -48,17 +76,24 @@ static ZEND_METHOD(MongoDB_Driver_ClientEncryption, decrypt); #if !(PHP_VERSION_ID >= 80000) static ZEND_METHOD(MongoDB_Driver_ClientEncryption, decrypt); #endif +static ZEND_METHOD(MongoDB_Driver_ClientEncryption, deleteKey); #if PHP_VERSION_ID >= 80000 static ZEND_METHOD(MongoDB_Driver_ClientEncryption, encrypt); #endif #if !(PHP_VERSION_ID >= 80000) static ZEND_METHOD(MongoDB_Driver_ClientEncryption, encrypt); #endif +static ZEND_METHOD(MongoDB_Driver_ClientEncryption, getKey); +static ZEND_METHOD(MongoDB_Driver_ClientEncryption, getKeyByAltName); +static ZEND_METHOD(MongoDB_Driver_ClientEncryption, getKeys); +static ZEND_METHOD(MongoDB_Driver_ClientEncryption, removeKeyAltName); +static ZEND_METHOD(MongoDB_Driver_ClientEncryption, rewrapManyDataKey); static ZEND_METHOD(MongoDB_Driver_ClientEncryption, __wakeup); static const zend_function_entry class_MongoDB_Driver_ClientEncryption_methods[] = { ZEND_ME(MongoDB_Driver_ClientEncryption, __construct, arginfo_class_MongoDB_Driver_ClientEncryption___construct, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL) + ZEND_ME(MongoDB_Driver_ClientEncryption, addKeyAltName, arginfo_class_MongoDB_Driver_ClientEncryption_addKeyAltName, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL) ZEND_ME(MongoDB_Driver_ClientEncryption, createDataKey, arginfo_class_MongoDB_Driver_ClientEncryption_createDataKey, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL) #if PHP_VERSION_ID >= 80000 ZEND_ME(MongoDB_Driver_ClientEncryption, decrypt, arginfo_class_MongoDB_Driver_ClientEncryption_decrypt, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL) @@ -66,12 +101,18 @@ static const zend_function_entry class_MongoDB_Driver_ClientEncryption_methods[] #if !(PHP_VERSION_ID >= 80000) ZEND_ME(MongoDB_Driver_ClientEncryption, decrypt, arginfo_class_MongoDB_Driver_ClientEncryption_decrypt, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL) #endif + ZEND_ME(MongoDB_Driver_ClientEncryption, deleteKey, arginfo_class_MongoDB_Driver_ClientEncryption_deleteKey, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL) #if PHP_VERSION_ID >= 80000 ZEND_ME(MongoDB_Driver_ClientEncryption, encrypt, arginfo_class_MongoDB_Driver_ClientEncryption_encrypt, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL) #endif #if !(PHP_VERSION_ID >= 80000) ZEND_ME(MongoDB_Driver_ClientEncryption, encrypt, arginfo_class_MongoDB_Driver_ClientEncryption_encrypt, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL) #endif + ZEND_ME(MongoDB_Driver_ClientEncryption, getKey, arginfo_class_MongoDB_Driver_ClientEncryption_getKey, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL) + ZEND_ME(MongoDB_Driver_ClientEncryption, getKeyByAltName, arginfo_class_MongoDB_Driver_ClientEncryption_getKeyByAltName, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL) + ZEND_ME(MongoDB_Driver_ClientEncryption, getKeys, arginfo_class_MongoDB_Driver_ClientEncryption_getKeys, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL) + ZEND_ME(MongoDB_Driver_ClientEncryption, removeKeyAltName, arginfo_class_MongoDB_Driver_ClientEncryption_removeKeyAltName, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL) + ZEND_ME(MongoDB_Driver_ClientEncryption, rewrapManyDataKey, arginfo_class_MongoDB_Driver_ClientEncryption_rewrapManyDataKey, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL) ZEND_ME(MongoDB_Driver_ClientEncryption, __wakeup, arginfo_class_MongoDB_Driver_ClientEncryption___wakeup, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL) ZEND_FE_END }; diff --git a/src/MongoDB/Cursor.c b/src/MongoDB/Cursor.c index 573346147..f19e5cd13 100644 --- a/src/MongoDB/Cursor.c +++ b/src/MongoDB/Cursor.c @@ -22,6 +22,7 @@ #include "phongo_bson.h" #include "phongo_client.h" #include "phongo_error.h" +#include "phongo_util.h" #include "MongoDB/Cursor.h" #include "MongoDB/Server.h" @@ -262,7 +263,7 @@ static PHP_METHOD(MongoDB_Driver_Cursor, rewind) PHONGO_PARSE_PARAMETERS_NONE(); - /* If the intern was never advanced (e.g. command intern), do so now */ + /* If the cursor was never advanced (e.g. command cursor), do so now */ if (!intern->advanced) { intern->advanced = true; @@ -445,6 +446,78 @@ void php_phongo_cursor_init_ce(INIT_FUNC_ARGS) /* {{{ */ php_phongo_handler_cursor.offset = XtOffsetOf(php_phongo_cursor_t, std); } /* }}} */ +static void phongo_cursor_init(zval* return_value, zval* manager, mongoc_cursor_t* cursor, zval* readPreference, zval* session) /* {{{ */ +{ + php_phongo_cursor_t* intern; + + object_init_ex(return_value, php_phongo_cursor_ce); + + intern = Z_CURSOR_OBJ_P(return_value); + intern->cursor = cursor; + intern->server_id = mongoc_cursor_get_hint(cursor); + intern->advanced = false; + intern->current = 0; + + ZVAL_ZVAL(&intern->manager, manager, 1, 0); + + if (readPreference) { + ZVAL_ZVAL(&intern->read_preference, readPreference, 1, 0); + } + + if (session) { + ZVAL_ZVAL(&intern->session, session, 1, 0); + } +} /* }}} */ + +/* Initialize the cursor for a query and return whether there is an error. This + * function always returns true. */ +bool phongo_cursor_init_for_command(zval* return_value, zval* manager, mongoc_cursor_t* cursor, const char* db, zval* command, zval* readPreference, zval* session) /* {{{ */ +{ + php_phongo_cursor_t* intern; + + phongo_cursor_init(return_value, manager, cursor, readPreference, session); + + intern = Z_CURSOR_OBJ_P(return_value); + intern->database = estrdup(db); + + ZVAL_ZVAL(&intern->command, command, 1, 0); + + return true; +} /* }}} */ + +/* Initialize the cursor for a query and return whether there is an error. The + * libmongoc cursor will be advanced once. On error, false is returned and an + * exception is thrown. */ +bool phongo_cursor_init_for_query(zval* return_value, zval* manager, mongoc_cursor_t* cursor, const char* namespace, zval* query, zval* readPreference, zval* session) /* {{{ */ +{ + php_phongo_cursor_t* intern; + + /* Advancing the cursor before phongo_cursor_init ensures that a server + * stream is obtained before mongoc_cursor_get_hint() is called. */ + if (!phongo_cursor_advance_and_check_for_error(cursor)) { + /* Exception should already have been thrown */ + return false; + } + + phongo_cursor_init(return_value, manager, cursor, readPreference, session); + + intern = Z_CURSOR_OBJ_P(return_value); + intern->advanced = true; + + /* The namespace should already have been validated, but we'll still check + * for an error and throw accordingly. */ + if (!phongo_split_namespace(namespace, &intern->database, &intern->collection)) { + phongo_throw_exception(PHONGO_ERROR_UNEXPECTED_VALUE, "Cannot initialize cursor with invalid namespace: %s", namespace); + zval_ptr_dtor(return_value); + + return false; + } + + ZVAL_ZVAL(&intern->query, query, 1, 0); + + return true; +} /* }}} */ + /* Advance the cursor and return whether there is an error. On error, false is * returned and an exception is thrown. */ bool phongo_cursor_advance_and_check_for_error(mongoc_cursor_t* cursor) /* {{{ */ diff --git a/src/MongoDB/Cursor.h b/src/MongoDB/Cursor.h index 5a7db233a..ac8681411 100644 --- a/src/MongoDB/Cursor.h +++ b/src/MongoDB/Cursor.h @@ -19,6 +19,11 @@ #include "mongoc/mongoc.h" +#include + +bool phongo_cursor_init_for_command(zval* return_value, zval* manager, mongoc_cursor_t* cursor, const char* db, zval* command, zval* readPreference, zval* session); +bool phongo_cursor_init_for_query(zval* return_value, zval* manager, mongoc_cursor_t* cursor, const char* namespace, zval* query, zval* readPreference, zval* session); + bool phongo_cursor_advance_and_check_for_error(mongoc_cursor_t* cursor); #endif /* PHONGO_CURSOR_H */ diff --git a/src/MongoDB/Query.c b/src/MongoDB/Query.c index ca611ecf8..1d035d94a 100644 --- a/src/MongoDB/Query.c +++ b/src/MongoDB/Query.c @@ -26,6 +26,7 @@ #include "phongo_bson_encode.h" #include "phongo_error.h" +#include "MongoDB/Query.h" #include "MongoDB/ReadConcern.h" #include "Query_arginfo.h" @@ -285,18 +286,34 @@ static bool php_phongo_query_init_max_await_time_ms(php_phongo_query_t* intern, return true; } /* }}} */ -/* Initializes the php_phongo_query_t from filter and options arguments. This - * function will fall back to a modifier in the absence of a top-level option - * (where applicable). */ -static bool php_phongo_query_init(php_phongo_query_t* intern, zval* filter, zval* options) /* {{{ */ +/* Initializes the query from filter and options arguments and returns whether + * an error occurred. If query is undefined, it will be initialized. + * + * This function will fall back to a modifier in the absence of a top-level + * option (where applicable). */ +bool phongo_query_init(zval* return_value, zval* filter, zval* options) /* {{{ */ { - zval* modifiers = NULL; + php_phongo_query_t* intern; + zval* modifiers = NULL; + + if (Z_ISUNDEF_P(return_value)) { + object_init_ex(return_value, php_phongo_query_ce); + } + + if (Z_TYPE_P(return_value) != IS_OBJECT || !instanceof_function(Z_OBJCE_P(return_value), php_phongo_query_ce)) { + phongo_throw_exception(PHONGO_ERROR_UNEXPECTED_VALUE, "Expected initialization object to be %s, %s given", ZSTR_VAL(php_phongo_query_ce->name), PHONGO_ZVAL_CLASS_OR_TYPE_NAME_P(return_value)); + return false; + } + + intern = Z_QUERY_OBJ_P(return_value); intern->filter = bson_new(); intern->opts = bson_new(); intern->max_await_time_ms = 0; - php_phongo_zval_to_bson(filter, PHONGO_BSON_NONE, intern->filter, NULL); + if (filter) { + php_phongo_zval_to_bson(filter, PHONGO_BSON_NONE, intern->filter, NULL); + } /* Note: if any exceptions are thrown, we can simply return as PHP will * invoke php_phongo_query_free_object to destruct the object. */ @@ -394,11 +411,8 @@ PHONGO_DISABLED_WAKEUP(MongoDB_Driver_Query) Constructs a new Query */ static PHP_METHOD(MongoDB_Driver_Query, __construct) { - php_phongo_query_t* intern; - zval* filter; - zval* options = NULL; - - intern = Z_QUERY_OBJ_P(getThis()); + zval* filter; + zval* options = NULL; PHONGO_PARSE_PARAMETERS_START(1, 2) PHONGO_PARAM_ARRAY_OR_OBJECT(filter) @@ -406,7 +420,7 @@ static PHP_METHOD(MongoDB_Driver_Query, __construct) Z_PARAM_ARRAY_OR_NULL(options) PHONGO_PARSE_PARAMETERS_END(); - php_phongo_query_init(intern, filter, options); + phongo_query_init(getThis(), filter, options); } /* }}} */ /* {{{ MongoDB\Driver\Query object handlers */ diff --git a/src/MongoDB/Query.h b/src/MongoDB/Query.h new file mode 100644 index 000000000..983e37f66 --- /dev/null +++ b/src/MongoDB/Query.h @@ -0,0 +1,24 @@ +/* + * Copyright 2022-present MongoDB, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PHONGO_QUERY_H +#define PHONGO_QUERY_H + +#include + +bool phongo_query_init(zval* return_value, zval* filter, zval* options); + +#endif /* PHONGO_QUERY_H */ diff --git a/src/phongo_client.c b/src/phongo_client.c index a7017d2ea..24a17810f 100644 --- a/src/phongo_client.c +++ b/src/phongo_client.c @@ -67,58 +67,6 @@ static mongoc_uri_t* php_phongo_make_uri(const char* uri_string) /* {{{ */ return uri; } /* }}} */ -static const char* php_phongo_bson_type_to_string(bson_type_t type) /* {{{ */ -{ - switch (type) { - case BSON_TYPE_EOD: - return "EOD"; - case BSON_TYPE_DOUBLE: - return "double"; - case BSON_TYPE_UTF8: - return "string"; - case BSON_TYPE_DOCUMENT: - return "document"; - case BSON_TYPE_ARRAY: - return "array"; - case BSON_TYPE_BINARY: - return "Binary"; - case BSON_TYPE_UNDEFINED: - return "undefined"; - case BSON_TYPE_OID: - return "ObjectId"; - case BSON_TYPE_BOOL: - return "boolean"; - case BSON_TYPE_DATE_TIME: - return "UTCDateTime"; - case BSON_TYPE_NULL: - return "null"; - case BSON_TYPE_REGEX: - return "Regex"; - case BSON_TYPE_DBPOINTER: - return "DBPointer"; - case BSON_TYPE_CODE: - return "Javascript"; - case BSON_TYPE_SYMBOL: - return "symbol"; - case BSON_TYPE_CODEWSCOPE: - return "Javascript with scope"; - case BSON_TYPE_INT32: - return "32-bit integer"; - case BSON_TYPE_TIMESTAMP: - return "Timestamp"; - case BSON_TYPE_INT64: - return "64-bit integer"; - case BSON_TYPE_DECIMAL128: - return "Decimal128"; - case BSON_TYPE_MAXKEY: - return "MaxKey"; - case BSON_TYPE_MINKEY: - return "MinKey"; - default: - return "unknown"; - } -} /* }}} */ - #define PHONGO_URI_INVALID_TYPE(iter, expected) \ phongo_throw_exception( \ PHONGO_ERROR_INVALID_ARGUMENT, \ diff --git a/src/phongo_execute.c b/src/phongo_execute.c index 8c695b3c1..a8dea90e7 100644 --- a/src/phongo_execute.c +++ b/src/phongo_execute.c @@ -32,58 +32,6 @@ #include "MongoDB/Session.h" #include "MongoDB/WriteResult.h" -static void phongo_cursor_init(zval* return_value, zval* manager, mongoc_cursor_t* cursor, zval* readPreference, zval* session) /* {{{ */ -{ - php_phongo_cursor_t* intern; - - object_init_ex(return_value, php_phongo_cursor_ce); - - intern = Z_CURSOR_OBJ_P(return_value); - intern->cursor = cursor; - intern->server_id = mongoc_cursor_get_hint(cursor); - intern->advanced = false; - intern->current = 0; - - ZVAL_ZVAL(&intern->manager, manager, 1, 0); - - if (readPreference) { - ZVAL_ZVAL(&intern->read_preference, readPreference, 1, 0); - } - - if (session) { - ZVAL_ZVAL(&intern->session, session, 1, 0); - } -} /* }}} */ - -static void phongo_cursor_init_for_command(zval* return_value, zval* manager, mongoc_cursor_t* cursor, const char* db, zval* command, zval* readPreference, zval* session) /* {{{ */ -{ - php_phongo_cursor_t* intern; - - phongo_cursor_init(return_value, manager, cursor, readPreference, session); - intern = Z_CURSOR_OBJ_P(return_value); - - intern->database = estrdup(db); - - ZVAL_ZVAL(&intern->command, command, 1, 0); -} /* }}} */ - -static void phongo_cursor_init_for_query(zval* return_value, zval* manager, mongoc_cursor_t* cursor, const char* namespace, zval* query, zval* readPreference, zval* session) /* {{{ */ -{ - php_phongo_cursor_t* intern; - - phongo_cursor_init(return_value, manager, cursor, readPreference, session); - intern = Z_CURSOR_OBJ_P(return_value); - - /* namespace has already been validated by phongo_execute_query() */ - phongo_split_namespace(namespace, &intern->database, &intern->collection); - - /* cursor has already been advanced by phongo_execute_query() calling - * phongo_cursor_advance_and_check_for_error() */ - intern->advanced = true; - - ZVAL_ZVAL(&intern->query, query, 1, 0); -} /* }}} */ - static bson_t* create_wrapped_command_envelope(const char* db, bson_t* reply) { bson_t* tmp; @@ -615,12 +563,12 @@ bool phongo_execute_query(zval* manager, const char* namespace, zval* zquery, zv mongoc_cursor_set_max_await_time_ms(cursor, query->max_await_time_ms); } - if (!phongo_cursor_advance_and_check_for_error(cursor)) { + /* Initialize the cursor and advance it once */ + if (!phongo_cursor_init_for_query(return_value, manager, cursor, namespace, zquery, zreadPreference, zsession)) { + /* Exception should already have been thrown */ mongoc_cursor_destroy(cursor); return false; } - phongo_cursor_init_for_query(return_value, manager, cursor, namespace, zquery, zreadPreference, zsession); - return true; } /* }}} */ diff --git a/src/phongo_structs.h b/src/phongo_structs.h index db7306df9..1ae866e78 100644 --- a/src/phongo_structs.h +++ b/src/phongo_structs.h @@ -41,6 +41,7 @@ typedef struct { typedef struct { mongoc_client_encryption_t* client_encryption; zval key_vault_client_manager; + char* key_vault_namespace; zend_object std; } php_phongo_clientencryption_t; diff --git a/src/phongo_util.c b/src/phongo_util.c index cc4f168b2..2feb6352b 100644 --- a/src/phongo_util.c +++ b/src/phongo_util.c @@ -21,6 +21,58 @@ #include "phongo_util.h" +const char* php_phongo_bson_type_to_string(bson_type_t type) /* {{{ */ +{ + switch (type) { + case BSON_TYPE_EOD: + return "EOD"; + case BSON_TYPE_DOUBLE: + return "double"; + case BSON_TYPE_UTF8: + return "string"; + case BSON_TYPE_DOCUMENT: + return "document"; + case BSON_TYPE_ARRAY: + return "array"; + case BSON_TYPE_BINARY: + return "Binary"; + case BSON_TYPE_UNDEFINED: + return "undefined"; + case BSON_TYPE_OID: + return "ObjectId"; + case BSON_TYPE_BOOL: + return "boolean"; + case BSON_TYPE_DATE_TIME: + return "UTCDateTime"; + case BSON_TYPE_NULL: + return "null"; + case BSON_TYPE_REGEX: + return "Regex"; + case BSON_TYPE_DBPOINTER: + return "DBPointer"; + case BSON_TYPE_CODE: + return "Javascript"; + case BSON_TYPE_SYMBOL: + return "symbol"; + case BSON_TYPE_CODEWSCOPE: + return "Javascript with scope"; + case BSON_TYPE_INT32: + return "32-bit integer"; + case BSON_TYPE_TIMESTAMP: + return "Timestamp"; + case BSON_TYPE_INT64: + return "64-bit integer"; + case BSON_TYPE_DECIMAL128: + return "Decimal128"; + case BSON_TYPE_MAXKEY: + return "MaxKey"; + case BSON_TYPE_MINKEY: + return "MinKey"; + default: + return "unknown"; + } +} /* }}} */ + /* If options is not an array, insert it as a field in a newly allocated array. * This may be used to convert legacy options (e.g. ReadPreference option for * an executeQuery method) into an options array. diff --git a/src/phongo_util.h b/src/phongo_util.h index 419144804..1d61be4d2 100644 --- a/src/phongo_util.h +++ b/src/phongo_util.h @@ -17,6 +17,10 @@ #ifndef PHONGO_UTIL_H #define PHONGO_UTIL_H +#include "bson/bson.h" + +const char* php_phongo_bson_type_to_string(bson_type_t type); + zval* php_phongo_prep_legacy_option(zval* options, const char* key, bool* allocated); void php_phongo_prep_legacy_option_free(zval* options); diff --git a/tests/clientEncryption/clientEncryption-addKeyAltName-001.phpt b/tests/clientEncryption/clientEncryption-addKeyAltName-001.phpt new file mode 100644 index 000000000..5107b1d41 --- /dev/null +++ b/tests/clientEncryption/clientEncryption-addKeyAltName-001.phpt @@ -0,0 +1,40 @@ +--TEST-- +MongoDB\Driver\ClientEncryption::addKeyAltName() +--SKIPIF-- + + + +createClientEncryption([ + 'keyVaultNamespace' => CSFLE_KEY_VAULT_NS, + 'kmsProviders' => ['local' => ['key' => new MongoDB\BSON\Binary(CSFLE_LOCAL_KEY, 0)]], +]); + +$keyId = $clientEncryption->createDataKey('local'); +$keyBeforeUpdate = $clientEncryption->addKeyAltName($keyId, 'foo'); +$keyAfterUpdate = $clientEncryption->getKey($keyId); + +var_dump($keyBeforeUpdate->_id == $keyId); +var_dump(empty($keyBeforeUpdate->keyAltNames)); + +var_dump($keyAfterUpdate->_id == $keyId); +var_dump(isset($keyAfterUpdate->keyAltNames) && is_array($keyAfterUpdate->keyAltNames)); +var_dump(in_array('foo', $keyAfterUpdate->keyAltNames)); + +?> +===DONE=== + +--EXPECT-- +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +===DONE=== diff --git a/tests/clientEncryption/clientEncryption-addKeyAltName-002.phpt b/tests/clientEncryption/clientEncryption-addKeyAltName-002.phpt new file mode 100644 index 000000000..7a618014d --- /dev/null +++ b/tests/clientEncryption/clientEncryption-addKeyAltName-002.phpt @@ -0,0 +1,29 @@ +--TEST-- +MongoDB\Driver\ClientEncryption::addKeyAltName() when key does not exist +--SKIPIF-- + + + +createClientEncryption([ + 'keyVaultNamespace' => CSFLE_KEY_VAULT_NS, + 'kmsProviders' => ['local' => ['key' => new MongoDB\BSON\Binary(CSFLE_LOCAL_KEY, 0)]], +]); + +$keyId = new MongoDB\BSON\Binary(random_bytes(16), MongoDB\BSON\Binary::TYPE_UUID); + +var_dump($clientEncryption->addKeyAltName($keyId, 'foo')); + +?> +===DONE=== + +--EXPECT-- +NULL +===DONE=== diff --git a/tests/clientEncryption/clientEncryption-addKeyAltName_error-001.phpt b/tests/clientEncryption/clientEncryption-addKeyAltName_error-001.phpt new file mode 100644 index 000000000..2aadb5c69 --- /dev/null +++ b/tests/clientEncryption/clientEncryption-addKeyAltName_error-001.phpt @@ -0,0 +1,31 @@ +--TEST-- +MongoDB\Driver\ClientEncryption::addKeyAltName() with invalid keyId +--SKIPIF-- + + + +--FILE-- +createClientEncryption([ + 'keyVaultNamespace' => CSFLE_KEY_VAULT_NS, + 'kmsProviders' => ['local' => ['key' => new MongoDB\BSON\Binary(CSFLE_LOCAL_KEY, 0)]], +]); + +$invalidKeyId = new MongoDB\BSON\Binary('', MongoDB\BSON\Binary::TYPE_GENERIC); + +echo throws(function () use ($clientEncryption, $invalidKeyId) { + $clientEncryption->addKeyAltName($invalidKeyId, 'foo'); +}, MongoDB\Driver\Exception\InvalidArgumentException::class), "\n"; + +?> +===DONE=== + +--EXPECT-- +OK: Got MongoDB\Driver\Exception\InvalidArgumentException +Expected keyid to have UUID Binary subtype (4), 0 given +===DONE=== diff --git a/tests/clientEncryption/clientEncryption-createDataKey-001.phpt b/tests/clientEncryption/clientEncryption-createDataKey-001.phpt index 4c4e78a6b..bfa29fd3d 100644 --- a/tests/clientEncryption/clientEncryption-createDataKey-001.phpt +++ b/tests/clientEncryption/clientEncryption-createDataKey-001.phpt @@ -17,7 +17,13 @@ $clientEncryption = $manager->createClientEncryption([ 'kmsProviders' => ['local' => ['key' => new MongoDB\BSON\Binary(CSFLE_LOCAL_KEY, 0)]], ]); -var_dump($clientEncryption->createDataKey('local')); +$keyId = $clientEncryption->createDataKey('local'); + +var_dump($keyId); + +$key = $clientEncryption->getKey($keyId); + +var_dump($key->_id == $keyId); ?> ===DONE=== @@ -29,4 +35,5 @@ object(MongoDB\BSON\Binary)#%d (%d) { ["type"]=> int(4) } +bool(true) ===DONE=== diff --git a/tests/clientEncryption/clientEncryption-createDataKey-002.phpt b/tests/clientEncryption/clientEncryption-createDataKey-002.phpt new file mode 100644 index 000000000..4e94b56d1 --- /dev/null +++ b/tests/clientEncryption/clientEncryption-createDataKey-002.phpt @@ -0,0 +1,34 @@ +--TEST-- +MongoDB\Driver\ClientEncryption::createDataKey() "keyAltNames" option +--SKIPIF-- + + + +createClientEncryption([ + 'keyVaultNamespace' => CSFLE_KEY_VAULT_NS, + 'kmsProviders' => ['local' => ['key' => new MongoDB\BSON\Binary(CSFLE_LOCAL_KEY, 0)]], +]); + +$keyId = $clientEncryption->createDataKey('local', ['keyAltNames' => ['foo', 'bar']]); +$key = $clientEncryption->getKey($keyId); + +var_dump($key->_id == $keyId); +var_dump(in_array('foo', $key->keyAltNames)); +var_dump(in_array('bar', $key->keyAltNames)); + +?> +===DONE=== + +--EXPECT-- +bool(true) +bool(true) +bool(true) +===DONE=== diff --git a/tests/clientEncryption/clientEncryption-deleteKey-001.phpt b/tests/clientEncryption/clientEncryption-deleteKey-001.phpt new file mode 100644 index 000000000..74c648ba0 --- /dev/null +++ b/tests/clientEncryption/clientEncryption-deleteKey-001.phpt @@ -0,0 +1,32 @@ +--TEST-- +MongoDB\Driver\ClientEncryption::deleteKey() +--SKIPIF-- + + + +createClientEncryption([ + 'keyVaultNamespace' => CSFLE_KEY_VAULT_NS, + 'kmsProviders' => ['local' => ['key' => new MongoDB\BSON\Binary(CSFLE_LOCAL_KEY, 0)]], +]); + +$keyId = $clientEncryption->createDataKey('local'); + +var_dump($clientEncryption->deleteKey($keyId)); + +?> +===DONE=== + +--EXPECTF-- +object(stdClass)#%d (%d) { + ["deletedCount"]=> + int(1) +} +===DONE=== diff --git a/tests/clientEncryption/clientEncryption-deleteKey-002.phpt b/tests/clientEncryption/clientEncryption-deleteKey-002.phpt new file mode 100644 index 000000000..1661a3a42 --- /dev/null +++ b/tests/clientEncryption/clientEncryption-deleteKey-002.phpt @@ -0,0 +1,32 @@ +--TEST-- +MongoDB\Driver\ClientEncryption::deleteKey() when key does not exist +--SKIPIF-- + + + +createClientEncryption([ + 'keyVaultNamespace' => CSFLE_KEY_VAULT_NS, + 'kmsProviders' => ['local' => ['key' => new MongoDB\BSON\Binary(CSFLE_LOCAL_KEY, 0)]], +]); + +$keyId = new MongoDB\BSON\Binary(random_bytes(16), MongoDB\BSON\Binary::TYPE_UUID); + +var_dump($clientEncryption->deleteKey($keyId)); + +?> +===DONE=== + +--EXPECTF-- +object(stdClass)#%d (%d) { + ["deletedCount"]=> + int(0) +} +===DONE=== diff --git a/tests/clientEncryption/clientEncryption-deleteKey_error-001.phpt b/tests/clientEncryption/clientEncryption-deleteKey_error-001.phpt new file mode 100644 index 000000000..14c3acd41 --- /dev/null +++ b/tests/clientEncryption/clientEncryption-deleteKey_error-001.phpt @@ -0,0 +1,32 @@ +--TEST-- +MongoDB\Driver\ClientEncryption::deleteKey() with invalid keyId +--SKIPIF-- + + + +createClientEncryption([ + 'keyVaultNamespace' => CSFLE_KEY_VAULT_NS, + 'kmsProviders' => ['local' => ['key' => new MongoDB\BSON\Binary(CSFLE_LOCAL_KEY, 0)]], +]); + +$invalidKeyId = new MongoDB\BSON\Binary('', MongoDB\BSON\Binary::TYPE_GENERIC); + +echo throws(function () use ($clientEncryption, $invalidKeyId) { + $clientEncryption->deleteKey($invalidKeyId); +}, MongoDB\Driver\Exception\InvalidArgumentException::class), "\n"; + +?> +===DONE=== + +--EXPECT-- +OK: Got MongoDB\Driver\Exception\InvalidArgumentException +Expected keyid to have UUID Binary subtype (4), 0 given +===DONE=== diff --git a/tests/clientEncryption/clientEncryption-getKey-001.phpt b/tests/clientEncryption/clientEncryption-getKey-001.phpt new file mode 100644 index 000000000..7599f4061 --- /dev/null +++ b/tests/clientEncryption/clientEncryption-getKey-001.phpt @@ -0,0 +1,30 @@ +--TEST-- +MongoDB\Driver\ClientEncryption::getKey() +--SKIPIF-- + + + +createClientEncryption([ + 'keyVaultNamespace' => CSFLE_KEY_VAULT_NS, + 'kmsProviders' => ['local' => ['key' => new MongoDB\BSON\Binary(CSFLE_LOCAL_KEY, 0)]], +]); + +$keyId = $clientEncryption->createDataKey('local'); +$key = $clientEncryption->getKey($keyId); + +var_dump($key->_id == $keyId); + +?> +===DONE=== + +--EXPECT-- +bool(true) +===DONE=== diff --git a/tests/clientEncryption/clientEncryption-getKey-002.phpt b/tests/clientEncryption/clientEncryption-getKey-002.phpt new file mode 100644 index 000000000..5269d304e --- /dev/null +++ b/tests/clientEncryption/clientEncryption-getKey-002.phpt @@ -0,0 +1,29 @@ +--TEST-- +MongoDB\Driver\ClientEncryption::getKey() when key does not exist +--SKIPIF-- + + + +createClientEncryption([ + 'keyVaultNamespace' => CSFLE_KEY_VAULT_NS, + 'kmsProviders' => ['local' => ['key' => new MongoDB\BSON\Binary(CSFLE_LOCAL_KEY, 0)]], +]); + +$keyId = new MongoDB\BSON\Binary(random_bytes(16), MongoDB\BSON\Binary::TYPE_UUID); + +var_dump($clientEncryption->getKey($keyId)); + +?> +===DONE=== + +--EXPECT-- +NULL +===DONE=== diff --git a/tests/clientEncryption/clientEncryption-getKeyByAltName-001.phpt b/tests/clientEncryption/clientEncryption-getKeyByAltName-001.phpt new file mode 100644 index 000000000..1b186c65d --- /dev/null +++ b/tests/clientEncryption/clientEncryption-getKeyByAltName-001.phpt @@ -0,0 +1,34 @@ +--TEST-- +MongoDB\Driver\ClientEncryption::getKeyByAltName() +--SKIPIF-- + + + +createClientEncryption([ + 'keyVaultNamespace' => CSFLE_KEY_VAULT_NS, + 'kmsProviders' => ['local' => ['key' => new MongoDB\BSON\Binary(CSFLE_LOCAL_KEY, 0)]], +]); + +$keyId = $clientEncryption->createDataKey('local', ['keyAltNames' => ['foo', 'bar']]); + +$key = $clientEncryption->getKeyByAltName('foo'); +var_dump($key->_id == $keyId); + +$key = $clientEncryption->getKeyByAltName('bar'); +var_dump($key->_id == $keyId); + +?> +===DONE=== + +--EXPECT-- +bool(true) +bool(true) +===DONE=== diff --git a/tests/clientEncryption/clientEncryption-getKeyByAltName-002.phpt b/tests/clientEncryption/clientEncryption-getKeyByAltName-002.phpt new file mode 100644 index 000000000..8e375b7fc --- /dev/null +++ b/tests/clientEncryption/clientEncryption-getKeyByAltName-002.phpt @@ -0,0 +1,27 @@ +--TEST-- +MongoDB\Driver\ClientEncryption::getKeyByAltName() when key does not exist +--SKIPIF-- + + + +createClientEncryption([ + 'keyVaultNamespace' => CSFLE_KEY_VAULT_NS, + 'kmsProviders' => ['local' => ['key' => new MongoDB\BSON\Binary(CSFLE_LOCAL_KEY, 0)]], +]); + +var_dump($clientEncryption->getKeyByAltName('foo')); + +?> +===DONE=== + +--EXPECT-- +NULL +===DONE=== diff --git a/tests/clientEncryption/clientEncryption-getKey_error-001.phpt b/tests/clientEncryption/clientEncryption-getKey_error-001.phpt new file mode 100644 index 000000000..b2c9d63ba --- /dev/null +++ b/tests/clientEncryption/clientEncryption-getKey_error-001.phpt @@ -0,0 +1,32 @@ +--TEST-- +MongoDB\Driver\ClientEncryption::getKey() with invalid keyId +--SKIPIF-- + + + +createClientEncryption([ + 'keyVaultNamespace' => CSFLE_KEY_VAULT_NS, + 'kmsProviders' => ['local' => ['key' => new MongoDB\BSON\Binary(CSFLE_LOCAL_KEY, 0)]], +]); + +$invalidKeyId = new MongoDB\BSON\Binary('', MongoDB\BSON\Binary::TYPE_GENERIC); + +echo throws(function () use ($clientEncryption, $invalidKeyId) { + $clientEncryption->getKey($invalidKeyId); +}, MongoDB\Driver\Exception\InvalidArgumentException::class), "\n"; + +?> +===DONE=== + +--EXPECT-- +OK: Got MongoDB\Driver\Exception\InvalidArgumentException +Expected keyid to have UUID Binary subtype (4), 0 given +===DONE=== diff --git a/tests/clientEncryption/clientEncryption-getKeys-001.phpt b/tests/clientEncryption/clientEncryption-getKeys-001.phpt new file mode 100644 index 000000000..23bd59f44 --- /dev/null +++ b/tests/clientEncryption/clientEncryption-getKeys-001.phpt @@ -0,0 +1,40 @@ +--TEST-- +MongoDB\Driver\ClientEncryption::getKeys() +--SKIPIF-- + + + +createClientEncryption([ + 'keyVaultNamespace' => CSFLE_KEY_VAULT_NS, + 'kmsProviders' => ['local' => ['key' => new MongoDB\BSON\Binary(CSFLE_LOCAL_KEY, 0)]], +]); + +$key1Id = $clientEncryption->createDataKey('local'); +$key2Id = $clientEncryption->createDataKey('local'); + +$keyIds = []; + +foreach ($clientEncryption->getKeys() as $key) { + $keyIds[] = $key->_id; +} + +var_dump(count($keyIds)); +var_dump(in_array($key1Id, $keyIds)); +var_dump(in_array($key2Id, $keyIds)); + +?> +===DONE=== + +--EXPECT-- +int(2) +bool(true) +bool(true) +===DONE=== diff --git a/tests/clientEncryption/clientEncryption-getKeys-002.phpt b/tests/clientEncryption/clientEncryption-getKeys-002.phpt new file mode 100644 index 000000000..4c229f332 --- /dev/null +++ b/tests/clientEncryption/clientEncryption-getKeys-002.phpt @@ -0,0 +1,27 @@ +--TEST-- +MongoDB\Driver\ClientEncryption::getKeys() with empty key vault +--SKIPIF-- + + + +createClientEncryption([ + 'keyVaultNamespace' => CSFLE_KEY_VAULT_NS, + 'kmsProviders' => ['local' => ['key' => new MongoDB\BSON\Binary(CSFLE_LOCAL_KEY, 0)]], +]); + +var_dump(iterator_count($clientEncryption->getKeys())); + +?> +===DONE=== + +--EXPECT-- +int(0) +===DONE=== diff --git a/tests/clientEncryption/clientEncryption-getKeys-003.phpt b/tests/clientEncryption/clientEncryption-getKeys-003.phpt new file mode 100644 index 000000000..1d2390dd4 --- /dev/null +++ b/tests/clientEncryption/clientEncryption-getKeys-003.phpt @@ -0,0 +1,61 @@ +--TEST-- +MongoDB\Driver\ClientEncryption::getKeys() cursor debug info +--SKIPIF-- + + + +createClientEncryption([ + 'keyVaultNamespace' => CSFLE_KEY_VAULT_NS, + 'kmsProviders' => ['local' => ['key' => new MongoDB\BSON\Binary(CSFLE_LOCAL_KEY, 0)]], +]); + +/* Note: the query reports a null readConcern property despite libmongoc using + * a majority read concern internally (as it does for all key vault reads). */ +var_dump($clientEncryption->getKeys()); + +?> +===DONE=== + +--EXPECTF-- +object(MongoDB\Driver\Cursor)#%d (%d) { + ["database"]=> + string(%d) "%s" + ["collection"]=> + string(%d) "%s" + ["query"]=> + object(MongoDB\Driver\Query)#%d (%d) { + ["filter"]=> + object(stdClass)#%d (%d) { + } + ["options"]=> + object(stdClass)#%d (%d) { + } + ["readConcern"]=> + NULL + } + ["command"]=> + NULL + ["readPreference"]=> + NULL + ["session"]=> + NULL + ["isDead"]=> + bool(true) + ["currentIndex"]=> + int(0) + ["currentDocument"]=> + NULL + ["server"]=> + object(MongoDB\Driver\Server)#%d (%d) { + %a + } +} +===DONE=== diff --git a/tests/clientEncryption/clientEncryption-removeKeyAltName-001.phpt b/tests/clientEncryption/clientEncryption-removeKeyAltName-001.phpt new file mode 100644 index 000000000..006ed868c --- /dev/null +++ b/tests/clientEncryption/clientEncryption-removeKeyAltName-001.phpt @@ -0,0 +1,38 @@ +--TEST-- +MongoDB\Driver\ClientEncryption::removeKeyAltName() +--SKIPIF-- + + + +createClientEncryption([ + 'keyVaultNamespace' => CSFLE_KEY_VAULT_NS, + 'kmsProviders' => ['local' => ['key' => new MongoDB\BSON\Binary(CSFLE_LOCAL_KEY, 0)]], +]); + +$keyId = $clientEncryption->createDataKey('local', ['keyAltNames' => ['foo', 'bar']]); + +$key = $clientEncryption->removeKeyAltName($keyId, 'foo'); +var_dump(in_array('foo', $key->keyAltNames)); +var_dump(in_array('bar', $key->keyAltNames)); + +$key = $clientEncryption->getKey($keyId); +var_dump(in_array('foo', $key->keyAltNames)); +var_dump(in_array('bar', $key->keyAltNames)); + +?> +===DONE=== + +--EXPECT-- +bool(true) +bool(true) +bool(false) +bool(true) +===DONE=== diff --git a/tests/clientEncryption/clientEncryption-removeKeyAltName-002.phpt b/tests/clientEncryption/clientEncryption-removeKeyAltName-002.phpt new file mode 100644 index 000000000..9c03a1fa2 --- /dev/null +++ b/tests/clientEncryption/clientEncryption-removeKeyAltName-002.phpt @@ -0,0 +1,29 @@ +--TEST-- +MongoDB\Driver\ClientEncryption::removeKeyAltName() when key does not exist +--SKIPIF-- + + + +createClientEncryption([ + 'keyVaultNamespace' => CSFLE_KEY_VAULT_NS, + 'kmsProviders' => ['local' => ['key' => new MongoDB\BSON\Binary(CSFLE_LOCAL_KEY, 0)]], +]); + +$keyId = new MongoDB\BSON\Binary(random_bytes(16), MongoDB\BSON\Binary::TYPE_UUID); + +var_dump($clientEncryption->removeKeyAltName($keyId, 'foo')); + +?> +===DONE=== + +--EXPECT-- +NULL +===DONE=== diff --git a/tests/clientEncryption/clientEncryption-removeKeyAltName-003.phpt b/tests/clientEncryption/clientEncryption-removeKeyAltName-003.phpt new file mode 100644 index 000000000..5815493f4 --- /dev/null +++ b/tests/clientEncryption/clientEncryption-removeKeyAltName-003.phpt @@ -0,0 +1,32 @@ +--TEST-- +MongoDB\Driver\ClientEncryption::removeKeyAltName() when keyAltName does not exist +--SKIPIF-- + + + +createClientEncryption([ + 'keyVaultNamespace' => CSFLE_KEY_VAULT_NS, + 'kmsProviders' => ['local' => ['key' => new MongoDB\BSON\Binary(CSFLE_LOCAL_KEY, 0)]], +]); + +$keyId = $clientEncryption->createDataKey('local', ['keyAltNames' => ['foo', 'bar']]); + +$keyBeforeRemoval = $clientEncryption->removeKeyAltName($keyId, 'baz'); +$keyAfterRemoval = $clientEncryption->getKey($keyId); + +var_dump($keyBeforeRemoval == $keyAfterRemoval); + +?> +===DONE=== + +--EXPECT-- +bool(true) +===DONE=== diff --git a/tests/clientEncryption/clientEncryption-removeKeyAltName_error-001.phpt b/tests/clientEncryption/clientEncryption-removeKeyAltName_error-001.phpt new file mode 100644 index 000000000..3ae205b08 --- /dev/null +++ b/tests/clientEncryption/clientEncryption-removeKeyAltName_error-001.phpt @@ -0,0 +1,32 @@ +--TEST-- +MongoDB\Driver\ClientEncryption::removeKeyAltName() with invalid keyId +--SKIPIF-- + + + +createClientEncryption([ + 'keyVaultNamespace' => CSFLE_KEY_VAULT_NS, + 'kmsProviders' => ['local' => ['key' => new MongoDB\BSON\Binary(CSFLE_LOCAL_KEY, 0)]], +]); + +$invalidKeyId = new MongoDB\BSON\Binary('', MongoDB\BSON\Binary::TYPE_GENERIC); + +echo throws(function () use ($clientEncryption, $invalidKeyId) { + $clientEncryption->removeKeyAltName($invalidKeyId, 'foo'); +}, MongoDB\Driver\Exception\InvalidArgumentException::class), "\n"; + +?> +===DONE=== + +--EXPECT-- +OK: Got MongoDB\Driver\Exception\InvalidArgumentException +Expected keyid to have UUID Binary subtype (4), 0 given +===DONE=== From 7573e6134157a9199ca930c45de6bfde6c117907 Mon Sep 17 00:00:00 2001 From: Jeremy Mikola Date: Tue, 2 Aug 2022 14:36:55 -0400 Subject: [PATCH 05/13] PHPC-2120: Bump libmongoc 1.22.1 and libmongocrypt 1.5.2 --- src/LIBMONGOCRYPT_VERSION_CURRENT | 2 +- src/LIBMONGOC_VERSION_CURRENT | 2 +- src/libmongoc | 2 +- src/libmongocrypt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/LIBMONGOCRYPT_VERSION_CURRENT b/src/LIBMONGOCRYPT_VERSION_CURRENT index bc80560fa..4cda8f19e 100644 --- a/src/LIBMONGOCRYPT_VERSION_CURRENT +++ b/src/LIBMONGOCRYPT_VERSION_CURRENT @@ -1 +1 @@ -1.5.0 +1.5.2 diff --git a/src/LIBMONGOC_VERSION_CURRENT b/src/LIBMONGOC_VERSION_CURRENT index 57807d6d0..6245beecd 100644 --- a/src/LIBMONGOC_VERSION_CURRENT +++ b/src/LIBMONGOC_VERSION_CURRENT @@ -1 +1 @@ -1.22.0 +1.22.1 diff --git a/src/libmongoc b/src/libmongoc index fc0e5338e..702d654d6 160000 --- a/src/libmongoc +++ b/src/libmongoc @@ -1 +1 @@ -Subproject commit fc0e5338e13f4061a50467c9d07fed11c80e64c3 +Subproject commit 702d654d67faf8358c203ddf3fd3b5257d202341 diff --git a/src/libmongocrypt b/src/libmongocrypt index c3be59f9b..8f8675fa1 160000 --- a/src/libmongocrypt +++ b/src/libmongocrypt @@ -1 +1 @@ -Subproject commit c3be59f9b0d756caa4c22c254e0704084cf6bca4 +Subproject commit 8f8675fa11922f00a4516a7f8a60621aa1ca1550 From 34820653ce52676b34de0e513043a9fed8404a87 Mon Sep 17 00:00:00 2001 From: Jeremy Mikola Date: Tue, 2 Aug 2022 16:58:13 -0400 Subject: [PATCH 06/13] Create separate macro for non-optional bson_t return value Also updates doc blocks to better clarify return types --- src/MongoDB/ClientEncryption.c | 37 ++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/src/MongoDB/ClientEncryption.c b/src/MongoDB/ClientEncryption.c index 76853d318..116031684 100644 --- a/src/MongoDB/ClientEncryption.c +++ b/src/MongoDB/ClientEncryption.c @@ -40,17 +40,22 @@ static void phongo_clientencryption_create_datakey(php_phongo_clientencryption_t static void phongo_clientencryption_encrypt(php_phongo_clientencryption_t* clientencryption, zval* zvalue, zval* zciphertext, zval* options); static void phongo_clientencryption_decrypt(php_phongo_clientencryption_t* clientencryption, zval* zciphertext, zval* zvalue); -#define RETVAL_OPTIONAL_BSON_T(reply) \ - do { \ - RETVAL_NULL(); \ - if (!bson_empty(&(reply))) { \ - php_phongo_bson_state state = { 0 }; \ - if (!php_phongo_bson_to_zval_ex(bson_get_data(&(reply)), (reply).len, &state)) { \ - zval_ptr_dtor(&state.zchild); \ - goto cleanup; \ - } \ - RETVAL_ZVAL(&state.zchild, 0, 1); \ - } \ +#define RETVAL_BSON_T(reply) \ + do { \ + php_phongo_bson_state state = { 0 }; \ + if (!php_phongo_bson_to_zval_ex(bson_get_data(&(reply)), (reply).len, &state)) { \ + zval_ptr_dtor(&state.zchild); \ + goto cleanup; \ + } \ + RETVAL_ZVAL(&state.zchild, 0, 1); \ + } while (0) + +#define RETVAL_OPTIONAL_BSON_T(reply) \ + do { \ + RETVAL_NULL(); \ + if (!bson_empty(&(reply))) { \ + RETVAL_BSON_T(reply); \ + } \ } while (0) /* Returns true if keyid is a UUID Binary value with an appropriate data length; @@ -133,7 +138,8 @@ static PHP_METHOD(MongoDB_Driver_ClientEncryption, addKeyAltName) } /* }}} */ /* {{{ proto MongoDB\BSON\Binary MongoDB\Driver\ClientEncryption::createDataKey(string $kmsProvider[, array $options]) - Creates a new key document and inserts into the key vault collection. */ + Creates a new key document and inserts into the key vault collection and + returns its identifier (UUID as a BSON binary with subtype 0x04). */ static PHP_METHOD(MongoDB_Driver_ClientEncryption, createDataKey) { char* kms_provider = NULL; @@ -183,7 +189,12 @@ static PHP_METHOD(MongoDB_Driver_ClientEncryption, deleteKey) goto cleanup; } - RETVAL_OPTIONAL_BSON_T(reply); + if (bson_empty(&reply)) { + phongo_throw_exception(PHONGO_ERROR_UNEXPECTED_VALUE, "mongoc_client_encryption_delete_key returned an empty document"); + goto cleanup; + } + + RETVAL_BSON_T(reply); cleanup: bson_value_destroy(&keyid); From cdbefae83ad8e4a23a1d6fdaea2b942b1e19eb1a Mon Sep 17 00:00:00 2001 From: Jeremy Mikola Date: Wed, 3 Aug 2022 16:03:04 -0400 Subject: [PATCH 07/13] ClientEncryption::rewrapManyDataKey method --- src/MongoDB/ClientEncryption.c | 12 +++- ...lientEncryption-rewrapManyDataKey-001.phpt | 57 +++++++++++++++++++ ...lientEncryption-rewrapManyDataKey-002.phpt | 43 ++++++++++++++ 3 files changed, 110 insertions(+), 2 deletions(-) create mode 100644 tests/clientEncryption/clientEncryption-rewrapManyDataKey-001.phpt create mode 100644 tests/clientEncryption/clientEncryption-rewrapManyDataKey-002.phpt diff --git a/src/MongoDB/ClientEncryption.c b/src/MongoDB/ClientEncryption.c index 116031684..c9dd613da 100644 --- a/src/MongoDB/ClientEncryption.c +++ b/src/MongoDB/ClientEncryption.c @@ -393,8 +393,10 @@ static PHP_METHOD(MongoDB_Driver_ClientEncryption, rewrapManyDataKey) zend_bool free_provider = false; bson_t* masterkey = NULL; bson_error_t error = { 0 }; + bson_t reply = BSON_INITIALIZER; mongoc_client_encryption_rewrap_many_datakey_result_t* result = NULL; + const bson_t* bulk_write_result; PHONGO_PARSE_PARAMETERS_START(1, 2) Z_PARAM_ARRAY_OR_OBJECT(zfilter) @@ -431,9 +433,15 @@ static PHP_METHOD(MongoDB_Driver_ClientEncryption, rewrapManyDataKey) goto cleanup; } - RETVAL_NULL(); + bulk_write_result = mongoc_client_encryption_rewrap_many_datakey_result_get_bulk_write_result(result); - /* TODO: mongoc_client_encryption_rewrap_many_datakey_result_t return value */ + if (bson_empty0(bulk_write_result)) { + BSON_APPEND_NULL(&reply, "bulkWriteResult"); + } else { + BSON_APPEND_DOCUMENT(&reply, "bulkWriteResult", bulk_write_result); + } + + RETVAL_BSON_T(reply); cleanup: if (free_provider) { diff --git a/tests/clientEncryption/clientEncryption-rewrapManyDataKey-001.phpt b/tests/clientEncryption/clientEncryption-rewrapManyDataKey-001.phpt new file mode 100644 index 000000000..b649db6fb --- /dev/null +++ b/tests/clientEncryption/clientEncryption-rewrapManyDataKey-001.phpt @@ -0,0 +1,57 @@ +--TEST-- +MongoDB\Driver\ClientEncryption::rewrapManyDataKey() +--SKIPIF-- + + + +createClientEncryption([ + 'keyVaultNamespace' => CSFLE_KEY_VAULT_NS, + 'kmsProviders' => ['local' => ['key' => new MongoDB\BSON\Binary(CSFLE_LOCAL_KEY, 0)]], +]); + +$keyId = $clientEncryption->createDataKey('local'); + +$orignalKey = $clientEncryption->getKey($keyId); + +var_dump($clientEncryption->rewrapManyDataKey([], ['provider' => 'local'])); + +$modifiedKey = $clientEncryption->getKey($keyId); + +var_dump($orignalKey->creationDate == $modifiedKey->creationDate); +var_dump($orignalKey->updateDate < $modifiedKey->updateDate); +var_dump($orignalKey->keyMaterial != $modifiedKey->keyMaterial); + +?> +===DONE=== + +--EXPECTF-- +object(stdClass)#%d (%d) { + ["bulkWriteResult"]=> + object(stdClass)#%d (%d) { + ["nInserted"]=> + int(0) + ["nMatched"]=> + int(1) + ["nModified"]=> + int(1) + ["nRemoved"]=> + int(0) + ["nUpserted"]=> + int(0) + ["writeErrors"]=> + array(0) { + } + } +} +bool(true) +bool(true) +bool(true) +===DONE=== diff --git a/tests/clientEncryption/clientEncryption-rewrapManyDataKey-002.phpt b/tests/clientEncryption/clientEncryption-rewrapManyDataKey-002.phpt new file mode 100644 index 000000000..725d1368a --- /dev/null +++ b/tests/clientEncryption/clientEncryption-rewrapManyDataKey-002.phpt @@ -0,0 +1,43 @@ +--TEST-- +MongoDB\Driver\ClientEncryption::rewrapManyDataKey() when filter matches no keys +--SKIPIF-- + + + +createClientEncryption([ + 'keyVaultNamespace' => CSFLE_KEY_VAULT_NS, + 'kmsProviders' => ['local' => ['key' => new MongoDB\BSON\Binary(CSFLE_LOCAL_KEY, 0)]], +]); + +$keyId = $clientEncryption->createDataKey('local'); + +$orignalKey = $clientEncryption->getKey($keyId); + +var_dump($clientEncryption->rewrapManyDataKey(['_id' => 'no-matching-key'])); + +$modifiedKey = $clientEncryption->getKey($keyId); + +var_dump($orignalKey->creationDate == $modifiedKey->creationDate); +var_dump($orignalKey->updateDate == $modifiedKey->updateDate); +var_dump($orignalKey->keyMaterial == $modifiedKey->keyMaterial); + +?> +===DONE=== + +--EXPECTF-- +object(stdClass)#%d (%d) { + ["bulkWriteResult"]=> + NULL +} +bool(true) +bool(true) +bool(true) +===DONE=== From 9ad85714b40639e06bf9a5519e4135302c573246 Mon Sep 17 00:00:00 2001 From: Jeremy Mikola Date: Tue, 9 Aug 2022 09:38:40 -0400 Subject: [PATCH 08/13] Use conditional for union types in rewrapManyDataKey --- src/MongoDB/ClientEncryption.stub.php | 5 +++++ src/MongoDB/ClientEncryption_arginfo.h | 21 ++++++++++++++++++++- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/MongoDB/ClientEncryption.stub.php b/src/MongoDB/ClientEncryption.stub.php index 985737220..0b64fdf7f 100644 --- a/src/MongoDB/ClientEncryption.stub.php +++ b/src/MongoDB/ClientEncryption.stub.php @@ -69,7 +69,12 @@ final public function getKeys(): \MongoDB\Driver\Cursor {} final public function removeKeyAltName(\MongoDB\BSON\Binary $keyId, string $keyAltName): ?object {} +#if PHP_VERSION_ID >= 80000 final public function rewrapManyDataKey(array|object $filter, array $options = []): object {} +#else + /** @param array|object $filter */ + final public function rewrapManyDataKey($filter, array $options = []): object {} +#endif final public function __wakeup(): void {} } diff --git a/src/MongoDB/ClientEncryption_arginfo.h b/src/MongoDB/ClientEncryption_arginfo.h index 217c88c14..c0cd17b6f 100644 --- a/src/MongoDB/ClientEncryption_arginfo.h +++ b/src/MongoDB/ClientEncryption_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 8a6eece2c09efd48a988f3acee25a1e0e9016298 */ + * Stub hash: 9b64f6db2b6c568fb134f8512968262b25ab0e78 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_MongoDB_Driver_ClientEncryption___construct, 0, 0, 1) ZEND_ARG_TYPE_INFO(0, options, IS_ARRAY, 0) @@ -58,10 +58,19 @@ ZEND_END_ARG_INFO() #define arginfo_class_MongoDB_Driver_ClientEncryption_removeKeyAltName arginfo_class_MongoDB_Driver_ClientEncryption_addKeyAltName +#if PHP_VERSION_ID >= 80000 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_MongoDB_Driver_ClientEncryption_rewrapManyDataKey, 0, 1, IS_OBJECT, 0) ZEND_ARG_TYPE_MASK(0, filter, MAY_BE_ARRAY|MAY_BE_OBJECT, NULL) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "[]") ZEND_END_ARG_INFO() +#endif + +#if !(PHP_VERSION_ID >= 80000) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_MongoDB_Driver_ClientEncryption_rewrapManyDataKey, 0, 1, IS_OBJECT, 0) + ZEND_ARG_INFO(0, filter) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "[]") +ZEND_END_ARG_INFO() +#endif ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_MongoDB_Driver_ClientEncryption___wakeup, 0, 0, IS_VOID, 0) ZEND_END_ARG_INFO() @@ -87,7 +96,12 @@ static ZEND_METHOD(MongoDB_Driver_ClientEncryption, getKey); static ZEND_METHOD(MongoDB_Driver_ClientEncryption, getKeyByAltName); static ZEND_METHOD(MongoDB_Driver_ClientEncryption, getKeys); static ZEND_METHOD(MongoDB_Driver_ClientEncryption, removeKeyAltName); +#if PHP_VERSION_ID >= 80000 static ZEND_METHOD(MongoDB_Driver_ClientEncryption, rewrapManyDataKey); +#endif +#if !(PHP_VERSION_ID >= 80000) +static ZEND_METHOD(MongoDB_Driver_ClientEncryption, rewrapManyDataKey); +#endif static ZEND_METHOD(MongoDB_Driver_ClientEncryption, __wakeup); @@ -112,7 +126,12 @@ static const zend_function_entry class_MongoDB_Driver_ClientEncryption_methods[] ZEND_ME(MongoDB_Driver_ClientEncryption, getKeyByAltName, arginfo_class_MongoDB_Driver_ClientEncryption_getKeyByAltName, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL) ZEND_ME(MongoDB_Driver_ClientEncryption, getKeys, arginfo_class_MongoDB_Driver_ClientEncryption_getKeys, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL) ZEND_ME(MongoDB_Driver_ClientEncryption, removeKeyAltName, arginfo_class_MongoDB_Driver_ClientEncryption_removeKeyAltName, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL) +#if PHP_VERSION_ID >= 80000 ZEND_ME(MongoDB_Driver_ClientEncryption, rewrapManyDataKey, arginfo_class_MongoDB_Driver_ClientEncryption_rewrapManyDataKey, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL) +#endif +#if !(PHP_VERSION_ID >= 80000) + ZEND_ME(MongoDB_Driver_ClientEncryption, rewrapManyDataKey, arginfo_class_MongoDB_Driver_ClientEncryption_rewrapManyDataKey, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL) +#endif ZEND_ME(MongoDB_Driver_ClientEncryption, __wakeup, arginfo_class_MongoDB_Driver_ClientEncryption___wakeup, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL) ZEND_FE_END }; From ba48a0e2f1e3135644e69b506f96ce7bd39cfa7d Mon Sep 17 00:00:00 2001 From: Jeremy Mikola Date: Tue, 9 Aug 2022 13:02:29 -0400 Subject: [PATCH 09/13] Use PHONGO_BSON_INIT_STATE macro in RETVAL_BSON_T Since php_phongo_bson_state contains other structs, using {0} as an initializer may cause "missing braces around initializer [-Werror=missing-braces]" errors on some platforms. --- src/MongoDB/ClientEncryption.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/MongoDB/ClientEncryption.c b/src/MongoDB/ClientEncryption.c index c9dd613da..49c289b7d 100644 --- a/src/MongoDB/ClientEncryption.c +++ b/src/MongoDB/ClientEncryption.c @@ -42,7 +42,8 @@ static void phongo_clientencryption_decrypt(php_phongo_clientencryption_t* clien #define RETVAL_BSON_T(reply) \ do { \ - php_phongo_bson_state state = { 0 }; \ + php_phongo_bson_state state; \ + PHONGO_BSON_INIT_STATE(state); \ if (!php_phongo_bson_to_zval_ex(bson_get_data(&(reply)), (reply).len, &state)) { \ zval_ptr_dtor(&state.zchild); \ goto cleanup; \ From 5e2afa5dc23ef1619bd25a63ffd134cf3aaeecfb Mon Sep 17 00:00:00 2001 From: Jeremy Mikola Date: Tue, 9 Aug 2022 13:05:14 -0400 Subject: [PATCH 10/13] Use PHONGO_PARAM_ARRAY_OR_OBJECT compat macro in rewrapManyDataKey --- src/MongoDB/ClientEncryption.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/MongoDB/ClientEncryption.c b/src/MongoDB/ClientEncryption.c index 49c289b7d..e8fb4fd00 100644 --- a/src/MongoDB/ClientEncryption.c +++ b/src/MongoDB/ClientEncryption.c @@ -400,7 +400,7 @@ static PHP_METHOD(MongoDB_Driver_ClientEncryption, rewrapManyDataKey) const bson_t* bulk_write_result; PHONGO_PARSE_PARAMETERS_START(1, 2) - Z_PARAM_ARRAY_OR_OBJECT(zfilter) + PHONGO_PARAM_ARRAY_OR_OBJECT(zfilter) Z_PARAM_OPTIONAL Z_PARAM_ARRAY(options) PHONGO_PARSE_PARAMETERS_END(); From 90c631a9743bb105b2e02904e140b85161bda8d0 Mon Sep 17 00:00:00 2001 From: Jeremy Mikola Date: Tue, 9 Aug 2022 13:28:11 -0400 Subject: [PATCH 11/13] Require server 4.2+ for CSFLE integration tests --- tests/clientEncryption/clientEncryption-addKeyAltName-001.phpt | 1 + tests/clientEncryption/clientEncryption-addKeyAltName-002.phpt | 1 + tests/clientEncryption/clientEncryption-createDataKey-001.phpt | 1 + tests/clientEncryption/clientEncryption-createDataKey-002.phpt | 1 + tests/clientEncryption/clientEncryption-decrypt-001.phpt | 1 + tests/clientEncryption/clientEncryption-deleteKey-001.phpt | 1 + tests/clientEncryption/clientEncryption-deleteKey-002.phpt | 1 + tests/clientEncryption/clientEncryption-encrypt-001.phpt | 1 + tests/clientEncryption/clientEncryption-getKey-001.phpt | 1 + tests/clientEncryption/clientEncryption-getKey-002.phpt | 1 + tests/clientEncryption/clientEncryption-getKeyByAltName-001.phpt | 1 + tests/clientEncryption/clientEncryption-getKeyByAltName-002.phpt | 1 + tests/clientEncryption/clientEncryption-getKeys-001.phpt | 1 + tests/clientEncryption/clientEncryption-getKeys-002.phpt | 1 + tests/clientEncryption/clientEncryption-getKeys-003.phpt | 1 + .../clientEncryption/clientEncryption-removeKeyAltName-001.phpt | 1 + .../clientEncryption/clientEncryption-removeKeyAltName-002.phpt | 1 + .../clientEncryption/clientEncryption-removeKeyAltName-003.phpt | 1 + .../clientEncryption/clientEncryption-rewrapManyDataKey-001.phpt | 1 + .../clientEncryption/clientEncryption-rewrapManyDataKey-002.phpt | 1 + 20 files changed, 20 insertions(+) diff --git a/tests/clientEncryption/clientEncryption-addKeyAltName-001.phpt b/tests/clientEncryption/clientEncryption-addKeyAltName-001.phpt index 5107b1d41..4e44943c4 100644 --- a/tests/clientEncryption/clientEncryption-addKeyAltName-001.phpt +++ b/tests/clientEncryption/clientEncryption-addKeyAltName-001.phpt @@ -4,6 +4,7 @@ MongoDB\Driver\ClientEncryption::addKeyAltName() + + + + + + + + + + + + + + + + + + + + Date: Tue, 9 Aug 2022 17:48:17 -0400 Subject: [PATCH 12/13] Accommodate PHP 7.2 error message in createDataKey error test --- .../clientEncryption-createDataKey_error-002.phpt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/clientEncryption/clientEncryption-createDataKey_error-002.phpt b/tests/clientEncryption/clientEncryption-createDataKey_error-002.phpt index 8d999cdc2..bae01480c 100644 --- a/tests/clientEncryption/clientEncryption-createDataKey_error-002.phpt +++ b/tests/clientEncryption/clientEncryption-createDataKey_error-002.phpt @@ -29,9 +29,9 @@ foreach ($tests as $opts) { ?> ===DONE=== ---EXPECT-- +--EXPECTF-- OK: Got MongoDB\Driver\Exception\InvalidArgumentException -Expected "keyMaterial" option to be MongoDB\BSON\Binary, int given +Expected "keyMaterial" option to be MongoDB\BSON\Binary, %r(int|integer)%r given OK: Got MongoDB\Driver\Exception\InvalidArgumentException Expected "keyMaterial" option to be MongoDB\BSON\Binary, stdClass given ===DONE=== From 854b73be2d2638eec544a12b9cfdad1b68dffd15 Mon Sep 17 00:00:00 2001 From: Jeremy Mikola Date: Wed, 17 Aug 2022 14:01:26 -0400 Subject: [PATCH 13/13] ClientEncryption::rewrapManyDataKey options are nullable --- src/MongoDB/ClientEncryption.c | 2 +- src/MongoDB/ClientEncryption.stub.php | 4 +-- src/MongoDB/ClientEncryption_arginfo.h | 6 ++-- ...lientEncryption-rewrapManyDataKey-003.phpt | 31 +++++++++++++++++++ 4 files changed, 37 insertions(+), 6 deletions(-) create mode 100644 tests/clientEncryption/clientEncryption-rewrapManyDataKey-003.phpt diff --git a/src/MongoDB/ClientEncryption.c b/src/MongoDB/ClientEncryption.c index e8fb4fd00..e893b096b 100644 --- a/src/MongoDB/ClientEncryption.c +++ b/src/MongoDB/ClientEncryption.c @@ -402,7 +402,7 @@ static PHP_METHOD(MongoDB_Driver_ClientEncryption, rewrapManyDataKey) PHONGO_PARSE_PARAMETERS_START(1, 2) PHONGO_PARAM_ARRAY_OR_OBJECT(zfilter) Z_PARAM_OPTIONAL - Z_PARAM_ARRAY(options) + Z_PARAM_ARRAY_OR_NULL(options) PHONGO_PARSE_PARAMETERS_END(); php_phongo_zval_to_bson(zfilter, PHONGO_BSON_NONE, &filter, NULL); diff --git a/src/MongoDB/ClientEncryption.stub.php b/src/MongoDB/ClientEncryption.stub.php index 0b64fdf7f..2dbdc9ef0 100644 --- a/src/MongoDB/ClientEncryption.stub.php +++ b/src/MongoDB/ClientEncryption.stub.php @@ -70,10 +70,10 @@ final public function getKeys(): \MongoDB\Driver\Cursor {} final public function removeKeyAltName(\MongoDB\BSON\Binary $keyId, string $keyAltName): ?object {} #if PHP_VERSION_ID >= 80000 - final public function rewrapManyDataKey(array|object $filter, array $options = []): object {} + final public function rewrapManyDataKey(array|object $filter, ?array $options = null): object {} #else /** @param array|object $filter */ - final public function rewrapManyDataKey($filter, array $options = []): object {} + final public function rewrapManyDataKey($filter, ?array $options = null): object {} #endif final public function __wakeup(): void {} diff --git a/src/MongoDB/ClientEncryption_arginfo.h b/src/MongoDB/ClientEncryption_arginfo.h index c0cd17b6f..bc1c7967c 100644 --- a/src/MongoDB/ClientEncryption_arginfo.h +++ b/src/MongoDB/ClientEncryption_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 9b64f6db2b6c568fb134f8512968262b25ab0e78 */ + * Stub hash: 706125ea8c95ec1b3720909c8351585e03aa4836 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_MongoDB_Driver_ClientEncryption___construct, 0, 0, 1) ZEND_ARG_TYPE_INFO(0, options, IS_ARRAY, 0) @@ -61,14 +61,14 @@ ZEND_END_ARG_INFO() #if PHP_VERSION_ID >= 80000 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_MongoDB_Driver_ClientEncryption_rewrapManyDataKey, 0, 1, IS_OBJECT, 0) ZEND_ARG_TYPE_MASK(0, filter, MAY_BE_ARRAY|MAY_BE_OBJECT, NULL) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "[]") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null") ZEND_END_ARG_INFO() #endif #if !(PHP_VERSION_ID >= 80000) ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_MongoDB_Driver_ClientEncryption_rewrapManyDataKey, 0, 1, IS_OBJECT, 0) ZEND_ARG_INFO(0, filter) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "[]") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null") ZEND_END_ARG_INFO() #endif diff --git a/tests/clientEncryption/clientEncryption-rewrapManyDataKey-003.phpt b/tests/clientEncryption/clientEncryption-rewrapManyDataKey-003.phpt new file mode 100644 index 000000000..700cbd0d1 --- /dev/null +++ b/tests/clientEncryption/clientEncryption-rewrapManyDataKey-003.phpt @@ -0,0 +1,31 @@ +--TEST-- +MongoDB\Driver\ClientEncryption::rewrapManyDataKey() accepts null for $options +--SKIPIF-- + + + + +createClientEncryption([ + 'keyVaultNamespace' => CSFLE_KEY_VAULT_NS, + 'kmsProviders' => ['local' => ['key' => new MongoDB\BSON\Binary(CSFLE_LOCAL_KEY, 0)]], +]); + +var_dump($clientEncryption->rewrapManyDataKey(['_id' => 'no-matching-key'], null)); + +?> +===DONE=== + +--EXPECTF-- +object(stdClass)#%d (%d) { + ["bulkWriteResult"]=> + NULL +} +===DONE===