firebase-tools 13.33.0 → 13.34.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/dataconnect-sql-grant.js +2 -2
- package/lib/commands/dataconnect-sql-setup.js +35 -0
- package/lib/commands/index.js +1 -0
- package/lib/dataconnect/schemaMigration.js +32 -9
- package/lib/deploy/index.js +16 -20
- package/lib/emulator/downloadableEmulators.js +9 -9
- package/lib/gcp/cloudsql/cloudsqladmin.js +22 -1
- package/lib/gcp/cloudsql/connect.js +4 -9
- package/lib/gcp/cloudsql/permissions.js +45 -99
- package/lib/gcp/cloudsql/permissions_setup.js +201 -0
- package/package.json +1 -1
|
@@ -9,8 +9,8 @@ const fileUtils_1 = require("../dataconnect/fileUtils");
|
|
|
9
9
|
const schemaMigration_1 = require("../dataconnect/schemaMigration");
|
|
10
10
|
const requireAuth_1 = require("../requireAuth");
|
|
11
11
|
const error_1 = require("../error");
|
|
12
|
-
const
|
|
13
|
-
const allowedRoles = Object.keys(
|
|
12
|
+
const permissions_setup_1 = require("../gcp/cloudsql/permissions_setup");
|
|
13
|
+
const allowedRoles = Object.keys(permissions_setup_1.fdcSqlRoleMap);
|
|
14
14
|
exports.command = new command_1.Command("dataconnect:sql:grant [serviceId]")
|
|
15
15
|
.description("Grants the SQL role <role> to the provided user or service account <email>.")
|
|
16
16
|
.option("-R, --role <role>", "The SQL role to grant. One of: owner, writer, or reader.")
|
|
@@ -0,0 +1,35 @@
|
|
|
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 fileUtils_1 = require("../dataconnect/fileUtils");
|
|
7
|
+
const error_1 = require("../error");
|
|
8
|
+
const requireAuth_1 = require("../requireAuth");
|
|
9
|
+
const requirePermissions_1 = require("../requirePermissions");
|
|
10
|
+
const ensureApis_1 = require("../dataconnect/ensureApis");
|
|
11
|
+
const permissions_setup_1 = require("../gcp/cloudsql/permissions_setup");
|
|
12
|
+
const permissions_1 = require("../gcp/cloudsql/permissions");
|
|
13
|
+
const schemaMigration_1 = require("../dataconnect/schemaMigration");
|
|
14
|
+
exports.command = new command_1.Command("dataconnect:sql:setup [serviceId]")
|
|
15
|
+
.description("Setup your CloudSQL database")
|
|
16
|
+
.before(requirePermissions_1.requirePermissions, [
|
|
17
|
+
"firebasedataconnect.services.list",
|
|
18
|
+
"firebasedataconnect.schemas.list",
|
|
19
|
+
"firebasedataconnect.schemas.update",
|
|
20
|
+
"cloudsql.instances.connect",
|
|
21
|
+
])
|
|
22
|
+
.before(requireAuth_1.requireAuth)
|
|
23
|
+
.action(async (serviceId, options) => {
|
|
24
|
+
var _a;
|
|
25
|
+
const projectId = (0, projectUtils_1.needProjectId)(options);
|
|
26
|
+
await (0, ensureApis_1.ensureApis)(projectId);
|
|
27
|
+
const serviceInfo = await (0, fileUtils_1.pickService)(projectId, options.config, serviceId);
|
|
28
|
+
const instanceId = (_a = serviceInfo.dataConnectYaml.schema.datasource.postgresql) === null || _a === void 0 ? void 0 : _a.cloudSql.instanceId;
|
|
29
|
+
if (!instanceId) {
|
|
30
|
+
throw new error_1.FirebaseError("dataconnect.yaml is missing field schema.datasource.postgresql.cloudsql.instanceId");
|
|
31
|
+
}
|
|
32
|
+
const { databaseId } = (0, schemaMigration_1.getIdentifiers)(serviceInfo.schema);
|
|
33
|
+
const schemaInfo = await (0, permissions_setup_1.getSchemaMetadata)(instanceId, databaseId, permissions_1.DEFAULT_SCHEMA, options);
|
|
34
|
+
await (0, permissions_setup_1.setupSQLPermissions)(instanceId, databaseId, schemaInfo, options);
|
|
35
|
+
});
|
package/lib/commands/index.js
CHANGED
|
@@ -216,6 +216,7 @@ function load(client) {
|
|
|
216
216
|
client.dataconnect.services.list = loadCommand("dataconnect-services-list");
|
|
217
217
|
client.dataconnect.sql = {};
|
|
218
218
|
client.dataconnect.sql.diff = loadCommand("dataconnect-sql-diff");
|
|
219
|
+
client.dataconnect.sql.setup = loadCommand("dataconnect-sql-setup");
|
|
219
220
|
client.dataconnect.sql.migrate = loadCommand("dataconnect-sql-migrate");
|
|
220
221
|
client.dataconnect.sql.grant = loadCommand("dataconnect-sql-grant");
|
|
221
222
|
client.dataconnect.sql.shell = loadCommand("dataconnect-sql-shell");
|
|
@@ -6,13 +6,15 @@ const sql_formatter_1 = require("sql-formatter");
|
|
|
6
6
|
const types_1 = require("./types");
|
|
7
7
|
const client_1 = require("./client");
|
|
8
8
|
const connect_1 = require("../gcp/cloudsql/connect");
|
|
9
|
-
const permissions_1 = require("../gcp/cloudsql/permissions");
|
|
10
|
-
const cloudSqlAdminClient = require("../gcp/cloudsql/cloudsqladmin");
|
|
11
9
|
const projectUtils_1 = require("../projectUtils");
|
|
10
|
+
const permissions_setup_1 = require("../gcp/cloudsql/permissions_setup");
|
|
11
|
+
const permissions_1 = require("../gcp/cloudsql/permissions");
|
|
12
12
|
const prompt_1 = require("../prompt");
|
|
13
13
|
const logger_1 = require("../logger");
|
|
14
14
|
const error_1 = require("../error");
|
|
15
15
|
const utils_1 = require("../utils");
|
|
16
|
+
const cloudsqladmin_1 = require("../gcp/cloudsql/cloudsqladmin");
|
|
17
|
+
const cloudSqlAdminClient = require("../gcp/cloudsql/cloudsqladmin");
|
|
16
18
|
const errors = require("./errors");
|
|
17
19
|
async function diffSchema(schema, schemaValidation) {
|
|
18
20
|
const { serviceName, instanceName, databaseId } = getIdentifiers(schema);
|
|
@@ -156,12 +158,29 @@ async function grantRoleToUserInSchema(options, schema) {
|
|
|
156
158
|
const { instanceId, databaseId } = getIdentifiers(schema);
|
|
157
159
|
const projectId = (0, projectUtils_1.needProjectId)(options);
|
|
158
160
|
const { user, mode } = (0, connect_1.toDatabaseUser)(email);
|
|
159
|
-
const fdcSqlRole =
|
|
160
|
-
const userIsCSQLAdmin = await (0,
|
|
161
|
+
const fdcSqlRole = permissions_setup_1.fdcSqlRoleMap[role](databaseId);
|
|
162
|
+
const userIsCSQLAdmin = await (0, cloudsqladmin_1.iamUserIsCSQLAdmin)(options);
|
|
161
163
|
if (!userIsCSQLAdmin) {
|
|
162
164
|
throw new error_1.FirebaseError(`Only users with 'roles/cloudsql.admin' can grant SQL roles. If you do not have this role, ask your database administrator to run this command or manually grant ${fdcSqlRole} to ${user}`);
|
|
163
165
|
}
|
|
164
|
-
await (0,
|
|
166
|
+
const schemaInfo = await (0, permissions_setup_1.getSchemaMetadata)(instanceId, databaseId, permissions_1.DEFAULT_SCHEMA, options);
|
|
167
|
+
let isGreenfieldSetup = schemaInfo.setupStatus === permissions_setup_1.SchemaSetupStatus.GreenField;
|
|
168
|
+
switch (schemaInfo.setupStatus) {
|
|
169
|
+
case permissions_setup_1.SchemaSetupStatus.NotSetup:
|
|
170
|
+
case permissions_setup_1.SchemaSetupStatus.NotFound:
|
|
171
|
+
const newSetupStatus = await (0, permissions_setup_1.setupSQLPermissions)(instanceId, databaseId, schemaInfo, options);
|
|
172
|
+
isGreenfieldSetup = newSetupStatus === permissions_setup_1.SchemaSetupStatus.GreenField;
|
|
173
|
+
break;
|
|
174
|
+
default:
|
|
175
|
+
logger_1.logger.info(`Detected schema "${schemaInfo.name}" is setup in ${schemaInfo.setupStatus} mode. Skipping Setup.`);
|
|
176
|
+
break;
|
|
177
|
+
}
|
|
178
|
+
if (!isGreenfieldSetup && fdcSqlRole === (0, permissions_1.firebaseowner)(databaseId, permissions_1.DEFAULT_SCHEMA)) {
|
|
179
|
+
const newSetupStatus = await (0, permissions_setup_1.setupSQLPermissions)(instanceId, databaseId, schemaInfo, options);
|
|
180
|
+
if (newSetupStatus !== permissions_setup_1.SchemaSetupStatus.GreenField) {
|
|
181
|
+
throw new error_1.FirebaseError(`Can't grant owner rule for brownfield databases. Consider fully migrating your database to FDC using 'firebase dataconnect:sql:setup'`);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
165
184
|
await cloudSqlAdminClient.createUser(projectId, instanceId, mode, user);
|
|
166
185
|
await (0, connect_1.executeSqlCmdsAsSuperUser)(options, instanceId, databaseId, [`GRANT "${fdcSqlRole}" TO "${user}"`], false);
|
|
167
186
|
}
|
|
@@ -232,16 +251,20 @@ async function handleIncompatibleSchemaError(args) {
|
|
|
232
251
|
if (commandsToExecute.length) {
|
|
233
252
|
const commandsToExecuteBySuperUser = commandsToExecute.filter((sql) => sql.startsWith("CREATE EXTENSION") || sql.startsWith("CREATE SCHEMA"));
|
|
234
253
|
const commandsToExecuteByOwner = commandsToExecute.filter((sql) => !commandsToExecuteBySuperUser.includes(sql));
|
|
235
|
-
const userIsCSQLAdmin = await (0,
|
|
254
|
+
const userIsCSQLAdmin = await (0, cloudsqladmin_1.iamUserIsCSQLAdmin)(options);
|
|
236
255
|
if (!userIsCSQLAdmin && commandsToExecuteBySuperUser.length) {
|
|
237
256
|
throw new error_1.FirebaseError(`Some SQL commands required for this migration require Admin permissions.\n
|
|
238
257
|
Please ask a user with 'roles/cloudsql.admin' to apply the following commands.\n
|
|
239
258
|
${commandsToExecuteBySuperUser.join("\n")}`);
|
|
240
259
|
}
|
|
241
|
-
|
|
242
|
-
|
|
260
|
+
const schemaInfo = await (0, permissions_setup_1.getSchemaMetadata)(instanceId, databaseId, permissions_1.DEFAULT_SCHEMA, options);
|
|
261
|
+
if (schemaInfo.setupStatus !== permissions_setup_1.SchemaSetupStatus.GreenField) {
|
|
262
|
+
const newSetupStatus = await (0, permissions_setup_1.setupSQLPermissions)(instanceId, databaseId, schemaInfo, options, true);
|
|
263
|
+
if (newSetupStatus !== permissions_setup_1.SchemaSetupStatus.GreenField) {
|
|
264
|
+
throw new error_1.FirebaseError(`Can't migrate brownfield databases. Consider fully migrating your database to FDC using 'firebase dataconnect:sql:setup'`);
|
|
265
|
+
}
|
|
243
266
|
}
|
|
244
|
-
if (!(await (0,
|
|
267
|
+
if (!(await (0, permissions_setup_1.checkSQLRoleIsGranted)(options, instanceId, databaseId, (0, permissions_1.firebaseowner)(databaseId), (await (0, connect_1.getIAMUser)(options)).user))) {
|
|
245
268
|
throw new error_1.FirebaseError(`Command aborted. Only users granted firebaseowner SQL role can run migrations.`);
|
|
246
269
|
}
|
|
247
270
|
if (commandsToExecuteBySuperUser.length) {
|
package/lib/deploy/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.deploy = exports.
|
|
3
|
+
exports.deploy = exports.isDeployingWebFramework = void 0;
|
|
4
4
|
const clc = require("colorette");
|
|
5
5
|
const logger_1 = require("../logger");
|
|
6
6
|
const api_1 = require("../api");
|
|
@@ -40,25 +40,20 @@ const chain = async function (fns, context, options, payload) {
|
|
|
40
40
|
await latest(context, options, payload);
|
|
41
41
|
}
|
|
42
42
|
};
|
|
43
|
-
const
|
|
44
|
-
if (!only)
|
|
45
|
-
return true;
|
|
46
|
-
if (!only.includes("hosting:"))
|
|
47
|
-
return true;
|
|
48
|
-
const targetStr = `hosting:${target !== null && target !== void 0 ? target : ""}`;
|
|
49
|
-
return only.split(",").some((t) => t === targetStr);
|
|
50
|
-
};
|
|
51
|
-
exports.matchesHostingTarget = matchesHostingTarget;
|
|
52
|
-
const prepareFrameworksIfNeeded = async function (targetNames, options, context) {
|
|
43
|
+
const isDeployingWebFramework = (options) => {
|
|
53
44
|
const config = options.config.get("hosting");
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
45
|
+
const webFrameworkInConfig = (Array.isArray(config) ? config : [config]).find((it) => it.source);
|
|
46
|
+
if (!webFrameworkInConfig)
|
|
47
|
+
return false;
|
|
48
|
+
if (!options.only)
|
|
49
|
+
return true;
|
|
50
|
+
return options.only.split(",").some((it) => {
|
|
51
|
+
const [target, site] = it.split(":");
|
|
52
|
+
return (target === "hosting" &&
|
|
53
|
+
[webFrameworkInConfig.site, webFrameworkInConfig.target].includes(site));
|
|
54
|
+
});
|
|
60
55
|
};
|
|
61
|
-
exports.
|
|
56
|
+
exports.isDeployingWebFramework = isDeployingWebFramework;
|
|
62
57
|
const deploy = async function (targetNames, options, customContext = {}) {
|
|
63
58
|
var _a, _b, _c;
|
|
64
59
|
const projectId = (0, projectUtils_1.needProjectId)(options);
|
|
@@ -70,8 +65,9 @@ const deploy = async function (targetNames, options, customContext = {}) {
|
|
|
70
65
|
const releases = [];
|
|
71
66
|
const postdeploys = [];
|
|
72
67
|
const startTime = Date.now();
|
|
73
|
-
if (targetNames.includes("hosting")) {
|
|
74
|
-
|
|
68
|
+
if (targetNames.includes("hosting") && (0, exports.isDeployingWebFramework)(options)) {
|
|
69
|
+
experiments.assertEnabled("webframeworks", "deploy a web framework from source");
|
|
70
|
+
await (0, frameworks_1.prepareFrameworks)("deploy", targetNames, context, options);
|
|
75
71
|
}
|
|
76
72
|
if (targetNames.includes("hosting") && (0, prepare_1.hasPinnedFunctions)(options)) {
|
|
77
73
|
experiments.assertEnabled("pintags", "deploy a tagged function as a hosting rewrite");
|
|
@@ -48,20 +48,20 @@ const EMULATOR_UPDATE_DETAILS = {
|
|
|
48
48
|
},
|
|
49
49
|
dataconnect: process.platform === "darwin"
|
|
50
50
|
? {
|
|
51
|
-
version: "1.8.
|
|
52
|
-
expectedSize:
|
|
53
|
-
expectedChecksum: "
|
|
51
|
+
version: "1.8.5",
|
|
52
|
+
expectedSize: 25600768,
|
|
53
|
+
expectedChecksum: "7e2a935f972ce30e075cca1f36e24663",
|
|
54
54
|
}
|
|
55
55
|
: process.platform === "win32"
|
|
56
56
|
? {
|
|
57
|
-
version: "1.8.
|
|
58
|
-
expectedSize:
|
|
59
|
-
expectedChecksum: "
|
|
57
|
+
version: "1.8.5",
|
|
58
|
+
expectedSize: 26031616,
|
|
59
|
+
expectedChecksum: "da063f9893b0ff4c99f280653c717977",
|
|
60
60
|
}
|
|
61
61
|
: {
|
|
62
|
-
version: "1.8.
|
|
63
|
-
expectedSize:
|
|
64
|
-
expectedChecksum: "
|
|
62
|
+
version: "1.8.5",
|
|
63
|
+
expectedSize: 25514136,
|
|
64
|
+
expectedChecksum: "6564f779f7f5a467e587d7093ed7c3e3",
|
|
65
65
|
},
|
|
66
66
|
};
|
|
67
67
|
exports.DownloadDetails = {
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.listUsers = exports.deleteUser = exports.getUser = exports.createUser = exports.deleteDatabase = exports.createDatabase = exports.getDatabase = exports.listDatabases = exports.updateInstanceForDataConnect = exports.createInstance = exports.instanceConsoleLink = exports.getInstance = exports.listInstances = void 0;
|
|
3
|
+
exports.listUsers = exports.deleteUser = exports.getUser = exports.createUser = exports.deleteDatabase = exports.createDatabase = exports.getDatabase = exports.listDatabases = exports.updateInstanceForDataConnect = exports.createInstance = exports.instanceConsoleLink = exports.getInstance = exports.listInstances = exports.iamUserIsCSQLAdmin = void 0;
|
|
4
4
|
const apiv2_1 = require("../../apiv2");
|
|
5
5
|
const api_1 = require("../../api");
|
|
6
6
|
const operationPoller = require("../../operation-poller");
|
|
7
|
+
const projectUtils_1 = require("../../projectUtils");
|
|
8
|
+
const logger_1 = require("../../logger");
|
|
9
|
+
const iam_1 = require("../iam");
|
|
7
10
|
const error_1 = require("../../error");
|
|
8
11
|
const API_VERSION = "v1";
|
|
9
12
|
const client = new apiv2_1.Client({
|
|
@@ -11,6 +14,24 @@ const client = new apiv2_1.Client({
|
|
|
11
14
|
auth: true,
|
|
12
15
|
apiVersion: API_VERSION,
|
|
13
16
|
});
|
|
17
|
+
async function iamUserIsCSQLAdmin(options) {
|
|
18
|
+
const projectId = (0, projectUtils_1.needProjectId)(options);
|
|
19
|
+
const requiredPermissions = [
|
|
20
|
+
"cloudsql.instances.connect",
|
|
21
|
+
"cloudsql.instances.get",
|
|
22
|
+
"cloudsql.users.create",
|
|
23
|
+
"cloudsql.users.update",
|
|
24
|
+
];
|
|
25
|
+
try {
|
|
26
|
+
const iamResult = await (0, iam_1.testIamPermissions)(projectId, requiredPermissions);
|
|
27
|
+
return iamResult.passed;
|
|
28
|
+
}
|
|
29
|
+
catch (err) {
|
|
30
|
+
logger_1.logger.debug(`[iam] error while checking permissions, command may fail: ${err}`);
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
exports.iamUserIsCSQLAdmin = iamUserIsCSQLAdmin;
|
|
14
35
|
async function listInstances(projectId) {
|
|
15
36
|
var _a;
|
|
16
37
|
const res = await client.get(`projects/${projectId}/instances`);
|
|
@@ -11,7 +11,6 @@ const utils = require("../../utils");
|
|
|
11
11
|
const logger_1 = require("../../logger");
|
|
12
12
|
const error_1 = require("../../error");
|
|
13
13
|
const fbToolsAuthClient_1 = require("./fbToolsAuthClient");
|
|
14
|
-
const permissions_1 = require("./permissions");
|
|
15
14
|
async function execute(sqlStatements, opts) {
|
|
16
15
|
const logFn = opts.silent ? logger_1.logger.debug : logger_1.logger.info;
|
|
17
16
|
const instance = await cloudSqlAdminClient.getInstance(opts.projectId, opts.instanceId);
|
|
@@ -61,11 +60,12 @@ async function execute(sqlStatements, opts) {
|
|
|
61
60
|
}
|
|
62
61
|
}
|
|
63
62
|
const conn = await pool.connect();
|
|
63
|
+
const results = [];
|
|
64
64
|
logFn(`Logged in as ${opts.username}`);
|
|
65
65
|
for (const s of sqlStatements) {
|
|
66
66
|
logFn(`Executing: '${s}'`);
|
|
67
67
|
try {
|
|
68
|
-
await conn.query(s);
|
|
68
|
+
results.push(await conn.query(s));
|
|
69
69
|
}
|
|
70
70
|
catch (err) {
|
|
71
71
|
throw new error_1.FirebaseError(`Error executing ${err}`);
|
|
@@ -74,6 +74,7 @@ async function execute(sqlStatements, opts) {
|
|
|
74
74
|
conn.release();
|
|
75
75
|
await pool.end();
|
|
76
76
|
connector.close();
|
|
77
|
+
return results;
|
|
77
78
|
}
|
|
78
79
|
exports.execute = execute;
|
|
79
80
|
async function executeSqlCmdsAsIamUser(options, instanceId, databaseId, cmds, silent = false) {
|
|
@@ -93,7 +94,7 @@ async function executeSqlCmdsAsSuperUser(options, instanceId, databaseId, cmds,
|
|
|
93
94
|
const superuser = "firebasesuperuser";
|
|
94
95
|
const temporaryPassword = utils.generateId(20);
|
|
95
96
|
await cloudSqlAdminClient.createUser(projectId, instanceId, "BUILT_IN", superuser, temporaryPassword);
|
|
96
|
-
return await execute([`SET ROLE =
|
|
97
|
+
return await execute([`SET ROLE = '${superuser}'`, ...cmds], {
|
|
97
98
|
projectId,
|
|
98
99
|
instanceId,
|
|
99
100
|
databaseId,
|
|
@@ -122,12 +123,6 @@ async function setupIAMUsers(instanceId, databaseId, options) {
|
|
|
122
123
|
const projectNumber = await (0, projectUtils_1.needProjectNumber)(options);
|
|
123
124
|
const { user: fdcP4SAUser, mode: fdcP4SAmode } = toDatabaseUser(getDataConnectP4SA(projectNumber));
|
|
124
125
|
await cloudSqlAdminClient.createUser(projectId, instanceId, fdcP4SAmode, fdcP4SAUser);
|
|
125
|
-
await (0, permissions_1.setupSQLPermissions)(instanceId, databaseId, options, true);
|
|
126
|
-
const grants = [
|
|
127
|
-
`GRANT "${(0, permissions_1.firebaseowner)(databaseId)}" TO "${user}"`,
|
|
128
|
-
`GRANT "${(0, permissions_1.firebasewriter)(databaseId)}" TO "${fdcP4SAUser}"`,
|
|
129
|
-
];
|
|
130
|
-
await executeSqlCmdsAsSuperUser(options, instanceId, databaseId, grants, true);
|
|
131
126
|
return user;
|
|
132
127
|
}
|
|
133
128
|
exports.setupIAMUsers = setupIAMUsers;
|
|
@@ -1,89 +1,22 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
const lodash_1 = require("lodash");
|
|
9
|
-
const error_1 = require("../../error");
|
|
10
|
-
function firebaseowner(databaseId) {
|
|
11
|
-
return `firebaseowner_${databaseId}_public`;
|
|
3
|
+
exports.defaultPermissions = exports.readerRolePermissions = exports.writerRolePermissions = exports.ownerRolePermissions = exports.firebasewriter = exports.firebasereader = exports.firebaseowner = exports.FIREBASE_SUPER_USER = exports.DEFAULT_SCHEMA = void 0;
|
|
4
|
+
exports.DEFAULT_SCHEMA = "public";
|
|
5
|
+
exports.FIREBASE_SUPER_USER = "firebasesuperuser";
|
|
6
|
+
function firebaseowner(databaseId, schema = exports.DEFAULT_SCHEMA) {
|
|
7
|
+
return `firebaseowner_${databaseId}_${schema}`;
|
|
12
8
|
}
|
|
13
9
|
exports.firebaseowner = firebaseowner;
|
|
14
|
-
function firebasereader(databaseId) {
|
|
15
|
-
return `firebasereader_${databaseId}
|
|
10
|
+
function firebasereader(databaseId, schema = exports.DEFAULT_SCHEMA) {
|
|
11
|
+
return `firebasereader_${databaseId}_${schema}`;
|
|
16
12
|
}
|
|
17
13
|
exports.firebasereader = firebasereader;
|
|
18
|
-
function firebasewriter(databaseId) {
|
|
19
|
-
return `firebasewriter_${databaseId}
|
|
14
|
+
function firebasewriter(databaseId, schema = exports.DEFAULT_SCHEMA) {
|
|
15
|
+
return `firebasewriter_${databaseId}_${schema}`;
|
|
20
16
|
}
|
|
21
17
|
exports.firebasewriter = firebasewriter;
|
|
22
|
-
exports.fdcSqlRoleMap = {
|
|
23
|
-
owner: firebaseowner,
|
|
24
|
-
writer: firebasewriter,
|
|
25
|
-
reader: firebasereader,
|
|
26
|
-
};
|
|
27
|
-
async function checkSQLRoleIsGranted(options, instanceId, databaseId, grantedRole, granteeRole) {
|
|
28
|
-
const checkCmd = `
|
|
29
|
-
DO $$
|
|
30
|
-
DECLARE
|
|
31
|
-
role_count INTEGER;
|
|
32
|
-
BEGIN
|
|
33
|
-
-- Count the number of rows matching the criteria
|
|
34
|
-
SELECT COUNT(*)
|
|
35
|
-
INTO role_count
|
|
36
|
-
FROM
|
|
37
|
-
pg_auth_members m
|
|
38
|
-
JOIN
|
|
39
|
-
pg_roles grantee ON grantee.oid = m.member
|
|
40
|
-
JOIN
|
|
41
|
-
pg_roles granted ON granted.oid = m.roleid
|
|
42
|
-
JOIN
|
|
43
|
-
pg_roles grantor ON grantor.oid = m.grantor
|
|
44
|
-
WHERE
|
|
45
|
-
granted.rolname = '${grantedRole}'
|
|
46
|
-
AND grantee.rolname = '${granteeRole}';
|
|
47
|
-
|
|
48
|
-
-- If no rows were found, raise an exception
|
|
49
|
-
IF role_count = 0 THEN
|
|
50
|
-
RAISE EXCEPTION 'Role "%", is not granted to role "%".', '${grantedRole}', '${granteeRole}';
|
|
51
|
-
END IF;
|
|
52
|
-
END $$;
|
|
53
|
-
`;
|
|
54
|
-
try {
|
|
55
|
-
await (0, connect_1.executeSqlCmdsAsIamUser)(options, instanceId, databaseId, [checkCmd], true);
|
|
56
|
-
return true;
|
|
57
|
-
}
|
|
58
|
-
catch (e) {
|
|
59
|
-
if (e instanceof error_1.FirebaseError && e.message.includes("not granted to role")) {
|
|
60
|
-
return false;
|
|
61
|
-
}
|
|
62
|
-
logger_1.logger.error(`Role Check Failed: ${e}`);
|
|
63
|
-
throw e;
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
exports.checkSQLRoleIsGranted = checkSQLRoleIsGranted;
|
|
67
|
-
async function iamUserIsCSQLAdmin(options) {
|
|
68
|
-
const projectId = (0, projectUtils_1.needProjectId)(options);
|
|
69
|
-
const requiredPermissions = [
|
|
70
|
-
"cloudsql.instances.connect",
|
|
71
|
-
"cloudsql.instances.get",
|
|
72
|
-
"cloudsql.users.create",
|
|
73
|
-
"cloudsql.users.update",
|
|
74
|
-
];
|
|
75
|
-
try {
|
|
76
|
-
const iamResult = await (0, iam_1.testIamPermissions)(projectId, requiredPermissions);
|
|
77
|
-
return iamResult.passed;
|
|
78
|
-
}
|
|
79
|
-
catch (err) {
|
|
80
|
-
logger_1.logger.debug(`[iam] error while checking permissions, command may fail: ${err}`);
|
|
81
|
-
return false;
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
exports.iamUserIsCSQLAdmin = iamUserIsCSQLAdmin;
|
|
85
18
|
function ownerRolePermissions(databaseId, superuser, schema) {
|
|
86
|
-
const firebaseOwnerRole = firebaseowner(databaseId);
|
|
19
|
+
const firebaseOwnerRole = firebaseowner(databaseId, schema);
|
|
87
20
|
return [
|
|
88
21
|
`do
|
|
89
22
|
$$
|
|
@@ -102,8 +35,9 @@ function ownerRolePermissions(databaseId, superuser, schema) {
|
|
|
102
35
|
`GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA "${schema}" TO "${firebaseOwnerRole}"`,
|
|
103
36
|
];
|
|
104
37
|
}
|
|
38
|
+
exports.ownerRolePermissions = ownerRolePermissions;
|
|
105
39
|
function writerRolePermissions(databaseId, superuser, schema) {
|
|
106
|
-
const firebaseWriterRole = firebasewriter(databaseId);
|
|
40
|
+
const firebaseWriterRole = firebasewriter(databaseId, schema);
|
|
107
41
|
return [
|
|
108
42
|
`do
|
|
109
43
|
$$
|
|
@@ -120,15 +54,11 @@ function writerRolePermissions(databaseId, superuser, schema) {
|
|
|
120
54
|
`GRANT SELECT, INSERT, UPDATE, DELETE, TRUNCATE ON ALL TABLES IN SCHEMA "${schema}" TO "${firebaseWriterRole}"`,
|
|
121
55
|
`GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA "${schema}" TO "${firebaseWriterRole}"`,
|
|
122
56
|
`GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA "${schema}" TO "${firebaseWriterRole}"`,
|
|
123
|
-
`SET ROLE = '${firebaseowner(databaseId)}';`,
|
|
124
|
-
`ALTER DEFAULT PRIVILEGES IN SCHEMA "${schema}" GRANT SELECT, INSERT, UPDATE, DELETE, TRUNCATE ON TABLES TO "${firebaseWriterRole}";`,
|
|
125
|
-
`ALTER DEFAULT PRIVILEGES IN SCHEMA "${schema}" GRANT USAGE ON SEQUENCES TO "${firebaseWriterRole}";`,
|
|
126
|
-
`ALTER DEFAULT PRIVILEGES IN SCHEMA "${schema}" GRANT EXECUTE ON FUNCTIONS TO "${firebaseWriterRole}"`,
|
|
127
|
-
`SET ROLE = cloudsqlsuperuser`,
|
|
128
57
|
];
|
|
129
58
|
}
|
|
59
|
+
exports.writerRolePermissions = writerRolePermissions;
|
|
130
60
|
function readerRolePermissions(databaseId, superuser, schema) {
|
|
131
|
-
const firebaseReaderRole = firebasereader(databaseId);
|
|
61
|
+
const firebaseReaderRole = firebasereader(databaseId, schema);
|
|
132
62
|
return [
|
|
133
63
|
`do
|
|
134
64
|
$$
|
|
@@ -145,21 +75,37 @@ function readerRolePermissions(databaseId, superuser, schema) {
|
|
|
145
75
|
`GRANT SELECT ON ALL TABLES IN SCHEMA "${schema}" TO "${firebaseReaderRole}"`,
|
|
146
76
|
`GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA "${schema}" TO "${firebaseReaderRole}"`,
|
|
147
77
|
`GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA "${schema}" TO "${firebaseReaderRole}"`,
|
|
148
|
-
`SET ROLE = '${firebaseowner(databaseId)}';`,
|
|
149
|
-
`ALTER DEFAULT PRIVILEGES IN SCHEMA "${schema}" GRANT SELECT ON TABLES TO "${firebaseReaderRole}";`,
|
|
150
|
-
`ALTER DEFAULT PRIVILEGES IN SCHEMA "${schema}" GRANT SELECT, USAGE ON SEQUENCES TO "${firebaseReaderRole}";`,
|
|
151
|
-
`ALTER DEFAULT PRIVILEGES IN SCHEMA "${schema}" GRANT EXECUTE ON FUNCTIONS TO "${firebaseReaderRole}"`,
|
|
152
|
-
`SET ROLE = cloudsqlsuperuser`,
|
|
153
78
|
];
|
|
154
79
|
}
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
const
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
80
|
+
exports.readerRolePermissions = readerRolePermissions;
|
|
81
|
+
function defaultPermissions(databaseId, schema, ownerRole) {
|
|
82
|
+
const firebaseWriterRole = firebasewriter(databaseId, schema);
|
|
83
|
+
const firebaseReaderRole = firebasereader(databaseId, schema);
|
|
84
|
+
return [
|
|
85
|
+
`ALTER DEFAULT PRIVILEGES
|
|
86
|
+
FOR ROLE "${ownerRole}"
|
|
87
|
+
IN SCHEMA "${schema}"
|
|
88
|
+
GRANT SELECT, INSERT, UPDATE, DELETE, TRUNCATE ON TABLES TO "${firebaseWriterRole}";`,
|
|
89
|
+
`ALTER DEFAULT PRIVILEGES
|
|
90
|
+
FOR ROLE "${ownerRole}"
|
|
91
|
+
IN SCHEMA "${schema}"
|
|
92
|
+
GRANT USAGE ON SEQUENCES TO "${firebaseWriterRole}";`,
|
|
93
|
+
`ALTER DEFAULT PRIVILEGES
|
|
94
|
+
FOR ROLE "${ownerRole}"
|
|
95
|
+
IN SCHEMA "${schema}"
|
|
96
|
+
GRANT EXECUTE ON FUNCTIONS TO "${firebaseWriterRole}";`,
|
|
97
|
+
`ALTER DEFAULT PRIVILEGES
|
|
98
|
+
FOR ROLE "${ownerRole}"
|
|
99
|
+
IN SCHEMA "${schema}"
|
|
100
|
+
GRANT SELECT ON TABLES TO "${firebaseReaderRole}";`,
|
|
101
|
+
`ALTER DEFAULT PRIVILEGES
|
|
102
|
+
FOR ROLE "${ownerRole}"
|
|
103
|
+
IN SCHEMA "${schema}"
|
|
104
|
+
GRANT USAGE ON SEQUENCES TO "${firebaseReaderRole}";`,
|
|
105
|
+
`ALTER DEFAULT PRIVILEGES
|
|
106
|
+
FOR ROLE "${ownerRole}"
|
|
107
|
+
IN SCHEMA "${schema}"
|
|
108
|
+
GRANT EXECUTE ON FUNCTIONS TO "${firebaseReaderRole}";`,
|
|
109
|
+
];
|
|
164
110
|
}
|
|
165
|
-
exports.
|
|
111
|
+
exports.defaultPermissions = defaultPermissions;
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.brownfieldSqlSetup = exports.setupBrownfieldAsGreenfield = exports.getSchemaMetadata = exports.greenFieldSchemaSetup = exports.setupSQLPermissions = exports.checkSQLRoleIsGranted = exports.fdcSqlRoleMap = exports.SchemaSetupStatus = void 0;
|
|
4
|
+
const permissions_1 = require("./permissions");
|
|
5
|
+
const cloudsqladmin_1 = require("./cloudsqladmin");
|
|
6
|
+
const connect_1 = require("./connect");
|
|
7
|
+
const logger_1 = require("../../logger");
|
|
8
|
+
const prompt_1 = require("../../prompt");
|
|
9
|
+
const clc = require("colorette");
|
|
10
|
+
const error_1 = require("../../error");
|
|
11
|
+
const projectUtils_1 = require("../../projectUtils");
|
|
12
|
+
const connect_2 = require("./connect");
|
|
13
|
+
const lodash_1 = require("lodash");
|
|
14
|
+
const connect_3 = require("./connect");
|
|
15
|
+
var SchemaSetupStatus;
|
|
16
|
+
(function (SchemaSetupStatus) {
|
|
17
|
+
SchemaSetupStatus["NotSetup"] = "not-setup";
|
|
18
|
+
SchemaSetupStatus["GreenField"] = "greenfield";
|
|
19
|
+
SchemaSetupStatus["BrownField"] = "brownfield";
|
|
20
|
+
SchemaSetupStatus["NotFound"] = "not-found";
|
|
21
|
+
})(SchemaSetupStatus = exports.SchemaSetupStatus || (exports.SchemaSetupStatus = {}));
|
|
22
|
+
exports.fdcSqlRoleMap = {
|
|
23
|
+
owner: permissions_1.firebaseowner,
|
|
24
|
+
writer: permissions_1.firebasewriter,
|
|
25
|
+
reader: permissions_1.firebasereader,
|
|
26
|
+
};
|
|
27
|
+
async function checkSQLRoleIsGranted(options, instanceId, databaseId, grantedRole, granteeRole) {
|
|
28
|
+
const checkCmd = `
|
|
29
|
+
DO $$
|
|
30
|
+
DECLARE
|
|
31
|
+
role_count INTEGER;
|
|
32
|
+
BEGIN
|
|
33
|
+
-- Count the number of rows matching the criteria
|
|
34
|
+
SELECT COUNT(*)
|
|
35
|
+
INTO role_count
|
|
36
|
+
FROM
|
|
37
|
+
pg_auth_members m
|
|
38
|
+
JOIN
|
|
39
|
+
pg_roles grantee ON grantee.oid = m.member
|
|
40
|
+
JOIN
|
|
41
|
+
pg_roles granted ON granted.oid = m.roleid
|
|
42
|
+
JOIN
|
|
43
|
+
pg_roles grantor ON grantor.oid = m.grantor
|
|
44
|
+
WHERE
|
|
45
|
+
granted.rolname = '${grantedRole}'
|
|
46
|
+
AND grantee.rolname = '${granteeRole}';
|
|
47
|
+
|
|
48
|
+
-- If no rows were found, raise an exception
|
|
49
|
+
IF role_count = 0 THEN
|
|
50
|
+
RAISE EXCEPTION 'Role "%", is not granted to role "%".', '${grantedRole}', '${granteeRole}';
|
|
51
|
+
END IF;
|
|
52
|
+
END $$;
|
|
53
|
+
`;
|
|
54
|
+
try {
|
|
55
|
+
await (0, connect_2.executeSqlCmdsAsIamUser)(options, instanceId, databaseId, [checkCmd], true);
|
|
56
|
+
return true;
|
|
57
|
+
}
|
|
58
|
+
catch (e) {
|
|
59
|
+
if (e instanceof error_1.FirebaseError && e.message.includes("not granted to role")) {
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
logger_1.logger.error(`Role Check Failed: ${e}`);
|
|
63
|
+
throw e;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
exports.checkSQLRoleIsGranted = checkSQLRoleIsGranted;
|
|
67
|
+
async function setupSQLPermissions(instanceId, databaseId, schemaInfo, options, silent = false) {
|
|
68
|
+
const schema = schemaInfo.name;
|
|
69
|
+
logger_1.logger.info(`Attempting to Setup SQL schema "${schema}".`);
|
|
70
|
+
const userIsCSQLAdmin = await (0, cloudsqladmin_1.iamUserIsCSQLAdmin)(options);
|
|
71
|
+
if (!userIsCSQLAdmin) {
|
|
72
|
+
throw new error_1.FirebaseError(`Missing required IAM permission to setup SQL schemas. SQL schema setup requires 'roles/cloudsql.admin' or an equivalent role.`);
|
|
73
|
+
}
|
|
74
|
+
await (0, connect_1.setupIAMUsers)(instanceId, databaseId, options);
|
|
75
|
+
if (schemaInfo.setupStatus === SchemaSetupStatus.GreenField) {
|
|
76
|
+
logger_1.logger.info(`Database ${databaseId} has already been setup. Rerunning setup to repair any missing permissions.`);
|
|
77
|
+
await greenFieldSchemaSetup(instanceId, databaseId, schema, options, silent);
|
|
78
|
+
return SchemaSetupStatus.GreenField;
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
logger_1.logger.info(`Detected schema "${schema}" setup status is ${schemaInfo.setupStatus}.`);
|
|
82
|
+
}
|
|
83
|
+
if (schemaInfo.tables.length === 0) {
|
|
84
|
+
logger_1.logger.info(`Found no tables in schema "${schema}", assuming greenfield project.`);
|
|
85
|
+
await greenFieldSchemaSetup(instanceId, databaseId, schema, options, silent);
|
|
86
|
+
logger_1.logger.info(clc.green("Database setup complete."));
|
|
87
|
+
return SchemaSetupStatus.GreenField;
|
|
88
|
+
}
|
|
89
|
+
if (options.nonInteractive || options.force) {
|
|
90
|
+
throw new error_1.FirebaseError(`Schema "${schema}" isn't set up and can only be set up in interactive mode.`);
|
|
91
|
+
}
|
|
92
|
+
const currentTablesOwners = [...new Set(schemaInfo.tables.map((t) => t.owner))];
|
|
93
|
+
logger_1.logger.info(`We found some existing object owners [${currentTablesOwners.join(", ")}] in your cloudsql "${schema}" schema.`);
|
|
94
|
+
const shouldSetupGreenfield = await (0, prompt_1.confirm)({
|
|
95
|
+
message: clc.yellow("Would you like FDC to handle SQL migrations for you moving forward?\n" +
|
|
96
|
+
`This means we will transfer schema and tables ownership to ${(0, permissions_1.firebaseowner)(databaseId, schema)}\n` +
|
|
97
|
+
"Note: your existing migration tools/roles may lose access."),
|
|
98
|
+
default: false,
|
|
99
|
+
});
|
|
100
|
+
if (shouldSetupGreenfield) {
|
|
101
|
+
await setupBrownfieldAsGreenfield(instanceId, databaseId, schemaInfo, options, silent);
|
|
102
|
+
return SchemaSetupStatus.GreenField;
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
logger_1.logger.info(clc.yellow("Setting up database in brownfield mode.\n" +
|
|
106
|
+
`Note: SQL migrations can't be done through ${clc.bold("firebase dataconnect:sql:migrate")} in this mode.`));
|
|
107
|
+
await brownfieldSqlSetup(instanceId, databaseId, schemaInfo, options, silent);
|
|
108
|
+
logger_1.logger.info(clc.green("Brownfield database setup complete."));
|
|
109
|
+
return SchemaSetupStatus.BrownField;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
exports.setupSQLPermissions = setupSQLPermissions;
|
|
113
|
+
async function greenFieldSchemaSetup(instanceId, databaseId, schema, options, silent = false) {
|
|
114
|
+
const revokes = [];
|
|
115
|
+
if (await checkSQLRoleIsGranted(options, instanceId, databaseId, "cloudsqlsuperuser", (0, permissions_1.firebaseowner)(databaseId))) {
|
|
116
|
+
logger_1.logger.warn("Detected cloudsqlsuperuser was previously given to firebase owner, revoking to improve database security.");
|
|
117
|
+
revokes.push(`REVOKE "cloudsqlsuperuser" FROM "${(0, permissions_1.firebaseowner)(databaseId)}"`);
|
|
118
|
+
}
|
|
119
|
+
const user = (await (0, connect_2.getIAMUser)(options)).user;
|
|
120
|
+
const projectNumber = await (0, projectUtils_1.needProjectNumber)(options);
|
|
121
|
+
const { user: fdcP4SAUser } = (0, connect_3.toDatabaseUser)((0, connect_3.getDataConnectP4SA)(projectNumber));
|
|
122
|
+
const sqlRoleSetupCmds = (0, lodash_1.concat)(revokes, [`CREATE SCHEMA IF NOT EXISTS "${schema}"`], (0, permissions_1.ownerRolePermissions)(databaseId, permissions_1.FIREBASE_SUPER_USER, schema), (0, permissions_1.writerRolePermissions)(databaseId, permissions_1.FIREBASE_SUPER_USER, schema), (0, permissions_1.readerRolePermissions)(databaseId, permissions_1.FIREBASE_SUPER_USER, schema), `GRANT "${(0, permissions_1.firebaseowner)(databaseId, schema)}" TO "${user}"`, `GRANT "${(0, permissions_1.firebasewriter)(databaseId, schema)}" TO "${fdcP4SAUser}"`, (0, permissions_1.defaultPermissions)(databaseId, schema, (0, permissions_1.firebaseowner)(databaseId, schema)));
|
|
123
|
+
await (0, connect_2.executeSqlCmdsAsSuperUser)(options, instanceId, databaseId, sqlRoleSetupCmds, silent);
|
|
124
|
+
}
|
|
125
|
+
exports.greenFieldSchemaSetup = greenFieldSchemaSetup;
|
|
126
|
+
async function getSchemaMetadata(instanceId, databaseId, schema, options) {
|
|
127
|
+
const checkSchemaExists = await (0, connect_2.executeSqlCmdsAsIamUser)(options, instanceId, databaseId, [
|
|
128
|
+
`SELECT pg_get_userbyid(nspowner)
|
|
129
|
+
FROM pg_namespace
|
|
130
|
+
WHERE nspname = '${schema}';`,
|
|
131
|
+
], true);
|
|
132
|
+
if (!checkSchemaExists[0].rows[0]) {
|
|
133
|
+
return {
|
|
134
|
+
name: schema,
|
|
135
|
+
owner: null,
|
|
136
|
+
setupStatus: SchemaSetupStatus.NotFound,
|
|
137
|
+
tables: [],
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
const schemaOwner = checkSchemaExists[0].rows[0].pg_get_userbyid;
|
|
141
|
+
const cmd = `SELECT tablename, tableowner FROM pg_tables WHERE schemaname='${schema}'`;
|
|
142
|
+
const res = await (0, connect_2.executeSqlCmdsAsIamUser)(options, instanceId, databaseId, [cmd], true);
|
|
143
|
+
const tables = res[0].rows.map((row) => {
|
|
144
|
+
return {
|
|
145
|
+
name: row.tablename,
|
|
146
|
+
owner: row.tableowner,
|
|
147
|
+
};
|
|
148
|
+
});
|
|
149
|
+
const checkRoleExists = async (role) => {
|
|
150
|
+
const cmd = [`SELECT to_regrole('"${role}"') IS NOT NULL AS exists;`];
|
|
151
|
+
const result = await (0, connect_2.executeSqlCmdsAsIamUser)(options, instanceId, databaseId, cmd, true);
|
|
152
|
+
return result[0].rows[0].exists;
|
|
153
|
+
};
|
|
154
|
+
let setupStatus;
|
|
155
|
+
if (!(await checkRoleExists((0, permissions_1.firebasewriter)(databaseId, schema)))) {
|
|
156
|
+
setupStatus = SchemaSetupStatus.NotSetup;
|
|
157
|
+
}
|
|
158
|
+
else if (tables.every((table) => table.owner === (0, permissions_1.firebaseowner)(databaseId, schema)) &&
|
|
159
|
+
schemaOwner === (0, permissions_1.firebaseowner)(databaseId, schema)) {
|
|
160
|
+
setupStatus = SchemaSetupStatus.GreenField;
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
setupStatus = SchemaSetupStatus.BrownField;
|
|
164
|
+
}
|
|
165
|
+
return {
|
|
166
|
+
name: schema,
|
|
167
|
+
owner: schemaOwner,
|
|
168
|
+
setupStatus,
|
|
169
|
+
tables: tables,
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
exports.getSchemaMetadata = getSchemaMetadata;
|
|
173
|
+
async function setupBrownfieldAsGreenfield(instanceId, databaseId, schemaInfo, options, silent = false) {
|
|
174
|
+
const schema = schemaInfo.name;
|
|
175
|
+
await greenFieldSchemaSetup(instanceId, databaseId, schema, options, silent);
|
|
176
|
+
const firebaseOwnerRole = (0, permissions_1.firebaseowner)(databaseId, schema);
|
|
177
|
+
const nonFirebasetablesOwners = [...new Set(schemaInfo.tables.map((t) => t.owner))].filter((owner) => owner !== firebaseOwnerRole);
|
|
178
|
+
const grantCmds = nonFirebasetablesOwners.map((owner) => `GRANT "${(0, permissions_1.firebasewriter)(databaseId, schema)}" TO "${owner}"`);
|
|
179
|
+
const alterTableCmds = schemaInfo.tables.map((table) => `ALTER TABLE "${schema}"."${table.name}" OWNER TO "${firebaseOwnerRole}";`);
|
|
180
|
+
await (0, connect_2.executeSqlCmdsAsSuperUser)(options, instanceId, databaseId, [...grantCmds, ...alterTableCmds], silent);
|
|
181
|
+
}
|
|
182
|
+
exports.setupBrownfieldAsGreenfield = setupBrownfieldAsGreenfield;
|
|
183
|
+
async function brownfieldSqlSetup(instanceId, databaseId, schemaInfo, options, silent = false) {
|
|
184
|
+
const schema = schemaInfo.name;
|
|
185
|
+
const uniqueTablesOwners = [...new Set(schemaInfo.tables.map((t) => t.owner))];
|
|
186
|
+
const grantOwnersToFirebasesuperuser = uniqueTablesOwners.map((owner) => `GRANT ${owner} TO ${permissions_1.FIREBASE_SUPER_USER}`);
|
|
187
|
+
const iamUser = (await (0, connect_2.getIAMUser)(options)).user;
|
|
188
|
+
const projectNumber = await (0, projectUtils_1.needProjectNumber)(options);
|
|
189
|
+
const { user: fdcP4SAUser } = (0, connect_3.toDatabaseUser)((0, connect_3.getDataConnectP4SA)(projectNumber));
|
|
190
|
+
const firebaseDefaultPermissions = uniqueTablesOwners.flatMap((owner) => (0, permissions_1.defaultPermissions)(databaseId, schema, owner));
|
|
191
|
+
const brownfieldSetupCmds = [
|
|
192
|
+
...grantOwnersToFirebasesuperuser,
|
|
193
|
+
...(0, permissions_1.writerRolePermissions)(databaseId, permissions_1.FIREBASE_SUPER_USER, schema),
|
|
194
|
+
...(0, permissions_1.readerRolePermissions)(databaseId, permissions_1.FIREBASE_SUPER_USER, schema),
|
|
195
|
+
`GRANT "${(0, permissions_1.firebasewriter)(databaseId, schema)}" TO "${iamUser}"`,
|
|
196
|
+
`GRANT "${(0, permissions_1.firebasewriter)(databaseId, schema)}" TO "${fdcP4SAUser}"`,
|
|
197
|
+
...firebaseDefaultPermissions,
|
|
198
|
+
];
|
|
199
|
+
await (0, connect_2.executeSqlCmdsAsSuperUser)(options, instanceId, databaseId, brownfieldSetupCmds, silent);
|
|
200
|
+
}
|
|
201
|
+
exports.brownfieldSqlSetup = brownfieldSqlSetup;
|