@rebasepro/common 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.
|
@@ -18,7 +18,7 @@ export interface PolicyEvalContext {
|
|
|
18
18
|
*
|
|
19
19
|
* Null here means *anonymous visitor*, not "server context" — a client is
|
|
20
20
|
* never the server context. `authUid` operands therefore resolve to
|
|
21
|
-
* {@link ANONYMOUS_USER_ID} rather than `null`, matching the `
|
|
21
|
+
* {@link ANONYMOUS_USER_ID} rather than `null`, matching the `rebase.uid()`
|
|
22
22
|
* the database would see for the same request.
|
|
23
23
|
*/
|
|
24
24
|
uid?: string | null;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rebasepro/common",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.16.0",
|
|
5
5
|
"description": "Rebase shared core — collection registry, data driver adapter and fluent query builder. No React dependency.",
|
|
6
6
|
"funding": {
|
|
7
7
|
"url": "https://github.com/sponsors/rebaseco"
|
|
@@ -40,8 +40,8 @@
|
|
|
40
40
|
"dependencies": {
|
|
41
41
|
"fast-equals": "6.0.2",
|
|
42
42
|
"json-logic-js": "^2.0.5",
|
|
43
|
-
"@rebasepro/
|
|
44
|
-
"@rebasepro/
|
|
43
|
+
"@rebasepro/utils": "0.16.0",
|
|
44
|
+
"@rebasepro/types": "0.16.0"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
47
|
"@jest/globals": "^30.4.1",
|
|
@@ -30,7 +30,7 @@ import { getPolicyNamesForRules } from "@rebasepro/utils";
|
|
|
30
30
|
* rows").
|
|
31
31
|
*
|
|
32
32
|
* **For auth collections additionally**
|
|
33
|
-
* 3. A permissive **self SELECT** grant (`id =
|
|
33
|
+
* 3. A permissive **self SELECT** grant (`id = rebase.uid()`), so users can read
|
|
34
34
|
* their own row (profile, session bootstrap) without every app re-declaring
|
|
35
35
|
* it.
|
|
36
36
|
* 4. A **restrictive** admin write gate. Restrictive policies are AND'd with
|
|
@@ -39,7 +39,7 @@ import { getPolicyNamesForRules } from "@rebasepro/utils";
|
|
|
39
39
|
* such as "a user may edit their own row". Without this, a permissive owner
|
|
40
40
|
* rule would let a user change their own `roles`.
|
|
41
41
|
*
|
|
42
|
-
* The server context is recognised as `
|
|
42
|
+
* The server context is recognised as `rebase.uid() IS NULL` (`policy.serverContext()`)
|
|
43
43
|
* — the built-in flows that run without a user (signup, migrations) set no user
|
|
44
44
|
* GUC — which also lets the owner connection satisfy these policies even under
|
|
45
45
|
* FORCE RLS. A *user* request never reaches that state: an anonymous one carries
|
|
@@ -51,7 +51,7 @@ import { getPolicyNamesForRules } from "@rebasepro/utils";
|
|
|
51
51
|
// Expressed structurally (not as raw SQL) so the admin UI can evaluate it
|
|
52
52
|
// exactly — the framework's most security-critical policies must be reflected
|
|
53
53
|
// precisely, not left as un-evaluable raw clauses. Compiles to
|
|
54
|
-
// `
|
|
54
|
+
// `rebase.uid() IS NULL OR (string_to_array(rebase.roles(), ',') && ARRAY['admin'])`.
|
|
55
55
|
//
|
|
56
56
|
// `serverContext()`, emphatically not `not(authenticated())`: the server arm of
|
|
57
57
|
// this grant must match the server context and nothing else. Anonymous visitors
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { rewriteLegacyRlsFunctions } from "@rebasepro/types";
|
|
1
2
|
import type {
|
|
2
3
|
ArrayProperty,
|
|
3
4
|
NumberProperty,
|
|
@@ -337,8 +338,14 @@ export function buildCollectionFromTableMetadata(
|
|
|
337
338
|
case "UPDATE": operations = ["update"]; break;
|
|
338
339
|
case "DELETE": operations = ["delete"]; break;
|
|
339
340
|
}
|
|
340
|
-
|
|
341
|
-
|
|
341
|
+
// Normalised on the way in, the same way `sqlToPolicy` normalises
|
|
342
|
+
// what the admin UI reads back. Without it, importing a table from a
|
|
343
|
+
// database provisioned before 1.0 copies `auth.uid()` straight into
|
|
344
|
+
// the project's config — a call to a function the framework no
|
|
345
|
+
// longer creates, which then boots with a legacy-helper warning
|
|
346
|
+
// forever and holds the `auth` schema open.
|
|
347
|
+
const qual = policy.qual ? rewriteLegacyRlsFunctions(policy.qual) : undefined;
|
|
348
|
+
const withCheck = policy.with_check ? rewriteLegacyRlsFunctions(policy.with_check) : undefined;
|
|
342
349
|
if (qual) {
|
|
343
350
|
securityRules.push({
|
|
344
351
|
name: policy.policy_name,
|
|
@@ -20,7 +20,7 @@ export interface PolicyEvalContext {
|
|
|
20
20
|
*
|
|
21
21
|
* Null here means *anonymous visitor*, not "server context" — a client is
|
|
22
22
|
* never the server context. `authUid` operands therefore resolve to
|
|
23
|
-
* {@link ANONYMOUS_USER_ID} rather than `null`, matching the `
|
|
23
|
+
* {@link ANONYMOUS_USER_ID} rather than `null`, matching the `rebase.uid()`
|
|
24
24
|
* the database would see for the same request.
|
|
25
25
|
*/
|
|
26
26
|
uid?: string | null;
|
|
@@ -68,7 +68,7 @@ export function evaluatePolicy(expr: PolicyExpression, ctx: PolicyEvalContext):
|
|
|
68
68
|
return ctx.uid != null && !isAnonymousUid(ctx.uid);
|
|
69
69
|
case "serverContext":
|
|
70
70
|
// A client is never the server context. Postgres decides this by
|
|
71
|
-
// `
|
|
71
|
+
// `rebase.uid() IS NULL`, which a client request can never produce:
|
|
72
72
|
// the driver substitutes ANONYMOUS_USER_ID for a missing id.
|
|
73
73
|
return false;
|
|
74
74
|
case "existsIn":
|
|
@@ -108,10 +108,10 @@ function resolveOperand(operand: PolicyOperand, ctx: PolicyEvalContext): Resolve
|
|
|
108
108
|
case "literal":
|
|
109
109
|
return { known: true, value: operand.value };
|
|
110
110
|
case "authUid":
|
|
111
|
-
// The sentinel, not null: `
|
|
111
|
+
// The sentinel, not null: `rebase.uid()` is never NULL for a request
|
|
112
112
|
// that came from a client, so comparing against null here would
|
|
113
113
|
// disagree with the database on exactly the rules that test for it
|
|
114
|
-
// (e.g. `
|
|
114
|
+
// (e.g. `rebase.uid() <> 'anonymous'`).
|
|
115
115
|
return { known: true, value: ctx.uid ?? ANONYMOUS_USER_ID };
|
|
116
116
|
case "authRoles":
|
|
117
117
|
return { known: true, value: ctx.roles ?? [] };
|