firebase-tools 10.3.1 → 10.4.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/commands/deploy.js +1 -1
- package/lib/commands/experimental-functions-shell.js +1 -1
- package/lib/commands/ext-configure.js +3 -2
- package/lib/commands/ext-export.js +7 -1
- package/lib/commands/ext-install.js +2 -2
- package/lib/commands/ext-update.js +2 -2
- package/lib/commands/functions-config-export.js +2 -2
- 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 +3 -0
- package/lib/deploy/extensions/planner.js +9 -3
- package/lib/emulator/auth/apiSpec.js +37 -0
- package/lib/emulator/commandUtils.js +2 -2
- package/lib/emulator/extensionsEmulator.js +3 -0
- package/lib/emulator/functionsEmulatorShared.js +4 -0
- 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 +52 -9
- package/lib/extensions/manifest.js +36 -4
- package/lib/extensions/paramHelper.js +4 -3
- package/lib/fsutils.js +14 -1
- package/lib/requireConfig.js +11 -9
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
- package/lib/prepareUpload.js +0 -44
package/lib/commands/deploy.js
CHANGED
|
@@ -6,7 +6,7 @@ const { checkServiceAccountIam } = require("../deploy/functions/checkIam");
|
|
|
6
6
|
const checkValidTargetFilters = require("../checkValidTargetFilters");
|
|
7
7
|
const { Command } = require("../command");
|
|
8
8
|
const deploy = require("../deploy");
|
|
9
|
-
const requireConfig = require("../requireConfig");
|
|
9
|
+
const { requireConfig } = require("../requireConfig");
|
|
10
10
|
const { filterTargets } = require("../filterTargets");
|
|
11
11
|
const { requireHostingSite } = require("../requireHostingSite");
|
|
12
12
|
const VALID_TARGETS = [
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
var { Command } = require("../command");
|
|
3
3
|
var { requirePermissions } = require("../requirePermissions");
|
|
4
4
|
var { actionFunction } = require("../functionsShellCommandAction");
|
|
5
|
-
var requireConfig = require("../requireConfig");
|
|
5
|
+
var { requireConfig } = require("../requireConfig");
|
|
6
6
|
module.exports = new Command("experimental:functions:shell")
|
|
7
7
|
.description("launch full Node shell with emulated functions. (Alias for `firebase functions:shell.)")
|
|
8
8
|
.option("-p, --port <port>", "the port on which to emulate functions (default: 5000)", 5000)
|
|
@@ -58,12 +58,13 @@ exports.default = new command_1.Command("ext:configure <extensionInstanceId>")
|
|
|
58
58
|
instanceId,
|
|
59
59
|
reconfiguring: true,
|
|
60
60
|
});
|
|
61
|
-
const
|
|
61
|
+
const newParamOptions = Object.assign(Object.assign({}, (0, paramHelper_1.buildBindingOptionsWithBaseValue)(oldParamValues)), mutableParamsBindingOptions);
|
|
62
62
|
await manifest.writeToManifest([
|
|
63
63
|
{
|
|
64
64
|
instanceId,
|
|
65
65
|
ref: targetRef,
|
|
66
|
-
params:
|
|
66
|
+
params: newParamOptions,
|
|
67
|
+
paramSpecs: extensionVersion.spec.params,
|
|
67
68
|
},
|
|
68
69
|
], config, {
|
|
69
70
|
nonInteractive: false,
|
|
@@ -6,6 +6,7 @@ const planner = require("../deploy/extensions/planner");
|
|
|
6
6
|
const export_1 = require("../extensions/export");
|
|
7
7
|
const extensionsHelper_1 = require("../extensions/extensionsHelper");
|
|
8
8
|
const manifest = require("../extensions/manifest");
|
|
9
|
+
const paramHelper_1 = require("../extensions/paramHelper");
|
|
9
10
|
const functional_1 = require("../functional");
|
|
10
11
|
const getProjectNumber_1 = require("../getProjectNumber");
|
|
11
12
|
const logger_1 = require("../logger");
|
|
@@ -41,8 +42,13 @@ module.exports = new command_1.Command("ext:export")
|
|
|
41
42
|
logger_1.logger.info("Exiting. No changes made.");
|
|
42
43
|
return;
|
|
43
44
|
}
|
|
45
|
+
const manifestSpecs = withRef.map((spec) => ({
|
|
46
|
+
instanceId: spec.instanceId,
|
|
47
|
+
ref: spec.ref,
|
|
48
|
+
params: (0, paramHelper_1.buildBindingOptionsWithBaseValue)(spec.params),
|
|
49
|
+
}));
|
|
44
50
|
const existingConfig = manifest.loadConfig(options);
|
|
45
|
-
await manifest.writeToManifest(
|
|
51
|
+
await manifest.writeToManifest(manifestSpecs, existingConfig, {
|
|
46
52
|
nonInteractive: options.nonInteractive,
|
|
47
53
|
force: options.force,
|
|
48
54
|
}, true);
|
|
@@ -183,13 +183,13 @@ async function installToManifest(options) {
|
|
|
183
183
|
paramsEnvPath,
|
|
184
184
|
instanceId,
|
|
185
185
|
});
|
|
186
|
-
const params = (0, paramHelper_1.getBaseParamBindings)(paramBindingOptions);
|
|
187
186
|
const ref = refs.parse(extVersion.ref);
|
|
188
187
|
await manifest.writeToManifest([
|
|
189
188
|
{
|
|
190
189
|
instanceId,
|
|
191
190
|
ref,
|
|
192
|
-
params,
|
|
191
|
+
params: paramBindingOptions,
|
|
192
|
+
paramSpecs: spec.params,
|
|
193
193
|
},
|
|
194
194
|
], config, { nonInteractive, force: force !== null && force !== void 0 ? force : false });
|
|
195
195
|
manifest.showPreviewWarning();
|
|
@@ -79,12 +79,12 @@ exports.default = new command_1.Command("ext:update <extensionInstanceId> [updat
|
|
|
79
79
|
nonInteractive: options.nonInteractive,
|
|
80
80
|
instanceId,
|
|
81
81
|
});
|
|
82
|
-
const newParamBindings = paramHelper.getBaseParamBindings(newParamBindingOptions);
|
|
83
82
|
await manifest.writeToManifest([
|
|
84
83
|
{
|
|
85
84
|
instanceId,
|
|
86
85
|
ref: refs.parse(newExtensionVersion.ref),
|
|
87
|
-
params:
|
|
86
|
+
params: newParamBindingOptions,
|
|
87
|
+
paramSpecs: newExtensionVersion.spec.params,
|
|
88
88
|
},
|
|
89
89
|
], config, {
|
|
90
90
|
nonInteractive: options.nonInteractive,
|
|
@@ -12,7 +12,7 @@ const requirePermissions_1 = require("../requirePermissions");
|
|
|
12
12
|
const utils_1 = require("../utils");
|
|
13
13
|
const functional_1 = require("../functional");
|
|
14
14
|
const configExport = require("../functions/runtimeConfigExport");
|
|
15
|
-
const
|
|
15
|
+
const requireConfig_1 = require("../requireConfig");
|
|
16
16
|
const REQUIRED_PERMISSIONS = [
|
|
17
17
|
"runtimeconfig.configs.list",
|
|
18
18
|
"runtimeconfig.configs.get",
|
|
@@ -76,7 +76,7 @@ exports.default = new command_1.Command("functions:config:export")
|
|
|
76
76
|
"runtimeconfig.variables.list",
|
|
77
77
|
"runtimeconfig.variables.get",
|
|
78
78
|
])
|
|
79
|
-
.before(requireConfig)
|
|
79
|
+
.before(requireConfig_1.requireConfig)
|
|
80
80
|
.before(requireInteractive_1.default)
|
|
81
81
|
.action(async (options) => {
|
|
82
82
|
let pInfos = configExport.getProjectInfos(options);
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
var { Command } = require("../command");
|
|
3
3
|
var { requirePermissions } = require("../requirePermissions");
|
|
4
4
|
var { actionFunction } = require("../functionsShellCommandAction");
|
|
5
|
-
var requireConfig = require("../requireConfig");
|
|
5
|
+
var { requireConfig } = require("../requireConfig");
|
|
6
6
|
var commandUtils = require("../emulator/commandUtils");
|
|
7
7
|
module.exports = new Command("functions:shell")
|
|
8
8
|
.description("launch full Node shell with emulated functions")
|
|
@@ -10,7 +10,7 @@ const prompt_1 = require("../prompt");
|
|
|
10
10
|
const requirePermissions_1 = require("../requirePermissions");
|
|
11
11
|
const projectUtils_1 = require("../projectUtils");
|
|
12
12
|
const logger_1 = require("../logger");
|
|
13
|
-
const
|
|
13
|
+
const requireConfig_1 = require("../requireConfig");
|
|
14
14
|
const { marked } = require("marked");
|
|
15
15
|
const requireHostingSite_1 = require("../requireHostingSite");
|
|
16
16
|
const LOG_TAG = "hosting:channel";
|
|
@@ -18,7 +18,7 @@ exports.default = new command_1.Command("hosting:channel:create [channelId]")
|
|
|
18
18
|
.description("create a Firebase Hosting channel")
|
|
19
19
|
.option("-e, --expires <duration>", "duration string (e.g. 12h or 30d) for channel expiration, max 30d")
|
|
20
20
|
.option("--site <siteId>", "site for which to create the channel")
|
|
21
|
-
.before(requireConfig)
|
|
21
|
+
.before(requireConfig_1.requireConfig)
|
|
22
22
|
.before(requirePermissions_1.requirePermissions, ["firebasehosting.sites.update"])
|
|
23
23
|
.before(requireHostingSite_1.requireHostingSite)
|
|
24
24
|
.action(async (channelId, options) => {
|
|
@@ -9,13 +9,13 @@ const prompt_1 = require("../prompt");
|
|
|
9
9
|
const requireHostingSite_1 = require("../requireHostingSite");
|
|
10
10
|
const requirePermissions_1 = require("../requirePermissions");
|
|
11
11
|
const projectUtils_1 = require("../projectUtils");
|
|
12
|
-
const
|
|
12
|
+
const requireConfig_1 = require("../requireConfig");
|
|
13
13
|
const logger_1 = require("../logger");
|
|
14
14
|
exports.default = new command_1.Command("hosting:channel:delete <channelId>")
|
|
15
15
|
.description("delete a Firebase Hosting channel")
|
|
16
16
|
.withForce()
|
|
17
17
|
.option("--site <siteId>", "site in which the channel exists")
|
|
18
|
-
.before(requireConfig)
|
|
18
|
+
.before(requireConfig_1.requireConfig)
|
|
19
19
|
.before(requirePermissions_1.requirePermissions, ["firebasehosting.sites.update"])
|
|
20
20
|
.before(requireHostingSite_1.requireHostingSite)
|
|
21
21
|
.action(async (channelId, options) => {
|
|
@@ -9,7 +9,7 @@ const requirePermissions_1 = require("../requirePermissions");
|
|
|
9
9
|
const deploy = require("../deploy");
|
|
10
10
|
const projectUtils_1 = require("../projectUtils");
|
|
11
11
|
const logger_1 = require("../logger");
|
|
12
|
-
const
|
|
12
|
+
const requireConfig_1 = require("../requireConfig");
|
|
13
13
|
const expireUtils_1 = require("../hosting/expireUtils");
|
|
14
14
|
const utils_1 = require("../utils");
|
|
15
15
|
const { marked } = require("marked");
|
|
@@ -21,7 +21,7 @@ exports.default = new command_1.Command("hosting:channel:deploy [channelId]")
|
|
|
21
21
|
.option("--only <target1,target2...>", "only create previews for specified targets")
|
|
22
22
|
.option("--open", "open a browser to the channel after deploying")
|
|
23
23
|
.option("--no-authorized-domains", "do not sync channel domains with Firebase Auth")
|
|
24
|
-
.before(requireConfig)
|
|
24
|
+
.before(requireConfig_1.requireConfig)
|
|
25
25
|
.before(requirePermissions_1.requirePermissions, ["firebasehosting.sites.update"])
|
|
26
26
|
.before(requireHostingSite_1.requireHostingSite)
|
|
27
27
|
.action(async (channelId, options) => {
|
|
@@ -7,14 +7,14 @@ const command_1 = require("../command");
|
|
|
7
7
|
const requirePermissions_1 = require("../requirePermissions");
|
|
8
8
|
const projectUtils_1 = require("../projectUtils");
|
|
9
9
|
const logger_1 = require("../logger");
|
|
10
|
-
const
|
|
10
|
+
const requireConfig_1 = require("../requireConfig");
|
|
11
11
|
const utils_1 = require("../utils");
|
|
12
12
|
const requireHostingSite_1 = require("../requireHostingSite");
|
|
13
13
|
const TABLE_HEAD = ["Channel ID", "Last Release Time", "URL", "Expire Time"];
|
|
14
14
|
exports.default = new command_1.Command("hosting:channel:list")
|
|
15
15
|
.description("list all Firebase Hosting channels for your project")
|
|
16
16
|
.option("--site <siteName>", "list channels for the specified site")
|
|
17
|
-
.before(requireConfig)
|
|
17
|
+
.before(requireConfig_1.requireConfig)
|
|
18
18
|
.before(requirePermissions_1.requirePermissions, ["firebasehosting.sites.update"])
|
|
19
19
|
.before(requireHostingSite_1.requireHostingSite)
|
|
20
20
|
.action(async (options) => {
|
|
@@ -8,7 +8,7 @@ const error_1 = require("../error");
|
|
|
8
8
|
const api_1 = require("../hosting/api");
|
|
9
9
|
const requirePermissions_1 = require("../requirePermissions");
|
|
10
10
|
const projectUtils_1 = require("../projectUtils");
|
|
11
|
-
const
|
|
11
|
+
const requireConfig_1 = require("../requireConfig");
|
|
12
12
|
const utils_1 = require("../utils");
|
|
13
13
|
const prompt_1 = require("../prompt");
|
|
14
14
|
const requireHostingSite_1 = require("../requireHostingSite");
|
|
@@ -16,7 +16,7 @@ exports.default = new command_1.Command("hosting:channel:open [channelId]")
|
|
|
16
16
|
.description("opens the URL for a Firebase Hosting channel")
|
|
17
17
|
.help("if unable to open the URL in a browser, it will be displayed in the output")
|
|
18
18
|
.option("--site <siteId>", "the site to which the channel belongs")
|
|
19
|
-
.before(requireConfig)
|
|
19
|
+
.before(requireConfig_1.requireConfig)
|
|
20
20
|
.before(requirePermissions_1.requirePermissions, ["firebasehosting.sites.get"])
|
|
21
21
|
.before(requireHostingSite_1.requireHostingSite)
|
|
22
22
|
.action(async (channelId, options) => {
|
|
@@ -8,13 +8,13 @@ const prompt_1 = require("../prompt");
|
|
|
8
8
|
const error_1 = require("../error");
|
|
9
9
|
const requirePermissions_1 = require("../requirePermissions");
|
|
10
10
|
const projectUtils_1 = require("../projectUtils");
|
|
11
|
-
const
|
|
11
|
+
const requireConfig_1 = require("../requireConfig");
|
|
12
12
|
const logger_1 = require("../logger");
|
|
13
13
|
const LOG_TAG = "hosting:sites";
|
|
14
14
|
exports.default = new command_1.Command("hosting:sites:delete <siteId>")
|
|
15
15
|
.description("delete a Firebase Hosting site")
|
|
16
16
|
.withForce()
|
|
17
|
-
.before(requireConfig)
|
|
17
|
+
.before(requireConfig_1.requireConfig)
|
|
18
18
|
.before(requirePermissions_1.requirePermissions, ["firebasehosting.sites.delete"])
|
|
19
19
|
.action(async (siteId, options) => {
|
|
20
20
|
const projectId = (0, projectUtils_1.needProjectId)(options);
|
package/lib/commands/serve.js
CHANGED
|
@@ -5,7 +5,7 @@ var { Command } = require("../command");
|
|
|
5
5
|
const { logger } = require("../logger");
|
|
6
6
|
var utils = require("../utils");
|
|
7
7
|
var { requirePermissions } = require("../requirePermissions");
|
|
8
|
-
var requireConfig = require("../requireConfig");
|
|
8
|
+
var { requireConfig } = require("../requireConfig");
|
|
9
9
|
var { serve } = require("../serve/index");
|
|
10
10
|
var { filterTargets } = require("../filterTargets");
|
|
11
11
|
var { needProjectNumber } = require("../projectUtils");
|
|
@@ -3,12 +3,12 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
const clc = require("cli-color");
|
|
4
4
|
const command_1 = require("../command");
|
|
5
5
|
const logger_1 = require("../logger");
|
|
6
|
-
const
|
|
6
|
+
const requireConfig_1 = require("../requireConfig");
|
|
7
7
|
const utils = require("../utils");
|
|
8
8
|
const error_1 = require("../error");
|
|
9
9
|
exports.default = new command_1.Command("target:apply <type> <name> <resources...>")
|
|
10
10
|
.description("apply a deploy target to a resource")
|
|
11
|
-
.before(requireConfig)
|
|
11
|
+
.before(requireConfig_1.requireConfig)
|
|
12
12
|
.action((type, name, resources, options) => {
|
|
13
13
|
if (!options.project) {
|
|
14
14
|
throw new error_1.FirebaseError(`Must have an active project to set deploy targets. Try ${clc.bold("firebase use --add")}`);
|
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const clc = require("cli-color");
|
|
4
4
|
const command_1 = require("../command");
|
|
5
|
-
const
|
|
5
|
+
const requireConfig_1 = require("../requireConfig");
|
|
6
6
|
const utils = require("../utils");
|
|
7
7
|
exports.default = new command_1.Command("target:clear <type> <target>")
|
|
8
8
|
.description("clear all resources from a named resource target")
|
|
9
|
-
.before(requireConfig)
|
|
9
|
+
.before(requireConfig_1.requireConfig)
|
|
10
10
|
.action((type, name, options) => {
|
|
11
11
|
const existed = options.rc.clearTarget(options.project, type, name);
|
|
12
12
|
if (existed) {
|
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const clc = require("cli-color");
|
|
4
4
|
const command_1 = require("../command");
|
|
5
|
-
const
|
|
5
|
+
const requireConfig_1 = require("../requireConfig");
|
|
6
6
|
const utils = require("../utils");
|
|
7
7
|
exports.default = new command_1.Command("target:remove <type> <resource>")
|
|
8
8
|
.description("remove a resource target")
|
|
9
|
-
.before(requireConfig)
|
|
9
|
+
.before(requireConfig_1.requireConfig)
|
|
10
10
|
.action((type, resource, options) => {
|
|
11
11
|
const name = options.rc.removeTarget(options.project, type, resource);
|
|
12
12
|
if (name) {
|
package/lib/commands/target.js
CHANGED
|
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
const clc = require("cli-color");
|
|
4
4
|
const command_1 = require("../command");
|
|
5
5
|
const logger_1 = require("../logger");
|
|
6
|
-
const
|
|
6
|
+
const requireConfig_1 = require("../requireConfig");
|
|
7
7
|
const utils = require("../utils");
|
|
8
8
|
function logTargets(type, targets) {
|
|
9
9
|
logger_1.logger.info(clc.cyan("[ " + type + " ]"));
|
|
@@ -13,7 +13,7 @@ function logTargets(type, targets) {
|
|
|
13
13
|
}
|
|
14
14
|
exports.default = new command_1.Command("target [type]")
|
|
15
15
|
.description("display configured deploy targets for the current project")
|
|
16
|
-
.before(requireConfig)
|
|
16
|
+
.before(requireConfig_1.requireConfig)
|
|
17
17
|
.action((type, options) => {
|
|
18
18
|
if (!options.project) {
|
|
19
19
|
return utils.reject("No active project, cannot list deploy targets.");
|
package/lib/config.js
CHANGED
|
@@ -81,11 +81,17 @@ async function want(args) {
|
|
|
81
81
|
}
|
|
82
82
|
exports.want = want;
|
|
83
83
|
async function resolveVersion(ref) {
|
|
84
|
-
if (!ref.version || ref.version === "latest") {
|
|
85
|
-
return "latest";
|
|
86
|
-
}
|
|
87
84
|
const extensionRef = refs.toExtensionRef(ref);
|
|
88
85
|
const versions = await extensionsApi.listExtensionVersions(extensionRef);
|
|
86
|
+
if (versions.length === 0) {
|
|
87
|
+
throw new error_1.FirebaseError(`No versions found for ${extensionRef}`);
|
|
88
|
+
}
|
|
89
|
+
if (!ref.version || ref.version === "latest") {
|
|
90
|
+
return versions
|
|
91
|
+
.map((ev) => ev.spec.version)
|
|
92
|
+
.sort(semver.compare)
|
|
93
|
+
.pop();
|
|
94
|
+
}
|
|
89
95
|
const maxSatisfying = semver.maxSatisfying(versions.map((ev) => ev.spec.version), ref.version);
|
|
90
96
|
if (!maxSatisfying) {
|
|
91
97
|
throw new error_1.FirebaseError(`No version of ${extensionRef} matches requested version ${ref.version}`);
|
|
@@ -5705,6 +5705,28 @@ exports.default = {
|
|
|
5705
5705
|
},
|
|
5706
5706
|
type: "object",
|
|
5707
5707
|
},
|
|
5708
|
+
GoogleCloudIdentitytoolkitAdminV2AllowByDefault: {
|
|
5709
|
+
description: "Defines a policy of allowing every region by default and adding disallowed regions to a disallow list.",
|
|
5710
|
+
properties: {
|
|
5711
|
+
disallowedRegions: {
|
|
5712
|
+
description: "Two letter unicode region codes to disallow as defined by https://cldr.unicode.org/ The full list of these region codes is here: https://github.com/unicode-cldr/cldr-localenames-full/blob/master/main/en/territories.json",
|
|
5713
|
+
items: { type: "string" },
|
|
5714
|
+
type: "array",
|
|
5715
|
+
},
|
|
5716
|
+
},
|
|
5717
|
+
type: "object",
|
|
5718
|
+
},
|
|
5719
|
+
GoogleCloudIdentitytoolkitAdminV2AllowlistOnly: {
|
|
5720
|
+
description: "Defines a policy of only allowing regions by explicitly adding them to an allowlist.",
|
|
5721
|
+
properties: {
|
|
5722
|
+
allowedRegions: {
|
|
5723
|
+
description: "Two letter unicode region codes to allow as defined by https://cldr.unicode.org/ The full list of these region codes is here: https://github.com/unicode-cldr/cldr-localenames-full/blob/master/main/en/territories.json",
|
|
5724
|
+
items: { type: "string" },
|
|
5725
|
+
type: "array",
|
|
5726
|
+
},
|
|
5727
|
+
},
|
|
5728
|
+
type: "object",
|
|
5729
|
+
},
|
|
5708
5730
|
GoogleCloudIdentitytoolkitAdminV2Anonymous: {
|
|
5709
5731
|
description: "Configuration options related to authenticating an anonymous user.",
|
|
5710
5732
|
properties: {
|
|
@@ -6315,6 +6337,18 @@ exports.default = {
|
|
|
6315
6337
|
},
|
|
6316
6338
|
type: "object",
|
|
6317
6339
|
},
|
|
6340
|
+
GoogleCloudIdentitytoolkitAdminV2SmsRegionConfig: {
|
|
6341
|
+
description: "Configures the regions where users are allowed to send verification SMS for the project or tenant. This is based on the calling code of the destination phone number.",
|
|
6342
|
+
properties: {
|
|
6343
|
+
allowByDefault: {
|
|
6344
|
+
$ref: "#/components/schemas/GoogleCloudIdentitytoolkitAdminV2AllowByDefault",
|
|
6345
|
+
},
|
|
6346
|
+
allowlistOnly: {
|
|
6347
|
+
$ref: "#/components/schemas/GoogleCloudIdentitytoolkitAdminV2AllowlistOnly",
|
|
6348
|
+
},
|
|
6349
|
+
},
|
|
6350
|
+
type: "object",
|
|
6351
|
+
},
|
|
6318
6352
|
GoogleCloudIdentitytoolkitAdminV2SmsTemplate: {
|
|
6319
6353
|
description: "The template to use when sending an SMS.",
|
|
6320
6354
|
properties: {
|
|
@@ -6424,6 +6458,9 @@ exports.default = {
|
|
|
6424
6458
|
readOnly: true,
|
|
6425
6459
|
type: "string",
|
|
6426
6460
|
},
|
|
6461
|
+
smsRegionConfig: {
|
|
6462
|
+
$ref: "#/components/schemas/GoogleCloudIdentitytoolkitAdminV2SmsRegionConfig",
|
|
6463
|
+
},
|
|
6427
6464
|
testPhoneNumbers: {
|
|
6428
6465
|
additionalProperties: { type: "string" },
|
|
6429
6466
|
description: "A map of pairs that can be used for MFA. The phone number should be in E.164 format (https://www.itu.int/rec/T-REC-E.164/) and a maximum of 10 pairs can be added (error will be thrown once exceeded).",
|
|
@@ -10,7 +10,7 @@ const logger_1 = require("../logger");
|
|
|
10
10
|
const path = require("path");
|
|
11
11
|
const constants_1 = require("./constants");
|
|
12
12
|
const requireAuth_1 = require("../requireAuth");
|
|
13
|
-
const
|
|
13
|
+
const requireConfig_1 = require("../requireConfig");
|
|
14
14
|
const types_1 = require("./types");
|
|
15
15
|
const error_1 = require("../error");
|
|
16
16
|
const registry_1 = require("./registry");
|
|
@@ -102,7 +102,7 @@ async function beforeEmulatorCommand(options) {
|
|
|
102
102
|
options.config = DEFAULT_CONFIG;
|
|
103
103
|
}
|
|
104
104
|
else {
|
|
105
|
-
await requireConfig(options);
|
|
105
|
+
await (0, requireConfig_1.requireConfig)(options);
|
|
106
106
|
}
|
|
107
107
|
}
|
|
108
108
|
exports.beforeEmulatorCommand = beforeEmulatorCommand;
|
|
@@ -65,6 +65,9 @@ class ExtensionsEmulator {
|
|
|
65
65
|
"./functions/package.json",
|
|
66
66
|
"./functions/node_modules",
|
|
67
67
|
];
|
|
68
|
+
if (!fs.existsSync(args.path)) {
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
68
71
|
for (const requiredFile of requiredFiles) {
|
|
69
72
|
const f = path.join(args.path, requiredFile);
|
|
70
73
|
if (!fs.existsSync(f)) {
|
|
@@ -65,6 +65,10 @@ function emulatedFunctionsFromEndpoints(endpoints) {
|
|
|
65
65
|
if (backend.isHttpsTriggered(endpoint)) {
|
|
66
66
|
def.httpsTrigger = endpoint.httpsTrigger;
|
|
67
67
|
}
|
|
68
|
+
else if (backend.isCallableTriggered(endpoint)) {
|
|
69
|
+
def.httpsTrigger = {};
|
|
70
|
+
def.labels = Object.assign(Object.assign({}, def.labels), { "deployment-callable": "true" });
|
|
71
|
+
}
|
|
68
72
|
else if (backend.isEventTriggered(endpoint)) {
|
|
69
73
|
const eventTrigger = endpoint.eventTrigger;
|
|
70
74
|
if (endpoint.platform === "gcfv1") {
|
|
@@ -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,15 @@ 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
|
+
var SecretLocation;
|
|
16
|
+
(function (SecretLocation) {
|
|
17
|
+
SecretLocation[SecretLocation["CLOUD"] = 1] = "CLOUD";
|
|
18
|
+
SecretLocation[SecretLocation["LOCAL"] = 2] = "LOCAL";
|
|
19
|
+
})(SecretLocation = exports.SecretLocation || (exports.SecretLocation = {}));
|
|
15
20
|
var SecretUpdateAction;
|
|
16
21
|
(function (SecretUpdateAction) {
|
|
17
|
-
SecretUpdateAction[SecretUpdateAction["LEAVE"] =
|
|
18
|
-
SecretUpdateAction[SecretUpdateAction["SET_NEW"] =
|
|
22
|
+
SecretUpdateAction[SecretUpdateAction["LEAVE"] = 1] = "LEAVE";
|
|
23
|
+
SecretUpdateAction[SecretUpdateAction["SET_NEW"] = 2] = "SET_NEW";
|
|
19
24
|
})(SecretUpdateAction || (SecretUpdateAction = {}));
|
|
20
25
|
function checkResponse(response, spec) {
|
|
21
26
|
let valid = true;
|
|
@@ -82,6 +87,8 @@ async function askForParam(args) {
|
|
|
82
87
|
const paramSpec = args.paramSpec;
|
|
83
88
|
let valid = false;
|
|
84
89
|
let response = "";
|
|
90
|
+
let responseForLocal;
|
|
91
|
+
let secretLocations = [];
|
|
85
92
|
const description = paramSpec.description || "";
|
|
86
93
|
const label = paramSpec.label.trim();
|
|
87
94
|
logger_1.logger.info(`\n${clc.bold(label)}${clc.bold(paramSpec.required ? "" : " (Optional)")}: ${marked(description).trim()}`);
|
|
@@ -116,16 +123,23 @@ async function askForParam(args) {
|
|
|
116
123
|
}
|
|
117
124
|
},
|
|
118
125
|
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.",
|
|
126
|
+
"Press Space to select, then Enter to confirm your choices. ",
|
|
121
127
|
choices: (0, utils_1.convertExtensionOptionToLabeledList)(paramSpec.options),
|
|
122
128
|
});
|
|
123
129
|
valid = checkResponse(response, paramSpec);
|
|
124
130
|
break;
|
|
125
131
|
case extensionsApi_1.ParamType.SECRET:
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
132
|
+
while (!secretLocations.length) {
|
|
133
|
+
secretLocations = await promptSecretLocations();
|
|
134
|
+
}
|
|
135
|
+
if (secretLocations.includes(SecretLocation.CLOUD.toString())) {
|
|
136
|
+
response = args.reconfiguring
|
|
137
|
+
? await promptReconfigureSecret(args.projectId, args.instanceId, paramSpec)
|
|
138
|
+
: await promptCreateSecret(args.projectId, args.instanceId, paramSpec);
|
|
139
|
+
}
|
|
140
|
+
if (secretLocations.includes(SecretLocation.LOCAL.toString())) {
|
|
141
|
+
responseForLocal = await promptLocalSecret(args.instanceId, paramSpec);
|
|
142
|
+
}
|
|
129
143
|
valid = true;
|
|
130
144
|
break;
|
|
131
145
|
default:
|
|
@@ -138,9 +152,38 @@ async function askForParam(args) {
|
|
|
138
152
|
valid = checkResponse(response, paramSpec);
|
|
139
153
|
}
|
|
140
154
|
}
|
|
141
|
-
return { baseValue: response };
|
|
155
|
+
return Object.assign({ baseValue: response }, (responseForLocal ? { local: responseForLocal } : {}));
|
|
142
156
|
}
|
|
143
157
|
exports.askForParam = askForParam;
|
|
158
|
+
async function promptSecretLocations() {
|
|
159
|
+
return await (0, prompt_1.promptOnce)({
|
|
160
|
+
name: "input",
|
|
161
|
+
type: "checkbox",
|
|
162
|
+
message: "Where would you like to store your secrets? You must select at least one value",
|
|
163
|
+
choices: [
|
|
164
|
+
{
|
|
165
|
+
checked: true,
|
|
166
|
+
name: "Google Cloud Secret Manager",
|
|
167
|
+
value: SecretLocation.CLOUD.toString(),
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
checked: false,
|
|
171
|
+
name: "Local file (Only used by Firebase Emulator)",
|
|
172
|
+
value: SecretLocation.LOCAL.toString(),
|
|
173
|
+
},
|
|
174
|
+
],
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
async function promptLocalSecret(instanceId, paramSpec) {
|
|
178
|
+
utils.logLabeledBullet(extensionsHelper_1.logPrefix, "Configure a local secret value for Extensions Emulator");
|
|
179
|
+
const value = await (0, prompt_1.promptOnce)({
|
|
180
|
+
name: paramSpec.param,
|
|
181
|
+
type: "input",
|
|
182
|
+
message: `This secret will be stored in ./extensions/${instanceId}.secret.local.\n` +
|
|
183
|
+
`Enter value for "${paramSpec.label.trim()}" to be used by Extensions Emulator:`,
|
|
184
|
+
});
|
|
185
|
+
return value;
|
|
186
|
+
}
|
|
144
187
|
async function promptReconfigureSecret(projectId, instanceId, paramSpec) {
|
|
145
188
|
const action = await (0, prompt_1.promptOnce)({
|
|
146
189
|
type: "list",
|
|
@@ -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
|
}
|
|
@@ -104,6 +104,7 @@ async function getParamsForUpdate(args) {
|
|
|
104
104
|
}
|
|
105
105
|
exports.getParamsForUpdate = getParamsForUpdate;
|
|
106
106
|
async function promptForNewParams(args) {
|
|
107
|
+
const newParamBindingOptions = buildBindingOptionsWithBaseValue(args.currentParams);
|
|
107
108
|
const firebaseProjectParams = await (0, extensionsHelper_1.getFirebaseProjectParams)(args.projectId);
|
|
108
109
|
const comparer = (param1, param2) => {
|
|
109
110
|
return param1.type === param2.type && param1.param === param2.param;
|
|
@@ -117,7 +118,7 @@ async function promptForNewParams(args) {
|
|
|
117
118
|
logger_1.logger.info("The following params will no longer be used:");
|
|
118
119
|
paramsDiffDeletions.forEach((param) => {
|
|
119
120
|
logger_1.logger.info(clc.red(`- ${param.param}: ${args.currentParams[param.param.toUpperCase()]}`));
|
|
120
|
-
delete
|
|
121
|
+
delete newParamBindingOptions[param.param.toUpperCase()];
|
|
121
122
|
});
|
|
122
123
|
}
|
|
123
124
|
if (paramsDiffAdditions.length) {
|
|
@@ -129,10 +130,10 @@ async function promptForNewParams(args) {
|
|
|
129
130
|
paramSpec: param,
|
|
130
131
|
reconfiguring: false,
|
|
131
132
|
});
|
|
132
|
-
|
|
133
|
+
newParamBindingOptions[param.param] = chosenValue;
|
|
133
134
|
}
|
|
134
135
|
}
|
|
135
|
-
return
|
|
136
|
+
return newParamBindingOptions;
|
|
136
137
|
}
|
|
137
138
|
exports.promptForNewParams = promptForNewParams;
|
|
138
139
|
function getParamsFromFile(args) {
|
package/lib/fsutils.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.dirExistsSync = exports.fileExistsSync = void 0;
|
|
3
|
+
exports.readFile = exports.dirExistsSync = exports.fileExistsSync = void 0;
|
|
4
4
|
const fs_1 = require("fs");
|
|
5
|
+
const error_1 = require("./error");
|
|
5
6
|
function fileExistsSync(path) {
|
|
6
7
|
try {
|
|
7
8
|
return (0, fs_1.statSync)(path).isFile();
|
|
@@ -20,3 +21,15 @@ function dirExistsSync(path) {
|
|
|
20
21
|
}
|
|
21
22
|
}
|
|
22
23
|
exports.dirExistsSync = dirExistsSync;
|
|
24
|
+
function readFile(path) {
|
|
25
|
+
try {
|
|
26
|
+
return (0, fs_1.readFileSync)(path).toString();
|
|
27
|
+
}
|
|
28
|
+
catch (e) {
|
|
29
|
+
if (e.code === "ENOENT") {
|
|
30
|
+
throw new error_1.FirebaseError(`File not found: ${path}`);
|
|
31
|
+
}
|
|
32
|
+
throw e;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
exports.readFile = readFile;
|
package/lib/requireConfig.js
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.requireConfig = void 0;
|
|
4
|
+
const error_1 = require("./error");
|
|
5
|
+
async function requireConfig(options) {
|
|
6
|
+
await Promise.resolve();
|
|
7
|
+
if (!options.config) {
|
|
8
|
+
throw options.configError
|
|
9
|
+
? options.configError
|
|
10
|
+
: new error_1.FirebaseError("Not in a Firebase project directory (could not locate firebase.json)");
|
|
6
11
|
}
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
exit: 1,
|
|
10
|
-
}));
|
|
11
|
-
};
|
|
12
|
+
}
|
|
13
|
+
exports.requireConfig = requireConfig;
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "firebase-tools",
|
|
3
|
-
"version": "10.
|
|
3
|
+
"version": "10.4.0",
|
|
4
4
|
"lockfileVersion": 2,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "firebase-tools",
|
|
9
|
-
"version": "10.
|
|
9
|
+
"version": "10.4.0",
|
|
10
10
|
"license": "MIT",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@google-cloud/pubsub": "^2.18.4",
|
package/package.json
CHANGED
package/lib/prepareUpload.js
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var fs = require("fs");
|
|
3
|
-
var path = require("path");
|
|
4
|
-
var tar = require("tar");
|
|
5
|
-
var tmp = require("tmp");
|
|
6
|
-
var { listFiles } = require("./listFiles");
|
|
7
|
-
var { FirebaseError } = require("./error");
|
|
8
|
-
var fsutils = require("./fsutils");
|
|
9
|
-
module.exports = function (options) {
|
|
10
|
-
var hostingConfig = options.config.get("hosting");
|
|
11
|
-
var publicDir = options.config.path(hostingConfig.public);
|
|
12
|
-
var indexPath = path.join(publicDir, "index.html");
|
|
13
|
-
var tmpFile = tmp.fileSync({
|
|
14
|
-
prefix: "firebase-upload-",
|
|
15
|
-
postfix: ".tar.gz",
|
|
16
|
-
});
|
|
17
|
-
var manifest = listFiles(publicDir, hostingConfig.ignore);
|
|
18
|
-
return tar
|
|
19
|
-
.c({
|
|
20
|
-
gzip: true,
|
|
21
|
-
file: tmpFile.name,
|
|
22
|
-
cwd: publicDir,
|
|
23
|
-
prefix: "public",
|
|
24
|
-
follow: true,
|
|
25
|
-
noDirRecurse: true,
|
|
26
|
-
portable: true,
|
|
27
|
-
}, manifest.slice(0))
|
|
28
|
-
.then(function () {
|
|
29
|
-
var stats = fs.statSync(tmpFile.name);
|
|
30
|
-
return {
|
|
31
|
-
file: tmpFile.name,
|
|
32
|
-
stream: fs.createReadStream(tmpFile.name),
|
|
33
|
-
manifest: manifest,
|
|
34
|
-
foundIndex: fsutils.fileExistsSync(indexPath),
|
|
35
|
-
size: stats.size,
|
|
36
|
-
};
|
|
37
|
-
})
|
|
38
|
-
.catch(function (err) {
|
|
39
|
-
return Promise.reject(new FirebaseError("There was an issue preparing Hosting files for upload.", {
|
|
40
|
-
original: err,
|
|
41
|
-
exit: 2,
|
|
42
|
-
}));
|
|
43
|
-
});
|
|
44
|
-
};
|