From 6982f00729de13907e9ca12dbce64ac8dbf94877 Mon Sep 17 00:00:00 2001 From: Matchu Date: Wed, 2 Sep 2020 03:49:58 -0700 Subject: [PATCH] script to export users to auth0 --- package.json | 4 +- scripts/build-user-export-for-auth0.js | 78 ++++++++++++++++++++++++++ src/server/db.js | 9 ++- yarn.lock | 41 +++++++++++++- 4 files changed, 127 insertions(+), 5 deletions(-) create mode 100644 scripts/build-user-export-for-auth0.js diff --git a/package.json b/package.json index 236564c..13dd271 100644 --- a/package.json +++ b/package.json @@ -42,7 +42,8 @@ "mysql": "mysql --host=impress.openneo.net --user=$(dotenv -p IMPRESS_MYSQL_USER) --password=$(dotenv -p IMPRESS_MYSQL_PASSWORD) --database=openneo_impress", "mysql-admin": "mysql --host=impress.openneo.net --user=matchu --password --database=openneo_impress", "build-cached-data": "node -r dotenv/config scripts/build-cached-data.js", - "cache-asset-manifests": "node -r dotenv/config scripts/cache-asset-manifests.js" + "cache-asset-manifests": "node -r dotenv/config scripts/cache-asset-manifests.js", + "build-user-export-for-auth0": "node -r dotenv/config scripts/build-user-export-for-auth0.js" }, "eslintConfig": { "extends": "react-app" @@ -65,6 +66,7 @@ "customize-cra-react-refresh": "^1.1.0", "dotenv-cli": "^3.1.0", "es6-promise-pool": "^2.5.0", + "inquirer": "^7.3.3", "prettier": "^2.0.5", "react-app-rewired": "^2.1.6" } diff --git a/scripts/build-user-export-for-auth0.js b/scripts/build-user-export-for-auth0.js new file mode 100644 index 0000000..f91d253 --- /dev/null +++ b/scripts/build-user-export-for-auth0.js @@ -0,0 +1,78 @@ +// This generates a JSON file to export our users into Auth0. +// +// This sorta creates a second copy of everyone's account, copied onto Auth0. +// We should be thoughtful about how we do the actual migration process! +// +// For now, we can run this whenever we want to make it _possible_ to log in +// with Auth0, even if things will be potentially out of sync, because traffic +// to Impress 2020 is just testers now anyway! +const fs = require("fs").promises; +const path = require("path"); +const os = require("os"); + +const inquirer = require("inquirer"); + +const connectToDb = require("../src/server/db"); +const { normalizeRow } = require("../src/server/util"); + +async function main() { + const { user, password, outputPath } = await inquirer.prompt([ + { name: "user", message: "MySQL admin user:" }, + { name: "password", type: "password" }, + { + name: "outputPath", + message: "Output path:", + default: path.join( + os.homedir(), + "Downloads/openneo-users-for-auth0.json" + ), + }, + ]); + const db = await connectToDb({ user, password }); + + let users; + try { + const [rows] = await db.query( + `SELECT id, name, email, encrypted_password, password_salt + FROM openneo_id.users ORDER BY id` + ); + users = rows.map(normalizeRow); + } finally { + db.close(); + } + + const usersInAuth0Format = users.map((user) => ({ + user_id: user.id, + username: user.name, + email: user.email, + custom_password_hash: { + algorithm: "hmac", + hash: { + value: user.encryptedPassword, + encoding: "hex", + digest: "sha256", + key: { + encoding: "utf8", + value: user.passwordSalt, + }, + }, + }, + })); + + for (let i = 0; i < users.length; i += 1000) { + const batchInAuth0Format = usersInAuth0Format.slice(i, i + 1000); + const batchOutputPath = outputPath.replace(/\.json$/, `-${i}.json`); + const jsonOutput = JSON.stringify(batchInAuth0Format); + await fs.writeFile(batchOutputPath, jsonOutput); + console.log( + `📚 Wrote ${batchInAuth0Format.length} users to ${batchOutputPath}` + ); + } +} + +main() + .catch((e) => { + console.error(e); + process.exit(1); + }) + .then(() => process.exit()); diff --git a/src/server/db.js b/src/server/db.js index 010f34d..7571a54 100644 --- a/src/server/db.js +++ b/src/server/db.js @@ -2,7 +2,10 @@ const mysql = require("mysql2"); let globalDb; -async function connectToDb() { +async function connectToDb({ + user = process.env["IMPRESS_MYSQL_USER"], + password = process.env["IMPRESS_MYSQL_PASSWORD"], +} = {}) { if (globalDb) { return globalDb; } @@ -10,8 +13,8 @@ async function connectToDb() { globalDb = mysql .createConnection({ host: "impress.openneo.net", - user: process.env["IMPRESS_MYSQL_USER"], - password: process.env["IMPRESS_MYSQL_PASSWORD"], + user, + password, database: "openneo_impress", }) // We upgrade to promises here, instead of using the mysql2/promise import, diff --git a/yarn.lock b/yarn.lock index ddc2d74..d30ad27 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4360,6 +4360,14 @@ chalk@^3.0.0: ansi-styles "^4.1.0" supports-color "^7.1.0" +chalk@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.0.tgz#4e14870a618d9e2edd97dd8345fd9d9dc315646a" + integrity sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + chardet@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" @@ -4463,6 +4471,11 @@ cli-width@^2.0.0: resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.0.tgz#ff19ede8a9a5e579324147b0c11f0fbcbabed639" integrity sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk= +cli-width@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6" + integrity sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw== + cliui@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/cliui/-/cliui-4.1.0.tgz#348422dbe82d800b3022eef4f6ac10bf2e4d1b49" @@ -7332,6 +7345,25 @@ inquirer@^7.0.0: strip-ansi "^6.0.0" through "^2.3.6" +inquirer@^7.3.3: + version "7.3.3" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.3.3.tgz#04d176b2af04afc157a83fd7c100e98ee0aad003" + integrity sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA== + dependencies: + ansi-escapes "^4.2.1" + chalk "^4.1.0" + cli-cursor "^3.1.0" + cli-width "^3.0.0" + external-editor "^3.0.3" + figures "^3.0.0" + lodash "^4.17.19" + mute-stream "0.0.8" + run-async "^2.4.0" + rxjs "^6.6.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + through "^2.3.6" + internal-ip@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/internal-ip/-/internal-ip-4.3.0.tgz#845452baad9d2ca3b69c635a137acb9a0dad0907" @@ -8608,7 +8640,7 @@ lodash.uniq@^4.5.0: resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== -lodash@>=4.17.19: +lodash@>=4.17.19, lodash@^4.17.19: version "4.17.20" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52" integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA== @@ -11580,6 +11612,13 @@ rxjs@^6.5.3: dependencies: tslib "^1.9.0" +rxjs@^6.6.0: + version "6.6.2" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.2.tgz#8096a7ac03f2cc4fe5860ef6e572810d9e01c0d2" + integrity sha512-BHdBMVoWC2sL26w//BCu3YzKT4s2jip/WhwsGEDmeKYBhKDZeYezVUnHatYB7L85v5xs0BAQmg6BEYJEKxBabg== + dependencies: + tslib "^1.9.0" + safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"