@prisma/security-rules 0.3.6

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 ADDED
@@ -0,0 +1,222 @@
1
+ # @prisma/security-rules
2
+
3
+ ## Permission rules for Prisma Postgres
4
+
5
+ Prisma Security Rules is a **permission rules system for Prisma ORM and Prisma Postgres**. With Security Rules, you can:
6
+
7
+ - define access permission rules in TypeScript
8
+ - deploy the permission rules to your Prisma Postgres instance
9
+ - query Prisma Postgres with the permission rules applied
10
+ - access Prisma Postgres from the frontend
11
+
12
+ At the core of it, you define rules that allow/deny requests to your database based on model name, operation (e.g. `findMany`) and the parameters you pass to said operation.
13
+
14
+ ## Usage
15
+
16
+ ### Prerequisites
17
+
18
+ - A project with Prisma ORM (>=6.2.0) installed and a Prisma Postgres database. If you don’t have one, you can get started [here](https://www.prisma.io/docs/getting-started/prisma-postgres/from-the-cli?utm_source=readme&utm_medium=rules_extension).
19
+
20
+ - The Prisma CLI being authenticated with Prisma Data Platform (via [`prisma platform auth login --early-access`](https://www.prisma.io/docs/platform/platform-cli/commands?utm_source=readme&utm_medium=rules_extension#auth-login)).
21
+
22
+ ### 1. Install the Prisma Client extension for Policy
23
+
24
+ ```shell
25
+ npm install @prisma/security-rules
26
+ ```
27
+
28
+ <details>
29
+ <summary>
30
+ Expand to see installation instructions for `pnpm`, `yarn` and `bun`
31
+ </summary>
32
+
33
+ #### pnpm
34
+
35
+ ```shell
36
+ pnpm add @prisma/security-rules
37
+ ```
38
+
39
+ #### Yarn
40
+
41
+ ```shell
42
+ yarn add @prisma/security-rules
43
+ ```
44
+
45
+ #### Bun
46
+
47
+ ```shell
48
+ bun install @prisma/security-rules
49
+ ```
50
+
51
+ </details>
52
+
53
+ ### 2. Extend your `PrismaClient` with the Policy extension
54
+
55
+ First, import the extension and add it to your existing Prisma Client:
56
+
57
+ ```ts
58
+ import { PrismaClient } from "@prisma/client";
59
+ import { withPolicy } from "@prisma/security-rules";
60
+ import { z } from "zod";
61
+
62
+ import { decode } from "path/to/decode"; // imaginary import of your jwt token decode helper
63
+
64
+ export const prisma = new PrismaClient().$extends(
65
+ withPolicy({
66
+ contextSchema: z.object({
67
+ token: z.string(),
68
+ }),
69
+ rules: {
70
+ user: {
71
+ async read(req) {
72
+ // On each request, the context object can be used to perform custom auth/access business logic. We know it's shape thanks to the `contextSchema` property.
73
+ const { userId } = await decode(req.context.token);
74
+
75
+ return {
76
+ $where: { id: userId },
77
+ };
78
+ },
79
+ $allOperations: false,
80
+ },
81
+ $allModels: false,
82
+ $transaction: false,
83
+ },
84
+ }),
85
+ );
86
+ ```
87
+
88
+ > Note: While the rules are applied to your `PrismaClient` instance, this instance will retain _full_ database access.
89
+
90
+ This example uses `zod`, but you can use any schema library that implements the [standard schema](https://github.com/standard-schema/standard-schema#what-schema-libraries-implement-the-spec) specification.
91
+
92
+ This example includes Policy rules that will deny all requests except for reads (e.g. `findMany`) on `user` model. It also ensures a user only sees their own data by applying a `where` override (effectively `AND(incoming_query_where, rule_$where)`) that filters by the user's id (decoded from the session token).
93
+
94
+ ### 3. Create a file exporting the `PolicyClient` for your application
95
+
96
+ ```ts
97
+ import { PolicyClient } from "@prisma/security-rules";
98
+
99
+ // it is very important to not forget the `type` annotation here, to tell TypeScript to only import the types!!!
100
+ import type { prisma as rpcClient } from "path/to/file/where/rules/are/used";
101
+
102
+ export const prisma = new PolicyClient<typeof rpcClient>({
103
+ publicKey: "<TBD>",
104
+ });
105
+ ```
106
+
107
+ ### 4. Deploy Policy rules
108
+
109
+ ```shell
110
+ prisma rules deploy <rules_name> -f path/to/file/where/rules/are/used
111
+ ```
112
+
113
+ Be sure to copy the public key from the deploy command's output in your terminal, and replace the `<TBD>` from the previous step.
114
+
115
+ ### 5. Use `PolicyClient` to access Prisma Postgres
116
+
117
+ `PolicyClient` is a lightweight version of `PrismaClient` that you can use in your browser. Once your rules are deployed, you can use it as follows in your application:
118
+
119
+ ```ts
120
+ import { prisma } from "path/to/rules-client";
121
+
122
+ prisma.setGlobalContext({ token: "<some_session_token>" });
123
+
124
+ const users = await prisma.users.findMany();
125
+ ```
126
+
127
+ ## How it works
128
+
129
+ This package exposes:
130
+
131
+ - A [Prisma Client extension](https://www.prisma.io/docs/orm/prisma-client/client-extensions) called `withPolicy` that helps attach Prisma Policy-related configuration to an existing Prisma ORM client, making it suitable to act as a Remote Procedure Call (RPC) called object once bundled and deployed to Prisma Policy's workers.
132
+
133
+ - A lightweight Remote Procedure Call (RPC) stub called `PolicyClient` that allows your application to execute Prisma ORM queries and transactions remotely on Prisma Policy's workers in a secure way (based on your Policy configuration).
134
+
135
+ The deployment logic is not part of this package, and is accessible via the Prisma CLI through `prisma rules`. A deployment's output will include a public key you'll need to pass to `PolicyClient`.
136
+
137
+ Here's a simplified high-level view of Policy's architecture:
138
+
139
+ ![Prisma Policy's RPC in excalidraw](https://www.unpkg.com/@prisma/security-rules@latest/assets/rpc.png)
140
+
141
+ ### Rule Engine
142
+
143
+ Each request, that is received by Policy's workers, will be evaluated by a rule engine:
144
+
145
+ - if a request does not adhere to your rules, it will be denied with a reason, and the `PolicyClient` will throw an error.
146
+
147
+ - if a request adheres to your rules, it will be executed with the `PrismaClient` and the results will be sent to the `PolicyClient`.
148
+
149
+ #### Rules
150
+
151
+ Rules are defined with a JavaScript plain nested object.
152
+
153
+ The root object is defined as:
154
+
155
+ ```ts
156
+ {
157
+ // model name must exist in your `PrismaClient`'s schema.
158
+ // when your schema changes, make sure to redeploy your rules,
159
+ // to avoid instant denies from the engine for requests that access new models.
160
+ [modelName: string]?: ModelRules;
161
+ // fallback in case the request's model name has no specific rules.
162
+ $allModels?: ModelRules;
163
+ // allow or deny transactions.
164
+ $transaction?: boolean;
165
+ }
166
+ ```
167
+
168
+ Model rules are defined as follows:
169
+
170
+ ```ts
171
+ type ModelRules =
172
+ // simple allow/deny toggle.
173
+ | boolean
174
+ // more nuanced rules.
175
+ | {
176
+ // rules for all create operations.
177
+ create?: OperationRules;
178
+ // rules for all read operations.
179
+ read?: OperationRules;
180
+ // rules for all update operations.
181
+ update?: OperationRules;
182
+ // rules for all delete operations.
183
+ delete?: OperationRules;
184
+ // fallback in case the request's operation group doesn't have specific rules.
185
+ $allOperations?: OperationRules;
186
+ // will deny requests if at least one of these fields is in the request's
187
+ // arguments or is returned by the query. If an operation has specific
188
+ // $blockedFields, they will override this property.
189
+ // Example: ['password']
190
+ $blockedFields?: string[];
191
+ };
192
+ ```
193
+
194
+ Operation rules are best described by the following diagram:
195
+
196
+ ![Prisma Policy's operation rules "onion"](https://www.unpkg.com/@prisma/security-rules@latest/assets/operation-rules.png)
197
+
198
+ It's like an 🧅.
199
+
200
+ `$where` overrides the incoming request's filtering by `AND`ing any existing filters with it.
201
+
202
+ The callback's `req` argument is an object that includes the model name, operation, args object, and the global context object.
203
+
204
+ `$rule` is a verbose way of defining both an access rule and additional rules/logic for a specific operation:
205
+
206
+ - `$before` allows running custom code before the query is executed by `PrismaClient`.
207
+
208
+ - `$after` allows running custom code after the query is executed by `PrismaClient`.
209
+
210
+ - `$blockedFields` behaves similarly to the one that can be defined at the model level, but overrides it if it exists.
211
+
212
+ #### Rule Evaluation
213
+
214
+ Here's a diagram depicting the rule engine's evaluation process in a simplified manner:
215
+
216
+ ![Prisma Policy's rule engine algorithm](https://www.unpkg.com/@prisma/security-rules@latest/assets/rule-engine.png)
217
+
218
+ The rule engine is designed to introduce as little overhead as possible. As soon as a rule is not adhered by some check, the request is denied. Meaning, it doesn't try and run additional checks to collect more deny reasons.
219
+
220
+ ## Need help?
221
+
222
+ If you need assistance, reach out in the #help-and-questions channel on our [Discord](https://pris.ly/discord), or connect with our community to see how others are using Policy.
Binary file
package/assets/rpc.png ADDED
Binary file
Binary file