import type { ISavedObjectTypeRegistry , SavedObject , SavedObjectsBaseOptions , SavedObjectsBulkCreateObject , SavedObjectsBulkGetObject , SavedObjectsBulkResolveObject , SavedObjectsBulkResponse , SavedObjectsBulkUpdateObject , SavedObjectsBulkUpdateResponse , SavedObjectsCheckConflictsObject , SavedObjectsClientContract , SavedObjectsClosePointInTimeOptions , SavedObjectsCollectMultiNamespaceReferencesObject , SavedObjectsCollectMultiNamespaceReferencesOptions , SavedObjectsCollectMultiNamespaceReferencesResponse , SavedObjectsCreateOptions , SavedObjectsCreatePointInTimeFinderDependencies , SavedObjectsCreatePointInTimeFinderOptions , SavedObjectsFindOptions , SavedObjectsFindResponse , SavedObjectsOpenPointInTimeOptions , SavedObjectsRemoveReferencesToOptions , SavedObjectsRemoveReferencesToResponse , SavedObjectsUpdateObjectsSpacesObject , SavedObjectsUpdateObjectsSpacesOptions , SavedObjectsUpdateOptions , SavedObjectsUpdateResponse , } from 'src/core/server' ; import { SavedObjectsUtils } from '../../../../../src/core/server' ; import type { AuthenticatedUser } from '../../../security/common/model' ; import type { EncryptedSavedObjectsService } from '../crypto' ; import { getDescriptorNamespace } from './get_descriptor_namespace' ; interface EncryptedSavedObjectsClientOptions { baseClient : SavedObjectsClientContract ; baseTypeRegistry : ISavedObjectTypeRegistry ; service : Readonly < EncryptedSavedObjectsService > ; getCurrentUser : ( ) => AuthenticatedUser | undefined ; } export class EncryptedSavedObjectsClientWrapper implements SavedObjectsClientContract { constructor ( private readonly options , public readonly errors = options . baseClient . errors ) { } public async checkConflicts ( objects = [ ] , options ? ) { return await this . options . baseClient . checkConflicts ( objects , options ) ; } public async create < T > ( type , attributes = { } as T , options = { } ) { if ( ! this . options . service . isRegistered ( type ) ) { return await this . options . baseClient . create ( type , attributes , options ) ; } const id = this . getValidId ( options . id , options . version , options . overwrite ) ; const namespace = getDescriptorNamespace ( this . options . baseTypeRegistry , type , options . namespace ) ; return await this . handleEncryptedAttributesInResponse ( await this . options . baseClient . create ( type , ( await this . options . service . encryptAttributes ( { type , id , namespace } , attributes as Record < string , unknown > , { user : this . options . getCurrentUser ( ) } ) ) as T , { ... options , id } ) , attributes , namespace ) ; } public async bulkCreate < T > ( objects , options ? ) { const encryptedObjects = await Promise . all ( objects . map ( async ( object ) => { if ( ! this . options . service . isRegistered ( object . type ) ) { return object ; } const id = this . getValidId ( object . id , object . version , options ?. overwrite ) ; const namespace = getDescriptorNamespace ( this . options . baseTypeRegistry , object . type , options ?. namespace ) ; return { ... object , id , attributes : await this . options . service . encryptAttributes ( { type : object . type , id , namespace } , object . attributes as Record < string , unknown > , { user : this . options . getCurrentUser ( ) } ) , } as SavedObjectsBulkCreateObject < T > ; } ) ) ; return await this . handleEncryptedAttributesInBulkResponse ( await this . options . baseClient . bulkCreate < T > ( encryptedObjects , options ) , objects ) ; } public async bulkUpdate < T > ( objects , options ? ) { const encryptedObjects = await Promise . all ( objects . map ( async ( object ) => { const { type , id , attributes , namespace : objectNamespace } = object ; if ( ! this . options . service . isRegistered ( type ) ) { return object ; } const namespace = getDescriptorNamespace ( this . options . baseTypeRegistry , type , objectNamespace ?? options ?. namespace ) ; return { ... object , attributes : await this . options . service . encryptAttributes ( { type , id , namespace } , attributes , { user : this . options . getCurrentUser ( ) } ) , } ; } ) ) ; return await this . handleEncryptedAttributesInBulkResponse ( await this . options . baseClient . bulkUpdate ( encryptedObjects , options ) , objects ) ; } public async delete ( type , id , options ? ) { return await this . options . baseClient . delete ( type , id , options ) ; } public async find < T , A > ( options ) { return await this . handleEncryptedAttributesInBulkResponse ( await this . options . baseClient . find < T , A > ( options ) , undefined ) ; } public async bulkGet < T > ( objects = [ ] , options ? ) { return await this . handleEncryptedAttributesInBulkResponse ( await this . options . baseClient . bulkGet < T > ( objects , options ) , undefined ) ; } public async get < T > ( type , id , options ? ) { return await this . handleEncryptedAttributesInResponse ( await this . options . baseClient . get < T > ( type , id , options ) , undefined as unknown , getDescriptorNamespace ( this . options . baseTypeRegistry , type , options ?. namespace ) ) ; } public async bulkResolve < T = unknown > ( objects , options ? ) { const bulkResolveResult = await this . options . baseClient . bulkResolve < T > ( objects , options ) ; for ( const resolved of bulkResolveResult . resolved_objects ) { const savedObject = resolved . saved_object ; await this . handleEncryptedAttributesInResponse ( savedObject , undefined as unknown , getDescriptorNamespace ( this . options . baseTypeRegistry , savedObject . type , savedObject . namespaces ? savedObject . namespaces [ 0 ] : undefined ) ) ; } return bulkResolveResult ; } public async resolve < T > ( type , id , options ? ) { const resolveResult = await this . options . baseClient . resolve < T > ( type , id , options ) ; const object = await this . handleEncryptedAttributesInResponse ( resolveResult . saved_object , undefined as unknown , getDescriptorNamespace ( this . options . baseTypeRegistry , type , options ?. namespace ) ) ; return { ... resolveResult , saved_object : object , } ; } public async update < T > ( type , id , attributes , options ? ) { if ( ! this . options . service . isRegistered ( type ) ) { return await this . options . baseClient . update ( type , id , attributes , options ) ; } const namespace = getDescriptorNamespace ( this . options . baseTypeRegistry , type , options ?. namespace ) ; return this . handleEncryptedAttributesInResponse ( await this . options . baseClient . update ( type , id , await this . options . service . encryptAttributes ( { type , id , namespace } , attributes , { user : this . options . getCurrentUser ( ) , } ) , options ) , attributes , namespace ) ; } public async removeReferencesTo ( type , id , options = { } ) { return await this . options . baseClient . removeReferencesTo ( type , id , options ) ; } public async openPointInTimeForType ( type , options = { } ) { return await this . options . baseClient . openPointInTimeForType ( type , options ) ; } public async closePointInTime ( id , options ? ) { return await this . options . baseClient . closePointInTime ( id , options ) ; } public createPointInTimeFinder < T = unknown , A = unknown > ( findOptions , dependencies ? ) { return this . options . baseClient . createPointInTimeFinder < T , A > ( findOptions , { client : this , ... dependencies , } ) ; } public async collectMultiNamespaceReferences ( objects , options ? ) { return await this . options . baseClient . collectMultiNamespaceReferences ( objects , options ) ; } public async updateObjectsSpaces ( objects , spacesToAdd , spacesToRemove , options ? ) { return await this . options . baseClient . updateObjectsSpaces ( objects , spacesToAdd , spacesToRemove , options ) ; } param response param originalAttributes param namespace private async handleEncryptedAttributesInResponse < T , R extends SavedObjectsUpdateResponse < T > | SavedObject < T > > ( response , originalAttributes ? , namespace ? ) { if ( response . attributes && this . options . service . isRegistered ( response . type ) ) { const { attributes , error } = await this . options . service . stripOrDecryptAttributes ( { id : response . id , type : response . type , namespace } , response . attributes as Record < string , unknown > , originalAttributes as Record < string , unknown > , { user : this . options . getCurrentUser ( ) } ) ; response . attributes = attributes as T ; if ( error ) { response . error = error as any ; } } return response ; } param response param objects private async handleEncryptedAttributesInBulkResponse < T , R extends SavedObjectsBulkResponse < T > | SavedObjectsFindResponse < T > | SavedObjectsBulkUpdateResponse < T > , O extends Array < SavedObjectsBulkCreateObject < T > > | Array < SavedObjectsBulkUpdateObject < T > > > ( response , objects ? ) { for ( const [ index , savedObject ] of response . saved_objects . entries ( ) ) { await this . handleEncryptedAttributesInResponse ( savedObject , objects ?. [ index ] . attributes ?? undefined , getDescriptorNamespace ( this . options . baseTypeRegistry , savedObject . type , savedObject . namespaces ? savedObject . namespaces [ 0 ] : undefined ) ) ; } return response ; } private getValidId ( id , version , overwrite ) { if ( id ) { const canSpecifyID = ( overwrite && version ) || SavedObjectsUtils . isRandomId ( id ) ; if ( ! canSpecifyID ) { throw this . errors . createBadRequestError ( 'Predefined IDs are not allowed for saved objects with encrypted attributes unless the ID is a UUID.' ) ; } return id ; } return SavedObjectsUtils . generateId ( ) ; } }