@rebasepro/studio 0.15.0 → 0.16.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/{ApiExplorer-CDOlUQzk.js → ApiExplorer-9iwGvNnt.js} +5 -5
- package/dist/ApiExplorer-9iwGvNnt.js.map +1 -0
- package/dist/{JSEditor-BaSrzWhF.js → JSEditor-BZnvK1n6.js} +14 -14
- package/dist/JSEditor-BZnvK1n6.js.map +1 -0
- package/dist/{RLSEditor-CV8R1G3W.js → RLSEditor-CBX4uD66.js} +163 -49
- package/dist/RLSEditor-CBX4uD66.js.map +1 -0
- package/dist/{SQLEditor-BmI0iIWK.js → SQLEditor-ByecwX8B.js} +16 -16
- package/dist/SQLEditor-ByecwX8B.js.map +1 -0
- package/dist/{SchemaVisualizer-vxKSG80C.js → SchemaVisualizer-D6tMZgeC.js} +4 -4
- package/dist/SchemaVisualizer-D6tMZgeC.js.map +1 -0
- package/dist/{StorageView-CwDI4sBG.js → StorageView-CGm-cacx.js} +9 -9
- package/dist/StorageView-CGm-cacx.js.map +1 -0
- package/dist/components/RLSEditor/PolicyEditor.d.ts +9 -1
- package/dist/components/RLSEditor/policy-presets.d.ts +74 -0
- package/dist/index.es.js +6 -6
- package/package.json +9 -9
- package/src/components/ApiExplorer/ApiExplorer.tsx +2 -2
- package/src/components/ApiExplorer/EndpointDetail.tsx +1 -1
- package/src/components/ApiExplorer/TryItPanel.tsx +1 -1
- package/src/components/JSEditor/JSEditor.tsx +7 -7
- package/src/components/JSEditor/JSEditorSidebar.tsx +6 -6
- package/src/components/RLSEditor/PolicyEditor.tsx +31 -96
- package/src/components/RLSEditor/RLSEditor.tsx +46 -4
- package/src/components/RLSEditor/policy-presets.ts +176 -0
- package/src/components/SQLEditor/SQLEditor.tsx +10 -10
- package/src/components/SQLEditor/SQLEditorSidebar.tsx +4 -4
- package/src/components/SQLEditor/SchemaBrowser.tsx +1 -1
- package/src/components/SchemaVisualizer/SchemaVisualizer.tsx +1 -1
- package/src/components/SchemaVisualizer/TableNode.tsx +2 -2
- package/src/components/StorageView/StorageView.tsx +8 -8
- package/dist/ApiExplorer-CDOlUQzk.js.map +0 -1
- package/dist/JSEditor-BaSrzWhF.js.map +0 -1
- package/dist/RLSEditor-CV8R1G3W.js.map +0 -1
- package/dist/SQLEditor-BmI0iIWK.js.map +0 -1
- package/dist/SchemaVisualizer-vxKSG80C.js.map +0 -1
- package/dist/StorageView-CwDI4sBG.js.map +0 -1
|
@@ -3,10 +3,18 @@ import { ErrorView, useApiBase, useApiConfig, useRebaseContext, useSnackbarContr
|
|
|
3
3
|
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
4
4
|
import { Alert, AlertTriangleIcon, Button, Chip, CircularProgress, DatabaseIcon, Dialog, DialogActions, DialogContent, DialogTitle, HelpCircleIcon, IconButton, KeyIcon, Link2Icon, LockIcon, MultiSelect, MultiSelectItem, Paper, RefreshCwIcon, ResizablePanels, Select, SelectItem, ShieldIcon, Tab, Tabs, TextField, Tooltip, Trash2Icon, Typography, cls, defaultBorderMixin, iconSize } from "@rebasepro/ui";
|
|
5
5
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
6
|
-
import { isPostgresCollectionConfig } from "@rebasepro/types";
|
|
6
|
+
import { RLS_JWT_SQL, RLS_ROLES_SQL, RLS_UID_SQL, isPostgresCollectionConfig, policy } from "@rebasepro/types";
|
|
7
7
|
import { getPolicyNamesForRule, getPolicyNamesForRules, getPolicyOperations } from "@rebasepro/utils";
|
|
8
|
-
import { JUNCTION_TABLES_SQL, REBASE_INTERNAL_PREFIXES, REBASE_INTERNAL_SCHEMAS, getEffectiveSecurityRules, getJunctionSecurityRules, resolveJunctionSpecs } from "@rebasepro/common";
|
|
9
|
-
//#region src/components/RLSEditor/
|
|
8
|
+
import { JUNCTION_TABLES_SQL, REBASE_INTERNAL_PREFIXES, REBASE_INTERNAL_SCHEMAS, REBASE_USER_ROLE, getEffectiveSecurityRules, getJunctionSecurityRules, policyToPostgres, resolveJunctionSpecs } from "@rebasepro/common";
|
|
9
|
+
//#region src/components/RLSEditor/policy-presets.ts
|
|
10
|
+
/**
|
|
11
|
+
* What the RLS editor offers before the user types anything: the roles a `TO`
|
|
12
|
+
* list may name, and the ready-made policies.
|
|
13
|
+
*
|
|
14
|
+
* Separated from the component because these are the parts that have to agree
|
|
15
|
+
* with the *server*, not with the UI — and the ways they can silently disagree
|
|
16
|
+
* are the reason this file has tests.
|
|
17
|
+
*/
|
|
10
18
|
var COMMAND_OPTIONS = [
|
|
11
19
|
"ALL",
|
|
12
20
|
"SELECT",
|
|
@@ -14,12 +22,72 @@ var COMMAND_OPTIONS = [
|
|
|
14
22
|
"UPDATE",
|
|
15
23
|
"DELETE"
|
|
16
24
|
];
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
25
|
+
/**
|
|
26
|
+
* The roles a policy's `TO` list can name when nothing better is known.
|
|
27
|
+
*
|
|
28
|
+
* Not `authenticated` / `anon` / `admin`, which is what this list used to be.
|
|
29
|
+
* The first two are Supabase's role names and Rebase never creates them, so
|
|
30
|
+
* `CREATE POLICY ... TO authenticated` — which is literally what the editor
|
|
31
|
+
* builds and executes — fails with `role "authenticated" does not exist`. The
|
|
32
|
+
* driver's `validatePolicyPgRoles` rejects the same three names in
|
|
33
|
+
* `SecurityRule.pgRoles` and says why: they are another platform's convention,
|
|
34
|
+
* and application roles belong in a condition, not in the `TO` list.
|
|
35
|
+
*
|
|
36
|
+
* `admin` was the more dangerous entry, because it is a plausible *application*
|
|
37
|
+
* role. Had one existed as a database role too, the policy would have been
|
|
38
|
+
* created successfully and then matched nothing at all: requests run as
|
|
39
|
+
* {@link REBASE_USER_ROLE} after a `SET LOCAL ROLE`, and a `TO` list naming a
|
|
40
|
+
* role the request never assumes filters every row without erroring.
|
|
41
|
+
*
|
|
42
|
+
* So: `public` (what the framework's own generator emits) and the role requests
|
|
43
|
+
* actually arrive as. "Signed-in users" and "admins" are not roles here — they
|
|
44
|
+
* are conditions over `rebase.uid()` and `rebase.roles()`, which is what the
|
|
45
|
+
* presets below now compile to.
|
|
46
|
+
*/
|
|
47
|
+
var FALLBACK_ROLE_OPTIONS = ["public", REBASE_USER_ROLE];
|
|
48
|
+
/**
|
|
49
|
+
* The `TO` list the editor offers, given what the database reported and what
|
|
50
|
+
* the policy being edited already targets.
|
|
51
|
+
*
|
|
52
|
+
* Three sources, and each is there for a reason the others do not cover:
|
|
53
|
+
*
|
|
54
|
+
* - {@link FALLBACK_ROLE_OPTIONS} always, because `public` is a **keyword, not
|
|
55
|
+
* a row in `pg_roles`** — `fetchAvailableRoles` cannot return it, so seeding
|
|
56
|
+
* from the live fetch alone would drop the one role every generated policy
|
|
57
|
+
* targets.
|
|
58
|
+
* - the live roles, so an operator's own `app_read` is reachable without
|
|
59
|
+
* hand-editing SQL.
|
|
60
|
+
* - the edited policy's own roles, because a `MultiSelect` silently drops a
|
|
61
|
+
* value with no matching item. That value is the `TO` list of a policy
|
|
62
|
+
* already enforcing something, so opening an unrelated policy for a one-word
|
|
63
|
+
* rename would have quietly rewritten who it applies to.
|
|
64
|
+
*/
|
|
65
|
+
function roleOptionsFor(fetched, policyRoles) {
|
|
66
|
+
return [.../* @__PURE__ */ new Set([
|
|
67
|
+
...FALLBACK_ROLE_OPTIONS,
|
|
68
|
+
...fetched ?? [],
|
|
69
|
+
...policyRoles ?? []
|
|
70
|
+
])];
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Preset conditions, compiled by the framework's own policy compiler rather
|
|
74
|
+
* than written out here.
|
|
75
|
+
*
|
|
76
|
+
* `policyToPostgres` is the function `db push` generates policies with, so a
|
|
77
|
+
* preset cannot drift from what the framework emits — and the one that matters
|
|
78
|
+
* has drifted before: `authenticated()` used to compile to a bare
|
|
79
|
+
* `IS NOT NULL`, which is true for anonymous visitors, and any copy of that
|
|
80
|
+
* string sitting in a preset would still be handing out the old grant today.
|
|
81
|
+
*/
|
|
82
|
+
var SIGNED_IN_SQL = policyToPostgres(policy.authenticated());
|
|
83
|
+
var IS_ADMIN_SQL = policyToPostgres(policy.rolesOverlap(["admin"]));
|
|
84
|
+
var OWNS_ROW_SQL = policyToPostgres(policy.compare(policy.authUid(), "eq", policy.field("uid")));
|
|
85
|
+
/**
|
|
86
|
+
* Every preset targets `TO public`, which is what the framework's generator
|
|
87
|
+
* emits and what actually reaches a request running as `rebase_user`. The part
|
|
88
|
+
* that used to be expressed as a role — "authenticated", "admin" — is a
|
|
89
|
+
* condition now, so it is enforced where Postgres will actually evaluate it.
|
|
90
|
+
*/
|
|
23
91
|
var POLICY_PRESETS = [
|
|
24
92
|
{
|
|
25
93
|
id: "public_read",
|
|
@@ -34,61 +102,74 @@ var POLICY_PRESETS = [
|
|
|
34
102
|
},
|
|
35
103
|
{
|
|
36
104
|
id: "auth_read",
|
|
37
|
-
label: "Enable read access for
|
|
38
|
-
description: "Only
|
|
39
|
-
policyname: "Enable read access for
|
|
105
|
+
label: "Enable read access for signed-in users only",
|
|
106
|
+
description: "Only signed-in users are allowed to read data. Anonymous requests carry a sentinel uid, so the condition excludes them explicitly.",
|
|
107
|
+
policyname: "Enable read access for signed-in users",
|
|
40
108
|
cmd: "SELECT",
|
|
41
109
|
permissive: "PERMISSIVE",
|
|
42
|
-
roles: ["
|
|
43
|
-
qual:
|
|
110
|
+
roles: ["public"],
|
|
111
|
+
qual: SIGNED_IN_SQL,
|
|
44
112
|
with_check: ""
|
|
45
113
|
},
|
|
46
114
|
{
|
|
47
115
|
id: "auth_insert",
|
|
48
|
-
label: "Enable insert for
|
|
49
|
-
description: "Only
|
|
50
|
-
policyname: "Enable insert for
|
|
116
|
+
label: "Enable insert for signed-in users only",
|
|
117
|
+
description: "Only signed-in users are allowed to insert new data.",
|
|
118
|
+
policyname: "Enable insert for signed-in users only",
|
|
51
119
|
cmd: "INSERT",
|
|
52
120
|
permissive: "PERMISSIVE",
|
|
53
|
-
roles: ["
|
|
121
|
+
roles: ["public"],
|
|
54
122
|
qual: "",
|
|
55
|
-
with_check:
|
|
123
|
+
with_check: SIGNED_IN_SQL
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
id: "admin_all",
|
|
127
|
+
label: "Admins can do anything",
|
|
128
|
+
description: "Restricted to users holding the `admin` application role, matched inside the policy via rebase.roles().",
|
|
129
|
+
policyname: "Admins have full access",
|
|
130
|
+
cmd: "ALL",
|
|
131
|
+
permissive: "PERMISSIVE",
|
|
132
|
+
roles: ["public"],
|
|
133
|
+
qual: IS_ADMIN_SQL,
|
|
134
|
+
with_check: IS_ADMIN_SQL
|
|
56
135
|
},
|
|
57
136
|
{
|
|
58
137
|
id: "user_select_own",
|
|
59
138
|
label: "Users can read their own rows",
|
|
60
|
-
description: "Users can only read rows
|
|
139
|
+
description: "Users can only read rows whose uid column matches their rebase.uid()",
|
|
61
140
|
policyname: "Users can select their own data",
|
|
62
141
|
cmd: "SELECT",
|
|
63
142
|
permissive: "PERMISSIVE",
|
|
64
|
-
roles: ["
|
|
65
|
-
qual:
|
|
143
|
+
roles: ["public"],
|
|
144
|
+
qual: OWNS_ROW_SQL,
|
|
66
145
|
with_check: ""
|
|
67
146
|
},
|
|
68
147
|
{
|
|
69
148
|
id: "user_update_own",
|
|
70
149
|
label: "Users can update their own rows",
|
|
71
|
-
description: "Users can only update rows
|
|
150
|
+
description: "Users can only update rows whose uid column matches their rebase.uid()",
|
|
72
151
|
policyname: "Users can update their own data",
|
|
73
152
|
cmd: "UPDATE",
|
|
74
153
|
permissive: "PERMISSIVE",
|
|
75
|
-
roles: ["
|
|
76
|
-
qual:
|
|
77
|
-
with_check:
|
|
154
|
+
roles: ["public"],
|
|
155
|
+
qual: OWNS_ROW_SQL,
|
|
156
|
+
with_check: OWNS_ROW_SQL
|
|
78
157
|
},
|
|
79
158
|
{
|
|
80
159
|
id: "user_delete_own",
|
|
81
160
|
label: "Users can delete their own rows",
|
|
82
|
-
description: "Users can only delete rows
|
|
161
|
+
description: "Users can only delete rows whose uid column matches their rebase.uid()",
|
|
83
162
|
policyname: "Users can delete their own data",
|
|
84
163
|
cmd: "DELETE",
|
|
85
164
|
permissive: "PERMISSIVE",
|
|
86
|
-
roles: ["
|
|
87
|
-
qual:
|
|
165
|
+
roles: ["public"],
|
|
166
|
+
qual: OWNS_ROW_SQL,
|
|
88
167
|
with_check: ""
|
|
89
168
|
}
|
|
90
169
|
];
|
|
91
|
-
|
|
170
|
+
//#endregion
|
|
171
|
+
//#region src/components/RLSEditor/PolicyEditor.tsx
|
|
172
|
+
var PolicyEditor = ({ policy, schema, table, roleOptions, onSave, onCancel }) => {
|
|
92
173
|
const { t } = useTranslation();
|
|
93
174
|
const [name, setName] = useState("");
|
|
94
175
|
const [helpOpen, setHelpOpen] = useState(false);
|
|
@@ -98,6 +179,7 @@ var PolicyEditor = ({ policy, schema, table, onSave, onCancel }) => {
|
|
|
98
179
|
const [usingExpr, setUsingExpr] = useState("");
|
|
99
180
|
const [checkExpr, setCheckExpr] = useState("");
|
|
100
181
|
const [selectedPreset, setSelectedPreset] = useState("");
|
|
182
|
+
const availableRoles = useMemo(() => roleOptionsFor(roleOptions, policy?.roles), [roleOptions, policy]);
|
|
101
183
|
useEffect(() => {
|
|
102
184
|
if (policy) {
|
|
103
185
|
setName(policy.policyname || "");
|
|
@@ -303,7 +385,7 @@ var PolicyEditor = ({ policy, schema, table, onSave, onCancel }) => {
|
|
|
303
385
|
value: roles,
|
|
304
386
|
onValueChange: setRoles,
|
|
305
387
|
placeholder: t("studio_policy_roles_placeholder"),
|
|
306
|
-
children:
|
|
388
|
+
children: availableRoles.map((role) => /* @__PURE__ */ jsx(MultiSelectItem, {
|
|
307
389
|
value: role,
|
|
308
390
|
children: role
|
|
309
391
|
}, role))
|
|
@@ -457,9 +539,13 @@ var PolicyEditor = ({ policy, schema, table, onSave, onCancel }) => {
|
|
|
457
539
|
className: "text-text-secondary dark:text-text-secondary-dark mb-1",
|
|
458
540
|
children: t("studio_policy_help_step3_desc")
|
|
459
541
|
}),
|
|
460
|
-
/* @__PURE__ */
|
|
542
|
+
/* @__PURE__ */ jsxs("div", {
|
|
461
543
|
className: cls("bg-surface-100 dark:bg-surface-950 px-3 py-2 rounded-md font-mono text-sm my-2", defaultBorderMixin),
|
|
462
|
-
children:
|
|
544
|
+
children: [
|
|
545
|
+
"Example: ",
|
|
546
|
+
RLS_UID_SQL,
|
|
547
|
+
" = uid"
|
|
548
|
+
]
|
|
463
549
|
}),
|
|
464
550
|
/* @__PURE__ */ jsx(Typography, {
|
|
465
551
|
variant: "caption",
|
|
@@ -481,9 +567,13 @@ var PolicyEditor = ({ policy, schema, table, onSave, onCancel }) => {
|
|
|
481
567
|
className: "text-text-secondary dark:text-text-secondary-dark mb-1",
|
|
482
568
|
children: t("studio_policy_help_step4_desc")
|
|
483
569
|
}),
|
|
484
|
-
/* @__PURE__ */
|
|
570
|
+
/* @__PURE__ */ jsxs("div", {
|
|
485
571
|
className: cls("bg-surface-100 dark:bg-surface-950 px-3 py-2 rounded-md font-mono text-sm my-2", defaultBorderMixin),
|
|
486
|
-
children:
|
|
572
|
+
children: [
|
|
573
|
+
"Example: ",
|
|
574
|
+
RLS_UID_SQL,
|
|
575
|
+
" = uid"
|
|
576
|
+
]
|
|
487
577
|
}),
|
|
488
578
|
/* @__PURE__ */ jsx(Typography, {
|
|
489
579
|
variant: "caption",
|
|
@@ -510,32 +600,36 @@ var PolicyEditor = ({ policy, schema, table, onSave, onCancel }) => {
|
|
|
510
600
|
children: [
|
|
511
601
|
/* @__PURE__ */ jsxs("li", { children: [/* @__PURE__ */ jsx("code", {
|
|
512
602
|
className: "bg-surface-100 dark:bg-surface-950 px-1.5 py-0.5 rounded mr-1 whitespace-nowrap",
|
|
513
|
-
children:
|
|
603
|
+
children: RLS_UID_SQL
|
|
514
604
|
}), /* @__PURE__ */ jsxs("span", {
|
|
515
605
|
className: "block mt-0.5",
|
|
516
|
-
children: ["Returns the current user's ID as text. Example: ", /* @__PURE__ */
|
|
606
|
+
children: ["Returns the current user's ID as text. Example: ", /* @__PURE__ */ jsxs("code", {
|
|
517
607
|
className: "bg-surface-100 dark:bg-surface-950 px-1 py-0.5 rounded text-[11px]",
|
|
518
|
-
children: "
|
|
608
|
+
children: [RLS_UID_SQL, " = uid"]
|
|
519
609
|
})]
|
|
520
610
|
})] }),
|
|
521
611
|
/* @__PURE__ */ jsxs("li", { children: [/* @__PURE__ */ jsx("code", {
|
|
522
612
|
className: "bg-surface-100 dark:bg-surface-950 px-1.5 py-0.5 rounded mr-1 whitespace-nowrap",
|
|
523
|
-
children:
|
|
613
|
+
children: RLS_JWT_SQL
|
|
524
614
|
}), /* @__PURE__ */ jsxs("span", {
|
|
525
615
|
className: "block mt-0.5",
|
|
526
|
-
children: ["Returns the full JWT payload as JSONB so you can check custom claims. Example: ", /* @__PURE__ */
|
|
616
|
+
children: ["Returns the full JWT payload as JSONB so you can check custom claims. Example: ", /* @__PURE__ */ jsxs("code", {
|
|
527
617
|
className: "bg-surface-100 dark:bg-surface-950 px-1 py-0.5 rounded text-[11px]",
|
|
528
|
-
children: "
|
|
618
|
+
children: [RLS_JWT_SQL, " ->> 'email' = 'admin@example.com'"]
|
|
529
619
|
})]
|
|
530
620
|
})] }),
|
|
531
621
|
/* @__PURE__ */ jsxs("li", { children: [/* @__PURE__ */ jsx("code", {
|
|
532
622
|
className: "bg-surface-100 dark:bg-surface-950 px-1.5 py-0.5 rounded mr-1 whitespace-nowrap",
|
|
533
|
-
children:
|
|
623
|
+
children: RLS_ROLES_SQL
|
|
534
624
|
}), /* @__PURE__ */ jsxs("span", {
|
|
535
625
|
className: "block mt-0.5",
|
|
536
|
-
children: ["Returns the user's role IDs as a comma-separated string. Best used with: ", /* @__PURE__ */
|
|
626
|
+
children: ["Returns the user's role IDs as a comma-separated string. Best used with: ", /* @__PURE__ */ jsxs("code", {
|
|
537
627
|
className: "bg-surface-100 dark:bg-surface-950 px-1 py-0.5 rounded text-[11px]",
|
|
538
|
-
children:
|
|
628
|
+
children: [
|
|
629
|
+
"string_to_array(",
|
|
630
|
+
RLS_ROLES_SQL,
|
|
631
|
+
", ',') @> ARRAY['admin']"
|
|
632
|
+
]
|
|
539
633
|
})]
|
|
540
634
|
})] })
|
|
541
635
|
]
|
|
@@ -729,6 +823,25 @@ var RLSEditor = ({ apiUrl = "" }) => {
|
|
|
729
823
|
const [activeTab, setActiveTab] = useState(0);
|
|
730
824
|
const [junctionTableNames, setJunctionTableNames] = useState(/* @__PURE__ */ new Set());
|
|
731
825
|
const [editingPolicy, setEditingPolicy] = useState(null);
|
|
826
|
+
/**
|
|
827
|
+
* Native PostgreSQL roles, for the policy editor's `TO` list.
|
|
828
|
+
*
|
|
829
|
+
* `fetchAvailableRoles` and not `fetchApplicationRoles`: a policy's `TO`
|
|
830
|
+
* clause names database roles, and an application role put there produces a
|
|
831
|
+
* policy that either fails to create or matches nothing. The editor falls
|
|
832
|
+
* back to a static pair when the driver cannot answer.
|
|
833
|
+
*/
|
|
834
|
+
const [pgRoleOptions, setPgRoleOptions] = useState([]);
|
|
835
|
+
useEffect(() => {
|
|
836
|
+
let mounted = true;
|
|
837
|
+
if (!databaseAdmin?.fetchAvailableRoles) return;
|
|
838
|
+
databaseAdmin.fetchAvailableRoles().then((roles) => {
|
|
839
|
+
if (mounted) setPgRoleOptions(roles);
|
|
840
|
+
}).catch((e) => console.error("Failed to fetch database roles", e));
|
|
841
|
+
return () => {
|
|
842
|
+
mounted = false;
|
|
843
|
+
};
|
|
844
|
+
}, [databaseAdmin]);
|
|
732
845
|
const [sidebarSize, setSidebarSize] = useState(() => {
|
|
733
846
|
try {
|
|
734
847
|
const saved = localStorage.getItem("rebase_rls_editor_sidebar_size");
|
|
@@ -899,7 +1012,7 @@ var RLSEditor = ({ apiUrl = "" }) => {
|
|
|
899
1012
|
tablename: activeTableData.tableName,
|
|
900
1013
|
permissive: (rule.mode || "permissive").toUpperCase(),
|
|
901
1014
|
cmd: (ops[opIdx] ?? rule.operation ?? "ALL").toUpperCase(),
|
|
902
|
-
roles: [...rule.
|
|
1015
|
+
roles: [...rule.pgRoles ?? ["public"]],
|
|
903
1016
|
qual: rule.using || null,
|
|
904
1017
|
with_check: rule.withCheck || null,
|
|
905
1018
|
status: policiesMap[policyName] ? "both" : "code_only"
|
|
@@ -968,7 +1081,7 @@ var RLSEditor = ({ apiUrl = "" }) => {
|
|
|
968
1081
|
className: cls("flex items-center justify-between px-3 py-2 border-b bg-surface-50 dark:bg-surface-900 min-h-[48px]", defaultBorderMixin),
|
|
969
1082
|
children: [/* @__PURE__ */ jsx(Typography, {
|
|
970
1083
|
variant: "caption",
|
|
971
|
-
className: "font-
|
|
1084
|
+
className: "font-semibold uppercase tracking-wider text-text-disabled dark:text-text-disabled-dark",
|
|
972
1085
|
children: t("studio_schema_tables")
|
|
973
1086
|
}), /* @__PURE__ */ jsx(IconButton, {
|
|
974
1087
|
size: "small",
|
|
@@ -1060,7 +1173,7 @@ var RLSEditor = ({ apiUrl = "" }) => {
|
|
|
1060
1173
|
className: cls("flex items-center justify-between px-3 py-2 border-b bg-surface-50 dark:bg-surface-900 min-h-[48px]", defaultBorderMixin),
|
|
1061
1174
|
children: /* @__PURE__ */ jsx(Typography, {
|
|
1062
1175
|
variant: "caption",
|
|
1063
|
-
className: "font-
|
|
1176
|
+
className: "font-semibold uppercase tracking-wider text-text-disabled dark:text-text-disabled-dark",
|
|
1064
1177
|
children: "Overview"
|
|
1065
1178
|
})
|
|
1066
1179
|
}), /* @__PURE__ */ jsxs("div", {
|
|
@@ -1285,6 +1398,7 @@ var RLSEditor = ({ apiUrl = "" }) => {
|
|
|
1285
1398
|
policy: editingPolicy === "new" ? void 0 : editingPolicy,
|
|
1286
1399
|
schema: activeTableData.schemaName,
|
|
1287
1400
|
table: activeTableData.tableName,
|
|
1401
|
+
roleOptions: pgRoleOptions,
|
|
1288
1402
|
onSave: async (newPolicy) => {
|
|
1289
1403
|
if (activeCollection && hasCodebase) {
|
|
1290
1404
|
const rule = {
|
|
@@ -1293,7 +1407,7 @@ var RLSEditor = ({ apiUrl = "" }) => {
|
|
|
1293
1407
|
mode: newPolicy.permissive?.toLowerCase(),
|
|
1294
1408
|
using: newPolicy.qual || void 0,
|
|
1295
1409
|
withCheck: newPolicy.with_check || void 0,
|
|
1296
|
-
roles: newPolicy.roles
|
|
1410
|
+
...newPolicy.roles && !(newPolicy.roles.length === 1 && newPolicy.roles[0] === "public") ? { pgRoles: newPolicy.roles } : {}
|
|
1297
1411
|
};
|
|
1298
1412
|
const existingRules = (isPostgresCollectionConfig(activeCollection) ? activeCollection.securityRules : void 0) || [];
|
|
1299
1413
|
let newRules;
|
|
@@ -1595,4 +1709,4 @@ var RLSEditor = ({ apiUrl = "" }) => {
|
|
|
1595
1709
|
//#endregion
|
|
1596
1710
|
export { RLSEditor };
|
|
1597
1711
|
|
|
1598
|
-
//# sourceMappingURL=RLSEditor-
|
|
1712
|
+
//# sourceMappingURL=RLSEditor-CBX4uD66.js.map
|