@cerbos/orm-prisma 3.3.0 → 4.0.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/README.md +83 -6
- package/lib/index.d.ts +29 -2
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +1459 -114
- package/lib/index.js.map +1 -1
- package/package.json +19 -13
- package/src/adversarial.test.ts +586 -0
- package/src/generated/prisma/commonInputTypes.ts +26 -0
- package/src/generated/prisma/internal/class.ts +9 -9
- package/src/generated/prisma/internal/prismaNamespace.ts +89 -19
- package/src/generated/prisma/internal/prismaNamespaceBrowser.ts +2 -1
- package/src/generated/prisma/models/Category.ts +1 -1
- package/src/generated/prisma/models/Label.ts +1 -1
- package/src/generated/prisma/models/NestedResource.ts +1 -1
- package/src/generated/prisma/models/NextLevelNestedResource.ts +1 -1
- package/src/generated/prisma/models/Resource.ts +62 -2
- package/src/generated/prisma/models/SubCategory.ts +1 -1
- package/src/generated/prisma/models/Tag.ts +1 -1
- package/src/generated/prisma/models/User.ts +1 -1
- package/src/generated/prisma-adversarial/browser.ts +44 -0
- package/src/generated/prisma-adversarial/client.ts +66 -0
- package/src/generated/prisma-adversarial/commonInputTypes.ts +352 -0
- package/src/generated/prisma-adversarial/enums.ts +15 -0
- package/src/generated/prisma-adversarial/internal/class.ts +244 -0
- package/src/generated/prisma-adversarial/internal/prismaNamespace.ts +1156 -0
- package/src/generated/prisma-adversarial/internal/prismaNamespaceBrowser.ts +140 -0
- package/src/generated/prisma-adversarial/models/AdversarialCategory.ts +1401 -0
- package/src/generated/prisma-adversarial/models/AdversarialLabel.ts +1272 -0
- package/src/generated/prisma-adversarial/models/AdversarialResource.ts +1665 -0
- package/src/generated/prisma-adversarial/models/AdversarialSubCategory.ts +1401 -0
- package/src/generated/prisma-adversarial/models/AdversarialTag.ts +1307 -0
- package/src/generated/prisma-adversarial/models.ts +16 -0
- package/src/generated/prisma-adversarial-v6/client.d.ts +1 -0
- package/src/generated/prisma-adversarial-v6/default.d.ts +1 -0
- package/src/generated/prisma-adversarial-v6/edge.d.ts +1 -0
- package/src/generated/prisma-adversarial-v6/index.d.ts +8640 -0
- package/src/generated/prisma-adversarial-v6/runtime/index-browser.d.ts +370 -0
- package/src/generated/prisma-adversarial-v6/runtime/library.d.ts +3982 -0
- package/src/generated/prisma-adversarial-v6/wasm.d.ts +1 -0
- package/src/generated/prisma-v6/index.d.ts +90 -3
- package/src/index.test.ts +3199 -636
- package/src/index.ts +2055 -148
- package/src/test-setup.adversarial.v6.ts +5 -0
- package/src/test-setup.adversarial.v7.ts +12 -0
package/README.md
CHANGED
|
@@ -16,13 +16,28 @@ An adapter library that takes a [Cerbos](https://cerbos.dev) Query Plan ([PlanRe
|
|
|
16
16
|
|
|
17
17
|
- One-to-one: `is`, `isNot`
|
|
18
18
|
- One-to-many/Many-to-many: `some`, `none`, `every`
|
|
19
|
-
- Collection operators: `exists`, `
|
|
19
|
+
- Collection operators: `exists`, `all`, `except` (`exists_one` requires counting matches,
|
|
20
|
+
which Prisma where-filters cannot express — it throws rather than silently degrading to
|
|
21
|
+
`exists`; `filter` only appears inside other expressions)
|
|
20
22
|
- Set operations: `hasIntersection`
|
|
21
23
|
|
|
22
|
-
####
|
|
24
|
+
#### Arithmetic
|
|
23
25
|
|
|
24
|
-
- `add`
|
|
25
|
-
|
|
26
|
+
- `add`, `sub`, `mult`, `div` with a constant side are solved algebraically to a plain column
|
|
27
|
+
comparison: `R.attr.aNumber + 1 > 2` → `{ aNumber: { gt: 1 } }` (Prisma where-filters cannot
|
|
28
|
+
express column arithmetic). Multiplying/dividing by a negative constant mirrors directional
|
|
29
|
+
operators. Arithmetic on both sides of a comparison, division BY a column, and equality or
|
|
30
|
+
inequality over fractional addition throw. The latter cannot be solved reversibly in IEEE-754
|
|
31
|
+
arithmetic.
|
|
32
|
+
- String concatenation solving: `P.attr.context == "projects:" + R.attr.id` → `{ id: { equals: "123" } }`
|
|
33
|
+
|
|
34
|
+
#### Field-to-field comparisons
|
|
35
|
+
|
|
36
|
+
Comparisons between two columns of the same model compile to
|
|
37
|
+
[Prisma field references](https://www.prisma.io/docs/orm/reference/prisma-client-reference#compare-columns-in-the-same-table).
|
|
38
|
+
Pass the Prisma model name via the `model` option (root columns) or `relation.model` in the
|
|
39
|
+
mapper (columns of a related model inside a collection expression). Comparisons across models
|
|
40
|
+
throw — Prisma only supports references between fields of the same model.
|
|
26
41
|
|
|
27
42
|
#### Hierarchy Operators
|
|
28
43
|
|
|
@@ -37,6 +52,65 @@ An adapter library that takes a [Cerbos](https://cerbos.dev) Query Plan ([PlanRe
|
|
|
37
52
|
- Collection mapping and filtering
|
|
38
53
|
- Complex condition combinations
|
|
39
54
|
- Type-safe field mappings
|
|
55
|
+
- Timestamp comparisons against Prisma `DateTime` columns. Mark the field mapping with
|
|
56
|
+
`valueType: "dateTime"`; applying `timestamp()` to an untyped/string mapping throws.
|
|
57
|
+
Literals must be strict RFC 3339 instants in CEL's supported year 0001–9999 range and
|
|
58
|
+
exactly representable at millisecond precision (fractional digits after the third may
|
|
59
|
+
only be zero). The mapped column/database must preserve that precision.
|
|
60
|
+
- Outer-column references inside collection expressions (e.g.
|
|
61
|
+
`R.attr.tags.exists(t, t.name == "x" && R.attr.aBool)`) are hoisted or case-split so every
|
|
62
|
+
filter lands on the model it belongs to
|
|
63
|
+
- Three-valued-logic guards for nullable element columns: mark a relation field as
|
|
64
|
+
`nullable: true` in the mapper and collection macros (`all`, negated `exists`,
|
|
65
|
+
`hasIntersection` over `map`) exclude rows whose elements hold `NULL` in that column,
|
|
66
|
+
matching Cerbos's treatment of a missing attribute as a deny
|
|
67
|
+
|
|
68
|
+
#### Known limitations (loud failures, never silently-wrong filters)
|
|
69
|
+
|
|
70
|
+
- LIKE wildcards: Prisma emits `LIKE` without an `ESCAPE` clause, so `contains`/`startsWith`/
|
|
71
|
+
`endsWith` with a needle containing `%` or `_`, or with a column-valued needle, throws.
|
|
72
|
+
(A constant *receiver* with a column needle — `"a-b".startsWith(R.attr.x)` — is translated
|
|
73
|
+
exactly by enumerating candidate needles into an `in` filter.)
|
|
74
|
+
- Hierarchy prefixes: `ancestorOf`, `descendentOf` and `overlaps` narrow a column with a
|
|
75
|
+
`startsWith`, so they throw when the constant hierarchy contains `%`, `_` or `[`. `[` is
|
|
76
|
+
rejected as well as the two LIKE wildcards because SQL Server opens a character class on
|
|
77
|
+
`[` even when an `ESCAPE` clause is declared, so it cannot be matched literally at all.
|
|
78
|
+
- Counting: `exists_one`, `size()` thresholds other than empty/non-empty, and string-length
|
|
79
|
+
comparisons throw (`_count` is not supported inside Prisma `where`).
|
|
80
|
+
- Cross-model column comparisons throw (Prisma field references are same-model only).
|
|
81
|
+
This includes membership between an outer scalar column and a related collection column.
|
|
82
|
+
|
|
83
|
+
#### Database collation is an authorization invariant
|
|
84
|
+
|
|
85
|
+
Cerbos string comparisons are case-sensitive. Prisma delegates comparison semantics to the
|
|
86
|
+
database collation, so a case-insensitive or accent-insensitive collation can make a generated
|
|
87
|
+
authorization filter return rows that Cerbos would deny. Treat the database collation used by
|
|
88
|
+
mapped authorization columns as part of the policy contract:
|
|
89
|
+
|
|
90
|
+
- PostgreSQL: use a deterministic, case-sensitive collation and avoid `citext` or an
|
|
91
|
+
insensitive Prisma query mode for mapped fields.
|
|
92
|
+
- MySQL/MariaDB: choose a case-sensitive (`_cs`) or binary collation rather than the common
|
|
93
|
+
case-insensitive (`_ci`) defaults.
|
|
94
|
+
- SQL Server: use a case-sensitive (`_CS_`) collation rather than a case-insensitive (`_CI_`)
|
|
95
|
+
collation.
|
|
96
|
+
- SQLite: do not apply `COLLATE NOCASE` to mapped fields, and verify the exact comparison
|
|
97
|
+
behavior of any database configuration or extension used in production.
|
|
98
|
+
|
|
99
|
+
The adapter cannot override a column's collation in a Prisma `where` filter. See Prisma's
|
|
100
|
+
[case-sensitivity documentation](https://docs.prisma.io/docs/orm/v6/prisma-client/queries/case-sensitivity)
|
|
101
|
+
for provider-specific details.
|
|
102
|
+
|
|
103
|
+
### Conformance contract
|
|
104
|
+
|
|
105
|
+
The adapter is differentially tested against Cerbos PDP 0.54.0 `checkResource` decisions using 20 hostile seed rows and both Prisma 6 and 7. The Spring Data adapter defines the reference semantics for this compatibility snapshot.
|
|
106
|
+
|
|
107
|
+
| Classification | Coverage |
|
|
108
|
+
| --- | --- |
|
|
109
|
+
| Oracle-tested | 83 reference actions |
|
|
110
|
+
| Fail-closed | 31 reference actions plus the 3 reference-unsupported shapes (34 actions total) |
|
|
111
|
+
| Known planner divergence | `has()` on a missing attribute is folded by the Cerbos planner to `ALWAYS_ALLOWED`, while `checkResource` denies the missing-attribute rows. Until the planner is fixed, use `R.attr.x != null` for database-backed attributes instead of `has(R.attr.x)` |
|
|
112
|
+
|
|
113
|
+
The fail-closed set consists of literal `LIKE` cases Prisma cannot escape safely, cross-model field references, arbitrary relation counts and string lengths, `exists_one`, unsolved column arithmetic, sub-millisecond `now()` thresholds, and the reference probes for regex, ordered indexing, and `timestamp()` over a string field. Supported timestamp plans require a mapper entry with `valueType: "dateTime"` and a strict, millisecond-exact RFC 3339 literal in CEL's supported instant range. These shapes throw instead of producing a broader authorization filter.
|
|
40
114
|
|
|
41
115
|
## Requirements
|
|
42
116
|
|
|
@@ -46,7 +120,7 @@ An adapter library that takes a [Cerbos](https://cerbos.dev) Query Plan ([PlanRe
|
|
|
46
120
|
|
|
47
121
|
## System Requirements
|
|
48
122
|
|
|
49
|
-
- Node.js >=
|
|
123
|
+
- Node.js >= 22.0.
|
|
50
124
|
- Prisma CLI & Client >= 6.0 (v7 supported)
|
|
51
125
|
- A database supported by Prisma (SQLite/PostgreSQL/MySQL/etc.) so the Prisma client can communicate with stored data
|
|
52
126
|
|
|
@@ -432,9 +506,12 @@ The mapper configuration is also fully typed:
|
|
|
432
506
|
```ts
|
|
433
507
|
type MapperConfig = {
|
|
434
508
|
field?: string;
|
|
509
|
+
valueType?: "dateTime";
|
|
510
|
+
nullable?: boolean;
|
|
435
511
|
relation?: {
|
|
436
512
|
name: string;
|
|
437
513
|
type: "one" | "many";
|
|
514
|
+
model?: string;
|
|
438
515
|
field?: string;
|
|
439
516
|
fields?: {
|
|
440
517
|
[key: string]: MapperConfig; // Recursive for nested fields
|
|
@@ -474,4 +551,4 @@ A complete example application using this adapter can be found at [https://githu
|
|
|
474
551
|
|
|
475
552
|
## License
|
|
476
553
|
|
|
477
|
-
Apache 2.0
|
|
554
|
+
Apache 2.0.
|
package/lib/index.d.ts
CHANGED
|
@@ -1,11 +1,32 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { PlanKind } from "@cerbos/core";
|
|
2
|
+
import type { PlanResourcesResponse } from "@cerbos/core";
|
|
2
3
|
export { PlanKind };
|
|
3
4
|
export type PrismaFilter = Record<string, any>;
|
|
4
5
|
export type MapperConfig = {
|
|
5
6
|
field?: string;
|
|
7
|
+
/**
|
|
8
|
+
* Declares the database value kind when the Cerbos expression wraps a field in a typed
|
|
9
|
+
* constructor. Date-time metadata is required for timestamp(field) so a string column is
|
|
10
|
+
* never silently treated as a temporal column merely because it happens to contain ISO text.
|
|
11
|
+
*/
|
|
12
|
+
valueType?: "dateTime";
|
|
13
|
+
/**
|
|
14
|
+
* Marks the mapped column as nullable in the database. Cerbos treats a missing attribute as
|
|
15
|
+
* an evaluation error (deny), which matches SQL three-valued logic for simple predicates —
|
|
16
|
+
* but relation subqueries (some/every/none) collapse UNKNOWN to false at the EXISTS boundary,
|
|
17
|
+
* so collection macros over elements with NULL fields need an explicit guard to stay
|
|
18
|
+
* deny-aligned. Declaring nullability here enables those guards.
|
|
19
|
+
*/
|
|
20
|
+
nullable?: boolean;
|
|
6
21
|
relation?: {
|
|
7
22
|
name: string;
|
|
8
23
|
type: "one" | "many";
|
|
24
|
+
/**
|
|
25
|
+
* The Prisma model name of the related record (e.g. "Tag"). Required only for
|
|
26
|
+
* field-to-field comparisons between two columns of the related model, which compile to
|
|
27
|
+
* Prisma field references and need the model name as their container.
|
|
28
|
+
*/
|
|
29
|
+
model?: string;
|
|
9
30
|
field?: string;
|
|
10
31
|
fields?: Record<string, MapperConfig>;
|
|
11
32
|
};
|
|
@@ -14,6 +35,12 @@ export type Mapper = Record<string, MapperConfig> | ((key: string) => MapperConf
|
|
|
14
35
|
export interface QueryPlanToPrismaArgs {
|
|
15
36
|
queryPlan: PlanResourcesResponse;
|
|
16
37
|
mapper?: Mapper;
|
|
38
|
+
/**
|
|
39
|
+
* The Prisma model name the generated filter targets (e.g. "Resource"). Required only for
|
|
40
|
+
* field-to-field comparisons between two root columns, which compile to Prisma field
|
|
41
|
+
* references and need the model name as their container.
|
|
42
|
+
*/
|
|
43
|
+
model?: string;
|
|
17
44
|
}
|
|
18
45
|
export type QueryPlanToPrismaResult = {
|
|
19
46
|
kind: PlanKind.ALWAYS_ALLOWED | PlanKind.ALWAYS_DENIED;
|
|
@@ -24,5 +51,5 @@ export type QueryPlanToPrismaResult = {
|
|
|
24
51
|
/**
|
|
25
52
|
* Converts a Cerbos query plan to a Prisma filter.
|
|
26
53
|
*/
|
|
27
|
-
export declare function queryPlanToPrisma({ queryPlan, mapper, }: QueryPlanToPrismaArgs): QueryPlanToPrismaResult;
|
|
54
|
+
export declare function queryPlanToPrisma({ queryPlan, mapper, model, }: QueryPlanToPrismaArgs): QueryPlanToPrismaResult;
|
|
28
55
|
//# sourceMappingURL=index.d.ts.map
|
package/lib/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AACxC,OAAO,KAAK,EACV,qBAAqB,EAGtB,MAAM,cAAc,CAAC;AAEtB,OAAO,EAAE,QAAQ,EAAE,CAAC;AAqGpB,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;AAE/C,MAAM,MAAM,YAAY,GAAG;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;OAIG;IACH,SAAS,CAAC,EAAE,UAAU,CAAC;IACvB;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE;QACT,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,KAAK,GAAG,MAAM,CAAC;QACrB;;;;WAIG;QACH,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;KACvC,CAAC;CACH,CAAC;AAEF,MAAM,MAAM,MAAM,GACd,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,GAC5B,CAAC,CAAC,GAAG,EAAE,MAAM,KAAK,YAAY,CAAC,CAAC;AAEpC,MAAM,WAAW,qBAAqB;IACpC,SAAS,EAAE,qBAAqB,CAAC;IACjC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,MAAM,uBAAuB,GAC/B;IACE,IAAI,EAAE,QAAQ,CAAC,cAAc,GAAG,QAAQ,CAAC,aAAa,CAAC;CACxD,GACD;IACE,IAAI,EAAE,QAAQ,CAAC,WAAW,CAAC;IAC3B,OAAO,EAAE,YAAY,CAAC;CACvB,CAAC;AAoZN;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,EAChC,SAAS,EACT,MAAW,EACX,KAAK,GACN,EAAE,qBAAqB,GAAG,uBAAuB,CAgCjD"}
|