diff --git a/.mci.yml b/.mci.yml index 3578c70631..829c18224f 100644 --- a/.mci.yml +++ b/.mci.yml @@ -421,6 +421,7 @@ functions: export VERSIONED_API_TESTS_PATH=$(pwd)/../data/versioned-api export COLLECTION_MANAGEMENT_TESTS_PATH="$(pwd)/../data/collection-management" export URI_OPTIONS_TESTS_PATH="$(pwd)/../data/uri-options" + export CLIENT_SIDE_ENCRYPTION_UNIFIED_TESTS_PATH="$(pwd)/../data/client_side_encryption/unified" export MONGODB_API_VERSION="${MONGODB_API_VERSION}" diff --git a/data/client_side_encryption/unified/addKeyAltName.json b/data/client_side_encryption/unified/addKeyAltName.json new file mode 100644 index 0000000000..f70bc572a8 --- /dev/null +++ b/data/client_side_encryption/unified/addKeyAltName.json @@ -0,0 +1,609 @@ +{ + "description": "addKeyAltName", + "schemaVersion": "1.8", + "runOnRequirements": [ + { + "csfle": true + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "clientEncryption": { + "id": "clientEncryption0", + "clientEncryptionOpts": { + "keyVaultClient": "client0", + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "local": { + "key": { + "$$placeholder": 1 + } + } + } + } + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "keyvault" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "datakeys" + } + } + ], + "initialData": [ + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "_id": { + "$binary": { + "base64": "bG9jYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "ABKBldDEoDW323yejOnIRk6YQmlD9d3eQthd16scKL75nz2LjNL9fgPDZWrFFOlqlhMCFaSrNJfGrFUjYk5JFDO7soG5Syb50k1niJoKg4ilsj0L4mpimFUtTpOr2nzZOeQtvAksEXc7gsFgq8gV7t/U3lsaXPY7I0t42DfSE8EGlPdxRjFdHnxh+OR8h7U9b8Qs5K5UuhgyeyxaBZ1Hgw==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "status": 1, + "masterKey": { + "provider": "local" + } + } + ] + } + ], + "tests": [ + { + "description": "add keyAltName to non-existent data key", + "operations": [ + { + "name": "addKeyAltName", + "object": "clientEncryption0", + "arguments": { + "id": { + "$binary": { + "base64": "AAAjYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + }, + "keyAltName": "new_key_alt_name" + }, + "expectResult": { + "$$unsetOrMatches": null + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "findAndModify": "datakeys", + "query": { + "_id": { + "$binary": { + "base64": "AAAjYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + } + }, + "update": { + "$addToSet": { + "keyAltNames": "new_key_alt_name" + } + }, + "writeConcern": { + "w": "majority" + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "datakeys", + "databaseName": "keyvault", + "documents": [ + { + "_id": { + "$binary": { + "base64": "bG9jYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "ABKBldDEoDW323yejOnIRk6YQmlD9d3eQthd16scKL75nz2LjNL9fgPDZWrFFOlqlhMCFaSrNJfGrFUjYk5JFDO7soG5Syb50k1niJoKg4ilsj0L4mpimFUtTpOr2nzZOeQtvAksEXc7gsFgq8gV7t/U3lsaXPY7I0t42DfSE8EGlPdxRjFdHnxh+OR8h7U9b8Qs5K5UuhgyeyxaBZ1Hgw==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "status": 1, + "masterKey": { + "provider": "local" + } + } + ] + } + ] + }, + { + "description": "add new keyAltName to data key with no keyAltNames", + "operations": [ + { + "name": "addKeyAltName", + "object": "clientEncryption0", + "arguments": { + "id": { + "$binary": { + "base64": "bG9jYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + }, + "keyAltName": "local_key" + }, + "expectResult": { + "_id": { + "$binary": { + "base64": "bG9jYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "ABKBldDEoDW323yejOnIRk6YQmlD9d3eQthd16scKL75nz2LjNL9fgPDZWrFFOlqlhMCFaSrNJfGrFUjYk5JFDO7soG5Syb50k1niJoKg4ilsj0L4mpimFUtTpOr2nzZOeQtvAksEXc7gsFgq8gV7t/U3lsaXPY7I0t42DfSE8EGlPdxRjFdHnxh+OR8h7U9b8Qs5K5UuhgyeyxaBZ1Hgw==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "status": 1, + "masterKey": { + "provider": "local" + } + } + }, + { + "name": "find", + "object": "collection0", + "arguments": { + "filter": {}, + "projection": { + "_id": 0, + "keyAltNames": 1 + } + }, + "expectResult": [ + { + "keyAltNames": [ + "local_key" + ] + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "findAndModify": "datakeys", + "query": { + "_id": { + "$binary": { + "base64": "bG9jYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + } + }, + "update": { + "$addToSet": { + "keyAltNames": "local_key" + } + }, + "writeConcern": { + "w": "majority" + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "find" + } + } + ] + } + ] + }, + { + "description": "add existing keyAltName to existing data key", + "operations": [ + { + "name": "addKeyAltName", + "object": "clientEncryption0", + "arguments": { + "id": { + "$binary": { + "base64": "bG9jYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + }, + "keyAltName": "local_key" + }, + "expectResult": { + "_id": { + "$binary": { + "base64": "bG9jYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "ABKBldDEoDW323yejOnIRk6YQmlD9d3eQthd16scKL75nz2LjNL9fgPDZWrFFOlqlhMCFaSrNJfGrFUjYk5JFDO7soG5Syb50k1niJoKg4ilsj0L4mpimFUtTpOr2nzZOeQtvAksEXc7gsFgq8gV7t/U3lsaXPY7I0t42DfSE8EGlPdxRjFdHnxh+OR8h7U9b8Qs5K5UuhgyeyxaBZ1Hgw==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "status": 1, + "masterKey": { + "provider": "local" + } + } + }, + { + "name": "addKeyAltName", + "object": "clientEncryption0", + "arguments": { + "id": { + "$binary": { + "base64": "bG9jYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + }, + "keyAltName": "local_key" + }, + "expectResult": { + "_id": { + "$binary": { + "base64": "bG9jYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + }, + "keyAltNames": [ + "local_key" + ], + "keyMaterial": { + "$$type": "binData" + }, + "creationDate": { + "$$type": "date" + }, + "updateDate": { + "$$type": "date" + }, + "status": 1, + "masterKey": { + "provider": "local" + } + } + }, + { + "name": "find", + "object": "collection0", + "arguments": { + "filter": {}, + "projection": { + "_id": 0, + "keyAltNames": 1 + } + }, + "expectResult": [ + { + "keyAltNames": [ + "local_key" + ] + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "findAndModify": "datakeys", + "query": { + "_id": { + "$binary": { + "base64": "bG9jYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + } + }, + "update": { + "$addToSet": { + "keyAltNames": "local_key" + } + }, + "writeConcern": { + "w": "majority" + } + } + } + }, + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "findAndModify": "datakeys", + "query": { + "_id": { + "$binary": { + "base64": "bG9jYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + } + }, + "update": { + "$addToSet": { + "keyAltNames": "local_key" + } + }, + "writeConcern": { + "w": "majority" + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "find" + } + } + ] + } + ] + }, + { + "description": "add new keyAltName to data key with keyAltNames", + "operations": [ + { + "name": "addKeyAltName", + "object": "clientEncryption0", + "arguments": { + "id": { + "$binary": { + "base64": "bG9jYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + }, + "keyAltName": "local_key" + }, + "expectResult": { + "_id": { + "$binary": { + "base64": "bG9jYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "ABKBldDEoDW323yejOnIRk6YQmlD9d3eQthd16scKL75nz2LjNL9fgPDZWrFFOlqlhMCFaSrNJfGrFUjYk5JFDO7soG5Syb50k1niJoKg4ilsj0L4mpimFUtTpOr2nzZOeQtvAksEXc7gsFgq8gV7t/U3lsaXPY7I0t42DfSE8EGlPdxRjFdHnxh+OR8h7U9b8Qs5K5UuhgyeyxaBZ1Hgw==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "status": 1, + "masterKey": { + "provider": "local" + } + } + }, + { + "name": "addKeyAltName", + "object": "clientEncryption0", + "arguments": { + "id": { + "$binary": { + "base64": "bG9jYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + }, + "keyAltName": "another_name" + }, + "expectResult": { + "_id": { + "$binary": { + "base64": "bG9jYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + }, + "keyAltNames": [ + "local_key" + ], + "keyMaterial": { + "$$type": "binData" + }, + "creationDate": { + "$$type": "date" + }, + "updateDate": { + "$$type": "date" + }, + "status": 1, + "masterKey": { + "provider": "local" + } + } + }, + { + "name": "aggregate", + "object": "collection0", + "arguments": { + "pipeline": [ + { + "$project": { + "_id": 0, + "keyAltNames": "$keyAltNames" + } + }, + { + "$unwind": "$keyAltNames" + }, + { + "$sort": { + "keyAltNames": 1 + } + } + ] + }, + "expectResult": [ + { + "keyAltNames": "another_name" + }, + { + "keyAltNames": "local_key" + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "findAndModify": "datakeys", + "query": { + "_id": { + "$binary": { + "base64": "bG9jYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + } + }, + "update": { + "$addToSet": { + "keyAltNames": "local_key" + } + }, + "writeConcern": { + "w": "majority" + } + } + } + }, + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "findAndModify": "datakeys", + "query": { + "_id": { + "$binary": { + "base64": "bG9jYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + } + }, + "update": { + "$addToSet": { + "keyAltNames": "another_name" + } + }, + "writeConcern": { + "w": "majority" + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "aggregate" + } + } + ] + } + ] + } + ] +} diff --git a/data/client_side_encryption/unified/createDataKey-kms_providers-invalid.json b/data/client_side_encryption/unified/createDataKey-kms_providers-invalid.json new file mode 100644 index 0000000000..2344a61a95 --- /dev/null +++ b/data/client_side_encryption/unified/createDataKey-kms_providers-invalid.json @@ -0,0 +1,119 @@ +{ + "description": "createDataKey-kms_providers-invalid", + "schemaVersion": "1.8", + "runOnRequirements": [ + { + "csfle": true + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent", + "commandSucceededEvent", + "commandFailedEvent" + ] + } + }, + { + "clientEncryption": { + "id": "clientEncryption0", + "clientEncryptionOpts": { + "keyVaultClient": "client0", + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "aws": { + "accessKeyId": { + "$$placeholder": 1 + }, + "secretAccessKey": { + "$$placeholder": 1 + } + } + } + } + } + } + ], + "tests": [ + { + "description": "create data key without required master key fields", + "operations": [ + { + "name": "createDataKey", + "object": "clientEncryption0", + "arguments": { + "kmsProvider": "aws", + "opts": { + "masterKey": {} + } + }, + "expectError": { + "isClientError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [] + } + ] + }, + { + "description": "create data key with invalid master key field", + "operations": [ + { + "name": "createDataKey", + "object": "clientEncryption0", + "arguments": { + "kmsProvider": "local", + "opts": { + "masterKey": { + "invalid": 1 + } + } + }, + "expectError": { + "isClientError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [] + } + ] + }, + { + "description": "create data key with invalid master key", + "operations": [ + { + "name": "createDataKey", + "object": "clientEncryption0", + "arguments": { + "kmsProvider": "aws", + "opts": { + "masterKey": { + "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", + "region": "invalid" + } + } + }, + "expectError": { + "isClientError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [] + } + ] + } + ] +} diff --git a/data/client_side_encryption/unified/createDataKey.json b/data/client_side_encryption/unified/createDataKey.json new file mode 100644 index 0000000000..110c726f9a --- /dev/null +++ b/data/client_side_encryption/unified/createDataKey.json @@ -0,0 +1,711 @@ +{ + "description": "createDataKey", + "schemaVersion": "1.8", + "runOnRequirements": [ + { + "csfle": true + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "clientEncryption": { + "id": "clientEncryption0", + "clientEncryptionOpts": { + "keyVaultClient": "client0", + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "aws": { + "accessKeyId": { + "$$placeholder": 1 + }, + "secretAccessKey": { + "$$placeholder": 1 + } + }, + "azure": { + "tenantId": { + "$$placeholder": 1 + }, + "clientId": { + "$$placeholder": 1 + }, + "clientSecret": { + "$$placeholder": 1 + } + }, + "gcp": { + "email": { + "$$placeholder": 1 + }, + "privateKey": { + "$$placeholder": 1 + } + }, + "kmip": { + "endpoint": { + "$$placeholder": 1 + } + }, + "local": { + "key": { + "$$placeholder": 1 + } + } + } + } + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "keyvault" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "datakeys" + } + } + ], + "initialData": [ + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [] + } + ], + "tests": [ + { + "description": "create data key with AWS KMS provider", + "operations": [ + { + "name": "createDataKey", + "object": "clientEncryption0", + "arguments": { + "kmsProvider": "aws", + "opts": { + "masterKey": { + "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", + "region": "us-east-1" + } + } + }, + "expectResult": { + "$$type": "binData" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "insert": "datakeys", + "documents": [ + { + "_id": { + "$$type": "binData" + }, + "keyMaterial": { + "$$type": "binData" + }, + "creationDate": { + "$$type": "date" + }, + "updateDate": { + "$$type": "date" + }, + "status": { + "$$exists": true + }, + "masterKey": { + "provider": "aws", + "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", + "region": "us-east-1" + } + } + ], + "writeConcern": { + "w": "majority" + } + } + } + } + ] + } + ] + }, + { + "description": "create datakey with Azure KMS provider", + "operations": [ + { + "name": "createDataKey", + "object": "clientEncryption0", + "arguments": { + "kmsProvider": "azure", + "opts": { + "masterKey": { + "keyVaultEndpoint": "key-vault-csfle.vault.azure.net", + "keyName": "key-name-csfle" + } + } + }, + "expectResult": { + "$$type": "binData" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "insert": "datakeys", + "documents": [ + { + "_id": { + "$$type": "binData" + }, + "keyMaterial": { + "$$type": "binData" + }, + "creationDate": { + "$$type": "date" + }, + "updateDate": { + "$$type": "date" + }, + "status": { + "$$exists": true + }, + "masterKey": { + "provider": "azure", + "keyVaultEndpoint": "key-vault-csfle.vault.azure.net", + "keyName": "key-name-csfle" + } + } + ], + "writeConcern": { + "w": "majority" + } + } + } + } + ] + } + ] + }, + { + "description": "create datakey with GCP KMS provider", + "operations": [ + { + "name": "createDataKey", + "object": "clientEncryption0", + "arguments": { + "kmsProvider": "gcp", + "opts": { + "masterKey": { + "projectId": "devprod-drivers", + "location": "global", + "keyRing": "key-ring-csfle", + "keyName": "key-name-csfle" + } + } + }, + "expectResult": { + "$$type": "binData" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "insert": "datakeys", + "documents": [ + { + "_id": { + "$$type": "binData" + }, + "keyMaterial": { + "$$type": "binData" + }, + "creationDate": { + "$$type": "date" + }, + "updateDate": { + "$$type": "date" + }, + "status": { + "$$exists": true + }, + "masterKey": { + "provider": "gcp", + "projectId": "devprod-drivers", + "location": "global", + "keyRing": "key-ring-csfle", + "keyName": "key-name-csfle" + } + } + ], + "writeConcern": { + "w": "majority" + } + } + } + } + ] + } + ] + }, + { + "description": "create datakey with KMIP KMS provider", + "operations": [ + { + "name": "createDataKey", + "object": "clientEncryption0", + "arguments": { + "kmsProvider": "kmip" + }, + "expectResult": { + "$$type": "binData" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "insert": "datakeys", + "documents": [ + { + "_id": { + "$$type": "binData" + }, + "keyMaterial": { + "$$type": "binData" + }, + "creationDate": { + "$$type": "date" + }, + "updateDate": { + "$$type": "date" + }, + "status": { + "$$exists": true + }, + "masterKey": { + "provider": "kmip", + "keyId": { + "$$type": "string" + } + } + } + ], + "writeConcern": { + "w": "majority" + } + } + } + } + ] + } + ] + }, + { + "description": "create datakey with local KMS provider", + "operations": [ + { + "name": "createDataKey", + "object": "clientEncryption0", + "arguments": { + "kmsProvider": "local" + }, + "expectResult": { + "$$type": "binData" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "insert": "datakeys", + "documents": [ + { + "_id": { + "$$type": "binData" + }, + "keyMaterial": { + "$$type": "binData" + }, + "creationDate": { + "$$type": "date" + }, + "updateDate": { + "$$type": "date" + }, + "status": { + "$$exists": true + }, + "masterKey": { + "provider": "local" + } + } + ], + "writeConcern": { + "w": "majority" + } + } + } + } + ] + } + ] + }, + { + "description": "create datakey with no keyAltName", + "operations": [ + { + "name": "createDataKey", + "object": "clientEncryption0", + "arguments": { + "kmsProvider": "local", + "opts": { + "keyAltNames": [] + } + }, + "expectResult": { + "$$type": "binData" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "insert": "datakeys", + "documents": [ + { + "_id": { + "$$type": "binData" + }, + "keyAltNames": { + "$$exists": false + }, + "keyMaterial": { + "$$type": "binData" + }, + "creationDate": { + "$$type": "date" + }, + "updateDate": { + "$$type": "date" + }, + "status": { + "$$type": "int" + }, + "masterKey": { + "$$type": "object" + } + } + ], + "writeConcern": { + "w": "majority" + } + } + } + } + ] + } + ] + }, + { + "description": "create datakey with single keyAltName", + "operations": [ + { + "name": "createDataKey", + "object": "clientEncryption0", + "arguments": { + "kmsProvider": "local", + "opts": { + "keyAltNames": [ + "local_key" + ] + } + }, + "expectResult": { + "$$type": "binData" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "insert": "datakeys", + "documents": [ + { + "_id": { + "$$type": "binData" + }, + "keyAltNames": [ + "local_key" + ], + "keyMaterial": { + "$$type": "binData" + }, + "creationDate": { + "$$type": "date" + }, + "updateDate": { + "$$type": "date" + }, + "status": { + "$$type": "int" + }, + "masterKey": { + "$$type": "object" + } + } + ], + "writeConcern": { + "w": "majority" + } + } + } + } + ] + } + ] + }, + { + "description": "create datakey with multiple keyAltNames", + "operations": [ + { + "name": "createDataKey", + "object": "clientEncryption0", + "arguments": { + "kmsProvider": "local", + "opts": { + "keyAltNames": [ + "abc", + "def" + ] + } + }, + "expectResult": { + "$$type": "binData" + } + }, + { + "name": "aggregate", + "object": "collection0", + "arguments": { + "pipeline": [ + { + "$project": { + "_id": 0, + "keyAltNames": 1 + } + }, + { + "$unwind": "$keyAltNames" + }, + { + "$sort": { + "keyAltNames": 1 + } + } + ] + }, + "expectResult": [ + { + "keyAltNames": "abc" + }, + { + "keyAltNames": "def" + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "insert": "datakeys", + "documents": [ + { + "_id": { + "$$type": "binData" + }, + "keyAltNames": { + "$$type": "array" + }, + "keyMaterial": { + "$$type": "binData" + }, + "creationDate": { + "$$type": "date" + }, + "updateDate": { + "$$type": "date" + }, + "status": { + "$$type": "int" + }, + "masterKey": { + "$$type": "object" + } + } + ], + "writeConcern": { + "w": "majority" + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "aggregate" + } + } + ] + } + ] + }, + { + "description": "create datakey with custom key material", + "operations": [ + { + "name": "createDataKey", + "object": "clientEncryption0", + "arguments": { + "kmsProvider": "local", + "opts": { + "keyMaterial": { + "$binary": { + "base64": "a2V5X21hdGVyaWFsa2V5X21hdGVyaWFsa2V5X21hdGVyaWFsa2V5X21hdGVyaWFsa2V5X21hdGVyaWFsa2V5X21hdGVyaWFsa2V5X21hdGVyaWFsa2V5X21hdGVyaWFs", + "subType": "00" + } + } + } + }, + "expectResult": { + "$$type": "binData" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "insert": "datakeys", + "documents": [ + { + "_id": { + "$$type": "binData" + }, + "keyMaterial": { + "$$type": "binData" + }, + "creationDate": { + "$$type": "date" + }, + "updateDate": { + "$$type": "date" + }, + "status": { + "$$type": "int" + }, + "masterKey": { + "$$type": "object" + } + } + ], + "writeConcern": { + "w": "majority" + } + } + } + } + ] + } + ] + }, + { + "description": "create datakey with invalid custom key material (too short)", + "operations": [ + { + "name": "createDataKey", + "object": "clientEncryption0", + "arguments": { + "kmsProvider": "local", + "opts": { + "keyMaterial": { + "$binary": { + "base64": "a2V5X21hdGVyaWFsa2V5X21hdGVyaWFsa2V5X21hdGVyaWFsa2V5X21hdGVyaWFsa2V5X21hdGVyaWFsa2V5X21hdGVyaWFsa2V5X21hdGVyaWFs", + "subType": "00" + } + } + } + }, + "expectError": { + "isClientError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [] + } + ] + } + ] +} diff --git a/data/client_side_encryption/unified/deleteKey.json b/data/client_side_encryption/unified/deleteKey.json new file mode 100644 index 0000000000..3a10fb082f --- /dev/null +++ b/data/client_side_encryption/unified/deleteKey.json @@ -0,0 +1,557 @@ +{ + "description": "deleteKey", + "schemaVersion": "1.8", + "runOnRequirements": [ + { + "csfle": true + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "clientEncryption": { + "id": "clientEncryption0", + "clientEncryptionOpts": { + "keyVaultClient": "client0", + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "local": { + "key": { + "$$placeholder": 1 + } + } + } + } + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "keyvault" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "datakeys" + } + } + ], + "initialData": [ + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "_id": { + "$binary": { + "base64": "YXdzYXdzYXdzYXdzYXdzYQ==", + "subType": "04" + } + }, + "keyAltNames": [ + "aws_key" + ], + "keyMaterial": { + "$binary": { + "base64": "AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gFXJqbF0Fy872MD7xl56D/2AAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDO7HPisPUlGzaio9vgIBEIB7/Qow46PMh/8JbEUbdXgTGhLfXPE+KIVW7T8s6YEMlGiRvMu7TV0QCIUJlSHPKZxzlJ2iwuz5yXeOag+EdY+eIQ0RKrsJ3b8UTisZYzGjfzZnxUKLzLoeXremtRCm3x47wCuHKd1dhh6FBbYt5TL2tDaj+vL2GBrKat2L", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "status": 1, + "masterKey": { + "provider": "aws", + "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", + "region": "us-east-1" + } + }, + { + "_id": { + "$binary": { + "base64": "bG9jYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + }, + "keyAltNames": [ + "local_key" + ], + "keyMaterial": { + "$binary": { + "base64": "ABKBldDEoDW323yejOnIRk6YQmlD9d3eQthd16scKL75nz2LjNL9fgPDZWrFFOlqlhMCFaSrNJfGrFUjYk5JFDO7soG5Syb50k1niJoKg4ilsj0L4mpimFUtTpOr2nzZOeQtvAksEXc7gsFgq8gV7t/U3lsaXPY7I0t42DfSE8EGlPdxRjFdHnxh+OR8h7U9b8Qs5K5UuhgyeyxaBZ1Hgw==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "status": 1, + "masterKey": { + "provider": "local" + } + } + ] + } + ], + "tests": [ + { + "description": "delete non-existent data key", + "operations": [ + { + "name": "deleteKey", + "object": "clientEncryption0", + "arguments": { + "id": { + "$binary": { + "base64": "AAAzYXdzYXdzYXdzYXdzYQ==", + "subType": "04" + } + } + }, + "expectResult": { + "deletedCount": 0 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "delete": "datakeys", + "deletes": [ + { + "q": { + "_id": { + "$binary": { + "base64": "AAAzYXdzYXdzYXdzYXdzYQ==", + "subType": "04" + } + } + }, + "limit": 1 + } + ], + "writeConcern": { + "w": "majority" + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "datakeys", + "databaseName": "keyvault", + "documents": [ + { + "_id": { + "$binary": { + "base64": "YXdzYXdzYXdzYXdzYXdzYQ==", + "subType": "04" + } + }, + "keyAltNames": [ + "aws_key" + ], + "keyMaterial": { + "$binary": { + "base64": "AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gFXJqbF0Fy872MD7xl56D/2AAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDO7HPisPUlGzaio9vgIBEIB7/Qow46PMh/8JbEUbdXgTGhLfXPE+KIVW7T8s6YEMlGiRvMu7TV0QCIUJlSHPKZxzlJ2iwuz5yXeOag+EdY+eIQ0RKrsJ3b8UTisZYzGjfzZnxUKLzLoeXremtRCm3x47wCuHKd1dhh6FBbYt5TL2tDaj+vL2GBrKat2L", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "status": 1, + "masterKey": { + "provider": "aws", + "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", + "region": "us-east-1" + } + }, + { + "_id": { + "$binary": { + "base64": "bG9jYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + }, + "keyAltNames": [ + "local_key" + ], + "keyMaterial": { + "$binary": { + "base64": "ABKBldDEoDW323yejOnIRk6YQmlD9d3eQthd16scKL75nz2LjNL9fgPDZWrFFOlqlhMCFaSrNJfGrFUjYk5JFDO7soG5Syb50k1niJoKg4ilsj0L4mpimFUtTpOr2nzZOeQtvAksEXc7gsFgq8gV7t/U3lsaXPY7I0t42DfSE8EGlPdxRjFdHnxh+OR8h7U9b8Qs5K5UuhgyeyxaBZ1Hgw==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "status": 1, + "masterKey": { + "provider": "local" + } + } + ] + } + ] + }, + { + "description": "delete existing AWS data key", + "operations": [ + { + "name": "deleteKey", + "object": "clientEncryption0", + "arguments": { + "id": { + "$binary": { + "base64": "YXdzYXdzYXdzYXdzYXdzYQ==", + "subType": "04" + } + } + }, + "expectResult": { + "deletedCount": 1 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "delete": "datakeys", + "deletes": [ + { + "q": { + "_id": { + "$binary": { + "base64": "YXdzYXdzYXdzYXdzYXdzYQ==", + "subType": "04" + } + } + }, + "limit": 1 + } + ], + "writeConcern": { + "w": "majority" + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "datakeys", + "databaseName": "keyvault", + "documents": [ + { + "_id": { + "$binary": { + "base64": "bG9jYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + }, + "keyAltNames": [ + "local_key" + ], + "keyMaterial": { + "$binary": { + "base64": "ABKBldDEoDW323yejOnIRk6YQmlD9d3eQthd16scKL75nz2LjNL9fgPDZWrFFOlqlhMCFaSrNJfGrFUjYk5JFDO7soG5Syb50k1niJoKg4ilsj0L4mpimFUtTpOr2nzZOeQtvAksEXc7gsFgq8gV7t/U3lsaXPY7I0t42DfSE8EGlPdxRjFdHnxh+OR8h7U9b8Qs5K5UuhgyeyxaBZ1Hgw==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "status": 1, + "masterKey": { + "provider": "local" + } + } + ] + } + ] + }, + { + "description": "delete existing local data key", + "operations": [ + { + "name": "deleteKey", + "object": "clientEncryption0", + "arguments": { + "id": { + "$binary": { + "base64": "bG9jYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + } + }, + "expectResult": { + "deletedCount": 1 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "delete": "datakeys", + "deletes": [ + { + "q": { + "_id": { + "$binary": { + "base64": "bG9jYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + } + }, + "limit": 1 + } + ], + "writeConcern": { + "w": "majority" + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "datakeys", + "databaseName": "keyvault", + "documents": [ + { + "_id": { + "$binary": { + "base64": "YXdzYXdzYXdzYXdzYXdzYQ==", + "subType": "04" + } + }, + "keyAltNames": [ + "aws_key" + ], + "keyMaterial": { + "$binary": { + "base64": "AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gFXJqbF0Fy872MD7xl56D/2AAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDO7HPisPUlGzaio9vgIBEIB7/Qow46PMh/8JbEUbdXgTGhLfXPE+KIVW7T8s6YEMlGiRvMu7TV0QCIUJlSHPKZxzlJ2iwuz5yXeOag+EdY+eIQ0RKrsJ3b8UTisZYzGjfzZnxUKLzLoeXremtRCm3x47wCuHKd1dhh6FBbYt5TL2tDaj+vL2GBrKat2L", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "status": 1, + "masterKey": { + "provider": "aws", + "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", + "region": "us-east-1" + } + } + ] + } + ] + }, + { + "description": "delete existing data key twice", + "operations": [ + { + "name": "deleteKey", + "object": "clientEncryption0", + "arguments": { + "id": { + "$binary": { + "base64": "YXdzYXdzYXdzYXdzYXdzYQ==", + "subType": "04" + } + } + }, + "expectResult": { + "deletedCount": 1 + } + }, + { + "name": "deleteKey", + "object": "clientEncryption0", + "arguments": { + "id": { + "$binary": { + "base64": "YXdzYXdzYXdzYXdzYXdzYQ==", + "subType": "04" + } + } + }, + "expectResult": { + "deletedCount": 0 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "delete": "datakeys", + "deletes": [ + { + "q": { + "_id": { + "$binary": { + "base64": "YXdzYXdzYXdzYXdzYXdzYQ==", + "subType": "04" + } + } + }, + "limit": 1 + } + ], + "writeConcern": { + "w": "majority" + } + } + } + }, + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "delete": "datakeys", + "deletes": [ + { + "q": { + "_id": { + "$binary": { + "base64": "YXdzYXdzYXdzYXdzYXdzYQ==", + "subType": "04" + } + } + }, + "limit": 1 + } + ], + "writeConcern": { + "w": "majority" + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "datakeys", + "databaseName": "keyvault", + "documents": [ + { + "_id": { + "$binary": { + "base64": "bG9jYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + }, + "keyAltNames": [ + "local_key" + ], + "keyMaterial": { + "$binary": { + "base64": "ABKBldDEoDW323yejOnIRk6YQmlD9d3eQthd16scKL75nz2LjNL9fgPDZWrFFOlqlhMCFaSrNJfGrFUjYk5JFDO7soG5Syb50k1niJoKg4ilsj0L4mpimFUtTpOr2nzZOeQtvAksEXc7gsFgq8gV7t/U3lsaXPY7I0t42DfSE8EGlPdxRjFdHnxh+OR8h7U9b8Qs5K5UuhgyeyxaBZ1Hgw==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "status": 1, + "masterKey": { + "provider": "local" + } + } + ] + } + ] + } + ] +} diff --git a/data/client_side_encryption/unified/getKey.json b/data/client_side_encryption/unified/getKey.json new file mode 100644 index 0000000000..2ea3fe7358 --- /dev/null +++ b/data/client_side_encryption/unified/getKey.json @@ -0,0 +1,319 @@ +{ + "description": "getKey", + "schemaVersion": "1.8", + "runOnRequirements": [ + { + "csfle": true + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "clientEncryption": { + "id": "clientEncryption0", + "clientEncryptionOpts": { + "keyVaultClient": "client0", + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "local": { + "key": { + "$$placeholder": 1 + } + } + } + } + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "keyvault" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "datakeys" + } + } + ], + "initialData": [ + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "_id": { + "$binary": { + "base64": "YXdzYXdzYXdzYXdzYXdzYQ==", + "subType": "04" + } + }, + "keyAltNames": [ + "aws_key" + ], + "keyMaterial": { + "$binary": { + "base64": "AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gFXJqbF0Fy872MD7xl56D/2AAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDO7HPisPUlGzaio9vgIBEIB7/Qow46PMh/8JbEUbdXgTGhLfXPE+KIVW7T8s6YEMlGiRvMu7TV0QCIUJlSHPKZxzlJ2iwuz5yXeOag+EdY+eIQ0RKrsJ3b8UTisZYzGjfzZnxUKLzLoeXremtRCm3x47wCuHKd1dhh6FBbYt5TL2tDaj+vL2GBrKat2L", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "status": 1, + "masterKey": { + "provider": "aws", + "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", + "region": "us-east-1" + } + }, + { + "_id": { + "$binary": { + "base64": "bG9jYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + }, + "keyAltNames": [ + "local_key" + ], + "keyMaterial": { + "$binary": { + "base64": "ABKBldDEoDW323yejOnIRk6YQmlD9d3eQthd16scKL75nz2LjNL9fgPDZWrFFOlqlhMCFaSrNJfGrFUjYk5JFDO7soG5Syb50k1niJoKg4ilsj0L4mpimFUtTpOr2nzZOeQtvAksEXc7gsFgq8gV7t/U3lsaXPY7I0t42DfSE8EGlPdxRjFdHnxh+OR8h7U9b8Qs5K5UuhgyeyxaBZ1Hgw==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "status": 1, + "masterKey": { + "provider": "local" + } + } + ] + } + ], + "tests": [ + { + "description": "get non-existent data key", + "operations": [ + { + "name": "getKey", + "object": "clientEncryption0", + "arguments": { + "id": { + "$binary": { + "base64": "AAAzYXdzYXdzYXdzYXdzYQ==", + "subType": "04" + } + } + }, + "expectResult": { + "$$unsetOrMatches": null + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "find": "datakeys", + "filter": { + "_id": { + "$binary": { + "base64": "AAAzYXdzYXdzYXdzYXdzYQ==", + "subType": "04" + } + } + }, + "readConcern": { + "level": "majority" + } + } + } + } + ] + } + ] + }, + { + "description": "get existing AWS data key", + "operations": [ + { + "name": "getKey", + "object": "clientEncryption0", + "arguments": { + "id": { + "$binary": { + "base64": "YXdzYXdzYXdzYXdzYXdzYQ==", + "subType": "04" + } + } + }, + "expectResult": { + "_id": { + "$binary": { + "base64": "YXdzYXdzYXdzYXdzYXdzYQ==", + "subType": "04" + } + }, + "keyAltNames": [ + "aws_key" + ], + "keyMaterial": { + "$binary": { + "base64": "AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gFXJqbF0Fy872MD7xl56D/2AAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDO7HPisPUlGzaio9vgIBEIB7/Qow46PMh/8JbEUbdXgTGhLfXPE+KIVW7T8s6YEMlGiRvMu7TV0QCIUJlSHPKZxzlJ2iwuz5yXeOag+EdY+eIQ0RKrsJ3b8UTisZYzGjfzZnxUKLzLoeXremtRCm3x47wCuHKd1dhh6FBbYt5TL2tDaj+vL2GBrKat2L", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "status": 1, + "masterKey": { + "provider": "aws", + "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", + "region": "us-east-1" + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "find": "datakeys", + "filter": { + "_id": { + "$binary": { + "base64": "YXdzYXdzYXdzYXdzYXdzYQ==", + "subType": "04" + } + } + }, + "readConcern": { + "level": "majority" + } + } + } + } + ] + } + ] + }, + { + "description": "get existing local data key", + "operations": [ + { + "name": "getKey", + "object": "clientEncryption0", + "arguments": { + "id": { + "$binary": { + "base64": "bG9jYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + } + }, + "expectResult": { + "_id": { + "$binary": { + "base64": "bG9jYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + }, + "keyAltNames": [ + "local_key" + ], + "keyMaterial": { + "$binary": { + "base64": "ABKBldDEoDW323yejOnIRk6YQmlD9d3eQthd16scKL75nz2LjNL9fgPDZWrFFOlqlhMCFaSrNJfGrFUjYk5JFDO7soG5Syb50k1niJoKg4ilsj0L4mpimFUtTpOr2nzZOeQtvAksEXc7gsFgq8gV7t/U3lsaXPY7I0t42DfSE8EGlPdxRjFdHnxh+OR8h7U9b8Qs5K5UuhgyeyxaBZ1Hgw==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "status": 1, + "masterKey": { + "provider": "local" + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "find": "datakeys", + "filter": { + "_id": { + "$binary": { + "base64": "bG9jYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + } + }, + "readConcern": { + "level": "majority" + } + } + } + } + ] + } + ] + } + ] +} diff --git a/data/client_side_encryption/unified/getKeyByAltName.json b/data/client_side_encryption/unified/getKeyByAltName.json new file mode 100644 index 0000000000..2505abc16e --- /dev/null +++ b/data/client_side_encryption/unified/getKeyByAltName.json @@ -0,0 +1,289 @@ +{ + "description": "getKeyByAltName", + "schemaVersion": "1.8", + "runOnRequirements": [ + { + "csfle": true + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "clientEncryption": { + "id": "clientEncryption0", + "clientEncryptionOpts": { + "keyVaultClient": "client0", + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "local": { + "key": { + "$$placeholder": 1 + } + } + } + } + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "keyvault" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "datakeys" + } + } + ], + "initialData": [ + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "_id": { + "$binary": { + "base64": "YXdzYXdzYXdzYXdzYXdzYQ==", + "subType": "04" + } + }, + "keyAltNames": [ + "aws_key" + ], + "keyMaterial": { + "$binary": { + "base64": "AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gFXJqbF0Fy872MD7xl56D/2AAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDO7HPisPUlGzaio9vgIBEIB7/Qow46PMh/8JbEUbdXgTGhLfXPE+KIVW7T8s6YEMlGiRvMu7TV0QCIUJlSHPKZxzlJ2iwuz5yXeOag+EdY+eIQ0RKrsJ3b8UTisZYzGjfzZnxUKLzLoeXremtRCm3x47wCuHKd1dhh6FBbYt5TL2tDaj+vL2GBrKat2L", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "status": 1, + "masterKey": { + "provider": "aws", + "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", + "region": "us-east-1" + } + }, + { + "_id": { + "$binary": { + "base64": "bG9jYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + }, + "keyAltNames": [ + "local_key" + ], + "keyMaterial": { + "$binary": { + "base64": "ABKBldDEoDW323yejOnIRk6YQmlD9d3eQthd16scKL75nz2LjNL9fgPDZWrFFOlqlhMCFaSrNJfGrFUjYk5JFDO7soG5Syb50k1niJoKg4ilsj0L4mpimFUtTpOr2nzZOeQtvAksEXc7gsFgq8gV7t/U3lsaXPY7I0t42DfSE8EGlPdxRjFdHnxh+OR8h7U9b8Qs5K5UuhgyeyxaBZ1Hgw==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "status": 1, + "masterKey": { + "provider": "local" + } + } + ] + } + ], + "tests": [ + { + "description": "get non-existent data key", + "operations": [ + { + "name": "getKeyByAltName", + "object": "clientEncryption0", + "arguments": { + "keyAltName": "does_not_exist" + }, + "expectResult": { + "$$unsetOrMatches": null + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "find": "datakeys", + "filter": { + "keyAltNames": "does_not_exist" + }, + "readConcern": { + "level": "majority" + } + } + } + } + ] + } + ] + }, + { + "description": "get existing AWS data key", + "operations": [ + { + "name": "getKeyByAltName", + "object": "clientEncryption0", + "arguments": { + "keyAltName": "aws_key" + }, + "expectResult": { + "_id": { + "$binary": { + "base64": "YXdzYXdzYXdzYXdzYXdzYQ==", + "subType": "04" + } + }, + "keyAltNames": [ + "aws_key" + ], + "keyMaterial": { + "$binary": { + "base64": "AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gFXJqbF0Fy872MD7xl56D/2AAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDO7HPisPUlGzaio9vgIBEIB7/Qow46PMh/8JbEUbdXgTGhLfXPE+KIVW7T8s6YEMlGiRvMu7TV0QCIUJlSHPKZxzlJ2iwuz5yXeOag+EdY+eIQ0RKrsJ3b8UTisZYzGjfzZnxUKLzLoeXremtRCm3x47wCuHKd1dhh6FBbYt5TL2tDaj+vL2GBrKat2L", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "status": 1, + "masterKey": { + "provider": "aws", + "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", + "region": "us-east-1" + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "find": "datakeys", + "filter": { + "keyAltNames": "aws_key" + }, + "readConcern": { + "level": "majority" + } + } + } + } + ] + } + ] + }, + { + "description": "get existing local data key", + "operations": [ + { + "name": "getKeyByAltName", + "object": "clientEncryption0", + "arguments": { + "keyAltName": "local_key" + }, + "expectResult": { + "_id": { + "$binary": { + "base64": "bG9jYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + }, + "keyAltNames": [ + "local_key" + ], + "keyMaterial": { + "$binary": { + "base64": "ABKBldDEoDW323yejOnIRk6YQmlD9d3eQthd16scKL75nz2LjNL9fgPDZWrFFOlqlhMCFaSrNJfGrFUjYk5JFDO7soG5Syb50k1niJoKg4ilsj0L4mpimFUtTpOr2nzZOeQtvAksEXc7gsFgq8gV7t/U3lsaXPY7I0t42DfSE8EGlPdxRjFdHnxh+OR8h7U9b8Qs5K5UuhgyeyxaBZ1Hgw==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "status": 1, + "masterKey": { + "provider": "local" + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "find": "datakeys", + "filter": { + "keyAltNames": "local_key" + }, + "readConcern": { + "level": "majority" + } + } + } + } + ] + } + ] + } + ] +} diff --git a/data/client_side_encryption/unified/getKeys.json b/data/client_side_encryption/unified/getKeys.json new file mode 100644 index 0000000000..d944712357 --- /dev/null +++ b/data/client_side_encryption/unified/getKeys.json @@ -0,0 +1,260 @@ +{ + "description": "getKeys", + "schemaVersion": "1.8", + "runOnRequirements": [ + { + "csfle": true + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "clientEncryption": { + "id": "clientEncryption0", + "clientEncryptionOpts": { + "keyVaultClient": "client0", + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "local": { + "key": { + "$$placeholder": 1 + } + } + } + } + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "keyvault" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "datakeys" + } + } + ], + "initialData": [ + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [] + } + ], + "tests": [ + { + "description": "getKeys with zero key documents", + "operations": [ + { + "name": "getKeys", + "object": "clientEncryption0", + "expectResult": [] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "find": "datakeys", + "filter": {}, + "readConcern": { + "level": "majority" + } + } + } + } + ] + } + ] + }, + { + "description": "getKeys with single key documents", + "operations": [ + { + "name": "createDataKey", + "object": "clientEncryption0", + "arguments": { + "kmsProvider": "local", + "opts": { + "keyAltNames": [ + "abc" + ] + } + }, + "expectResult": { + "$$type": "binData" + } + }, + { + "name": "getKeys", + "object": "clientEncryption0", + "expectResult": [ + { + "_id": { + "$$type": "binData" + }, + "keyAltNames": [ + "abc" + ], + "keyMaterial": { + "$$type": "binData" + }, + "creationDate": { + "$$type": "date" + }, + "updateDate": { + "$$type": "date" + }, + "status": { + "$$type": "int" + }, + "masterKey": { + "$$type": "object" + } + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "find": "datakeys", + "filter": {}, + "readConcern": { + "level": "majority" + } + } + } + } + ] + } + ] + }, + { + "description": "getKeys with many key documents", + "operations": [ + { + "name": "createDataKey", + "object": "clientEncryption0", + "arguments": { + "kmsProvider": "local" + }, + "expectResult": { + "$$type": "binData" + } + }, + { + "name": "createDataKey", + "object": "clientEncryption0", + "arguments": { + "kmsProvider": "local" + }, + "expectResult": { + "$$type": "binData" + } + }, + { + "name": "getKeys", + "object": "clientEncryption0", + "expectResult": [ + { + "_id": { + "$$type": "binData" + }, + "keyMaterial": { + "$$type": "binData" + }, + "creationDate": { + "$$type": "date" + }, + "updateDate": { + "$$type": "date" + }, + "status": { + "$$type": "int" + }, + "masterKey": { + "$$type": "object" + } + }, + { + "_id": { + "$$type": "binData" + }, + "keyMaterial": { + "$$type": "binData" + }, + "creationDate": { + "$$type": "date" + }, + "updateDate": { + "$$type": "date" + }, + "status": { + "$$type": "int" + }, + "masterKey": { + "$$type": "object" + } + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "find": "datakeys", + "filter": {}, + "readConcern": { + "level": "majority" + } + } + } + } + ] + } + ] + } + ] +} diff --git a/data/client_side_encryption/unified/kmsProviders-explicit_kms_credentials.json b/data/client_side_encryption/unified/kmsProviders-explicit_kms_credentials.json new file mode 100644 index 0000000000..7cc74939eb --- /dev/null +++ b/data/client_side_encryption/unified/kmsProviders-explicit_kms_credentials.json @@ -0,0 +1,52 @@ +{ + "description": "kmsProviders-explicit_kms_credentials", + "schemaVersion": "1.8", + "runOnRequirements": [ + { + "csfle": true + } + ], + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "clientEncryption": { + "id": "clientEncryption0", + "clientEncryptionOpts": { + "keyVaultClient": "client0", + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "aws": { + "accessKeyId": "accessKeyId", + "secretAccessKey": "secretAccessKey" + }, + "azure": { + "tenantId": "tenantId", + "clientId": "clientId", + "clientSecret": "clientSecret" + }, + "gcp": { + "email": "email", + "privateKey": "cHJpdmF0ZUtleQo=" + }, + "kmip": { + "endpoint": "endpoint" + }, + "local": { + "key": "a2V5a2V5a2V5a2V5a2V5a2V5a2V5a2V5a2V5a2V5a2V5a2V5a2V5a2V5a2V5a2V5a2V5a2V5a2V5a2V5a2V5a2V5a2V5a2V5a2V5a2V5a2V5a2V5a2V5a2V5a2V5a2V5" + } + } + } + } + } + ], + "tests": [ + { + "description": "", + "operations": [] + } + ] +} diff --git a/data/client_side_encryption/unified/kmsProviders-mixed_kms_credential_fields.json b/data/client_side_encryption/unified/kmsProviders-mixed_kms_credential_fields.json new file mode 100644 index 0000000000..363f2a4576 --- /dev/null +++ b/data/client_side_encryption/unified/kmsProviders-mixed_kms_credential_fields.json @@ -0,0 +1,54 @@ +{ + "description": "kmsProviders-mixed_kms_credential_fields", + "schemaVersion": "1.8", + "runOnRequirements": [ + { + "csfle": true + } + ], + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "clientEncryption": { + "id": "clientEncryption0", + "clientEncryptionOpts": { + "keyVaultClient": "client0", + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "aws": { + "accessKeyId": "accessKeyId", + "secretAccessKey": { + "$$placeholder": 1 + } + }, + "azure": { + "tenantId": "tenantId", + "clientId": { + "$$placeholder": 1 + }, + "clientSecret": { + "$$placeholder": 1 + } + }, + "gcp": { + "email": "email", + "privateKey": { + "$$placeholder": 1 + } + } + } + } + } + } + ], + "tests": [ + { + "description": "", + "operations": [] + } + ] +} diff --git a/data/client_side_encryption/unified/kmsProviders-placeholder_kms_credentials.json b/data/client_side_encryption/unified/kmsProviders-placeholder_kms_credentials.json new file mode 100644 index 0000000000..3f7721f01d --- /dev/null +++ b/data/client_side_encryption/unified/kmsProviders-placeholder_kms_credentials.json @@ -0,0 +1,70 @@ +{ + "description": "kmsProviders-placeholder_kms_credentials", + "schemaVersion": "1.8", + "runOnRequirements": [ + { + "csfle": true + } + ], + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "clientEncryption": { + "id": "clientEncryption0", + "clientEncryptionOpts": { + "keyVaultClient": "client0", + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "aws": { + "accessKeyId": { + "$$placeholder": 1 + }, + "secretAccessKey": { + "$$placeholder": 1 + } + }, + "azure": { + "tenantId": { + "$$placeholder": 1 + }, + "clientId": { + "$$placeholder": 1 + }, + "clientSecret": { + "$$placeholder": 1 + } + }, + "gcp": { + "email": { + "$$placeholder": 1 + }, + "privateKey": { + "$$placeholder": 1 + } + }, + "kmip": { + "endpoint": { + "$$placeholder": 1 + } + }, + "local": { + "key": { + "$$placeholder": 1 + } + } + } + } + } + } + ], + "tests": [ + { + "description": "", + "operations": [] + } + ] +} diff --git a/data/client_side_encryption/unified/kmsProviders-unconfigured_kms.json b/data/client_side_encryption/unified/kmsProviders-unconfigured_kms.json new file mode 100644 index 0000000000..12ca580941 --- /dev/null +++ b/data/client_side_encryption/unified/kmsProviders-unconfigured_kms.json @@ -0,0 +1,39 @@ +{ + "description": "kmsProviders-unconfigured_kms", + "schemaVersion": "1.8", + "runOnRequirements": [ + { + "csfle": true + } + ], + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "clientEncryption": { + "id": "clientEncryption0", + "clientEncryptionOpts": { + "keyVaultClient": "client0", + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "aws": {}, + "azure": {}, + "gcp": {}, + "kmip": {}, + "local": {} + } + } + } + } + ], + "tests": [ + { + "description": "", + "skipReason": "DRIVERS-2280: waiting on driver support for on-demand credentials", + "operations": [] + } + ] +} diff --git a/data/client_side_encryption/unified/removeKeyAltName.json b/data/client_side_encryption/unified/removeKeyAltName.json new file mode 100644 index 0000000000..1b7077077a --- /dev/null +++ b/data/client_side_encryption/unified/removeKeyAltName.json @@ -0,0 +1,672 @@ +{ + "description": "removeKeyAltName", + "schemaVersion": "1.8", + "runOnRequirements": [ + { + "csfle": true + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "clientEncryption": { + "id": "clientEncryption0", + "clientEncryptionOpts": { + "keyVaultClient": "client0", + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "local": { + "key": { + "$$placeholder": 1 + } + } + } + } + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "keyvault" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "datakeys" + } + } + ], + "initialData": [ + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "_id": { + "$binary": { + "base64": "bG9jYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + }, + "keyAltNames": [ + "alternate_name", + "local_key" + ], + "keyMaterial": { + "$binary": { + "base64": "ABKBldDEoDW323yejOnIRk6YQmlD9d3eQthd16scKL75nz2LjNL9fgPDZWrFFOlqlhMCFaSrNJfGrFUjYk5JFDO7soG5Syb50k1niJoKg4ilsj0L4mpimFUtTpOr2nzZOeQtvAksEXc7gsFgq8gV7t/U3lsaXPY7I0t42DfSE8EGlPdxRjFdHnxh+OR8h7U9b8Qs5K5UuhgyeyxaBZ1Hgw==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "status": 1, + "masterKey": { + "provider": "local" + } + } + ] + } + ], + "tests": [ + { + "description": "remove keyAltName from non-existent data key", + "operations": [ + { + "name": "removeKeyAltName", + "object": "clientEncryption0", + "arguments": { + "id": { + "$binary": { + "base64": "AAAjYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + }, + "keyAltName": "does_not_exist" + }, + "expectResult": { + "$$unsetOrMatches": null + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "findAndModify": "datakeys", + "query": { + "_id": { + "$binary": { + "base64": "AAAjYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + } + }, + "update": [ + { + "$set": { + "keyAltNames": { + "$cond": [ + { + "$eq": [ + "$keyAltNames", + [ + "does_not_exist" + ] + ] + }, + "$$REMOVE", + { + "$filter": { + "input": "$keyAltNames", + "cond": { + "$ne": [ + "$$this", + "does_not_exist" + ] + } + } + } + ] + } + } + } + ], + "writeConcern": { + "w": "majority" + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "datakeys", + "databaseName": "keyvault", + "documents": [ + { + "_id": { + "$binary": { + "base64": "bG9jYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + }, + "keyAltNames": [ + "alternate_name", + "local_key" + ], + "keyMaterial": { + "$binary": { + "base64": "ABKBldDEoDW323yejOnIRk6YQmlD9d3eQthd16scKL75nz2LjNL9fgPDZWrFFOlqlhMCFaSrNJfGrFUjYk5JFDO7soG5Syb50k1niJoKg4ilsj0L4mpimFUtTpOr2nzZOeQtvAksEXc7gsFgq8gV7t/U3lsaXPY7I0t42DfSE8EGlPdxRjFdHnxh+OR8h7U9b8Qs5K5UuhgyeyxaBZ1Hgw==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "status": 1, + "masterKey": { + "provider": "local" + } + } + ] + } + ] + }, + { + "description": "remove non-existent keyAltName from existing data key", + "operations": [ + { + "name": "removeKeyAltName", + "object": "clientEncryption0", + "arguments": { + "id": { + "$binary": { + "base64": "bG9jYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + }, + "keyAltName": "does_not_exist" + }, + "expectResult": { + "_id": { + "$binary": { + "base64": "bG9jYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + }, + "keyAltNames": [ + "alternate_name", + "local_key" + ], + "keyMaterial": { + "$binary": { + "base64": "ABKBldDEoDW323yejOnIRk6YQmlD9d3eQthd16scKL75nz2LjNL9fgPDZWrFFOlqlhMCFaSrNJfGrFUjYk5JFDO7soG5Syb50k1niJoKg4ilsj0L4mpimFUtTpOr2nzZOeQtvAksEXc7gsFgq8gV7t/U3lsaXPY7I0t42DfSE8EGlPdxRjFdHnxh+OR8h7U9b8Qs5K5UuhgyeyxaBZ1Hgw==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "status": 1, + "masterKey": { + "provider": "local" + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "findAndModify": "datakeys", + "query": { + "_id": { + "$binary": { + "base64": "bG9jYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + } + }, + "update": [ + { + "$set": { + "keyAltNames": { + "$cond": [ + { + "$eq": [ + "$keyAltNames", + [ + "does_not_exist" + ] + ] + }, + "$$REMOVE", + { + "$filter": { + "input": "$keyAltNames", + "cond": { + "$ne": [ + "$$this", + "does_not_exist" + ] + } + } + } + ] + } + } + } + ], + "writeConcern": { + "w": "majority" + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "datakeys", + "databaseName": "keyvault", + "documents": [ + { + "_id": { + "$binary": { + "base64": "bG9jYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + }, + "keyAltNames": [ + "alternate_name", + "local_key" + ], + "keyMaterial": { + "$binary": { + "base64": "ABKBldDEoDW323yejOnIRk6YQmlD9d3eQthd16scKL75nz2LjNL9fgPDZWrFFOlqlhMCFaSrNJfGrFUjYk5JFDO7soG5Syb50k1niJoKg4ilsj0L4mpimFUtTpOr2nzZOeQtvAksEXc7gsFgq8gV7t/U3lsaXPY7I0t42DfSE8EGlPdxRjFdHnxh+OR8h7U9b8Qs5K5UuhgyeyxaBZ1Hgw==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "status": 1, + "masterKey": { + "provider": "local" + } + } + ] + } + ] + }, + { + "description": "remove an existing keyAltName from an existing data key", + "operations": [ + { + "name": "removeKeyAltName", + "object": "clientEncryption0", + "arguments": { + "id": { + "$binary": { + "base64": "bG9jYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + }, + "keyAltName": "alternate_name" + }, + "expectResult": { + "_id": { + "$binary": { + "base64": "bG9jYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + }, + "keyAltNames": [ + "alternate_name", + "local_key" + ], + "keyMaterial": { + "$binary": { + "base64": "ABKBldDEoDW323yejOnIRk6YQmlD9d3eQthd16scKL75nz2LjNL9fgPDZWrFFOlqlhMCFaSrNJfGrFUjYk5JFDO7soG5Syb50k1niJoKg4ilsj0L4mpimFUtTpOr2nzZOeQtvAksEXc7gsFgq8gV7t/U3lsaXPY7I0t42DfSE8EGlPdxRjFdHnxh+OR8h7U9b8Qs5K5UuhgyeyxaBZ1Hgw==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "status": 1, + "masterKey": { + "provider": "local" + } + } + }, + { + "name": "find", + "object": "collection0", + "arguments": { + "filter": {}, + "projection": { + "_id": 0, + "keyAltNames": 1 + } + }, + "expectResult": [ + { + "keyAltNames": [ + "local_key" + ] + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "findAndModify": "datakeys", + "query": { + "_id": { + "$binary": { + "base64": "bG9jYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + } + }, + "update": [ + { + "$set": { + "keyAltNames": { + "$cond": [ + { + "$eq": [ + "$keyAltNames", + [ + "alternate_name" + ] + ] + }, + "$$REMOVE", + { + "$filter": { + "input": "$keyAltNames", + "cond": { + "$ne": [ + "$$this", + "alternate_name" + ] + } + } + } + ] + } + } + } + ], + "writeConcern": { + "w": "majority" + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "find" + } + } + ] + } + ] + }, + { + "description": "remove the last keyAltName from an existing data key", + "operations": [ + { + "name": "removeKeyAltName", + "object": "clientEncryption0", + "arguments": { + "id": { + "$binary": { + "base64": "bG9jYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + }, + "keyAltName": "alternate_name" + }, + "expectResult": { + "_id": { + "$binary": { + "base64": "bG9jYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + }, + "keyAltNames": [ + "alternate_name", + "local_key" + ], + "keyMaterial": { + "$binary": { + "base64": "ABKBldDEoDW323yejOnIRk6YQmlD9d3eQthd16scKL75nz2LjNL9fgPDZWrFFOlqlhMCFaSrNJfGrFUjYk5JFDO7soG5Syb50k1niJoKg4ilsj0L4mpimFUtTpOr2nzZOeQtvAksEXc7gsFgq8gV7t/U3lsaXPY7I0t42DfSE8EGlPdxRjFdHnxh+OR8h7U9b8Qs5K5UuhgyeyxaBZ1Hgw==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "status": 1, + "masterKey": { + "provider": "local" + } + } + }, + { + "name": "removeKeyAltName", + "object": "clientEncryption0", + "arguments": { + "id": { + "$binary": { + "base64": "bG9jYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + }, + "keyAltName": "local_key" + }, + "expectResult": { + "_id": { + "$binary": { + "base64": "bG9jYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + }, + "keyAltNames": [ + "local_key" + ], + "keyMaterial": { + "$$type": "binData" + }, + "creationDate": { + "$$type": "date" + }, + "updateDate": { + "$$type": "date" + }, + "status": 1, + "masterKey": { + "provider": "local" + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "findAndModify": "datakeys", + "query": { + "_id": { + "$binary": { + "base64": "bG9jYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + } + }, + "update": [ + { + "$set": { + "keyAltNames": { + "$cond": [ + { + "$eq": [ + "$keyAltNames", + [ + "alternate_name" + ] + ] + }, + "$$REMOVE", + { + "$filter": { + "input": "$keyAltNames", + "cond": { + "$ne": [ + "$$this", + "alternate_name" + ] + } + } + } + ] + } + } + } + ], + "writeConcern": { + "w": "majority" + } + } + } + }, + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "findAndModify": "datakeys", + "query": { + "_id": { + "$binary": { + "base64": "bG9jYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + } + }, + "update": [ + { + "$set": { + "keyAltNames": { + "$cond": [ + { + "$eq": [ + "$keyAltNames", + [ + "local_key" + ] + ] + }, + "$$REMOVE", + { + "$filter": { + "input": "$keyAltNames", + "cond": { + "$ne": [ + "$$this", + "local_key" + ] + } + } + } + ] + } + } + } + ] + } + } + } + ] + } + ] + } + ] +} diff --git a/data/client_side_encryption/unified/rewrapManyDataKey-decrypt_failure.json b/data/client_side_encryption/unified/rewrapManyDataKey-decrypt_failure.json new file mode 100644 index 0000000000..4c7d4e8048 --- /dev/null +++ b/data/client_side_encryption/unified/rewrapManyDataKey-decrypt_failure.json @@ -0,0 +1,162 @@ +{ + "description": "rewrapManyDataKey-decrypt_failure", + "schemaVersion": "1.8", + "runOnRequirements": [ + { + "csfle": true + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "clientEncryption": { + "id": "clientEncryption0", + "clientEncryptionOpts": { + "keyVaultClient": "client0", + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "aws": { + "accessKeyId": { + "$$placeholder": 1 + }, + "secretAccessKey": { + "$$placeholder": 1 + } + }, + "azure": { + "tenantId": { + "$$placeholder": 1 + }, + "clientId": { + "$$placeholder": 1 + }, + "clientSecret": { + "$$placeholder": 1 + } + }, + "gcp": { + "email": { + "$$placeholder": 1 + }, + "privateKey": { + "$$placeholder": 1 + } + }, + "kmip": { + "endpoint": { + "$$placeholder": 1 + } + }, + "local": { + "key": { + "$$placeholder": 1 + } + } + } + } + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "keyvault" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "datakeys" + } + } + ], + "initialData": [ + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "_id": { + "$binary": { + "base64": "YXdzYXdzYXdzYXdzYXdzYQ==", + "subType": "04" + } + }, + "keyAltNames": [ + "aws_key" + ], + "keyMaterial": { + "$binary": { + "base64": "AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gFXJqbF0Fy872MD7xl56D/2AAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDO7HPisPUlGzaio9vgIBEIB7/Qow46PMh/8JbEUbdXgTGhLfXPE+KIVW7T8s6YEMlGiRvMu7TV0QCIUJlSHPKZxzlJ2iwuz5yXeOag+EdY+eIQ0RKrsJ3b8UTisZYzGjfzZnxUKLzLoeXremtRCm3x47wCuHKd1dhh6FBbYt5TL2tDaj+vL2GBrKat2L", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "status": 1, + "masterKey": { + "provider": "aws", + "key": "arn:aws:kms:us-east-2:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", + "region": "us-east-2" + } + } + ] + } + ], + "tests": [ + { + "description": "rewrap data key that fails during decryption due to invalid masterKey", + "operations": [ + { + "name": "rewrapManyDataKey", + "object": "clientEncryption0", + "arguments": { + "filter": {}, + "opts": { + "provider": "local" + } + }, + "expectError": { + "isClientError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "find", + "databaseName": "keyvault", + "command": { + "find": "datakeys", + "filter": {}, + "readConcern": { + "level": "majority" + } + } + } + } + ] + } + ] + } + ] +} diff --git a/data/client_side_encryption/unified/rewrapManyDataKey-encrypt_failure.json b/data/client_side_encryption/unified/rewrapManyDataKey-encrypt_failure.json new file mode 100644 index 0000000000..cd2d20c255 --- /dev/null +++ b/data/client_side_encryption/unified/rewrapManyDataKey-encrypt_failure.json @@ -0,0 +1,250 @@ +{ + "description": "rewrapManyDataKey-encrypt_failure", + "schemaVersion": "1.8", + "runOnRequirements": [ + { + "csfle": true + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "clientEncryption": { + "id": "clientEncryption0", + "clientEncryptionOpts": { + "keyVaultClient": "client0", + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "aws": { + "accessKeyId": { + "$$placeholder": 1 + }, + "secretAccessKey": { + "$$placeholder": 1 + } + }, + "azure": { + "tenantId": { + "$$placeholder": 1 + }, + "clientId": { + "$$placeholder": 1 + }, + "clientSecret": { + "$$placeholder": 1 + } + }, + "gcp": { + "email": { + "$$placeholder": 1 + }, + "privateKey": { + "$$placeholder": 1 + } + }, + "kmip": { + "endpoint": { + "$$placeholder": 1 + } + }, + "local": { + "key": { + "$$placeholder": 1 + } + } + } + } + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "keyvault" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "datakeys" + } + } + ], + "initialData": [ + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "_id": { + "$binary": { + "base64": "bG9jYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + }, + "keyAltNames": [ + "local_key" + ], + "keyMaterial": { + "$binary": { + "base64": "ABKBldDEoDW323yejOnIRk6YQmlD9d3eQthd16scKL75nz2LjNL9fgPDZWrFFOlqlhMCFaSrNJfGrFUjYk5JFDO7soG5Syb50k1niJoKg4ilsj0L4mpimFUtTpOr2nzZOeQtvAksEXc7gsFgq8gV7t/U3lsaXPY7I0t42DfSE8EGlPdxRjFdHnxh+OR8h7U9b8Qs5K5UuhgyeyxaBZ1Hgw==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "status": 1, + "masterKey": { + "provider": "local" + } + } + ] + } + ], + "tests": [ + { + "description": "rewrap with invalid masterKey for AWS KMS provider", + "operations": [ + { + "name": "rewrapManyDataKey", + "object": "clientEncryption0", + "arguments": { + "filter": {}, + "opts": { + "provider": "aws", + "masterKey": { + "key": "arn:aws:kms:us-east-2:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", + "region": "us-east-2" + } + } + }, + "expectError": { + "isClientError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "find", + "databaseName": "keyvault", + "command": { + "find": "datakeys", + "filter": {}, + "readConcern": { + "level": "majority" + } + } + } + } + ] + } + ] + }, + { + "description": "rewrap with invalid masterKey for Azure KMS provider", + "operations": [ + { + "name": "rewrapManyDataKey", + "object": "clientEncryption0", + "arguments": { + "filter": {}, + "opts": { + "provider": "azure", + "masterKey": { + "keyVaultEndpoint": "invalid-vault-csfle.vault.azure.net", + "keyName": "invalid-name-csfle" + } + } + }, + "expectError": { + "isClientError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "find", + "databaseName": "keyvault", + "command": { + "find": "datakeys", + "filter": {}, + "readConcern": { + "level": "majority" + } + } + } + } + ] + } + ] + }, + { + "description": "rewrap with invalid masterKey for GCP KMS provider", + "operations": [ + { + "name": "rewrapManyDataKey", + "object": "clientEncryption0", + "arguments": { + "filter": {}, + "opts": { + "provider": "gcp", + "masterKey": { + "projectId": "devprod-drivers", + "location": "global", + "keyRing": "invalid-ring-csfle", + "keyName": "invalid-name-csfle" + } + } + }, + "expectError": { + "isClientError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "find", + "databaseName": "keyvault", + "command": { + "find": "datakeys", + "filter": {}, + "readConcern": { + "level": "majority" + } + } + } + } + ] + } + ] + } + ] +} diff --git a/data/client_side_encryption/unified/rewrapManyDataKey.json b/data/client_side_encryption/unified/rewrapManyDataKey.json new file mode 100644 index 0000000000..6b3c9664a9 --- /dev/null +++ b/data/client_side_encryption/unified/rewrapManyDataKey.json @@ -0,0 +1,1493 @@ +{ + "description": "rewrapManyDataKey", + "schemaVersion": "1.8", + "runOnRequirements": [ + { + "csfle": true + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "clientEncryption": { + "id": "clientEncryption0", + "clientEncryptionOpts": { + "keyVaultClient": "client0", + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "aws": { + "accessKeyId": { + "$$placeholder": 1 + }, + "secretAccessKey": { + "$$placeholder": 1 + } + }, + "azure": { + "tenantId": { + "$$placeholder": 1 + }, + "clientId": { + "$$placeholder": 1 + }, + "clientSecret": { + "$$placeholder": 1 + } + }, + "gcp": { + "email": { + "$$placeholder": 1 + }, + "privateKey": { + "$$placeholder": 1 + } + }, + "kmip": { + "endpoint": { + "$$placeholder": 1 + } + }, + "local": { + "key": { + "$$placeholder": 1 + } + } + } + } + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "keyvault" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "datakeys" + } + } + ], + "initialData": [ + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "_id": { + "$binary": { + "base64": "YXdzYXdzYXdzYXdzYXdzYQ==", + "subType": "04" + } + }, + "keyAltNames": [ + "aws_key" + ], + "keyMaterial": { + "$binary": { + "base64": "AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gFXJqbF0Fy872MD7xl56D/2AAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDO7HPisPUlGzaio9vgIBEIB7/Qow46PMh/8JbEUbdXgTGhLfXPE+KIVW7T8s6YEMlGiRvMu7TV0QCIUJlSHPKZxzlJ2iwuz5yXeOag+EdY+eIQ0RKrsJ3b8UTisZYzGjfzZnxUKLzLoeXremtRCm3x47wCuHKd1dhh6FBbYt5TL2tDaj+vL2GBrKat2L", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "status": 1, + "masterKey": { + "provider": "aws", + "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", + "region": "us-east-1" + } + }, + { + "_id": { + "$binary": { + "base64": "YXp1cmVhenVyZWF6dXJlYQ==", + "subType": "04" + } + }, + "keyAltNames": [ + "azure_key" + ], + "keyMaterial": { + "$binary": { + "base64": "pr01l7qDygUkFE/0peFwpnNlv3iIy8zrQK38Q9i12UCN2jwZHDmfyx8wokiIKMb9kAleeY+vnt3Cf1MKu9kcDmI+KxbNDd+V3ytAAGzOVLDJr77CiWjF9f8ntkXRHrAY9WwnVDANYkDwXlyU0Y2GQFTiW65jiQhUtYLYH63Tk48SsJuQvnWw1Q+PzY8ga+QeVec8wbcThwtm+r2IHsCFnc72Gv73qq7weISw+O4mN08z3wOp5FOS2ZM3MK7tBGmPdBcktW7F8ODGsOQ1FU53OrWUnyX2aTi2ftFFFMWVHqQo7EYuBZHru8RRODNKMyQk0BFfKovAeTAVRv9WH9QU7g==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "status": 1, + "masterKey": { + "provider": "azure", + "keyVaultEndpoint": "key-vault-csfle.vault.azure.net", + "keyName": "key-name-csfle" + } + }, + { + "_id": { + "$binary": { + "base64": "Z2NwZ2NwZ2NwZ2NwZ2NwZw==", + "subType": "04" + } + }, + "keyAltNames": [ + "gcp_key" + ], + "keyMaterial": { + "$binary": { + "base64": "CiQAIgLj0USbQtof/pYRLQO96yg/JEtZbD1UxKueaC37yzT5tTkSiQEAhClWB5ZCSgzHgxv8raWjNB4r7e8ePGdsmSuYTYmLC5oHHS/BdQisConzNKFaobEQZHamTCjyhy5NotKF8MWoo+dyfQApwI29+vAGyrUIQCXzKwRnNdNQ+lb3vJtS5bqvLTvSxKHpVca2kqyC9nhonV+u4qru5Q2bAqUgVFc8fL4pBuvlowZFTQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "status": 1, + "masterKey": { + "provider": "gcp", + "projectId": "devprod-drivers", + "location": "global", + "keyRing": "key-ring-csfle", + "keyName": "key-name-csfle" + } + }, + { + "_id": { + "$binary": { + "base64": "a21pcGttaXBrbWlwa21pcA==", + "subType": "04" + } + }, + "keyAltNames": [ + "kmip_key" + ], + "keyMaterial": { + "$binary": { + "base64": "CklVctHzke4mcytd0TxGqvepkdkQN8NUF4+jV7aZQITAKdz6WjdDpq3lMt9nSzWGG2vAEfvRb3mFEVjV57qqGqxjq2751gmiMRHXz0btStbIK3mQ5xbY9kdye4tsixlCryEwQONr96gwlwKKI9Nubl9/8+uRF6tgYjje7Q7OjauEf1SrJwKcoQ3WwnjZmEqAug0kImCpJ/irhdqPzivRiA==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "status": 1, + "masterKey": { + "provider": "kmip", + "keyId": "1" + } + }, + { + "_id": { + "$binary": { + "base64": "bG9jYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + }, + "keyAltNames": [ + "local_key" + ], + "keyMaterial": { + "$binary": { + "base64": "ABKBldDEoDW323yejOnIRk6YQmlD9d3eQthd16scKL75nz2LjNL9fgPDZWrFFOlqlhMCFaSrNJfGrFUjYk5JFDO7soG5Syb50k1niJoKg4ilsj0L4mpimFUtTpOr2nzZOeQtvAksEXc7gsFgq8gV7t/U3lsaXPY7I0t42DfSE8EGlPdxRjFdHnxh+OR8h7U9b8Qs5K5UuhgyeyxaBZ1Hgw==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "status": 1, + "masterKey": { + "provider": "local" + } + } + ] + } + ], + "tests": [ + { + "description": "no keys to rewrap due to no filter matches", + "operations": [ + { + "name": "rewrapManyDataKey", + "object": "clientEncryption0", + "arguments": { + "filter": { + "keyAltNames": "no_matching_keys" + }, + "opts": { + "provider": "local" + } + }, + "expectResult": { + "bulkWriteResult": { + "$$exists": false + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "find": "datakeys", + "filter": { + "keyAltNames": "no_matching_keys" + }, + "readConcern": { + "level": "majority" + } + } + } + } + ] + } + ] + }, + { + "description": "rewrap with new AWS KMS provider", + "operations": [ + { + "name": "rewrapManyDataKey", + "object": "clientEncryption0", + "arguments": { + "filter": { + "keyAltNames": { + "$ne": "aws_key" + } + }, + "opts": { + "provider": "aws", + "masterKey": { + "key": "arn:aws:kms:us-east-1:579766882180:key/061334ae-07a8-4ceb-a813-8135540e837d", + "region": "us-east-1" + } + } + }, + "expectResult": { + "bulkWriteResult": { + "insertedCount": 0, + "matchedCount": 4, + "modifiedCount": 4, + "deletedCount": 0, + "upsertedCount": 0, + "upsertedIds": {}, + "insertedIds": { + "$$unsetOrMatches": {} + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "find": "datakeys", + "filter": { + "keyAltNames": { + "$ne": "aws_key" + } + }, + "readConcern": { + "level": "majority" + } + } + } + }, + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "update": "datakeys", + "ordered": true, + "updates": [ + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "aws", + "key": "arn:aws:kms:us-east-1:579766882180:key/061334ae-07a8-4ceb-a813-8135540e837d", + "region": "us-east-1" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "aws", + "key": "arn:aws:kms:us-east-1:579766882180:key/061334ae-07a8-4ceb-a813-8135540e837d", + "region": "us-east-1" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "aws", + "key": "arn:aws:kms:us-east-1:579766882180:key/061334ae-07a8-4ceb-a813-8135540e837d", + "region": "us-east-1" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "aws", + "key": "arn:aws:kms:us-east-1:579766882180:key/061334ae-07a8-4ceb-a813-8135540e837d", + "region": "us-east-1" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "writeConcern": { + "w": "majority" + } + } + } + } + ] + } + ] + }, + { + "description": "rewrap with new Azure KMS provider", + "operations": [ + { + "name": "rewrapManyDataKey", + "object": "clientEncryption0", + "arguments": { + "filter": { + "keyAltNames": { + "$ne": "azure_key" + } + }, + "opts": { + "provider": "azure", + "masterKey": { + "keyVaultEndpoint": "key-vault-csfle.vault.azure.net", + "keyName": "key-name-csfle" + } + } + }, + "expectResult": { + "bulkWriteResult": { + "insertedCount": 0, + "matchedCount": 4, + "modifiedCount": 4, + "deletedCount": 0, + "upsertedCount": 0, + "upsertedIds": {}, + "insertedIds": { + "$$unsetOrMatches": {} + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "find": "datakeys", + "filter": { + "keyAltNames": { + "$ne": "azure_key" + } + }, + "readConcern": { + "level": "majority" + } + } + } + }, + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "update": "datakeys", + "ordered": true, + "updates": [ + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "azure", + "keyVaultEndpoint": "key-vault-csfle.vault.azure.net", + "keyName": "key-name-csfle" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "azure", + "keyVaultEndpoint": "key-vault-csfle.vault.azure.net", + "keyName": "key-name-csfle" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "azure", + "keyVaultEndpoint": "key-vault-csfle.vault.azure.net", + "keyName": "key-name-csfle" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "azure", + "keyVaultEndpoint": "key-vault-csfle.vault.azure.net", + "keyName": "key-name-csfle" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "writeConcern": { + "w": "majority" + } + } + } + } + ] + } + ] + }, + { + "description": "rewrap with new GCP KMS provider", + "operations": [ + { + "name": "rewrapManyDataKey", + "object": "clientEncryption0", + "arguments": { + "filter": { + "keyAltNames": { + "$ne": "gcp_key" + } + }, + "opts": { + "provider": "gcp", + "masterKey": { + "projectId": "devprod-drivers", + "location": "global", + "keyRing": "key-ring-csfle", + "keyName": "key-name-csfle" + } + } + }, + "expectResult": { + "bulkWriteResult": { + "insertedCount": 0, + "matchedCount": 4, + "modifiedCount": 4, + "deletedCount": 0, + "upsertedCount": 0, + "upsertedIds": {}, + "insertedIds": { + "$$unsetOrMatches": {} + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "find": "datakeys", + "filter": { + "keyAltNames": { + "$ne": "gcp_key" + } + }, + "readConcern": { + "level": "majority" + } + } + } + }, + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "update": "datakeys", + "ordered": true, + "updates": [ + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "gcp", + "projectId": "devprod-drivers", + "location": "global", + "keyRing": "key-ring-csfle", + "keyName": "key-name-csfle" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "gcp", + "projectId": "devprod-drivers", + "location": "global", + "keyRing": "key-ring-csfle", + "keyName": "key-name-csfle" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "gcp", + "projectId": "devprod-drivers", + "location": "global", + "keyRing": "key-ring-csfle", + "keyName": "key-name-csfle" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "gcp", + "projectId": "devprod-drivers", + "location": "global", + "keyRing": "key-ring-csfle", + "keyName": "key-name-csfle" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "writeConcern": { + "w": "majority" + } + } + } + } + ] + } + ] + }, + { + "description": "rewrap with new KMIP KMS provider", + "operations": [ + { + "name": "rewrapManyDataKey", + "object": "clientEncryption0", + "arguments": { + "filter": { + "keyAltNames": { + "$ne": "kmip_key" + } + }, + "opts": { + "provider": "kmip" + } + }, + "expectResult": { + "bulkWriteResult": { + "insertedCount": 0, + "matchedCount": 4, + "modifiedCount": 4, + "deletedCount": 0, + "upsertedCount": 0, + "upsertedIds": {}, + "insertedIds": { + "$$unsetOrMatches": {} + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "find": "datakeys", + "filter": { + "keyAltNames": { + "$ne": "kmip_key" + } + }, + "readConcern": { + "level": "majority" + } + } + } + }, + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "update": "datakeys", + "ordered": true, + "updates": [ + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "kmip", + "keyId": { + "$$type": "string" + } + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "kmip", + "keyId": { + "$$type": "string" + } + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "kmip", + "keyId": { + "$$type": "string" + } + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "kmip", + "keyId": { + "$$type": "string" + } + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "writeConcern": { + "w": "majority" + } + } + } + } + ] + } + ] + }, + { + "description": "rewrap with new local KMS provider", + "operations": [ + { + "name": "rewrapManyDataKey", + "object": "clientEncryption0", + "arguments": { + "filter": { + "keyAltNames": { + "$ne": "local_key" + } + }, + "opts": { + "provider": "local" + } + }, + "expectResult": { + "bulkWriteResult": { + "insertedCount": 0, + "matchedCount": 4, + "modifiedCount": 4, + "deletedCount": 0, + "upsertedCount": 0, + "upsertedIds": {}, + "insertedIds": { + "$$unsetOrMatches": {} + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "find": "datakeys", + "filter": { + "keyAltNames": { + "$ne": "local_key" + } + }, + "readConcern": { + "level": "majority" + } + } + } + }, + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "update": "datakeys", + "ordered": true, + "updates": [ + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "local" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "local" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "local" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "local" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "writeConcern": { + "w": "majority" + } + } + } + } + ] + } + ] + }, + { + "description": "rewrap with current KMS provider", + "operations": [ + { + "name": "rewrapManyDataKey", + "object": "clientEncryption0", + "arguments": { + "filter": {} + }, + "expectResult": { + "bulkWriteResult": { + "insertedCount": 0, + "matchedCount": 5, + "modifiedCount": 5, + "deletedCount": 0, + "upsertedCount": 0, + "upsertedIds": {}, + "insertedIds": { + "$$unsetOrMatches": {} + } + } + } + }, + { + "name": "find", + "object": "collection0", + "arguments": { + "filter": {}, + "projection": { + "masterKey": 1 + }, + "sort": { + "keyAltNames": 1 + } + }, + "expectResult": [ + { + "_id": { + "$binary": { + "base64": "YXdzYXdzYXdzYXdzYXdzYQ==", + "subType": "04" + } + }, + "masterKey": { + "provider": "aws", + "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", + "region": "us-east-1" + } + }, + { + "_id": { + "$binary": { + "base64": "YXp1cmVhenVyZWF6dXJlYQ==", + "subType": "04" + } + }, + "masterKey": { + "provider": "azure", + "keyVaultEndpoint": "key-vault-csfle.vault.azure.net", + "keyName": "key-name-csfle" + } + }, + { + "_id": { + "$binary": { + "base64": "Z2NwZ2NwZ2NwZ2NwZ2NwZw==", + "subType": "04" + } + }, + "masterKey": { + "provider": "gcp", + "projectId": "devprod-drivers", + "location": "global", + "keyRing": "key-ring-csfle", + "keyName": "key-name-csfle" + } + }, + { + "_id": { + "$binary": { + "base64": "a21pcGttaXBrbWlwa21pcA==", + "subType": "04" + } + }, + "masterKey": { + "provider": "kmip", + "keyId": "1" + } + }, + { + "_id": { + "$binary": { + "base64": "bG9jYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + }, + "masterKey": { + "provider": "local" + } + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "find": "datakeys", + "filter": {}, + "readConcern": { + "level": "majority" + } + } + } + }, + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "update": "datakeys", + "ordered": true, + "updates": [ + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "$$type": "object" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "$$type": "object" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "$$type": "object" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "$$type": "object" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "$$type": "object" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "writeConcern": { + "w": "majority" + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "find" + } + } + ] + } + ] + } + ] +} diff --git a/data/client_side_encryption/unified/test_files.txt b/data/client_side_encryption/unified/test_files.txt new file mode 100644 index 0000000000..1b36358a96 --- /dev/null +++ b/data/client_side_encryption/unified/test_files.txt @@ -0,0 +1,15 @@ +addKeyAltName.json +createDataKey-kms_providers-invalid.json +createDataKey.json +deleteKey.json +getKey.json +getKeyByAltName.json +getKeys.json +kmsProviders-explicit_kms_credentials.json +kmsProviders-mixed_kms_credential_fields.json +kmsProviders-placeholder_kms_credentials.json +kmsProviders-unconfigured_kms.json +removeKeyAltName.json +rewrapManyDataKey-decrypt_failure.json +rewrapManyDataKey-encrypt_failure.json +rewrapManyDataKey.json diff --git a/src/mongocxx/CMakeLists.txt b/src/mongocxx/CMakeLists.txt index a7e6ded938..e2b8bf16f4 100644 --- a/src/mongocxx/CMakeLists.txt +++ b/src/mongocxx/CMakeLists.txt @@ -156,6 +156,7 @@ set(mongocxx_sources options/insert.cpp options/pool.cpp options/replace.cpp + options/rewrap_many_datakey.cpp options/server_api.cpp options/tls.cpp options/transaction.cpp @@ -174,6 +175,7 @@ set(mongocxx_sources result/insert_many.cpp result/insert_one.cpp result/replace_one.cpp + result/rewrap_many_datakey.cpp result/update.cpp uri.cpp validation_criteria.cpp @@ -406,6 +408,8 @@ set_local_dist (src_mongocxx_DIST_local options/private/transaction.hh options/replace.cpp options/replace.hpp + options/rewrap_many_datakey.cpp + options/rewrap_many_datakey.hpp options/server_api.cpp options/server_api.hpp options/ssl.hpp @@ -459,6 +463,8 @@ set_local_dist (src_mongocxx_DIST_local result/insert_one.hpp result/replace_one.cpp result/replace_one.hpp + result/rewrap_many_datakey.cpp + result/rewrap_many_datakey.hpp result/update.cpp result/update.hpp stdx.hpp diff --git a/src/mongocxx/client_encryption.cpp b/src/mongocxx/client_encryption.cpp index 13e8040740..48542cadf2 100644 --- a/src/mongocxx/client_encryption.cpp +++ b/src/mongocxx/client_encryption.cpp @@ -45,5 +45,38 @@ bsoncxx::types::bson_value::value client_encryption::decrypt( return _impl->decrypt(value); } +result::rewrap_many_datakey client_encryption::rewrap_many_datakey( + bsoncxx::document::view_or_value filter, const options::rewrap_many_datakey& opts) { + return _impl->rewrap_many_datakey(filter, opts); +} + +result::delete_result client_encryption::delete_key(bsoncxx::types::bson_value::view_or_value id) { + return _impl->delete_key(id); +} + +stdx::optional client_encryption::get_key( + bsoncxx::types::bson_value::view_or_value id) { + return _impl->get_key(id); +} + +mongocxx::cursor client_encryption::get_keys() { + return _impl->get_keys(); +} + +stdx::optional client_encryption::add_key_alt_name( + bsoncxx::types::bson_value::view_or_value id, bsoncxx::string::view_or_value key_alt_name) { + return _impl->add_key_alt_name(id, key_alt_name); +} + +stdx::optional client_encryption::get_key_by_alt_name( + bsoncxx::string::view_or_value key_alt_name) { + return _impl->get_key_by_alt_name(key_alt_name); +} + +stdx::optional client_encryption::remove_key_alt_name( + bsoncxx::types::bson_value::view_or_value id, bsoncxx::string::view_or_value key_alt_name) { + return _impl->remove_key_alt_name(id, key_alt_name); +} + MONGOCXX_INLINE_NAMESPACE_END } // namespace mongocxx diff --git a/src/mongocxx/client_encryption.hpp b/src/mongocxx/client_encryption.hpp index 4dd1af2b87..15ab547035 100644 --- a/src/mongocxx/client_encryption.hpp +++ b/src/mongocxx/client_encryption.hpp @@ -16,9 +16,13 @@ #include #include +#include #include #include #include +#include +#include +#include #include @@ -112,6 +116,116 @@ class MONGOCXX_API client_encryption { /// bsoncxx::types::bson_value::value decrypt(bsoncxx::types::bson_value::view value); + /// + /// Decrypts multiple data keys and (re-)encrypts them with a new masterKey, + /// or with their current masterKey if a new one is not given. The updated + /// fields of each rewrapped data key is updated in the key vault collection + /// as part of a single bulk write operation. If no data key matches the + /// given filter, no bulk write operation is executed. + /// + /// @param filter + /// Document to filter which keys get re-wrapped. + /// + /// @param opts + /// Options to specify which provider to encrypt the data keys and an optional + /// master key document. + /// + /// @return a RewrapManyDataKeyResult. + /// + /// @throws mongocxx::exception if there is an error rewrapping the key. + /// + /// @see + /// https://www.mongodb.com/docs/manual/reference/method/KeyVault.rewrapManyDataKey/ + /// + result::rewrap_many_datakey rewrap_many_datakey(bsoncxx::document::view_or_value filter, + const options::rewrap_many_datakey& opts); + + /// + /// Removes the key document with the given UUID (BSON binary subtype 0x04) + /// from the key vault collection. + /// + /// @param id Binary id of which key to delete + /// + /// @throws mongocxx::exception if there is an error deleting the key. + /// + /// @return the result of the internal deleteOne() operation on the key vault collection. + /// + /// @see https://www.mongodb.com/docs/manual/reference/method/KeyVault.deleteKey/ + /// + result::delete_result delete_key(bsoncxx::types::bson_value::view_or_value id); + + /// + /// Finds a single key document with the given UUID (BSON binary subtype 0x04). + /// + /// @param id Binary id of which key to delete + /// + /// @throws mongocxx::exception if there is an error getting the key. + /// + /// @return The result of the internal find() operation on the key vault collection. + /// + /// @see https://www.mongodb.com/docs/manual/reference/method/KeyVault.getKey/ + /// + stdx::optional get_key(bsoncxx::types::bson_value::view_or_value id); + + /// + /// Finds all documents in the key vault collection. + /// + /// @throws mongocxx::exception if there is an error getting the keys. + /// + /// @return the result of the internal find() operation on the key vault collection. + /// + /// @see https://www.mongodb.com/docs/manual/reference/method/KeyVault.getKeys/ + /// + mongocxx::cursor get_keys(); + + /// + /// Adds a keyAltName to the keyAltNames array of the key document in the + /// key vault collection with the given UUID (BSON binary subtype 0x04). + /// + /// @param id Binary id of the key to add the key alternate name to + /// + /// @param key_alt_name String alternative name for the key + /// + /// @throws mongocxx::exception if there is an error adding the key alt name. + /// + /// @return the previous version of the key document. + /// + /// @see https://www.mongodb.com/docs/manual/reference/method/KeyVault.addKeyAlternateName/ + /// + stdx::optional add_key_alt_name( + bsoncxx::types::bson_value::view_or_value id, bsoncxx::string::view_or_value key_alt_name); + + /// + /// Removes a keyAltName from the keyAltNames array of the key document in + /// the key vault collection with the given UUID (BSON binary subtype 0x04). + /// + /// @param id Binary id of the key to remove the key alternate name from + /// + /// @param key_alt_name String alternative name for the key + /// + /// @throws mongocxx::exception if there is an error removing the key alt name. + /// + /// @return The previous version of the key document. + /// + /// @see https://www.mongodb.com/docs/manual/reference/method/KeyVault.removeKeyAlternateName/ + /// + stdx::optional remove_key_alt_name( + bsoncxx::types::bson_value::view_or_value id, bsoncxx::string::view_or_value key_alt_name); + + /// + /// Get the key document from the key vault collection with the provided name. + /// + /// @param key_alt_name String alternative name for the key + /// + /// @throws mongocxx::exception if there is an error getting the key by alt name. + /// + /// @return A key document in the key vault collection with the given keyAltName. + /// + /// @see https://www.mongodb.com/docs/manual/reference/method/KeyVault.getKeyByAltName/ + /// + stdx::optional get_key_by_alt_name( + bsoncxx::string::view_or_value key_alt_name); + private: class MONGOCXX_PRIVATE impl; diff --git a/src/mongocxx/cursor.hpp b/src/mongocxx/cursor.hpp index f85ce59a0f..2823f83751 100644 --- a/src/mongocxx/cursor.hpp +++ b/src/mongocxx/cursor.hpp @@ -81,6 +81,7 @@ class MONGOCXX_API cursor { private: friend class collection; friend class client; + friend class client_encryption; friend class database; friend class index_view; friend class cursor::iterator; diff --git a/src/mongocxx/options/data_key.cpp b/src/mongocxx/options/data_key.cpp index f61801655b..574cecf7e1 100644 --- a/src/mongocxx/options/data_key.cpp +++ b/src/mongocxx/options/data_key.cpp @@ -64,9 +64,24 @@ void* data_key::convert() const { bson_free(names); } + if (_key_material) { + uint32_t size = static_cast(_key_material->size()); + libmongoc::client_encryption_datakey_opts_set_keymaterial( + opts_t, _key_material->data(), size); + } + return opts_t; } +data_key& data_key::key_material(data_key::key_material_type key_material) { + _key_material = std::move(key_material); + return *this; +} + +const stdx::optional& data_key::key_material() { + return _key_material; +} + } // namespace options MONGOCXX_INLINE_NAMESPACE_END } // namespace mongocxx diff --git a/src/mongocxx/options/data_key.hpp b/src/mongocxx/options/data_key.hpp index 530af2988e..212776fe4f 100644 --- a/src/mongocxx/options/data_key.hpp +++ b/src/mongocxx/options/data_key.hpp @@ -121,12 +121,44 @@ class MONGOCXX_API data_key { /// const std::vector& key_alt_names() const; + /// + /// Sets the binary data for the key material + /// + /// An optional BinData of 96 bytes to use as custom key material for the + /// data key being created. If keyMaterial is given, the custom key material + /// is used for encrypting and decrypting data. + /// + /// Otherwise, the key material for the new data key is generated from a + /// cryptographically secure random device. + /// + /// @param key_material + /// The binary data for the keyMaterial + /// + /// @return + /// A reference to this object. + /// + /// @see https://www.mongodb.com/docs/v6.0/reference/method/KeyVault.createKey/ + /// + using key_material_type = std::vector; + data_key& key_material(key_material_type key_material); + + /// + /// Gets the keyMaterial as binary data + /// + /// @return + /// The binary data for the key material + /// + /// @see https://www.mongodb.com/docs/v6.0/reference/method/KeyVault.createKey/ + /// + const stdx::optional& key_material(); + private: friend class mongocxx::client_encryption; MONGOCXX_PRIVATE void* convert() const; stdx::optional _master_key; std::vector _key_alt_names; + stdx::optional _key_material; }; } // namespace options diff --git a/src/mongocxx/options/rewrap_many_datakey.cpp b/src/mongocxx/options/rewrap_many_datakey.cpp new file mode 100644 index 0000000000..0ee398b061 --- /dev/null +++ b/src/mongocxx/options/rewrap_many_datakey.cpp @@ -0,0 +1,48 @@ +// Copyright 2023 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. + +#include +#include +#include +#include +#include +#include + +#include + +namespace mongocxx { +MONGOCXX_INLINE_NAMESPACE_BEGIN +namespace options { + +rewrap_many_datakey& rewrap_many_datakey::provider(bsoncxx::string::view_or_value provider) { + _provider = std::move(provider); + return *this; +} + +bsoncxx::string::view_or_value rewrap_many_datakey::provider() const { + return _provider; +} + +rewrap_many_datakey& rewrap_many_datakey::master_key(bsoncxx::document::view_or_value master_key) { + _master_key = std::move(master_key); + return *this; +} + +const stdx::optional& rewrap_many_datakey::master_key() const { + return _master_key; +} + +} // namespace options +MONGOCXX_INLINE_NAMESPACE_END +} // namespace mongocxx diff --git a/src/mongocxx/options/rewrap_many_datakey.hpp b/src/mongocxx/options/rewrap_many_datakey.hpp new file mode 100644 index 0000000000..a8d352650e --- /dev/null +++ b/src/mongocxx/options/rewrap_many_datakey.hpp @@ -0,0 +1,109 @@ +// Copyright 2023 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. + +#pragma once + +#include +#include +#include +#include +#include + +#include + +namespace mongocxx { +MONGOCXX_INLINE_NAMESPACE_BEGIN + +class client_encryption; + +namespace options { + +class MONGOCXX_API rewrap_many_datakey { + public: + /// + /// Set the optional KMS provider use to encrypt the data keys. Do not set to use the current + /// KMS provider(s). + /// + /// A KMS provider (AWS KMS, Azure Key Vault, GCP KMS, the local provider, + /// or KMIP) is used to decrypt data keys after fetching from the MongoDB + /// Key Vault, and encrypt newly created data keys. + /// + /// @param provider String name of the provider. + /// + /// @return + /// An optional document containing the TLS options. + /// + /// @see + /// https://www.mongodb.com/docs/manual/core/csfle/reference/kms-providers/#std-label-csfle-reference-kms-providers + /// + rewrap_many_datakey& provider(bsoncxx::string::view_or_value provider); + + /// + /// Get the KMS provider + /// + /// A KMS provider (AWS KMS, Azure Key Vault, GCP KMS, the local provider, + /// or KMIP) is used to decrypt data keys after fetching from the MongoDB + /// Key Vault, and encrypt newly created data keys. + /// + /// @return + /// An optional string name of the provider. + /// + /// @see + /// https://www.mongodb.com/docs/manual/core/csfle/reference/kms-providers/#std-label-csfle-reference-kms-providers + /// + bsoncxx::string::view_or_value provider() const; + + /// + /// Set the masterKey option. + /// + /// The masterKey document MUST have the fields corresponding to the given + /// provider as specified in masterKey. masterKey MUST NOT be given if it is + /// not applicable for the given provider. + /// + /// @param master_key A document of the master key. + /// + /// @return + /// A reference to this object to facilitate method chaining. + /// + /// @see + /// https://www.mongodb.com/docs/manual/core/csfle/reference/kms-providers/#std-label-csfle-reference-kms-providers-create-and-store + /// + rewrap_many_datakey& master_key(bsoncxx::document::view_or_value master_key); + + /// + /// Get the masterKey option. + /// + /// The masterKey document MUST have the fields corresponding to the given + /// provider as specified in masterKey. masterKey MUST NOT be given if it is + /// not applicable for the given provider. + /// + /// @return + /// A reference to this object to facilitate method chaining. + /// + /// @see + /// https://www.mongodb.com/docs/manual/core/csfle/reference/kms-providers/#std-label-csfle-reference-kms-providers-create-and-store + /// + const stdx::optional& master_key() const; + + private: + friend class mongocxx::client_encryption; + bsoncxx::string::view_or_value _provider; + stdx::optional _master_key; +}; + +} // namespace options +MONGOCXX_INLINE_NAMESPACE_END +} // namespace mongocxx + +#include diff --git a/src/mongocxx/private/client_encryption.hh b/src/mongocxx/private/client_encryption.hh index 761f35fe0b..3105503f0a 100644 --- a/src/mongocxx/private/client_encryption.hh +++ b/src/mongocxx/private/client_encryption.hh @@ -24,8 +24,10 @@ #include #include #include +#include #include #include +#include #include @@ -142,6 +144,196 @@ class client_encryption::impl { return decrypted; } + result::rewrap_many_datakey rewrap_many_datakey(bsoncxx::document::view_or_value filter, + const options::rewrap_many_datakey& opts) { + bson_error_t error; + + std::unique_ptr + result_ptr(libmongoc::client_encryption_rewrap_many_datakey_result_new(), + libmongoc::client_encryption_rewrap_many_datakey_result_destroy); + + const auto provider = opts.provider(); + const auto provider_terminated = provider.terminated(); + const char* provider_ptr = + provider_terminated.view().empty() ? nullptr : provider_terminated.data(); + + const auto optional_master_key = opts.master_key(); + stdx::optional bson_master_key; + if (optional_master_key) { + bson_master_key.emplace(); + bson_master_key->init_from_static(optional_master_key.value().view()); + } + + libbson::scoped_bson_t bson_filter; + bson_filter.init_from_static(filter); + + const auto r = libmongoc::client_encryption_rewrap_many_datakey( + _client_encryption_t, + bson_filter.bson(), + provider_ptr, + bson_master_key ? bson_master_key->bson() : nullptr, + result_ptr.get(), + &error); + + if (!r) { + throw_exception(error); + } + + const bson_t* bulk_write_result = + libmongoc::client_encryption_rewrap_many_datakey_result_get_bulk_write_result( + result_ptr.get()); + + if (bulk_write_result) { + const auto doc = + bsoncxx::document::view(bson_get_data(bulk_write_result), bulk_write_result->len); + return result::rewrap_many_datakey(result::bulk_write(bsoncxx::document::value(doc))); + } else { + return result::rewrap_many_datakey(); + } + } + + result::delete_result delete_key(bsoncxx::types::bson_value::view_or_value id) { + using bsoncxx::builder::basic::kvp; + using bsoncxx::builder::basic::make_document; + + bson_error_t error; + libbson::scoped_bson_t reply_ptr; + + bson_value_t libbson_key; + + convert_to_libbson(&libbson_key, id); + + const auto r = libmongoc::client_encryption_delete_key( + _client_encryption_t, &libbson_key, reply_ptr.bson_for_init(), &error); + + bson_value_destroy(&libbson_key); + + if (!r) { + throw_exception(error); + } + + const auto val = reply_ptr.view(); + + // The C driver calls this field "deletedCount", but the C++ driver + // refers to this as "nRemoved". Make a new document with the field name + // changed to get around this. + // + // See: mongo-cxx-driver/src/mongocxx/result/bulk_write.cpp + // Function: std::int32_t bulk_write::deleted_count() const { + // return view()["nRemoved"].get_int32(); + // } + return result::delete_result( + result::bulk_write(make_document(kvp("nRemoved", val["deletedCount"].get_int32())))); + } + + stdx::optional get_key(bsoncxx::types::bson_value::view_or_value id) { + bson_error_t error; + libbson::scoped_bson_t key_doc; + + bson_value_t libbson_value; + + convert_to_libbson(&libbson_value, id); + + const auto r = libmongoc::client_encryption_get_key( + _client_encryption_t, &libbson_value, key_doc.bson_for_init(), &error); + + const auto cleanup = [&]() { bson_value_destroy(&libbson_value); }; + + if (!r) { + cleanup(); + throw_exception(error); + } + + cleanup(); + return key_doc.view().empty() ? stdx::nullopt + : stdx::optional{key_doc.steal()}; + } + + mongocxx::cursor get_keys() { + bson_error_t error; + + mongoc_cursor_t* cursor = mongoc_client_encryption_get_keys(_client_encryption_t, &error); + + if (!cursor) { + throw_exception(error); + } + + mongocxx::cursor wrapped_cursor{cursor}; + return wrapped_cursor; + } + + stdx::optional add_key_alt_name( + bsoncxx::types::bson_value::view_or_value id, bsoncxx::string::view_or_value key_alt_name) { + bson_error_t error; + libbson::scoped_bson_t key_doc; + bson_value_t key_id; + + convert_to_libbson(&key_id, id); + + const auto key_alt_name_terminated = key_alt_name.terminated(); + const auto r = libmongoc::client_encryption_add_key_alt_name(_client_encryption_t, + &key_id, + key_alt_name_terminated.data(), + key_doc.bson_for_init(), + &error); + + const auto cleanup = [&]() { bson_value_destroy(&key_id); }; + + if (!r) { + cleanup(); + throw_exception(error); + } + + cleanup(); + return key_doc.view().empty() ? stdx::nullopt + : stdx::optional{key_doc.steal()}; + } + + stdx::optional get_key_by_alt_name( + bsoncxx::string::view_or_value key_alt_name) { + bson_error_t error; + libbson::scoped_bson_t key_doc; + + const auto key_alt_name_terminated = key_alt_name.terminated(); + const auto r = libmongoc::client_encryption_get_key_by_alt_name( + _client_encryption_t, key_alt_name_terminated.data(), key_doc.bson_for_init(), &error); + + if (!r) { + throw_exception(error); + } + return key_doc.view().empty() ? stdx::nullopt + : stdx::optional{key_doc.steal()}; + } + + stdx::optional remove_key_alt_name( + bsoncxx::types::bson_value::view_or_value id, bsoncxx::string::view_or_value key_alt_name) { + bson_error_t error; + libbson::scoped_bson_t key_doc; + bson_value_t key_id; + + convert_to_libbson(&key_id, id); + + const auto key_alt_name_terminated = key_alt_name.terminated(); + const auto r = + libmongoc::client_encryption_remove_key_alt_name(_client_encryption_t, + &key_id, + key_alt_name_terminated.data(), + key_doc.bson_for_init(), + &error); + + const auto cleanup = [&]() { bson_value_destroy(&key_id); }; + + if (!r) { + cleanup(); + throw_exception(error); + } + + cleanup(); + return key_doc.view().empty() ? stdx::nullopt + : stdx::optional{key_doc.steal()}; + } + options::client_encryption _opts; mongoc_client_encryption_t* _client_encryption_t; }; diff --git a/src/mongocxx/private/libmongoc_symbols.hh b/src/mongocxx/private/libmongoc_symbols.hh index 0bae6c9bfd..515b27b066 100644 --- a/src/mongocxx/private/libmongoc_symbols.hh +++ b/src/mongocxx/private/libmongoc_symbols.hh @@ -124,12 +124,15 @@ MONGOCXX_LIBMONGOC_SYMBOL(cleanup) MONGOCXX_LIBMONGOC_SYMBOL(client_command_simple_with_server_id) MONGOCXX_LIBMONGOC_SYMBOL(client_destroy) MONGOCXX_LIBMONGOC_SYMBOL(client_enable_auto_encryption) +MONGOCXX_LIBMONGOC_SYMBOL(client_encryption_add_key_alt_name) MONGOCXX_LIBMONGOC_SYMBOL(client_encryption_create_datakey) MONGOCXX_LIBMONGOC_SYMBOL(client_encryption_datakey_opts_destroy) MONGOCXX_LIBMONGOC_SYMBOL(client_encryption_datakey_opts_new) MONGOCXX_LIBMONGOC_SYMBOL(client_encryption_datakey_opts_set_keyaltnames) +MONGOCXX_LIBMONGOC_SYMBOL(client_encryption_datakey_opts_set_keymaterial) MONGOCXX_LIBMONGOC_SYMBOL(client_encryption_datakey_opts_set_masterkey) MONGOCXX_LIBMONGOC_SYMBOL(client_encryption_decrypt) +MONGOCXX_LIBMONGOC_SYMBOL(client_encryption_delete_key) MONGOCXX_LIBMONGOC_SYMBOL(client_encryption_destroy) MONGOCXX_LIBMONGOC_SYMBOL(client_encryption_encrypt) MONGOCXX_LIBMONGOC_SYMBOL(client_encryption_encrypt_opts_destroy) @@ -139,6 +142,9 @@ MONGOCXX_LIBMONGOC_SYMBOL(client_encryption_encrypt_opts_set_contention_factor) MONGOCXX_LIBMONGOC_SYMBOL(client_encryption_encrypt_opts_set_keyaltname) MONGOCXX_LIBMONGOC_SYMBOL(client_encryption_encrypt_opts_set_keyid) MONGOCXX_LIBMONGOC_SYMBOL(client_encryption_encrypt_opts_set_query_type) +MONGOCXX_LIBMONGOC_SYMBOL(client_encryption_get_key) +MONGOCXX_LIBMONGOC_SYMBOL(client_encryption_get_key_by_alt_name) +MONGOCXX_LIBMONGOC_SYMBOL(client_encryption_get_keys) MONGOCXX_LIBMONGOC_SYMBOL(client_encryption_opts_destroy) MONGOCXX_LIBMONGOC_SYMBOL(client_encryption_opts_new) MONGOCXX_LIBMONGOC_SYMBOL(client_encryption_opts_set_keyvault_client) @@ -146,6 +152,11 @@ MONGOCXX_LIBMONGOC_SYMBOL(client_encryption_opts_set_keyvault_namespace) MONGOCXX_LIBMONGOC_SYMBOL(client_encryption_opts_set_kms_providers) MONGOCXX_LIBMONGOC_SYMBOL(client_encryption_opts_set_tls_opts) MONGOCXX_LIBMONGOC_SYMBOL(client_encryption_new) +MONGOCXX_LIBMONGOC_SYMBOL(client_encryption_remove_key_alt_name) +MONGOCXX_LIBMONGOC_SYMBOL(client_encryption_rewrap_many_datakey) +MONGOCXX_LIBMONGOC_SYMBOL(client_encryption_rewrap_many_datakey_result_get_bulk_write_result) +MONGOCXX_LIBMONGOC_SYMBOL(client_encryption_rewrap_many_datakey_result_destroy) +MONGOCXX_LIBMONGOC_SYMBOL(client_encryption_rewrap_many_datakey_result_new) MONGOCXX_LIBMONGOC_SYMBOL(client_find_databases_with_opts) MONGOCXX_LIBMONGOC_SYMBOL(client_get_collection) MONGOCXX_LIBMONGOC_SYMBOL(client_get_database) diff --git a/src/mongocxx/result/rewrap_many_datakey.cpp b/src/mongocxx/result/rewrap_many_datakey.cpp new file mode 100644 index 0000000000..b6899695f1 --- /dev/null +++ b/src/mongocxx/result/rewrap_many_datakey.cpp @@ -0,0 +1,35 @@ +// Copyright 2023 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. + +#include +#include +#include + +#include + +namespace mongocxx { +MONGOCXX_INLINE_NAMESPACE_BEGIN +namespace result { + +rewrap_many_datakey::rewrap_many_datakey(mongocxx::result::bulk_write bulk_write_result_doc) { + _result = std::move(bulk_write_result_doc); +} + +const bsoncxx::stdx::optional& rewrap_many_datakey::result() { + return _result; +} + +} // namespace result +MONGOCXX_INLINE_NAMESPACE_END +} // namespace mongocxx diff --git a/src/mongocxx/result/rewrap_many_datakey.hpp b/src/mongocxx/result/rewrap_many_datakey.hpp new file mode 100644 index 0000000000..c9279ddf79 --- /dev/null +++ b/src/mongocxx/result/rewrap_many_datakey.hpp @@ -0,0 +1,49 @@ +// Copyright 2023 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. + +#pragma once + +#include +#include +#include + +#include + +namespace mongocxx { +MONGOCXX_INLINE_NAMESPACE_BEGIN +namespace result { + +/// Class representing the result of a MongoDB rewrap_many_datakey operation. +class MONGOCXX_API rewrap_many_datakey { + public: + rewrap_many_datakey() = default; + + explicit rewrap_many_datakey(mongocxx::result::bulk_write bulk_write_result_doc); + + /// + /// Returns the bulk write result for this rewrap_many_datakey operation. + /// + /// @return The raw bulk write result. + /// + const bsoncxx::stdx::optional& result(); + + private: + bsoncxx::stdx::optional _result; +}; + +} // namespace result +MONGOCXX_INLINE_NAMESPACE_END +} // namespace mongocxx + +#include diff --git a/src/mongocxx/test/CMakeLists.txt b/src/mongocxx/test/CMakeLists.txt index d5fbc71f8d..a3fa993076 100644 --- a/src/mongocxx/test/CMakeLists.txt +++ b/src/mongocxx/test/CMakeLists.txt @@ -267,6 +267,7 @@ set_tests_properties(read_write_concern_specs PROPERTIES set_property(TEST unified_format_spec APPEND PROPERTY ENVIRONMENT "CHANGE_STREAMS_UNIFIED_TESTS_PATH=${PROJECT_SOURCE_DIR}/../../data/change-streams/unified" + "CLIENT_SIDE_ENCRYPTION_UNIFIED_TESTS_PATH=${PROJECT_SOURCE_DIR}/../../data/client_side_encryption/unified" "COLLECTION_MANAGEMENT_TESTS_PATH=${PROJECT_SOURCE_DIR}/../../data/collection-management" "CRUD_UNIFIED_TESTS_PATH=${PROJECT_SOURCE_DIR}/../../data/crud/unified" "SESSION_UNIFIED_TESTS_PATH=${PROJECT_SOURCE_DIR}/../../data/sessions/unified/" diff --git a/src/mongocxx/test/client_side_encryption.cpp b/src/mongocxx/test/client_side_encryption.cpp index 140fb54c9c..b66b28dbc6 100644 --- a/src/mongocxx/test/client_side_encryption.cpp +++ b/src/mongocxx/test/client_side_encryption.cpp @@ -30,10 +30,12 @@ #include #include #include +#include #include #include #include #include +#include #include #include #include @@ -66,6 +68,7 @@ const auto kKmipKeyUUID = "\x28\xc2\x0f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ using bsoncxx::builder::concatenate; using bsoncxx::builder::basic::kvp; +using bsoncxx::builder::basic::make_array; using bsoncxx::builder::basic::make_document; using bsoncxx::builder::basic::sub_document; @@ -2390,4 +2393,260 @@ TEST_CASE("Explicit Encryption", "[client_side_encryption]") { } } +TEST_CASE("Unique Index on keyAltNames", "[client_side_encryption]") { + instance::current(); + + if (!mongocxx::test_util::should_run_client_side_encryption_test()) { + return; + } + + if (!test_util::newer_than(uri{}, "4.2")) { + WARN("Skipping - requires MongoDB server 4.2+"); + return; + } + + // 1. Create a MongoClient object (referred to as client). + mongocxx::client client{mongocxx::uri{}, test_util::add_test_server_api()}; + + // 2. Using client, drop the collection keyvault.datakeys. + client["keyvault"]["datakeys"].drop(); + + // 3. Using client, create a unique index on keyAltNames with a partial index filter for only + // documents where keyAltNames exists using writeConcern "majority". The command should be + // equivalent to: + // + // db.runCommand( + // { + // createIndexes: "datakeys", + // indexes: [ + // { + // name: "keyAltNames_1", + // key: { "keyAltNames": 1 }, + // unique: true, + // partialFilterExpression: { keyAltNames: { $exists: true } } + // } + // ], + // writeConcern: { w: "majority" } + // } + // ) + auto db = client["keyvault"]; + db.run_command(make_document( + kvp("createIndexes", "datakeys"), + kvp("indexes", + make_array(make_document( + kvp("name", "keyAltNames_1"), + kvp("key", make_document(kvp("keyAltNames", 1))), + kvp("unique", true), + kvp("partialFilterExpression", + make_document(kvp("keyAltNames", make_document(kvp("$exists", true)))))))), + kvp("writeConcern", make_document(kvp("w", "majority"))))); + + // 4. Create a ClientEncryption object (referred to as client_encryption) with client set as the + // keyVaultClient. + options::client_encryption ce_opts; + ce_opts.key_vault_client(&client); + ce_opts.key_vault_namespace({"keyvault", "datakeys"}); + ce_opts.kms_providers(_make_kms_doc(false)); + client_encryption client_encryption(std::move(ce_opts)); + + // 5. Using client_encryption, create a data key with a local KMS provider and the keyAltName + // "def". + mongocxx::options::data_key dk_opts; + dk_opts.key_alt_names({"def"}); + std::string provider = "local"; + auto existing_key = client_encryption.create_data_key(provider, dk_opts); + + SECTION("Case 1: createKey()") { + // 1. Use client_encryption to create a new local data key with a keyAltName "abc" and + // assert the operation does not fail. + { + mongocxx::options::data_key dk_opts; + dk_opts.key_alt_names({"abc"}); + std::string provider = "local"; + client_encryption.create_data_key(provider, dk_opts); + } + + // 2. Repeat Step 1 and assert the operation fails due to a duplicate key server error + // (error code 11000). + { + mongocxx::options::data_key dk_opts; + dk_opts.key_alt_names({"abc"}); + std::string provider = "local"; + bool exception_thrown = false; + try { + client_encryption.create_data_key(provider, dk_opts); + } catch (mongocxx::operation_exception& e) { + REQUIRE(std::strstr( + e.what(), + "E11000 duplicate key error collection: keyvault.datakeys index: keyAltNames_1 " + "dup key: { keyAltNames: \"abc\" }: generic server error")); + exception_thrown = true; + } + REQUIRE(exception_thrown); + } + + // 3. Use client_encryption to create a new local data key with a keyAltName "def" and + // assert the operation fails due to a duplicate key server error (error code 11000). + { + mongocxx::options::data_key dk_opts; + dk_opts.key_alt_names({"def"}); + std::string provider = "local"; + bool exception_thrown = false; + try { + client_encryption.create_data_key(provider, dk_opts); + } catch (mongocxx::operation_exception& e) { + REQUIRE(std::strstr( + e.what(), + "E11000 duplicate key error collection: keyvault.datakeys index: keyAltNames_1 " + "dup key: { keyAltNames: \"def\" }: generic server error")); + exception_thrown = true; + } + REQUIRE(exception_thrown); + } + } + + SECTION("Case 2: addKeyAltName()") { + // 1. Use client_encryption to create a new local data key and assert the operation does not + // fail. + auto key_doc = client_encryption.create_data_key("local"); + + // 2. Use client_encryption to add a keyAltName "abc" to the key created in Step 1 and + // assert the operation does not fail. + client_encryption.add_key_alt_name(key_doc.view(), "abc"); + + // 3. Repeat Step 2, assert the operation does not fail, and assert the returned key + // document contains the keyAltName "abc" added in Step 2. + { + auto alt_key = client_encryption.add_key_alt_name(key_doc.view(), "abc"); + REQUIRE(std::string(alt_key.value()["keyAltNames"][0].get_string().value) == "abc"); + } + + // 4. Use client_encryption to add a keyAltName "def" to the key created in Step 1 and + // assert the operation fails due to a duplicate key server error (error code 11000). + { + bool exception_thrown = false; + try { + client_encryption.add_key_alt_name(key_doc.view(), "def"); + } catch (mongocxx::operation_exception& e) { + REQUIRE(std::strstr( + e.what(), + "E11000 duplicate key error collection: keyvault.datakeys index: keyAltNames_1 " + "dup key: { keyAltNames: \"def\" }: generic server error")); + exception_thrown = true; + } + REQUIRE(exception_thrown); + } + + // 5. Use client_encryption to add a keyAltName "def" to the existing key, assert the + // operation does not fail, and assert the returned key document contains the keyAltName + // "def" added during Setup. + { + auto alt_key = client_encryption.add_key_alt_name(existing_key.view(), "def"); + REQUIRE(std::string(alt_key.value()["keyAltNames"][0].get_string().value) == "def"); + } + } +} + +TEST_CASE("Custom Key Material Test", "[client_side_encryption]") { + instance::current(); + + if (!mongocxx::test_util::should_run_client_side_encryption_test()) { + std::cerr << "Skipping Custom Key Material Test prose tests" << std::endl; + return; + } + + if (!test_util::newer_than(uri{}, "4.2")) { + std::cerr << "Custom Key Material Test requires MongoDB server 4.2+." << std::endl; + return; + } + + // 1. Create a MongoClient object (referred to as client). + mongocxx::client client{mongocxx::uri{}, test_util::add_test_server_api()}; + + // 2. Using client, drop the collection keyvault.datakeys. + client["keyvault"]["datakeys"].drop(); + + // 3. Create a ClientEncryption object (referred to as client_encryption) with client set as the + // keyVaultClient. + options::client_encryption ce_opts; + ce_opts.key_vault_client(&client); + ce_opts.key_vault_namespace({"keyvault", "datakeys"}); + ce_opts.kms_providers(_make_kms_doc(false)); + client_encryption client_encryption(std::move(ce_opts)); + + // 4. Using client_encryption, create a data key with a local KMS provider and the following + // custom key material (given as base64): + // xPTAjBRG5JiPm+d3fj6XLi2q5DMXUS/f1f+SMAlhhwkhDRL0kr8r9GDLIGTAGlvC+HVjSIgdL+RKwZCvpXSyxTICWSXTUYsWYPyu3IoHbuBZdmw2faM3WhcRIgbMReU5 + mongocxx::options::data_key dk_opts; + std::vector key_material{ + 0xc4, 0xf4, 0xc0, 0x8c, 0x14, 0x46, 0xe4, 0x98, 0x8f, 0x9b, 0xe7, 0x77, 0x7e, 0x3e, + 0x97, 0x2e, 0x2d, 0xaa, 0xe4, 0x33, 0x17, 0x51, 0x2f, 0xdf, 0xd5, 0xff, 0x92, 0x30, + 0x9, 0x61, 0x87, 0x9, 0x21, 0xd, 0x12, 0xf4, 0x92, 0xbf, 0x2b, 0xf4, 0x60, 0xcb, + 0x20, 0x64, 0xc0, 0x1a, 0x5b, 0xc2, 0xf8, 0x75, 0x63, 0x48, 0x88, 0x1d, 0x2f, 0xe4, + 0x4a, 0xc1, 0x90, 0xaf, 0xa5, 0x74, 0xb2, 0xc5, 0x32, 0x2, 0x59, 0x25, 0xd3, 0x51, + 0x8b, 0x16, 0x60, 0xfc, 0xae, 0xdc, 0x8a, 0x7, 0x6e, 0xe0, 0x59, 0x76, 0x6c, 0x36, + 0x7d, 0xa3, 0x37, 0x5a, 0x17, 0x11, 0x22, 0x6, 0xcc, 0x45, 0xe5, 0x39}; + dk_opts.key_material(key_material); + auto key = client_encryption.create_data_key("local", dk_opts); + auto key_id = key.view().get_binary(); + + // 5. Find the resulting key document in keyvault.datakeys, save a copy of the key document, + // then remove the key document from the collection. + auto cursor = client["keyvault"]["datakeys"].find(make_document(kvp("_id", key_id))); + const auto doc = *cursor.begin(); + client["keyvault"]["datakeys"].delete_one(make_document(kvp("_id", key_id))); + + // 6. Replace the _id field in the copied key document with a UUID with base64 value + // AAAAAAAAAAAAAAAAAAAAAA== (16 bytes all equal to 0x00) and insert the modified key document + // into keyvault.datakeys with majority write concern. + std::vector id = { + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}; + bsoncxx::types::b_binary id_bin{ + bsoncxx::binary_sub_type::k_uuid, (uint32_t)id.size(), id.data()}; + auto key_doc = make_document(kvp("_id", id_bin)); + + mongocxx::libbson::scoped_bson_t bson_doc; + bson_doc.init_from_static(doc); + mongocxx::libbson::scoped_bson_t doc_without_id; + bson_copy_to_excluding_noinit(bson_doc.bson(), doc_without_id.bson_for_init(), "_id", NULL); + + bsoncxx::document::value new_doc(doc_without_id.steal()); + + bsoncxx::builder::basic::document builder; + builder.append(concatenate(key_doc.view())); + builder.append(concatenate(new_doc.view())); + auto doc_with_new_key = builder.extract(); + + write_concern wc_majority; + wc_majority.acknowledge_level(write_concern::level::k_majority); + + options::insert insert_opts; + insert_opts.write_concern(std::move(wc_majority)); + + client["keyvault"]["datakeys"].insert_one(doc_with_new_key.view(), insert_opts); + + // 7. Using client_encryption, encrypt the string "test" with the modified data key using the + // AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic algorithm and assert the resulting value is equal + // to the following (given as base64): + // AQAAAAAAAAAAAAAAAAAAAAACz0ZOLuuhEYi807ZXTdhbqhLaS2/t9wLifJnnNYwiw79d75QYIZ6M/aYC1h9nCzCjZ7pGUpAuNnkUhnIXM3PjrA== + options::encrypt encrypt_opts{}; + encrypt_opts.key_id(key_doc.view()["_id"].get_value()); + encrypt_opts.algorithm(options::encrypt::encryption_algorithm::k_deterministic); + + auto to_encrypt = make_value("test"); + + auto encrypted = client_encryption.encrypt(to_encrypt.view(), encrypt_opts); + std::vector expected = { + 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x2, 0xcf, 0x46, 0x4e, 0x2e, 0xeb, 0xa1, 0x11, 0x88, 0xbc, 0xd3, + 0xb6, 0x57, 0x4d, 0xd8, 0x5b, 0xaa, 0x12, 0xda, 0x4b, 0x6f, 0xed, 0xf7, 0x2, 0xe2, + 0x7c, 0x99, 0xe7, 0x35, 0x8c, 0x22, 0xc3, 0xbf, 0x5d, 0xef, 0x94, 0x18, 0x21, 0x9e, + 0x8c, 0xfd, 0xa6, 0x2, 0xd6, 0x1f, 0x67, 0xb, 0x30, 0xa3, 0x67, 0xba, 0x46, 0x52, + 0x90, 0x2e, 0x36, 0x79, 0x14, 0x86, 0x72, 0x17, 0x33, 0x73, 0xe3, 0xac}; + + auto encrypted_as_binary = encrypted.view().get_binary(); + REQUIRE(expected.size() == encrypted_as_binary.size); + REQUIRE(std::memcmp(expected.data(), encrypted_as_binary.bytes, expected.size()) == 0); +} + } // namespace diff --git a/src/mongocxx/test/spec/unified_tests/entity.cpp b/src/mongocxx/test/spec/unified_tests/entity.cpp index 783e172845..46b5922a13 100644 --- a/src/mongocxx/test/spec/unified_tests/entity.cpp +++ b/src/mongocxx/test/spec/unified_tests/entity.cpp @@ -61,6 +61,10 @@ bool map::insert(const key_type& key, mongocxx::cursor&& value) { return _insert(key, std::move(value), _cursor_map); } +bool map::insert(const key_type& key, mongocxx::client_encryption&& value) { + return _insert(key, std::move(value), _client_encryption_map); +} + client& map::get_client(const key_type& key) { return _client_map.at(key); } @@ -93,7 +97,13 @@ mongocxx::cursor& map::get_cursor(const key_type& key) { return _cursor_map.at(key); } +mongocxx::client_encryption& map::get_client_encryption(const key_type& key) { + return _client_encryption_map.at(key); +} + const std::type_info& map::type(const key_type& key) { + if (_client_encryption_map.find(key) != _client_encryption_map.end()) + return typeid(mongocxx::client_encryption); if (_database_map.find(key) != _database_map.end()) return typeid(mongocxx::database); if (_collection_map.find(key) != _collection_map.end()) @@ -131,12 +141,13 @@ void map::clear() noexcept { _value_map.clear(); _client_map.clear(); _cursor_map.clear(); + _client_encryption_map.clear(); } void map::erase(const key_type& key) { _database_map.erase(key) || _collection_map.erase(key) || _session_map.erase(key) || _bucket_map.erase(key) || _stream_map.erase(key) || _value_map.erase(key) || - _client_map.erase(key) || _cursor_map.erase(key); + _client_map.erase(key) || _cursor_map.erase(key) || _client_encryption_map.erase(key); } } // namespace entity diff --git a/src/mongocxx/test/spec/unified_tests/entity.hh b/src/mongocxx/test/spec/unified_tests/entity.hh index 2436af759b..6d0143dd77 100644 --- a/src/mongocxx/test/spec/unified_tests/entity.hh +++ b/src/mongocxx/test/spec/unified_tests/entity.hh @@ -19,6 +19,7 @@ #include #include +#include #include @@ -49,6 +50,7 @@ class map { bool insert(const key_type& key, mongocxx::change_stream&&); bool insert(const key_type& key, bsoncxx::types::bson_value::value&&); bool insert(const key_type& key, mongocxx::cursor&&); + bool insert(const key_type& key, mongocxx::client_encryption&&); client& get_client(const key_type& key); database& get_database(const key_type& key); @@ -58,6 +60,7 @@ class map { gridfs::bucket& get_bucket(const key_type& key); bsoncxx::types::bson_value::value& get_value(const key_type& key); cursor& get_cursor(const key_type& key); + mongocxx::client_encryption& get_client_encryption(const key_type& key); database& get_database_by_name(stdx::string_view name); @@ -81,6 +84,7 @@ class map { std::unordered_map _stream_map; std::unordered_map _value_map; std::unordered_map _cursor_map; + std::unordered_map _client_encryption_map; }; } // namespace entity MONGOCXX_INLINE_NAMESPACE_END diff --git a/src/mongocxx/test/spec/unified_tests/operations.cpp b/src/mongocxx/test/spec/unified_tests/operations.cpp index 0d46cc3867..f0df630cea 100644 --- a/src/mongocxx/test/spec/unified_tests/operations.cpp +++ b/src/mongocxx/test/spec/unified_tests/operations.cpp @@ -114,6 +114,22 @@ options::find make_find_options(document::view arguments) { return options; } +document::value bulk_write_result_to_document(const mongocxx::result::bulk_write& result) { + builder::basic::document upserted_ids_builder; + for (auto&& index_and_id : result.upserted_ids()) { + upserted_ids_builder.append( + kvp(std::to_string(index_and_id.first), index_and_id.second.get_int32().value)); + } + + return make_document(kvp("matchedCount", result.matched_count()), + kvp("modifiedCount", result.matched_count()), + kvp("deletedCount", result.deleted_count()), + kvp("insertedCount", result.inserted_count()), + kvp("upsertedCount", result.upserted_count()), + kvp("insertedIds", make_document()), + kvp("upsertedIds", upserted_ids_builder.extract())); +} + document::value find(collection& coll, client_session* session, document::view operation) { document::view arguments = operation["arguments"].get_document().value; document::value empty_filter = builder::basic::make_document(); @@ -1528,6 +1544,170 @@ document::value distinct(collection& coll, client_session* session, document::vi return result.extract(); } +document::value rewrap_many_datakey(entity::map& map, + const std::string& object, + document::view operation) { + auto filter = operation["arguments"]["filter"].get_document(); + + options::rewrap_many_datakey rewrap_opts; + std::string provider; + if (operation["arguments"]["opts"]["provider"]) { + provider = std::string(operation["arguments"]["opts"]["provider"].get_string().value); + } + rewrap_opts.provider(provider); + + if (operation["arguments"]["opts"]["masterKey"]) { + auto master_key = operation["arguments"]["opts"]["masterKey"].get_document().view(); + rewrap_opts.master_key(master_key); + } + + client_encryption& client_encryption = map.get_client_encryption(object); + auto result = client_encryption.rewrap_many_datakey(filter.view(), rewrap_opts); + + auto maybe_bulk_write = result.result(); + if (maybe_bulk_write) { + auto bulk_write_result = maybe_bulk_write.value(); + auto doc = make_document( + kvp("result", + make_document( + kvp("bulkWriteResult", bulk_write_result_to_document(bulk_write_result))))); + return doc; + } else { + auto doc = + make_document(kvp("result", make_document(kvp("bulkWriteResult", types::b_null{})))); + return doc; + } +} + +document::value delete_key(entity::map& map, const std::string& object, document::view operation) { + auto arguments = operation["arguments"].get_document().value; + auto key = arguments["id"].get_value(); + + client_encryption& client_encryption = map.get_client_encryption(object); + auto result = client_encryption.delete_key(key); + + auto doc = + make_document(kvp("result", make_document(kvp("deletedCount", result.deleted_count())))); + return doc; +} + +document::value get_key(entity::map& map, const std::string& object, document::view operation) { + auto arguments = operation["arguments"].get_document().value; + auto key = arguments["id"].get_value(); + + client_encryption& client_encryption = map.get_client_encryption(object); + auto result = client_encryption.get_key(key); + + if (result) { + auto doc = make_document(kvp("result", *result)); + return doc; + } else { + auto doc = make_document(kvp("result", types::b_null{})); + return doc; + } +} + +document::value get_keys(entity::map& map, const std::string& object, document::view operation) { + (void)operation; + client_encryption& client_encryption = map.get_client_encryption(object); + auto cursor = client_encryption.get_keys(); + + return make_document(kvp("result", [&cursor](builder::basic::sub_array array) { + for (auto&& document : cursor) { + array.append(document); + } + })); +} + +document::value create_data_key(entity::map& map, + const std::string& object, + document::view operation) { + std::string provider; + if (operation["arguments"]["kmsProvider"]) { + provider = std::string(operation["arguments"]["kmsProvider"].get_string().value); + } + + options::data_key data_key_opts; + if (operation["arguments"]["opts"]["masterKey"]) { + auto master_key = operation["arguments"]["opts"]["masterKey"].get_document().value; + data_key_opts.master_key(master_key); + } + if (operation["arguments"]["opts"]["keyAltNames"]) { + std::vector key_alt_names; + auto arr = operation["arguments"]["opts"]["keyAltNames"].get_array().value; + for (const auto& it : arr) { + key_alt_names.push_back(std::string(it.get_string().value)); + } + data_key_opts.key_alt_names(key_alt_names); + } + if (operation["arguments"]["opts"]["keyMaterial"]) { + auto bin = operation["arguments"]["opts"]["keyMaterial"].get_binary(); + std::vector bytes(bin.bytes, bin.bytes + bin.size); + data_key_opts.key_material(bytes); + } + + client_encryption& client_encryption = map.get_client_encryption(object); + auto result = client_encryption.create_data_key(provider, data_key_opts); + + return make_document(kvp("result", result)); +} + +document::value add_key_alt_name(entity::map& map, + const std::string& object, + document::view operation) { + auto arguments = operation["arguments"].get_document().value; + auto id = arguments["id"].get_value(); + std::string key_alt_name = std::string(arguments["keyAltName"].get_string().value); + + client_encryption& client_encryption = map.get_client_encryption(object); + auto result = client_encryption.add_key_alt_name(id, key_alt_name); + + if (result) { + auto doc = make_document(kvp("result", *result)); + return doc; + } else { + auto doc = make_document(kvp("result", types::b_null{})); + return doc; + } +} + +document::value get_key_by_alt_name(entity::map& map, + const std::string& object, + document::view operation) { + auto arguments = operation["arguments"].get_document().value; + std::string key_alt_name = std::string(arguments["keyAltName"].get_string().value); + + client_encryption& client_encryption = map.get_client_encryption(object); + auto result = client_encryption.get_key_by_alt_name(key_alt_name); + + if (result) { + auto doc = make_document(kvp("result", *result)); + return doc; + } else { + auto doc = make_document(kvp("result", types::b_null{})); + return doc; + } +} + +document::value remove_key_alt_name(entity::map& map, + const std::string& object, + document::view operation) { + auto arguments = operation["arguments"].get_document().value; + auto id = arguments["id"].get_value(); + std::string key_alt_name = std::string(arguments["keyAltName"].get_string().value); + + client_encryption& client_encryption = map.get_client_encryption(object); + auto result = client_encryption.remove_key_alt_name(id, key_alt_name); + + if (result) { + auto doc = make_document(kvp("result", *result)); + return doc; + } else { + auto doc = make_document(kvp("result", types::b_null{})); + return doc; + } +} + document::value create_find_cursor(entity::map& map, const std::string& object, client_session* session, @@ -1859,6 +2039,30 @@ document::value operations::run(entity::map& entity_map, auto& coll = entity_map.get_collection(object); return distinct(coll, get_session(op_view, entity_map), op_view); } + if (name == "rewrapManyDataKey") { + return rewrap_many_datakey(entity_map, object, op_view); + } + if (name == "deleteKey") { + return delete_key(entity_map, object, op_view); + } + if (name == "getKey") { + return get_key(entity_map, object, op_view); + } + if (name == "getKeys") { + return get_keys(entity_map, object, op_view); + } + if (name == "createDataKey") { + return create_data_key(entity_map, object, op_view); + } + if (name == "addKeyAltName") { + return add_key_alt_name(entity_map, object, op_view); + } + if (name == "getKeyByAltName") { + return get_key_by_alt_name(entity_map, object, op_view); + } + if (name == "removeKeyAltName") { + return remove_key_alt_name(entity_map, object, op_view); + } throw std::logic_error{"unsupported operation: " + name}; } diff --git a/src/mongocxx/test/spec/unified_tests/runner.cpp b/src/mongocxx/test/spec/unified_tests/runner.cpp index 010a7dd41d..f4b950b584 100644 --- a/src/mongocxx/test/spec/unified_tests/runner.cpp +++ b/src/mongocxx/test/spec/unified_tests/runner.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -47,7 +48,7 @@ using bsoncxx::builder::basic::make_document; using schema_versions_t = std::array, 2 /* supported version */>; -constexpr schema_versions_t schema_versions{{{{1, 1, 0}}, {{1, 7, 0}}}}; +constexpr schema_versions_t schema_versions{{{{1, 1, 0}}, {{1, 8, 0}}}}; std::pair&, entity::map&> init_maps() { // Below initializes the static apm map and entity map if needed, in that order. This will also @@ -73,6 +74,88 @@ entity::map& get_entity_map() { return init_maps().second; } +const auto kLocalMasterKey = + "\x32\x78\x34\x34\x2b\x78\x64\x75\x54\x61\x42\x42\x6b\x59\x31\x36\x45\x72" + "\x35\x44\x75\x41\x44\x61\x67\x68\x76\x53\x34\x76\x77\x64\x6b\x67\x38\x74" + "\x70\x50\x70\x33\x74\x7a\x36\x67\x56\x30\x31\x41\x31\x43\x77\x62\x44\x39" + "\x69\x74\x51\x32\x48\x46\x44\x67\x50\x57\x4f\x70\x38\x65\x4d\x61\x43\x31" + "\x4f\x69\x37\x36\x36\x4a\x7a\x58\x5a\x42\x64\x42\x64\x62\x64\x4d\x75\x72" + "\x64\x6f\x6e\x4a\x31\x64"; + +bsoncxx::document::value get_kms_values() { + char key_storage[96]; + memcpy(&(key_storage[0]), kLocalMasterKey, 96); + const bsoncxx::types::b_binary local_master_key{ + bsoncxx::binary_sub_type::k_binary, 96, (const uint8_t*)&key_storage}; + + auto kms_doc = make_document( + kvp("aws", + make_document( + kvp("accessKeyId", test_util::getenv_or_fail("MONGOCXX_TEST_AWS_ACCESS_KEY_ID")), + kvp("secretAccessKey", + test_util::getenv_or_fail("MONGOCXX_TEST_AWS_SECRET_ACCESS_KEY")))), + kvp("azure", + make_document( + kvp("tenantId", test_util::getenv_or_fail("MONGOCXX_TEST_AZURE_TENANT_ID")), + kvp("clientId", test_util::getenv_or_fail("MONGOCXX_TEST_AZURE_CLIENT_ID")), + kvp("clientSecret", + test_util::getenv_or_fail("MONGOCXX_TEST_AZURE_CLIENT_SECRET")))), + kvp("gcp", + make_document( + kvp("email", test_util::getenv_or_fail("MONGOCXX_TEST_GCP_EMAIL")), + kvp("privateKey", test_util::getenv_or_fail("MONGOCXX_TEST_GCP_PRIVATEKEY")))), + kvp("kmip", make_document(kvp("endpoint", "localhost:5698"))), + kvp("local", make_document(kvp("key", local_master_key)))); + + return kms_doc; +} + +bsoncxx::document::value parse_kms_doc(bsoncxx::document::view_or_value test_kms_doc) { + const auto kms_values = get_kms_values(); + auto doc = bsoncxx::builder::basic::document{}; + const auto test_kms_doc_view = test_kms_doc.view(); + for (const auto& it : test_kms_doc_view) { + const auto provider = it.key(); + if (!kms_values[provider]) { + FAIL("FAIL: got unexpected KMS provider: " << provider); + } + auto variables_doc = bsoncxx::builder::basic::document{}; + const auto variables = test_kms_doc_view[provider].get_document().view(); + for (const auto& i : variables) { + const auto variable = i.key(); + const auto actual_value = kms_values[provider][variable]; + if (!kms_values[provider][variable]) { + FAIL("FAIL: expecting to find variable: '" + << variable << "' in KMS doc for provider: '" << provider << "'"); + } + bool is_placeholder = false; + if (i.type() == bsoncxx::type::k_document && + i.get_document().value == make_document(kvp("$$placeholder", 1))) { + is_placeholder = true; + } + if (!is_placeholder) { + // Append value as-is. + variables_doc.append(kvp(variable, i.get_value())); + continue; + } + // A placeholder was specified. Append the credential from the environment. + switch (actual_value.type()) { + case bsoncxx::type::k_string: + variables_doc.append(kvp(variable, actual_value.get_string())); + break; + case bsoncxx::type::k_binary: + variables_doc.append(kvp(variable, actual_value.get_binary())); + break; + default: + FAIL("FAIL: unexpected variable type in KMS doc: '" + << bsoncxx::to_string(actual_value.type()) << "'"); + } + } + doc.append(kvp(provider, variables_doc.extract())); + } + return doc.extract(); +} + // Spec: Version strings, which are used for schemaVersion and runOnRequirement, MUST conform to // one of the following formats, where each component is a non-negative integer: // .. @@ -135,44 +218,44 @@ bool equals_server_topology(const document::element& topologies) { // The server's topology will not change during the test. No need to make a round-trip for every // test file. - static std::string actual = test_util::get_topology(); - auto equals = [&](const bsoncxx::array::element& expected) { + const static std::string actual = test_util::get_topology(); + const auto equals = [&](const bsoncxx::array::element& expected) { return expected == value(actual) || (expected == value("sharded") && actual == "sharded-replicaset"); }; - auto t = topologies.get_array().value; + const auto t = topologies.get_array().value; return std::end(t) != std::find_if(std::begin(t), std::end(t), equals); } bool compatible_with_server(const bsoncxx::array::element& requirement) { // The server's version will not change during the test. No need to make a round-trip for every // test file. - static std::vector expected = get_version(test_util::get_server_version()); + const static std::vector expected = get_version(test_util::get_server_version()); - if (auto min_server_version = requirement["minServerVersion"]) { - auto actual = get_version(min_server_version); + if (const auto min_server_version = requirement["minServerVersion"]) { + const auto actual = get_version(min_server_version); if (!is_compatible_version(actual, expected)) return false; } - if (auto max_server_version = requirement["maxServerVersion"]) { - auto actual = get_version(max_server_version); + if (const auto max_server_version = requirement["maxServerVersion"]) { + const auto actual = get_version(max_server_version); if (!is_compatible_version(expected, actual)) return false; } - if (auto topologies = requirement["topologies"]) + if (const auto topologies = requirement["topologies"]) return equals_server_topology(topologies); - if (auto server_params = requirement["serverParameters"]) { + if (const auto server_params = requirement["serverParameters"]) { document::value actual = make_document(); try { actual = test_util::get_server_params(); } catch (const operation_exception& e) { // Mongohouse does not support getParameter, so if we get an error from // getParameter, exit this logic early and skip the test. - std::string message = e.what(); + const std::string message = e.what(); if (message.find("command getParameter is unsupported") != std::string::npos) { return false; } @@ -180,15 +263,30 @@ bool compatible_with_server(const bsoncxx::array::element& requirement) { throw e; } - for (auto kvp : server_params.get_document().view()) { - auto param = kvp.key(); - auto value = kvp.get_value(); + for (const auto kvp : server_params.get_document().view()) { + const auto param = kvp.key(); + const auto value = kvp.get_value(); // If actual parameter is unset or unequal to requirement, skip test. if (!actual[param] || actual[param].get_bool() != value.get_bool()) { return false; } } } + + if (const auto csfle = requirement["csfle"]) { + // csfle: Optional boolean. If true, the tests MUST only run if the + // driver and server support Client-Side Field Level Encryption. A + // server supports CSFLE if it is version 4.2.0 or higher. If false, + // tests MUST only run if CSFLE is not enabled. If this field is + // omitted, there is no CSFLE requirement. + const std::vector requires_at_least{4, 2, 0}; + const bool is_csfle = csfle.get_bool().value; + if (is_csfle) { + if (!is_compatible_version(requires_at_least, expected)) { + return false; + } + } + } return true; } @@ -196,7 +294,7 @@ bool has_run_on_requirements(const bsoncxx::document::view test) { if (!test["runOnRequirements"]) return true; - auto requirements = test["runOnRequirements"].get_array().value; + const auto requirements = test["runOnRequirements"].get_array().value; return std::any_of(std::begin(requirements), std::end(requirements), compatible_with_server); } @@ -207,7 +305,7 @@ std::string json_kvp_to_uri_kvp(std::string s) { // 3. "key=value" -- final step (without quotes) using namespace std; - auto should_remove = [&](const char c) { + const auto should_remove = [&](const char c) { const auto remove = {' ', '"', '{', '}'}; return end(remove) != find(begin(remove), end(remove), c); }; @@ -229,7 +327,7 @@ std::string json_to_uri_opts(const std::string& input) { std::back_inserter(output), json_kvp_to_uri_kvp); - auto join = [](const std::string& s1, const std::string& s2) { return s1 + "&" + s2; }; + const auto join = [](const std::string& s1, const std::string& s2) { return s1 + "&" + s2; }; return std::accumulate(std::begin(output) + 1, std::end(output), output[0], join); } @@ -245,8 +343,8 @@ std::string uri_options_to_string(document::view object) { // 'readPreferenceTags' keys in the object. REQUIRE_FALSE(object["readPreferenceTags"]); - auto json = to_json(object["uriOptions"].get_document()); - auto opts = json_to_uri_opts(json); + const auto json = to_json(object["uriOptions"].get_document()); + const auto opts = json_to_uri_opts(json); CAPTURE(json, opts); return opts; @@ -270,11 +368,11 @@ std::string get_hostnames(document::view object) { // slash, then a comma-separated list of the hostnames of each member of the replica set, as in // the following example: // { ... , "host" : "shard0001/localhost:27018,localhost:27019,localhost:27020", ... } - auto host = test_util::get_hosts(); - auto after_slash = ++std::find(std::begin(host), std::end(host), '/'); + const auto host = test_util::get_hosts(); + const auto after_slash = ++std::find(std::begin(host), std::end(host), '/'); REQUIRE(after_slash < std::end(host)); - auto hostnames = std::string{after_slash, std::end(host)}; + const auto hostnames = std::string{after_slash, std::end(host)}; CAPTURE(host, hostnames); // require multiple mongos hosts @@ -287,13 +385,13 @@ void add_observe_events(options::apm& apm_opts, document::view object) { if (!object["observeEvents"]) return; - auto name = string::to_string(object["id"].get_string().value); + const auto name = string::to_string(object["id"].get_string().value); auto& apm = get_apm_map()[name]; - auto observe_sensitive = object["observeSensitiveCommands"]; + const auto observe_sensitive = object["observeSensitiveCommands"]; apm.observe_sensitive_events = observe_sensitive && observe_sensitive.get_bool(); - auto events = object["observeEvents"].get_array().value; + const auto events = object["observeEvents"].get_array().value; if (std::end(events) != std::find(std::begin(events), std::end(events), value("commandStartedEvent"))) apm.set_command_started_unified(apm_opts); @@ -312,7 +410,7 @@ void add_ignore_command_monitoring_events(document::view object) { return; for (auto cme : object["ignoreCommandMonitoringEvents"].get_array().value) { CAPTURE(cme.get_string()); - auto name = string::to_string(object["id"].get_string().value); + const auto name = string::to_string(object["id"].get_string().value); auto& apm = get_apm_map()[name]; apm.set_ignore_command_monitoring_event(string::to_string(cme.get_string().value)); } @@ -325,7 +423,7 @@ options::server_api create_server_api(document::view object) { } REQUIRE(sav.type() == type::k_string); - auto version = options::server_api::version_from_string(sav.get_string().value); + const auto version = options::server_api::version_from_string(sav.get_string().value); auto server_api_opts = options::server_api(version); if (auto de = object["serverApi"]["deprecationErrors"]) { @@ -364,7 +462,7 @@ write_concern get_write_concern(const document::element& opts) { auto wc = write_concern{}; if (auto w = opts["writeConcern"]["w"]) { if (w.type() == type::k_utf8) { - auto strval = w.get_string().value; + const auto strval = w.get_string().value; if (0 == strval.compare("majority")) { wc.acknowledge_level(mongocxx::write_concern::level::k_majority); } else { @@ -386,7 +484,7 @@ write_concern get_write_concern(const document::element& opts) { read_concern get_read_concern(const document::element& opts) { auto rc = read_concern{}; - if (auto level = opts["readConcern"]["level"]) { + if (const auto level = opts["readConcern"]["level"]) { rc.acknowledge_string(level.get_string().value); } @@ -445,36 +543,77 @@ options::client_session get_session_options(document::view object) { return session_opts; } +options::client_encryption get_client_encryption_options(document::view object) { + const auto key_vault_namespace = + std::string(object["clientEncryptionOpts"]["keyVaultNamespace"].get_string().value); + const auto dot = key_vault_namespace.find("."); + const std::string db = key_vault_namespace.substr(0, dot); + const std::string coll = key_vault_namespace.substr(dot + 1); + + const auto id = + string::to_string(object["clientEncryptionOpts"]["keyVaultClient"].get_string().value); + + auto& map = get_entity_map(); + auto& client = map.get_client(id); + CAPTURE(id); + + const auto providers = object["clientEncryptionOpts"]["kmsProviders"].get_document().value; + + options::client_encryption ce_opts; + ce_opts.key_vault_client(&client); + ce_opts.key_vault_namespace({db, coll}); + ce_opts.kms_providers(parse_kms_doc(providers)); + + if (!providers.empty()) { + // Configure TLS options. + auto tls_opts = make_document(kvp( + "kmip", + make_document( + kvp("tlsCAFile", test_util::getenv_or_fail("MONGOCXX_TEST_CSFLE_TLS_CA_FILE")), + kvp("tlsCertificateKeyFile", + test_util::getenv_or_fail("MONGOCXX_TEST_CSFLE_TLS_CERTIFICATE_KEY_FILE"))))); + ce_opts.tls_opts(std::move(tls_opts)); + } + return ce_opts; +} + gridfs::bucket create_bucket(document::view object) { - auto id = string::to_string(object["database"].get_string().value); + const auto id = string::to_string(object["database"].get_string().value); auto& map = get_entity_map(); auto& db = map.get_database(id); - auto opts = get_bucket_options(object); - auto bucket = db.gridfs_bucket(opts); + const auto opts = get_bucket_options(object); + const auto bucket = db.gridfs_bucket(opts); CAPTURE(id); return bucket; } client_session create_session(document::view object) { - auto id = string::to_string(object["client"].get_string().value); + const auto id = string::to_string(object["client"].get_string().value); auto& map = get_entity_map(); auto& client = map.get_client(id); - auto opts = get_session_options(object); + const auto opts = get_session_options(object); auto session = client.start_session(opts); CAPTURE(id); return session; } +client_encryption create_client_encryption(document::view object) { + const auto opts = get_client_encryption_options(object); + client_encryption ce(std::move(opts)); + + return ce; +} + collection create_collection(document::view object) { - auto id = string::to_string(object["database"].get_string().value); + const auto id = string::to_string(object["database"].get_string().value); auto& map = get_entity_map(); auto& db = map.get_database(id); - auto name = string::to_string(object["collectionName"].get_string().value); + const auto name = string::to_string(object["collectionName"].get_string().value); auto coll = collection{db.collection(name)}; set_common_options(coll, object["collectionOptions"]); @@ -484,11 +623,11 @@ collection create_collection(document::view object) { } database create_database(document::view object) { - auto id = string::to_string(object["client"].get_string().value); + const auto id = string::to_string(object["client"].get_string().value); auto& map = get_entity_map(); auto& client = map.get_client(id); - auto name = string::to_string(object["databaseName"].get_string().value); + const auto name = string::to_string(object["databaseName"].get_string().value); auto db = database{client.database(name)}; set_common_options(db, object["databaseOptions"]); @@ -498,12 +637,12 @@ database create_database(document::view object) { } client create_client(document::view object) { - auto conn = "mongodb://" + get_hostnames(object) + "/?" + uri_options_to_string(object); + const auto conn = "mongodb://" + get_hostnames(object) + "/?" + uri_options_to_string(object); auto apm_opts = options::apm{}; auto client_opts = options::client{}; // Use specified serverApi or default if none is provided. if (object["serverApi"]) { - auto server_api_opts = create_server_api(object); + const auto server_api_opts = create_server_api(object); client_opts.server_api_opts(server_api_opts); } else { client_opts = test_util::add_test_server_api(); @@ -521,18 +660,24 @@ bool add_to_map(const array::element& obj) { // maps to a nested object, which specifies a unique name for the entity ('id' key) and any // other parameters necessary for its construction. auto doc = obj.get_document().view().begin(); - auto type = string::to_string(doc->key()); - auto params = doc->get_document().view(); - auto id = string::to_string(params["id"].get_string().value); + const auto type = string::to_string(doc->key()); + const auto params = doc->get_document().view(); + const auto id = string::to_string(params["id"].get_string().value); auto& map = get_entity_map(); - // clang-format off - if (type == "client") return map.insert(id, create_client(params)); - if (type == "database") return map.insert(id, create_database(params)); - if (type == "collection") return map.insert(id, create_collection(params)); - if (type == "bucket") return map.insert(id, create_bucket(params)); - if (type == "session") return map.insert(id, create_session(params)); - // clang-format on + if (type == "client") { + return map.insert(id, create_client(params)); + } else if (type == "database") { + return map.insert(id, create_database(params)); + } else if (type == "collection") { + return map.insert(id, create_collection(params)); + } else if (type == "bucket") { + return map.insert(id, create_bucket(params)); + } else if (type == "session") { + return map.insert(id, create_session(params)); + } else if (type == "clientEncryption") { + return map.insert(id, create_client_encryption(params)); + } CAPTURE(type, id, params); FAIL("unrecognized type { " + type + " }"); @@ -545,20 +690,21 @@ void create_entities(const document::view test) { get_entity_map().clear(); get_apm_map().clear(); - auto entities = test["createEntities"].get_array().value; + const auto entities = test["createEntities"].get_array().value; REQUIRE(std::all_of(std::begin(entities), std::end(entities), add_to_map)); } document::value parse_test_file(const std::string& test_path) { - bsoncxx::stdx::optional test_spec = test_util::parse_test_file(test_path); + const bsoncxx::stdx::optional test_spec = + test_util::parse_test_file(test_path); REQUIRE(test_spec); return test_spec.value(); } bool is_compatible_schema_version(document::view test_spec) { REQUIRE(test_spec["schemaVersion"]); - auto test_schema_version = get_version(test_spec["schemaVersion"]); - auto compat = [&](std::array v) { + const auto test_schema_version = get_version(test_spec["schemaVersion"]); + const auto compat = [&](std::array v) { // Test files are considered compatible with a test runner if their schemaVersion is less // than or equal to a supported version in the test runner, given the same major version // component. @@ -583,14 +729,14 @@ std::vector versions_to_string(schema_versions_t versions) { std::vector array_elements_to_documents(array::view array) { // no implicit conversion from 'bsoncxx::array::view' to 'bsoncxx::document::view' auto docs = std::vector{}; - auto arr_to_doc = [](const array::element& doc) { return doc.get_document().value; }; + const auto arr_to_doc = [](const array::element& doc) { return doc.get_document().value; }; std::transform(std::begin(array), std::end(array), std::back_inserter(docs), arr_to_doc); return docs; } void add_data_to_collection(const array::element& data) { - auto db_name = data["databaseName"].get_string().value; + const auto db_name = data["databaseName"].get_string().value; auto& map = get_entity_map(); auto& db = map.get_database_by_name(db_name); auto insert_opts = mongocxx::options::insert(); @@ -599,7 +745,7 @@ void add_data_to_collection(const array::element& data) { wc.acknowledge_level(write_concern::level::k_majority); wc.majority(std::chrono::milliseconds{0}); - auto coll_name = data["collectionName"].get_string().value; + const auto coll_name = data["collectionName"].get_string().value; if (db.has_collection(coll_name)) db[coll_name].drop(); @@ -607,7 +753,7 @@ void add_data_to_collection(const array::element& data) { auto coll = db.create_collection(coll_name, {}, wc); insert_opts.write_concern(wc); - auto to_insert = array_elements_to_documents(data["documents"].get_array().value); + const auto to_insert = array_elements_to_documents(data["documents"].get_array().value); REQUIRE((to_insert.empty() || coll.insert_many(to_insert, insert_opts)->result().inserted_count() != 0)); } @@ -616,7 +762,7 @@ void load_initial_data(document::view test) { if (!test["initialData"]) return; - auto data = test["initialData"].get_array().value; + const auto data = test["initialData"].get_array().value; for (auto&& d : data) add_data_to_collection(d); } @@ -624,10 +770,11 @@ void load_initial_data(document::view test) { void assert_result(const array::element& ops, document::view actual_result, bool is_array_of_root_docs) { - if (!ops["expectResult"]) + if (!ops["expectResult"]) { return; + } - auto expected_result = ops["expectResult"]; + const auto expected_result = ops["expectResult"]; assert::matches(actual_result["result"].get_value(), expected_result.get_value(), get_entity_map(), @@ -635,7 +782,7 @@ void assert_result(const array::element& ops, is_array_of_root_docs); if (ops["saveResultAsEntity"]) { - auto key = string::to_string(ops["saveResultAsEntity"].get_string().value); + const auto key = string::to_string(ops["saveResultAsEntity"].get_string().value); get_entity_map().insert(key, actual_result); } } @@ -643,23 +790,23 @@ void assert_result(const array::element& ops, void assert_error(const mongocxx::operation_exception& exception, const array::element& expected, document::view actual) { - std::string server_error_msg = + const std::string server_error_msg = exception.raw_server_error() ? to_json(*exception.raw_server_error()) : "no server error"; CAPTURE(exception.what(), server_error_msg); - auto expect_error = expected["expectError"]; + const auto expect_error = expected["expectError"]; REQUIRE(expect_error); if (expect_error["isError"]) return; - auto actual_result = actual["result"]; - if (auto expected_result = expect_error["expectResult"]) { + const auto actual_result = actual["result"]; + if (const auto expected_result = expect_error["expectResult"]) { assert::matches(actual_result.get_value(), expected_result.get_value(), get_entity_map()); } - if (auto is_client_error = expect_error["isClientError"]) { + if (const auto is_client_error = expect_error["isClientError"]) { if (std::strstr(exception.what(), "Snapshot reads require MongoDB 5.0 or later") != nullptr) { // Original error: { MONGOC_ERROR_CLIENT, MONGOC_ERROR_CLIENT_SESSION_FAILURE } @@ -674,30 +821,53 @@ void assert_error(const mongocxx::operation_exception& exception, // The C++ driver throws this error as a server-side error operation_exception. // Remove this special case as part of CXX-2377. REQUIRE(is_client_error.get_bool()); + } else if (std::strstr(exception.what(), "Error in KMS response") != nullptr) { + REQUIRE(is_client_error.get_bool()); + } else if (std::strstr(exception.what(), + "keyMaterial should have length 96, but has length 84") != nullptr) { + REQUIRE(is_client_error.get_bool()); + } else if (std::strstr(exception.what(), "expected UTF-8 key") != nullptr) { + REQUIRE(is_client_error.get_bool()); + } else if (std::strstr(exception.what(), "Unexpected field: 'invalid'") != nullptr) { + REQUIRE(is_client_error.get_bool()); + } else if (std::strstr(exception.what(), "Failed to resolve kms.invalid.amazonaws.com") != + nullptr) { + REQUIRE(is_client_error.get_bool()); + } else if (std::strstr( + exception.what(), + "The ciphertext refers to a customer master key that does not exist") != + nullptr) { + REQUIRE(is_client_error.get_bool()); + } else if (std::strstr(exception.what(), "does not exist") != nullptr) { + REQUIRE(is_client_error.get_bool()); + } else if (std::strstr(exception.what(), + "Failed to resolve invalid-vault-csfle.vault.azure.net") != + nullptr) { + REQUIRE(is_client_error.get_bool()); } else if (is_client_error.get_bool()) { // An operation_exception represents a server-side error. REQUIRE(!is_client_error.get_bool()); } } - if (auto contains = expect_error["errorLabelsContain"]) { - auto labels = contains.get_array().value; - auto has_error_label = [&](const array::element& ele) { + if (const auto contains = expect_error["errorLabelsContain"]) { + const auto labels = contains.get_array().value; + const auto has_error_label = [&](const array::element& ele) { return exception.has_error_label(ele.get_string().value); }; REQUIRE(std::all_of(std::begin(labels), std::end(labels), has_error_label)); } - if (auto omit = expect_error["errorLabelsOmit"]) { - auto labels = omit.get_array().value; - auto has_error_label = [&](const array::element& ele) { + if (const auto omit = expect_error["errorLabelsOmit"]) { + const auto labels = omit.get_array().value; + const auto has_error_label = [&](const array::element& ele) { return exception.has_error_label(ele.get_string().value); }; REQUIRE(std::none_of(std::begin(labels), std::end(labels), has_error_label)); } - if (auto expected_code = expect_error["errorCode"]) { - auto actual_code = exception.code().value(); + if (const auto expected_code = expect_error["errorCode"]) { + const auto actual_code = exception.code().value(); REQUIRE(actual_code == expected_code.get_int32()); } @@ -733,13 +903,13 @@ void assert_error(const mongocxx::operation_exception& exception, void assert_error(mongocxx::exception& e, const array::element& ops) { CAPTURE(e.what()); - auto expect_error = ops["expectError"]; + const auto expect_error = ops["expectError"]; REQUIRE(expect_error); if (expect_error["isError"]) return; - if (auto is_client_error = expect_error["isClientError"]) { + if (const auto is_client_error = expect_error["isClientError"]) { REQUIRE(is_client_error.get_bool()); } @@ -782,20 +952,20 @@ void assert_outcome(const array::element& test) { if (!test["outcome"]) return; - for (auto outcome : test["outcome"].get_array().value) { + for (const auto outcome : test["outcome"].get_array().value) { CAPTURE(to_json(outcome.get_document())); - auto db_name = outcome["databaseName"].get_string().value; - auto coll_name = outcome["collectionName"].get_string().value; - auto docs = outcome["documents"].get_array().value; + const auto db_name = outcome["databaseName"].get_string().value; + const auto coll_name = outcome["collectionName"].get_string().value; + const auto docs = outcome["documents"].get_array().value; - auto db = get_entity_map().get_database_by_name(db_name); + const auto db = get_entity_map().get_database_by_name(db_name); auto coll = db.collection(coll_name); auto results = coll.find({}, options::find{}.sort(make_document(kvp("_id", 1)))); auto actual = results.begin(); - for (auto& expected : docs) { + for (const auto& expected : docs) { assert::matches( types::bson_value::value(*actual), expected.get_value(), get_entity_map()); ++actual; @@ -820,9 +990,9 @@ struct disable_fail_point { }; document::value bulk_write_result(const mongocxx::bulk_write_exception& e) { - auto reply = e.raw_server_error().value(); + const auto reply = e.raw_server_error().value(); - auto get_or_default = [&](bsoncxx::stdx::string_view key) { + const auto get_or_default = [&](bsoncxx::stdx::string_view key) { return reply[key] ? reply[key].get_int32().value : 0; }; @@ -841,8 +1011,8 @@ document::value bulk_write_result(const mongocxx::bulk_write_exception& e) { void run_tests(document::view test) { REQUIRE(test["tests"]); - for (auto ele : test["tests"].get_array().value) { - auto description = string::to_string(ele["description"].get_string().value); + for (const auto ele : test["tests"].get_array().value) { + const auto description = string::to_string(ele["description"].get_string().value); SECTION(description) { if (!has_run_on_requirements(ele.get_document())) { std::stringstream warning; @@ -866,7 +1036,7 @@ void run_tests(document::view test) { operations::state state; - for (auto ops : ele["operations"].get_array().value) { + for (const auto ops : ele["operations"].get_array().value) { const auto ignore_result_and_error = [&]() -> bool { const auto elem = ops["ignoreResultAndError"]; return elem && elem.get_bool().value; @@ -910,12 +1080,12 @@ void run_tests(document::view test) { if (!ignore_result_and_error) { assert_result(ops, result, is_array_of_root_docs); } - } catch (mongocxx::bulk_write_exception& e) { + } catch (const mongocxx::bulk_write_exception& e) { if (!ignore_result_and_error) { auto result = bulk_write_result(e); assert_error(e, ops, result); } - } catch (mongocxx::operation_exception& e) { + } catch (const mongocxx::operation_exception& e) { if (!ignore_result_and_error) { assert_error(e, ops, make_document()); } @@ -934,8 +1104,8 @@ void run_tests(document::view test) { } void run_tests_in_file(const std::string& test_path) { - auto test_spec = parse_test_file(test_path); - auto test_spec_view = test_spec.view(); + const auto test_spec = parse_test_file(test_path); + const auto test_spec_view = test_spec.view(); if (!is_compatible_schema_version(test_spec_view)) { std::stringstream error; @@ -943,7 +1113,7 @@ void run_tests_in_file(const std::string& test_path) { << "Expected: " << test_spec_view["schemaVersion"].get_string().value << std::endl << "Supported versions:" << std::endl; - auto v = versions_to_string(schema_versions); + const auto v = versions_to_string(schema_versions); std::copy(std::begin(v), std::end(v), std::ostream_iterator(error, "\n")); FAIL(error.str()); @@ -980,7 +1150,7 @@ bool run_unified_format_tests_in_env_dir( const std::string base_path{p}; - auto test_file_set_path = base_path + "/test_files.txt"; + const auto test_file_set_path = base_path + "/test_files.txt"; std::ifstream files{test_file_set_path}; if (!files.good()) { @@ -1032,4 +1202,14 @@ TEST_CASE("collection management spec automated tests", "[unified_format_spec]") CHECK(run_unified_format_tests_in_env_dir("COLLECTION_MANAGEMENT_TESTS_PATH")); } +// See: +// https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/client-side-encryption.rst +TEST_CASE("client side encryption unified format spec automated tests", "[unified_format_spec]") { + if (!mongocxx::test_util::should_run_client_side_encryption_test()) { + WARN("Skipping - client side encryption unified tests"); + return; + } + CHECK(run_unified_format_tests_in_env_dir("CLIENT_SIDE_ENCRYPTION_UNIFIED_TESTS_PATH")); +} + } // namespace