orchid-orm 1.70.1 → 1.71.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/dist/index.d.ts +14 -6
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +6 -1
- package/dist/index.mjs.map +1 -1
- package/dist/migrations/index.js +691 -16
- package/dist/migrations/index.js.map +1 -1
- package/dist/migrations/index.mjs +691 -16
- package/dist/migrations/index.mjs.map +1 -1
- package/package.json +5 -5
package/dist/migrations/index.js
CHANGED
|
@@ -1336,28 +1336,42 @@ const dropCheck = ({ changeTableAst: { drop }, changingColumns }, dbCheck, name)
|
|
|
1336
1336
|
};
|
|
1337
1337
|
const defaultRlsState = {
|
|
1338
1338
|
enable: false,
|
|
1339
|
-
force:
|
|
1339
|
+
force: true
|
|
1340
1340
|
};
|
|
1341
1341
|
const normalizeRlsFlag = (value, defaultValue) => {
|
|
1342
1342
|
if (value === void 0) return defaultValue;
|
|
1343
1343
|
return value === true || value === "true" || value === "t";
|
|
1344
1344
|
};
|
|
1345
|
-
const processTableRls = (ast, dbStructure, tables, currentSchema) => {
|
|
1345
|
+
const processTableRls = async (adapter, ast, dbStructure, tables, currentSchema, generatorIgnore) => {
|
|
1346
1346
|
const projectRlsDefaults = tables[0]?.internal.rls?.tableRlsDefaults;
|
|
1347
|
+
const ignore = generatorIgnore;
|
|
1348
|
+
const ignoredTableSet = toIgnoredTableSet(ignore?.tables, currentSchema);
|
|
1349
|
+
const ignoredRlsTableSet = toIgnoredTableSet(ignore?.rls?.tables, currentSchema);
|
|
1350
|
+
const ignoredPolicyMap = toIgnoredPolicyMap(ignore?.rls?.policies, currentSchema);
|
|
1347
1351
|
for (const table of tables) {
|
|
1348
1352
|
const tableRls = table.internal.tableRls;
|
|
1349
1353
|
if (!tableRls) continue;
|
|
1350
1354
|
const schemaName = table.q.schema ?? currentSchema;
|
|
1355
|
+
const tableId = `${schemaName}.${table.table}`;
|
|
1356
|
+
if (ignoredTableSet.has(tableId) || ignoredRlsTableSet.has(tableId)) continue;
|
|
1351
1357
|
const dbTable = dbStructure.tables.find((item) => item.schemaName === schemaName && item.name === table.table);
|
|
1352
|
-
|
|
1358
|
+
const ignoredPolicyNames = ignoredPolicyMap.get(tableId);
|
|
1353
1359
|
const codeRls = {
|
|
1354
1360
|
enable: normalizeRlsFlag(tableRls.enable ?? projectRlsDefaults?.enable, defaultRlsState.enable),
|
|
1355
1361
|
force: normalizeRlsFlag(tableRls.force ?? projectRlsDefaults?.force, defaultRlsState.force)
|
|
1356
1362
|
};
|
|
1357
1363
|
const dbRls = {
|
|
1358
|
-
enable: normalizeRlsFlag(dbTable
|
|
1359
|
-
force: normalizeRlsFlag(dbTable
|
|
1364
|
+
enable: normalizeRlsFlag(dbTable?.rls?.enable, defaultRlsState.enable),
|
|
1365
|
+
force: normalizeRlsFlag(dbTable?.rls?.force, false)
|
|
1360
1366
|
};
|
|
1367
|
+
const policyExpressionComparisons = [];
|
|
1368
|
+
const policyChanges = collectPolicyChanges(schemaName, table.table, normalizeCodePolicies(tableRls, ignoredPolicyNames), normalizeDbPolicies(dbTable?.rls?.policies, ignoredPolicyNames), policyExpressionComparisons, (0, rake_db.concatSchemaAndName)({
|
|
1369
|
+
schema: schemaName,
|
|
1370
|
+
name: table.table
|
|
1371
|
+
}));
|
|
1372
|
+
if (policyExpressionComparisons.length) await compareSqlExpressions(policyExpressionComparisons, adapter);
|
|
1373
|
+
const disableFirst = !codeRls.enable && dbRls.enable;
|
|
1374
|
+
if (!disableFirst) pushPolicyChanges(ast, policyChanges);
|
|
1361
1375
|
if (codeRls.enable !== dbRls.enable) ast.push({
|
|
1362
1376
|
type: "tableRls",
|
|
1363
1377
|
action: codeRls.enable ? "enable" : "disable",
|
|
@@ -1370,7 +1384,250 @@ const processTableRls = (ast, dbStructure, tables, currentSchema) => {
|
|
|
1370
1384
|
schema: schemaName,
|
|
1371
1385
|
table: table.table
|
|
1372
1386
|
});
|
|
1387
|
+
if (disableFirst) pushPolicyChanges(ast, policyChanges);
|
|
1388
|
+
}
|
|
1389
|
+
};
|
|
1390
|
+
const toIgnoredTableSet = (tables, currentSchema) => {
|
|
1391
|
+
const ignored = /* @__PURE__ */ new Set();
|
|
1392
|
+
if (!tables) return ignored;
|
|
1393
|
+
for (const name of tables) {
|
|
1394
|
+
const [schema = currentSchema, table] = (0, rake_db.getSchemaAndTableFromName)(currentSchema, name);
|
|
1395
|
+
ignored.add(`${schema}.${table}`);
|
|
1396
|
+
}
|
|
1397
|
+
return ignored;
|
|
1398
|
+
};
|
|
1399
|
+
const toIgnoredPolicyMap = (items, currentSchema) => {
|
|
1400
|
+
const result = /* @__PURE__ */ new Map();
|
|
1401
|
+
if (!items) return result;
|
|
1402
|
+
for (const item of items) {
|
|
1403
|
+
const [schema = currentSchema, table] = (0, rake_db.getSchemaAndTableFromName)(currentSchema, item.table);
|
|
1404
|
+
const key = `${schema}.${table}`;
|
|
1405
|
+
let set = result.get(key);
|
|
1406
|
+
if (!set) {
|
|
1407
|
+
set = /* @__PURE__ */ new Set();
|
|
1408
|
+
result.set(key, set);
|
|
1409
|
+
}
|
|
1410
|
+
for (const name of item.names) set.add(name);
|
|
1411
|
+
}
|
|
1412
|
+
return result;
|
|
1413
|
+
};
|
|
1414
|
+
const normalizeCodePolicies = (tableRls, ignoredPolicyNames) => {
|
|
1415
|
+
const result = [];
|
|
1416
|
+
const permit = tableRls.permit ?? [];
|
|
1417
|
+
const restrict = tableRls.restrict ?? [];
|
|
1418
|
+
for (const policy of permit) {
|
|
1419
|
+
const normalized = normalizeCodePolicy("PERMISSIVE", policy);
|
|
1420
|
+
if (normalized && !ignoredPolicyNames?.has(normalized.name)) result.push(normalized);
|
|
1421
|
+
}
|
|
1422
|
+
for (const policy of restrict) {
|
|
1423
|
+
const normalized = normalizeCodePolicy("RESTRICTIVE", policy);
|
|
1424
|
+
if (normalized && !ignoredPolicyNames?.has(normalized.name)) result.push(normalized);
|
|
1373
1425
|
}
|
|
1426
|
+
return result;
|
|
1427
|
+
};
|
|
1428
|
+
const normalizeCodePolicy = (as, policy) => {
|
|
1429
|
+
if (!policy?.name) return;
|
|
1430
|
+
return {
|
|
1431
|
+
name: policy.name,
|
|
1432
|
+
as,
|
|
1433
|
+
for: policy.for ?? "ALL",
|
|
1434
|
+
to: normalizePolicyRoles(policy.to),
|
|
1435
|
+
using: toSqlText(policy.using),
|
|
1436
|
+
withCheck: toSqlText(policy.withCheck)
|
|
1437
|
+
};
|
|
1438
|
+
};
|
|
1439
|
+
const normalizeDbPolicies = (policies, ignoredPolicyNames) => {
|
|
1440
|
+
if (!policies) return [];
|
|
1441
|
+
const result = [];
|
|
1442
|
+
for (const policy of policies) {
|
|
1443
|
+
if (ignoredPolicyNames?.has(policy.name)) continue;
|
|
1444
|
+
result.push({
|
|
1445
|
+
name: policy.name,
|
|
1446
|
+
as: policy.mode,
|
|
1447
|
+
for: policy.command,
|
|
1448
|
+
to: normalizePolicyRoles(policy.roles),
|
|
1449
|
+
using: policy.using,
|
|
1450
|
+
withCheck: policy.withCheck
|
|
1451
|
+
});
|
|
1452
|
+
}
|
|
1453
|
+
return result;
|
|
1454
|
+
};
|
|
1455
|
+
const normalizePolicyRoles = (roles) => {
|
|
1456
|
+
const result = (Array.isArray(roles) ? roles : roles ? [roles] : ["public"]).map((role) => role.toLowerCase() === "public" ? "public" : role);
|
|
1457
|
+
return result.length ? result : ["public"];
|
|
1458
|
+
};
|
|
1459
|
+
const toSqlText = (value) => {
|
|
1460
|
+
if (!value) return;
|
|
1461
|
+
return value.toSQL({ values: [] });
|
|
1462
|
+
};
|
|
1463
|
+
const collectPolicyChanges = (schema, table, codePolicies, dbPolicies, compareExpressions, source) => {
|
|
1464
|
+
const changes = [];
|
|
1465
|
+
const dbPolicyMap = /* @__PURE__ */ new Map();
|
|
1466
|
+
for (const policy of dbPolicies) dbPolicyMap.set(policy.name, policy);
|
|
1467
|
+
const codePolicyMap = /* @__PURE__ */ new Map();
|
|
1468
|
+
for (const policy of codePolicies) codePolicyMap.set(policy.name, policy);
|
|
1469
|
+
const unmatchedCodePolicies = [];
|
|
1470
|
+
const unmatchedDbPolicies = [];
|
|
1471
|
+
for (const policy of codePolicies) {
|
|
1472
|
+
const dbPolicy = dbPolicyMap.get(policy.name);
|
|
1473
|
+
if (!dbPolicy) {
|
|
1474
|
+
unmatchedCodePolicies.push(policy);
|
|
1475
|
+
continue;
|
|
1476
|
+
}
|
|
1477
|
+
const recreate = policy.as !== dbPolicy.as || policy.for !== dbPolicy.for;
|
|
1478
|
+
const toChanged = !isSameStringArray(policy.to, dbPolicy.to);
|
|
1479
|
+
const from = {};
|
|
1480
|
+
const to = {};
|
|
1481
|
+
const codeUsing = policy.using;
|
|
1482
|
+
const dbUsing = dbPolicy.using;
|
|
1483
|
+
const codeWithCheck = policy.withCheck;
|
|
1484
|
+
const dbWithCheck = dbPolicy.withCheck;
|
|
1485
|
+
const hasUsing = codeUsing !== void 0 || dbUsing !== void 0;
|
|
1486
|
+
const hasWithCheck = codeWithCheck !== void 0 || dbWithCheck !== void 0;
|
|
1487
|
+
if (toChanged) {
|
|
1488
|
+
from.to = dbPolicy.to;
|
|
1489
|
+
to.to = policy.to;
|
|
1490
|
+
}
|
|
1491
|
+
const usingCanCompare = codeUsing !== void 0 && dbUsing !== void 0;
|
|
1492
|
+
const withCheckCanCompare = codeWithCheck !== void 0 && dbWithCheck !== void 0;
|
|
1493
|
+
const applyExpressionDiff = (matched) => {
|
|
1494
|
+
const usingChanged = usingCanCompare ? !matched : hasUsing && !usingCanCompare;
|
|
1495
|
+
const withCheckChanged = withCheckCanCompare ? !matched : hasWithCheck && !withCheckCanCompare;
|
|
1496
|
+
if (usingChanged) {
|
|
1497
|
+
from.using = dbPolicy.using;
|
|
1498
|
+
to.using = policy.using;
|
|
1499
|
+
}
|
|
1500
|
+
if (withCheckChanged) {
|
|
1501
|
+
from.withCheck = dbPolicy.withCheck;
|
|
1502
|
+
to.withCheck = policy.withCheck;
|
|
1503
|
+
}
|
|
1504
|
+
if (recreate) {
|
|
1505
|
+
changes.push({
|
|
1506
|
+
type: "changePolicy",
|
|
1507
|
+
schema,
|
|
1508
|
+
table,
|
|
1509
|
+
name: policy.name,
|
|
1510
|
+
from: {
|
|
1511
|
+
as: dbPolicy.as,
|
|
1512
|
+
for: dbPolicy.for,
|
|
1513
|
+
to: dbPolicy.to,
|
|
1514
|
+
using: dbPolicy.using,
|
|
1515
|
+
withCheck: dbPolicy.withCheck
|
|
1516
|
+
},
|
|
1517
|
+
to: {
|
|
1518
|
+
as: policy.as,
|
|
1519
|
+
for: policy.for,
|
|
1520
|
+
to: policy.to,
|
|
1521
|
+
using: policy.using,
|
|
1522
|
+
withCheck: policy.withCheck
|
|
1523
|
+
}
|
|
1524
|
+
});
|
|
1525
|
+
return;
|
|
1526
|
+
}
|
|
1527
|
+
if (!Object.keys(from).length) return;
|
|
1528
|
+
changes.push({
|
|
1529
|
+
type: "changePolicy",
|
|
1530
|
+
schema,
|
|
1531
|
+
table,
|
|
1532
|
+
name: policy.name,
|
|
1533
|
+
from,
|
|
1534
|
+
to
|
|
1535
|
+
});
|
|
1536
|
+
};
|
|
1537
|
+
if (!hasUsing && !hasWithCheck) {
|
|
1538
|
+
applyExpressionDiff(true);
|
|
1539
|
+
continue;
|
|
1540
|
+
}
|
|
1541
|
+
const compare = [];
|
|
1542
|
+
if (usingCanCompare) compare.push({
|
|
1543
|
+
inDb: dbUsing,
|
|
1544
|
+
inCode: [codeUsing]
|
|
1545
|
+
});
|
|
1546
|
+
if (withCheckCanCompare) compare.push({
|
|
1547
|
+
inDb: dbWithCheck,
|
|
1548
|
+
inCode: [codeWithCheck]
|
|
1549
|
+
});
|
|
1550
|
+
if (!compare.length) {
|
|
1551
|
+
applyExpressionDiff(false);
|
|
1552
|
+
continue;
|
|
1553
|
+
}
|
|
1554
|
+
compareExpressions.push({
|
|
1555
|
+
source,
|
|
1556
|
+
compare,
|
|
1557
|
+
handle(i) {
|
|
1558
|
+
applyExpressionDiff(i !== void 0);
|
|
1559
|
+
}
|
|
1560
|
+
});
|
|
1561
|
+
}
|
|
1562
|
+
for (const policy of dbPolicies) {
|
|
1563
|
+
if (codePolicyMap.has(policy.name)) continue;
|
|
1564
|
+
unmatchedDbPolicies.push(policy);
|
|
1565
|
+
}
|
|
1566
|
+
const pairedDbPolicyNames = /* @__PURE__ */ new Set();
|
|
1567
|
+
const pairedCodePolicyNames = /* @__PURE__ */ new Set();
|
|
1568
|
+
for (const policy of unmatchedCodePolicies) {
|
|
1569
|
+
const renamedFrom = unmatchedDbPolicies.find((item) => !pairedDbPolicyNames.has(item.name) && item.as === policy.as && item.for === policy.for);
|
|
1570
|
+
if (!renamedFrom) continue;
|
|
1571
|
+
pairedDbPolicyNames.add(renamedFrom.name);
|
|
1572
|
+
pairedCodePolicyNames.add(policy.name);
|
|
1573
|
+
changes.push({
|
|
1574
|
+
type: "changePolicy",
|
|
1575
|
+
schema,
|
|
1576
|
+
table,
|
|
1577
|
+
name: renamedFrom.name,
|
|
1578
|
+
from: {
|
|
1579
|
+
name: renamedFrom.name,
|
|
1580
|
+
to: renamedFrom.to,
|
|
1581
|
+
using: renamedFrom.using,
|
|
1582
|
+
withCheck: renamedFrom.withCheck
|
|
1583
|
+
},
|
|
1584
|
+
to: {
|
|
1585
|
+
name: policy.name,
|
|
1586
|
+
to: policy.to,
|
|
1587
|
+
using: policy.using,
|
|
1588
|
+
withCheck: policy.withCheck
|
|
1589
|
+
}
|
|
1590
|
+
});
|
|
1591
|
+
}
|
|
1592
|
+
for (const policy of unmatchedCodePolicies) {
|
|
1593
|
+
if (pairedCodePolicyNames.has(policy.name)) continue;
|
|
1594
|
+
changes.push({
|
|
1595
|
+
type: "policy",
|
|
1596
|
+
action: "create",
|
|
1597
|
+
schema,
|
|
1598
|
+
table,
|
|
1599
|
+
name: policy.name,
|
|
1600
|
+
as: policy.as,
|
|
1601
|
+
for: policy.for,
|
|
1602
|
+
to: policy.to,
|
|
1603
|
+
using: policy.using,
|
|
1604
|
+
withCheck: policy.withCheck
|
|
1605
|
+
});
|
|
1606
|
+
}
|
|
1607
|
+
for (const policy of unmatchedDbPolicies) {
|
|
1608
|
+
if (pairedDbPolicyNames.has(policy.name)) continue;
|
|
1609
|
+
changes.push({
|
|
1610
|
+
type: "policy",
|
|
1611
|
+
action: "drop",
|
|
1612
|
+
schema,
|
|
1613
|
+
table,
|
|
1614
|
+
name: policy.name,
|
|
1615
|
+
as: policy.as,
|
|
1616
|
+
for: policy.for,
|
|
1617
|
+
to: policy.to,
|
|
1618
|
+
using: policy.using,
|
|
1619
|
+
withCheck: policy.withCheck
|
|
1620
|
+
});
|
|
1621
|
+
}
|
|
1622
|
+
return changes;
|
|
1623
|
+
};
|
|
1624
|
+
const pushPolicyChanges = (ast, changes) => {
|
|
1625
|
+
for (const change of changes) ast.push(change);
|
|
1626
|
+
};
|
|
1627
|
+
const isSameStringArray = (a, b) => {
|
|
1628
|
+
if (a.length !== b.length) return false;
|
|
1629
|
+
for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false;
|
|
1630
|
+
return true;
|
|
1374
1631
|
};
|
|
1375
1632
|
const processTables = async (ast, domainsMap, adapter, dbStructure, config, { structureToAstCtx, codeItems: { tables }, currentSchema, internal: { generatorIgnore }, verifying }, pendingDbTypes) => {
|
|
1376
1633
|
const createTables = collectCreateTables(tables, dbStructure, currentSchema);
|
|
@@ -1385,7 +1642,7 @@ const processTables = async (ast, domainsMap, adapter, dbStructure, config, { st
|
|
|
1385
1642
|
await applyChangeTables(adapter, changeTables, structureToAstCtx, dbStructure, domainsMap, ast, currentSchema, config, compareSql, tableExpressions, verifying, pendingDbTypes);
|
|
1386
1643
|
processForeignKeys(config, ast, changeTables, currentSchema, tableShapes);
|
|
1387
1644
|
await Promise.all([applyCompareSql(compareSql, adapter), compareSqlExpressions(tableExpressions, adapter)]);
|
|
1388
|
-
processTableRls(ast, dbStructure, tables, currentSchema);
|
|
1645
|
+
await processTableRls(adapter, ast, dbStructure, tables, currentSchema, generatorIgnore);
|
|
1389
1646
|
for (const dbTable of dropTables) ast.push((0, rake_db.tableToAst)(structureToAstCtx, dbStructure, dbTable, "drop", domainsMap));
|
|
1390
1647
|
};
|
|
1391
1648
|
const collectCreateTables = (tables, dbStructure, currentSchema) => {
|
|
@@ -1745,7 +2002,7 @@ const privilegeConfigsMatch = (a, b, objectType, objectTypeToAllPrivileges) => {
|
|
|
1745
2002
|
if (b.privilege === "ALL" && a.isGrantable === b.isGrantable) return expectedPrivs.includes(a.privilege);
|
|
1746
2003
|
return false;
|
|
1747
2004
|
};
|
|
1748
|
-
const normalizeRoleName = (name) => {
|
|
2005
|
+
const normalizeRoleName$1 = (name) => {
|
|
1749
2006
|
if (name.startsWith("\"") && name.endsWith("\"")) return name.slice(1, -1);
|
|
1750
2007
|
return name;
|
|
1751
2008
|
};
|
|
@@ -1815,7 +2072,7 @@ const processDefaultPrivileges = (ast, dbStructure, { internal: { roles } }) =>
|
|
|
1815
2072
|
}
|
|
1816
2073
|
const found = /* @__PURE__ */ new Set();
|
|
1817
2074
|
for (const dbPrivilege of dbStructure.defaultPrivileges) {
|
|
1818
|
-
const grantee = normalizeRoleName(dbPrivilege.grantee);
|
|
2075
|
+
const grantee = normalizeRoleName$1(dbPrivilege.grantee);
|
|
1819
2076
|
if (grantee === "postgres") continue;
|
|
1820
2077
|
const key = `${grantee}.${dbPrivilege.schema}`;
|
|
1821
2078
|
const codePrivilege = codePrivileges.get(key);
|
|
@@ -1891,6 +2148,280 @@ const processDefaultPrivileges = (ast, dbStructure, { internal: { roles } }) =>
|
|
|
1891
2148
|
});
|
|
1892
2149
|
}
|
|
1893
2150
|
};
|
|
2151
|
+
const targetKeys = [
|
|
2152
|
+
"schemas",
|
|
2153
|
+
"tables",
|
|
2154
|
+
"sequences",
|
|
2155
|
+
"routines",
|
|
2156
|
+
"types",
|
|
2157
|
+
"domains",
|
|
2158
|
+
"databases"
|
|
2159
|
+
];
|
|
2160
|
+
const schemaWideTargetKeys = [
|
|
2161
|
+
"allTablesIn",
|
|
2162
|
+
"allSequencesIn",
|
|
2163
|
+
"allRoutinesIn"
|
|
2164
|
+
];
|
|
2165
|
+
const supportedPrivileges = {
|
|
2166
|
+
schemas: ["USAGE", "CREATE"],
|
|
2167
|
+
tables: [
|
|
2168
|
+
"SELECT",
|
|
2169
|
+
"INSERT",
|
|
2170
|
+
"UPDATE",
|
|
2171
|
+
"DELETE",
|
|
2172
|
+
"TRUNCATE",
|
|
2173
|
+
"REFERENCES",
|
|
2174
|
+
"TRIGGER",
|
|
2175
|
+
"MAINTAIN"
|
|
2176
|
+
],
|
|
2177
|
+
sequences: [
|
|
2178
|
+
"USAGE",
|
|
2179
|
+
"SELECT",
|
|
2180
|
+
"UPDATE"
|
|
2181
|
+
],
|
|
2182
|
+
routines: ["EXECUTE"],
|
|
2183
|
+
types: ["USAGE"],
|
|
2184
|
+
domains: ["USAGE"],
|
|
2185
|
+
databases: [
|
|
2186
|
+
"CREATE",
|
|
2187
|
+
"CONNECT",
|
|
2188
|
+
"TEMPORARY"
|
|
2189
|
+
]
|
|
2190
|
+
};
|
|
2191
|
+
const schemaWideToConcreteTarget = {
|
|
2192
|
+
allTablesIn: "tables",
|
|
2193
|
+
allSequencesIn: "sequences",
|
|
2194
|
+
allRoutinesIn: "routines"
|
|
2195
|
+
};
|
|
2196
|
+
const processGrants = (ast, dbStructure, params) => {
|
|
2197
|
+
const { grants } = params.internal;
|
|
2198
|
+
if (!grants || !dbStructure.grants) return;
|
|
2199
|
+
const codeStates = collectCodeGrants(dbStructure, params).filter((grant) => !isIgnoredGrant(grant, params));
|
|
2200
|
+
const dbStates = collectDbGrants(dbStructure, params.currentSchema).filter((grant) => !isIgnoredGrant(grant, params));
|
|
2201
|
+
const managedTargets = getManagedGrantTargets(params);
|
|
2202
|
+
for (const code of codeStates) {
|
|
2203
|
+
const actual = dbStates.filter((db) => isSameGrantTarget(code, db));
|
|
2204
|
+
addGrantAst(ast, "grant", code, missingPrivileges(code.grantablePrivileges, actual, "grantablePrivileges"), "grantablePrivileges");
|
|
2205
|
+
addGrantAst(ast, "revoke", code, grantOptionsToRevoke(code.privileges, code.grantablePrivileges, actual), "grantablePrivileges");
|
|
2206
|
+
addGrantAst(ast, "grant", code, missingPrivileges(code.privileges, actual, "privileges"), "privileges");
|
|
2207
|
+
}
|
|
2208
|
+
for (const actual of dbStates) {
|
|
2209
|
+
const configured = codeStates.filter((code) => isSameGrantTarget(code, actual));
|
|
2210
|
+
if (!shouldRevokeActualGrant(actual, configured, managedTargets)) continue;
|
|
2211
|
+
const revokeGrant = getRevokeGrantState(actual, configured);
|
|
2212
|
+
addGrantAst(ast, "revoke", revokeGrant, privilegesToRevoke(actual.privileges, configured), "privileges");
|
|
2213
|
+
addGrantAst(ast, "revoke", revokeGrant, privilegesToRevoke(actual.grantablePrivileges, configured), "grantablePrivileges");
|
|
2214
|
+
}
|
|
2215
|
+
};
|
|
2216
|
+
const getManagedGrantTargets = ({ codeItems, currentSchema }) => {
|
|
2217
|
+
const targets = {
|
|
2218
|
+
tables: /* @__PURE__ */ new Set(),
|
|
2219
|
+
domains: /* @__PURE__ */ new Set(),
|
|
2220
|
+
types: /* @__PURE__ */ new Set()
|
|
2221
|
+
};
|
|
2222
|
+
for (const table of codeItems.tables) targets.tables.add(`${table.q.schema ?? currentSchema}.${table.table}`);
|
|
2223
|
+
for (const domain of codeItems.domains) targets.domains.add(`${domain.schemaName}.${domain.name}`);
|
|
2224
|
+
for (const enumItem of codeItems.enums.values()) targets.types.add(`${enumItem.schema ?? currentSchema}.${enumItem.name}`);
|
|
2225
|
+
return targets;
|
|
2226
|
+
};
|
|
2227
|
+
const shouldRevokeActualGrant = (actual, configured, managedTargets) => {
|
|
2228
|
+
if (configured.length) return true;
|
|
2229
|
+
if (actual.targetKey === "tables") return managedTargets.tables.has(actual.target);
|
|
2230
|
+
if (actual.targetKey === "domains") return managedTargets.domains.has(actual.target);
|
|
2231
|
+
if (actual.targetKey === "types") return managedTargets.types.has(actual.target);
|
|
2232
|
+
return false;
|
|
2233
|
+
};
|
|
2234
|
+
const getRevokeGrantState = (actual, configured) => {
|
|
2235
|
+
if (configured.some((grant) => grant.grantedBy)) return actual;
|
|
2236
|
+
return {
|
|
2237
|
+
...actual,
|
|
2238
|
+
grantedBy: void 0
|
|
2239
|
+
};
|
|
2240
|
+
};
|
|
2241
|
+
const collectCodeGrants = (dbStructure, { currentSchema, internal }) => {
|
|
2242
|
+
const states = [];
|
|
2243
|
+
for (const grant of internal.grants ?? []) {
|
|
2244
|
+
for (const targetKey of targetKeys) {
|
|
2245
|
+
const values = grant[targetKey];
|
|
2246
|
+
if (!values?.length) continue;
|
|
2247
|
+
addStates(states, withEffectiveGrantor(grant, internal), targetKey, values, currentSchema);
|
|
2248
|
+
}
|
|
2249
|
+
for (const targetKey of schemaWideTargetKeys) {
|
|
2250
|
+
const values = grant[targetKey];
|
|
2251
|
+
if (!values?.length) continue;
|
|
2252
|
+
const concreteTargetKey = schemaWideToConcreteTarget[targetKey];
|
|
2253
|
+
addStates(states, withEffectiveGrantor(grant, internal), concreteTargetKey, getSchemaWideTargets(dbStructure, concreteTargetKey, values), currentSchema);
|
|
2254
|
+
}
|
|
2255
|
+
}
|
|
2256
|
+
return states;
|
|
2257
|
+
};
|
|
2258
|
+
const collectDbGrants = (dbStructure, currentSchema) => {
|
|
2259
|
+
const states = [];
|
|
2260
|
+
for (const grant of dbStructure.grants ?? []) for (const targetKey of targetKeys) {
|
|
2261
|
+
const values = grant[targetKey];
|
|
2262
|
+
if (!values?.length) continue;
|
|
2263
|
+
addStates(states, grant, targetKey, values, currentSchema);
|
|
2264
|
+
}
|
|
2265
|
+
return states;
|
|
2266
|
+
};
|
|
2267
|
+
const withEffectiveGrantor = (grant, internal) => {
|
|
2268
|
+
return {
|
|
2269
|
+
...grant,
|
|
2270
|
+
grantedBy: grant.grantedBy ?? internal.defaultGrantedBy
|
|
2271
|
+
};
|
|
2272
|
+
};
|
|
2273
|
+
const isIgnoredGrant = (grant, { currentSchema, internal: { generatorIgnore } }) => {
|
|
2274
|
+
const { grants } = generatorIgnore ?? {};
|
|
2275
|
+
if (matchesSelector(grants?.roles, grant.to)) return true;
|
|
2276
|
+
const names = getGrantTargetNames(grant, currentSchema);
|
|
2277
|
+
if (matchesSelector(grants?.[grant.targetKey], names)) return true;
|
|
2278
|
+
if (grant.targetKey === "tables" && names.schema && matchesSelector(grants?.allTablesIn, names.schema)) return true;
|
|
2279
|
+
if (grant.targetKey === "sequences" && names.schema && matchesSelector(grants?.allSequencesIn, names.schema)) return true;
|
|
2280
|
+
if (grant.targetKey === "routines" && names.schema && matchesSelector(grants?.allRoutinesIn, names.schema)) return true;
|
|
2281
|
+
return isTopLevelIgnoredGrant(grant, names, generatorIgnore);
|
|
2282
|
+
};
|
|
2283
|
+
const isTopLevelIgnoredGrant = (grant, names, generatorIgnore) => {
|
|
2284
|
+
if (grant.targetKey === "schemas") return !!generatorIgnore?.schemas?.includes(grant.target);
|
|
2285
|
+
if (names.schema && generatorIgnore?.schemas?.includes(names.schema)) return true;
|
|
2286
|
+
if (grant.targetKey === "tables") return isIgnoredByName(generatorIgnore?.tables, names);
|
|
2287
|
+
if (grant.targetKey === "domains") return isIgnoredByName(generatorIgnore?.domains, names);
|
|
2288
|
+
return false;
|
|
2289
|
+
};
|
|
2290
|
+
const getGrantTargetNames = (grant, currentSchema) => {
|
|
2291
|
+
if (grant.targetKey === "schemas" || grant.targetKey === "databases") return {
|
|
2292
|
+
name: grant.target,
|
|
2293
|
+
qualified: grant.target
|
|
2294
|
+
};
|
|
2295
|
+
const [schema, name] = (0, rake_db.getSchemaAndTableFromName)(currentSchema, grant.target);
|
|
2296
|
+
return {
|
|
2297
|
+
schema,
|
|
2298
|
+
name,
|
|
2299
|
+
qualified: schema ? `${schema}.${name}` : name
|
|
2300
|
+
};
|
|
2301
|
+
};
|
|
2302
|
+
const isIgnoredByName = (ignored, names) => {
|
|
2303
|
+
return !!ignored?.some((name) => name === names.qualified || name === names.name || (names.schema ? name === `${names.schema}.${names.name}` : false));
|
|
2304
|
+
};
|
|
2305
|
+
const matchesSelector = (selector, value) => {
|
|
2306
|
+
if (!selector) return false;
|
|
2307
|
+
const values = typeof value === "string" ? [value] : [
|
|
2308
|
+
value.qualified,
|
|
2309
|
+
value.name,
|
|
2310
|
+
value.schema
|
|
2311
|
+
].filter(isString);
|
|
2312
|
+
return (Array.isArray(selector) ? selector : [selector]).some((item) => values.some((name) => typeof item === "string" ? item === name : item.test(name)));
|
|
2313
|
+
};
|
|
2314
|
+
const isString = (value) => {
|
|
2315
|
+
return !!value;
|
|
2316
|
+
};
|
|
2317
|
+
const addStates = (states, grant, targetKey, values, currentSchema) => {
|
|
2318
|
+
for (const to of grant.to) for (const value of values) {
|
|
2319
|
+
const target = typeof value === "string" ? normalizeTarget(targetKey, value, currentSchema) : value.target;
|
|
2320
|
+
addOrMergeState(states, {
|
|
2321
|
+
targetKey,
|
|
2322
|
+
target,
|
|
2323
|
+
outputTarget: typeof value === "string" ? normalizeOutputTarget(target, currentSchema) : value.outputTarget,
|
|
2324
|
+
to: normalizeRoleName(to),
|
|
2325
|
+
grantedBy: grant.grantedBy ? normalizeRoleName(grant.grantedBy) : void 0,
|
|
2326
|
+
privileges: expandPrivileges(targetKey, grant.privileges),
|
|
2327
|
+
grantablePrivileges: expandPrivileges(targetKey, grant.grantablePrivileges)
|
|
2328
|
+
});
|
|
2329
|
+
}
|
|
2330
|
+
};
|
|
2331
|
+
const addOrMergeState = (states, state) => {
|
|
2332
|
+
const existing = states.find((item) => isSameExactGrantTarget(item, state));
|
|
2333
|
+
if (!existing) {
|
|
2334
|
+
removeGrantableFromOrdinary(state);
|
|
2335
|
+
states.push(state);
|
|
2336
|
+
return;
|
|
2337
|
+
}
|
|
2338
|
+
for (const privilege of state.privileges) if (!existing.grantablePrivileges.has(privilege)) existing.privileges.add(privilege);
|
|
2339
|
+
for (const privilege of state.grantablePrivileges) {
|
|
2340
|
+
existing.grantablePrivileges.add(privilege);
|
|
2341
|
+
existing.privileges.delete(privilege);
|
|
2342
|
+
}
|
|
2343
|
+
};
|
|
2344
|
+
const removeGrantableFromOrdinary = (state) => {
|
|
2345
|
+
for (const privilege of state.grantablePrivileges) state.privileges.delete(privilege);
|
|
2346
|
+
};
|
|
2347
|
+
const normalizeRoleName = (name) => {
|
|
2348
|
+
return name.startsWith("\"") && name.endsWith("\"") ? name.slice(1, -1) : name;
|
|
2349
|
+
};
|
|
2350
|
+
const getSchemaWideTargets = (dbStructure, targetKey, schemas) => {
|
|
2351
|
+
const selectedSchemas = new Set(schemas);
|
|
2352
|
+
if (targetKey === "tables") return [...dbStructure.tables.map((table) => ({
|
|
2353
|
+
target: `${table.schemaName}.${table.name}`,
|
|
2354
|
+
outputTarget: `${table.schemaName}.${table.name}`
|
|
2355
|
+
})), ...dbStructure.views.map((view) => ({
|
|
2356
|
+
target: `${view.schemaName}.${view.name}`,
|
|
2357
|
+
outputTarget: `${view.schemaName}.${view.name}`
|
|
2358
|
+
}))].filter((item) => selectedSchemas.has(item.target.split(".")[0]));
|
|
2359
|
+
if (targetKey === "sequences" || targetKey === "routines") return getSchemaWideGrantTargets(dbStructure, targetKey, selectedSchemas);
|
|
2360
|
+
return [];
|
|
2361
|
+
};
|
|
2362
|
+
const getSchemaWideGrantTargets = (dbStructure, targetKey, selectedSchemas) => {
|
|
2363
|
+
const targets = /* @__PURE__ */ new Map();
|
|
2364
|
+
for (const grant of dbStructure.grants ?? []) for (const target of grant[targetKey] ?? []) {
|
|
2365
|
+
const [schema] = target.split(".");
|
|
2366
|
+
if (!selectedSchemas.has(schema)) continue;
|
|
2367
|
+
targets.set(target, {
|
|
2368
|
+
target,
|
|
2369
|
+
outputTarget: target
|
|
2370
|
+
});
|
|
2371
|
+
}
|
|
2372
|
+
return [...targets.values()];
|
|
2373
|
+
};
|
|
2374
|
+
const normalizeTarget = (targetKey, value, currentSchema) => {
|
|
2375
|
+
if (targetKey === "schemas" || targetKey === "databases") return value;
|
|
2376
|
+
const [schema, name] = (0, rake_db.getSchemaAndTableFromName)(currentSchema, value);
|
|
2377
|
+
return `${schema ?? currentSchema}.${name}`;
|
|
2378
|
+
};
|
|
2379
|
+
const normalizeOutputTarget = (target, currentSchema) => {
|
|
2380
|
+
const [schema, name] = target.split(".");
|
|
2381
|
+
if (!name) return target;
|
|
2382
|
+
return schema === currentSchema ? name : target;
|
|
2383
|
+
};
|
|
2384
|
+
const expandPrivileges = (targetKey, privileges) => {
|
|
2385
|
+
const set = /* @__PURE__ */ new Set();
|
|
2386
|
+
for (const privilege of privileges ?? []) if (privilege === "ALL") for (const supported of supportedPrivileges[targetKey]) set.add(supported);
|
|
2387
|
+
else set.add(privilege === "TEMP" ? "TEMPORARY" : privilege);
|
|
2388
|
+
return set;
|
|
2389
|
+
};
|
|
2390
|
+
const isSameGrantTarget = (a, b) => {
|
|
2391
|
+
return a.targetKey === b.targetKey && a.target === b.target && a.to === b.to && (!a.grantedBy || !b.grantedBy || a.grantedBy === b.grantedBy);
|
|
2392
|
+
};
|
|
2393
|
+
const isSameExactGrantTarget = (a, b) => {
|
|
2394
|
+
return a.targetKey === b.targetKey && a.target === b.target && a.to === b.to && a.grantedBy === b.grantedBy;
|
|
2395
|
+
};
|
|
2396
|
+
const missingPrivileges = (expected, actual, key) => {
|
|
2397
|
+
const result = [];
|
|
2398
|
+
for (const privilege of expected) if (!actual.some((state) => state[key].has(privilege))) result.push(privilege);
|
|
2399
|
+
return result;
|
|
2400
|
+
};
|
|
2401
|
+
const grantOptionsToRevoke = (expectedOrdinary, expectedGrantable, actual) => {
|
|
2402
|
+
const result = [];
|
|
2403
|
+
for (const privilege of expectedOrdinary) {
|
|
2404
|
+
if (expectedGrantable.has(privilege)) continue;
|
|
2405
|
+
if (actual.some((state) => state.grantablePrivileges.has(privilege))) result.push(privilege);
|
|
2406
|
+
}
|
|
2407
|
+
return result;
|
|
2408
|
+
};
|
|
2409
|
+
const privilegesToRevoke = (actual, configured) => {
|
|
2410
|
+
const result = [];
|
|
2411
|
+
for (const privilege of actual) if (!configured.some((state) => state.privileges.has(privilege) || state.grantablePrivileges.has(privilege))) result.push(privilege);
|
|
2412
|
+
return result;
|
|
2413
|
+
};
|
|
2414
|
+
const addGrantAst = (ast, action, grant, privileges, privilegeKey) => {
|
|
2415
|
+
if (!privileges.length) return;
|
|
2416
|
+
ast.push({
|
|
2417
|
+
type: "grant",
|
|
2418
|
+
action,
|
|
2419
|
+
to: [grant.to],
|
|
2420
|
+
[grant.targetKey]: [grant.outputTarget],
|
|
2421
|
+
[privilegeKey]: privileges,
|
|
2422
|
+
grantedBy: grant.grantedBy
|
|
2423
|
+
});
|
|
2424
|
+
};
|
|
1894
2425
|
/**
|
|
1895
2426
|
* This is needed to compare SQLs of table expressions.
|
|
1896
2427
|
* Need to exclude table columns of pending types, such as enums or domains,
|
|
@@ -1909,6 +2440,7 @@ const composeMigration = async (adapter, config, ast, dbStructure, params) => {
|
|
|
1909
2440
|
const { structureToAstCtx, currentSchema } = params;
|
|
1910
2441
|
await processRoles(ast, dbStructure, params);
|
|
1911
2442
|
processDefaultPrivileges(ast, dbStructure, params);
|
|
2443
|
+
processGrants(ast, dbStructure, params);
|
|
1912
2444
|
const domainsMap = (0, rake_db.makeDomainsMap)(structureToAstCtx, dbStructure);
|
|
1913
2445
|
await processSchemas(ast, dbStructure, params);
|
|
1914
2446
|
processExtensions(ast, dbStructure, params);
|
|
@@ -1919,7 +2451,7 @@ const composeMigration = async (adapter, config, ast, dbStructure, params) => {
|
|
|
1919
2451
|
return (0, rake_db.astToMigration)(currentSchema, config, ast);
|
|
1920
2452
|
};
|
|
1921
2453
|
const rollbackErr = /* @__PURE__ */ new Error("Rollback");
|
|
1922
|
-
const verifyMigration = async (adapter, config, migrationCode, generateMigrationParams, roles,
|
|
2454
|
+
const verifyMigration = async (adapter, config, migrationCode, generateMigrationParams, roles, structureParams) => {
|
|
1923
2455
|
const migrationFn = new Function("change", migrationCode);
|
|
1924
2456
|
let code;
|
|
1925
2457
|
try {
|
|
@@ -1936,7 +2468,7 @@ const verifyMigration = async (adapter, config, migrationCode, generateMigration
|
|
|
1936
2468
|
const dbStructure = await (0, rake_db.introspectDbSchema)(trx, {
|
|
1937
2469
|
rls: generateMigrationParams.codeItems.tables.some((table) => !!table.internal.tableRls),
|
|
1938
2470
|
roles,
|
|
1939
|
-
|
|
2471
|
+
...structureParams
|
|
1940
2472
|
});
|
|
1941
2473
|
generateMigrationParams.verifying = true;
|
|
1942
2474
|
try {
|
|
@@ -2122,6 +2654,14 @@ const report = (ast, config, currentSchema) => {
|
|
|
2122
2654
|
if (parts.length) code.push(parts.join("\n"));
|
|
2123
2655
|
break;
|
|
2124
2656
|
}
|
|
2657
|
+
case "grant": {
|
|
2658
|
+
const parts = [];
|
|
2659
|
+
const target = grantTargetToReport(a, currentSchema);
|
|
2660
|
+
if (a.privileges?.length) parts.push(`${a.action === "grant" ? green("+ grant privileges") : red("- revoke privileges")} ${a.privileges.map(mapGrantPrivilege).join(", ")} on ${target} ${a.action === "grant" ? "to" : "from"} ${a.to.join(", ")}`);
|
|
2661
|
+
if (a.grantablePrivileges?.length) parts.push(`${a.action === "grant" ? green("+ grant privileges") : red("- revoke privileges")} ${a.grantablePrivileges.map(mapGrantPrivilege).join(", ")} on ${target} with grant option ${a.action === "grant" ? "to" : "from"} ${a.to.join(", ")}`);
|
|
2662
|
+
if (parts.length) code.push(parts.join("\n"));
|
|
2663
|
+
break;
|
|
2664
|
+
}
|
|
2125
2665
|
case "tableRls": {
|
|
2126
2666
|
const table = dbItemName({
|
|
2127
2667
|
schema: a.schema,
|
|
@@ -2131,6 +2671,28 @@ const report = (ast, config, currentSchema) => {
|
|
|
2131
2671
|
code.push(message);
|
|
2132
2672
|
break;
|
|
2133
2673
|
}
|
|
2674
|
+
case "policy": {
|
|
2675
|
+
const summary = formatPolicySummary(a, dbItemName({
|
|
2676
|
+
schema: a.schema,
|
|
2677
|
+
name: a.table
|
|
2678
|
+
}, currentSchema));
|
|
2679
|
+
const message = a.action === "create" ? `${green("+ create policy")} ${a.name}: ${summary}` : `${red("- drop policy")} ${a.name}: ${summary}`;
|
|
2680
|
+
code.push(message);
|
|
2681
|
+
break;
|
|
2682
|
+
}
|
|
2683
|
+
case "changePolicy": {
|
|
2684
|
+
const table = dbItemName({
|
|
2685
|
+
schema: a.schema,
|
|
2686
|
+
name: a.table
|
|
2687
|
+
}, currentSchema);
|
|
2688
|
+
const fromName = a.from.name ?? a.name;
|
|
2689
|
+
const toName = a.to.name ?? a.name;
|
|
2690
|
+
const message = fromName === toName ? `${yellow("~ change policy")} ${a.name} on ${table}:` : `${yellow("~ rename policy")} ${fromName} ${yellow("=>")} ${toName} on ${table}:`;
|
|
2691
|
+
const inner = [`${yellow("from")}: ${formatPolicyChangeDefinition(a.from)}`, `${yellow("to")}: ${formatPolicyChangeDefinition(a.to)}`];
|
|
2692
|
+
code.push(message);
|
|
2693
|
+
code.push(inner);
|
|
2694
|
+
break;
|
|
2695
|
+
}
|
|
2134
2696
|
default: (0, pqb_internal.exhaustive)(a);
|
|
2135
2697
|
}
|
|
2136
2698
|
const result = (0, pqb_internal.codeToString)(code, "", " ");
|
|
@@ -2139,6 +2701,64 @@ const report = (ast, config, currentSchema) => {
|
|
|
2139
2701
|
const dbItemName = ({ schema, name }, currentSchema) => {
|
|
2140
2702
|
return schema && schema !== currentSchema ? `${schema}.${name}` : name;
|
|
2141
2703
|
};
|
|
2704
|
+
const grantTargetKeys = [
|
|
2705
|
+
"schemas",
|
|
2706
|
+
"tables",
|
|
2707
|
+
"sequences",
|
|
2708
|
+
"routines",
|
|
2709
|
+
"types",
|
|
2710
|
+
"domains",
|
|
2711
|
+
"databases"
|
|
2712
|
+
];
|
|
2713
|
+
const mapGrantPrivilege = (privilege) => {
|
|
2714
|
+
return privilege === "ALL" ? "ALL PRIVILEGES" : privilege;
|
|
2715
|
+
};
|
|
2716
|
+
const grantTargetToReport = (grant, currentSchema) => {
|
|
2717
|
+
for (const key of grantTargetKeys) {
|
|
2718
|
+
const targets = grant[key];
|
|
2719
|
+
if (!targets?.length) continue;
|
|
2720
|
+
return `${key} ${targets.map((target) => formatGrantTarget(key, target, currentSchema)).join(", ")}`;
|
|
2721
|
+
}
|
|
2722
|
+
return "unknown target";
|
|
2723
|
+
};
|
|
2724
|
+
const formatGrantTarget = (key, target, currentSchema) => {
|
|
2725
|
+
if (key === "schemas" || key === "databases") return target;
|
|
2726
|
+
const [schema, name] = (0, rake_db.getSchemaAndTableFromName)(currentSchema, target);
|
|
2727
|
+
return dbItemName({
|
|
2728
|
+
schema,
|
|
2729
|
+
name
|
|
2730
|
+
}, currentSchema);
|
|
2731
|
+
};
|
|
2732
|
+
const formatPolicyRoles = (roles) => {
|
|
2733
|
+
return roles?.length ? roles.join(", ") : "public";
|
|
2734
|
+
};
|
|
2735
|
+
const formatPolicyCommand = (command) => {
|
|
2736
|
+
return (command ?? "ALL").toLowerCase();
|
|
2737
|
+
};
|
|
2738
|
+
const formatPolicyMode = (mode) => {
|
|
2739
|
+
return mode === "PERMISSIVE" ? "permit" : "restrict";
|
|
2740
|
+
};
|
|
2741
|
+
const formatPolicySummary = (policy, table) => {
|
|
2742
|
+
const parts = [
|
|
2743
|
+
`${formatPolicyMode(policy.as)} access on ${table}`,
|
|
2744
|
+
`to ${formatPolicyRoles(policy.to)}`,
|
|
2745
|
+
`for ${formatPolicyCommand(policy.for)}`
|
|
2746
|
+
];
|
|
2747
|
+
if (policy.using) parts.push(`using (${policy.using})`);
|
|
2748
|
+
if (policy.withCheck) parts.push(`with check (${policy.withCheck})`);
|
|
2749
|
+
return parts.join(", ");
|
|
2750
|
+
};
|
|
2751
|
+
const formatPolicyChangeDefinition = (policy) => {
|
|
2752
|
+
const parts = [];
|
|
2753
|
+
if (policy.name) parts.push(`name ${policy.name}`);
|
|
2754
|
+
if (policy.table) parts.push(`table ${policy.table}`);
|
|
2755
|
+
if (policy.as) parts.push(`as ${policy.as.toLowerCase()}`);
|
|
2756
|
+
if (policy.for) parts.push(`for ${policy.for.toLowerCase()}`);
|
|
2757
|
+
if ("to" in policy) parts.push(`to ${formatPolicyRoles(policy.to)}`);
|
|
2758
|
+
if ("using" in policy && policy.using) parts.push(`using (${policy.using})`);
|
|
2759
|
+
if ("withCheck" in policy && policy.withCheck) parts.push(`with check (${policy.withCheck})`);
|
|
2760
|
+
return parts.join(", ");
|
|
2761
|
+
};
|
|
2142
2762
|
var AbortSignal = class extends Error {};
|
|
2143
2763
|
const generate = async (adapters, config, args, afterPull) => {
|
|
2144
2764
|
let { dbPath } = config;
|
|
@@ -2154,17 +2774,25 @@ const generate = async (adapters, config, args, afterPull) => {
|
|
|
2154
2774
|
if (afterPull) adapters = [afterPull.adapter];
|
|
2155
2775
|
const db = await getDbFromConfig(config, dbPath);
|
|
2156
2776
|
const { columnTypes, internal } = db.$qb;
|
|
2777
|
+
const structureParams = {
|
|
2778
|
+
loadDefaultPrivileges: internal.roles?.some((role) => role.defaultPrivileges !== void 0) ?? false,
|
|
2779
|
+
loadGrants: !!internal.grants || hasCodeTablesWithGrants(db)
|
|
2780
|
+
};
|
|
2157
2781
|
const rolesDbStructureParam = internal.roles ? internal.managedRolesSql ? { whereSql: internal.managedRolesSql } : pqb_internal.emptyObject : void 0;
|
|
2158
|
-
const { dbStructure } = await migrateAndPullStructures(adapters, config, db, rolesDbStructureParam,
|
|
2782
|
+
const { dbStructure } = await migrateAndPullStructures(adapters, config, db, rolesDbStructureParam, structureParams, afterPull);
|
|
2159
2783
|
const [adapter] = adapters;
|
|
2160
2784
|
const adapterSchema = adapter.getSchema();
|
|
2161
2785
|
const currentSchema = (typeof adapterSchema === "function" ? adapterSchema() : adapterSchema) ?? "public";
|
|
2162
2786
|
const codeItems = await getActualItems(db, currentSchema, internal, columnTypes);
|
|
2787
|
+
const effectiveGrants = getEffectiveGrants(internal.grants, codeItems);
|
|
2163
2788
|
const generateMigrationParams = {
|
|
2164
2789
|
structureToAstCtx: (0, rake_db.makeStructureToAstCtx)(config, currentSchema),
|
|
2165
2790
|
codeItems,
|
|
2166
2791
|
currentSchema,
|
|
2167
|
-
internal
|
|
2792
|
+
internal: {
|
|
2793
|
+
...internal,
|
|
2794
|
+
grants: effectiveGrants
|
|
2795
|
+
}
|
|
2168
2796
|
};
|
|
2169
2797
|
const ast = [];
|
|
2170
2798
|
let migrationCode;
|
|
@@ -2178,7 +2806,7 @@ const generate = async (adapters, config, args, afterPull) => {
|
|
|
2178
2806
|
throw err;
|
|
2179
2807
|
}
|
|
2180
2808
|
if (migrationCode && !afterPull) {
|
|
2181
|
-
const result = await verifyMigration(adapter, config, migrationCode, generateMigrationParams, rolesDbStructureParam,
|
|
2809
|
+
const result = await verifyMigration(adapter, config, migrationCode, generateMigrationParams, rolesDbStructureParam, structureParams);
|
|
2182
2810
|
if (result !== void 0) throw new Error(`Failed to verify generated migration: some of database changes were not applied properly. This is a bug, please open an issue, attach the following migration code:\n${migrationCode}${result === false ? "" : `\nAfter applying:\n${result}`}`);
|
|
2183
2811
|
}
|
|
2184
2812
|
const { logger } = config;
|
|
@@ -2207,7 +2835,7 @@ const getDbFromConfig = async (config, dbPath) => {
|
|
|
2207
2835
|
if (!db?.$qb) throw new Error(`Unable to import OrchidORM instance as ${config.dbExportedAs ?? "db"} from ${config.dbPath}`);
|
|
2208
2836
|
return db;
|
|
2209
2837
|
};
|
|
2210
|
-
const migrateAndPullStructures = async (adapters, config, db, roles,
|
|
2838
|
+
const migrateAndPullStructures = async (adapters, config, db, roles, structureParams, afterPull) => {
|
|
2211
2839
|
if (afterPull) return { dbStructure: {
|
|
2212
2840
|
version: await (0, rake_db.getDbVersion)(adapters[0]),
|
|
2213
2841
|
schemas: [],
|
|
@@ -2226,7 +2854,8 @@ const migrateAndPullStructures = async (adapters, config, db, roles, defaultPriv
|
|
|
2226
2854
|
const dbStructures = await Promise.all(adapters.map((adapter) => (0, rake_db.introspectDbSchema)(adapter, {
|
|
2227
2855
|
rls: hasCodeTablesWithRls(db),
|
|
2228
2856
|
roles,
|
|
2229
|
-
loadDefaultPrivileges:
|
|
2857
|
+
loadDefaultPrivileges: structureParams?.loadDefaultPrivileges,
|
|
2858
|
+
loadGrants: structureParams?.loadGrants
|
|
2230
2859
|
})));
|
|
2231
2860
|
const dbStructure = dbStructures[0];
|
|
2232
2861
|
for (let i = 1; i < dbStructures.length; i++) compareDbStructures(dbStructure, dbStructures[i], i);
|
|
@@ -2239,6 +2868,30 @@ const hasCodeTablesWithRls = (db) => {
|
|
|
2239
2868
|
}
|
|
2240
2869
|
return false;
|
|
2241
2870
|
};
|
|
2871
|
+
const hasCodeTablesWithGrants = (db) => {
|
|
2872
|
+
for (const key in db) {
|
|
2873
|
+
if (key[0] === "$") continue;
|
|
2874
|
+
if (db[key].internal.tableGrants?.length) return true;
|
|
2875
|
+
}
|
|
2876
|
+
return false;
|
|
2877
|
+
};
|
|
2878
|
+
const getEffectiveGrants = (grants, codeItems) => {
|
|
2879
|
+
const effectiveGrants = grants ? [...grants] : [];
|
|
2880
|
+
for (const table of codeItems.tables) {
|
|
2881
|
+
const tableGrants = table.internal.tableGrants;
|
|
2882
|
+
if (!tableGrants?.length) continue;
|
|
2883
|
+
const tableTarget = table.q.schema ? `${table.q.schema}.${table.table}` : table.table;
|
|
2884
|
+
for (const grant of tableGrants) {
|
|
2885
|
+
const internalGrant = {
|
|
2886
|
+
...grant,
|
|
2887
|
+
to: (0, pqb_internal.toArray)(grant.to),
|
|
2888
|
+
tables: [tableTarget]
|
|
2889
|
+
};
|
|
2890
|
+
effectiveGrants.push(internalGrant);
|
|
2891
|
+
}
|
|
2892
|
+
}
|
|
2893
|
+
return effectiveGrants.length ? effectiveGrants : void 0;
|
|
2894
|
+
};
|
|
2242
2895
|
const compareDbStructures = (a, b, i, path) => {
|
|
2243
2896
|
let err;
|
|
2244
2897
|
if (typeof a !== typeof b) err = true;
|
|
@@ -2274,7 +2927,10 @@ const getActualItems = async (db, currentSchema, internal, columnTypes) => {
|
|
|
2274
2927
|
codeItems.tables.push({
|
|
2275
2928
|
table: table.table,
|
|
2276
2929
|
shape: table.shape,
|
|
2277
|
-
internal:
|
|
2930
|
+
internal: {
|
|
2931
|
+
...table.internal,
|
|
2932
|
+
rls: internal.rls
|
|
2933
|
+
},
|
|
2278
2934
|
q: { schema: (0, pqb_internal.getQuerySchema)(table) }
|
|
2279
2935
|
});
|
|
2280
2936
|
for (const key in table.relations) {
|
|
@@ -2319,8 +2975,27 @@ const getActualItems = async (db, currentSchema, internal, columnTypes) => {
|
|
|
2319
2975
|
for (const privilege of role.defaultPrivileges) if (privilege.schema) codeItems.schemas.add(privilege.schema);
|
|
2320
2976
|
}
|
|
2321
2977
|
}
|
|
2978
|
+
if (internal.grants) for (const grant of internal.grants) addGrantSchemas(codeItems.schemas, currentSchema, grant);
|
|
2322
2979
|
return codeItems;
|
|
2323
2980
|
};
|
|
2981
|
+
const addGrantSchemas = (schemas, currentSchema, grant) => {
|
|
2982
|
+
for (const schema of [
|
|
2983
|
+
...grant.schemas ?? [],
|
|
2984
|
+
...grant.allTablesIn ?? [],
|
|
2985
|
+
...grant.allSequencesIn ?? [],
|
|
2986
|
+
...grant.allRoutinesIn ?? []
|
|
2987
|
+
]) schemas.add(schema);
|
|
2988
|
+
for (const target of [
|
|
2989
|
+
...grant.tables ?? [],
|
|
2990
|
+
...grant.sequences ?? [],
|
|
2991
|
+
...grant.routines ?? [],
|
|
2992
|
+
...grant.types ?? [],
|
|
2993
|
+
...grant.domains ?? []
|
|
2994
|
+
]) {
|
|
2995
|
+
const [schema] = (0, rake_db.getSchemaAndTableFromName)(currentSchema, target);
|
|
2996
|
+
if (schema) schemas.add(schema);
|
|
2997
|
+
}
|
|
2998
|
+
};
|
|
2324
2999
|
const processEnumColumn = (column, currentSchema, codeItems) => {
|
|
2325
3000
|
const { enumName, options } = column;
|
|
2326
3001
|
const [schema, name] = (0, rake_db.getSchemaAndTableFromName)(currentSchema, enumName);
|