UNPKG

firebase-tools

Version:
126 lines (125 loc) • 5.13 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.addServiceAccountRoles = exports.firebaseRoles = void 0; exports.getIamPolicy = getIamPolicy; exports.setIamPolicy = setIamPolicy; exports.addServiceAccountToRoles = addServiceAccountToRoles; exports.serviceAccountHasRoles = serviceAccountHasRoles; exports.removeServiceAccountRoles = removeServiceAccountRoles; exports.getServiceAccountRoles = getServiceAccountRoles; const api_1 = require("../api"); const apiv2_1 = require("../apiv2"); const error_1 = require("../error"); const utils = require("../utils"); const iam_1 = require("./iam"); const API_VERSION = "v1"; const apiClient = new apiv2_1.Client({ urlPrefix: (0, api_1.resourceManagerOrigin)(), apiVersion: API_VERSION }); exports.firebaseRoles = { apiKeysViewer: "roles/serviceusage.apiKeysViewer", authAdmin: "roles/firebaseauth.admin", functionsDeveloper: "roles/cloudfunctions.developer", hostingAdmin: "roles/firebasehosting.admin", runViewer: "roles/run.viewer", serviceUsageConsumer: "roles/serviceusage.serviceUsageConsumer", }; async function getIamPolicy(projectIdOrNumber) { const response = await apiClient.post(`/projects/${projectIdOrNumber}:getIamPolicy`); return response.body; } async function setIamPolicy(projectIdOrNumber, newPolicy, updateMask = "") { const response = await apiClient.post(`/projects/${projectIdOrNumber}:setIamPolicy`, { policy: newPolicy, updateMask: updateMask, }); return response.body; } function isRetryableIamError(err) { const status = (0, error_1.getErrStatus)(err); if (status === 409) { return true; } const msg = (0, error_1.getErrMsg)(err).toLowerCase(); const hasSa = msg.includes("service account") || msg.includes("serviceaccount"); if (status === 400 && hasSa && msg.includes("does not exist")) { return true; } return false; } async function addServiceAccountToRoles(projectId, serviceAccountName, roles, skipAccountLookup = false) { const { name: fullServiceAccountName } = skipAccountLookup ? { name: serviceAccountName } : await (0, iam_1.getServiceAccount)(projectId, serviceAccountName); const newMemberName = `serviceAccount:${fullServiceAccountName.split("/").pop()}`; return await utils.retryWithBackoff(async () => { const projectPolicy = await getIamPolicy(projectId); projectPolicy.bindings = projectPolicy.bindings || []; const requiredBindings = roles.map((role) => ({ role, members: [newMemberName], })); (0, iam_1.mergeBindings)(projectPolicy, requiredBindings); return await setIamPolicy(projectId, projectPolicy, "bindings"); }, { retries: 7, delay: 1000, maxDelay: 5000, retryPredicate: isRetryableIamError, }); } async function serviceAccountHasRoles(projectId, serviceAccountName, roles, skipAccountLookup = false) { const [{ name: fullServiceAccountName }, projectPolicy] = await Promise.all([ skipAccountLookup ? Promise.resolve({ name: serviceAccountName }) : (0, iam_1.getServiceAccount)(projectId, serviceAccountName), getIamPolicy(projectId), ]); projectPolicy.bindings = projectPolicy.bindings || []; const memberName = `serviceAccount:${fullServiceAccountName.split("/").pop()}`; for (const roleName of roles) { const binding = projectPolicy.bindings.find((b) => b.role === roleName); if (!binding) { return false; } if (!binding.members.includes(memberName)) { return false; } } return true; } async function removeServiceAccountRoles(projectId, serviceAccountEmail, rolesToRemove) { const projectPolicy = await getIamPolicy(projectId); const bindings = projectPolicy.bindings || []; const memberName = `serviceAccount:${serviceAccountEmail}`; let updated = false; for (let i = bindings.length - 1; i >= 0; i--) { const binding = bindings[i]; if (rolesToRemove.includes(binding.role)) { const memberIndex = binding.members.indexOf(memberName); if (memberIndex !== -1) { binding.members.splice(memberIndex, 1); updated = true; if (binding.members.length === 0) { bindings.splice(i, 1); } } } } if (updated) { projectPolicy.bindings = bindings; return await setIamPolicy(projectId, projectPolicy, "bindings"); } return projectPolicy; } async function getServiceAccountRoles(projectId, serviceAccountEmail) { const projectPolicy = await getIamPolicy(projectId); const memberName = `serviceAccount:${serviceAccountEmail}`; const roles = []; const bindings = projectPolicy.bindings || []; for (const binding of bindings) { if (binding.members.includes(memberName)) { roles.push(binding.role); } } return roles; } exports.addServiceAccountRoles = addServiceAccountToRoles;