diff --git a/x-pack/plugins/encrypted_saved_objects/README.md b/x-pack/plugins/encrypted_saved_objects/README.md index 887ba85976cb2..d05c83f44a7fa 100644 --- a/x-pack/plugins/encrypted_saved_objects/README.md +++ b/x-pack/plugins/encrypted_saved_objects/README.md @@ -99,7 +99,7 @@ const savedObjectWithDecryptedContent = await esoClient.getDecryptedAsInternalU one would pass to `SavedObjectsClient.get`. These argument allows to specify `namespace` property that, for example, is required if Saved Object was created within a non-default space. -### defining migrations +### Defining migrations EncryptedSavedObjects rely on standard SavedObject migrations, but due to the additional complexity introduced by the need to decrypt and reencrypt the migrated document, there are some caveats to how we support this. The good news is, most of this complexity is abstracted away by the plugin and all you need to do is leverage our api. @@ -118,7 +118,7 @@ For example: ```typescript const migration790 = encryptedSavedObjects.createMigration( - function shouldbeMigrated(doc): doc is SavedObjectUnsanitizedDoc { + function shouldBeMigrated(doc): doc is SavedObjectUnsanitizedDoc { return doc.consumer === 'alerting' || doc.consumer === undefined; }, (doc: SavedObjectUnsanitizedDoc): SavedObjectUnsanitizedDoc => { @@ -135,16 +135,16 @@ const migration790 = encryptedSavedObjects.createMigration( }, // type hasn't changed as the field we're updating is not an encrypted one { - type: 'alert', - attributesToEncrypt: new Set(['apiKey']), - attributesToExcludeFromAAD: new Set(['mutedInstanceIds', 'updatedBy']), - } + type: 'alert', + attributesToEncrypt: new Set(['apiKey']), + attributesToExcludeFromAAD: new Set(['mutedInstanceIds', 'updatedBy']), + } ); ``` In the above example you can see thwe following: -1. In `shouldbeMigrated` we limit the migrated alerts to those whose `consumer` field equals `alerting` or is undefined. -2. In the migration function we then migrate the value of `consumer` to the value we want (`alerts` or `unknown`, dependsing on the current value). In this function we can assume that only documents with a `consumer` of `alerting` or `undefined` will be passed in, but it's still safest not to, and so we use the current `consumer` as the default when needed. +1. In `shouldBeMigrated` we limit the migrated alerts to those whose `consumer` field equals `alerting` or is undefined. +2. In the migration function we then migrate the value of `consumer` to the value we want (`alerts` or `unknown`, depending on the current value). In this function we can assume that only documents with a `consumer` of `alerting` or `undefined` will be passed in, but it's still safest not to, and so we use the current `consumer` as the default when needed. 3. We provide the type, which remains unchanged across this migration and so we can omit the fourth argument. As we said above, an EncryptedSavedObject migration is a normal SavedObjects migration, and so we can plug it into the underlying SavedObject just like any other kind of migration: diff --git a/x-pack/plugins/encrypted_saved_objects/server/create_migration.ts b/x-pack/plugins/encrypted_saved_objects/server/create_migration.ts index c4ce358b1bac8..7d653d03065a9 100644 --- a/x-pack/plugins/encrypted_saved_objects/server/create_migration.ts +++ b/x-pack/plugins/encrypted_saved_objects/server/create_migration.ts @@ -8,11 +8,13 @@ import { SavedObjectUnsanitizedDoc, SavedObjectMigrationFn, SavedObjectMigrationContext, -} from 'kibana/server'; -import { pick } from 'lodash'; -import { EncryptedSavedObjectAttributesDefinition } from './crypto/encrypted_saved_object_type_definition'; -import { EncryptedSavedObjectTypeRegistration, SavedObjectDescriptor } from './crypto'; -import { EncryptedSavedObjectsMigrationService } from './crypto/encrypted_saved_objects_migration_service'; +} from 'src/core/server'; +import { + EncryptedSavedObjectTypeRegistration, + SavedObjectDescriptor, + EncryptedSavedObjectsMigrationService, + EncryptedSavedObjectAttributesDefinition, +} from './crypto'; type SavedObjectOptionalMigrationFn = ( doc: SavedObjectUnsanitizedDoc | SavedObjectUnsanitizedDoc, @@ -25,7 +27,7 @@ type IsMigrationNeededPredicate = ( | SavedObjectUnsanitizedDoc ) => encryptedDoc is SavedObjectUnsanitizedDoc; -export type CreateESOMigrationFn = < +export type CreateEncryptedSavedObjectsMigrationFn = < InputAttributes = unknown, MigratedAttributes = InputAttributes >( @@ -37,7 +39,7 @@ export type CreateESOMigrationFn = < export const getCreateMigration = ( migrationService: EncryptedSavedObjectsMigrationService -): CreateESOMigrationFn => ( +): CreateEncryptedSavedObjectsMigrationFn => ( isMigrationNeededPredicate, migration, inputType, @@ -51,37 +53,32 @@ export const getCreateMigration = ( const inputTypeDefinition = new EncryptedSavedObjectAttributesDefinition(inputType); const migratedTypeDefinition = new EncryptedSavedObjectAttributesDefinition(migratedType); return (encryptedDoc, context) => { - if (isMigrationNeededPredicate(encryptedDoc)) { - const descriptor = pick( - encryptedDoc, - 'id', - 'type', - 'namespace' - ); + if (!isMigrationNeededPredicate(encryptedDoc)) { + return encryptedDoc; + } + const descriptor = { + id: encryptedDoc.id, + type: encryptedDoc.type, + namespace: encryptedDoc.namespace, + }; - // decrypt the attributes using the input type definition - // then migrate the document - // then encrypt the attributes using the migration type definition - return mapAttributes( - migration( - mapAttributes(encryptedDoc, (inputAttributes) => - migrationService.decryptAttributes( - descriptor, - inputTypeDefinition, - inputAttributes - ) - ), - context + // decrypt the attributes using the input type definition + // then migrate the document + // then encrypt the attributes using the migration type definition + return mapAttributes( + migration( + mapAttributes(encryptedDoc, (inputAttributes) => + migrationService.decryptAttributes(descriptor, inputTypeDefinition, inputAttributes) ), - (migratedAttributes) => - migrationService.encryptAttributes( - descriptor, - migratedTypeDefinition, - migratedAttributes - ) - ); - } - return encryptedDoc; + context + ), + (migratedAttributes) => + migrationService.encryptAttributes( + descriptor, + migratedTypeDefinition, + migratedAttributes + ) + ); }; }; diff --git a/x-pack/plugins/encrypted_saved_objects/server/crypto/encrypted_saved_objects_migration_service.test.ts b/x-pack/plugins/encrypted_saved_objects/server/crypto/encrypted_saved_objects_migration_service.test.ts index 236b2168197b6..4a87e00f95254 100644 --- a/x-pack/plugins/encrypted_saved_objects/server/crypto/encrypted_saved_objects_migration_service.test.ts +++ b/x-pack/plugins/encrypted_saved_objects/server/crypto/encrypted_saved_objects_migration_service.test.ts @@ -9,7 +9,7 @@ import nodeCrypto, { Crypto } from '@elastic/node-crypto'; import { EncryptedSavedObjectsMigrationService } from './encrypted_saved_objects_migration_service'; import { EncryptedSavedObjectAttributesDefinition } from './encrypted_saved_object_type_definition'; import { EncryptionError } from './encryption_error'; -import { loggingServiceMock } from 'src/core/server/mocks'; +import { loggingSystemMock } from 'src/core/server/mocks'; let service: EncryptedSavedObjectsMigrationService; @@ -25,7 +25,7 @@ const mockNodeCrypto: jest.Mocked = { beforeEach(() => { service = new EncryptedSavedObjectsMigrationService( mockNodeCrypto, - loggingServiceMock.create().get() + loggingSystemMock.create().get() ); // Call actual `@elastic/node-crypto` by default, but allow to override implementation in tests. @@ -49,7 +49,7 @@ describe('#encryptAttributes', () => { service = new EncryptedSavedObjectsMigrationService( mockNodeCrypto, - loggingServiceMock.create().get() + loggingSystemMock.create().get() ); }); @@ -575,7 +575,7 @@ describe('#decryptAttributes', () => { it('fails if encrypted with another encryption key', () => { service = new EncryptedSavedObjectsMigrationService( nodeCrypto({ encryptionKey: 'encryption-key-abc*' }), - loggingServiceMock.create().get() + loggingSystemMock.create().get() ); const type = new EncryptedSavedObjectAttributesDefinition({ diff --git a/x-pack/plugins/encrypted_saved_objects/server/crypto/index.ts b/x-pack/plugins/encrypted_saved_objects/server/crypto/index.ts index 0849f0eb320dd..23e293e75ae34 100644 --- a/x-pack/plugins/encrypted_saved_objects/server/crypto/index.ts +++ b/x-pack/plugins/encrypted_saved_objects/server/crypto/index.ts @@ -11,3 +11,5 @@ export { SavedObjectDescriptor, } from './encrypted_saved_objects_service'; export { EncryptionError } from './encryption_error'; +export { EncryptedSavedObjectsMigrationService } from './encrypted_saved_objects_migration_service'; +export { EncryptedSavedObjectAttributesDefinition } from './encrypted_saved_object_type_definition'; diff --git a/x-pack/plugins/encrypted_saved_objects/server/plugin.ts b/x-pack/plugins/encrypted_saved_objects/server/plugin.ts index 5fd7f47006001..6553fdfd94de2 100644 --- a/x-pack/plugins/encrypted_saved_objects/server/plugin.ts +++ b/x-pack/plugins/encrypted_saved_objects/server/plugin.ts @@ -13,11 +13,11 @@ import { EncryptedSavedObjectsService, EncryptedSavedObjectTypeRegistration, EncryptionError, + EncryptedSavedObjectsMigrationService, } from './crypto'; import { EncryptedSavedObjectsAuditLogger } from './audit'; import { setupSavedObjects, ClientInstanciator } from './saved_objects'; -import { EncryptedSavedObjectsMigrationService } from './crypto/encrypted_saved_objects_migration_service'; -import { getCreateMigration, CreateESOMigrationFn } from './create_migration'; +import { getCreateMigration, CreateEncryptedSavedObjectsMigrationFn } from './create_migration'; export interface PluginsSetup { security?: SecurityPluginSetup; @@ -26,7 +26,7 @@ export interface PluginsSetup { export interface EncryptedSavedObjectsPluginSetup { registerType: (typeRegistration: EncryptedSavedObjectTypeRegistration) => void; usingEphemeralEncryptionKey: boolean; - createMigration: CreateESOMigrationFn; + createMigration: CreateEncryptedSavedObjectsMigrationFn; } export interface EncryptedSavedObjectsPluginStart {