File: //usr/local/lib/node_modules/firebase-tools/lib/gcp/cloudsql/permissions.js
"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;