From 7fa66160c40759f5af86bee18c2308eb5098a0d3 Mon Sep 17 00:00:00 2001 From: Aditi Khare Date: Wed, 29 Jan 2025 16:30:54 -0500 Subject: [PATCH 1/2] src code changes --- types/schemaoptions.d.ts | 2 ++ types/schematypes.d.ts | 26 ++++++++++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/types/schemaoptions.d.ts b/types/schemaoptions.d.ts index 4df87a806ea..d9bb080878d 100644 --- a/types/schemaoptions.d.ts +++ b/types/schemaoptions.d.ts @@ -258,6 +258,8 @@ declare module 'mongoose' { * @default false */ overwriteModels?: boolean; + + encryptionType?: 'csfle' | 'queryableEncryption'; } interface DefaultSchemaOptions { diff --git a/types/schematypes.d.ts b/types/schematypes.d.ts index 5f364f0cea4..22e2992f6e0 100644 --- a/types/schematypes.d.ts +++ b/types/schematypes.d.ts @@ -1,3 +1,5 @@ +import * as BSON from 'bson'; + declare module 'mongoose' { /** The Mongoose Date [SchemaType](/docs/schematypes.html). */ @@ -207,6 +209,8 @@ declare module 'mongoose' { maxlength?: number | [number, string] | readonly [number, string]; [other: string]: any; + + encrypt?: EncryptSchemaTypeOptions; } interface Validator { @@ -218,6 +222,28 @@ declare module 'mongoose' { type ValidatorFunction = (this: DocType, value: any, validatorProperties?: Validator) => any; + export interface EncryptSchemaTypeOptions { + /** The id of the dataKey to use for encryption */ + keyId: BSON.UUID; + + /** + * Specifies the type of queries that the field can be queried on for Queryable Encryption. + * Required when `SchemaOptions.encryptionType` is 'queryableEncryption' + */ + queries?: 'equality' | 'range'; + + /** + * The algorithm to use for encryption. + * Required when `SchemaOptions.encryptionType` is 'csfle' + */ + algorithm?: + | 'AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic' + | 'AEAD_AES_256_CBC_HMAC_SHA_512-Random' + | 'Indexed' + | 'Unindexed' + | 'Range'; + } + class SchemaType { /** SchemaType constructor */ constructor(path: string, options?: AnyObject, instance?: string); From 616ce14defb00b811e5ca4b4dd4ad8a097608cff Mon Sep 17 00:00:00 2001 From: Aditi Khare Date: Wed, 29 Jan 2025 17:30:41 -0500 Subject: [PATCH 2/2] tests added --- test/types/schema.test.ts | 12 ++++++++- test/types/schemaTypeOptions.test.ts | 37 ++++++++++++++++++++++++++ types/schemaoptions.d.ts | 3 +++ types/schematypes.d.ts | 39 ++++++++++++---------------- 4 files changed, 67 insertions(+), 24 deletions(-) diff --git a/test/types/schema.test.ts b/test/types/schema.test.ts index 13408eaf293..00e0445878f 100644 --- a/test/types/schema.test.ts +++ b/test/types/schema.test.ts @@ -24,7 +24,7 @@ import { ValidateOpts, BufferToBinary } from 'mongoose'; -import { Binary } from 'mongodb'; +import { Binary, BSON } from 'mongodb'; import { IsPathRequired } from '../../types/inferschematype'; import { expectType, expectError, expectAssignable } from 'tsd'; import { ObtainDocumentPathType, ResolvePathType } from '../../types/inferschematype'; @@ -591,6 +591,16 @@ const batchSchema2 = new Schema({ name: String }, { discriminatorKey: 'kind', st } } }); batchSchema2.discriminator('event', eventSchema2); + +function encryptionType() { + const keyId = new BSON.UUID(); + expectError(new Schema({ name: { type: String, encrypt: { keyId } } }, { encryptionType: 'newFakeEncryptionType' })); + expectError(new Schema({ name: { type: String, encrypt: { keyId } } }, { encryptionType: 1 })); + + expectType(new Schema({ name: { type: String, encrypt: { keyId } } }, { encryptionType: 'queryableEncryption' })); + expectType(new Schema({ name: { type: String, encrypt: { keyId } } }, { encryptionType: 'csfle' })); +} + function gh11828() { interface IUser { name: string; diff --git a/test/types/schemaTypeOptions.test.ts b/test/types/schemaTypeOptions.test.ts index 3514b01d7e9..4f38ceab909 100644 --- a/test/types/schemaTypeOptions.test.ts +++ b/test/types/schemaTypeOptions.test.ts @@ -1,3 +1,4 @@ +import { BSON } from 'mongodb'; import { AnyArray, Schema, @@ -74,3 +75,39 @@ function defaultOptions() { expectType>(new Schema.Types.Subdocument('none').defaultOptions); expectType>(new Schema.Types.UUID('none').defaultOptions); } + +function encrypt() { + const keyId = new BSON.UUID(); + + new SchemaTypeOptions()['encrypt'] = { keyId, algorithm: 'AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic' }; + new SchemaTypeOptions()['encrypt'] = { keyId, algorithm: 'AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic' }; + new SchemaTypeOptions()['encrypt'] = { keyId, algorithm: 'AEAD_AES_256_CBC_HMAC_SHA_512-Random' }; + new SchemaTypeOptions()['encrypt'] = { keyId, algorithm: 'Indexed' }; + new SchemaTypeOptions()['encrypt'] = { keyId, algorithm: 'Unindexed' }; + new SchemaTypeOptions()['encrypt'] = { keyId, algorithm: 'Range' }; + new SchemaTypeOptions()['encrypt'] = { keyId, algorithm: undefined }; + + // qe + valid queries + new SchemaTypeOptions()['encrypt'] = { keyId, queries: 'equality' }; + new SchemaTypeOptions()['encrypt'] = { keyId, queries: 'range' }; + new SchemaTypeOptions()['encrypt'] = { keyId, queries: undefined }; + + // empty object + expectError['encrypt']>({}); + + // invalid keyId + expectError['encrypt']>({ keyId: 'fakeId' }); + + // missing keyId + expectError['encrypt']>({ queries: 'equality' }); + expectError['encrypt']>({ algorithm: 'AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic' }); + + // invalid algorithm + expectError['encrypt']>({ keyId, algorithm: 'SHA_FAKE_ALG' }); + + // invalid queries + expectError['encrypt']>({ keyId, queries: 'fakeQueryOption' }); + + // invalid input option + expectError['encrypt']>({ keyId, invalidKey: 'fakeKeyOption' }); +} diff --git a/types/schemaoptions.d.ts b/types/schemaoptions.d.ts index d9bb080878d..f661e1643de 100644 --- a/types/schemaoptions.d.ts +++ b/types/schemaoptions.d.ts @@ -259,6 +259,9 @@ declare module 'mongoose' { */ overwriteModels?: boolean; + /** + * Required when the schema is encrypted. + */ encryptionType?: 'csfle' | 'queryableEncryption'; } diff --git a/types/schematypes.d.ts b/types/schematypes.d.ts index 22e2992f6e0..a59f8c46668 100644 --- a/types/schematypes.d.ts +++ b/types/schematypes.d.ts @@ -210,7 +210,22 @@ declare module 'mongoose' { [other: string]: any; - encrypt?: EncryptSchemaTypeOptions; + encrypt?: { + /** The id of the dataKey to use for encryption */ + keyId: BSON.UUID; + + /** + * Specifies the type of queries that the field can be queried on for Queryable Encryption. + * Required when `SchemaOptions.encryptionType` is 'queryableEncryption' + */ + queries?: 'equality' | 'range'; + + /** + * The algorithm to use for encryption. + * Required when `SchemaOptions.encryptionType` is 'csfle' + */ + algorithm?: 'AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic' | 'AEAD_AES_256_CBC_HMAC_SHA_512-Random' | 'Indexed' | 'Unindexed' | 'Range'; + }; } interface Validator { @@ -222,28 +237,6 @@ declare module 'mongoose' { type ValidatorFunction = (this: DocType, value: any, validatorProperties?: Validator) => any; - export interface EncryptSchemaTypeOptions { - /** The id of the dataKey to use for encryption */ - keyId: BSON.UUID; - - /** - * Specifies the type of queries that the field can be queried on for Queryable Encryption. - * Required when `SchemaOptions.encryptionType` is 'queryableEncryption' - */ - queries?: 'equality' | 'range'; - - /** - * The algorithm to use for encryption. - * Required when `SchemaOptions.encryptionType` is 'csfle' - */ - algorithm?: - | 'AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic' - | 'AEAD_AES_256_CBC_HMAC_SHA_512-Random' - | 'Indexed' - | 'Unindexed' - | 'Range'; - } - class SchemaType { /** SchemaType constructor */ constructor(path: string, options?: AnyObject, instance?: string);