Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(apple): Add apple user migrations script #15406

Merged
merged 1 commit into from
Jun 7, 2023
Merged

feat(apple): Add apple user migrations script #15406

merged 1 commit into from
Jun 7, 2023

Conversation

vbudhram
Copy link
Contributor

@vbudhram vbudhram commented Jun 2, 2023

Because

  • Ops team needs the ability to run migration script and import users into FxA from Pocket

This pull request

  • Adds script

Issue that this pull request solves

Closes: https://mozilla-hub.atlassian.net/browse/FXA-7490

Checklist

  • My commit is GPG signed.
  • If applicable, I have modified or added tests which pass locally.
  • I have added necessary documentation (if appropriate).
  • I have verified that my changes render correctly in RTL (if appropriate).

Other information (Optional)

I need to find a better way to test this script. Since it is making network calls from another Node process, I can't use nock. I'm putting this up for initial review, it is still missing a few things

  • Better test coverage
  • Write csv file with status of imigration
  • Emit a new notification when user is migrated

@vbudhram vbudhram marked this pull request as ready for review June 5, 2023 21:10
@vbudhram vbudhram requested a review from a team as a code owner June 5, 2023 21:10
@vbudhram vbudhram requested a review from dschom June 5, 2023 21:10
this.appleUserInfo = res.data;
return res.data;
} catch (err) {
this.setFailure(err);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I took a look at the retry later ability and kinda went down a rabbit hole. Instead, we mark the user as failed and can reattempt later.


async createLinkedAccount(accountRecord, sub) {
// If the user already has a linked account, delete it and create a new one.
await this.db.deleteLinkedAccount(accountRecord.uid, APPLE_PROVIDER);
Copy link
Contributor Author

@vbudhram vbudhram Jun 5, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There shouldn't be any harm in deleting a linked third party auth account. The feature is not broadcasted so no real users should have this set.


it('should exchange identifiers correctly and update user', async function() {
const stub = sandbox.stub(axios, 'post').resolves({ data: { sub: 'sub', email: 'email@example.com', is_private_email: true } });
const data = await user.exchangeIdentifiers('accessToken');

Check failure

Code scanning / CodeQL

Hard-coded credentials

The hard-coded value "accessToken" is used as [authorization header](1).
it('should transfer user correctly', async function() {
sandbox.stub(user, 'exchangeIdentifiers').resolves();
sandbox.stub(user, 'createUpdateFxAUser').resolves();
await user.transferUser('accessToken');

Check failure

Code scanning / CodeQL

Hard-coded credentials

The hard-coded value "accessToken" is used as [authorization header](1).
Comment on lines 19 to 20
.option('-d, --delimiter [delimiter]', 'Delimiter for input file', ',')
.option('-o, --output <filename>', 'Output filename to save results to')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Q: Does the program.delimiter and program.output get used?

return input.split(/\n/).map((s, index) => {
if (index === 0) return;

const delimiter = program.delimiter || ',';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was the only other instance of program i found in this file:

Should this be using the unused(?) delimiter from packages/fxa-auth-server/scripts/apple-transfer-users.js#L7 above?

const err = (user.err && user.err.message) || '';
return `${transferSub},${uid},${fxaEmail},${appleEmail},${success},${err}`;
}).join('\n');
fs.writeFileSync(path.resolve(`${Date.now()}_output.csv`), output);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Q: Should this accept an output filename?
packages/fxa-auth-server/scripts/apple-transfer-users.js#L20 specifies an output filename in the commander config, but I don't think it's used currently.

const transferSub = user.transferSub;
const success = user.success;
const err = (user.err && user.err.message) || '';
return `${transferSub},${uid},${fxaEmail},${appleEmail},${success},${err}`;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Q: Would there be any potential issues if the ${err} string had a comma (thus throwing off column counts, etc)?

Comment on lines 253 to 255
const tokenRes = await axios.post(config.appleAuthConfig.tokenEndpoint,
new URLSearchParams(tokenOptions).toString(),
);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Q: does this need a try..catch in the unlikely event of a server/fetch error?

const migration = new ApplePocketFxAMigration(program.input);

await migration.load();
await migration.printUsers();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think printUsers() is an async method:

printUsers() {
console.table(this.users);
}

await migration.load();
await migration.printUsers();
await migration.transferUsers();
await migration.saveResults();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same w/ non-async saveResults():

saveResults() {
const output = this.users.map((user) => {

Copy link
Contributor

@dschom dschom left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a couple nits. Would be good to address the comments in saveResults.

@@ -0,0 +1,259 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Might be nice to just call this /transfer-users/apple.js. That way if there are other types of user transfers in the future, we can just add more scripts here.

AUTH_FIRESTORE_EMULATOR_HOST: 'localhost:9090',
},
};
const axios = require('axios');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Any reason to axios instead of fetch?

return res.data;
} catch (err) {
this.setFailure(err);
if (err.response && err.response.status === 429) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps we want to add a delay option, so we can throttle?


// Exchanges the Apple `transfer_sub` for the user's profile information and
// moves the user to the new team.
// Ref: https://developer.apple.com/documentation/sign_in_with_apple/bringing_new_apps_and_users_into_your_team#3559300
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍


program
.option('-d, --delimiter [delimiter]', 'Delimiter for input file', ',')
.option('-o, --output <filename>', 'Output filename to save results to')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this have a default value?

const err = (user.err && user.err.message) || '';
return `${transferSub},${uid},${fxaEmail},${appleEmail},${success},${err}`;
}).join('\n');
fs.writeFileSync(path.resolve(`${Date.now()}_output.csv`), output);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be program.output?

}

saveResults() {
const output = this.users.map((user) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be a really big string. Perhaps write the file line by line?

@vbudhram
Copy link
Contributor Author

vbudhram commented Jun 7, 2023

@dschom @pdehaan Thanks for reviews, I cleaned up the code a bit and updated it to stream results to a file.

@vbudhram vbudhram merged commit d714f5b into main Jun 7, 2023
@vbudhram vbudhram deleted the fxa-7490 branch June 7, 2023 16:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants