|
2 | 2 |
|
3 | 3 | namespace MongoDB\Tests;
|
4 | 4 |
|
| 5 | +use MongoDB\BSON\Binary; |
5 | 6 | use MongoDB\BSON\ObjectId;
|
6 | 7 | use MongoDB\BSON\UTCDateTime;
|
7 | 8 | use MongoDB\Collection;
|
|
11 | 12 | use MongoDB\Driver\Exception\Exception;
|
12 | 13 | use MongoDB\Driver\ReadPreference;
|
13 | 14 | use MongoDB\Driver\WriteConcern;
|
| 15 | +use MongoDB\Tests\SpecTests\ClientSideEncryptionSpecTest; |
14 | 16 |
|
| 17 | +use function base64_decode; |
15 | 18 | use function in_array;
|
16 | 19 | use function microtime;
|
17 | 20 | use function ob_end_clean;
|
@@ -1848,6 +1851,111 @@ public function testWithTransactionExample(): void
|
1848 | 1851 | // phpcs:enable
|
1849 | 1852 | }
|
1850 | 1853 |
|
| 1854 | + /** |
| 1855 | + * Queryable encryption examples (not parsed for server manual includes). |
| 1856 | + * |
| 1857 | + * @see https://jira.mongodb.org/browse/PHPLIB-863 |
| 1858 | + * @see ClientSideEncryptionSpecTest::testExplicitEncryption |
| 1859 | + */ |
| 1860 | + public function testQueryableEncryption(): void |
| 1861 | + { |
| 1862 | + if ($this->isStandalone() || ($this->isShardedCluster() && ! $this->isShardedClusterUsingReplicasets())) { |
| 1863 | + $this->markTestSkipped('Queryable encryption requires replica sets'); |
| 1864 | + } |
| 1865 | + |
| 1866 | + if (version_compare($this->getServerVersion(), '6.0.0', '<')) { |
| 1867 | + $this->markTestSkipped('Queryable encryption requires MongoDB 6.0 or later'); |
| 1868 | + } |
| 1869 | + |
| 1870 | + if (! $this->isEnterprise()) { |
| 1871 | + $this->markTestSkipped('Automatic encryption requires MongoDB Enterprise'); |
| 1872 | + } |
| 1873 | + |
| 1874 | + // Fetch names for the database and collection under test |
| 1875 | + $collectionName = $this->getCollectionName(); |
| 1876 | + $databaseName = $this->getDatabaseName(); |
| 1877 | + $namespace = $this->getNamespace(); |
| 1878 | + |
| 1879 | + /* Create a client without auto encryption. Drop existing data in both |
| 1880 | + * the keyvault and database under test. The latter is necessary since |
| 1881 | + * setUp() only drops the collection under test, which will leave behind |
| 1882 | + * internal collections for queryable encryption. */ |
| 1883 | + $client = static::createTestClient(); |
| 1884 | + $client->selectDatabase('keyvault')->drop(['writeConcern' => new WriteConcern(WriteConcern::MAJORITY)]); |
| 1885 | + $client->selectDatabase($databaseName)->drop(['writeConcern' => new WriteConcern(WriteConcern::MAJORITY)]); |
| 1886 | + |
| 1887 | + /* Although ClientEncryption can be constructed directly, the library |
| 1888 | + * provides a helper to do so. With this method, the keyVaultClient will |
| 1889 | + * default to the same client. */ |
| 1890 | + $clientEncryption = $client->createClientEncryption([ |
| 1891 | + 'keyVaultNamespace' => 'keyvault.datakeys', |
| 1892 | + 'kmsProviders' => ['local' => ['key' => new Binary(base64_decode(ClientSideEncryptionSpecTest::LOCAL_MASTERKEY), 0)]], |
| 1893 | + ]); |
| 1894 | + |
| 1895 | + // Create two data keys, one for each encrypted field |
| 1896 | + $dataKeyId1 = $clientEncryption->createDataKey('local'); |
| 1897 | + $dataKeyId2 = $clientEncryption->createDataKey('local'); |
| 1898 | + |
| 1899 | + $autoEncryptionOpts = [ |
| 1900 | + 'keyVaultNamespace' => 'keyvault.datakeys', |
| 1901 | + 'kmsProviders' => ['local' => ['key' => new Binary(base64_decode(ClientSideEncryptionSpecTest::LOCAL_MASTERKEY), 0)]], |
| 1902 | + 'encryptedFieldsMap' => [ |
| 1903 | + $namespace => [ |
| 1904 | + 'fields' => [ |
| 1905 | + [ |
| 1906 | + 'path' => 'encryptedIndexed', |
| 1907 | + 'bsonType' => 'string', |
| 1908 | + 'keyId' => $dataKeyId1, |
| 1909 | + 'queries' => ['queryType' => 'equality'], |
| 1910 | + ], |
| 1911 | + [ |
| 1912 | + 'path' => 'encryptedUnindexed', |
| 1913 | + 'bsonType' => 'string', |
| 1914 | + 'keyId' => $dataKeyId2, |
| 1915 | + ], |
| 1916 | + ], |
| 1917 | + ], |
| 1918 | + ], |
| 1919 | + ]; |
| 1920 | + |
| 1921 | + $encryptedClient = static::createTestClient(null, [], ['autoEncryption' => $autoEncryptionOpts]); |
| 1922 | + |
| 1923 | + /* Create the collection under test. The createCollection() helper will |
| 1924 | + * reference the client's encryptedFieldsMap and create additional, |
| 1925 | + * internal collections automatically. */ |
| 1926 | + $encryptedClient->selectDatabase($databaseName)->createCollection($collectionName); |
| 1927 | + $encryptedCollection = $encryptedClient->selectCollection($databaseName, $collectionName); |
| 1928 | + |
| 1929 | + /* Using a client with auto encryption, insert a document with encrypted |
| 1930 | + * fields and assert that those fields are automatically decrypted when |
| 1931 | + * querying. */ |
| 1932 | + $indexedValue = 'indexedValue'; |
| 1933 | + $unindexedValue = 'unindexedValue'; |
| 1934 | + |
| 1935 | + $encryptedCollection->insertOne([ |
| 1936 | + '_id' => 1, |
| 1937 | + 'encryptedIndexed' => $indexedValue, |
| 1938 | + 'encryptedUnindexed' => $unindexedValue, |
| 1939 | + ]); |
| 1940 | + |
| 1941 | + $result = $encryptedCollection->findOne(['encryptedIndexed' => $indexedValue]); |
| 1942 | + |
| 1943 | + $this->assertSame(1, $result['_id']); |
| 1944 | + $this->assertSame($indexedValue, $result['encryptedIndexed']); |
| 1945 | + $this->assertSame($unindexedValue, $result['encryptedUnindexed']); |
| 1946 | + |
| 1947 | + /* Using a client without auto encryption, query for the same |
| 1948 | + * document and assert that encrypted data is returned. */ |
| 1949 | + $unencryptedClient = static::createTestClient(); |
| 1950 | + $unencryptedCollection = $unencryptedClient->selectCollection($databaseName, $collectionName); |
| 1951 | + |
| 1952 | + $result = $unencryptedCollection->findOne(['_id' => 1]); |
| 1953 | + |
| 1954 | + $this->assertSame(1, $result['_id']); |
| 1955 | + $this->assertInstanceOf(Binary::class, $result['encryptedIndexed']); |
| 1956 | + $this->assertInstanceOf(Binary::class, $result['encryptedUnindexed']); |
| 1957 | + } |
| 1958 | + |
1851 | 1959 | /**
|
1852 | 1960 | * Return the test collection name.
|
1853 | 1961 | *
|
|
0 commit comments