firebase-tools 9.20.0 → 9.23.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +5 -1
- package/lib/api.js +2 -0
- package/lib/apiv2.js +7 -4
- package/lib/commands/crashlytics-symbols-upload.js +2 -2
- package/lib/commands/deploy.js +9 -1
- package/lib/commands/ext-configure.js +1 -1
- package/lib/commands/ext-dev-deprecate.js +63 -0
- package/lib/commands/ext-dev-undeprecate.js +56 -0
- package/lib/commands/ext-dev-unpublish.js +10 -3
- package/lib/commands/ext-export.js +44 -0
- package/lib/commands/ext-install.js +1 -1
- package/lib/commands/ext-update.js +1 -1
- package/lib/commands/functions-delete.js +55 -42
- package/lib/commands/functions-list.js +11 -11
- package/lib/commands/index.js +6 -5
- package/lib/commands/init.js +3 -0
- package/lib/config.js +3 -2
- package/lib/deploy/extensions/args.js +2 -0
- package/lib/deploy/extensions/deploy.js +49 -0
- package/lib/deploy/extensions/deploymentSummary.js +52 -0
- package/lib/deploy/extensions/errors.js +31 -0
- package/lib/deploy/extensions/index.js +8 -0
- package/lib/deploy/extensions/params.js +39 -0
- package/lib/deploy/extensions/planner.js +94 -0
- package/lib/deploy/extensions/prepare.js +111 -0
- package/lib/deploy/extensions/release.js +43 -0
- package/lib/deploy/extensions/secrets.js +150 -0
- package/lib/deploy/extensions/tasks.js +98 -0
- package/lib/deploy/extensions/validate.js +17 -0
- package/lib/deploy/functions/backend.js +84 -115
- package/lib/deploy/functions/checkIam.js +73 -12
- package/lib/deploy/functions/containerCleaner.js +97 -50
- package/lib/deploy/functions/deploy.js +4 -10
- package/lib/deploy/functions/eventTypes.js +10 -0
- package/lib/deploy/functions/functionsDeployHelper.js +3 -68
- package/lib/deploy/functions/prepare.js +72 -29
- package/lib/deploy/functions/pricing.js +17 -17
- package/lib/deploy/functions/prompts.js +22 -21
- package/lib/deploy/functions/release/executor.js +39 -0
- package/lib/deploy/functions/release/fabricator.js +425 -0
- package/lib/deploy/functions/release/index.js +73 -0
- package/lib/deploy/functions/release/planner.js +162 -0
- package/lib/deploy/functions/release/reporter.js +165 -0
- package/lib/deploy/functions/release/sourceTokenScraper.js +28 -0
- package/lib/deploy/functions/release/timer.js +14 -0
- package/lib/deploy/functions/runtimes/discovery/v1alpha1.js +129 -126
- package/lib/deploy/functions/runtimes/node/parseTriggers.js +32 -54
- package/lib/deploy/functions/services/index.js +38 -0
- package/lib/deploy/functions/services/storage.js +43 -0
- package/lib/deploy/functions/triggerRegionHelper.js +9 -25
- package/lib/deploy/functions/validate.js +1 -24
- package/lib/deploy/index.js +10 -1
- package/lib/emulator/auth/apiSpec.js +37 -6
- package/lib/emulator/auth/operations.js +45 -17
- package/lib/emulator/auth/server.js +16 -2
- package/lib/emulator/auth/state.js +34 -15
- package/lib/emulator/auth/widget_ui.js +14 -0
- package/lib/emulator/downloadableEmulators.js +7 -7
- package/lib/emulator/functionsEmulator.js +18 -4
- package/lib/emulator/storage/cloudFunctions.js +37 -7
- package/lib/ensureApiEnabled.js +10 -12
- package/lib/extensions/askUserForParam.js +14 -6
- package/lib/extensions/checkProjectBilling.js +7 -7
- package/lib/extensions/export.js +107 -0
- package/lib/extensions/extensionsApi.js +103 -21
- package/lib/extensions/extensionsHelper.js +5 -2
- package/lib/extensions/listExtensions.js +16 -11
- package/lib/extensions/paramHelper.js +6 -4
- package/lib/extensions/provisioningHelper.js +16 -3
- package/lib/extensions/refs.js +9 -1
- package/lib/extensions/secretsUtils.js +10 -9
- package/lib/extensions/updateHelper.js +12 -2
- package/lib/extensions/versionHelper.js +14 -0
- package/lib/extensions/warnings.js +33 -1
- package/lib/gcp/artifactregistry.js +16 -0
- package/lib/gcp/cloudfunctions.js +25 -72
- package/lib/gcp/cloudfunctionsv2.js +46 -98
- package/lib/gcp/cloudscheduler.js +22 -16
- package/lib/gcp/cloudtasks.js +143 -0
- package/lib/gcp/docker.js +36 -2
- package/lib/gcp/location.js +44 -0
- package/lib/gcp/proto.js +2 -2
- package/lib/gcp/pubsub.js +1 -9
- package/lib/gcp/secretManager.js +27 -6
- package/lib/gcp/storage.js +48 -32
- package/lib/init/features/project.js +2 -1
- package/lib/previews.js +1 -1
- package/lib/projectUtils.js +10 -1
- package/lib/utils.js +30 -1
- package/package.json +5 -4
- package/schema/firebase-config.json +9 -0
- package/lib/deploy/functions/deploymentPlanner.js +0 -113
- package/lib/deploy/functions/deploymentTimer.js +0 -23
- package/lib/deploy/functions/errorHandler.js +0 -75
- package/lib/deploy/functions/release.js +0 -116
- package/lib/deploy/functions/tasks.js +0 -324
- package/lib/functions/listFunctions.js +0 -10
- package/lib/functionsDelete.js +0 -60
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ensureStorageTriggerRegion = exports.obtainStorageBindings = void 0;
|
|
4
|
+
const storage = require("../../../gcp/storage");
|
|
5
|
+
const logger_1 = require("../../../logger");
|
|
6
|
+
const error_1 = require("../../../error");
|
|
7
|
+
const location_1 = require("../../../gcp/location");
|
|
8
|
+
const PUBSUB_PUBLISHER_ROLE = "roles/pubsub.publisher";
|
|
9
|
+
async function obtainStorageBindings(projectId, existingPolicy) {
|
|
10
|
+
const storageResponse = await storage.getServiceAccount(projectId);
|
|
11
|
+
const storageServiceAgent = `serviceAccount:${storageResponse.email_address}`;
|
|
12
|
+
let pubsubBinding = existingPolicy.bindings.find((b) => b.role === PUBSUB_PUBLISHER_ROLE);
|
|
13
|
+
if (!pubsubBinding) {
|
|
14
|
+
pubsubBinding = {
|
|
15
|
+
role: PUBSUB_PUBLISHER_ROLE,
|
|
16
|
+
members: [],
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
if (!pubsubBinding.members.find((m) => m === storageServiceAgent)) {
|
|
20
|
+
pubsubBinding.members.push(storageServiceAgent);
|
|
21
|
+
}
|
|
22
|
+
return [pubsubBinding];
|
|
23
|
+
}
|
|
24
|
+
exports.obtainStorageBindings = obtainStorageBindings;
|
|
25
|
+
async function ensureStorageTriggerRegion(endpoint, eventTrigger) {
|
|
26
|
+
if (!eventTrigger.region) {
|
|
27
|
+
logger_1.logger.debug("Looking up bucket region for the storage event trigger");
|
|
28
|
+
try {
|
|
29
|
+
const bucket = await storage.getBucket(eventTrigger.eventFilters.bucket);
|
|
30
|
+
eventTrigger.region = bucket.location.toLowerCase();
|
|
31
|
+
logger_1.logger.debug("Setting the event trigger region to", eventTrigger.region, ".");
|
|
32
|
+
}
|
|
33
|
+
catch (err) {
|
|
34
|
+
throw new error_1.FirebaseError("Can't find the storage bucket region", { original: err });
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
if (endpoint.region !== eventTrigger.region &&
|
|
38
|
+
eventTrigger.region !== "us-central1" &&
|
|
39
|
+
!location_1.regionInLocation(endpoint.region, eventTrigger.region)) {
|
|
40
|
+
throw new error_1.FirebaseError(`A function in region ${endpoint.region} cannot listen to a bucket in region ${eventTrigger.region}`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
exports.ensureStorageTriggerRegion = ensureStorageTriggerRegion;
|
|
@@ -1,32 +1,16 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.ensureTriggerRegions = void 0;
|
|
4
4
|
const backend = require("./backend");
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
if (wantFn.platform === "gcfv1" || !backend.isEventTrigger(wantFn.trigger)) {
|
|
5
|
+
const services_1 = require("./services");
|
|
6
|
+
async function ensureTriggerRegions(want) {
|
|
7
|
+
const regionLookups = [];
|
|
8
|
+
for (const ep of backend.allEndpoints(want)) {
|
|
9
|
+
if (ep.platform === "gcfv1" || !backend.isEventTriggered(ep)) {
|
|
11
10
|
continue;
|
|
12
11
|
}
|
|
13
|
-
|
|
14
|
-
if (match === null || match === void 0 ? void 0 : match.region) {
|
|
15
|
-
wantFn.trigger.region = match.region;
|
|
16
|
-
}
|
|
17
|
-
else {
|
|
18
|
-
await setTriggerRegionFromTriggerType(wantFn.trigger);
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
exports.setTriggerRegion = setTriggerRegion;
|
|
23
|
-
async function setTriggerRegionFromTriggerType(trigger) {
|
|
24
|
-
if (trigger.eventFilters.bucket) {
|
|
25
|
-
try {
|
|
26
|
-
trigger.region = (await storage.getBucket(trigger.eventFilters.bucket)).location.toLowerCase();
|
|
27
|
-
}
|
|
28
|
-
catch (err) {
|
|
29
|
-
throw new error_1.FirebaseError("Can't find the storage bucket region", { original: err });
|
|
30
|
-
}
|
|
12
|
+
regionLookups.push(services_1.serviceForEndpoint(ep).ensureTriggerRegion(ep, ep.eventTrigger));
|
|
31
13
|
}
|
|
14
|
+
await Promise.all(regionLookups);
|
|
32
15
|
}
|
|
16
|
+
exports.ensureTriggerRegions = ensureTriggerRegions;
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.functionIdsAreValid = exports.functionsDirectoryExists = void 0;
|
|
4
4
|
const clc = require("cli-color");
|
|
5
5
|
const error_1 = require("../../error");
|
|
6
|
-
const functionsDeployHelper_1 = require("./functionsDeployHelper");
|
|
7
|
-
const backend = require("./backend");
|
|
8
6
|
const fsutils = require("../../fsutils");
|
|
9
7
|
const projectPath = require("../../projectPath");
|
|
10
8
|
function functionsDirectoryExists(options, sourceDirName) {
|
|
@@ -36,24 +34,3 @@ function functionIdsAreValid(functions) {
|
|
|
36
34
|
}
|
|
37
35
|
}
|
|
38
36
|
exports.functionIdsAreValid = functionIdsAreValid;
|
|
39
|
-
function checkForInvalidChangeOfTrigger(fn, exFn) {
|
|
40
|
-
var _a, _b;
|
|
41
|
-
const wantEventTrigger = backend.isEventTrigger(fn.trigger);
|
|
42
|
-
const haveEventTrigger = backend.isEventTrigger(exFn.trigger);
|
|
43
|
-
if (!wantEventTrigger && haveEventTrigger) {
|
|
44
|
-
throw new error_1.FirebaseError(`[${functionsDeployHelper_1.getFunctionLabel(fn)}] Changing from a background triggered function to an HTTPS function is not allowed. Please delete your function and create a new one instead.`);
|
|
45
|
-
}
|
|
46
|
-
if (wantEventTrigger && !haveEventTrigger) {
|
|
47
|
-
throw new error_1.FirebaseError(`[${functionsDeployHelper_1.getFunctionLabel(fn)}] Changing from an HTTPS function to an background triggered function is not allowed. Please delete your function and create a new one instead.`);
|
|
48
|
-
}
|
|
49
|
-
if (fn.platform == "gcfv2" && exFn.platform == "gcfv1") {
|
|
50
|
-
throw new error_1.FirebaseError(`[${functionsDeployHelper_1.getFunctionLabel(fn)}] Upgrading from GCFv1 to GCFv2 is not yet supported. Please delete your old function or wait for this feature to be ready.`);
|
|
51
|
-
}
|
|
52
|
-
if (fn.platform == "gcfv1" && exFn.platform == "gcfv2") {
|
|
53
|
-
throw new error_1.FirebaseError(`[${functionsDeployHelper_1.getFunctionLabel(fn)}] Functions cannot be downgraded from GCFv2 to GCFv1`);
|
|
54
|
-
}
|
|
55
|
-
if (((_a = exFn.labels) === null || _a === void 0 ? void 0 : _a["deployment-scheduled"]) && !((_b = fn.labels) === null || _b === void 0 ? void 0 : _b["deployment-scheduled"])) {
|
|
56
|
-
throw new error_1.FirebaseError(`[${functionsDeployHelper_1.getFunctionLabel(fn)}] Scheduled functions cannot be changed to event handler or HTTP functions`);
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
exports.checkForInvalidChangeOfTrigger = checkForInvalidChangeOfTrigger;
|
package/lib/deploy/index.js
CHANGED
|
@@ -15,6 +15,7 @@ var TARGETS = {
|
|
|
15
15
|
functions: require("./functions"),
|
|
16
16
|
storage: require("./storage"),
|
|
17
17
|
remoteconfig: require("./remoteconfig"),
|
|
18
|
+
extensions: require("./extensions"),
|
|
18
19
|
};
|
|
19
20
|
var _noop = function () {
|
|
20
21
|
return Promise.resolve();
|
|
@@ -37,6 +38,7 @@ var deploy = function (targetNames, options, customContext = {}) {
|
|
|
37
38
|
var deploys = [];
|
|
38
39
|
var releases = [];
|
|
39
40
|
var postdeploys = [];
|
|
41
|
+
var startTime = Date.now();
|
|
40
42
|
for (var i = 0; i < targetNames.length; i++) {
|
|
41
43
|
var targetName = targetNames[i];
|
|
42
44
|
var target = TARGETS[targetName];
|
|
@@ -74,8 +76,15 @@ var deploy = function (targetNames, options, customContext = {}) {
|
|
|
74
76
|
})
|
|
75
77
|
.then(function () {
|
|
76
78
|
if (_.has(options, "config.notes.databaseRules")) {
|
|
77
|
-
track("Rules Deploy", options.config.notes.databaseRules);
|
|
79
|
+
return track("Rules Deploy", options.config.notes.databaseRules);
|
|
78
80
|
}
|
|
81
|
+
return;
|
|
82
|
+
})
|
|
83
|
+
.then(function () {
|
|
84
|
+
const duration = Date.now() - startTime;
|
|
85
|
+
return track("Product Deploy", [...targetNames].sort().join(","), duration);
|
|
86
|
+
})
|
|
87
|
+
.then(function () {
|
|
79
88
|
logger.info();
|
|
80
89
|
utils.logSuccess(clc.underline.bold("Deploy complete!"));
|
|
81
90
|
logger.info();
|
|
@@ -3580,6 +3580,37 @@ exports.default = {
|
|
|
3580
3580
|
tags: ["emulator"],
|
|
3581
3581
|
},
|
|
3582
3582
|
},
|
|
3583
|
+
"/emulator/v1/projects/{targetProjectId}/tenants/{tenantId}/accounts": {
|
|
3584
|
+
parameters: [
|
|
3585
|
+
{
|
|
3586
|
+
name: "targetProjectId",
|
|
3587
|
+
in: "path",
|
|
3588
|
+
description: "The ID of the Google Cloud project that the accounts belong to.",
|
|
3589
|
+
required: true,
|
|
3590
|
+
schema: { type: "string" },
|
|
3591
|
+
},
|
|
3592
|
+
{
|
|
3593
|
+
name: "tenantId",
|
|
3594
|
+
in: "path",
|
|
3595
|
+
description: "The ID of the Identity Platform tenant the accounts belongs to. If not specified, accounts on the Identity Platform project are returned.",
|
|
3596
|
+
required: true,
|
|
3597
|
+
schema: { type: "string" },
|
|
3598
|
+
},
|
|
3599
|
+
],
|
|
3600
|
+
servers: [{ url: "" }],
|
|
3601
|
+
delete: {
|
|
3602
|
+
description: "Remove all accounts in the project, regardless of state.",
|
|
3603
|
+
operationId: "emulator.projects.accounts.delete",
|
|
3604
|
+
responses: {
|
|
3605
|
+
200: {
|
|
3606
|
+
description: "Successful response",
|
|
3607
|
+
content: { "application/json": { schema: { type: "object" } } },
|
|
3608
|
+
},
|
|
3609
|
+
},
|
|
3610
|
+
security: [],
|
|
3611
|
+
tags: ["emulator"],
|
|
3612
|
+
},
|
|
3613
|
+
},
|
|
3583
3614
|
"/emulator/v1/projects/{targetProjectId}/config": {
|
|
3584
3615
|
parameters: [
|
|
3585
3616
|
{
|
|
@@ -4978,7 +5009,7 @@ exports.default = {
|
|
|
4978
5009
|
type: "string",
|
|
4979
5010
|
},
|
|
4980
5011
|
postBody: {
|
|
4981
|
-
description: "If the user is signing in with an authorization response obtained via a previous CreateAuthUri authorization request, this is the body of the HTTP POST callback from the IdP, if present. Otherwise, if the user is signing in with a manually provided IdP credential, this should be a URL-encoded form that contains the credential (e.g. an ID token or access token for OAuth 2.0 IdPs) and the provider ID of the IdP that issued the credential. For example, if the user is signing in to the Google provider using a Google ID token, this should be set to `id_token=[GOOGLE_ID_TOKEN]&providerId=google.com`, where `[GOOGLE_ID_TOKEN]` should be replaced with the Google ID token. If the user is signing in to the Facebook provider using a Facebook access token, this should be set to `access_token=[FACEBOOK_ACCESS_TOKEN]&providerId=facebook.com`, where `[FACEBOOK_ACCESS_TOKEN]` should be replaced with the Facebook access token. If the user is signing in to the Twitter provider using a Twitter OAuth 1.0 credential, this should be set to `access_token=[TWITTER_ACCESS_TOKEN]&oauth_token_secret=[TWITTER_TOKEN_SECRET]&providerId=twitter.com`, where `[TWITTER_ACCESS_TOKEN]` and `[TWITTER_TOKEN_SECRET]` should be replaced with the Twitter OAuth access token and Twitter OAuth token secret respectively.",
|
|
5012
|
+
description: "If the user is signing in with an authorization response obtained via a previous CreateAuthUri authorization request, this is the body of the HTTP POST callback from the IdP, if present. Otherwise, if the user is signing in with a manually provided IdP credential, this should be a URL-encoded form that contains the credential (e.g. an ID token or access token for OAuth 2.0 IdPs) and the provider ID of the IdP that issued the credential. For example, if the user is signing in to the Google provider using a Google ID token, this should be set to `id_token=[GOOGLE_ID_TOKEN]&providerId=google.com`, where `[GOOGLE_ID_TOKEN]` should be replaced with the Google ID token. If the user is signing in to the Facebook provider using a Facebook authentication token, this should be set to `id_token=[FACEBOOK_AUTHENTICATION_TOKEN]&providerId=facebook.com&nonce= [NONCE]`, where `[FACEBOOK_AUTHENTICATION_TOKEN]` should be replaced with the Facebook authentication token. Nonce is required for validating the token. The request will fail if no nonce is provided. If the user is signing in to the Facebook provider using a Facebook access token, this should be set to `access_token=[FACEBOOK_ACCESS_TOKEN]&providerId=facebook.com`, where `[FACEBOOK_ACCESS_TOKEN]` should be replaced with the Facebook access token. If the user is signing in to the Twitter provider using a Twitter OAuth 1.0 credential, this should be set to `access_token=[TWITTER_ACCESS_TOKEN]&oauth_token_secret=[TWITTER_TOKEN_SECRET]&providerId=twitter.com`, where `[TWITTER_ACCESS_TOKEN]` and `[TWITTER_TOKEN_SECRET]` should be replaced with the Twitter OAuth access token and Twitter OAuth token secret respectively.",
|
|
4982
5013
|
type: "string",
|
|
4983
5014
|
},
|
|
4984
5015
|
requestUri: {
|
|
@@ -6665,16 +6696,16 @@ exports.default = {
|
|
|
6665
6696
|
type: "object",
|
|
6666
6697
|
},
|
|
6667
6698
|
GoogleIamV1Binding: {
|
|
6668
|
-
description: "Associates `members
|
|
6699
|
+
description: "Associates `members`, or principals, with a `role`.",
|
|
6669
6700
|
properties: {
|
|
6670
6701
|
condition: { $ref: "#/components/schemas/GoogleTypeExpr" },
|
|
6671
6702
|
members: {
|
|
6672
|
-
description: "Specifies the
|
|
6703
|
+
description: "Specifies the principals requesting access for a Cloud Platform resource. `members` can have the following values: * `allUsers`: A special identifier that represents anyone who is on the internet; with or without a Google account. * `allAuthenticatedUsers`: A special identifier that represents anyone who is authenticated with a Google account or a service account. * `user:{emailid}`: An email address that represents a specific Google account. For example, `alice@example.com` . * `serviceAccount:{emailid}`: An email address that represents a service account. For example, `my-other-app@appspot.gserviceaccount.com`. * `group:{emailid}`: An email address that represents a Google group. For example, `admins@example.com`. * `deleted:user:{emailid}?uid={uniqueid}`: An email address (plus unique identifier) representing a user that has been recently deleted. For example, `alice@example.com?uid=123456789012345678901`. If the user is recovered, this value reverts to `user:{emailid}` and the recovered user retains the role in the binding. * `deleted:serviceAccount:{emailid}?uid={uniqueid}`: An email address (plus unique identifier) representing a service account that has been recently deleted. For example, `my-other-app@appspot.gserviceaccount.com?uid=123456789012345678901`. If the service account is undeleted, this value reverts to `serviceAccount:{emailid}` and the undeleted service account retains the role in the binding. * `deleted:group:{emailid}?uid={uniqueid}`: An email address (plus unique identifier) representing a Google group that has been recently deleted. For example, `admins@example.com?uid=123456789012345678901`. If the group is recovered, this value reverts to `group:{emailid}` and the recovered group retains the role in the binding. * `domain:{domain}`: The G Suite domain (primary) that represents all the users of that domain. For example, `google.com` or `example.com`. ",
|
|
6673
6704
|
items: { type: "string" },
|
|
6674
6705
|
type: "array",
|
|
6675
6706
|
},
|
|
6676
6707
|
role: {
|
|
6677
|
-
description: "Role that is assigned to `members
|
|
6708
|
+
description: "Role that is assigned to the list of `members`, or principals. For example, `roles/viewer`, `roles/editor`, or `roles/owner`.",
|
|
6678
6709
|
type: "string",
|
|
6679
6710
|
},
|
|
6680
6711
|
},
|
|
@@ -6697,7 +6728,7 @@ exports.default = {
|
|
|
6697
6728
|
type: "object",
|
|
6698
6729
|
},
|
|
6699
6730
|
GoogleIamV1Policy: {
|
|
6700
|
-
description: 'An Identity and Access Management (IAM) policy, which specifies access controls for Google Cloud resources. A `Policy` is a collection of `bindings`. A `binding` binds one or more `members
|
|
6731
|
+
description: 'An Identity and Access Management (IAM) policy, which specifies access controls for Google Cloud resources. A `Policy` is a collection of `bindings`. A `binding` binds one or more `members`, or principals, to a single `role`. Principals can be user accounts, service accounts, Google groups, and domains (such as G Suite). A `role` is a named list of permissions; each `role` can be an IAM predefined role or a user-created custom role. For some types of Google Cloud resources, a `binding` can also specify a `condition`, which is a logical expression that allows access to a resource only if the expression evaluates to `true`. A condition can add constraints based on attributes of the request, the resource, or both. To learn which resources support conditions in their IAM policies, see the [IAM documentation](https://cloud.google.com/iam/help/conditions/resource-policies). **JSON example:** { "bindings": [ { "role": "roles/resourcemanager.organizationAdmin", "members": [ "user:mike@example.com", "group:admins@example.com", "domain:google.com", "serviceAccount:my-project-id@appspot.gserviceaccount.com" ] }, { "role": "roles/resourcemanager.organizationViewer", "members": [ "user:eve@example.com" ], "condition": { "title": "expirable access", "description": "Does not grant access after Sep 2020", "expression": "request.time < timestamp(\'2020-10-01T00:00:00.000Z\')", } } ], "etag": "BwWWja0YfJA=", "version": 3 } **YAML example:** bindings: - members: - user:mike@example.com - group:admins@example.com - domain:google.com - serviceAccount:my-project-id@appspot.gserviceaccount.com role: roles/resourcemanager.organizationAdmin - members: - user:eve@example.com role: roles/resourcemanager.organizationViewer condition: title: expirable access description: Does not grant access after Sep 2020 expression: request.time < timestamp(\'2020-10-01T00:00:00.000Z\') etag: BwWWja0YfJA= version: 3 For a description of IAM and its features, see the [IAM documentation](https://cloud.google.com/iam/docs/).',
|
|
6701
6732
|
properties: {
|
|
6702
6733
|
auditConfigs: {
|
|
6703
6734
|
description: "Specifies cloud audit logging configuration for this policy.",
|
|
@@ -6705,7 +6736,7 @@ exports.default = {
|
|
|
6705
6736
|
type: "array",
|
|
6706
6737
|
},
|
|
6707
6738
|
bindings: {
|
|
6708
|
-
description: "Associates a list of `members
|
|
6739
|
+
description: "Associates a list of `members`, or principals, with a `role`. Optionally, may specify a `condition` that determines how and when the `bindings` are applied. Each of the `bindings` must contain at least one principal. The `bindings` in a `Policy` can refer to up to 1,500 principals; up to 250 of these principals can be Google groups. Each occurrence of a principal counts towards these limits. For example, if the `bindings` grant 50 different roles to `user:alice@example.com`, and not to any other principal, then you can add another 1,450 principals to the `bindings` in the `Policy`.",
|
|
6709
6740
|
items: { $ref: "#/components/schemas/GoogleIamV1Binding" },
|
|
6710
6741
|
type: "array",
|
|
6711
6742
|
},
|
|
@@ -160,8 +160,8 @@ function signUp(state, reqBody, ctx) {
|
|
|
160
160
|
generateEnrollmentIds: true,
|
|
161
161
|
});
|
|
162
162
|
}
|
|
163
|
-
if (
|
|
164
|
-
updates.tenantId =
|
|
163
|
+
if (state instanceof state_1.TenantProjectState) {
|
|
164
|
+
updates.tenantId = state.tenantId;
|
|
165
165
|
}
|
|
166
166
|
let user;
|
|
167
167
|
if (reqBody.idToken) {
|
|
@@ -267,6 +267,8 @@ function batchCreate(state, reqBody) {
|
|
|
267
267
|
};
|
|
268
268
|
if (userInfo.tenantId) {
|
|
269
269
|
errors_1.assert(state instanceof state_1.TenantProjectState && state.tenantId === userInfo.tenantId, "Tenant id in userInfo does not match the tenant id in request.");
|
|
270
|
+
}
|
|
271
|
+
if (state instanceof state_1.TenantProjectState) {
|
|
270
272
|
fields.tenantId = state.tenantId;
|
|
271
273
|
}
|
|
272
274
|
if (userInfo.passwordHash) {
|
|
@@ -921,7 +923,7 @@ function signInWithCustomToken(state, reqBody) {
|
|
|
921
923
|
else {
|
|
922
924
|
const decoded = jsonwebtoken_1.decode(reqBody.token, { complete: true });
|
|
923
925
|
if (state instanceof state_1.TenantProjectState) {
|
|
924
|
-
errors_1.assert((decoded === null || decoded === void 0 ? void 0 : decoded.payload.
|
|
926
|
+
errors_1.assert((decoded === null || decoded === void 0 ? void 0 : decoded.payload.tenant_id) === state.tenantId, "TENANT_ID_MISMATCH");
|
|
925
927
|
}
|
|
926
928
|
errors_1.assert(decoded, "INVALID_CUSTOM_TOKEN : Invalid assertion format");
|
|
927
929
|
if (decoded.header.alg !== "none") {
|
|
@@ -1008,7 +1010,7 @@ function signInWithEmailLink(state, reqBody) {
|
|
|
1008
1010
|
}
|
|
1009
1011
|
}
|
|
1010
1012
|
function signInWithIdp(state, reqBody) {
|
|
1011
|
-
var _a, _b;
|
|
1013
|
+
var _a, _b, _c;
|
|
1012
1014
|
errors_1.assert(!state.disableAuth, "PROJECT_DISABLED");
|
|
1013
1015
|
errors_1.assert(state.usageMode !== state_1.UsageMode.PASSTHROUGH, "UNSUPPORTED_PASSTHROUGH_OPERATION");
|
|
1014
1016
|
if (reqBody.returnRefreshToken) {
|
|
@@ -1039,7 +1041,16 @@ function signInWithIdp(state, reqBody) {
|
|
|
1039
1041
|
throw new errors_1.NotImplementedError("The Auth Emulator only supports sign-in with credentials (id_token required).");
|
|
1040
1042
|
}
|
|
1041
1043
|
}
|
|
1042
|
-
let
|
|
1044
|
+
let samlResponse;
|
|
1045
|
+
let signInAttributes = undefined;
|
|
1046
|
+
if (normalizedUri.searchParams.get("SAMLResponse")) {
|
|
1047
|
+
samlResponse = JSON.parse(normalizedUri.searchParams.get("SAMLResponse"));
|
|
1048
|
+
signInAttributes = (_b = samlResponse.assertion) === null || _b === void 0 ? void 0 : _b.attributeStatements;
|
|
1049
|
+
errors_1.assert(samlResponse.assertion, "INVALID_IDP_RESPONSE ((Missing assertion in SAMLResponse.))");
|
|
1050
|
+
errors_1.assert(samlResponse.assertion.subject, "INVALID_IDP_RESPONSE ((Missing assertion.subject in SAMLResponse.))");
|
|
1051
|
+
errors_1.assert(samlResponse.assertion.subject.nameId, "INVALID_IDP_RESPONSE ((Missing assertion.subject.nameId in SAMLResponse.))");
|
|
1052
|
+
}
|
|
1053
|
+
let { response, rawId } = fakeFetchUserInfoFromIdp(providerId, claims, samlResponse);
|
|
1043
1054
|
response.oauthAccessToken =
|
|
1044
1055
|
oauthAccessToken || `FirebaseAuthEmulatorFakeAccessToken_${providerId}`;
|
|
1045
1056
|
response.oauthIdToken = oauthIdToken;
|
|
@@ -1099,12 +1110,12 @@ function signInWithIdp(state, reqBody) {
|
|
|
1099
1110
|
if (state instanceof state_1.TenantProjectState) {
|
|
1100
1111
|
response.tenantId = state.tenantId;
|
|
1101
1112
|
}
|
|
1102
|
-
if ((state.mfaConfig.state === "ENABLED" || state.mfaConfig.state === "MANDATORY") && ((
|
|
1113
|
+
if ((state.mfaConfig.state === "ENABLED" || state.mfaConfig.state === "MANDATORY") && ((_c = user.mfaInfo) === null || _c === void 0 ? void 0 : _c.length)) {
|
|
1103
1114
|
return Object.assign(Object.assign({}, response), mfaPending(state, user, providerId));
|
|
1104
1115
|
}
|
|
1105
1116
|
else {
|
|
1106
1117
|
user = state.updateUserByLocalId(user.localId, { lastLoginAt: Date.now().toString() });
|
|
1107
|
-
return Object.assign(Object.assign({}, response), issueTokens(state, user, providerId));
|
|
1118
|
+
return Object.assign(Object.assign({}, response), issueTokens(state, user, providerId, { signInAttributes }));
|
|
1108
1119
|
}
|
|
1109
1120
|
}
|
|
1110
1121
|
function signInWithPassword(state, reqBody) {
|
|
@@ -1389,7 +1400,7 @@ function redactPasswordHash(user) {
|
|
|
1389
1400
|
function hashPassword(password, salt) {
|
|
1390
1401
|
return `fakeHash:salt=${salt}:password=${password}`;
|
|
1391
1402
|
}
|
|
1392
|
-
function issueTokens(state, user, signInProvider, { extraClaims, secondFactor, } = {}) {
|
|
1403
|
+
function issueTokens(state, user, signInProvider, { extraClaims, secondFactor, signInAttributes, } = {}) {
|
|
1393
1404
|
user = state.updateUserByLocalId(user.localId, { lastRefreshAt: new Date().toISOString() });
|
|
1394
1405
|
const usageMode = state.usageMode === state_1.UsageMode.PASSTHROUGH ? "passthrough" : undefined;
|
|
1395
1406
|
const tenantId = state instanceof state_1.TenantProjectState ? state.tenantId : undefined;
|
|
@@ -1402,6 +1413,7 @@ function issueTokens(state, user, signInProvider, { extraClaims, secondFactor, }
|
|
|
1402
1413
|
secondFactor,
|
|
1403
1414
|
usageMode,
|
|
1404
1415
|
tenantId,
|
|
1416
|
+
signInAttributes,
|
|
1405
1417
|
});
|
|
1406
1418
|
const refreshToken = state.usageMode === state_1.UsageMode.DEFAULT
|
|
1407
1419
|
? state.createRefreshTokenFor(user, signInProvider, {
|
|
@@ -1433,7 +1445,7 @@ function parseIdToken(state, idToken) {
|
|
|
1433
1445
|
const signInProvider = decoded.payload.firebase.sign_in_provider;
|
|
1434
1446
|
return { user, signInProvider, payload: decoded.payload };
|
|
1435
1447
|
}
|
|
1436
|
-
function generateJwt(user, { projectId, signInProvider, expiresInSeconds, extraClaims = {}, secondFactor, usageMode, tenantId, }) {
|
|
1448
|
+
function generateJwt(user, { projectId, signInProvider, expiresInSeconds, extraClaims = {}, secondFactor, usageMode, tenantId, signInAttributes, }) {
|
|
1437
1449
|
const identities = {};
|
|
1438
1450
|
if (user.email) {
|
|
1439
1451
|
identities["email"] = [user.email];
|
|
@@ -1457,6 +1469,7 @@ function generateJwt(user, { projectId, signInProvider, expiresInSeconds, extraC
|
|
|
1457
1469
|
sign_in_second_factor: secondFactor === null || secondFactor === void 0 ? void 0 : secondFactor.provider,
|
|
1458
1470
|
usage_mode: usageMode,
|
|
1459
1471
|
tenant: tenantId,
|
|
1472
|
+
sign_in_attributes: signInAttributes,
|
|
1460
1473
|
} });
|
|
1461
1474
|
const jwtStr = jsonwebtoken_1.sign(customPayloadFields, "", {
|
|
1462
1475
|
algorithm: "none",
|
|
@@ -1601,7 +1614,8 @@ function parseClaims(idTokenOrJsonClaims) {
|
|
|
1601
1614
|
errors_1.assert(typeof claims.sub === "string", 'INVALID_IDP_RESPONSE : ((The "sub" field must be a string.))');
|
|
1602
1615
|
return claims;
|
|
1603
1616
|
}
|
|
1604
|
-
function fakeFetchUserInfoFromIdp(providerId, claims) {
|
|
1617
|
+
function fakeFetchUserInfoFromIdp(providerId, claims, samlResponse) {
|
|
1618
|
+
var _a, _b, _c, _d, _e;
|
|
1605
1619
|
const rawId = claims.sub;
|
|
1606
1620
|
const email = claims.email ? utils_1.canonicalizeEmailAddress(claims.email) : undefined;
|
|
1607
1621
|
const emailVerified = !!claims.email_verified;
|
|
@@ -1618,7 +1632,7 @@ function fakeFetchUserInfoFromIdp(providerId, claims) {
|
|
|
1618
1632
|
emailVerified,
|
|
1619
1633
|
photoUrl,
|
|
1620
1634
|
};
|
|
1621
|
-
let federatedId;
|
|
1635
|
+
let federatedId = rawId;
|
|
1622
1636
|
switch (providerId) {
|
|
1623
1637
|
case "google.com": {
|
|
1624
1638
|
federatedId = `https://accounts.google.com/${rawId}`;
|
|
@@ -1641,8 +1655,14 @@ function fakeFetchUserInfoFromIdp(providerId, claims) {
|
|
|
1641
1655
|
});
|
|
1642
1656
|
break;
|
|
1643
1657
|
}
|
|
1658
|
+
case (_a = providerId.match(/^saml\./)) === null || _a === void 0 ? void 0 : _a.input:
|
|
1659
|
+
const nameId = (_c = (_b = samlResponse === null || samlResponse === void 0 ? void 0 : samlResponse.assertion) === null || _b === void 0 ? void 0 : _b.subject) === null || _c === void 0 ? void 0 : _c.nameId;
|
|
1660
|
+
response.email = nameId && utils_1.isValidEmailAddress(nameId) ? nameId : response.email;
|
|
1661
|
+
response.emailVerified = true;
|
|
1662
|
+
response.rawUserInfo = JSON.stringify((_d = samlResponse === null || samlResponse === void 0 ? void 0 : samlResponse.assertion) === null || _d === void 0 ? void 0 : _d.attributeStatements);
|
|
1663
|
+
break;
|
|
1664
|
+
case (_e = providerId.match(/^oidc\./)) === null || _e === void 0 ? void 0 : _e.input:
|
|
1644
1665
|
default:
|
|
1645
|
-
federatedId = rawId;
|
|
1646
1666
|
response.rawUserInfo = JSON.stringify(claims);
|
|
1647
1667
|
break;
|
|
1648
1668
|
}
|
|
@@ -1798,16 +1818,24 @@ function parsePendingCredential(state, pendingCredential) {
|
|
|
1798
1818
|
return { user, signInProvider };
|
|
1799
1819
|
}
|
|
1800
1820
|
function createTenant(state, reqBody) {
|
|
1821
|
+
var _a, _b, _c, _d, _e;
|
|
1801
1822
|
if (!(state instanceof state_1.AgentProjectState)) {
|
|
1802
1823
|
throw new errors_1.InternalError("INTERNAL_ERROR: Can only create tenant in agent project", "INTERNAL");
|
|
1803
1824
|
}
|
|
1825
|
+
const mfaConfig = (_a = reqBody.mfaConfig) !== null && _a !== void 0 ? _a : {};
|
|
1826
|
+
if (!("state" in mfaConfig)) {
|
|
1827
|
+
mfaConfig.state = "DISABLED";
|
|
1828
|
+
}
|
|
1829
|
+
if (!("enabledProviders" in mfaConfig)) {
|
|
1830
|
+
mfaConfig.enabledProviders = [];
|
|
1831
|
+
}
|
|
1804
1832
|
const tenant = {
|
|
1805
1833
|
displayName: reqBody.displayName,
|
|
1806
|
-
allowPasswordSignup: reqBody.allowPasswordSignup,
|
|
1807
|
-
enableEmailLinkSignin: reqBody.enableEmailLinkSignin,
|
|
1808
|
-
enableAnonymousUser: reqBody.enableAnonymousUser,
|
|
1809
|
-
disableAuth: reqBody.disableAuth,
|
|
1810
|
-
mfaConfig:
|
|
1834
|
+
allowPasswordSignup: (_b = reqBody.allowPasswordSignup) !== null && _b !== void 0 ? _b : false,
|
|
1835
|
+
enableEmailLinkSignin: (_c = reqBody.enableEmailLinkSignin) !== null && _c !== void 0 ? _c : false,
|
|
1836
|
+
enableAnonymousUser: (_d = reqBody.enableAnonymousUser) !== null && _d !== void 0 ? _d : false,
|
|
1837
|
+
disableAuth: (_e = reqBody.disableAuth) !== null && _e !== void 0 ? _e : false,
|
|
1838
|
+
mfaConfig: mfaConfig,
|
|
1811
1839
|
tenantId: "",
|
|
1812
1840
|
};
|
|
1813
1841
|
return state.createTenant(tenant);
|
|
@@ -17,6 +17,7 @@ const lodash_1 = require("lodash");
|
|
|
17
17
|
const handlers_1 = require("./handlers");
|
|
18
18
|
const bodyParser = require("body-parser");
|
|
19
19
|
const url_1 = require("url");
|
|
20
|
+
const jsonwebtoken_1 = require("jsonwebtoken");
|
|
20
21
|
const apiSpec = apiSpec_1.default;
|
|
21
22
|
const API_SPEC_PATH = "/emulator/openapi.json";
|
|
22
23
|
const AUTH_HEADER_PREFIX = "bearer ";
|
|
@@ -70,6 +71,10 @@ async function createApp(defaultProjectId, projectStateForId = new Map()) {
|
|
|
70
71
|
const app = express();
|
|
71
72
|
app.set("json spaces", 2);
|
|
72
73
|
app.use(cors({ origin: true }));
|
|
74
|
+
app.delete("*", (req, _, next) => {
|
|
75
|
+
delete req.headers["content-type"];
|
|
76
|
+
next();
|
|
77
|
+
});
|
|
73
78
|
app.get("/", (req, res) => {
|
|
74
79
|
return res.json({
|
|
75
80
|
authEmulator: {
|
|
@@ -334,7 +339,7 @@ function toExegesisController(ops, getProjectStateById) {
|
|
|
334
339
|
}
|
|
335
340
|
function toExegesisOperation(operation) {
|
|
336
341
|
return (ctx) => {
|
|
337
|
-
var _a, _b, _c, _d, _e;
|
|
342
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
338
343
|
let targetProjectId = ctx.params.path.targetProjectId || ((_a = ctx.requestBody) === null || _a === void 0 ? void 0 : _a.targetProjectId);
|
|
339
344
|
if (targetProjectId) {
|
|
340
345
|
if ((_b = ctx.api.operationObject.security) === null || _b === void 0 ? void 0 : _b.some((sec) => sec.Oauth2)) {
|
|
@@ -344,10 +349,19 @@ function toExegesisController(ops, getProjectStateById) {
|
|
|
344
349
|
else {
|
|
345
350
|
targetProjectId = ctx.user;
|
|
346
351
|
}
|
|
352
|
+
let targetTenantId = undefined;
|
|
347
353
|
if (ctx.params.path.tenantId && ((_d = ctx.requestBody) === null || _d === void 0 ? void 0 : _d.tenantId)) {
|
|
348
354
|
errors_2.assert(ctx.params.path.tenantId === ctx.requestBody.tenantId, "TENANT_ID_MISMATCH");
|
|
349
355
|
}
|
|
350
|
-
|
|
356
|
+
targetTenantId = ctx.params.path.tenantId || ((_e = ctx.requestBody) === null || _e === void 0 ? void 0 : _e.tenantId);
|
|
357
|
+
if ((_f = ctx.requestBody) === null || _f === void 0 ? void 0 : _f.idToken) {
|
|
358
|
+
const idToken = (_g = ctx.requestBody) === null || _g === void 0 ? void 0 : _g.idToken;
|
|
359
|
+
const decoded = jsonwebtoken_1.decode(idToken, { complete: true });
|
|
360
|
+
if ((decoded === null || decoded === void 0 ? void 0 : decoded.payload.firebase.tenant) && targetTenantId) {
|
|
361
|
+
errors_2.assert((decoded === null || decoded === void 0 ? void 0 : decoded.payload.firebase.tenant) === targetTenantId, "TENANT_ID_MISMATCH");
|
|
362
|
+
}
|
|
363
|
+
targetTenantId = targetTenantId || (decoded === null || decoded === void 0 ? void 0 : decoded.payload.firebase.tenant);
|
|
364
|
+
}
|
|
351
365
|
return operation(getProjectStateById(targetProjectId, targetTenantId), ctx.requestBody, ctx);
|
|
352
366
|
};
|
|
353
367
|
}
|
|
@@ -459,7 +459,17 @@ class AgentProjectState extends ProjectState {
|
|
|
459
459
|
}
|
|
460
460
|
getTenantProject(tenantId) {
|
|
461
461
|
if (!this.tenantProjectForTenantId.has(tenantId)) {
|
|
462
|
-
this.createTenantWithTenantId(tenantId, {
|
|
462
|
+
this.createTenantWithTenantId(tenantId, {
|
|
463
|
+
tenantId,
|
|
464
|
+
allowPasswordSignup: true,
|
|
465
|
+
disableAuth: false,
|
|
466
|
+
mfaConfig: {
|
|
467
|
+
state: "ENABLED",
|
|
468
|
+
enabledProviders: ["PHONE_SMS"],
|
|
469
|
+
},
|
|
470
|
+
enableAnonymousUser: true,
|
|
471
|
+
enableEmailLinkSignin: true,
|
|
472
|
+
});
|
|
463
473
|
}
|
|
464
474
|
return this.tenantProjectForTenantId.get(tenantId);
|
|
465
475
|
}
|
|
@@ -525,34 +535,43 @@ class TenantProjectState extends ProjectState {
|
|
|
525
535
|
return this._tenantConfig;
|
|
526
536
|
}
|
|
527
537
|
get allowPasswordSignup() {
|
|
528
|
-
|
|
529
|
-
return (_a = this._tenantConfig.allowPasswordSignup) !== null && _a !== void 0 ? _a : true;
|
|
538
|
+
return this._tenantConfig.allowPasswordSignup;
|
|
530
539
|
}
|
|
531
540
|
get disableAuth() {
|
|
532
|
-
|
|
533
|
-
return (_a = this._tenantConfig.disableAuth) !== null && _a !== void 0 ? _a : false;
|
|
541
|
+
return this._tenantConfig.disableAuth;
|
|
534
542
|
}
|
|
535
543
|
get mfaConfig() {
|
|
536
|
-
|
|
537
|
-
return ((_a = this._tenantConfig.mfaConfig) !== null && _a !== void 0 ? _a : {
|
|
538
|
-
state: "ENABLED",
|
|
539
|
-
enabledProviders: ["PHONE_SMS"],
|
|
540
|
-
});
|
|
544
|
+
return this._tenantConfig.mfaConfig;
|
|
541
545
|
}
|
|
542
546
|
get enableAnonymousUser() {
|
|
543
|
-
|
|
544
|
-
return (_a = this._tenantConfig.enableAnonymousUser) !== null && _a !== void 0 ? _a : true;
|
|
547
|
+
return this._tenantConfig.enableAnonymousUser;
|
|
545
548
|
}
|
|
546
549
|
get enableEmailLinkSignin() {
|
|
547
|
-
|
|
548
|
-
return (_a = this._tenantConfig.enableEmailLinkSignin) !== null && _a !== void 0 ? _a : true;
|
|
550
|
+
return this._tenantConfig.enableEmailLinkSignin;
|
|
549
551
|
}
|
|
550
552
|
delete() {
|
|
551
553
|
this.parentProject.deleteTenant(this.tenantId);
|
|
552
554
|
}
|
|
553
555
|
updateTenant(update, updateMask) {
|
|
556
|
+
var _a, _b, _c, _d, _e;
|
|
554
557
|
if (!updateMask) {
|
|
555
|
-
|
|
558
|
+
const mfaConfig = (_a = update.mfaConfig) !== null && _a !== void 0 ? _a : {};
|
|
559
|
+
if (!("state" in mfaConfig)) {
|
|
560
|
+
mfaConfig.state = "DISABLED";
|
|
561
|
+
}
|
|
562
|
+
if (!("enabledProviders" in mfaConfig)) {
|
|
563
|
+
mfaConfig.enabledProviders = [];
|
|
564
|
+
}
|
|
565
|
+
this._tenantConfig = {
|
|
566
|
+
tenantId: this.tenantId,
|
|
567
|
+
name: this.tenantConfig.name,
|
|
568
|
+
allowPasswordSignup: (_b = update.allowPasswordSignup) !== null && _b !== void 0 ? _b : false,
|
|
569
|
+
disableAuth: (_c = update.disableAuth) !== null && _c !== void 0 ? _c : false,
|
|
570
|
+
mfaConfig: mfaConfig,
|
|
571
|
+
enableAnonymousUser: (_d = update.enableAnonymousUser) !== null && _d !== void 0 ? _d : false,
|
|
572
|
+
enableEmailLinkSignin: (_e = update.enableEmailLinkSignin) !== null && _e !== void 0 ? _e : false,
|
|
573
|
+
displayName: update.displayName,
|
|
574
|
+
};
|
|
556
575
|
return this.tenantConfig;
|
|
557
576
|
}
|
|
558
577
|
const paths = updateMask.split(",");
|
|
@@ -33,6 +33,7 @@ var firebaseAppId = query.get('appId');
|
|
|
33
33
|
var apn = query.get('apn');
|
|
34
34
|
var ibi = query.get('ibi');
|
|
35
35
|
var appIdentifier = apn || ibi;
|
|
36
|
+
var isSamlProvider = !!providerId.match(/^saml\./);
|
|
36
37
|
assert(
|
|
37
38
|
appName || clientId || firebaseAppId || appIdentifier,
|
|
38
39
|
'Missing one of appName / clientId / appId / apn / ibi query params.'
|
|
@@ -172,6 +173,19 @@ function finishWithUser(urlEncodedIdToken) {
|
|
|
172
173
|
// Avoid URLSearchParams for browser compatibility.
|
|
173
174
|
url += '?providerId=' + encodeURIComponent(providerId);
|
|
174
175
|
url += '&id_token=' + urlEncodedIdToken;
|
|
176
|
+
|
|
177
|
+
// Save reasonable defaults for SAML providers
|
|
178
|
+
if (isSamlProvider) {
|
|
179
|
+
var email = document.getElementById('email-input').value;
|
|
180
|
+
url += '&SAMLResponse=' + encodeURIComponent(JSON.stringify({
|
|
181
|
+
assertion: {
|
|
182
|
+
subject: {
|
|
183
|
+
nameId: email,
|
|
184
|
+
},
|
|
185
|
+
},
|
|
186
|
+
}));
|
|
187
|
+
}
|
|
188
|
+
|
|
175
189
|
saveAuthEvent({
|
|
176
190
|
type: authType,
|
|
177
191
|
eventId: eventId,
|
|
@@ -50,15 +50,15 @@ exports.DownloadDetails = {
|
|
|
50
50
|
},
|
|
51
51
|
},
|
|
52
52
|
ui: {
|
|
53
|
-
version: "1.6.
|
|
54
|
-
downloadPath: path.join(CACHE_DIR, "ui-v1.6.
|
|
55
|
-
unzipDir: path.join(CACHE_DIR, "ui-v1.6.
|
|
56
|
-
binaryPath: path.join(CACHE_DIR, "ui-v1.6.
|
|
53
|
+
version: "1.6.4",
|
|
54
|
+
downloadPath: path.join(CACHE_DIR, "ui-v1.6.4.zip"),
|
|
55
|
+
unzipDir: path.join(CACHE_DIR, "ui-v1.6.4"),
|
|
56
|
+
binaryPath: path.join(CACHE_DIR, "ui-v1.6.4", "server.bundle.js"),
|
|
57
57
|
opts: {
|
|
58
58
|
cacheDir: CACHE_DIR,
|
|
59
|
-
remoteUrl: "https://storage.googleapis.com/firebase-preview-drop/emulator/ui-v1.6.
|
|
60
|
-
expectedSize:
|
|
61
|
-
expectedChecksum: "
|
|
59
|
+
remoteUrl: "https://storage.googleapis.com/firebase-preview-drop/emulator/ui-v1.6.4.zip",
|
|
60
|
+
expectedSize: 3757300,
|
|
61
|
+
expectedChecksum: "20d4ee71e4ff7527b1843b6a8636142e",
|
|
62
62
|
namePrefix: "ui",
|
|
63
63
|
},
|
|
64
64
|
},
|