firebase-tools 11.8.1 → 11.10.0
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/bin/firebase.js +5 -5
- package/lib/commands/ext-install.js +2 -2
- package/lib/crashlytics/buildToolsJarHelper.js +1 -1
- package/lib/deploy/functions/build.js +3 -4
- package/lib/deploy/functions/cache/applyHash.js +2 -9
- package/lib/deploy/functions/cel.js +86 -47
- package/lib/deploy/functions/deploy.js +21 -2
- package/lib/deploy/functions/params.js +35 -8
- package/lib/deploy/functions/prepare.js +13 -2
- package/lib/deploy/functions/prepareFunctionsUpload.js +2 -2
- package/lib/deploy/functions/release/planner.js +1 -0
- package/lib/deploy/functions/services/index.js +11 -0
- package/lib/deploy/functions/services/remoteConfig.js +14 -0
- package/lib/emulator/auth/server.js +6 -0
- package/lib/emulator/auth/state.js +3 -1
- package/lib/emulator/downloadableEmulators.js +10 -10
- package/lib/emulator/functionsEmulator.js +71 -171
- package/lib/emulator/functionsEmulatorRuntime.js +104 -142
- package/lib/emulator/functionsEmulatorShared.js +3 -0
- package/lib/emulator/functionsEmulatorShell.js +1 -1
- package/lib/emulator/functionsRuntimeWorker.js +55 -36
- package/lib/emulator/storage/files.js +5 -5
- package/lib/emulator/storage/rules/runtime.js +41 -6
- package/lib/emulator/storage/rules/types.js +7 -1
- package/lib/emulator/storage/rules/utils.js +3 -1
- package/lib/emulator/storage/server.js +6 -0
- package/lib/ensureApiEnabled.js +2 -0
- package/lib/extensions/askUserForConsent.js +1 -38
- package/lib/extensions/displayExtensionInfo.js +27 -2
- package/lib/extensions/updateHelper.js +3 -35
- package/lib/functions/events/v2.js +2 -1
- package/lib/gcp/cloudfunctionsv2.js +5 -2
- package/lib/previews.js +1 -1
- package/lib/rulesDeploy.js +1 -2
- package/lib/serve/hosting.js +1 -1
- package/npm-shrinkwrap.json +69 -9
- package/package.json +3 -2
|
@@ -10,15 +10,13 @@ const types_1 = require("./types");
|
|
|
10
10
|
const constants_1 = require("./constants");
|
|
11
11
|
const functionsEmulatorShared_1 = require("./functionsEmulatorShared");
|
|
12
12
|
const functionsEmulatorUtils_1 = require("./functionsEmulatorUtils");
|
|
13
|
+
const types_2 = require("./events/types");
|
|
13
14
|
let functionModule;
|
|
14
15
|
let FUNCTION_TARGET_NAME;
|
|
15
16
|
let FUNCTION_SIGNATURE;
|
|
16
17
|
let FUNCTION_DEBUG_MODE;
|
|
17
18
|
let developerPkgJSON;
|
|
18
19
|
const dynamicImport = new Function("modulePath", "return import(modulePath)");
|
|
19
|
-
function isFeatureEnabled(frb, feature) {
|
|
20
|
-
return frb.disabled_features ? !frb.disabled_features[feature] : true;
|
|
21
|
-
}
|
|
22
20
|
function noOp() {
|
|
23
21
|
return false;
|
|
24
22
|
}
|
|
@@ -485,73 +483,14 @@ async function initializeFunctionsConfigHelper() {
|
|
|
485
483
|
function rawBodySaver(req, res, buf) {
|
|
486
484
|
req.rawBody = buf;
|
|
487
485
|
}
|
|
488
|
-
async function
|
|
489
|
-
const ephemeralServer = express();
|
|
490
|
-
await new Promise((resolveEphemeralServer, rejectEphemeralServer) => {
|
|
491
|
-
ephemeralServer.enable("trust proxy");
|
|
492
|
-
ephemeralServer.use(bodyParser.json({
|
|
493
|
-
limit: "10mb",
|
|
494
|
-
verify: rawBodySaver,
|
|
495
|
-
}));
|
|
496
|
-
ephemeralServer.use(bodyParser.text({
|
|
497
|
-
limit: "10mb",
|
|
498
|
-
verify: rawBodySaver,
|
|
499
|
-
}));
|
|
500
|
-
ephemeralServer.use(bodyParser.urlencoded({
|
|
501
|
-
extended: true,
|
|
502
|
-
limit: "10mb",
|
|
503
|
-
verify: rawBodySaver,
|
|
504
|
-
}));
|
|
505
|
-
ephemeralServer.use(bodyParser.raw({
|
|
506
|
-
type: "*/*",
|
|
507
|
-
limit: "10mb",
|
|
508
|
-
verify: rawBodySaver,
|
|
509
|
-
}));
|
|
510
|
-
let server;
|
|
511
|
-
function closeServer() {
|
|
512
|
-
if (server) {
|
|
513
|
-
server.close((err) => {
|
|
514
|
-
if (err) {
|
|
515
|
-
rejectEphemeralServer(err);
|
|
516
|
-
}
|
|
517
|
-
else {
|
|
518
|
-
resolveEphemeralServer();
|
|
519
|
-
}
|
|
520
|
-
});
|
|
521
|
-
}
|
|
522
|
-
}
|
|
523
|
-
ephemeralServer.get("/__/health", (req, res) => {
|
|
524
|
-
res.status(200).send();
|
|
525
|
-
});
|
|
526
|
-
ephemeralServer.all("/favicon.ico|/robots.txt", (req, res) => {
|
|
527
|
-
res.on("finish", closeServer);
|
|
528
|
-
res.status(404).send();
|
|
529
|
-
});
|
|
530
|
-
ephemeralServer.all(`/*`, async (req, res) => {
|
|
531
|
-
try {
|
|
532
|
-
logDebug(`Ephemeral server handling ${req.method} request`);
|
|
533
|
-
res.on("finish", closeServer);
|
|
534
|
-
await runHTTPS(trigger, [req, res]);
|
|
535
|
-
}
|
|
536
|
-
catch (err) {
|
|
537
|
-
rejectEphemeralServer(err);
|
|
538
|
-
}
|
|
539
|
-
});
|
|
540
|
-
server = ephemeralServer.listen(process.env.PORT);
|
|
541
|
-
logDebug(`Listening to port: ${process.env.PORT}`);
|
|
542
|
-
server.on("error", rejectEphemeralServer);
|
|
543
|
-
});
|
|
544
|
-
}
|
|
545
|
-
async function processBackground(trigger, frb, signature) {
|
|
546
|
-
const proto = frb.proto;
|
|
547
|
-
logDebug("ProcessBackground", proto);
|
|
486
|
+
async function processBackground(trigger, reqBody, signature) {
|
|
548
487
|
if (signature === "cloudevent") {
|
|
549
|
-
return runCloudEvent(trigger,
|
|
488
|
+
return runCloudEvent(trigger, reqBody);
|
|
550
489
|
}
|
|
551
|
-
const data =
|
|
552
|
-
delete
|
|
553
|
-
const context =
|
|
554
|
-
if (!
|
|
490
|
+
const data = reqBody.data;
|
|
491
|
+
delete reqBody.data;
|
|
492
|
+
const context = reqBody.context ? reqBody.context : reqBody;
|
|
493
|
+
if (!reqBody.eventType || !reqBody.eventType.startsWith("google.storage")) {
|
|
555
494
|
if (context.resource && context.resource.name) {
|
|
556
495
|
logDebug("ProcessBackground: lifting resource.name from resource", context.resource);
|
|
557
496
|
context.resource = context.resource.name;
|
|
@@ -567,15 +506,14 @@ async function runFunction(func) {
|
|
|
567
506
|
catch (err) {
|
|
568
507
|
caughtErr = err;
|
|
569
508
|
}
|
|
570
|
-
logDebug(`Ephemeral server survived.`);
|
|
571
509
|
if (caughtErr) {
|
|
572
510
|
throw caughtErr;
|
|
573
511
|
}
|
|
574
512
|
}
|
|
575
|
-
async function runBackground(trigger,
|
|
576
|
-
logDebug("RunBackground",
|
|
513
|
+
async function runBackground(trigger, reqBody) {
|
|
514
|
+
logDebug("RunBackground", reqBody);
|
|
577
515
|
await runFunction(() => {
|
|
578
|
-
return trigger(
|
|
516
|
+
return trigger(reqBody.data, reqBody.context);
|
|
579
517
|
});
|
|
580
518
|
}
|
|
581
519
|
async function runCloudEvent(trigger, event) {
|
|
@@ -613,43 +551,6 @@ async function moduleResolutionDetective(error) {
|
|
|
613
551
|
function logDebug(msg, data) {
|
|
614
552
|
new types_1.EmulatorLog("DEBUG", "runtime-status", `[${process.pid}] ${msg}`, data).log();
|
|
615
553
|
}
|
|
616
|
-
async function invokeTrigger(trigger, frb) {
|
|
617
|
-
new types_1.EmulatorLog("INFO", "runtime-status", `Beginning execution of "${FUNCTION_TARGET_NAME}"`, {
|
|
618
|
-
frb,
|
|
619
|
-
}).log();
|
|
620
|
-
logDebug(`Running ${FUNCTION_TARGET_NAME} in signature ${FUNCTION_SIGNATURE}`);
|
|
621
|
-
let seconds = 0;
|
|
622
|
-
const timerId = setInterval(() => {
|
|
623
|
-
seconds++;
|
|
624
|
-
}, 1000);
|
|
625
|
-
let timeoutId;
|
|
626
|
-
if (isFeatureEnabled(frb, "timeout")) {
|
|
627
|
-
let timeout = process.env.FUNCTIONS_EMULATOR_TIMEOUT_SECONDS || "60";
|
|
628
|
-
if (timeout.endsWith("s")) {
|
|
629
|
-
timeout = timeout.slice(0, -1);
|
|
630
|
-
}
|
|
631
|
-
const timeoutMs = parseInt(timeout, 10) * 1000;
|
|
632
|
-
timeoutId = setTimeout(() => {
|
|
633
|
-
new types_1.EmulatorLog("WARN", "runtime-status", `Your function timed out after ~${timeout}s. To configure this timeout, see
|
|
634
|
-
https://firebase.google.com/docs/functions/manage-functions#set_timeout_and_memory_allocation.`).log();
|
|
635
|
-
throw new Error("Function timed out.");
|
|
636
|
-
}, timeoutMs);
|
|
637
|
-
}
|
|
638
|
-
switch (FUNCTION_SIGNATURE) {
|
|
639
|
-
case "event":
|
|
640
|
-
case "cloudevent":
|
|
641
|
-
await processBackground(trigger, frb, FUNCTION_SIGNATURE);
|
|
642
|
-
break;
|
|
643
|
-
case "http":
|
|
644
|
-
await processHTTPS(trigger);
|
|
645
|
-
break;
|
|
646
|
-
}
|
|
647
|
-
if (timeoutId) {
|
|
648
|
-
clearTimeout(timeoutId);
|
|
649
|
-
}
|
|
650
|
-
clearInterval(timerId);
|
|
651
|
-
new types_1.EmulatorLog("INFO", "runtime-status", `Finished "${FUNCTION_TARGET_NAME}" in ~${Math.max(seconds, 1)}s`).log();
|
|
652
|
-
}
|
|
653
554
|
async function initializeRuntime() {
|
|
654
555
|
FUNCTION_DEBUG_MODE = process.env.FUNCTION_DEBUG_MODE || "";
|
|
655
556
|
if (!FUNCTION_DEBUG_MODE) {
|
|
@@ -695,50 +596,25 @@ async function flushAndExit(code) {
|
|
|
695
596
|
await types_1.EmulatorLog.waitForFlush();
|
|
696
597
|
process.exit(code);
|
|
697
598
|
}
|
|
698
|
-
async function goIdle() {
|
|
699
|
-
new types_1.EmulatorLog("SYSTEM", "runtime-status", "Runtime is now idle", { state: "idle" }).log();
|
|
700
|
-
await types_1.EmulatorLog.waitForFlush();
|
|
701
|
-
}
|
|
702
599
|
async function handleMessage(message) {
|
|
703
|
-
let
|
|
600
|
+
let debug;
|
|
704
601
|
try {
|
|
705
|
-
|
|
602
|
+
debug = JSON.parse(message);
|
|
706
603
|
}
|
|
707
604
|
catch (e) {
|
|
708
605
|
new types_1.EmulatorLog("FATAL", "runtime-error", `Got unexpected message body: ${message}`).log();
|
|
709
606
|
await flushAndExit(1);
|
|
710
607
|
return;
|
|
711
608
|
}
|
|
712
|
-
if (
|
|
713
|
-
|
|
714
|
-
|
|
609
|
+
if (FUNCTION_DEBUG_MODE) {
|
|
610
|
+
if (debug) {
|
|
611
|
+
FUNCTION_TARGET_NAME = debug.functionTarget;
|
|
612
|
+
FUNCTION_SIGNATURE = debug.functionSignature;
|
|
715
613
|
}
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
new types_1.EmulatorLog("FATAL", "runtime-status", `Failed to initialize and load triggers. This shouldn't happen: ${e.message}`).log();
|
|
719
|
-
await flushAndExit(1);
|
|
720
|
-
return;
|
|
614
|
+
else {
|
|
615
|
+
new types_1.EmulatorLog("WARN", "runtime-warning", "Expected debug payload while in debug mode.");
|
|
721
616
|
}
|
|
722
617
|
}
|
|
723
|
-
if (FUNCTION_DEBUG_MODE) {
|
|
724
|
-
FUNCTION_TARGET_NAME = runtimeArgs.frb.debug.functionTarget;
|
|
725
|
-
FUNCTION_SIGNATURE = runtimeArgs.frb.debug.functionSignature;
|
|
726
|
-
}
|
|
727
|
-
const trigger = FUNCTION_TARGET_NAME.split(".").reduce((mod, functionTargetPart) => {
|
|
728
|
-
return mod === null || mod === void 0 ? void 0 : mod[functionTargetPart];
|
|
729
|
-
}, functionModule);
|
|
730
|
-
if (!trigger) {
|
|
731
|
-
throw new Error(`Failed to find function ${FUNCTION_TARGET_NAME} in the loaded module`);
|
|
732
|
-
}
|
|
733
|
-
logDebug(`Beginning invocation function ${FUNCTION_TARGET_NAME}!`);
|
|
734
|
-
try {
|
|
735
|
-
await invokeTrigger(trigger, runtimeArgs.frb);
|
|
736
|
-
await goIdle();
|
|
737
|
-
}
|
|
738
|
-
catch (err) {
|
|
739
|
-
new types_1.EmulatorLog("FATAL", "runtime-error", err.stack ? err.stack : err).log();
|
|
740
|
-
await flushAndExit(1);
|
|
741
|
-
}
|
|
742
618
|
}
|
|
743
619
|
async function main() {
|
|
744
620
|
let lastSignal = new Date().getTime();
|
|
@@ -755,6 +631,92 @@ async function main() {
|
|
|
755
631
|
}
|
|
756
632
|
});
|
|
757
633
|
await initializeRuntime();
|
|
634
|
+
try {
|
|
635
|
+
functionModule = await loadTriggers();
|
|
636
|
+
}
|
|
637
|
+
catch (e) {
|
|
638
|
+
new types_1.EmulatorLog("FATAL", "runtime-status", `Failed to initialize and load triggers. This shouldn't happen: ${e.message}`).log();
|
|
639
|
+
await flushAndExit(1);
|
|
640
|
+
}
|
|
641
|
+
const app = express();
|
|
642
|
+
app.enable("trust proxy");
|
|
643
|
+
app.use(bodyParser.json({
|
|
644
|
+
limit: "10mb",
|
|
645
|
+
verify: rawBodySaver,
|
|
646
|
+
}));
|
|
647
|
+
app.use(bodyParser.text({
|
|
648
|
+
limit: "10mb",
|
|
649
|
+
verify: rawBodySaver,
|
|
650
|
+
}));
|
|
651
|
+
app.use(bodyParser.urlencoded({
|
|
652
|
+
extended: true,
|
|
653
|
+
limit: "10mb",
|
|
654
|
+
verify: rawBodySaver,
|
|
655
|
+
}));
|
|
656
|
+
app.use(bodyParser.raw({
|
|
657
|
+
type: "*/*",
|
|
658
|
+
limit: "10mb",
|
|
659
|
+
verify: rawBodySaver,
|
|
660
|
+
}));
|
|
661
|
+
app.get("/__/health", (req, res) => {
|
|
662
|
+
res.status(200).send();
|
|
663
|
+
});
|
|
664
|
+
app.all("/favicon.ico|/robots.txt", (req, res) => {
|
|
665
|
+
res.status(404).send();
|
|
666
|
+
});
|
|
667
|
+
app.all(`/*`, async (req, res) => {
|
|
668
|
+
var _a;
|
|
669
|
+
try {
|
|
670
|
+
new types_1.EmulatorLog("INFO", "runtime-status", `Beginning execution of "${FUNCTION_TARGET_NAME}"`).log();
|
|
671
|
+
const trigger = FUNCTION_TARGET_NAME.split(".").reduce((mod, functionTargetPart) => {
|
|
672
|
+
return mod === null || mod === void 0 ? void 0 : mod[functionTargetPart];
|
|
673
|
+
}, functionModule);
|
|
674
|
+
if (!trigger) {
|
|
675
|
+
throw new Error(`Failed to find function ${FUNCTION_TARGET_NAME} in the loaded module`);
|
|
676
|
+
}
|
|
677
|
+
const startHrTime = process.hrtime();
|
|
678
|
+
res.on("finish", () => {
|
|
679
|
+
const elapsedHrTime = process.hrtime(startHrTime);
|
|
680
|
+
new types_1.EmulatorLog("INFO", "runtime-status", `Finished "${FUNCTION_TARGET_NAME}" in ${elapsedHrTime[0] * 1000 + elapsedHrTime[1] / 1000000}ms`).log();
|
|
681
|
+
});
|
|
682
|
+
switch (FUNCTION_SIGNATURE) {
|
|
683
|
+
case "event":
|
|
684
|
+
case "cloudevent":
|
|
685
|
+
const rawBody = req.rawBody;
|
|
686
|
+
let reqBody = JSON.parse(rawBody.toString());
|
|
687
|
+
if ((_a = req.headers["content-type"]) === null || _a === void 0 ? void 0 : _a.includes("cloudevent")) {
|
|
688
|
+
if (types_2.EventUtils.isBinaryCloudEvent(req)) {
|
|
689
|
+
reqBody = types_2.EventUtils.extractBinaryCloudEventContext(req);
|
|
690
|
+
reqBody.data = req.body;
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
await processBackground(trigger, reqBody, FUNCTION_SIGNATURE);
|
|
694
|
+
res.send({ status: "acknowledged" });
|
|
695
|
+
break;
|
|
696
|
+
case "http":
|
|
697
|
+
await runHTTPS(trigger, [req, res]);
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
catch (err) {
|
|
701
|
+
new types_1.EmulatorLog("FATAL", "runtime-error", err.stack ? err.stack : err).log();
|
|
702
|
+
res.status(500).send(err.message);
|
|
703
|
+
}
|
|
704
|
+
});
|
|
705
|
+
const server = app.listen(process.env.PORT, () => {
|
|
706
|
+
logDebug(`Listening to port: ${process.env.PORT}`);
|
|
707
|
+
});
|
|
708
|
+
if (!FUNCTION_DEBUG_MODE) {
|
|
709
|
+
let timeout = process.env.FUNCTIONS_EMULATOR_TIMEOUT_SECONDS || "60";
|
|
710
|
+
if (timeout.endsWith("s")) {
|
|
711
|
+
timeout = timeout.slice(0, -1);
|
|
712
|
+
}
|
|
713
|
+
const timeoutMs = parseInt(timeout, 10) * 1000;
|
|
714
|
+
server.setTimeout(timeoutMs, () => {
|
|
715
|
+
new types_1.EmulatorLog("FATAL", "runtime-error", `Your function timed out after ~${timeout}s. To configure this timeout, see
|
|
716
|
+
https://firebase.google.com/docs/functions/manage-functions#set_timeout_and_memory_allocation.`).log();
|
|
717
|
+
return flushAndExit(1);
|
|
718
|
+
});
|
|
719
|
+
}
|
|
758
720
|
let messageHandlePromise = Promise.resolve();
|
|
759
721
|
process.on("message", (message) => {
|
|
760
722
|
messageHandlePromise = messageHandlePromise
|
|
@@ -22,19 +22,6 @@ class RuntimeWorker {
|
|
|
22
22
|
this.id = uuid.v4();
|
|
23
23
|
this.key = key;
|
|
24
24
|
this.runtime = runtime;
|
|
25
|
-
this.runtime.events.on("log", (log) => {
|
|
26
|
-
if (log.type === "runtime-status") {
|
|
27
|
-
if (log.data.state === "idle") {
|
|
28
|
-
if (this.state === RuntimeWorkerState.BUSY) {
|
|
29
|
-
this.state = RuntimeWorkerState.IDLE;
|
|
30
|
-
}
|
|
31
|
-
else if (this.state === RuntimeWorkerState.FINISHING) {
|
|
32
|
-
this.log(`IDLE --> FINISHING`);
|
|
33
|
-
this.runtime.process.kill();
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
});
|
|
38
25
|
const childProc = this.runtime.process;
|
|
39
26
|
let msgBuffer = "";
|
|
40
27
|
childProc.on("message", (msg) => {
|
|
@@ -71,11 +58,55 @@ class RuntimeWorker {
|
|
|
71
58
|
}
|
|
72
59
|
return lines[lines.length - 1];
|
|
73
60
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
61
|
+
sendDebugMsg(debug) {
|
|
62
|
+
return new Promise((resolve, reject) => {
|
|
63
|
+
this.runtime.process.send(JSON.stringify(debug), (err) => {
|
|
64
|
+
if (err) {
|
|
65
|
+
reject(err);
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
resolve();
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
request(req, resp, body) {
|
|
77
74
|
this.state = RuntimeWorkerState.BUSY;
|
|
78
|
-
|
|
75
|
+
const onFinish = () => {
|
|
76
|
+
if (this.state === RuntimeWorkerState.BUSY) {
|
|
77
|
+
this.state = RuntimeWorkerState.IDLE;
|
|
78
|
+
}
|
|
79
|
+
else if (this.state === RuntimeWorkerState.FINISHING) {
|
|
80
|
+
this.log(`IDLE --> FINISHING`);
|
|
81
|
+
this.runtime.process.kill();
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
return new Promise((resolve) => {
|
|
85
|
+
const proxy = http.request({
|
|
86
|
+
method: req.method,
|
|
87
|
+
path: req.path,
|
|
88
|
+
headers: req.headers,
|
|
89
|
+
socketPath: this.runtime.socketPath,
|
|
90
|
+
}, (_resp) => {
|
|
91
|
+
resp.writeHead(_resp.statusCode || 200, _resp.headers);
|
|
92
|
+
const piped = _resp.pipe(resp);
|
|
93
|
+
piped.on("finish", () => {
|
|
94
|
+
onFinish();
|
|
95
|
+
resolve();
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
proxy.on("error", (err) => {
|
|
99
|
+
resp.writeHead(500);
|
|
100
|
+
resp.write(JSON.stringify(err));
|
|
101
|
+
resp.end();
|
|
102
|
+
this.runtime.process.kill();
|
|
103
|
+
resolve();
|
|
104
|
+
});
|
|
105
|
+
if (body) {
|
|
106
|
+
proxy.write(body);
|
|
107
|
+
}
|
|
108
|
+
proxy.end();
|
|
109
|
+
});
|
|
79
110
|
}
|
|
80
111
|
get state() {
|
|
81
112
|
return this._state;
|
|
@@ -100,20 +131,6 @@ class RuntimeWorker {
|
|
|
100
131
|
}
|
|
101
132
|
this.runtime.events.on("log", listener);
|
|
102
133
|
}
|
|
103
|
-
waitForDone() {
|
|
104
|
-
if (this.state === RuntimeWorkerState.IDLE || this.state === RuntimeWorkerState.FINISHED) {
|
|
105
|
-
return Promise.resolve();
|
|
106
|
-
}
|
|
107
|
-
return new Promise((res) => {
|
|
108
|
-
const listener = () => {
|
|
109
|
-
this.stateEvents.removeListener(RuntimeWorkerState.IDLE, listener);
|
|
110
|
-
this.stateEvents.removeListener(RuntimeWorkerState.FINISHED, listener);
|
|
111
|
-
res();
|
|
112
|
-
};
|
|
113
|
-
this.stateEvents.once(RuntimeWorkerState.IDLE, listener);
|
|
114
|
-
this.stateEvents.once(RuntimeWorkerState.FINISHED, listener);
|
|
115
|
-
});
|
|
116
|
-
}
|
|
117
134
|
isSocketReady() {
|
|
118
135
|
return new Promise((resolve, reject) => {
|
|
119
136
|
const req = http
|
|
@@ -198,14 +215,16 @@ class RuntimeWorkerPool {
|
|
|
198
215
|
const idleWorker = this.getIdleWorker(triggerId);
|
|
199
216
|
return !!idleWorker;
|
|
200
217
|
}
|
|
201
|
-
|
|
202
|
-
this.log(`
|
|
218
|
+
async submitRequest(triggerId, req, resp, body, debug) {
|
|
219
|
+
this.log(`submitRequest(triggerId=${triggerId})`);
|
|
203
220
|
const worker = this.getIdleWorker(triggerId);
|
|
204
221
|
if (!worker) {
|
|
205
|
-
throw new error_1.FirebaseError("Internal Error: can't call
|
|
222
|
+
throw new error_1.FirebaseError("Internal Error: can't call submitRequest without checking for idle workers");
|
|
206
223
|
}
|
|
207
|
-
|
|
208
|
-
|
|
224
|
+
if (debug) {
|
|
225
|
+
await worker.sendDebugMsg(debug);
|
|
226
|
+
}
|
|
227
|
+
return worker.request(req, resp, body);
|
|
209
228
|
}
|
|
210
229
|
getIdleWorker(triggerId) {
|
|
211
230
|
this.cleanUpWorkers();
|
|
@@ -63,7 +63,7 @@ class StorageLayer {
|
|
|
63
63
|
const hasValidDownloadToken = ((metadata === null || metadata === void 0 ? void 0 : metadata.downloadTokens) || []).includes((_a = request.downloadToken) !== null && _a !== void 0 ? _a : "");
|
|
64
64
|
let authorized = hasValidDownloadToken;
|
|
65
65
|
if (!authorized) {
|
|
66
|
-
authorized = await this._rulesValidator.validate(["b", request.bucketId, "o", request.decodedObjectId].join("/"), request.bucketId, types_1.RulesetOperationMethod.GET, { before: metadata === null || metadata === void 0 ? void 0 : metadata.asRulesResource() }, request.authorization);
|
|
66
|
+
authorized = await this._rulesValidator.validate(["b", request.bucketId, "o", request.decodedObjectId].join("/"), request.bucketId, types_1.RulesetOperationMethod.GET, { before: metadata === null || metadata === void 0 ? void 0 : metadata.asRulesResource() }, this._projectId, request.authorization);
|
|
67
67
|
}
|
|
68
68
|
if (!authorized) {
|
|
69
69
|
throw new errors_1.ForbiddenError("Failed auth");
|
|
@@ -92,7 +92,7 @@ class StorageLayer {
|
|
|
92
92
|
}
|
|
93
93
|
async deleteObject(request) {
|
|
94
94
|
const storedMetadata = this.getMetadata(request.bucketId, request.decodedObjectId);
|
|
95
|
-
const authorized = await this._rulesValidator.validate(["b", request.bucketId, "o", request.decodedObjectId].join("/"), request.bucketId, types_1.RulesetOperationMethod.DELETE, { before: storedMetadata === null || storedMetadata === void 0 ? void 0 : storedMetadata.asRulesResource() }, request.authorization);
|
|
95
|
+
const authorized = await this._rulesValidator.validate(["b", request.bucketId, "o", request.decodedObjectId].join("/"), request.bucketId, types_1.RulesetOperationMethod.DELETE, { before: storedMetadata === null || storedMetadata === void 0 ? void 0 : storedMetadata.asRulesResource() }, this._projectId, request.authorization);
|
|
96
96
|
if (!authorized) {
|
|
97
97
|
throw new errors_1.ForbiddenError();
|
|
98
98
|
}
|
|
@@ -126,7 +126,7 @@ class StorageLayer {
|
|
|
126
126
|
const authorized = await this._rulesValidator.validate(["b", request.bucketId, "o", request.decodedObjectId].join("/"), request.bucketId, types_1.RulesetOperationMethod.UPDATE, {
|
|
127
127
|
before: storedMetadata === null || storedMetadata === void 0 ? void 0 : storedMetadata.asRulesResource(),
|
|
128
128
|
after: storedMetadata === null || storedMetadata === void 0 ? void 0 : storedMetadata.asRulesResource(request.metadata),
|
|
129
|
-
}, request.authorization);
|
|
129
|
+
}, this._projectId, request.authorization);
|
|
130
130
|
if (!authorized) {
|
|
131
131
|
throw new errors_1.ForbiddenError();
|
|
132
132
|
}
|
|
@@ -162,7 +162,7 @@ class StorageLayer {
|
|
|
162
162
|
const authorized = await this._rulesValidator.validate(["b", upload.bucketId, "o", upload.objectId].join("/"), upload.bucketId, types_1.RulesetOperationMethod.CREATE, {
|
|
163
163
|
before: storedMetadata === null || storedMetadata === void 0 ? void 0 : storedMetadata.asRulesResource(),
|
|
164
164
|
after: metadata.asRulesResource(),
|
|
165
|
-
}, upload.authorization);
|
|
165
|
+
}, this._projectId, upload.authorization);
|
|
166
166
|
if (!authorized) {
|
|
167
167
|
this._persistence.deleteFile(upload.path);
|
|
168
168
|
throw new errors_1.ForbiddenError();
|
|
@@ -220,7 +220,7 @@ class StorageLayer {
|
|
|
220
220
|
async listObjects(request) {
|
|
221
221
|
var _a;
|
|
222
222
|
const { bucketId, prefix, delimiter, pageToken, authorization } = request;
|
|
223
|
-
const authorized = await this._rulesValidator.validate(["b", bucketId, "o", prefix.replace(TRAILING_SLASHES_PATTERN, "")].join("/"), bucketId, types_1.RulesetOperationMethod.LIST, {}, authorization, delimiter);
|
|
223
|
+
const authorized = await this._rulesValidator.validate(["b", bucketId, "o", prefix.replace(TRAILING_SLASHES_PATTERN, "")].join("/"), bucketId, types_1.RulesetOperationMethod.LIST, {}, this._projectId, authorization, delimiter);
|
|
224
224
|
if (!authorized) {
|
|
225
225
|
throw new errors_1.ForbiddenError();
|
|
226
226
|
}
|
|
@@ -14,6 +14,8 @@ const constants_1 = require("../../constants");
|
|
|
14
14
|
const download_1 = require("../../download");
|
|
15
15
|
const fs = require("fs-extra");
|
|
16
16
|
const downloadableEmulators_1 = require("../../downloadableEmulators");
|
|
17
|
+
const registry_1 = require("../../registry");
|
|
18
|
+
const apiv2_1 = require("../../../apiv2");
|
|
17
19
|
const lock = new AsyncLock();
|
|
18
20
|
const synchonizationKey = "key";
|
|
19
21
|
class StorageRulesetInstance {
|
|
@@ -118,6 +120,7 @@ class StorageRulesRuntime {
|
|
|
118
120
|
}
|
|
119
121
|
});
|
|
120
122
|
(_b = this._childprocess.stdout) === null || _b === void 0 ? void 0 : _b.on("data", (buf) => {
|
|
123
|
+
var _a;
|
|
121
124
|
const serializedRuntimeActionResponse = buf.toString("utf-8").trim();
|
|
122
125
|
if (serializedRuntimeActionResponse !== "") {
|
|
123
126
|
let rap;
|
|
@@ -128,8 +131,13 @@ class StorageRulesRuntime {
|
|
|
128
131
|
emulatorLogger_1.EmulatorLogger.forEmulator(types_2.Emulators.STORAGE).log("INFO", serializedRuntimeActionResponse);
|
|
129
132
|
return;
|
|
130
133
|
}
|
|
131
|
-
const
|
|
132
|
-
if (
|
|
134
|
+
const id = (_a = rap.id) !== null && _a !== void 0 ? _a : rap.server_request_id;
|
|
135
|
+
if (id === undefined) {
|
|
136
|
+
console.log(`Received no ID from server response ${serializedRuntimeActionResponse}`);
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
const request = this._requests[id];
|
|
140
|
+
if (rap.status !== "ok" && !("action" in rap)) {
|
|
133
141
|
console.warn(`[RULES] ${rap.status}: ${rap.message}`);
|
|
134
142
|
rap.errors.forEach(console.warn.bind(console));
|
|
135
143
|
return;
|
|
@@ -148,12 +156,15 @@ class StorageRulesRuntime {
|
|
|
148
156
|
var _a;
|
|
149
157
|
(_a = this._childprocess) === null || _a === void 0 ? void 0 : _a.kill("SIGINT");
|
|
150
158
|
}
|
|
151
|
-
async _sendRequest(rab) {
|
|
159
|
+
async _sendRequest(rab, overrideId) {
|
|
152
160
|
if (!this._childprocess) {
|
|
153
161
|
throw new error_1.FirebaseError("Attempted to send Cloud Storage rules request before child was ready");
|
|
154
162
|
}
|
|
155
|
-
const runtimeActionRequest = Object.assign(Object.assign({}, rab), { id: this._requestCount++ });
|
|
156
|
-
if (
|
|
163
|
+
const runtimeActionRequest = Object.assign(Object.assign({}, rab), { id: overrideId !== null && overrideId !== void 0 ? overrideId : this._requestCount++ });
|
|
164
|
+
if (overrideId !== undefined) {
|
|
165
|
+
delete this._requests[overrideId];
|
|
166
|
+
}
|
|
167
|
+
else if (this._requests[runtimeActionRequest.id]) {
|
|
157
168
|
throw new error_1.FirebaseError("Attempted to send Cloud Storage rules request with stale id");
|
|
158
169
|
}
|
|
159
170
|
return new Promise((resolve) => {
|
|
@@ -211,7 +222,14 @@ class StorageRulesRuntime {
|
|
|
211
222
|
variables: runtimeVariables,
|
|
212
223
|
},
|
|
213
224
|
};
|
|
214
|
-
|
|
225
|
+
return this._completeVerifyWithRuleset(opts.projectId, runtimeActionRequest);
|
|
226
|
+
}
|
|
227
|
+
async _completeVerifyWithRuleset(projectId, runtimeActionRequest, overrideId) {
|
|
228
|
+
const response = (await this._sendRequest(runtimeActionRequest, overrideId));
|
|
229
|
+
if ("context" in response) {
|
|
230
|
+
const dataResponse = await fetchFirestoreDocument(projectId, response);
|
|
231
|
+
return this._completeVerifyWithRuleset(projectId, dataResponse, response.server_request_id);
|
|
232
|
+
}
|
|
215
233
|
if (!response.errors)
|
|
216
234
|
response.errors = [];
|
|
217
235
|
if (!response.warnings)
|
|
@@ -282,6 +300,23 @@ function toExpressionValue(obj) {
|
|
|
282
300
|
}
|
|
283
301
|
throw new error_1.FirebaseError(`Cannot convert "${obj}" of type ${typeof obj} for Firebase Storage rules runtime`);
|
|
284
302
|
}
|
|
303
|
+
async function fetchFirestoreDocument(projectId, request) {
|
|
304
|
+
const url = registry_1.EmulatorRegistry.url(types_2.Emulators.FIRESTORE);
|
|
305
|
+
const pathname = `projects/${projectId}${request.context.path}`;
|
|
306
|
+
const client = new apiv2_1.Client({
|
|
307
|
+
urlPrefix: url.toString(),
|
|
308
|
+
apiVersion: "v1",
|
|
309
|
+
});
|
|
310
|
+
try {
|
|
311
|
+
const doc = await client.get(pathname);
|
|
312
|
+
const { name, fields } = doc.body;
|
|
313
|
+
const result = { name, fields };
|
|
314
|
+
return { result, status: types_1.DataLoadStatus.OK, warnings: [], errors: [] };
|
|
315
|
+
}
|
|
316
|
+
catch (e) {
|
|
317
|
+
return { status: types_1.DataLoadStatus.NOT_FOUND, warnings: [], errors: [] };
|
|
318
|
+
}
|
|
319
|
+
}
|
|
285
320
|
function createAuthExpressionValue(opts) {
|
|
286
321
|
if (!opts.token) {
|
|
287
322
|
return toExpressionValue(null);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.RulesetOperationMethod = void 0;
|
|
3
|
+
exports.DataLoadStatus = exports.RulesetOperationMethod = void 0;
|
|
4
4
|
var RulesetOperationMethod;
|
|
5
5
|
(function (RulesetOperationMethod) {
|
|
6
6
|
RulesetOperationMethod["READ"] = "read";
|
|
@@ -11,3 +11,9 @@ var RulesetOperationMethod;
|
|
|
11
11
|
RulesetOperationMethod["UPDATE"] = "update";
|
|
12
12
|
RulesetOperationMethod["DELETE"] = "delete";
|
|
13
13
|
})(RulesetOperationMethod = exports.RulesetOperationMethod || (exports.RulesetOperationMethod = {}));
|
|
14
|
+
var DataLoadStatus;
|
|
15
|
+
(function (DataLoadStatus) {
|
|
16
|
+
DataLoadStatus["OK"] = "ok";
|
|
17
|
+
DataLoadStatus["NOT_FOUND"] = "not_found";
|
|
18
|
+
DataLoadStatus["INVALID_STATE"] = "invalid_state";
|
|
19
|
+
})(DataLoadStatus = exports.DataLoadStatus || (exports.DataLoadStatus = {}));
|
|
@@ -5,12 +5,13 @@ const emulatorLogger_1 = require("../../emulatorLogger");
|
|
|
5
5
|
const types_1 = require("../../types");
|
|
6
6
|
function getFirebaseRulesValidator(rulesetProvider) {
|
|
7
7
|
return {
|
|
8
|
-
validate: async (path, bucketId, method, variableOverrides, authorization, delimiter) => {
|
|
8
|
+
validate: async (path, bucketId, method, variableOverrides, projectId, authorization, delimiter) => {
|
|
9
9
|
return await isPermitted({
|
|
10
10
|
ruleset: rulesetProvider(bucketId),
|
|
11
11
|
file: variableOverrides,
|
|
12
12
|
path,
|
|
13
13
|
method,
|
|
14
|
+
projectId,
|
|
14
15
|
authorization,
|
|
15
16
|
delimiter,
|
|
16
17
|
});
|
|
@@ -42,6 +43,7 @@ async function isPermitted(opts) {
|
|
|
42
43
|
method: opts.method,
|
|
43
44
|
path: opts.path,
|
|
44
45
|
file: opts.file,
|
|
46
|
+
projectId: opts.projectId,
|
|
45
47
|
token: opts.authorization ? opts.authorization.split(" ")[1] : undefined,
|
|
46
48
|
delimiter: opts.delimiter,
|
|
47
49
|
});
|
|
@@ -13,6 +13,12 @@ function createApp(defaultProjectId, emulator) {
|
|
|
13
13
|
const { storageLayer } = emulator;
|
|
14
14
|
const app = express();
|
|
15
15
|
emulatorLogger_1.EmulatorLogger.forEmulator(types_1.Emulators.STORAGE).log("DEBUG", `Temp file directory for storage emulator: ${storageLayer.dirPath}`);
|
|
16
|
+
app.use("/", (req, res, next) => {
|
|
17
|
+
if (req.headers["access-control-request-private-network"]) {
|
|
18
|
+
res.setHeader("access-control-allow-private-network", "true");
|
|
19
|
+
}
|
|
20
|
+
next();
|
|
21
|
+
});
|
|
16
22
|
app.use(cors({
|
|
17
23
|
origin: true,
|
|
18
24
|
exposedHeaders: [
|
package/lib/ensureApiEnabled.js
CHANGED
|
@@ -17,6 +17,7 @@ const apiClient = new apiv2_1.Client({
|
|
|
17
17
|
});
|
|
18
18
|
async function check(projectId, apiName, prefix, silent = false) {
|
|
19
19
|
const res = await apiClient.get(`/projects/${projectId}/services/${apiName}`, {
|
|
20
|
+
headers: { "x-goog-quota-user": `projects/${projectId}` },
|
|
20
21
|
skipLog: { resBody: true },
|
|
21
22
|
});
|
|
22
23
|
const isEnabled = res.body.state === "ENABLED";
|
|
@@ -29,6 +30,7 @@ exports.check = check;
|
|
|
29
30
|
async function enable(projectId, apiName) {
|
|
30
31
|
try {
|
|
31
32
|
await apiClient.post(`/projects/${projectId}/services/${apiName}:enable`, undefined, {
|
|
33
|
+
headers: { "x-goog-quota-user": `projects/${projectId}` },
|
|
32
34
|
skipLog: { resBody: true },
|
|
33
35
|
});
|
|
34
36
|
}
|