Skip to content

Commit

Permalink
fix(apple): emit notification when apple user migration is complete
Browse files Browse the repository at this point in the history
  • Loading branch information
vbudhram committed Jun 26, 2023
1 parent 6d65d20 commit adf89d7
Show file tree
Hide file tree
Showing 9 changed files with 166 additions and 5 deletions.
28 changes: 26 additions & 2 deletions packages/fxa-auth-server/scripts/transfer-users/apple.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const program = require('commander');

const axios = require('axios');
const random = require('../../lib/crypto/random');
const Log = require('../../lib/log')
const uuid = require('uuid');

const GRANT_TYPE = 'client_credentials';
Expand All @@ -20,7 +21,7 @@ const USER_MIGRATION_ENDPOINT = 'https://appleid.apple.com/auth/usermigrationinf
const APPLE_PROVIDER = 'apple';

export class AppleUser {
constructor(email, transferSub, uid, alternateEmails, db, writeStream, config, mock) {
constructor(email, transferSub, uid, alternateEmails, db, writeStream, config, mock, log) {
this.email = email;
this.transferSub = transferSub;
this.uid = uid;
Expand All @@ -29,6 +30,7 @@ export class AppleUser {
this.writeStream = writeStream;
this.config = config;
this.mock = mock;
this.log = log;
}

// Exchanges the Apple `transfer_sub` for the user's profile information and
Expand Down Expand Up @@ -208,6 +210,18 @@ export class AppleUser {
const transferSub = this.transferSub;
const success = this.success;
const err = (this.err && this.err.message) || '';

this.log.notifyAttachedServices(
'appleUserMigration', {},
{
uid,
appleEmail,
fxaEmail,
transferSub,
success,
err,
},
);
const line = `${transferSub},${uid},${fxaEmail},${appleEmail},${success},${err}`;
this.writeStream.write(line + '\n');
}
Expand All @@ -229,6 +243,16 @@ export class ApplePocketFxAMigration {
this.writeStream.on('error', (err) => {
console.error(`There was an error writing the file: ${err}`);
});

const statsd = {
increment: () => {},
timing: () => {},
close: () => {},
};
this.log = Log({
...config.log,
statsd,
});
}

parseCSV() {
Expand Down Expand Up @@ -258,7 +282,7 @@ export class ApplePocketFxAMigration {
// Splits on `:` since they are not allowed in emails
alternateEmails = tokens[3].replaceAll('"', '').split(':');
}
return new AppleUser(email, transferSub, uid, alternateEmails, this.db, this.writeStream, this.config, this.mock);
return new AppleUser(email, transferSub, uid, alternateEmails, this.db, this.writeStream, this.config, this.mock, this.log);
}).filter((user) => user);
} catch (err) {
console.error('No such file or directory');
Expand Down
17 changes: 15 additions & 2 deletions packages/fxa-auth-server/test/scripts/apple-transfer-users.js
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ describe('ApplePocketFxAMigration', function() {
});

describe('AppleUser', function() {
let sandbox, dbStub, user, writeStreamStub;
let sandbox, dbStub, user, writeStreamStub, log;
beforeEach(function() {
sandbox = sinon.createSandbox();
dbStub = {
Expand All @@ -184,7 +184,10 @@ describe('AppleUser', function() {
writeStreamStub = {
write: sandbox.stub()
};
user = new AppleUser('[email protected]', 'transferSub', 'uid', ['[email protected]'], dbStub, writeStreamStub, config);
log = {
notifyAttachedServices: sandbox.stub().resolves()
}
user = new AppleUser('[email protected]', 'transferSub', 'uid', ['[email protected]'], dbStub, writeStreamStub, config, false, log);
});

afterEach(function() {
Expand Down Expand Up @@ -300,5 +303,15 @@ describe('AppleUser', function() {
user.saveResult();
const expectedOutput = 'transferSub,uid,[email protected],[email protected],true,\n';
assert.calledWith(user.writeStream.write, expectedOutput);

assert.calledOnceWithExactly(user.log.notifyAttachedServices, 'appleUserMigration', {},
{
uid: 'uid',
appleEmail:'[email protected]',
fxaEmail:'[email protected]',
transferSub: 'transferSub',
success: true,
err: '',
})
});
});
17 changes: 17 additions & 0 deletions packages/fxa-event-broker/src/jwtset/jwtset.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,4 +116,21 @@ export class JwtsetService {
uid: delEvent.uid,
});
}

public generateAppleMigrationSET(appleMigrationEvent: set.appleMigrationEvent): Promise<string> {
return this.generateSET({
uid: appleMigrationEvent.uid,
clientId: appleMigrationEvent.clientId,
events: {
[set.APPLE_USER_MIGRATION_ID]: {
fxaEmail: appleMigrationEvent.fxaEmail,
appleEmail: appleMigrationEvent.appleEmail,
transferSub: appleMigrationEvent.transferSub,
success: appleMigrationEvent.success,
err: appleMigrationEvent.err,
uid: appleMigrationEvent.uid,
}
}
});
}
}
14 changes: 14 additions & 0 deletions packages/fxa-event-broker/src/jwtset/set.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ export const PROFILE_EVENT_ID =
export const SUBSCRIPTION_STATE_EVENT_ID =
'https://schemas.accounts.firefox.com/event/subscription-state-change';

export const APPLE_USER_MIGRATION_ID =
'https://schemas.accounts.firefox.com/event/apple-user-migration';


export type deleteEvent = {
clientId: string;
uid: string;
Expand Down Expand Up @@ -45,3 +49,13 @@ export type subscriptionEvent = {
isActive: boolean;
changeTime: number;
};

export type appleMigrationEvent = {
uid: string;
clientId: string;
fxaEmail: string;
appleEmail: string;
transferSub: string;
success: boolean;
err: string;
};
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,17 @@ export class PubsubProxyController {
email: message.email,
});
}
case dto.APPLE_USER_MIGRATION_EVENT: {
return await this.jwtset.generateAppleMigrationSET({
clientId,
uid: message.uid,
fxaEmail: message.fxaEmail,
appleEmail: message.appleEmail,
transferSub: message.transferSub,
success: message.success,
err: message.error
});
}
default:
throw Error(`Invalid event: ${message.event}`);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,18 @@ const baseProfileMessage = {
event: 'profileDataChange',
};

const appleMigrationMessage = {
event: 'appleUserMigration',
timestamp: now,
ts: now / 1000,
uid: '993d26bac72b471991b197b3d298a5de',
fxaEmail: '[email protected]',
appleEmail: '[email protected]',
transferSub: '123',
success: true,
err: '',
};

const updateStubMessage = (message: any) => {
return {
Body: JSON.stringify(message),
Expand Down Expand Up @@ -149,6 +161,7 @@ describe('QueueworkerService', () => {
serviceNotificationQueueUrl:
'https://sqs.us-east-2.amazonaws.com/queue.mozilla/321321321/notifications',
log: { app: 'test' },
topicPrefix: 'rp',
};
const MockConfig: Provider = {
provide: ConfigService,
Expand Down Expand Up @@ -250,6 +263,28 @@ describe('QueueworkerService', () => {
'resource-server-client-id',
]);
});

it('handles apple migration event', async () => {
const msg = updateStubMessage(appleMigrationMessage);
await (service as any).handleMessage(msg);

const topicName = 'rp749818d3f2e7857f';
expect(pubsub.topic).toBeCalledWith(topicName);
expect(pubsub.topic(topicName).publishMessage).toBeCalledTimes(1);
expect(pubsub.topic(topicName).publishMessage).toBeCalledWith({
json: {
'appleEmail': '[email protected]',
'err': '',
'event': 'appleUserMigration',
'fxaEmail': '[email protected]',
'success': true,
'timestamp': now,
'transferSub': '123',
'ts': now / 1000,
'uid': '993d26bac72b471991b197b3d298a5de',
},
});
});

const fetchOnValidMessage = {
'delete message': baseDeleteMessage,
Expand Down
22 changes: 21 additions & 1 deletion packages/fxa-event-broker/src/queueworker/queueworker.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ export class QueueworkerService
* @param eventType Event type to use for metrics
*/
private async handleMessageFanout(
message: dto.deleteSchema | dto.profileSchema | dto.passwordSchema,
message: dto.deleteSchema | dto.profileSchema | dto.passwordSchema | dto.appleUserMigrationSchema,
eventType: string
) {
this.metrics.increment('message.type', { eventType });
Expand Down Expand Up @@ -261,6 +261,22 @@ export class QueueworkerService
await Promise.all(notifyClientPromises);
}

/**
* Notify Pocket that a user's Apple account has been migrated.
*
* @param message Incoming SQS Message
*/
private async handleAppleUserMigrationEvent(message: dto.appleUserMigrationSchema) {
// Note the hardcoded Pocket clientId, this value should not change in production
const clientId = '749818d3f2e7857f';
this.metrics.increment('message.type', { eventType: 'appleMigration' });
const rpMessage = {
timestamp: Date.now(),
...message,
};
await this.publishMessage(clientId, rpMessage);
}

/**
* Process a SQS message, dispatch based on message event type.
*
Expand Down Expand Up @@ -306,6 +322,10 @@ export class QueueworkerService
await this.handleMessageFanout(message, 'password');
break;
}
case dto.APPLE_USER_MIGRATION_EVENT: {
await this.handleAppleUserMigrationEvent(message);
break;
}
default:
unhandledEventType(message);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export type ServiceNotification =
| dto.passwordSchema
| dto.profileSchema
| dto.subscriptionUpdateSchema
| dto.appleUserMigrationSchema
| undefined;

interface SchemaTable {
Expand All @@ -28,6 +29,7 @@ const eventSchemas = {
[dto.PRIMARY_EMAIL_EVENT]: dto.PROFILE_CHANGE_SCHEMA,
[dto.PASSWORD_CHANGE_EVENT]: dto.PASSWORD_CHANGE_SCHEMA,
[dto.PASSWORD_RESET_EVENT]: dto.PASSWORD_CHANGE_SCHEMA,
[dto.APPLE_USER_MIGRATION_EVENT]: dto.APPLE_USER_MIGRATION_SCHEMA,
};

/**
Expand Down
25 changes: 25 additions & 0 deletions packages/fxa-event-broker/src/queueworker/sqs.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export const PASSWORD_RESET_EVENT = 'reset';
export const PRIMARY_EMAIL_EVENT = 'primaryEmailChanged';
export const PROFILE_CHANGE_EVENT = 'profileDataChange';
export const SUBSCRIPTION_UPDATE_EVENT = 'subscription:update';
export const APPLE_USER_MIGRATION_EVENT = 'appleUserMigration';

// Message schemas
export const CLIENT_ID = joi.string().regex(/[a-z0-9]{16}/);
Expand Down Expand Up @@ -91,6 +92,18 @@ export const PROFILE_CHANGE_SCHEMA = joi
.unknown(true)
.required();

export const APPLE_USER_MIGRATION_SCHEMA = joi
.object()
.keys({
event: joi.string().valid(APPLE_USER_MIGRATION_EVENT),
timestamp: joi.number().optional(),
ts: joi.number().required(),
uid: joi.string().required(),
})
.unknown(true)
.required();


export type deleteSchema = {
event: typeof DELETE_EVENT;
timestamp?: number;
Expand Down Expand Up @@ -136,3 +149,15 @@ export type subscriptionUpdateSchema = {
ts: number;
uid: string;
};

export type appleUserMigrationSchema = {
event: typeof APPLE_USER_MIGRATION_EVENT;
timestamp?: number;
ts: number;
uid: string;
fxaEmail: string;
appleEmail: string;
transferSub: string;
success: boolean;
err: string;
}

0 comments on commit adf89d7

Please sign in to comment.