GSI - Employe Self Service Mobile
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

305 lines
11 KiB

"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;