firebase-tools 10.3.1 → 10.4.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/accountExporter.js +95 -84
- package/lib/commands/deploy.js +1 -1
- package/lib/commands/experimental-functions-shell.js +1 -1
- package/lib/commands/ext-configure.js +7 -6
- package/lib/commands/ext-export.js +7 -1
- package/lib/commands/ext-install.js +7 -6
- package/lib/commands/ext-update.js +4 -3
- package/lib/commands/functions-config-export.js +5 -3
- package/lib/commands/functions-shell.js +1 -1
- package/lib/commands/hosting-channel-create.js +2 -2
- package/lib/commands/hosting-channel-delete.js +2 -2
- package/lib/commands/hosting-channel-deploy.js +2 -2
- package/lib/commands/hosting-channel-list.js +2 -2
- package/lib/commands/hosting-channel-open.js +2 -2
- package/lib/commands/hosting-sites-delete.js +2 -2
- package/lib/commands/serve.js +1 -1
- package/lib/commands/target-apply.js +2 -2
- package/lib/commands/target-clear.js +2 -2
- package/lib/commands/target-remove.js +2 -2
- package/lib/commands/target.js +2 -2
- package/lib/config.js +14 -4
- package/lib/deploy/extensions/planner.js +9 -3
- package/lib/deploy/functions/checkIam.js +44 -1
- package/lib/deploy/functions/deploy.js +3 -7
- package/lib/deploy/functions/prepare.js +7 -5
- package/lib/deploy/functions/prepareFunctionsUpload.js +7 -13
- package/lib/deploy/functions/release/fabricator.js +13 -1
- package/lib/deploy/functions/release/index.js +1 -1
- package/lib/deploy/functions/services/firebaseAlerts.js +1 -17
- package/lib/deploy/functions/services/index.js +2 -1
- package/lib/deploy/hosting/deploy.js +10 -0
- package/lib/emulator/auth/apiSpec.js +37 -0
- package/lib/emulator/commandUtils.js +2 -2
- package/lib/emulator/controller.js +14 -8
- package/lib/emulator/downloadableEmulators.js +5 -5
- package/lib/emulator/extensionsEmulator.js +3 -0
- package/lib/emulator/functionsEmulator.js +8 -18
- package/lib/emulator/functionsEmulatorShared.js +31 -1
- package/lib/emulator/storage/apis/firebase.js +4 -6
- package/lib/emulator/storage/files.js +5 -5
- package/lib/emulator/storage/index.js +6 -9
- package/lib/emulator/storage/rules/config.js +6 -5
- package/lib/emulator/storage/rules/manager.js +49 -32
- package/lib/emulator/storage/rules/runtime.js +4 -0
- package/lib/emulator/storage/rules/utils.js +2 -2
- package/lib/emulator/storage/server.js +1 -1
- package/lib/extensions/askUserForParam.js +87 -16
- package/lib/extensions/extensionsHelper.js +11 -2
- package/lib/extensions/manifest.js +36 -4
- package/lib/extensions/paramHelper.js +11 -6
- package/lib/fsutils.js +14 -1
- package/lib/functions/projectConfig.js +34 -0
- package/lib/init/features/functions/index.js +4 -2
- package/lib/init/features/hosting/index.js +32 -41
- package/lib/init/features/index.js +22 -12
- package/lib/init/index.js +28 -11
- package/lib/requireConfig.js +11 -9
- package/lib/requirePermissions.js +4 -1
- package/lib/serve/functions.js +5 -5
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
- package/schema/firebase-config.json +93 -36
- package/lib/prepareUpload.js +0 -44
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getSecretLocalPath = exports.getSignatureType = exports.formatHost = exports.findModuleRoot = exports.waitForBody = exports.getServiceFromEventType = exports.getFunctionService = exports.getTemporarySocketPath = exports.getEmulatedTriggersFromDefinitions = exports.emulatedFunctionsByRegion = exports.emulatedFunctionsFromEndpoints = exports.EmulatedTrigger = exports.HttpConstants = void 0;
|
|
3
|
+
exports.toBackendInfo = exports.getSecretLocalPath = exports.getSignatureType = exports.formatHost = exports.findModuleRoot = exports.waitForBody = exports.getServiceFromEventType = exports.getFunctionService = exports.getTemporarySocketPath = exports.getEmulatedTriggersFromDefinitions = exports.emulatedFunctionsByRegion = exports.emulatedFunctionsFromEndpoints = exports.EmulatedTrigger = exports.HttpConstants = void 0;
|
|
4
4
|
const _ = require("lodash");
|
|
5
5
|
const os = require("os");
|
|
6
6
|
const path = require("path");
|
|
@@ -10,6 +10,7 @@ const constants_1 = require("./constants");
|
|
|
10
10
|
const proto_1 = require("../gcp/proto");
|
|
11
11
|
const logger_1 = require("../logger");
|
|
12
12
|
const manifest_1 = require("../extensions/manifest");
|
|
13
|
+
const extensionsHelper_1 = require("../extensions/extensionsHelper");
|
|
13
14
|
const memoryLookup = {
|
|
14
15
|
"128MB": 128,
|
|
15
16
|
"256MB": 256,
|
|
@@ -65,6 +66,10 @@ function emulatedFunctionsFromEndpoints(endpoints) {
|
|
|
65
66
|
if (backend.isHttpsTriggered(endpoint)) {
|
|
66
67
|
def.httpsTrigger = endpoint.httpsTrigger;
|
|
67
68
|
}
|
|
69
|
+
else if (backend.isCallableTriggered(endpoint)) {
|
|
70
|
+
def.httpsTrigger = {};
|
|
71
|
+
def.labels = Object.assign(Object.assign({}, def.labels), { "deployment-callable": "true" });
|
|
72
|
+
}
|
|
68
73
|
else if (backend.isEventTriggered(endpoint)) {
|
|
69
74
|
const eventTrigger = endpoint.eventTrigger;
|
|
70
75
|
if (endpoint.platform === "gcfv1") {
|
|
@@ -238,3 +243,28 @@ function getSecretLocalPath(backend, projectDir) {
|
|
|
238
243
|
return path.join(secretDirectory, secretsFile);
|
|
239
244
|
}
|
|
240
245
|
exports.getSecretLocalPath = getSecretLocalPath;
|
|
246
|
+
function toBackendInfo(e, cf3Triggers) {
|
|
247
|
+
var _a;
|
|
248
|
+
const envWithSecrets = Object.assign({}, e.env);
|
|
249
|
+
for (const s of e.secretEnv) {
|
|
250
|
+
envWithSecrets[s.key] = backend.secretVersionName(s);
|
|
251
|
+
}
|
|
252
|
+
let extensionVersion = e.extensionVersion;
|
|
253
|
+
if (extensionVersion) {
|
|
254
|
+
extensionVersion = (0, extensionsHelper_1.substituteParams)(extensionVersion, e.env);
|
|
255
|
+
}
|
|
256
|
+
let extensionSpec = e.extensionSpec;
|
|
257
|
+
if (extensionSpec) {
|
|
258
|
+
extensionSpec = (0, extensionsHelper_1.substituteParams)(extensionSpec, e.env);
|
|
259
|
+
}
|
|
260
|
+
return JSON.parse(JSON.stringify({
|
|
261
|
+
directory: e.functionsDir,
|
|
262
|
+
env: envWithSecrets,
|
|
263
|
+
extensionInstanceId: e.extensionInstanceId,
|
|
264
|
+
extension: e.extension,
|
|
265
|
+
extensionVersion: extensionVersion,
|
|
266
|
+
extensionSpec: extensionSpec,
|
|
267
|
+
functionTriggers: (_a = e.predefinedTriggers) !== null && _a !== void 0 ? _a : cf3Triggers,
|
|
268
|
+
}));
|
|
269
|
+
}
|
|
270
|
+
exports.toBackendInfo = toBackendInfo;
|
|
@@ -52,8 +52,10 @@ function createFirebaseEndpoints(emulator) {
|
|
|
52
52
|
next();
|
|
53
53
|
});
|
|
54
54
|
}
|
|
55
|
-
firebaseStorageAPI.use((req, res, next) => {
|
|
56
|
-
|
|
55
|
+
firebaseStorageAPI.use(/.*\/b\/(.+?)\/.*/, (req, res, next) => {
|
|
56
|
+
const bucketId = req.params[0];
|
|
57
|
+
storageLayer.createBucket(bucketId);
|
|
58
|
+
if (!emulator.rulesManager.getRuleset(bucketId)) {
|
|
57
59
|
emulatorLogger_1.EmulatorLogger.forEmulator(types_1.Emulators.STORAGE).log("WARN", "Permission denied because no Storage ruleset is currently loaded, check your rules for syntax errors.");
|
|
58
60
|
return res.status(403).json({
|
|
59
61
|
error: {
|
|
@@ -64,10 +66,6 @@ function createFirebaseEndpoints(emulator) {
|
|
|
64
66
|
}
|
|
65
67
|
next();
|
|
66
68
|
});
|
|
67
|
-
firebaseStorageAPI.use(/.*\/b\/(.+?)\/.*/, (req, res, next) => {
|
|
68
|
-
storageLayer.createBucket(req.params[0]);
|
|
69
|
-
next();
|
|
70
|
-
});
|
|
71
69
|
firebaseStorageAPI.get("/b/:bucketId/o/:objectId", async (req, res) => {
|
|
72
70
|
var _a;
|
|
73
71
|
let metadata;
|
|
@@ -72,7 +72,7 @@ class StorageLayer {
|
|
|
72
72
|
const hasValidDownloadToken = ((metadata === null || metadata === void 0 ? void 0 : metadata.downloadTokens) || []).includes((_a = request.downloadToken) !== null && _a !== void 0 ? _a : "");
|
|
73
73
|
let authorized = skipAuth || hasValidDownloadToken;
|
|
74
74
|
if (!authorized) {
|
|
75
|
-
authorized = await this._rulesValidator.validate(["b", request.bucketId, "o", request.decodedObjectId].join("/"), types_1.RulesetOperationMethod.GET, { before: metadata === null || metadata === void 0 ? void 0 : metadata.asRulesResource() }, request.authorization);
|
|
75
|
+
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);
|
|
76
76
|
}
|
|
77
77
|
if (!authorized) {
|
|
78
78
|
throw new errors_1.ForbiddenError("Failed auth");
|
|
@@ -102,7 +102,7 @@ class StorageLayer {
|
|
|
102
102
|
async handleDeleteObject(request, skipAuth = false) {
|
|
103
103
|
const storedMetadata = this.getMetadata(request.bucketId, request.decodedObjectId);
|
|
104
104
|
const authorized = skipAuth ||
|
|
105
|
-
(await this._rulesValidator.validate(["b", request.bucketId, "o", request.decodedObjectId].join("/"), types_1.RulesetOperationMethod.DELETE, { before: storedMetadata === null || storedMetadata === void 0 ? void 0 : storedMetadata.asRulesResource() }, request.authorization));
|
|
105
|
+
(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));
|
|
106
106
|
if (!authorized) {
|
|
107
107
|
throw new errors_1.ForbiddenError();
|
|
108
108
|
}
|
|
@@ -134,7 +134,7 @@ class StorageLayer {
|
|
|
134
134
|
async handleUpdateObjectMetadata(request, skipAuth = false) {
|
|
135
135
|
const storedMetadata = this.getMetadata(request.bucketId, request.decodedObjectId);
|
|
136
136
|
const authorized = skipAuth ||
|
|
137
|
-
(await this._rulesValidator.validate(["b", request.bucketId, "o", request.decodedObjectId].join("/"), types_1.RulesetOperationMethod.UPDATE, {
|
|
137
|
+
(await this._rulesValidator.validate(["b", request.bucketId, "o", request.decodedObjectId].join("/"), request.bucketId, types_1.RulesetOperationMethod.UPDATE, {
|
|
138
138
|
before: storedMetadata === null || storedMetadata === void 0 ? void 0 : storedMetadata.asRulesResource(),
|
|
139
139
|
after: storedMetadata === null || storedMetadata === void 0 ? void 0 : storedMetadata.asRulesResource(request.metadata),
|
|
140
140
|
}, request.authorization));
|
|
@@ -163,7 +163,7 @@ class StorageLayer {
|
|
|
163
163
|
customMetadata: upload.metadata.metadata,
|
|
164
164
|
}, this._cloudFunctions, this._persistence.readBytes(upload.path, upload.size));
|
|
165
165
|
const authorized = skipAuth ||
|
|
166
|
-
(await this._rulesValidator.validate(["b", upload.bucketId, "o", upload.objectId].join("/"), types_1.RulesetOperationMethod.CREATE, { after: metadata === null || metadata === void 0 ? void 0 : metadata.asRulesResource() }, upload.authorization));
|
|
166
|
+
(await this._rulesValidator.validate(["b", upload.bucketId, "o", upload.objectId].join("/"), upload.bucketId, types_1.RulesetOperationMethod.CREATE, { after: metadata === null || metadata === void 0 ? void 0 : metadata.asRulesResource() }, upload.authorization));
|
|
167
167
|
if (!authorized) {
|
|
168
168
|
this._persistence.deleteFile(upload.path);
|
|
169
169
|
throw new errors_1.ForbiddenError();
|
|
@@ -210,7 +210,7 @@ class StorageLayer {
|
|
|
210
210
|
async handleListObjects(request, skipAuth = false) {
|
|
211
211
|
var _a, _b, _c;
|
|
212
212
|
const authorized = skipAuth ||
|
|
213
|
-
(await this._rulesValidator.validate(["b", request.bucketId, "o", request.prefix].join("/"), types_1.RulesetOperationMethod.LIST, {}, request.authorization));
|
|
213
|
+
(await this._rulesValidator.validate(["b", request.bucketId, "o", request.prefix].join("/"), request.bucketId, types_1.RulesetOperationMethod.LIST, {}, request.authorization));
|
|
214
214
|
if (!authorized) {
|
|
215
215
|
throw new errors_1.ForbiddenError();
|
|
216
216
|
}
|
|
@@ -18,9 +18,9 @@ class StorageEmulator {
|
|
|
18
18
|
this.args = args;
|
|
19
19
|
this._logger = emulatorLogger_1.EmulatorLogger.forEmulator(types_1.Emulators.STORAGE);
|
|
20
20
|
this._rulesRuntime = new runtime_1.StorageRulesRuntime();
|
|
21
|
-
this._rulesManager =
|
|
21
|
+
this._rulesManager = (0, manager_1.createStorageRulesManager)(this.args.rules, this._rulesRuntime);
|
|
22
22
|
this._persistence = new persistence_1.Persistence(this.getPersistenceTmpDir());
|
|
23
|
-
this._storageLayer = new files_1.StorageLayer(args.projectId, (0, utils_1.getRulesValidator)(() => this.
|
|
23
|
+
this._storageLayer = new files_1.StorageLayer(args.projectId, (0, utils_1.getRulesValidator)((resource) => this._rulesManager.getRuleset(resource)), (0, utils_1.getAdminCredentialValidator)(), this._persistence);
|
|
24
24
|
this._uploadService = new upload_1.UploadService(this._persistence);
|
|
25
25
|
}
|
|
26
26
|
get storageLayer() {
|
|
@@ -29,8 +29,8 @@ class StorageEmulator {
|
|
|
29
29
|
get uploadService() {
|
|
30
30
|
return this._uploadService;
|
|
31
31
|
}
|
|
32
|
-
get
|
|
33
|
-
return this._rulesManager
|
|
32
|
+
get rulesManager() {
|
|
33
|
+
return this._rulesManager;
|
|
34
34
|
}
|
|
35
35
|
get logger() {
|
|
36
36
|
return this._logger;
|
|
@@ -43,19 +43,16 @@ class StorageEmulator {
|
|
|
43
43
|
async start() {
|
|
44
44
|
const { host, port } = this.getInfo();
|
|
45
45
|
await this._rulesRuntime.start(this.args.auto_download);
|
|
46
|
-
await this._rulesManager.
|
|
46
|
+
await this._rulesManager.start();
|
|
47
47
|
this._app = await (0, server_1.createApp)(this.args.projectId, this);
|
|
48
48
|
const server = this._app.listen(port, host);
|
|
49
49
|
this.destroyServer = utils.createDestroyer(server);
|
|
50
50
|
}
|
|
51
51
|
async connect() {
|
|
52
52
|
}
|
|
53
|
-
async setRules(rules) {
|
|
54
|
-
return this._rulesManager.setSourceFile(rules);
|
|
55
|
-
}
|
|
56
53
|
async stop() {
|
|
57
54
|
await this._persistence.deleteAll();
|
|
58
|
-
await this._rulesManager.
|
|
55
|
+
await this._rulesManager.stop();
|
|
59
56
|
return this.destroyServer ? this.destroyServer() : Promise.resolve();
|
|
60
57
|
}
|
|
61
58
|
getInfo() {
|
|
@@ -2,8 +2,10 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.getStorageRulesConfig = void 0;
|
|
4
4
|
const error_1 = require("../../../error");
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
const fsutils_1 = require("../../../fsutils");
|
|
6
|
+
function getSourceFile(rules, options) {
|
|
7
|
+
const path = options.config.path(rules);
|
|
8
|
+
return { name: path, content: (0, fsutils_1.readFile)(path) };
|
|
7
9
|
}
|
|
8
10
|
function getStorageRulesConfig(projectId, options) {
|
|
9
11
|
const storageConfig = options.config.data.storage;
|
|
@@ -14,8 +16,7 @@ function getStorageRulesConfig(projectId, options) {
|
|
|
14
16
|
if (!storageConfig.rules) {
|
|
15
17
|
throw new error_1.FirebaseError("Cannot start the Storage emulator without rules file specified in firebase.json: run 'firebase init' and set up your Storage configuration");
|
|
16
18
|
}
|
|
17
|
-
|
|
18
|
-
return [{ resource, rules: getAbsoluteRulesPath(storageConfig.rules, options) }];
|
|
19
|
+
return getSourceFile(storageConfig.rules, options);
|
|
19
20
|
}
|
|
20
21
|
const results = [];
|
|
21
22
|
const { rc } = options;
|
|
@@ -25,7 +26,7 @@ function getStorageRulesConfig(projectId, options) {
|
|
|
25
26
|
}
|
|
26
27
|
rc.requireTarget(projectId, "storage", targetConfig.target);
|
|
27
28
|
rc.target(projectId, "storage", targetConfig.target).forEach((resource) => {
|
|
28
|
-
results.push({ resource, rules:
|
|
29
|
+
results.push({ resource, rules: getSourceFile(targetConfig.rules, options) });
|
|
29
30
|
});
|
|
30
31
|
}
|
|
31
32
|
return results;
|
|
@@ -1,39 +1,37 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.createStorageRulesManager = void 0;
|
|
4
4
|
const chokidar = require("chokidar");
|
|
5
|
-
const fs = require("fs");
|
|
6
5
|
const emulatorLogger_1 = require("../../emulatorLogger");
|
|
7
6
|
const types_1 = require("../../types");
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
const runtime_1 = require("./runtime");
|
|
8
|
+
function createStorageRulesManager(rules, runtime) {
|
|
9
|
+
return Array.isArray(rules)
|
|
10
|
+
? new ResourceBasedStorageRulesManager(rules, runtime)
|
|
11
|
+
: new DefaultStorageRulesManager(rules, runtime);
|
|
12
|
+
}
|
|
13
|
+
exports.createStorageRulesManager = createStorageRulesManager;
|
|
14
|
+
class DefaultStorageRulesManager {
|
|
15
|
+
constructor(_rules, _runtime) {
|
|
11
16
|
this._runtime = _runtime;
|
|
12
17
|
this._watcher = new chokidar.FSWatcher();
|
|
13
18
|
this._logger = emulatorLogger_1.EmulatorLogger.forEmulator(types_1.Emulators.STORAGE);
|
|
19
|
+
this._rules = _rules;
|
|
20
|
+
}
|
|
21
|
+
start() {
|
|
22
|
+
return this.updateSourceFile(this._rules);
|
|
14
23
|
}
|
|
15
|
-
|
|
24
|
+
getRuleset() {
|
|
16
25
|
return this._ruleset;
|
|
17
26
|
}
|
|
18
|
-
async
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
let rulesFile;
|
|
22
|
-
if (typeof rules === "string") {
|
|
23
|
-
this._sourceFile = { name: rules, content: readSourceFile(rules) };
|
|
24
|
-
rulesFile = rules;
|
|
25
|
-
}
|
|
26
|
-
else {
|
|
27
|
-
this._sourceFile = rules;
|
|
28
|
-
rulesFile = rules.name;
|
|
29
|
-
}
|
|
27
|
+
async updateSourceFile(rules) {
|
|
28
|
+
const prevRulesFile = this._rules.name;
|
|
29
|
+
this._rules = rules;
|
|
30
30
|
const issues = await this.loadRuleset();
|
|
31
|
-
this.updateWatcher(
|
|
31
|
+
this.updateWatcher(rules.name, prevRulesFile);
|
|
32
32
|
return issues;
|
|
33
33
|
}
|
|
34
|
-
async
|
|
35
|
-
delete this._sourceFile;
|
|
36
|
-
delete this._ruleset;
|
|
34
|
+
async stop() {
|
|
37
35
|
await this._watcher.close();
|
|
38
36
|
}
|
|
39
37
|
updateWatcher(rulesFile, prevRulesFile) {
|
|
@@ -49,12 +47,11 @@ class StorageRulesManager {
|
|
|
49
47
|
});
|
|
50
48
|
}
|
|
51
49
|
async loadRuleset() {
|
|
52
|
-
const { ruleset, issues } = await this._runtime.loadRuleset({ files: [this.
|
|
50
|
+
const { ruleset, issues } = await this._runtime.loadRuleset({ files: [this._rules] });
|
|
53
51
|
if (ruleset) {
|
|
54
52
|
this._ruleset = ruleset;
|
|
55
53
|
return issues;
|
|
56
54
|
}
|
|
57
|
-
delete this._ruleset;
|
|
58
55
|
issues.all.forEach((issue) => {
|
|
59
56
|
try {
|
|
60
57
|
const parsedIssue = JSON.parse(issue);
|
|
@@ -67,15 +64,35 @@ class StorageRulesManager {
|
|
|
67
64
|
return issues;
|
|
68
65
|
}
|
|
69
66
|
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
67
|
+
class ResourceBasedStorageRulesManager {
|
|
68
|
+
constructor(_rulesConfig, _runtime) {
|
|
69
|
+
this._runtime = _runtime;
|
|
70
|
+
this._rulesManagers = new Map();
|
|
71
|
+
for (const { resource, rules } of _rulesConfig) {
|
|
72
|
+
this.createRulesManager(resource, rules);
|
|
73
|
+
}
|
|
74
74
|
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
75
|
+
async start() {
|
|
76
|
+
const allIssues = new runtime_1.StorageRulesIssues();
|
|
77
|
+
for (const rulesManager of this._rulesManagers.values()) {
|
|
78
|
+
allIssues.extend(await rulesManager.start());
|
|
78
79
|
}
|
|
79
|
-
|
|
80
|
+
return allIssues;
|
|
81
|
+
}
|
|
82
|
+
getRuleset(resource) {
|
|
83
|
+
var _a;
|
|
84
|
+
return (_a = this._rulesManagers.get(resource)) === null || _a === void 0 ? void 0 : _a.getRuleset();
|
|
85
|
+
}
|
|
86
|
+
updateSourceFile(rules, resource) {
|
|
87
|
+
const rulesManager = this._rulesManagers.get(resource) || this.createRulesManager(resource, rules);
|
|
88
|
+
return rulesManager.updateSourceFile(rules);
|
|
89
|
+
}
|
|
90
|
+
async stop() {
|
|
91
|
+
await Promise.all(Array.from(this._rulesManagers.values(), async (rulesManager) => await rulesManager.stop()));
|
|
92
|
+
}
|
|
93
|
+
createRulesManager(resource, rules) {
|
|
94
|
+
const rulesManager = new DefaultStorageRulesManager(rules, this._runtime);
|
|
95
|
+
this._rulesManagers.set(resource, rulesManager);
|
|
96
|
+
return rulesManager;
|
|
80
97
|
}
|
|
81
98
|
}
|
|
@@ -49,6 +49,10 @@ class StorageRulesIssues {
|
|
|
49
49
|
exist() {
|
|
50
50
|
return !!(this.errors.length || this.warnings.length);
|
|
51
51
|
}
|
|
52
|
+
extend(other) {
|
|
53
|
+
this.errors.push(...other.errors);
|
|
54
|
+
this.warnings.push(...other.warnings);
|
|
55
|
+
}
|
|
52
56
|
}
|
|
53
57
|
exports.StorageRulesIssues = StorageRulesIssues;
|
|
54
58
|
class StorageRulesRuntime {
|
|
@@ -5,9 +5,9 @@ const emulatorLogger_1 = require("../../emulatorLogger");
|
|
|
5
5
|
const types_1 = require("../../types");
|
|
6
6
|
function getRulesValidator(rulesetProvider) {
|
|
7
7
|
return {
|
|
8
|
-
validate: async (path, method, variableOverrides, authorization) => {
|
|
8
|
+
validate: async (path, bucketId, method, variableOverrides, authorization) => {
|
|
9
9
|
return await isPermitted({
|
|
10
|
-
ruleset: rulesetProvider(),
|
|
10
|
+
ruleset: rulesetProvider(bucketId),
|
|
11
11
|
file: variableOverrides,
|
|
12
12
|
path,
|
|
13
13
|
method,
|
|
@@ -58,7 +58,7 @@ function createApp(defaultProjectId, emulator) {
|
|
|
58
58
|
}
|
|
59
59
|
const name = file.name;
|
|
60
60
|
const content = file.content;
|
|
61
|
-
const issues = await emulator.
|
|
61
|
+
const issues = await emulator.rulesManager.updateSourceFile({ name, content }, req.params.bucketId);
|
|
62
62
|
if (issues.errors.length > 0) {
|
|
63
63
|
res.status(400).json({
|
|
64
64
|
message: "There was an error updating rules, see logs for more details",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getInquirerDefault = exports.promptCreateSecret = exports.askForParam = exports.ask = exports.checkResponse = void 0;
|
|
3
|
+
exports.getInquirerDefault = exports.promptCreateSecret = exports.askForParam = exports.ask = exports.checkResponse = exports.SecretLocation = void 0;
|
|
4
4
|
const _ = require("lodash");
|
|
5
5
|
const clc = require("cli-color");
|
|
6
6
|
const { marked } = require("marked");
|
|
@@ -12,10 +12,16 @@ const utils_1 = require("./utils");
|
|
|
12
12
|
const logger_1 = require("../logger");
|
|
13
13
|
const prompt_1 = require("../prompt");
|
|
14
14
|
const utils = require("../utils");
|
|
15
|
+
const projectUtils_1 = require("../projectUtils");
|
|
16
|
+
var SecretLocation;
|
|
17
|
+
(function (SecretLocation) {
|
|
18
|
+
SecretLocation[SecretLocation["CLOUD"] = 1] = "CLOUD";
|
|
19
|
+
SecretLocation[SecretLocation["LOCAL"] = 2] = "LOCAL";
|
|
20
|
+
})(SecretLocation = exports.SecretLocation || (exports.SecretLocation = {}));
|
|
15
21
|
var SecretUpdateAction;
|
|
16
22
|
(function (SecretUpdateAction) {
|
|
17
|
-
SecretUpdateAction[SecretUpdateAction["LEAVE"] =
|
|
18
|
-
SecretUpdateAction[SecretUpdateAction["SET_NEW"] =
|
|
23
|
+
SecretUpdateAction[SecretUpdateAction["LEAVE"] = 1] = "LEAVE";
|
|
24
|
+
SecretUpdateAction[SecretUpdateAction["SET_NEW"] = 2] = "SET_NEW";
|
|
19
25
|
})(SecretUpdateAction || (SecretUpdateAction = {}));
|
|
20
26
|
function checkResponse(response, spec) {
|
|
21
27
|
let valid = true;
|
|
@@ -55,21 +61,21 @@ function checkResponse(response, spec) {
|
|
|
55
61
|
return valid;
|
|
56
62
|
}
|
|
57
63
|
exports.checkResponse = checkResponse;
|
|
58
|
-
async function ask(
|
|
59
|
-
if (_.isEmpty(paramSpecs)) {
|
|
64
|
+
async function ask(args) {
|
|
65
|
+
if (_.isEmpty(args.paramSpecs)) {
|
|
60
66
|
logger_1.logger.debug("No params were specified for this extension.");
|
|
61
67
|
return {};
|
|
62
68
|
}
|
|
63
69
|
utils.logLabeledBullet(extensionsHelper_1.logPrefix, "answer the questions below to configure your extension:");
|
|
64
|
-
const substituted = (0, extensionsHelper_1.substituteParams)(paramSpecs, firebaseProjectParams);
|
|
70
|
+
const substituted = (0, extensionsHelper_1.substituteParams)(args.paramSpecs, args.firebaseProjectParams);
|
|
65
71
|
const result = {};
|
|
66
72
|
const promises = _.map(substituted, (paramSpec) => {
|
|
67
73
|
return async () => {
|
|
68
74
|
result[paramSpec.param] = await askForParam({
|
|
69
|
-
projectId,
|
|
70
|
-
instanceId,
|
|
71
|
-
paramSpec,
|
|
72
|
-
reconfiguring,
|
|
75
|
+
projectId: args.projectId,
|
|
76
|
+
instanceId: args.instanceId,
|
|
77
|
+
paramSpec: paramSpec,
|
|
78
|
+
reconfiguring: args.reconfiguring,
|
|
73
79
|
});
|
|
74
80
|
};
|
|
75
81
|
});
|
|
@@ -82,6 +88,8 @@ async function askForParam(args) {
|
|
|
82
88
|
const paramSpec = args.paramSpec;
|
|
83
89
|
let valid = false;
|
|
84
90
|
let response = "";
|
|
91
|
+
let responseForLocal;
|
|
92
|
+
let secretLocations = [];
|
|
85
93
|
const description = paramSpec.description || "";
|
|
86
94
|
const label = paramSpec.label.trim();
|
|
87
95
|
logger_1.logger.info(`\n${clc.bold(label)}${clc.bold(paramSpec.required ? "" : " (Optional)")}: ${marked(description).trim()}`);
|
|
@@ -116,16 +124,24 @@ async function askForParam(args) {
|
|
|
116
124
|
}
|
|
117
125
|
},
|
|
118
126
|
message: "Which options do you want enabled for this parameter? " +
|
|
119
|
-
"Press Space to select, then Enter to confirm your choices. "
|
|
120
|
-
"You may select multiple options.",
|
|
127
|
+
"Press Space to select, then Enter to confirm your choices. ",
|
|
121
128
|
choices: (0, utils_1.convertExtensionOptionToLabeledList)(paramSpec.options),
|
|
122
129
|
});
|
|
123
130
|
valid = checkResponse(response, paramSpec);
|
|
124
131
|
break;
|
|
125
132
|
case extensionsApi_1.ParamType.SECRET:
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
133
|
+
do {
|
|
134
|
+
secretLocations = await promptSecretLocations(paramSpec);
|
|
135
|
+
} while (!isValidSecretLocations(secretLocations, paramSpec));
|
|
136
|
+
if (secretLocations.includes(SecretLocation.CLOUD.toString())) {
|
|
137
|
+
const projectId = (0, projectUtils_1.needProjectId)({ projectId: args.projectId });
|
|
138
|
+
response = args.reconfiguring
|
|
139
|
+
? await promptReconfigureSecret(projectId, args.instanceId, paramSpec)
|
|
140
|
+
: await promptCreateSecret(projectId, args.instanceId, paramSpec);
|
|
141
|
+
}
|
|
142
|
+
if (secretLocations.includes(SecretLocation.LOCAL.toString())) {
|
|
143
|
+
responseForLocal = await promptLocalSecret(args.instanceId, paramSpec);
|
|
144
|
+
}
|
|
129
145
|
valid = true;
|
|
130
146
|
break;
|
|
131
147
|
default:
|
|
@@ -138,9 +154,64 @@ async function askForParam(args) {
|
|
|
138
154
|
valid = checkResponse(response, paramSpec);
|
|
139
155
|
}
|
|
140
156
|
}
|
|
141
|
-
return { baseValue: response };
|
|
157
|
+
return Object.assign({ baseValue: response }, (responseForLocal ? { local: responseForLocal } : {}));
|
|
142
158
|
}
|
|
143
159
|
exports.askForParam = askForParam;
|
|
160
|
+
function isValidSecretLocations(secretLocations, paramSpec) {
|
|
161
|
+
if (paramSpec.required) {
|
|
162
|
+
return !!secretLocations.length;
|
|
163
|
+
}
|
|
164
|
+
return true;
|
|
165
|
+
}
|
|
166
|
+
async function promptSecretLocations(paramSpec) {
|
|
167
|
+
if (paramSpec.required) {
|
|
168
|
+
return await (0, prompt_1.promptOnce)({
|
|
169
|
+
name: "input",
|
|
170
|
+
type: "checkbox",
|
|
171
|
+
message: "Where would you like to store your secrets? You must select at least one value",
|
|
172
|
+
choices: [
|
|
173
|
+
{
|
|
174
|
+
checked: true,
|
|
175
|
+
name: "Google Cloud Secret Manager",
|
|
176
|
+
value: SecretLocation.CLOUD.toString(),
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
checked: false,
|
|
180
|
+
name: "Local file (Only used by Firebase Emulator)",
|
|
181
|
+
value: SecretLocation.LOCAL.toString(),
|
|
182
|
+
},
|
|
183
|
+
],
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
return await (0, prompt_1.promptOnce)({
|
|
187
|
+
name: "input",
|
|
188
|
+
type: "checkbox",
|
|
189
|
+
message: "Where would you like to store your secrets? " +
|
|
190
|
+
"If you don't want to set this optional secret, leave both options unselected to skip it",
|
|
191
|
+
choices: [
|
|
192
|
+
{
|
|
193
|
+
checked: false,
|
|
194
|
+
name: "Google Cloud Secret Manager",
|
|
195
|
+
value: SecretLocation.CLOUD.toString(),
|
|
196
|
+
},
|
|
197
|
+
{
|
|
198
|
+
checked: false,
|
|
199
|
+
name: "Local file (Only used by Firebase Emulator)",
|
|
200
|
+
value: SecretLocation.LOCAL.toString(),
|
|
201
|
+
},
|
|
202
|
+
],
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
async function promptLocalSecret(instanceId, paramSpec) {
|
|
206
|
+
utils.logLabeledBullet(extensionsHelper_1.logPrefix, "Configure a local secret value for Extensions Emulator");
|
|
207
|
+
const value = await (0, prompt_1.promptOnce)({
|
|
208
|
+
name: paramSpec.param,
|
|
209
|
+
type: "input",
|
|
210
|
+
message: `This secret will be stored in ./extensions/${instanceId}.secret.local.\n` +
|
|
211
|
+
`Enter value for "${paramSpec.label.trim()}" to be used by Extensions Emulator:`,
|
|
212
|
+
});
|
|
213
|
+
return value;
|
|
214
|
+
}
|
|
144
215
|
async function promptReconfigureSecret(projectId, instanceId, paramSpec) {
|
|
145
216
|
const action = await (0, prompt_1.promptOnce)({
|
|
146
217
|
type: "list",
|
|
@@ -80,6 +80,9 @@ function getDBInstanceFromURL(databaseUrl = "") {
|
|
|
80
80
|
exports.getDBInstanceFromURL = getDBInstanceFromURL;
|
|
81
81
|
async function getFirebaseProjectParams(projectId, emulatorMode = false) {
|
|
82
82
|
var _a, _b;
|
|
83
|
+
if (!projectId) {
|
|
84
|
+
return {};
|
|
85
|
+
}
|
|
83
86
|
const body = emulatorMode
|
|
84
87
|
? await (0, adminSdkConfig_1.getProjectAdminSdkConfigOrCached)(projectId)
|
|
85
88
|
: await (0, functionsConfig_1.getFirebaseConfig)({ project: projectId });
|
|
@@ -263,7 +266,10 @@ async function promptForValidInstanceId(instanceId) {
|
|
|
263
266
|
}
|
|
264
267
|
exports.promptForValidInstanceId = promptForValidInstanceId;
|
|
265
268
|
async function ensureExtensionsApiEnabled(options) {
|
|
266
|
-
const projectId = (0, projectUtils_1.
|
|
269
|
+
const projectId = (0, projectUtils_1.getProjectId)(options);
|
|
270
|
+
if (!projectId) {
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
267
273
|
return await (0, ensureApiEnabled_1.ensure)(projectId, "firebaseextensions.googleapis.com", "extensions", options.markdown);
|
|
268
274
|
}
|
|
269
275
|
exports.ensureExtensionsApiEnabled = ensureExtensionsApiEnabled;
|
|
@@ -519,7 +525,10 @@ async function confirm(args) {
|
|
|
519
525
|
}
|
|
520
526
|
exports.confirm = confirm;
|
|
521
527
|
async function diagnoseAndFixProject(options) {
|
|
522
|
-
const projectId = (0, projectUtils_1.
|
|
528
|
+
const projectId = (0, projectUtils_1.getProjectId)(options);
|
|
529
|
+
if (!projectId) {
|
|
530
|
+
return;
|
|
531
|
+
}
|
|
523
532
|
const ok = await (0, diagnose_1.diagnose)(projectId);
|
|
524
533
|
if (!ok) {
|
|
525
534
|
throw new error_1.FirebaseError("Unable to proceed until all issues are resolved.");
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.showPreviewWarning = exports.showDeprecationWarning = exports.readInstanceParam = exports.getInstanceRef = exports.instanceExists = exports.loadConfig = exports.removeFromManifest = exports.writeToManifest = exports.ENV_DIRECTORY = void 0;
|
|
3
|
+
exports.showPreviewWarning = exports.showDeprecationWarning = exports.readInstanceParam = exports.getInstanceRef = exports.instanceExists = exports.loadConfig = exports.removeFromManifest = exports.writeLocalSecrets = exports.writeToManifest = exports.ENV_DIRECTORY = void 0;
|
|
4
4
|
const clc = require("cli-color");
|
|
5
5
|
const path = require("path");
|
|
6
6
|
const refs = require("./refs");
|
|
@@ -11,6 +11,7 @@ const paramHelper_1 = require("./paramHelper");
|
|
|
11
11
|
const error_1 = require("../error");
|
|
12
12
|
const utils = require("../utils");
|
|
13
13
|
const extensionsHelper_1 = require("./extensionsHelper");
|
|
14
|
+
const extensionsApi_1 = require("./extensionsApi");
|
|
14
15
|
exports.ENV_DIRECTORY = "extensions";
|
|
15
16
|
async function writeToManifest(specs, config, options, allowOverwrite = false) {
|
|
16
17
|
if (config.has("extensions") &&
|
|
@@ -36,8 +37,33 @@ async function writeToManifest(specs, config, options, allowOverwrite = false) {
|
|
|
36
37
|
}
|
|
37
38
|
writeExtensionsToFirebaseJson(specs, config);
|
|
38
39
|
await writeEnvFiles(specs, config, options.force);
|
|
40
|
+
await writeLocalSecrets(specs, config, options.force);
|
|
39
41
|
}
|
|
40
42
|
exports.writeToManifest = writeToManifest;
|
|
43
|
+
async function writeLocalSecrets(specs, config, force) {
|
|
44
|
+
for (const spec of specs) {
|
|
45
|
+
if (!spec.paramSpecs) {
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
const writeBuffer = {};
|
|
49
|
+
const locallyOverridenSecretParams = spec.paramSpecs.filter((p) => p.type === extensionsApi_1.ParamType.SECRET && spec.params[p.param].local);
|
|
50
|
+
for (const paramSpec of locallyOverridenSecretParams) {
|
|
51
|
+
const key = paramSpec.param;
|
|
52
|
+
const localValue = spec.params[key].local;
|
|
53
|
+
writeBuffer[key] = localValue;
|
|
54
|
+
}
|
|
55
|
+
const content = Object.entries(writeBuffer)
|
|
56
|
+
.sort((a, b) => {
|
|
57
|
+
return a[0].localeCompare(b[0]);
|
|
58
|
+
})
|
|
59
|
+
.map((r) => `${r[0]}=${r[1]}`)
|
|
60
|
+
.join("\n");
|
|
61
|
+
if (content) {
|
|
62
|
+
await config.askWriteProjectFile(`extensions/${spec.instanceId}.secret.local`, content, force);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
exports.writeLocalSecrets = writeLocalSecrets;
|
|
41
67
|
function removeFromManifest(instanceId, config) {
|
|
42
68
|
if (!instanceExists(instanceId, config)) {
|
|
43
69
|
throw new error_1.FirebaseError(`Extension instance ${instanceId} not found in firebase.json.`);
|
|
@@ -49,8 +75,14 @@ function removeFromManifest(instanceId, config) {
|
|
|
49
75
|
logger_1.logger.info(`Removed extension instance ${instanceId} from firebase.json`);
|
|
50
76
|
config.deleteProjectFile(`extensions/${instanceId}.env`);
|
|
51
77
|
logger_1.logger.info(`Removed extension instance environment config extensions/${instanceId}.env`);
|
|
52
|
-
config.
|
|
53
|
-
|
|
78
|
+
if (config.projectFileExists(`extensions/${instanceId}.env.local`)) {
|
|
79
|
+
config.deleteProjectFile(`extensions/${instanceId}.env.local`);
|
|
80
|
+
logger_1.logger.info(`Removed extension instance local environment config extensions/${instanceId}.env.local`);
|
|
81
|
+
}
|
|
82
|
+
if (config.projectFileExists(`extensions/${instanceId}.secret.local`)) {
|
|
83
|
+
config.deleteProjectFile(`extensions/${instanceId}.secret.local`);
|
|
84
|
+
logger_1.logger.info(`Removed extension instance local secret config extensions/${instanceId}.secret.local`);
|
|
85
|
+
}
|
|
54
86
|
}
|
|
55
87
|
exports.removeFromManifest = removeFromManifest;
|
|
56
88
|
function loadConfig(options) {
|
|
@@ -88,7 +120,7 @@ async function writeEnvFiles(specs, config, force) {
|
|
|
88
120
|
.sort((a, b) => {
|
|
89
121
|
return a[0].localeCompare(b[0]);
|
|
90
122
|
})
|
|
91
|
-
.map((r) => `${r[0]}=${r[1]}`)
|
|
123
|
+
.map((r) => `${r[0]}=${r[1].baseValue}`)
|
|
92
124
|
.join("\n");
|
|
93
125
|
await config.askWriteProjectFile(`extensions/${spec.instanceId}.env`, content, force);
|
|
94
126
|
}
|