Nenhuma descrição
Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

accountExporter.js 6.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. exports.serialExportUsers = exports.validateOptions = void 0;
  4. const os = require("os");
  5. const path = require("path");
  6. const apiv2_1 = require("./apiv2");
  7. const error_1 = require("./error");
  8. const api_1 = require("./api");
  9. const utils = require("./utils");
  10. const apiClient = new apiv2_1.Client({
  11. urlPrefix: api_1.googleOrigin,
  12. });
  13. const EXPORTED_JSON_KEYS = [
  14. "localId",
  15. "email",
  16. "emailVerified",
  17. "passwordHash",
  18. "salt",
  19. "displayName",
  20. "photoUrl",
  21. "lastLoginAt",
  22. "createdAt",
  23. "phoneNumber",
  24. "disabled",
  25. "customAttributes",
  26. ];
  27. const EXPORTED_JSON_KEYS_RENAMING = {
  28. lastLoginAt: "lastSignedInAt",
  29. };
  30. const EXPORTED_PROVIDER_USER_INFO_KEYS = [
  31. "providerId",
  32. "rawId",
  33. "email",
  34. "displayName",
  35. "photoUrl",
  36. ];
  37. const PROVIDER_ID_INDEX_MAP = new Map([
  38. ["google.com", 7],
  39. ["facebook.com", 11],
  40. ["twitter.com", 15],
  41. ["github.com", 19],
  42. ]);
  43. function escapeComma(str) {
  44. if (str.includes(",")) {
  45. return `"${str}"`;
  46. }
  47. return str;
  48. }
  49. function convertToNormalBase64(data) {
  50. return data.replace(/_/g, "/").replace(/-/g, "+");
  51. }
  52. function addProviderUserInfo(providerInfo, arr, startPos) {
  53. arr[startPos] = providerInfo.rawId;
  54. arr[startPos + 1] = providerInfo.email || "";
  55. arr[startPos + 2] = escapeComma(providerInfo.displayName || "");
  56. arr[startPos + 3] = providerInfo.photoUrl || "";
  57. }
  58. function transUserToArray(user) {
  59. const arr = Array(27).fill("");
  60. arr[0] = user.localId;
  61. arr[1] = user.email || "";
  62. arr[2] = user.emailVerified || false;
  63. arr[3] = convertToNormalBase64(user.passwordHash || "");
  64. arr[4] = convertToNormalBase64(user.salt || "");
  65. arr[5] = escapeComma(user.displayName || "");
  66. arr[6] = user.photoUrl || "";
  67. for (let i = 0; i < (!user.providerUserInfo ? 0 : user.providerUserInfo.length); i++) {
  68. const providerInfo = user.providerUserInfo[i];
  69. if (providerInfo) {
  70. const providerIndex = PROVIDER_ID_INDEX_MAP.get(providerInfo.providerId);
  71. if (providerIndex) {
  72. addProviderUserInfo(providerInfo, arr, providerIndex);
  73. }
  74. }
  75. }
  76. arr[23] = user.createdAt;
  77. arr[24] = user.lastLoginAt;
  78. arr[25] = user.phoneNumber;
  79. arr[26] = user.disabled;
  80. arr[27] = user.customAttributes;
  81. return arr;
  82. }
  83. function transUserJson(user) {
  84. const newUser = {};
  85. const pickedUser = {};
  86. for (const k of EXPORTED_JSON_KEYS) {
  87. pickedUser[k] = user[k];
  88. }
  89. for (const [key, value] of Object.entries(pickedUser)) {
  90. const newKey = EXPORTED_JSON_KEYS_RENAMING[key] || key;
  91. newUser[newKey] = value;
  92. }
  93. if (newUser.passwordHash) {
  94. newUser.passwordHash = convertToNormalBase64(newUser.passwordHash);
  95. }
  96. if (newUser.salt) {
  97. newUser.salt = convertToNormalBase64(newUser.salt);
  98. }
  99. if (user.providerUserInfo) {
  100. newUser.providerUserInfo = [];
  101. for (const providerInfo of user.providerUserInfo) {
  102. if (PROVIDER_ID_INDEX_MAP.has(providerInfo.providerId)) {
  103. const picked = {};
  104. for (const k of EXPORTED_PROVIDER_USER_INFO_KEYS) {
  105. picked[k] = providerInfo[k];
  106. }
  107. newUser.providerUserInfo.push(picked);
  108. }
  109. }
  110. }
  111. return newUser;
  112. }
  113. function validateOptions(options, fileName) {
  114. const exportOptions = {};
  115. if (fileName === undefined) {
  116. throw new error_1.FirebaseError("Must specify data file");
  117. }
  118. const extName = path.extname(fileName.toLowerCase());
  119. if (extName === ".csv") {
  120. exportOptions.format = "csv";
  121. }
  122. else if (extName === ".json") {
  123. exportOptions.format = "json";
  124. }
  125. else if (options.format) {
  126. const format = options.format.toLowerCase();
  127. if (format === "csv" || format === "json") {
  128. exportOptions.format = format;
  129. }
  130. else {
  131. throw new error_1.FirebaseError("Unsupported data file format, should be csv or json");
  132. }
  133. }
  134. else {
  135. throw new error_1.FirebaseError("Please specify data file format in file name, or use `format` parameter");
  136. }
  137. return exportOptions;
  138. }
  139. exports.validateOptions = validateOptions;
  140. function createWriteUsersToFile() {
  141. let jsonSep = "";
  142. return (userList, format, writeStream) => {
  143. userList.map((user) => {
  144. if (user.passwordHash && user.version !== 0) {
  145. delete user.passwordHash;
  146. delete user.salt;
  147. }
  148. if (format === "csv") {
  149. writeStream.write(transUserToArray(user).join(",") + "," + os.EOL, "utf8");
  150. }
  151. else {
  152. writeStream.write(jsonSep + JSON.stringify(transUserJson(user), null, 2), "utf8");
  153. jsonSep = "," + os.EOL;
  154. }
  155. });
  156. };
  157. }
  158. async function serialExportUsers(projectId, options) {
  159. var _a;
  160. if (!options.writeUsersToFile) {
  161. options.writeUsersToFile = createWriteUsersToFile();
  162. }
  163. const postBody = {
  164. targetProjectId: projectId,
  165. maxResults: options.batchSize,
  166. };
  167. if (options.nextPageToken) {
  168. postBody.nextPageToken = options.nextPageToken;
  169. }
  170. if (!options.timeoutRetryCount) {
  171. options.timeoutRetryCount = 0;
  172. }
  173. try {
  174. const ret = await apiClient.post("/identitytoolkit/v3/relyingparty/downloadAccount", postBody, {
  175. skipLog: { resBody: true },
  176. });
  177. options.timeoutRetryCount = 0;
  178. const userList = ret.body.users;
  179. if (userList && userList.length > 0) {
  180. options.writeUsersToFile(userList, options.format, options.writeStream);
  181. utils.logSuccess("Exported " + userList.length + " account(s) successfully.");
  182. if (!ret.body.nextPageToken) {
  183. return;
  184. }
  185. options.nextPageToken = ret.body.nextPageToken;
  186. return serialExportUsers(projectId, options);
  187. }
  188. }
  189. catch (err) {
  190. if (((_a = err.original) === null || _a === void 0 ? void 0 : _a.code) === "ETIMEDOUT") {
  191. options.timeoutRetryCount++;
  192. if (options.timeoutRetryCount > 5) {
  193. return err;
  194. }
  195. return serialExportUsers(projectId, options);
  196. }
  197. if (err instanceof error_1.FirebaseError) {
  198. throw err;
  199. }
  200. else {
  201. throw new error_1.FirebaseError(`Failed to export accounts: ${err}`, { original: err });
  202. }
  203. }
  204. }
  205. exports.serialExportUsers = serialExportUsers;