tenant.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  1. /*! firebase-admin v12.1.1 */
  2. "use strict";
  3. /*!
  4. * Copyright 2019 Google Inc.
  5. *
  6. * Licensed under the Apache License, Version 2.0 (the "License");
  7. * you may not use this file except in compliance with the License.
  8. * You may obtain a copy of the License at
  9. *
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. *
  12. * Unless required by applicable law or agreed to in writing, software
  13. * distributed under the License is distributed on an "AS IS" BASIS,
  14. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15. * See the License for the specific language governing permissions and
  16. * limitations under the License.
  17. */
  18. Object.defineProperty(exports, "__esModule", { value: true });
  19. exports.Tenant = void 0;
  20. const validator = require("../utils/validator");
  21. const deep_copy_1 = require("../utils/deep-copy");
  22. const error_1 = require("../utils/error");
  23. const auth_config_1 = require("./auth-config");
  24. /**
  25. * Represents a tenant configuration.
  26. *
  27. * Multi-tenancy support requires Google Cloud's Identity Platform
  28. * (GCIP). To learn more about GCIP, including pricing and features,
  29. * see the {@link https://cloud.google.com/identity-platform | GCIP documentation}.
  30. *
  31. * Before multi-tenancy can be used on a Google Cloud Identity Platform project,
  32. * tenants must be allowed on that project via the Cloud Console UI.
  33. *
  34. * A tenant configuration provides information such as the display name, tenant
  35. * identifier and email authentication configuration.
  36. * For OIDC/SAML provider configuration management, `TenantAwareAuth` instances should
  37. * be used instead of a `Tenant` to retrieve the list of configured IdPs on a tenant.
  38. * When configuring these providers, note that tenants will inherit
  39. * whitelisted domains and authenticated redirect URIs of their parent project.
  40. *
  41. * All other settings of a tenant will also be inherited. These will need to be managed
  42. * from the Cloud Console UI.
  43. */
  44. class Tenant {
  45. /**
  46. * Builds the corresponding server request for a TenantOptions object.
  47. *
  48. * @param tenantOptions - The properties to convert to a server request.
  49. * @param createRequest - Whether this is a create request.
  50. * @returns The equivalent server request.
  51. *
  52. * @internal
  53. */
  54. static buildServerRequest(tenantOptions, createRequest) {
  55. Tenant.validate(tenantOptions, createRequest);
  56. let request = {};
  57. if (typeof tenantOptions.emailSignInConfig !== 'undefined') {
  58. request = auth_config_1.EmailSignInConfig.buildServerRequest(tenantOptions.emailSignInConfig);
  59. }
  60. if (typeof tenantOptions.displayName !== 'undefined') {
  61. request.displayName = tenantOptions.displayName;
  62. }
  63. if (typeof tenantOptions.anonymousSignInEnabled !== 'undefined') {
  64. request.enableAnonymousUser = tenantOptions.anonymousSignInEnabled;
  65. }
  66. if (typeof tenantOptions.multiFactorConfig !== 'undefined') {
  67. request.mfaConfig = auth_config_1.MultiFactorAuthConfig.buildServerRequest(tenantOptions.multiFactorConfig);
  68. }
  69. if (typeof tenantOptions.testPhoneNumbers !== 'undefined') {
  70. // null will clear existing test phone numbers. Translate to empty object.
  71. request.testPhoneNumbers = tenantOptions.testPhoneNumbers ?? {};
  72. }
  73. if (typeof tenantOptions.smsRegionConfig !== 'undefined') {
  74. request.smsRegionConfig = tenantOptions.smsRegionConfig;
  75. }
  76. if (typeof tenantOptions.recaptchaConfig !== 'undefined') {
  77. request.recaptchaConfig = tenantOptions.recaptchaConfig;
  78. }
  79. if (typeof tenantOptions.passwordPolicyConfig !== 'undefined') {
  80. request.passwordPolicyConfig = auth_config_1.PasswordPolicyAuthConfig.buildServerRequest(tenantOptions.passwordPolicyConfig);
  81. }
  82. if (typeof tenantOptions.emailPrivacyConfig !== 'undefined') {
  83. request.emailPrivacyConfig = tenantOptions.emailPrivacyConfig;
  84. }
  85. return request;
  86. }
  87. /**
  88. * Returns the tenant ID corresponding to the resource name if available.
  89. *
  90. * @param resourceName - The server side resource name
  91. * @returns The tenant ID corresponding to the resource, null otherwise.
  92. *
  93. * @internal
  94. */
  95. static getTenantIdFromResourceName(resourceName) {
  96. // name is of form projects/project1/tenants/tenant1
  97. const matchTenantRes = resourceName.match(/\/tenants\/(.*)$/);
  98. if (!matchTenantRes || matchTenantRes.length < 2) {
  99. return null;
  100. }
  101. return matchTenantRes[1];
  102. }
  103. /**
  104. * Validates a tenant options object. Throws an error on failure.
  105. *
  106. * @param request - The tenant options object to validate.
  107. * @param createRequest - Whether this is a create request.
  108. */
  109. static validate(request, createRequest) {
  110. const validKeys = {
  111. displayName: true,
  112. emailSignInConfig: true,
  113. anonymousSignInEnabled: true,
  114. multiFactorConfig: true,
  115. testPhoneNumbers: true,
  116. smsRegionConfig: true,
  117. recaptchaConfig: true,
  118. passwordPolicyConfig: true,
  119. emailPrivacyConfig: true,
  120. };
  121. const label = createRequest ? 'CreateTenantRequest' : 'UpdateTenantRequest';
  122. if (!validator.isNonNullObject(request)) {
  123. throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_ARGUMENT, `"${label}" must be a valid non-null object.`);
  124. }
  125. // Check for unsupported top level attributes.
  126. for (const key in request) {
  127. if (!(key in validKeys)) {
  128. throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_ARGUMENT, `"${key}" is not a valid ${label} parameter.`);
  129. }
  130. }
  131. // Validate displayName type if provided.
  132. if (typeof request.displayName !== 'undefined' &&
  133. !validator.isNonEmptyString(request.displayName)) {
  134. throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_ARGUMENT, `"${label}.displayName" must be a valid non-empty string.`);
  135. }
  136. // Validate emailSignInConfig type if provided.
  137. if (typeof request.emailSignInConfig !== 'undefined') {
  138. // This will throw an error if invalid.
  139. auth_config_1.EmailSignInConfig.buildServerRequest(request.emailSignInConfig);
  140. }
  141. // Validate test phone numbers if provided.
  142. if (typeof request.testPhoneNumbers !== 'undefined' &&
  143. request.testPhoneNumbers !== null) {
  144. (0, auth_config_1.validateTestPhoneNumbers)(request.testPhoneNumbers);
  145. }
  146. else if (request.testPhoneNumbers === null && createRequest) {
  147. // null allowed only for update operations.
  148. throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_ARGUMENT, `"${label}.testPhoneNumbers" must be a non-null object.`);
  149. }
  150. // Validate multiFactorConfig type if provided.
  151. if (typeof request.multiFactorConfig !== 'undefined') {
  152. // This will throw an error if invalid.
  153. auth_config_1.MultiFactorAuthConfig.buildServerRequest(request.multiFactorConfig);
  154. }
  155. // Validate SMS Regions Config if provided.
  156. if (typeof request.smsRegionConfig !== 'undefined') {
  157. auth_config_1.SmsRegionsAuthConfig.validate(request.smsRegionConfig);
  158. }
  159. // Validate reCAPTCHAConfig type if provided.
  160. if (typeof request.recaptchaConfig !== 'undefined') {
  161. auth_config_1.RecaptchaAuthConfig.validate(request.recaptchaConfig);
  162. }
  163. // Validate passwordPolicyConfig type if provided.
  164. if (typeof request.passwordPolicyConfig !== 'undefined') {
  165. // This will throw an error if invalid.
  166. auth_config_1.PasswordPolicyAuthConfig.buildServerRequest(request.passwordPolicyConfig);
  167. }
  168. // Validate Email Privacy Config if provided.
  169. if (typeof request.emailPrivacyConfig !== 'undefined') {
  170. auth_config_1.EmailPrivacyAuthConfig.validate(request.emailPrivacyConfig);
  171. }
  172. }
  173. /**
  174. * The Tenant object constructor.
  175. *
  176. * @param response - The server side response used to initialize the Tenant object.
  177. * @constructor
  178. * @internal
  179. */
  180. constructor(response) {
  181. const tenantId = Tenant.getTenantIdFromResourceName(response.name);
  182. if (!tenantId) {
  183. throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INTERNAL_ERROR, 'INTERNAL ASSERT FAILED: Invalid tenant response');
  184. }
  185. this.tenantId = tenantId;
  186. this.displayName = response.displayName;
  187. try {
  188. this.emailSignInConfig_ = new auth_config_1.EmailSignInConfig(response);
  189. }
  190. catch (e) {
  191. // If allowPasswordSignup is undefined, it is disabled by default.
  192. this.emailSignInConfig_ = new auth_config_1.EmailSignInConfig({
  193. allowPasswordSignup: false,
  194. });
  195. }
  196. this.anonymousSignInEnabled = !!response.enableAnonymousUser;
  197. if (typeof response.mfaConfig !== 'undefined') {
  198. this.multiFactorConfig_ = new auth_config_1.MultiFactorAuthConfig(response.mfaConfig);
  199. }
  200. if (typeof response.testPhoneNumbers !== 'undefined') {
  201. this.testPhoneNumbers = (0, deep_copy_1.deepCopy)(response.testPhoneNumbers || {});
  202. }
  203. if (typeof response.smsRegionConfig !== 'undefined') {
  204. this.smsRegionConfig = (0, deep_copy_1.deepCopy)(response.smsRegionConfig);
  205. }
  206. if (typeof response.recaptchaConfig !== 'undefined') {
  207. this.recaptchaConfig_ = new auth_config_1.RecaptchaAuthConfig(response.recaptchaConfig);
  208. }
  209. if (typeof response.passwordPolicyConfig !== 'undefined') {
  210. this.passwordPolicyConfig = new auth_config_1.PasswordPolicyAuthConfig(response.passwordPolicyConfig);
  211. }
  212. if (typeof response.emailPrivacyConfig !== 'undefined') {
  213. this.emailPrivacyConfig = (0, deep_copy_1.deepCopy)(response.emailPrivacyConfig);
  214. }
  215. }
  216. /**
  217. * The email sign in provider configuration.
  218. */
  219. get emailSignInConfig() {
  220. return this.emailSignInConfig_;
  221. }
  222. /**
  223. * The multi-factor auth configuration on the current tenant.
  224. */
  225. get multiFactorConfig() {
  226. return this.multiFactorConfig_;
  227. }
  228. /**
  229. * The recaptcha config auth configuration of the current tenant.
  230. */
  231. get recaptchaConfig() {
  232. return this.recaptchaConfig_;
  233. }
  234. /**
  235. * Returns a JSON-serializable representation of this object.
  236. *
  237. * @returns A JSON-serializable representation of this object.
  238. */
  239. toJSON() {
  240. const json = {
  241. tenantId: this.tenantId,
  242. displayName: this.displayName,
  243. emailSignInConfig: this.emailSignInConfig_?.toJSON(),
  244. multiFactorConfig: this.multiFactorConfig_?.toJSON(),
  245. anonymousSignInEnabled: this.anonymousSignInEnabled,
  246. testPhoneNumbers: this.testPhoneNumbers,
  247. smsRegionConfig: (0, deep_copy_1.deepCopy)(this.smsRegionConfig),
  248. recaptchaConfig: this.recaptchaConfig_?.toJSON(),
  249. passwordPolicyConfig: (0, deep_copy_1.deepCopy)(this.passwordPolicyConfig),
  250. emailPrivacyConfig: (0, deep_copy_1.deepCopy)(this.emailPrivacyConfig),
  251. };
  252. if (typeof json.multiFactorConfig === 'undefined') {
  253. delete json.multiFactorConfig;
  254. }
  255. if (typeof json.testPhoneNumbers === 'undefined') {
  256. delete json.testPhoneNumbers;
  257. }
  258. if (typeof json.smsRegionConfig === 'undefined') {
  259. delete json.smsRegionConfig;
  260. }
  261. if (typeof json.recaptchaConfig === 'undefined') {
  262. delete json.recaptchaConfig;
  263. }
  264. if (typeof json.passwordPolicyConfig === 'undefined') {
  265. delete json.passwordPolicyConfig;
  266. }
  267. if (typeof json.emailPrivacyConfig === 'undefined') {
  268. delete json.emailPrivacyConfig;
  269. }
  270. return json;
  271. }
  272. }
  273. exports.Tenant = Tenant;