From 951de0f80c092d1f9f1f0729518d84b3902f3b32 Mon Sep 17 00:00:00 2001 From: Austin Golding Date: Tue, 8 Nov 2022 21:56:30 -0800 Subject: [PATCH 1/2] Move anonymization machine to final state if error occurs during anonymization Print debug log for anonymization error --- .../server/src/machines/anonymization/anonymization.machine.ts | 2 +- applications/server/src/store/campaign-resolvers.ts | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/applications/server/src/machines/anonymization/anonymization.machine.ts b/applications/server/src/machines/anonymization/anonymization.machine.ts index 9e101411..47b15b7a 100644 --- a/applications/server/src/machines/anonymization/anonymization.machine.ts +++ b/applications/server/src/machines/anonymization/anonymization.machine.ts @@ -67,7 +67,7 @@ export const anonymizationMachine = createMachine( target: 'finished', }, onError: { - target: 'idle', + target: 'finished', actions: ['setError'], }, }, diff --git a/applications/server/src/store/campaign-resolvers.ts b/applications/server/src/store/campaign-resolvers.ts index 93c0d57a..16d0b391 100644 --- a/applications/server/src/store/campaign-resolvers.ts +++ b/applications/server/src/store/campaign-resolvers.ts @@ -129,6 +129,7 @@ export class CampaignResolvers { machine.onDone(() => { // If there is an error during anonymization delete copy and return the error if (machine.state.context.error) { + console.debug('Error during export & anonymization ', machine.state.context.error); fs.rmSync(anonymizedDbPath, { recursive: true }); return reject(Error(machine.state.context.error)); } else { From 1d19e76009b5fcf577cd42ea948247222d465d98 Mon Sep 17 00:00:00 2001 From: Austin Golding Date: Tue, 8 Nov 2022 22:55:15 -0800 Subject: [PATCH 2/2] Clean up anonymization mutation & machine Ensure anonymization feedback is clear in dialog --- .../AnonymizeDialog/AnonymizeDialog.tsx | 4 ++ .../anonymization/anonymization.machine.ts | 11 +--- .../anonymization.machine.typegen.ts | 8 +-- .../server/src/store/campaign-resolvers.ts | 59 +++++++++---------- 4 files changed, 40 insertions(+), 42 deletions(-) diff --git a/applications/client/src/views/Campaigns/AnonymizeDialog/AnonymizeDialog.tsx b/applications/client/src/views/Campaigns/AnonymizeDialog/AnonymizeDialog.tsx index 6c47b368..d3d639d7 100644 --- a/applications/client/src/views/Campaigns/AnonymizeDialog/AnonymizeDialog.tsx +++ b/applications/client/src/views/Campaigns/AnonymizeDialog/AnonymizeDialog.tsx @@ -79,6 +79,10 @@ export const AnonymizeDialog = observer(({ campaign, onClo ), }, }); + if (!fileName) { + this.error = 'Error anonymizing campaign'; + return (this.isLoading = false); + } const res: Response = yield store.auth.protectedFetch( `${store.auth.serverUrl}/api/campaign/download/${fileName.anonymizeCampaign}`, { diff --git a/applications/server/src/machines/anonymization/anonymization.machine.ts b/applications/server/src/machines/anonymization/anonymization.machine.ts index 47b15b7a..54f137df 100644 --- a/applications/server/src/machines/anonymization/anonymization.machine.ts +++ b/applications/server/src/machines/anonymization/anonymization.machine.ts @@ -43,7 +43,7 @@ export interface AnonymizationMachineContext extends AnonymizationInput { error?: string; } -type AnonymizationMachineEvent = { type: 'ANONYMIZE' } | { type: 'SET_ERROR'; error: Error }; +type AnonymizationMachineEvent = { type: 'ANONYMIZE' }; export const anonymizationMachine = createMachine( { @@ -67,13 +67,8 @@ export const anonymizationMachine = createMachine( target: 'finished', }, onError: { + actions: 'setError', target: 'finished', - actions: ['setError'], - }, - }, - on: { - SET_ERROR: { - actions: ['setError'], }, }, }, @@ -85,7 +80,7 @@ export const anonymizationMachine = createMachine( { actions: { setError: assign((_, event) => ({ - error: 'error' in event ? event.error.message : undefined, + error: 'data' in event ? (event.data as Error)?.message : undefined, })), }, services: { diff --git a/applications/server/src/machines/anonymization/anonymization.machine.typegen.ts b/applications/server/src/machines/anonymization/anonymization.machine.typegen.ts index b0b1f0ed..3720340d 100644 --- a/applications/server/src/machines/anonymization/anonymization.machine.typegen.ts +++ b/applications/server/src/machines/anonymization/anonymization.machine.typegen.ts @@ -2,9 +2,6 @@ export interface Typegen0 { '@@xstate/typegen': true; - eventsCausingActions: { - setError: 'error.platform.ANONYMIZATION.anonymizing:invocation[0]' | 'SET_ERROR'; - }; internalEvents: { 'error.platform.ANONYMIZATION.anonymizing:invocation[0]': { type: 'error.platform.ANONYMIZATION.anonymizing:invocation[0]'; @@ -21,11 +18,14 @@ export interface Typegen0 { guards: never; delays: never; }; + eventsCausingActions: { + setError: 'error.platform.ANONYMIZATION.anonymizing:invocation[0]'; + }; eventsCausingServices: { anonymizeService: 'ANONYMIZE'; }; eventsCausingGuards: {}; eventsCausingDelays: {}; - matchesStates: 'idle' | 'anonymizing' | 'finished'; + matchesStates: 'anonymizing' | 'finished' | 'idle'; tags: never; } diff --git a/applications/server/src/store/campaign-resolvers.ts b/applications/server/src/store/campaign-resolvers.ts index 16d0b391..b479b4ca 100644 --- a/applications/server/src/store/campaign-resolvers.ts +++ b/applications/server/src/store/campaign-resolvers.ts @@ -96,42 +96,41 @@ export class CampaignResolvers { @Authorized() @Mutation(() => String, { description: 'Anonymize campaign for export' }) - anonymizeCampaign( + async anonymizeCampaign( @Ctx() ctx: GraphQLContext, @Arg('campaignId', () => String) campaignId: string, @Arg('anonymizeOptions') anonymizeOptions: AnonymizationInput ): Promise { - return new Promise(async (resolve, reject) => { - if (ctx.config.blueTeam) return reject(new AuthenticationError('Blue team cannot export')); - const tempCampaignFolder = `campaign-${randomUUID()}`; - const dbPath = path.join(getDbPath(ctx.config.databaseMode), 'campaign', campaignId); - const anonymizedDbPath = path.join(getDbPath(ctx.config.databaseMode), 'anonymized-campaigns', tempCampaignFolder); - const exists = existsSync(dbPath); - if (!exists) { - return reject(Error('Database not found')); - } else { - try { - const filePath = path.join(anonymizedDbPath, 'db.redeye'); - - await fs.copy(dbPath, anonymizedDbPath); - const orm = await MikroORM.init({ ...getProjectMikroOrmConfig(filePath) }); - await orm.em.getDriver().execute('PRAGMA wal_checkpoint'); - await orm.close(); + if (ctx.config.blueTeam) throw new AuthenticationError('Blue team cannot export'); + const tempCampaignFolder = `campaign-${randomUUID()}`; + const dbPath = path.join(getDbPath(ctx.config.databaseMode), 'campaign', campaignId); + const anonymizedDbPath = path.join(getDbPath(ctx.config.databaseMode), 'anonymized-campaigns', tempCampaignFolder); + const exists = existsSync(dbPath); + if (!exists) { + throw Error('Database not found'); + } else { + try { + const filePath = path.join(anonymizedDbPath, 'db.redeye'); - const machine = interpret( - anonymizationMachine.withContext({ - database: filePath, - ...anonymizeOptions, - }) - ).start(); - machine.send('ANONYMIZE'); + await fs.copy(dbPath, anonymizedDbPath); + const orm = await MikroORM.init({ ...getProjectMikroOrmConfig(filePath) }); + await orm.em.getDriver().execute('PRAGMA wal_checkpoint'); + await orm.close(); + const machine = interpret( + anonymizationMachine.withContext({ + database: filePath, + ...anonymizeOptions, + }) + ).start(); + machine.send('ANONYMIZE'); + return new Promise(async (resolve, reject) => { machine.onDone(() => { // If there is an error during anonymization delete copy and return the error if (machine.state.context.error) { console.debug('Error during export & anonymization ', machine.state.context.error); fs.rmSync(anonymizedDbPath, { recursive: true }); - return reject(Error(machine.state.context.error)); + return reject(new Error(machine.state.context.error)); } else { // After a minute, delete the folder if it still exists setTimeout(() => { @@ -142,11 +141,11 @@ export class CampaignResolvers { return resolve(tempCampaignFolder); } }); - } catch (e) { - fs.rmSync(anonymizedDbPath, { recursive: true }); - return reject(Error((e as Error).message)); - } + }); + } catch (e) { + fs.rmSync(anonymizedDbPath, { recursive: true }); + throw Error((e as Error).message); } - }); + } } }