UNPKG

firebase-tools

Version:
166 lines (164 loc) 7.23 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.setupSQLPermissions = exports.iamUserIsCSQLAdmin = exports.checkSQLRoleIsGranted = exports.fdcSqlRoleMap = exports.firebasewriter = exports.firebasereader = exports.firebaseowner = void 0; const projectUtils_1 = require("../../projectUtils"); const connect_1 = require("./connect"); const iam_1 = require("../iam"); const logger_1 = require("../../logger"); const lodash_1 = require("lodash"); const error_1 = require("../../error"); function firebaseowner(databaseId) { return `firebaseowner_${databaseId}_public`; } exports.firebaseowner = firebaseowner; function firebasereader(databaseId) { return `firebasereader_${databaseId}_public`; } exports.firebasereader = firebasereader; function firebasewriter(databaseId) { return `firebasewriter_${databaseId}_public`; } exports.firebasewriter = firebasewriter; exports.fdcSqlRoleMap = { owner: firebaseowner, writer: firebasewriter, reader: firebasereader, }; async function checkSQLRoleIsGranted(options, instanceId, databaseId, grantedRole, granteeRole) { const checkCmd = ` DO $$ DECLARE role_count INTEGER; BEGIN -- Count the number of rows matching the criteria SELECT COUNT(*) INTO role_count FROM pg_auth_members m JOIN pg_roles grantee ON grantee.oid = m.member JOIN pg_roles granted ON granted.oid = m.roleid JOIN pg_roles grantor ON grantor.oid = m.grantor WHERE granted.rolname = '${grantedRole}' AND grantee.rolname = '${granteeRole}'; -- If no rows were found, raise an exception IF role_count = 0 THEN RAISE EXCEPTION 'Role "%", is not granted to role "%".', '${grantedRole}', '${granteeRole}'; END IF; END $$; `; try { await (0, connect_1.executeSqlCmdsAsIamUser)(options, instanceId, databaseId, [checkCmd], true); return true; } catch (e) { if (e instanceof error_1.FirebaseError && e.message.includes("not granted to role")) { return false; } logger_1.logger.error(`Role Check Failed: ${e}`); throw e; } } exports.checkSQLRoleIsGranted = checkSQLRoleIsGranted; async function iamUserIsCSQLAdmin(options) { const projectId = (0, projectUtils_1.needProjectId)(options); const requiredPermissions = [ "cloudsql.instances.connect", "cloudsql.instances.get", "cloudsql.users.create", "cloudsql.users.update", ]; try { const iamResult = await (0, iam_1.testIamPermissions)(projectId, requiredPermissions); return iamResult.passed; } catch (err) { logger_1.logger.debug(`[iam] error while checking permissions, command may fail: ${err}`); return false; } } exports.iamUserIsCSQLAdmin = iamUserIsCSQLAdmin; function ownerRolePermissions(databaseId, superuser, schema) { const firebaseOwnerRole = firebaseowner(databaseId); return [ `do $$ begin if not exists (select FROM pg_catalog.pg_roles WHERE rolname = '${firebaseOwnerRole}') then CREATE ROLE "${firebaseOwnerRole}" WITH ADMIN "${superuser}"; end if; end $$ ;`, `GRANT "${firebaseOwnerRole}" TO "cloudsqlsuperuser"`, `ALTER SCHEMA "${schema}" OWNER TO "${firebaseOwnerRole}"`, `GRANT USAGE ON SCHEMA "${schema}" TO "${firebaseOwnerRole}"`, `GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA "${schema}" TO "${firebaseOwnerRole}"`, `GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA "${schema}" TO "${firebaseOwnerRole}"`, ]; } function writerRolePermissions(databaseId, superuser, schema) { const firebaseWriterRole = firebasewriter(databaseId); return [ `do $$ begin if not exists (select FROM pg_catalog.pg_roles WHERE rolname = '${firebaseWriterRole}') then CREATE ROLE "${firebaseWriterRole}" WITH ADMIN "${superuser}"; end if; end $$ ;`, `GRANT "${firebaseWriterRole}" TO "cloudsqlsuperuser"`, `GRANT USAGE ON SCHEMA "${schema}" TO "${firebaseWriterRole}"`, `GRANT SELECT, INSERT, UPDATE, DELETE, TRUNCATE ON ALL TABLES IN SCHEMA "${schema}" TO "${firebaseWriterRole}"`, `GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA "${schema}" TO "${firebaseWriterRole}"`, `GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA "${schema}" TO "${firebaseWriterRole}"`, `SET ROLE = '${firebaseowner(databaseId)}';`, `ALTER DEFAULT PRIVILEGES IN SCHEMA "${schema}" GRANT SELECT, INSERT, UPDATE, DELETE, TRUNCATE ON TABLES TO "${firebaseWriterRole}";`, `ALTER DEFAULT PRIVILEGES IN SCHEMA "${schema}" GRANT USAGE ON SEQUENCES TO "${firebaseWriterRole}";`, `ALTER DEFAULT PRIVILEGES IN SCHEMA "${schema}" GRANT EXECUTE ON FUNCTIONS TO "${firebaseWriterRole}"`, `SET ROLE = cloudsqlsuperuser`, ]; } function readerRolePermissions(databaseId, superuser, schema) { const firebaseReaderRole = firebasereader(databaseId); return [ `do $$ begin if not exists (select FROM pg_catalog.pg_roles WHERE rolname = '${firebaseReaderRole}') then CREATE ROLE "${firebaseReaderRole}" WITH ADMIN "${superuser}"; end if; end $$ ;`, `GRANT "${firebaseReaderRole}" TO "cloudsqlsuperuser"`, `GRANT USAGE ON SCHEMA "${schema}" TO "${firebaseReaderRole}"`, `GRANT SELECT ON ALL TABLES IN SCHEMA "${schema}" TO "${firebaseReaderRole}"`, `GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA "${schema}" TO "${firebaseReaderRole}"`, `GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA "${schema}" TO "${firebaseReaderRole}"`, `SET ROLE = '${firebaseowner(databaseId)}';`, `ALTER DEFAULT PRIVILEGES IN SCHEMA "${schema}" GRANT SELECT ON TABLES TO "${firebaseReaderRole}";`, `ALTER DEFAULT PRIVILEGES IN SCHEMA "${schema}" GRANT SELECT, USAGE ON SEQUENCES TO "${firebaseReaderRole}";`, `ALTER DEFAULT PRIVILEGES IN SCHEMA "${schema}" GRANT EXECUTE ON FUNCTIONS TO "${firebaseReaderRole}"`, `SET ROLE = cloudsqlsuperuser`, ]; } async function setupSQLPermissions(instanceId, databaseId, options, silent = false) { const superuser = "firebasesuperuser"; const revokes = []; if (await checkSQLRoleIsGranted(options, instanceId, databaseId, "cloudsqlsuperuser", firebaseowner(databaseId))) { logger_1.logger.warn("Detected cloudsqlsuperuser was previously given to firebase owner, revoking to improve database security."); revokes.push(`REVOKE "cloudsqlsuperuser" FROM "${firebaseowner(databaseId)}"`); } const sqlRoleSetupCmds = (0, lodash_1.concat)(revokes, [`CREATE SCHEMA IF NOT EXISTS "public"`], ownerRolePermissions(databaseId, superuser, "public"), writerRolePermissions(databaseId, superuser, "public"), readerRolePermissions(databaseId, superuser, "public")); return (0, connect_1.executeSqlCmdsAsSuperUser)(options, instanceId, databaseId, sqlRoleSetupCmds, silent); } exports.setupSQLPermissions = setupSQLPermissions;