firebase-tools 15.21.0 → 15.22.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.
@@ -32,7 +32,7 @@ function toYamlTestCases(testCases) {
32
32
  }));
33
33
  }
34
34
  function toYaml(testCases) {
35
- return jsYaml.safeDump({ tests: toYamlTestCases(testCases) });
35
+ return jsYaml.dump({ tests: toYamlTestCases(testCases) });
36
36
  }
37
37
  function castExists(it, thing) {
38
38
  if (it == null) {
@@ -77,7 +77,7 @@ function fromYamlTestCases(appName, yamlTestCases) {
77
77
  function fromYaml(appName, yaml) {
78
78
  let parsedYaml;
79
79
  try {
80
- parsedYaml = jsYaml.safeLoad(yaml);
80
+ parsedYaml = jsYaml.load(yaml);
81
81
  }
82
82
  catch (err) {
83
83
  throw new error_1.FirebaseError(`Failed to parse YAML: ${(0, error_1.getErrMsg)(err)}`);
@@ -4,6 +4,8 @@ exports.toMulti = toMulti;
4
4
  exports.serviceAccountsForBackend = serviceAccountsForBackend;
5
5
  exports.grantSecretAccess = grantSecretAccess;
6
6
  exports.grantEmailsSecretAccess = grantEmailsSecretAccess;
7
+ exports.revokeSecretAccess = revokeSecretAccess;
8
+ exports.revokeEmailsSecretAccess = revokeEmailsSecretAccess;
7
9
  exports.upsertSecret = upsertSecret;
8
10
  exports.loadSecret = loadSecret;
9
11
  exports.fetchSecrets = fetchSecrets;
@@ -117,6 +119,81 @@ async function grantEmailsSecretAccess(projectId, secretNames, emails) {
117
119
  utils.logSuccess(`Successfully set IAM bindings on secret ${secretName}.\n`);
118
120
  }
119
121
  }
122
+ async function revokeSecretAccess(projectId, secretName, accounts) {
123
+ const bindingsToRevoke = [
124
+ {
125
+ role: "roles/secretmanager.secretAccessor",
126
+ members: [...accounts.buildServiceAccounts, ...accounts.runServiceAccounts].map((sa) => `serviceAccount:${sa}`),
127
+ },
128
+ {
129
+ role: "roles/secretmanager.viewer",
130
+ members: accounts.buildServiceAccounts.map((sa) => `serviceAccount:${sa}`),
131
+ },
132
+ ];
133
+ await revokeSecretBindings(projectId, secretName, bindingsToRevoke);
134
+ }
135
+ async function revokeEmailsSecretAccess(projectId, secretNames, emails) {
136
+ const bindingsToRevoke = [
137
+ {
138
+ role: "roles/secretmanager.secretAccessor",
139
+ members: emails.flatMap((email) => [`user:${email}`, `group:${email}`]),
140
+ },
141
+ ];
142
+ for (const secretName of secretNames) {
143
+ await revokeSecretBindings(projectId, secretName, bindingsToRevoke);
144
+ }
145
+ }
146
+ async function revokeSecretBindings(projectId, secretName, bindingsToRevoke) {
147
+ let existingBindings;
148
+ try {
149
+ existingBindings = (await gcsm.getIamPolicy({ projectId, name: secretName })).bindings || [];
150
+ }
151
+ catch (err) {
152
+ throw new error_1.FirebaseError(`Failed to get IAM bindings on secret: ${secretName}. Ensure you have the permissions to do so and try again.`, { original: (0, error_1.getError)(err) });
153
+ }
154
+ const removalsByRole = new Map();
155
+ for (const binding of bindingsToRevoke) {
156
+ let members = removalsByRole.get(binding.role);
157
+ if (!members) {
158
+ members = new Set();
159
+ removalsByRole.set(binding.role, members);
160
+ }
161
+ for (const member of binding.members) {
162
+ members.add(member);
163
+ }
164
+ }
165
+ let updated = false;
166
+ const bindings = [];
167
+ for (const binding of existingBindings) {
168
+ const removals = removalsByRole.get(binding.role);
169
+ if (!removals || binding.condition) {
170
+ bindings.push(binding);
171
+ continue;
172
+ }
173
+ const members = binding.members.filter((member) => !removals.has(member));
174
+ if (members.length !== binding.members.length) {
175
+ updated = true;
176
+ }
177
+ if (members.length) {
178
+ bindings.push({ ...binding, members });
179
+ }
180
+ else {
181
+ updated = true;
182
+ }
183
+ }
184
+ if (!updated) {
185
+ utils.logSuccess(`No matching IAM bindings found on secret ${secretName}.\n`);
186
+ return;
187
+ }
188
+ try {
189
+ await gcsm.setIamPolicy({ projectId, name: secretName }, bindings);
190
+ }
191
+ catch (err) {
192
+ throw new error_1.FirebaseError(`Failed to revoke IAM bindings ${JSON.stringify(bindingsToRevoke)} on secret: ${secretName}. Ensure you have the permissions to do so and try again. ` +
193
+ "For more information visit https://cloud.google.com/secret-manager/docs/manage-access-to-secrets#required-roles", { original: (0, error_1.getError)(err) });
194
+ }
195
+ utils.logSuccess(`Successfully revoked IAM bindings on secret ${secretName}.\n`);
196
+ }
120
197
  async function upsertSecret(project, secret, location) {
121
198
  let existing;
122
199
  try {
@@ -0,0 +1,56 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.command = void 0;
4
+ const command_1 = require("../command");
5
+ const projectUtils_1 = require("../projectUtils");
6
+ const error_1 = require("../error");
7
+ const requireAuth_1 = require("../requireAuth");
8
+ const secretManager = require("../gcp/secretManager");
9
+ const requirePermissions_1 = require("../requirePermissions");
10
+ const apphosting = require("../gcp/apphosting");
11
+ const secrets = require("../apphosting/secrets");
12
+ const backend_1 = require("../apphosting/backend");
13
+ exports.command = new command_1.Command("apphosting:secrets:revokeaccess <secretNames>")
14
+ .description("Revoke service accounts, users, or groups permissions from the provided secret(s). Can pass one or more secrets, separated by a comma")
15
+ .option("-l, --location <location>", "the location of the backend to revoke secret access from. Cannot be combined with --emails", "-")
16
+ .option("-b, --backend <backend>", "the name of the backend to revoke secret access from. Cannot be combined with --emails")
17
+ .option("-e, --emails <emails>", "comma delimited list of user or group emails to revoke secret access from. Cannot be combined with --backend")
18
+ .before(requireAuth_1.requireAuth)
19
+ .before(secretManager.ensureApi)
20
+ .before(apphosting.ensureApiEnabled)
21
+ .before(requirePermissions_1.requirePermissions, [
22
+ "secretmanager.secrets.get",
23
+ "secretmanager.secrets.getIamPolicy",
24
+ "secretmanager.secrets.setIamPolicy",
25
+ ])
26
+ .action(async (secretNames, options) => {
27
+ const projectId = (0, projectUtils_1.needProjectId)(options);
28
+ if (!options.backend && !options.emails) {
29
+ throw new error_1.FirebaseError("Missing required flag --backend or --emails. See firebase apphosting:secrets:revokeaccess --help for more info");
30
+ }
31
+ if (options.backend && options.emails) {
32
+ throw new error_1.FirebaseError("Cannot specify both --backend and --emails. See firebase apphosting:secrets:revokeaccess --help for more info");
33
+ }
34
+ const secretList = secretNames.split(",");
35
+ for (const secretName of secretList) {
36
+ const exists = await secretManager.secretExists(projectId, secretName);
37
+ if (!exists) {
38
+ throw new error_1.FirebaseError(`Cannot find secret ${secretName}`);
39
+ }
40
+ }
41
+ if (options.emails) {
42
+ return await secrets.revokeEmailsSecretAccess(projectId, secretList, String(options.emails).split(","));
43
+ }
44
+ const projectNumber = await (0, projectUtils_1.needProjectNumber)(options);
45
+ const backendId = options.backend;
46
+ const location = options.location;
47
+ let backend;
48
+ if (location === "" || location === "-") {
49
+ backend = await (0, backend_1.getBackendForAmbiguousLocation)(projectId, backendId, "Please select the location of your backend:");
50
+ }
51
+ else {
52
+ backend = await apphosting.getBackend(projectId, location, backendId);
53
+ }
54
+ const accounts = secrets.toMulti(await secrets.serviceAccountsForBackend(projectNumber, backend));
55
+ await Promise.all(secretList.map((secretName) => secrets.revokeSecretAccess(projectId, secretName, accounts)));
56
+ });
@@ -195,6 +195,7 @@ function load(client) {
195
195
  client.apphosting.backends.delete = loadCommand("apphosting-backends-delete");
196
196
  client.apphosting.secrets = {};
197
197
  client.apphosting.secrets.set = loadCommand("apphosting-secrets-set");
198
+ client.apphosting.secrets.revokeaccess = loadCommand("apphosting-secrets-revokeaccess");
198
199
  client.apphosting.secrets.grantaccess = loadCommand("apphosting-secrets-grantaccess");
199
200
  client.apphosting.secrets.describe = loadCommand("apphosting-secrets-describe");
200
201
  client.apphosting.secrets.access = loadCommand("apphosting-secrets-access");
@@ -44,46 +44,46 @@
44
44
  }
45
45
  },
46
46
  "pubsub": {
47
- "version": "0.8.32",
48
- "expectedSize": 52942305,
49
- "expectedChecksum": "b47a3698985bff5f6aedec04fad9354e",
50
- "expectedChecksumSHA256": "476d6492718210837f13e64b5b9f54335469381827bad25eda62e36572e47a82",
51
- "remoteUrl": "https://storage.googleapis.com/firebase-preview-drop/emulator/pubsub-emulator-0.8.32.zip",
52
- "downloadPathRelativeToCacheDir": "pubsub-emulator-0.8.32.zip",
53
- "binaryPathRelativeToCacheDir": "pubsub-emulator-0.8.32/pubsub-emulator/bin/cloud-pubsub-emulator"
47
+ "version": "0.8.33",
48
+ "expectedSize": 52948835,
49
+ "expectedChecksum": "7dc33e0c8bb37948228e49a18375d53b",
50
+ "expectedChecksumSHA256": "93768f8763d85c37f7f6e0f64d10195abaa088f4ff559b7d9fb8a2fc5520848d",
51
+ "remoteUrl": "https://storage.googleapis.com/firebase-preview-drop/emulator/pubsub-emulator-0.8.33.zip",
52
+ "downloadPathRelativeToCacheDir": "pubsub-emulator-0.8.33.zip",
53
+ "binaryPathRelativeToCacheDir": "pubsub-emulator-0.8.33/pubsub-emulator/bin/cloud-pubsub-emulator"
54
54
  },
55
55
  "dataconnect": {
56
56
  "darwin": {
57
- "version": "3.4.11",
58
- "expectedSize": 32444112,
59
- "expectedChecksum": "b7d6dfb69ff26d5952ad93bbb6b071c9",
60
- "expectedChecksumSHA256": "66ef23bd048e7cb93dd7574ab92fbbdc66c82e7b72e38625d7a9f1f8b724532a",
61
- "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-amd64-v3.4.11",
62
- "downloadPathRelativeToCacheDir": "dataconnect-emulator-3.4.11"
57
+ "version": "3.4.12",
58
+ "expectedSize": 29963104,
59
+ "expectedChecksum": "ee4d81abfba3576c7c6c8082313acfd0",
60
+ "expectedChecksumSHA256": "8aed1dc6cc8c35ab23fdf10e519e9cf3d93c8a0ccb0ff7d5af3ddc41a4847e3e",
61
+ "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-amd64-v3.4.12",
62
+ "downloadPathRelativeToCacheDir": "dataconnect-emulator-3.4.12"
63
63
  },
64
64
  "darwin_arm64": {
65
- "version": "3.4.11",
66
- "expectedSize": 30587970,
67
- "expectedChecksum": "ab0a05b2441ebdda9e04dc8d4dfd3118",
68
- "expectedChecksumSHA256": "f22fa6b588644f112def735591f43f05b8864e5c2c2f2aff4a4e00c435f9f101",
69
- "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-arm64-v3.4.11",
70
- "downloadPathRelativeToCacheDir": "dataconnect-emulator-3.4.11"
65
+ "version": "3.4.12",
66
+ "expectedSize": 29442946,
67
+ "expectedChecksum": "15a6ee63f48021197df5395455666f29",
68
+ "expectedChecksumSHA256": "4a3933f55db380f20c478cd1a3cc4da8742da75bdcea589fc6e1f52b18943f48",
69
+ "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-arm64-v3.4.12",
70
+ "downloadPathRelativeToCacheDir": "dataconnect-emulator-3.4.12"
71
71
  },
72
72
  "win32": {
73
- "version": "3.4.11",
74
- "expectedSize": 32483840,
75
- "expectedChecksum": "211210d49b7595e229a2552f56c1d9d3",
76
- "expectedChecksumSHA256": "dd88350a2f4fd77ccdc8dcf4b465edc6be08609d7c79a195a57febd9b31db6cd",
77
- "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-windows-amd64-v3.4.11",
78
- "downloadPathRelativeToCacheDir": "dataconnect-emulator-3.4.11.exe"
73
+ "version": "3.4.12",
74
+ "expectedSize": 30456320,
75
+ "expectedChecksum": "546019a713be28620a3b43d49fc9d4b0",
76
+ "expectedChecksumSHA256": "ec1de720e173f0a613fbbb51767c0288121aa2f80e45e6bad0f3d2aff12689ec",
77
+ "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-windows-amd64-v3.4.12",
78
+ "downloadPathRelativeToCacheDir": "dataconnect-emulator-3.4.12.exe"
79
79
  },
80
80
  "linux": {
81
- "version": "3.4.11",
82
- "expectedSize": 31600824,
83
- "expectedChecksum": "73385ff19e1324724d01ad1577552fdd",
84
- "expectedChecksumSHA256": "28870bff742c78221e7be4940d993189c56fc26f50fc2cc17afcf062c84dc439",
85
- "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-linux-amd64-v3.4.11",
86
- "downloadPathRelativeToCacheDir": "dataconnect-emulator-3.4.11"
81
+ "version": "3.4.12",
82
+ "expectedSize": 29888696,
83
+ "expectedChecksum": "5ab8da956043a48b43199f07b94ac48c",
84
+ "expectedChecksumSHA256": "1a420f3d2672627eae2d9b0b6747b833517cfc08f7e80ae3a79389788691b67a",
85
+ "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-linux-amd64-v3.4.12",
86
+ "downloadPathRelativeToCacheDir": "dataconnect-emulator-3.4.12"
87
87
  }
88
88
  }
89
89
  }