firebase-tools 10.7.1 → 10.7.2
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/lib/deploy/functions/build.js +19 -8
- package/lib/deploy/functions/checkIam.js +1 -8
- package/lib/deploy/functions/containerCleaner.js +8 -7
- package/lib/deploy/functions/release/executor.js +1 -1
- package/lib/deploy/functions/runtimes/golang/index.js +3 -0
- package/lib/deploy/functions/runtimes/node/index.js +7 -0
- package/lib/deploy/functions/runtimes/node/parseTriggers.js +108 -1
- package/lib/deploy/functions/validate.js +1 -1
- package/lib/emulator/auth/cloudFunctions.js +6 -2
- package/lib/emulator/auth/operations.js +0 -1
- package/lib/emulator/auth/server.js +8 -1
- package/lib/emulator/auth/state.js +27 -24
- package/lib/emulator/databaseEmulator.js +36 -3
- package/lib/emulator/downloadableEmulators.js +7 -7
- package/lib/emulator/functionsEmulator.js +2 -6
- package/lib/emulator/functionsEmulatorRuntime.js +1 -1
- package/lib/emulator/functionsEmulatorShared.js +3 -0
- package/lib/emulator/functionsEmulatorUtils.js +5 -1
- package/lib/emulator/storage/apis/firebase.js +26 -4
- package/lib/extensions/paramHelper.js +2 -0
- package/lib/extensions/warnings.js +8 -1
- package/lib/gcp/cloudfunctionsv2.js +13 -6
- package/npm-shrinkwrap.json +214 -527
- package/package.json +3 -3
|
@@ -1,11 +1,25 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.resolveBackend = void 0;
|
|
3
|
+
exports.resolveBackend = exports.of = exports.empty = void 0;
|
|
4
4
|
const backend = require("./backend");
|
|
5
5
|
const proto = require("../../gcp/proto");
|
|
6
6
|
const api = require("../../.../../api");
|
|
7
7
|
const error_1 = require("../../error");
|
|
8
8
|
const functional_1 = require("../../functional");
|
|
9
|
+
function empty() {
|
|
10
|
+
return {
|
|
11
|
+
requiredAPIs: [],
|
|
12
|
+
endpoints: {},
|
|
13
|
+
params: [],
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
exports.empty = empty;
|
|
17
|
+
function of(endpoints) {
|
|
18
|
+
const build = empty();
|
|
19
|
+
build.endpoints = endpoints;
|
|
20
|
+
return build;
|
|
21
|
+
}
|
|
22
|
+
exports.of = of;
|
|
9
23
|
function resolveInt(from) {
|
|
10
24
|
if (from == null) {
|
|
11
25
|
return 0;
|
|
@@ -59,23 +73,20 @@ function resolveBackend(build) {
|
|
|
59
73
|
else {
|
|
60
74
|
timeout = 60;
|
|
61
75
|
}
|
|
62
|
-
const bkEndpoint = Object.assign({ id: endpointId, project:
|
|
76
|
+
const bkEndpoint = Object.assign({ id: endpointId, project: endpoint.project, region: region, entryPoint: endpoint.entryPoint, platform: endpoint.platform, runtime: endpoint.runtime, timeoutSeconds: timeout }, trigger);
|
|
63
77
|
proto.renameIfPresent(bkEndpoint, endpoint, "maxInstances", "maxInstances", resolveInt);
|
|
64
78
|
proto.renameIfPresent(bkEndpoint, endpoint, "minInstances", "minInstances", resolveInt);
|
|
65
79
|
proto.renameIfPresent(bkEndpoint, endpoint, "concurrency", "concurrency", resolveInt);
|
|
66
|
-
proto.copyIfPresent(bkEndpoint, endpoint, "ingressSettings");
|
|
80
|
+
proto.copyIfPresent(bkEndpoint, endpoint, "ingressSettings", "availableMemoryMb", "environmentVariables", "labels");
|
|
67
81
|
if (endpoint.vpc) {
|
|
68
82
|
bkEndpoint.vpc = {
|
|
69
|
-
connector: resolveString(endpoint.vpc.connector),
|
|
70
|
-
egressSettings: endpoint.vpc.egressSettings,
|
|
83
|
+
connector: resolveString(endpoint.vpc.connector).replace("$REGION", region),
|
|
71
84
|
};
|
|
85
|
+
proto.copyIfPresent(bkEndpoint.vpc, endpoint.vpc, "egressSettings");
|
|
72
86
|
}
|
|
73
87
|
if (endpoint.serviceAccount) {
|
|
74
88
|
bkEndpoint.serviceAccountEmail = endpoint.serviceAccount;
|
|
75
89
|
}
|
|
76
|
-
else {
|
|
77
|
-
bkEndpoint.serviceAccountEmail = "default";
|
|
78
|
-
}
|
|
79
90
|
bkEndpoints.push(bkEndpoint);
|
|
80
91
|
}
|
|
81
92
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.ensureServiceAgentRoles = exports.mergeBindings = exports.
|
|
3
|
+
exports.ensureServiceAgentRoles = exports.mergeBindings = exports.obtainDefaultComputeServiceAgentBindings = exports.obtainPubSubServiceAgentBindings = exports.obtainBinding = exports.checkHttpIam = exports.checkServiceAccountIam = exports.EVENTARC_EVENT_RECEIVER_ROLE = exports.RUN_INVOKER_ROLE = exports.SERVICE_ACCOUNT_TOKEN_CREATOR_ROLE = void 0;
|
|
4
4
|
const cli_color_1 = require("cli-color");
|
|
5
5
|
const logger_1 = require("../../logger");
|
|
6
6
|
const functionsDeployHelper_1 = require("./functionsDeployHelper");
|
|
@@ -16,7 +16,6 @@ const PERMISSION = "cloudfunctions.functions.setIamPolicy";
|
|
|
16
16
|
exports.SERVICE_ACCOUNT_TOKEN_CREATOR_ROLE = "roles/iam.serviceAccountTokenCreator";
|
|
17
17
|
exports.RUN_INVOKER_ROLE = "roles/run.invoker";
|
|
18
18
|
exports.EVENTARC_EVENT_RECEIVER_ROLE = "roles/eventarc.eventReceiver";
|
|
19
|
-
exports.EVENTARC_SERVICE_AGENT_ROLE = "roles/eventarc.serviceAgent";
|
|
20
19
|
async function checkServiceAccountIam(projectId) {
|
|
21
20
|
const saEmail = `${projectId}@appspot.gserviceaccount.com`;
|
|
22
21
|
let passed = false;
|
|
@@ -101,11 +100,6 @@ function obtainDefaultComputeServiceAgentBindings(projectNumber, existingPolicy)
|
|
|
101
100
|
return [invokerBinding, eventReceiverBinding];
|
|
102
101
|
}
|
|
103
102
|
exports.obtainDefaultComputeServiceAgentBindings = obtainDefaultComputeServiceAgentBindings;
|
|
104
|
-
function obtainEventarcServiceAgentBindings(projectNumber, existingPolicy) {
|
|
105
|
-
const eventarcServiceAgent = `serviceAccount:service-${projectNumber}@gcp-sa-eventarc.iam.gserviceaccount.com`;
|
|
106
|
-
return [obtainBinding(existingPolicy, eventarcServiceAgent, exports.EVENTARC_SERVICE_AGENT_ROLE)];
|
|
107
|
-
}
|
|
108
|
-
exports.obtainEventarcServiceAgentBindings = obtainEventarcServiceAgentBindings;
|
|
109
103
|
function mergeBindings(policy, allRequiredBindings) {
|
|
110
104
|
for (const requiredBindings of allRequiredBindings) {
|
|
111
105
|
if (requiredBindings.length === 0) {
|
|
@@ -149,7 +143,6 @@ async function ensureServiceAgentRoles(projectNumber, want, have) {
|
|
|
149
143
|
if (haveServices.length === 0) {
|
|
150
144
|
allRequiredBindings.push(obtainPubSubServiceAgentBindings(projectNumber, policy));
|
|
151
145
|
allRequiredBindings.push(obtainDefaultComputeServiceAgentBindings(projectNumber, policy));
|
|
152
|
-
allRequiredBindings.push(obtainEventarcServiceAgentBindings(projectNumber, policy));
|
|
153
146
|
}
|
|
154
147
|
if (!allRequiredBindings.find((bindings) => bindings.length > 0)) {
|
|
155
148
|
return;
|
|
@@ -251,13 +251,14 @@ class DockerHelper {
|
|
|
251
251
|
this.client = new docker.Client(origin);
|
|
252
252
|
}
|
|
253
253
|
async ls(path) {
|
|
254
|
-
if (!this.cache
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
254
|
+
if (!(path in this.cache)) {
|
|
255
|
+
this.cache[path] = retry(() => this.client.listTags(path)).then((res) => {
|
|
256
|
+
return {
|
|
257
|
+
tags: res.tags,
|
|
258
|
+
digests: Object.keys(res.manifest),
|
|
259
|
+
children: res.child,
|
|
260
|
+
};
|
|
261
|
+
});
|
|
261
262
|
}
|
|
262
263
|
return this.cache[path];
|
|
263
264
|
}
|
|
@@ -13,7 +13,7 @@ async function handler(op) {
|
|
|
13
13
|
((_b = (_a = err.context) === null || _a === void 0 ? void 0 : _a.response) === null || _b === void 0 ? void 0 : _b.statusCode) ||
|
|
14
14
|
((_c = err.original) === null || _c === void 0 ? void 0 : _c.code) ||
|
|
15
15
|
((_f = (_e = (_d = err.original) === null || _d === void 0 ? void 0 : _d.context) === null || _e === void 0 ? void 0 : _e.response) === null || _f === void 0 ? void 0 : _f.statusCode);
|
|
16
|
-
if (code === 429 || code === 409) {
|
|
16
|
+
if (code === 429 || code === 409 || code === 503) {
|
|
17
17
|
throw err;
|
|
18
18
|
}
|
|
19
19
|
op.error = err;
|
|
@@ -105,6 +105,9 @@ class Delegate {
|
|
|
105
105
|
return p;
|
|
106
106
|
});
|
|
107
107
|
}
|
|
108
|
+
async discoverBuild(configValues, envs) {
|
|
109
|
+
return { requiredAPIs: [], endpoints: {}, params: [] };
|
|
110
|
+
}
|
|
108
111
|
async discoverSpec(configValues, envs) {
|
|
109
112
|
let discovered = await discovery.detectFromYaml(this.sourceDir, this.projectId, this.runtime);
|
|
110
113
|
if (!discovered) {
|
|
@@ -84,6 +84,10 @@ class Delegate {
|
|
|
84
84
|
}
|
|
85
85
|
async discoverSpec(config, env) {
|
|
86
86
|
if (previews_1.previews.functionsv2) {
|
|
87
|
+
if (!semver.valid(this.sdkVersion)) {
|
|
88
|
+
logger_1.logger.debug(`Could not parse firebase-functions version '${this.sdkVersion}' into semver. Falling back to parseTriggers.`);
|
|
89
|
+
return parseTriggers.discoverBackend(this.projectId, this.sourceDir, this.runtime, config, env);
|
|
90
|
+
}
|
|
87
91
|
if (semver.lt(this.sdkVersion, MIN_FUNCTIONS_SDK_VERSION)) {
|
|
88
92
|
(0, utils_1.logLabeledWarning)("functions", `You are using an old version of firebase-functions SDK (${this.sdkVersion}). ` +
|
|
89
93
|
`Please update firebase-functions SDK to >=${MIN_FUNCTIONS_SDK_VERSION}`);
|
|
@@ -106,5 +110,8 @@ class Delegate {
|
|
|
106
110
|
}
|
|
107
111
|
return parseTriggers.discoverBackend(this.projectId, this.sourceDir, this.runtime, config, env);
|
|
108
112
|
}
|
|
113
|
+
async discoverBuild(config, env) {
|
|
114
|
+
return parseTriggers.discoverBuild(this.projectId, this.sourceDir, this.runtime, config, env);
|
|
115
|
+
}
|
|
109
116
|
}
|
|
110
117
|
exports.Delegate = Delegate;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.addResourcesToBackend = exports.mergeRequiredAPIs = exports.discoverBackend = exports.useStrategy = void 0;
|
|
3
|
+
exports.addResourcesToBackend = exports.addResourcesToBuild = exports.mergeRequiredAPIs = exports.discoverBackend = exports.discoverBuild = exports.useStrategy = void 0;
|
|
4
4
|
const path = require("path");
|
|
5
5
|
const _ = require("lodash");
|
|
6
6
|
const child_process_1 = require("child_process");
|
|
@@ -49,6 +49,19 @@ function useStrategy(context) {
|
|
|
49
49
|
return Promise.resolve(true);
|
|
50
50
|
}
|
|
51
51
|
exports.useStrategy = useStrategy;
|
|
52
|
+
async function discoverBuild(projectId, sourceDir, runtime, configValues, envs) {
|
|
53
|
+
const triggerAnnotations = await parseTriggers(projectId, sourceDir, configValues, envs);
|
|
54
|
+
const want = {
|
|
55
|
+
requiredAPIs: [],
|
|
56
|
+
endpoints: {},
|
|
57
|
+
params: [],
|
|
58
|
+
};
|
|
59
|
+
for (const annotation of triggerAnnotations) {
|
|
60
|
+
addResourcesToBuild(projectId, runtime, annotation, want);
|
|
61
|
+
}
|
|
62
|
+
return want;
|
|
63
|
+
}
|
|
64
|
+
exports.discoverBuild = discoverBuild;
|
|
52
65
|
async function discoverBackend(projectId, sourceDir, runtime, configValues, envs) {
|
|
53
66
|
const triggerAnnotations = await parseTriggers(projectId, sourceDir, configValues, envs);
|
|
54
67
|
const want = Object.assign(Object.assign({}, backend.empty()), { environmentVariables: envs });
|
|
@@ -74,6 +87,100 @@ function mergeRequiredAPIs(backend) {
|
|
|
74
87
|
backend.requiredAPIs = merged;
|
|
75
88
|
}
|
|
76
89
|
exports.mergeRequiredAPIs = mergeRequiredAPIs;
|
|
90
|
+
function addResourcesToBuild(projectId, runtime, annotation, want) {
|
|
91
|
+
var _a;
|
|
92
|
+
Object.freeze(annotation);
|
|
93
|
+
const regions = annotation.regions || [api.functionsDefaultRegion];
|
|
94
|
+
let triggered;
|
|
95
|
+
const triggerCount = +!!annotation.httpsTrigger +
|
|
96
|
+
+!!annotation.eventTrigger +
|
|
97
|
+
+!!annotation.taskQueueTrigger +
|
|
98
|
+
+!!annotation.blockingTrigger;
|
|
99
|
+
if (triggerCount !== 1) {
|
|
100
|
+
throw new error_1.FirebaseError("Unexpected annotation generated by the Firebase Functions SDK. This should never happen.");
|
|
101
|
+
}
|
|
102
|
+
if (annotation.taskQueueTrigger) {
|
|
103
|
+
want.requiredAPIs.push({
|
|
104
|
+
api: "cloudtasks.googleapis.com",
|
|
105
|
+
reason: "Needed for task queue functions.",
|
|
106
|
+
});
|
|
107
|
+
triggered = {
|
|
108
|
+
taskQueueTrigger: {},
|
|
109
|
+
};
|
|
110
|
+
proto.copyIfPresent(triggered.taskQueueTrigger, annotation.taskQueueTrigger, "invoker");
|
|
111
|
+
proto.copyIfPresent(triggered.taskQueueTrigger, annotation.taskQueueTrigger, "rateLimits");
|
|
112
|
+
if (annotation.taskQueueTrigger.retryConfig) {
|
|
113
|
+
triggered.taskQueueTrigger.retryConfig = Object.assign(annotation.taskQueueTrigger.retryConfig, {
|
|
114
|
+
maxRetryDurationSeconds: proto.secondsFromDuration(annotation.taskQueueTrigger.retryConfig.maxRetryDuration || "0"),
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
else if (annotation.httpsTrigger) {
|
|
119
|
+
if ((_a = annotation.labels) === null || _a === void 0 ? void 0 : _a["deployment-callable"]) {
|
|
120
|
+
delete annotation.labels["deployment-callable"];
|
|
121
|
+
triggered = { callableTrigger: {} };
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
const trigger = {};
|
|
125
|
+
if (annotation.failurePolicy) {
|
|
126
|
+
logger_1.logger.warn(`Ignoring retry policy for HTTPS function ${annotation.name}`);
|
|
127
|
+
}
|
|
128
|
+
if (annotation.httpsTrigger.invoker) {
|
|
129
|
+
trigger.invoker = annotation.httpsTrigger.invoker[0];
|
|
130
|
+
}
|
|
131
|
+
triggered = { httpsTrigger: trigger };
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
else if (annotation.schedule) {
|
|
135
|
+
want.requiredAPIs.push({
|
|
136
|
+
api: "cloudscheduler.googleapis.com",
|
|
137
|
+
reason: "Needed for scheduled functions.",
|
|
138
|
+
});
|
|
139
|
+
triggered = {
|
|
140
|
+
scheduleTrigger: {
|
|
141
|
+
schedule: annotation.schedule.schedule,
|
|
142
|
+
timeZone: annotation.schedule.timeZone || "what's the default timezone?",
|
|
143
|
+
retryConfig: annotation.schedule.retryConfig || {},
|
|
144
|
+
},
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
else if (annotation.blockingTrigger) {
|
|
148
|
+
if (events.v1.AUTH_BLOCKING_EVENTS.includes(annotation.blockingTrigger.eventType)) {
|
|
149
|
+
want.requiredAPIs.push({
|
|
150
|
+
api: "identitytoolkit.googleapis.com",
|
|
151
|
+
reason: "Needed for auth blocking functions.",
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
triggered = {
|
|
155
|
+
blockingTrigger: {
|
|
156
|
+
eventType: annotation.blockingTrigger.eventType,
|
|
157
|
+
},
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
else {
|
|
161
|
+
triggered = {
|
|
162
|
+
eventTrigger: {
|
|
163
|
+
eventType: annotation.eventTrigger.eventType,
|
|
164
|
+
eventFilters: { resource: annotation.eventTrigger.resource },
|
|
165
|
+
retry: !!annotation.failurePolicy,
|
|
166
|
+
},
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
const endpointId = annotation.name;
|
|
170
|
+
const endpoint = Object.assign({ platform: annotation.platform || "gcfv1", region: regions, project: projectId, entryPoint: annotation.entryPoint, runtime: runtime, serviceAccount: annotation.serviceAccountEmail || "default" }, triggered);
|
|
171
|
+
if (annotation.vpcConnector != null) {
|
|
172
|
+
let maybeId = annotation.vpcConnector;
|
|
173
|
+
if (maybeId && !maybeId.includes("/")) {
|
|
174
|
+
maybeId = `projects/${projectId}/locations/$REGION/connectors/${maybeId}`;
|
|
175
|
+
}
|
|
176
|
+
endpoint.vpc = { connector: maybeId };
|
|
177
|
+
proto.renameIfPresent(endpoint.vpc, annotation, "egressSettings", "vpcConnectorEgressSettings");
|
|
178
|
+
}
|
|
179
|
+
proto.copyIfPresent(endpoint, annotation, "concurrency", "labels", "ingressSettings", "maxInstances", "minInstances", "availableMemoryMb");
|
|
180
|
+
proto.renameIfPresent(endpoint, annotation, "timeoutSeconds", "timeout", proto.secondsFromDuration);
|
|
181
|
+
want.endpoints[endpointId] = endpoint;
|
|
182
|
+
}
|
|
183
|
+
exports.addResourcesToBuild = addResourcesToBuild;
|
|
77
184
|
function addResourcesToBackend(projectId, runtime, annotation, want) {
|
|
78
185
|
var _a;
|
|
79
186
|
Object.freeze(annotation);
|
|
@@ -86,7 +86,7 @@ function functionIdsAreValid(functions) {
|
|
|
86
86
|
return fn.platform === "gcfv2" && !v2FunctionName.test(fn.id);
|
|
87
87
|
});
|
|
88
88
|
if (invalidV2Ids.length !== 0) {
|
|
89
|
-
const msg = `${invalidV2Ids.map((f) => f.id).join(", ")} v2 function name(s) can only
|
|
89
|
+
const msg = `${invalidV2Ids.map((f) => f.id).join(", ")} v2 function name(s) can only contain lower ` +
|
|
90
90
|
`case letters, numbers, hyphens, and not exceed 62 characters in length`;
|
|
91
91
|
throw new error_1.FirebaseError(msg);
|
|
92
92
|
}
|
|
@@ -62,8 +62,12 @@ class AuthCloudFunction {
|
|
|
62
62
|
phoneNumber: user.phoneNumber,
|
|
63
63
|
disabled: user.disabled,
|
|
64
64
|
metadata: {
|
|
65
|
-
creationTime: user.createdAt
|
|
66
|
-
|
|
65
|
+
creationTime: user.createdAt
|
|
66
|
+
? new Date(parseInt(user.createdAt, 10)).toISOString()
|
|
67
|
+
: undefined,
|
|
68
|
+
lastSignInTime: user.lastLoginAt
|
|
69
|
+
? new Date(parseInt(user.lastLoginAt, 10)).toISOString()
|
|
70
|
+
: undefined,
|
|
67
71
|
},
|
|
68
72
|
customClaims: JSON.parse(user.customAttributes || "{}"),
|
|
69
73
|
providerData: user.providerUserInfo,
|
|
@@ -1210,7 +1210,6 @@ function grantToken(state, reqBody) {
|
|
|
1210
1210
|
(0, errors_1.assert)(reqBody.grantType === "refresh_token", "INVALID_GRANT_TYPE");
|
|
1211
1211
|
(0, errors_1.assert)(reqBody.refreshToken, "MISSING_REFRESH_TOKEN");
|
|
1212
1212
|
const refreshTokenRecord = state.validateRefreshToken(reqBody.refreshToken);
|
|
1213
|
-
(0, errors_1.assert)(refreshTokenRecord, "INVALID_REFRESH_TOKEN");
|
|
1214
1213
|
(0, errors_1.assert)(!refreshTokenRecord.user.disabled, "USER_DISABLED");
|
|
1215
1214
|
const tokens = issueTokens(state, refreshTokenRecord.user, refreshTokenRecord.provider, {
|
|
1216
1215
|
extraClaims: refreshTokenRecord.extraClaims,
|
|
@@ -342,7 +342,7 @@ function toExegesisController(ops, getProjectStateById) {
|
|
|
342
342
|
}
|
|
343
343
|
function toExegesisOperation(operation) {
|
|
344
344
|
return (ctx) => {
|
|
345
|
-
var _a, _b, _c, _d, _e, _f, _g;
|
|
345
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
346
346
|
let targetProjectId = ctx.params.path.targetProjectId || ((_a = ctx.requestBody) === null || _a === void 0 ? void 0 : _a.targetProjectId);
|
|
347
347
|
if (targetProjectId) {
|
|
348
348
|
if ((_b = ctx.api.operationObject.security) === null || _b === void 0 ? void 0 : _b.some((sec) => sec.Oauth2)) {
|
|
@@ -365,6 +365,13 @@ function toExegesisController(ops, getProjectStateById) {
|
|
|
365
365
|
}
|
|
366
366
|
targetTenantId = targetTenantId || (decoded === null || decoded === void 0 ? void 0 : decoded.payload.firebase.tenant);
|
|
367
367
|
}
|
|
368
|
+
if ((_h = ctx.requestBody) === null || _h === void 0 ? void 0 : _h.refreshToken) {
|
|
369
|
+
const refreshTokenRecord = (0, state_1.decodeRefreshToken)(ctx.requestBody.refreshToken);
|
|
370
|
+
if (refreshTokenRecord.tenantId && targetTenantId) {
|
|
371
|
+
(0, errors_2.assert)(refreshTokenRecord.tenantId === targetTenantId, "TENANT_ID_MISMATCH: ((Refresh token tenant ID does not match target tenant ID.))");
|
|
372
|
+
}
|
|
373
|
+
targetTenantId = targetTenantId || refreshTokenRecord.tenantId;
|
|
374
|
+
}
|
|
368
375
|
return operation(getProjectStateById(targetProjectId, targetTenantId), ctx.requestBody, ctx);
|
|
369
376
|
};
|
|
370
377
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.BlockingFunctionEvents = exports.UsageMode = exports.TenantProjectState = exports.AgentProjectState = exports.ProjectState = exports.SIGNIN_METHOD_EMAIL_LINK = exports.PROVIDER_GAME_CENTER = exports.PROVIDER_CUSTOM = exports.PROVIDER_ANONYMOUS = exports.PROVIDER_PHONE = exports.PROVIDER_PASSWORD = void 0;
|
|
3
|
+
exports.decodeRefreshToken = exports.encodeRefreshToken = exports.BlockingFunctionEvents = exports.UsageMode = exports.TenantProjectState = exports.AgentProjectState = exports.ProjectState = exports.SIGNIN_METHOD_EMAIL_LINK = exports.PROVIDER_GAME_CENTER = exports.PROVIDER_CUSTOM = exports.PROVIDER_ANONYMOUS = exports.PROVIDER_PHONE = exports.PROVIDER_PASSWORD = void 0;
|
|
4
4
|
const utils_1 = require("./utils");
|
|
5
5
|
const cloudFunctions_1 = require("./cloudFunctions");
|
|
6
6
|
const errors_1 = require("./errors");
|
|
@@ -19,8 +19,6 @@ class ProjectState {
|
|
|
19
19
|
this.localIdForPhoneNumber = new Map();
|
|
20
20
|
this.localIdsForProviderEmail = new Map();
|
|
21
21
|
this.userIdForProviderRawId = new Map();
|
|
22
|
-
this.refreshTokens = new Map();
|
|
23
|
-
this.refreshTokensForLocalId = new Map();
|
|
24
22
|
this.oobs = new Map();
|
|
25
23
|
this.verificationCodes = new Map();
|
|
26
24
|
this.temporaryProofs = new Map();
|
|
@@ -73,13 +71,6 @@ class ProjectState {
|
|
|
73
71
|
deleteUser(user) {
|
|
74
72
|
this.users.delete(user.localId);
|
|
75
73
|
this.removeUserFromIndex(user);
|
|
76
|
-
const refreshTokens = this.refreshTokensForLocalId.get(user.localId);
|
|
77
|
-
if (refreshTokens) {
|
|
78
|
-
this.refreshTokensForLocalId.delete(user.localId);
|
|
79
|
-
for (const refreshToken of refreshTokens) {
|
|
80
|
-
this.refreshTokens.delete(refreshToken);
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
74
|
this.authCloudFunction.dispatch("delete", user);
|
|
84
75
|
}
|
|
85
76
|
updateUserByLocalId(localId, fields, options = {}) {
|
|
@@ -282,26 +273,23 @@ class ProjectState {
|
|
|
282
273
|
}
|
|
283
274
|
createRefreshTokenFor(userInfo, provider, { extraClaims = {}, secondFactor, } = {}) {
|
|
284
275
|
const localId = userInfo.localId;
|
|
285
|
-
const
|
|
286
|
-
|
|
276
|
+
const refreshTokenRecord = {
|
|
277
|
+
_AuthEmulatorRefreshToken: "DO NOT MODIFY",
|
|
287
278
|
localId,
|
|
288
279
|
provider,
|
|
289
280
|
extraClaims,
|
|
281
|
+
projectId: this.projectId,
|
|
290
282
|
secondFactor,
|
|
291
283
|
tenantId: userInfo.tenantId,
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
if (!refreshTokens) {
|
|
295
|
-
refreshTokens = new Set();
|
|
296
|
-
this.refreshTokensForLocalId.set(localId, refreshTokens);
|
|
297
|
-
}
|
|
298
|
-
refreshTokens.add(refreshToken);
|
|
284
|
+
};
|
|
285
|
+
const refreshToken = encodeRefreshToken(refreshTokenRecord);
|
|
299
286
|
return refreshToken;
|
|
300
287
|
}
|
|
301
288
|
validateRefreshToken(refreshToken) {
|
|
302
|
-
const record =
|
|
303
|
-
|
|
304
|
-
|
|
289
|
+
const record = decodeRefreshToken(refreshToken);
|
|
290
|
+
(0, errors_1.assert)(record.projectId === this.projectId, "INVALID_REFRESH_TOKEN");
|
|
291
|
+
if (this instanceof TenantProjectState) {
|
|
292
|
+
(0, errors_1.assert)(record.tenantId === this.tenantId, "TENANT_ID_MISMATCH");
|
|
305
293
|
}
|
|
306
294
|
return {
|
|
307
295
|
user: this.getUserByLocalIdAssertingExists(record.localId),
|
|
@@ -356,8 +344,6 @@ class ProjectState {
|
|
|
356
344
|
this.localIdForPhoneNumber.clear();
|
|
357
345
|
this.localIdsForProviderEmail.clear();
|
|
358
346
|
this.userIdForProviderRawId.clear();
|
|
359
|
-
this.refreshTokens.clear();
|
|
360
|
-
this.refreshTokensForLocalId.clear();
|
|
361
347
|
}
|
|
362
348
|
getUserCount() {
|
|
363
349
|
return this.users.size;
|
|
@@ -617,6 +603,23 @@ var BlockingFunctionEvents;
|
|
|
617
603
|
BlockingFunctionEvents["BEFORE_CREATE"] = "beforeCreate";
|
|
618
604
|
BlockingFunctionEvents["BEFORE_SIGN_IN"] = "beforeSignIn";
|
|
619
605
|
})(BlockingFunctionEvents = exports.BlockingFunctionEvents || (exports.BlockingFunctionEvents = {}));
|
|
606
|
+
function encodeRefreshToken(refreshTokenRecord) {
|
|
607
|
+
return Buffer.from(JSON.stringify(refreshTokenRecord), "utf8").toString("base64");
|
|
608
|
+
}
|
|
609
|
+
exports.encodeRefreshToken = encodeRefreshToken;
|
|
610
|
+
function decodeRefreshToken(refreshTokenString) {
|
|
611
|
+
let refreshTokenRecord;
|
|
612
|
+
try {
|
|
613
|
+
const json = Buffer.from(refreshTokenString, "base64").toString("utf8");
|
|
614
|
+
refreshTokenRecord = JSON.parse(json);
|
|
615
|
+
}
|
|
616
|
+
catch (_a) {
|
|
617
|
+
throw new errors_1.BadRequestError("INVALID_REFRESH_TOKEN");
|
|
618
|
+
}
|
|
619
|
+
(0, errors_1.assert)(refreshTokenRecord._AuthEmulatorRefreshToken, "INVALID_REFRESH_TOKEN");
|
|
620
|
+
return refreshTokenRecord;
|
|
621
|
+
}
|
|
622
|
+
exports.decodeRefreshToken = decodeRefreshToken;
|
|
620
623
|
function getProviderEmailsForUser(user) {
|
|
621
624
|
var _a;
|
|
622
625
|
const emails = new Set();
|
|
@@ -56,7 +56,15 @@ class DatabaseEmulator {
|
|
|
56
56
|
if (!c.instance) {
|
|
57
57
|
continue;
|
|
58
58
|
}
|
|
59
|
-
|
|
59
|
+
try {
|
|
60
|
+
await this.updateRules(c.instance, c.rules);
|
|
61
|
+
}
|
|
62
|
+
catch (e) {
|
|
63
|
+
const rulesError = this.prettyPrintRulesError(c.rules, e);
|
|
64
|
+
this.logger.logLabeled("WARN", "database", rulesError);
|
|
65
|
+
this.logger.logLabeled("WARN", "database", "Failed to update rules");
|
|
66
|
+
throw new error_1.FirebaseError(`Failed to load initial ${constants_1.Constants.description(this.getName())} rules:\n${rulesError}`);
|
|
67
|
+
}
|
|
60
68
|
}
|
|
61
69
|
}
|
|
62
70
|
}
|
|
@@ -114,6 +122,7 @@ class DatabaseEmulator {
|
|
|
114
122
|
});
|
|
115
123
|
}
|
|
116
124
|
async updateRules(instance, rulesPath) {
|
|
125
|
+
var _a;
|
|
117
126
|
const rulesExt = path.extname(rulesPath);
|
|
118
127
|
const content = rulesExt === ".bolt"
|
|
119
128
|
? parseBoltRules(rulesPath).toString()
|
|
@@ -131,12 +140,36 @@ class DatabaseEmulator {
|
|
|
131
140
|
if (e.context && e.context.body) {
|
|
132
141
|
throw e.context.body.error;
|
|
133
142
|
}
|
|
134
|
-
throw e.original;
|
|
143
|
+
throw (_a = e.original) !== null && _a !== void 0 ? _a : e;
|
|
135
144
|
}
|
|
136
145
|
}
|
|
137
146
|
prettyPrintRulesError(filePath, error) {
|
|
147
|
+
let errStr;
|
|
148
|
+
switch (typeof error) {
|
|
149
|
+
case "string":
|
|
150
|
+
errStr = error;
|
|
151
|
+
break;
|
|
152
|
+
case "object":
|
|
153
|
+
if (error != null && "message" in error) {
|
|
154
|
+
const message = error.message;
|
|
155
|
+
errStr = `${message}`;
|
|
156
|
+
if (typeof message === "string") {
|
|
157
|
+
try {
|
|
158
|
+
const parsed = JSON.parse(message);
|
|
159
|
+
if (typeof parsed === "object" && parsed.error) {
|
|
160
|
+
errStr = `${parsed.error}`;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
catch (_) {
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
break;
|
|
167
|
+
}
|
|
168
|
+
default:
|
|
169
|
+
errStr = `Unknown error: ${JSON.stringify(error)}`;
|
|
170
|
+
}
|
|
138
171
|
const relativePath = path.relative(process.cwd(), filePath);
|
|
139
|
-
return `${clc.cyan(relativePath)}:${
|
|
172
|
+
return `${clc.cyan(relativePath)}:${errStr.trim()}`;
|
|
140
173
|
}
|
|
141
174
|
}
|
|
142
175
|
exports.DatabaseEmulator = DatabaseEmulator;
|
|
@@ -83,15 +83,15 @@ exports.DownloadDetails = {
|
|
|
83
83
|
},
|
|
84
84
|
}
|
|
85
85
|
: {
|
|
86
|
-
version: "1.6.
|
|
87
|
-
downloadPath: path.join(CACHE_DIR, "ui-v1.6.
|
|
88
|
-
unzipDir: path.join(CACHE_DIR, "ui-v1.6.
|
|
89
|
-
binaryPath: path.join(CACHE_DIR, "ui-v1.6.
|
|
86
|
+
version: "1.6.6",
|
|
87
|
+
downloadPath: path.join(CACHE_DIR, "ui-v1.6.6.zip"),
|
|
88
|
+
unzipDir: path.join(CACHE_DIR, "ui-v1.6.6"),
|
|
89
|
+
binaryPath: path.join(CACHE_DIR, "ui-v1.6.6", "server.bundle.js"),
|
|
90
90
|
opts: {
|
|
91
91
|
cacheDir: CACHE_DIR,
|
|
92
|
-
remoteUrl: "https://storage.googleapis.com/firebase-preview-drop/emulator/ui-v1.6.
|
|
93
|
-
expectedSize:
|
|
94
|
-
expectedChecksum: "
|
|
92
|
+
remoteUrl: "https://storage.googleapis.com/firebase-preview-drop/emulator/ui-v1.6.6.zip",
|
|
93
|
+
expectedSize: 3817247,
|
|
94
|
+
expectedChecksum: "c80a3f0ae1e3f682ace0a18a9cdd2861",
|
|
95
95
|
namePrefix: "ui",
|
|
96
96
|
},
|
|
97
97
|
},
|
|
@@ -223,7 +223,6 @@ class FunctionsEmulator {
|
|
|
223
223
|
return Promise.resolve();
|
|
224
224
|
}
|
|
225
225
|
async connect() {
|
|
226
|
-
const loadTriggerPromises = [];
|
|
227
226
|
for (const backend of this.args.emulatableBackends) {
|
|
228
227
|
this.logger.logLabeled("BULLET", "functions", `Watching "${backend.functionsDir}" for Cloud Functions...`);
|
|
229
228
|
const watcher = chokidar.watch(backend.functionsDir, {
|
|
@@ -239,9 +238,8 @@ class FunctionsEmulator {
|
|
|
239
238
|
this.logger.log("DEBUG", `File ${filePath} changed, reloading triggers`);
|
|
240
239
|
return debouncedLoadTriggers();
|
|
241
240
|
});
|
|
242
|
-
|
|
241
|
+
await this.loadTriggers(backend, true);
|
|
243
242
|
}
|
|
244
|
-
await Promise.all(loadTriggerPromises);
|
|
245
243
|
await this.performPostLoadOperations();
|
|
246
244
|
return;
|
|
247
245
|
}
|
|
@@ -853,11 +851,9 @@ class FunctionsEmulator {
|
|
|
853
851
|
}
|
|
854
852
|
async reloadTriggers() {
|
|
855
853
|
this.triggerGeneration++;
|
|
856
|
-
const loadTriggerPromises = [];
|
|
857
854
|
for (const backend of this.args.emulatableBackends) {
|
|
858
|
-
|
|
855
|
+
await this.loadTriggers(backend);
|
|
859
856
|
}
|
|
860
|
-
await Promise.all(loadTriggerPromises);
|
|
861
857
|
await this.performPostLoadOperations();
|
|
862
858
|
return;
|
|
863
859
|
}
|
|
@@ -213,7 +213,7 @@ function initializeNetworkFiltering(frb) {
|
|
|
213
213
|
})
|
|
214
214
|
.filter((v) => v);
|
|
215
215
|
const href = (hrefs.length && hrefs[0]) || "";
|
|
216
|
-
if (href && !history[href] && !
|
|
216
|
+
if (href && !history[href] && !(0, functionsEmulatorUtils_1.isLocalHost)(href)) {
|
|
217
217
|
history[href] = true;
|
|
218
218
|
if (href.indexOf("googleapis.com") !== -1) {
|
|
219
219
|
new types_1.EmulatorLog("SYSTEM", "googleapis-network-access", "", {
|
|
@@ -106,6 +106,9 @@ function emulatedFunctionsFromEndpoints(endpoints) {
|
|
|
106
106
|
options: endpoint.blockingTrigger.options || {},
|
|
107
107
|
};
|
|
108
108
|
}
|
|
109
|
+
else if (backend.isTaskQueueTriggered(endpoint)) {
|
|
110
|
+
def.httpsTrigger = {};
|
|
111
|
+
}
|
|
109
112
|
else {
|
|
110
113
|
}
|
|
111
114
|
regionDefinitions.push(def);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.compareVersionStrings = exports.parseVersionString = exports.parseRuntimeVersion = exports.removePathSegments = exports.trimSlashes = exports.isValidWildcardMatch = exports.extractParamsFromPath = void 0;
|
|
3
|
+
exports.isLocalHost = exports.compareVersionStrings = exports.parseVersionString = exports.parseRuntimeVersion = exports.removePathSegments = exports.trimSlashes = exports.isValidWildcardMatch = exports.extractParamsFromPath = void 0;
|
|
4
4
|
const wildcardRegex = new RegExp("{[^/{}]*}");
|
|
5
5
|
const wildcardKeyRegex = new RegExp("^{(.+)}$");
|
|
6
6
|
function extractParamsFromPath(wildcardPath, snapshotPath) {
|
|
@@ -85,3 +85,7 @@ function compareVersionStrings(a, b) {
|
|
|
85
85
|
return 0;
|
|
86
86
|
}
|
|
87
87
|
exports.compareVersionStrings = compareVersionStrings;
|
|
88
|
+
function isLocalHost(href) {
|
|
89
|
+
return !!href.match(/^(http(s)?:\/\/)?(localhost|127.0.0.1|\[::1])/);
|
|
90
|
+
}
|
|
91
|
+
exports.isLocalHost = isLocalHost;
|
|
@@ -118,7 +118,7 @@ function createFirebaseEndpoints(emulator) {
|
|
|
118
118
|
return res.json(new metadata_1.OutgoingFirebaseMetadata(metadata));
|
|
119
119
|
});
|
|
120
120
|
firebaseStorageAPI.get("/b/:bucketId/o", async (req, res) => {
|
|
121
|
-
var _a, _b, _c, _d
|
|
121
|
+
var _a, _b, _c, _d;
|
|
122
122
|
const maxResults = (_a = req.query.maxResults) === null || _a === void 0 ? void 0 : _a.toString();
|
|
123
123
|
let listResponse;
|
|
124
124
|
try {
|
|
@@ -144,10 +144,12 @@ function createFirebaseEndpoints(emulator) {
|
|
|
144
144
|
}
|
|
145
145
|
return res.status(200).json({
|
|
146
146
|
nextPageToken: listResponse.nextPageToken,
|
|
147
|
-
prefixes: (_c = listResponse.prefixes) !== null && _c !== void 0 ? _c : [],
|
|
148
|
-
items: (
|
|
147
|
+
prefixes: ((_c = listResponse.prefixes) !== null && _c !== void 0 ? _c : []).filter(isValidPrefix),
|
|
148
|
+
items: ((_d = listResponse.items) !== null && _d !== void 0 ? _d : [])
|
|
149
|
+
.filter((item) => isValidNonEncodedPathString(item.name))
|
|
150
|
+
.map((item) => {
|
|
149
151
|
return { name: item.name, bucket: item.bucket };
|
|
150
|
-
})
|
|
152
|
+
}),
|
|
151
153
|
});
|
|
152
154
|
});
|
|
153
155
|
const handleUpload = async (req, res) => {
|
|
@@ -467,3 +469,23 @@ function setObjectHeaders(res, metadata, headerOverride = { "Content-Encoding":
|
|
|
467
469
|
res.setHeader("Content-Language", metadata.contentLanguage);
|
|
468
470
|
}
|
|
469
471
|
}
|
|
472
|
+
function isValidPrefix(prefix) {
|
|
473
|
+
return isValidNonEncodedPathString(removeAtMostOneTrailingSlash(prefix));
|
|
474
|
+
}
|
|
475
|
+
function isValidNonEncodedPathString(path) {
|
|
476
|
+
if (path.startsWith("/")) {
|
|
477
|
+
path = path.substring(1);
|
|
478
|
+
}
|
|
479
|
+
if (!path) {
|
|
480
|
+
return false;
|
|
481
|
+
}
|
|
482
|
+
for (const pathSegment of path.split("/")) {
|
|
483
|
+
if (!pathSegment) {
|
|
484
|
+
return false;
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
return true;
|
|
488
|
+
}
|
|
489
|
+
function removeAtMostOneTrailingSlash(path) {
|
|
490
|
+
return path.replace(/\/$/, "");
|
|
491
|
+
}
|