123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305 |
- "use strict";
- Object.defineProperty(exports, "__esModule", { value: true });
- exports.serialImportUsers = exports.validateUserJson = exports.validateOptions = exports.transArrayToUser = void 0;
- const clc = require("colorette");
- const apiv2_1 = require("./apiv2");
- const api_1 = require("./api");
- const logger_1 = require("./logger");
- const error_1 = require("./error");
- const utils = require("./utils");
- const apiClient = new apiv2_1.Client({
- urlPrefix: api_1.googleOrigin,
- });
- const ALLOWED_JSON_KEYS = [
- "localId",
- "email",
- "emailVerified",
- "passwordHash",
- "salt",
- "displayName",
- "photoUrl",
- "createdAt",
- "lastSignedInAt",
- "providerUserInfo",
- "phoneNumber",
- "disabled",
- "customAttributes",
- ];
- const ALLOWED_JSON_KEYS_RENAMING = {
- lastSignedInAt: "lastLoginAt",
- };
- const ALLOWED_PROVIDER_USER_INFO_KEYS = ["providerId", "rawId", "email", "displayName", "photoUrl"];
- const ALLOWED_PROVIDER_IDS = ["google.com", "facebook.com", "twitter.com", "github.com"];
- function isValidBase64(str) {
- const expected = Buffer.from(str, "base64").toString("base64");
- if (str.length < expected.length && !str.endsWith("=")) {
- str += "=".repeat(expected.length - str.length);
- }
- return expected === str;
- }
- function toWebSafeBase64(data) {
- return data.replace(/\//g, "_").replace(/\+/g, "-");
- }
- function addProviderUserInfo(user, providerId, arr) {
- if (arr[0]) {
- user.providerUserInfo.push({
- providerId: providerId,
- rawId: arr[0],
- email: arr[1],
- displayName: arr[2],
- photoUrl: arr[3],
- });
- }
- }
- function genUploadAccountPostBody(projectId, accounts, hashOptions) {
- const postBody = {
- users: accounts.map((account) => {
- if (account.passwordHash) {
- account.passwordHash = toWebSafeBase64(account.passwordHash);
- }
- if (account.salt) {
- account.salt = toWebSafeBase64(account.salt);
- }
- for (const [key, value] of Object.entries(ALLOWED_JSON_KEYS_RENAMING)) {
- if (account[key]) {
- account[value] = account[key];
- delete account[key];
- }
- }
- return account;
- }),
- };
- if (hashOptions.hashAlgo) {
- postBody.hashAlgorithm = hashOptions.hashAlgo;
- }
- if (hashOptions.hashKey) {
- postBody.signerKey = toWebSafeBase64(hashOptions.hashKey);
- }
- if (hashOptions.saltSeparator) {
- postBody.saltSeparator = toWebSafeBase64(hashOptions.saltSeparator);
- }
- if (hashOptions.rounds) {
- postBody.rounds = hashOptions.rounds;
- }
- if (hashOptions.memCost) {
- postBody.memoryCost = hashOptions.memCost;
- }
- if (hashOptions.cpuMemCost) {
- postBody.cpuMemCost = hashOptions.cpuMemCost;
- }
- if (hashOptions.parallelization) {
- postBody.parallelization = hashOptions.parallelization;
- }
- if (hashOptions.blockSize) {
- postBody.blockSize = hashOptions.blockSize;
- }
- if (hashOptions.dkLen) {
- postBody.dkLen = hashOptions.dkLen;
- }
- if (hashOptions.passwordHashOrder) {
- postBody.passwordHashOrder = hashOptions.passwordHashOrder;
- }
- postBody.targetProjectId = projectId;
- return postBody;
- }
- function transArrayToUser(arr) {
- const user = {
- localId: arr[0],
- email: arr[1],
- emailVerified: arr[2] === "true",
- passwordHash: arr[3],
- salt: arr[4],
- displayName: arr[5],
- photoUrl: arr[6],
- createdAt: arr[23],
- lastLoginAt: arr[24],
- phoneNumber: arr[25],
- providerUserInfo: [],
- disabled: arr[26],
- customAttributes: arr[27],
- };
- addProviderUserInfo(user, "google.com", arr.slice(7, 11));
- addProviderUserInfo(user, "facebook.com", arr.slice(11, 15));
- addProviderUserInfo(user, "twitter.com", arr.slice(15, 19));
- addProviderUserInfo(user, "github.com", arr.slice(19, 23));
- if (user.passwordHash && !isValidBase64(user.passwordHash)) {
- return {
- error: "Password hash should be base64 encoded.",
- };
- }
- if (user.salt && !isValidBase64(user.salt)) {
- return {
- error: "Password salt should be base64 encoded.",
- };
- }
- return user;
- }
- exports.transArrayToUser = transArrayToUser;
- function validateOptions(options) {
- const hashOptions = validateRequiredParameters(options);
- if (!hashOptions.valid) {
- return hashOptions;
- }
- const hashInputOrder = options.hashInputOrder ? options.hashInputOrder.toUpperCase() : undefined;
- if (hashInputOrder) {
- if (hashInputOrder !== "SALT_FIRST" && hashInputOrder !== "PASSWORD_FIRST") {
- throw new error_1.FirebaseError("Unknown password hash order flag");
- }
- else {
- hashOptions["passwordHashOrder"] =
- hashInputOrder === "SALT_FIRST" ? "SALT_AND_PASSWORD" : "PASSWORD_AND_SALT";
- }
- }
- return hashOptions;
- }
- exports.validateOptions = validateOptions;
- function validateRequiredParameters(options) {
- if (!options.hashAlgo) {
- utils.logWarning("No hash algorithm specified. Password users cannot be imported.");
- return { valid: true };
- }
- const hashAlgo = options.hashAlgo.toUpperCase();
- let roundsNum;
- switch (hashAlgo) {
- case "HMAC_SHA512":
- case "HMAC_SHA256":
- case "HMAC_SHA1":
- case "HMAC_MD5":
- if (!options.hashKey || options.hashKey === "") {
- throw new error_1.FirebaseError("Must provide hash key(base64 encoded) for hash algorithm " + options.hashAlgo);
- }
- return { hashAlgo: hashAlgo, hashKey: options.hashKey, valid: true };
- case "MD5":
- case "SHA1":
- case "SHA256":
- case "SHA512":
- roundsNum = parseInt(options.rounds, 10);
- const minRounds = hashAlgo === "MD5" ? 0 : 1;
- if (isNaN(roundsNum) || roundsNum < minRounds || roundsNum > 8192) {
- throw new error_1.FirebaseError(`Must provide valid rounds(${minRounds}..8192) for hash algorithm ${options.hashAlgo}`);
- }
- return { hashAlgo: hashAlgo, rounds: options.rounds, valid: true };
- case "PBKDF_SHA1":
- case "PBKDF2_SHA256":
- roundsNum = parseInt(options.rounds, 10);
- if (isNaN(roundsNum) || roundsNum < 0 || roundsNum > 120000) {
- throw new error_1.FirebaseError("Must provide valid rounds(0..120000) for hash algorithm " + options.hashAlgo);
- }
- return { hashAlgo: hashAlgo, rounds: options.rounds, valid: true };
- case "SCRYPT":
- if (!options.hashKey || options.hashKey === "") {
- throw new error_1.FirebaseError("Must provide hash key(base64 encoded) for hash algorithm " + options.hashAlgo);
- }
- roundsNum = parseInt(options.rounds, 10);
- if (isNaN(roundsNum) || roundsNum <= 0 || roundsNum > 8) {
- throw new error_1.FirebaseError("Must provide valid rounds(1..8) for hash algorithm " + options.hashAlgo);
- }
- const memCost = parseInt(options.memCost, 10);
- if (isNaN(memCost) || memCost <= 0 || memCost > 14) {
- throw new error_1.FirebaseError("Must provide valid memory cost(1..14) for hash algorithm " + options.hashAlgo);
- }
- let saltSeparator = "";
- if (options.saltSeparator) {
- saltSeparator = options.saltSeparator;
- }
- return {
- hashAlgo: hashAlgo,
- hashKey: options.hashKey,
- saltSeparator: saltSeparator,
- rounds: options.rounds,
- memCost: options.memCost,
- valid: true,
- };
- case "BCRYPT":
- return { hashAlgo: hashAlgo, valid: true };
- case "STANDARD_SCRYPT":
- const cpuMemCost = parseInt(options.memCost, 10);
- const parallelization = parseInt(options.parallelization, 10);
- const blockSize = parseInt(options.blockSize, 10);
- const dkLen = parseInt(options.dkLen, 10);
- return {
- hashAlgo: hashAlgo,
- valid: true,
- cpuMemCost: cpuMemCost,
- parallelization: parallelization,
- blockSize: blockSize,
- dkLen: dkLen,
- };
- default:
- throw new error_1.FirebaseError("Unsupported hash algorithm " + clc.bold(options.hashAlgo));
- }
- }
- function validateProviderUserInfo(providerUserInfo) {
- if (!ALLOWED_PROVIDER_IDS.includes(providerUserInfo.providerId)) {
- return {
- error: JSON.stringify(providerUserInfo, null, 2) + " has unsupported providerId",
- };
- }
- const keydiff = Object.keys(providerUserInfo).filter((k) => !ALLOWED_PROVIDER_USER_INFO_KEYS.includes(k));
- if (keydiff.length) {
- return {
- error: JSON.stringify(providerUserInfo, null, 2) + " has unsupported keys: " + keydiff.join(","),
- };
- }
- return {};
- }
- function validateUserJson(userJson) {
- const keydiff = Object.keys(userJson).filter((k) => !ALLOWED_JSON_KEYS.includes(k));
- if (keydiff.length) {
- return {
- error: JSON.stringify(userJson, null, 2) + " has unsupported keys: " + keydiff.join(","),
- };
- }
- if (userJson.providerUserInfo) {
- for (let i = 0; i < userJson.providerUserInfo.length; i++) {
- const res = validateProviderUserInfo(userJson.providerUserInfo[i]);
- if (res.error) {
- return res;
- }
- }
- }
- const badFormat = JSON.stringify(userJson, null, 2) + " has invalid data format: ";
- if (userJson.passwordHash && !isValidBase64(userJson.passwordHash)) {
- return {
- error: badFormat + "Password hash should be base64 encoded.",
- };
- }
- if (userJson.salt && !isValidBase64(userJson.salt)) {
- return {
- error: badFormat + "Password salt should be base64 encoded.",
- };
- }
- return {};
- }
- exports.validateUserJson = validateUserJson;
- async function sendRequest(projectId, userList, hashOptions) {
- logger_1.logger.info("Starting importing " + userList.length + " account(s).");
- const postData = genUploadAccountPostBody(projectId, userList, hashOptions);
- return apiClient
- .post("/identitytoolkit/v3/relyingparty/uploadAccount", postData, {
- skipLog: { body: true },
- })
- .then((ret) => {
- if (ret.body.error) {
- logger_1.logger.info("Encountered problems while importing accounts. Details:");
- logger_1.logger.info(ret.body.error.map((rawInfo) => {
- return {
- account: JSON.stringify(userList[parseInt(rawInfo.index, 10)], null, 2),
- reason: rawInfo.message,
- };
- }));
- }
- else {
- utils.logSuccess("Imported successfully.");
- }
- logger_1.logger.info();
- });
- }
- function serialImportUsers(projectId, hashOptions, userListArr, index) {
- return sendRequest(projectId, userListArr[index], hashOptions).then(() => {
- if (index < userListArr.length - 1) {
- return serialImportUsers(projectId, hashOptions, userListArr, index + 1);
- }
- });
- }
- exports.serialImportUsers = serialImportUsers;
|