diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index aa6aeecf9..3242db31a 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -155,3 +155,4 @@ jobs: env: DOCTRINE_MONGODB_SERVER: ${{ steps.setup-mongodb.outputs.cluster-uri }} USE_LAZY_GHOST_OBJECTS: ${{ matrix.proxy == 'lazy-ghost' && '1' || '0' }}" + CRYPT_SHARED_LIB_PATH: ${{ steps.setup-mongodb.outputs.crypt-shared-lib-path }} diff --git a/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/Encrypt.php b/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/Encrypt.php index 8a5a51a35..1f7fbc90e 100644 --- a/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/Encrypt.php +++ b/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/Encrypt.php @@ -28,7 +28,7 @@ final class Encrypt implements Annotation /** * @param EncryptQuery|null $queryType Set the query type for the field, null if not queryable. * @param int<1, 4>|null $sparsity - * @param positive-int|null $prevision + * @param positive-int|null $precision * @param positive-int|null $trimFactor * @param positive-int|null $contention */ @@ -37,7 +37,7 @@ public function __construct( int|float|Int64|Decimal128|UTCDateTime|DateTimeInterface|null $min = null, int|float|Int64|Decimal128|UTCDateTime|DateTimeInterface|null $max = null, public ?int $sparsity = null, - public ?int $prevision = null, + public ?int $precision = null, public ?int $trimFactor = null, public ?int $contention = null, ) { diff --git a/lib/Doctrine/ODM/MongoDB/Mapping/ClassMetadata.php b/lib/Doctrine/ODM/MongoDB/Mapping/ClassMetadata.php index d05e4c4b7..2db3dc562 100644 --- a/lib/Doctrine/ODM/MongoDB/Mapping/ClassMetadata.php +++ b/lib/Doctrine/ODM/MongoDB/Mapping/ClassMetadata.php @@ -107,7 +107,7 @@ * order?: int|string, * background?: bool, * enumType?: class-string, - * encrypt?: array{queryType?: ?string, min?: mixed, max?: mixed, sparsity?: int<1, 4>, prevision?: int, trimFactor?: int, contention?: int} + * encrypt?: array{queryType?: ?string, min?: mixed, max?: mixed, sparsity?: int<1, 4>, precision?: int, trimFactor?: int, contention?: int} * } * @phpstan-type FieldMapping array{ * type: string, @@ -154,7 +154,7 @@ * alsoLoadFields?: list, * enumType?: class-string, * storeEmptyArray?: bool, - * encrypt?: array{queryType?: ?string, min?: mixed, max?: mixed, sparsity?: int<1, 4>, prevision?: int, trimFactor?: int, contention?: int}, + * encrypt?: array{queryType?: ?string, min?: mixed, max?: mixed, sparsity?: int<1, 4>, precision?: int, trimFactor?: int, contention?: int}, * } * @phpstan-type AssociationFieldMapping array{ * type?: string, diff --git a/lib/Doctrine/ODM/MongoDB/Mapping/Driver/XmlDriver.php b/lib/Doctrine/ODM/MongoDB/Mapping/Driver/XmlDriver.php index 4e362e064..70b9708d6 100644 --- a/lib/Doctrine/ODM/MongoDB/Mapping/Driver/XmlDriver.php +++ b/lib/Doctrine/ODM/MongoDB/Mapping/Driver/XmlDriver.php @@ -4,7 +4,6 @@ namespace Doctrine\ODM\MongoDB\Mapping\Driver; -use DateTimeImmutable; use Doctrine\ODM\MongoDB\Mapping\Annotations\TimeSeries; use Doctrine\ODM\MongoDB\Mapping\ClassMetadata; use Doctrine\ODM\MongoDB\Mapping\MappingException; @@ -15,9 +14,7 @@ use DOMDocument; use InvalidArgumentException; use LibXMLError; -use MongoDB\BSON\Decimal128; use MongoDB\BSON\Document; -use MongoDB\BSON\UTCDateTime; use MongoDB\Driver\Exception\UnexpectedValueException; use SimpleXMLElement; @@ -319,14 +316,8 @@ public function loadMetadataForClass($className, \Doctrine\Persistence\Mapping\C foreach ($field->encrypt->attributes() as $encryptKey => $encryptValue) { $mapping['encrypt'][$encryptKey] = match ($encryptKey) { 'queryType' => (string) $encryptValue, - 'min', 'max' => match ($mapping['type']) { - Type::INT => (int) $encryptValue, - Type::FLOAT => (float) $encryptValue, - Type::DECIMAL128 => new Decimal128((string) $encryptValue), - Type::DATE, Type::DATE_IMMUTABLE => new UTCDateTime(new DateTimeImmutable((string) $encryptValue)), - default => null, // Invalid - }, - 'sparsity', 'prevision', 'trimFactor', 'contention' => (int) $encryptValue, + 'min', 'max' => Type::getType($mapping['type'])->convertToDatabaseValue((string) $encryptValue), + 'sparsity', 'precision', 'trimFactor', 'contention' => (int) $encryptValue, }; } } diff --git a/lib/Doctrine/ODM/MongoDB/Utility/EncryptedFieldsMapGenerator.php b/lib/Doctrine/ODM/MongoDB/Utility/EncryptedFieldsMapGenerator.php index 08492e0d4..b426852c2 100644 --- a/lib/Doctrine/ODM/MongoDB/Utility/EncryptedFieldsMapGenerator.php +++ b/lib/Doctrine/ODM/MongoDB/Utility/EncryptedFieldsMapGenerator.php @@ -8,11 +8,14 @@ use Doctrine\ODM\MongoDB\Mapping\ClassMetadata; use Doctrine\ODM\MongoDB\Mapping\ClassMetadataFactoryInterface; use Doctrine\ODM\MongoDB\Mapping\MappingException; +use Doctrine\ODM\MongoDB\Types\Type; use Generator; +use LogicException; use function array_filter; use function assert; use function iterator_to_array; +use function sprintf; final class EncryptedFieldsMapGenerator { @@ -108,11 +111,19 @@ private function createEncryptedFieldsMapForClass( $field = [ 'path' => $path . $mapping['name'], 'bsonType' => match ($mapping['type']) { - 'one' => 'object', - 'many' => 'array', - default => $mapping['type'], + ClassMetadata::ONE, Type::HASH => 'object', + ClassMetadata::MANY, Type::COLLECTION => 'array', + Type::INT, Type::INTEGER => 'int', + Type::FLOAT => 'double', + Type::DECIMAL128 => 'decimal', + Type::DATE, Type::DATE_IMMUTABLE => 'date', + Type::TIMESTAMP => 'timestamp', + Type::OBJECTID => 'objectId', + Type::STRING => 'string', + Type::BINDATA, Type::BINDATABYTEARRAY, Type::BINDATAFUNC, Type::BINDATACUSTOM, Type::BINDATAUUID, Type::BINDATAMD5, Type::BINDATAUUIDRFC4122 => 'binData', + Type::BOOL, Type::BOOLEAN => 'bool', + default => throw new LogicException(sprintf('Type "%s" is not supported in encrypted fields map.', $mapping['type'])), }, - // @todo allow setting a keyId in #[Encrypt] attribute 'keyId' => null, // Generate the key automatically ]; diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 0ad708f9b..cbdfa32c4 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -961,7 +961,7 @@ parameters: path: lib/Doctrine/ODM/MongoDB/Mapping/Driver/XmlDriver.php - - message: '#^Parameter \#2 \$mapping of method Doctrine\\ODM\\MongoDB\\Mapping\\Driver\\XmlDriver\:\:addFieldMapping\(\) expects array\{type\?\: string, fieldName\?\: string, name\?\: string, strategy\?\: string, association\?\: int, id\?\: bool, isOwningSide\?\: bool, collectionClass\?\: class\-string, \.\.\.\}, array\\|string, float\|int\|MongoDB\\BSON\\Decimal128\|MongoDB\\BSON\\UTCDateTime\|string\|null\>\|bool\|string\> given\.$#' + message: '#^Parameter \#2 \$mapping of method Doctrine\\ODM\\MongoDB\\Mapping\\Driver\\XmlDriver\:\:addFieldMapping\(\) expects array\{type\?\: string, fieldName\?\: string, name\?\: string, strategy\?\: string, association\?\: int, id\?\: bool, isOwningSide\?\: bool, collectionClass\?\: class\-string, \.\.\.\}, array\\|string, mixed\>\|bool\|string\> given\.$#' identifier: argument.type count: 1 path: lib/Doctrine/ODM/MongoDB/Mapping/Driver/XmlDriver.php @@ -1668,12 +1668,6 @@ parameters: count: 3 path: tests/Doctrine/ODM/MongoDB/Tests/Functional/QueryableEncryptionTest.php - - - message: '#^Parameter \#1 \$options of method Doctrine\\ODM\\MongoDB\\Configuration\:\:setAutoEncryption\(\) expects array\{keyVaultNamespace\: string, kmsProviders\: array\\>, tlsOptions\?\: array\{kmip\: array\{tlsCAFile\: string, tlsCertificateKeyFile\: string\}\}\}, array\{keyVaultNamespace\: non\-falsy\-string, kmsProviders\: array\{local\: array\{key\: MongoDB\\BSON\\Binary\}\}\} given\.$#' - identifier: argument.type - count: 1 - path: tests/Doctrine/ODM/MongoDB/Tests/Functional/QueryableEncryptionTest.php - - message: '#^Parameter \#1 \$value of function count expects array\|Countable, Iterator\ given\.$#' identifier: argument.type @@ -2005,43 +1999,43 @@ parameters: path: tests/Doctrine/ODM/MongoDB/Tests/Tools/EncryptedFieldsMapGeneratorTest.php - - message: '#^Method Doctrine\\Persistence\\Mapping\\AbstractClassMetadataFactory@anonymous/tests/Doctrine/ODM/MongoDB/Tests/Tools/EncryptedFieldsMapGeneratorTest\.php\:165\:\:__construct\(\) has parameter \$classNames with no value type specified in iterable type array\.$#' + message: '#^Method Doctrine\\Persistence\\Mapping\\AbstractClassMetadataFactory@anonymous/tests/Doctrine/ODM/MongoDB/Tests/Tools/EncryptedFieldsMapGeneratorTest\.php\:168\:\:__construct\(\) has parameter \$classNames with no value type specified in iterable type array\.$#' identifier: missingType.iterableValue count: 1 path: tests/Doctrine/ODM/MongoDB/Tests/Tools/EncryptedFieldsMapGeneratorTest.php - - message: '#^Method Doctrine\\Persistence\\Mapping\\AbstractClassMetadataFactory@anonymous/tests/Doctrine/ODM/MongoDB/Tests/Tools/EncryptedFieldsMapGeneratorTest\.php\:165\:\:doLoadMetadata\(\) has parameter \$class with generic interface Doctrine\\Persistence\\Mapping\\ClassMetadata but does not specify its types\: T$#' + message: '#^Method Doctrine\\Persistence\\Mapping\\AbstractClassMetadataFactory@anonymous/tests/Doctrine/ODM/MongoDB/Tests/Tools/EncryptedFieldsMapGeneratorTest\.php\:168\:\:doLoadMetadata\(\) has parameter \$class with generic interface Doctrine\\Persistence\\Mapping\\ClassMetadata but does not specify its types\: T$#' identifier: missingType.generics count: 1 path: tests/Doctrine/ODM/MongoDB/Tests/Tools/EncryptedFieldsMapGeneratorTest.php - - message: '#^Method Doctrine\\Persistence\\Mapping\\AbstractClassMetadataFactory@anonymous/tests/Doctrine/ODM/MongoDB/Tests/Tools/EncryptedFieldsMapGeneratorTest\.php\:165\:\:doLoadMetadata\(\) has parameter \$parent with generic interface Doctrine\\Persistence\\Mapping\\ClassMetadata but does not specify its types\: T$#' + message: '#^Method Doctrine\\Persistence\\Mapping\\AbstractClassMetadataFactory@anonymous/tests/Doctrine/ODM/MongoDB/Tests/Tools/EncryptedFieldsMapGeneratorTest\.php\:168\:\:doLoadMetadata\(\) has parameter \$parent with generic interface Doctrine\\Persistence\\Mapping\\ClassMetadata but does not specify its types\: T$#' identifier: missingType.generics count: 1 path: tests/Doctrine/ODM/MongoDB/Tests/Tools/EncryptedFieldsMapGeneratorTest.php - - message: '#^Method Doctrine\\Persistence\\Mapping\\AbstractClassMetadataFactory@anonymous/tests/Doctrine/ODM/MongoDB/Tests/Tools/EncryptedFieldsMapGeneratorTest\.php\:165\:\:getAllMetadata\(\) return type with generic interface Doctrine\\Persistence\\Mapping\\ClassMetadata does not specify its types\: T$#' + message: '#^Method Doctrine\\Persistence\\Mapping\\AbstractClassMetadataFactory@anonymous/tests/Doctrine/ODM/MongoDB/Tests/Tools/EncryptedFieldsMapGeneratorTest\.php\:168\:\:getAllMetadata\(\) return type with generic interface Doctrine\\Persistence\\Mapping\\ClassMetadata does not specify its types\: T$#' identifier: missingType.generics count: 1 path: tests/Doctrine/ODM/MongoDB/Tests/Tools/EncryptedFieldsMapGeneratorTest.php - - message: '#^Method Doctrine\\Persistence\\Mapping\\AbstractClassMetadataFactory@anonymous/tests/Doctrine/ODM/MongoDB/Tests/Tools/EncryptedFieldsMapGeneratorTest\.php\:165\:\:initializeReflection\(\) has parameter \$class with generic interface Doctrine\\Persistence\\Mapping\\ClassMetadata but does not specify its types\: T$#' + message: '#^Method Doctrine\\Persistence\\Mapping\\AbstractClassMetadataFactory@anonymous/tests/Doctrine/ODM/MongoDB/Tests/Tools/EncryptedFieldsMapGeneratorTest\.php\:168\:\:initializeReflection\(\) has parameter \$class with generic interface Doctrine\\Persistence\\Mapping\\ClassMetadata but does not specify its types\: T$#' identifier: missingType.generics count: 1 path: tests/Doctrine/ODM/MongoDB/Tests/Tools/EncryptedFieldsMapGeneratorTest.php - - message: '#^Method Doctrine\\Persistence\\Mapping\\AbstractClassMetadataFactory@anonymous/tests/Doctrine/ODM/MongoDB/Tests/Tools/EncryptedFieldsMapGeneratorTest\.php\:165\:\:isEntity\(\) has parameter \$class with generic interface Doctrine\\Persistence\\Mapping\\ClassMetadata but does not specify its types\: T$#' + message: '#^Method Doctrine\\Persistence\\Mapping\\AbstractClassMetadataFactory@anonymous/tests/Doctrine/ODM/MongoDB/Tests/Tools/EncryptedFieldsMapGeneratorTest\.php\:168\:\:isEntity\(\) has parameter \$class with generic interface Doctrine\\Persistence\\Mapping\\ClassMetadata but does not specify its types\: T$#' identifier: missingType.generics count: 1 path: tests/Doctrine/ODM/MongoDB/Tests/Tools/EncryptedFieldsMapGeneratorTest.php - - message: '#^Method Doctrine\\Persistence\\Mapping\\AbstractClassMetadataFactory@anonymous/tests/Doctrine/ODM/MongoDB/Tests/Tools/EncryptedFieldsMapGeneratorTest\.php\:165\:\:wakeupReflection\(\) has parameter \$class with generic interface Doctrine\\Persistence\\Mapping\\ClassMetadata but does not specify its types\: T$#' + message: '#^Method Doctrine\\Persistence\\Mapping\\AbstractClassMetadataFactory@anonymous/tests/Doctrine/ODM/MongoDB/Tests/Tools/EncryptedFieldsMapGeneratorTest\.php\:168\:\:wakeupReflection\(\) has parameter \$class with generic interface Doctrine\\Persistence\\Mapping\\ClassMetadata but does not specify its types\: T$#' identifier: missingType.generics count: 1 path: tests/Doctrine/ODM/MongoDB/Tests/Tools/EncryptedFieldsMapGeneratorTest.php diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Functional/QueryableEncryptionTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Functional/QueryableEncryptionTest.php index c12030688..644e9005c 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/QueryableEncryptionTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/QueryableEncryptionTest.php @@ -14,6 +14,7 @@ use MongoDB\Model\BSONDocument; use function count; +use function getenv; use function iterator_to_array; use function random_bytes; @@ -99,6 +100,15 @@ protected static function createTestDocumentManager(): DocumentManager 'key' => new Binary(random_bytes(96)), ]); + $autoEncryptionOptions = []; + + $cryptSharedLibPath = getenv('CRYPT_SHARED_LIB_PATH'); + if ($cryptSharedLibPath) { + $autoEncryptionOptions['extraOptions']['cryptSharedLibPath'] = $cryptSharedLibPath; + } + + $config->setAutoEncryption($autoEncryptionOptions); + $client = new Client(self::getUri(), [], $config->getDriverOptions()); return DocumentManager::create($client, $config); diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Tools/EncryptedFieldsMapGeneratorTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Tools/EncryptedFieldsMapGeneratorTest.php index 569d99043..028fe827e 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Tools/EncryptedFieldsMapGeneratorTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Tools/EncryptedFieldsMapGeneratorTest.php @@ -90,24 +90,27 @@ public function testVariousRangeTypes(): void ], [ 'path' => 'floatField', - 'bsonType' => 'float', + 'bsonType' => 'double', 'keyId' => null, - 'queries' => ['queryType' => 'range', 'min' => 5.5, 'max' => 10.5], + 'queries' => ['queryType' => 'range', 'min' => 5.5, 'max' => 10.5, 'precision' => 1], ], [ 'path' => 'decimalField', - 'bsonType' => 'decimal128', + 'bsonType' => 'decimal', 'keyId' => null, - 'queries' => ['queryType' => 'range', 'min' => new Decimal128('0.1'), 'max' => new Decimal128('0.2')], + 'queries' => ['queryType' => 'range', 'min' => new Decimal128('0.1'), 'max' => new Decimal128('0.2'), 'precision' => 2], ], [ 'path' => 'dateField', - 'bsonType' => 'date_immutable', + 'bsonType' => 'date', 'keyId' => null, 'queries' => [ 'queryType' => 'range', 'min' => new UTCDateTime(new DateTimeImmutable('2000-01-01 00:00:00')), 'max' => new UTCDateTime(new DateTimeImmutable('2100-01-01 00:00:00')), + 'sparsity' => 1, + 'trimFactor' => 3, + 'contention' => 4, ], ], ]; diff --git a/tests/Documents/Encryption/RangeTypes.php b/tests/Documents/Encryption/RangeTypes.php index a16c60afb..a0684506f 100644 --- a/tests/Documents/Encryption/RangeTypes.php +++ b/tests/Documents/Encryption/RangeTypes.php @@ -29,14 +29,21 @@ class RangeTypes public int $intField; #[Field(type: Type::FLOAT)] - #[Encrypt(EncryptQuery::Range, min: 5.5, max: 10.5)] + #[Encrypt(EncryptQuery::Range, min: 5.5, max: 10.5, precision: 1)] public float $floatField; #[Field(type: Type::DECIMAL128)] - #[Encrypt(EncryptQuery::Range, min: new Decimal128('0.1'), max: new Decimal128('0.2'))] + #[Encrypt(EncryptQuery::Range, min: new Decimal128('0.1'), max: new Decimal128('0.2'), precision: 2)] public Decimal128 $decimalField; #[Field(type: Type::DATE_IMMUTABLE)] - #[Encrypt(EncryptQuery::Range, min: new DateTimeImmutable('2000-01-01 00:00:00'), max: new DateTimeImmutable('2100-01-01 00:00:00'))] + #[Encrypt( + queryType: EncryptQuery::Range, + min: new DateTimeImmutable('2000-01-01 00:00:00'), + max: new DateTimeImmutable('2100-01-01 00:00:00'), + sparsity: 1, + trimFactor: 3, + contention: 4, + )] public DateTimeImmutable $dateField; }