easy-psql-rbac 1.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 ADDED
@@ -0,0 +1,303 @@
1
+ # easy-psql-rbac
2
+
3
+ Role-based access control (RBAC) layer for [easy-psql](https://github.com/Dimitris-Tzilopoylos/easy-psql). Intercepts queries and enforces fine-grained permissions — column filtering, ownership rules, and server-side pre-conditions — based on the authenticated user's role.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install easy-psql-rbac
9
+ ```
10
+
11
+ `easy-psql` is a peer dependency and must be installed separately:
12
+
13
+ ```bash
14
+ npm install easy-psql
15
+ ```
16
+
17
+ ## Quick Start
18
+
19
+ ```typescript
20
+ import { EasyPSQLRBAC } from "easy-psql-rbac";
21
+
22
+ const rbac = new EasyPSQLRBAC();
23
+
24
+ // Define roles
25
+ rbac.withRole("viewer", (role) =>
26
+ role
27
+ .findMany("public", "posts", { columns: ["id", "title", "body"] })
28
+ .findOne("public", "posts", { columns: ["id", "title", "body"] })
29
+ );
30
+
31
+ rbac.withRole("author", (role) =>
32
+ role
33
+ .findMany("public", "posts", {
34
+ columns: ["id", "title", "body", "author_id"],
35
+ ownership: { enabled: true, columns: ["author_id"] },
36
+ })
37
+ .createOne("public", "posts", {
38
+ columns: ["title", "body", "author_id"],
39
+ preConditions: { input: { author_id: null } }, // auto-set to user identity
40
+ })
41
+ .updateOne("public", "posts", {
42
+ columns: ["title", "body"],
43
+ ownership: { enabled: true, columns: ["author_id"] },
44
+ })
45
+ .deleteOne("public", "posts", {
46
+ ownership: { enabled: true, columns: ["author_id"] },
47
+ })
48
+ );
49
+
50
+ // Use with a request
51
+ const user = { id: "user-123", roleId: "author" };
52
+
53
+ const model = rbac.findManyModel({
54
+ schema: "public",
55
+ table: "posts",
56
+ user,
57
+ query: { select: { id: true, title: true }, where: {} },
58
+ });
59
+ // Executes with ownership filter: WHERE author_id = 'user-123'
60
+ ```
61
+
62
+ ## Core Concepts
63
+
64
+ ### Roles and Permissions
65
+
66
+ Permissions are defined per **schema + table + operation**. Each permission entry controls:
67
+
68
+ - **`columns`** — whitelist of columns the role may read or write
69
+ - **`ownership`** — automatically scopes queries to the current user's rows
70
+ - **`preConditions`** — server-side conditions injected at query time
71
+
72
+ ### Ownership Filtering
73
+
74
+ When `ownership.enabled` is `true`, the library automatically appends a `WHERE` condition matching the specified columns to the current user's identity:
75
+
76
+ ```typescript
77
+ ownership: {
78
+ enabled: true,
79
+ columns: ["author_id"], // column(s) to filter on
80
+ columnToUserFieldMapper: { author_id: "id" }, // which user field to match (default: userIdentityKey)
81
+ }
82
+ ```
83
+
84
+ For inserts, ownership columns are automatically set to the current user's identity value.
85
+
86
+ ### Pre-conditions
87
+
88
+ Pre-conditions are server-side rules that cannot be overridden by clients:
89
+
90
+ ```typescript
91
+ preConditions: {
92
+ where: { is_published: { _eq: true } }, // appended to every query's WHERE
93
+ input: { tenant_id: "acme" }, // merged into every INSERT/UPDATE body
94
+ }
95
+ ```
96
+
97
+ ## API Reference
98
+
99
+ ### `new EasyPSQLRBAC(options?)`
100
+
101
+ | Option | Type | Default | Description |
102
+ |--------|------|---------|-------------|
103
+ | `userIdentityKey` | `string` | `"id"` | Field on the user object used for ownership matching |
104
+
105
+ ```typescript
106
+ const rbac = new EasyPSQLRBAC({ userIdentityKey: "userId" });
107
+ ```
108
+
109
+ ---
110
+
111
+ ### Role Definition
112
+
113
+ #### `rbac.withRole(id, callback)`
114
+
115
+ Registers or replaces a role. The callback receives a `RoleConfig` builder and must return it.
116
+
117
+ ```typescript
118
+ rbac.withRole("admin", (role) =>
119
+ role
120
+ .findMany("public", "users", { columns: ["id", "email", "name"] })
121
+ .updateOne("public", "users", { columns: ["email", "name"] })
122
+ );
123
+ ```
124
+
125
+ #### `rbac.upsertRole(roleConfig)`
126
+
127
+ Upserts a pre-built `RoleConfig` instance.
128
+
129
+ #### `rbac.deleteRole(id)`
130
+
131
+ Removes a role from the registry.
132
+
133
+ ---
134
+
135
+ ### `RoleConfig` Builder Methods
136
+
137
+ Each method takes `(schema: string, table: string, permissions: EntityPermissions)` and returns the builder for chaining.
138
+
139
+ | Method | Operation |
140
+ |--------|-----------|
141
+ | `.findMany(schema, table, permissions)` | SELECT multiple rows |
142
+ | `.findOne(schema, table, permissions)` | SELECT single row |
143
+ | `.aggregate(schema, table, permissions)` | Aggregate queries |
144
+ | `.createOne(schema, table, permissions)` | INSERT single row |
145
+ | `.createMany(schema, table, permissions)` | INSERT multiple rows |
146
+ | `.updateOne(schema, table, permissions)` | UPDATE single row |
147
+ | `.updateMany(schema, table, permissions)` | UPDATE multiple rows |
148
+ | `.deleteOne(schema, table, permissions)` | DELETE single row |
149
+ | `.deleteMany(schema, table, permissions)` | DELETE multiple rows |
150
+
151
+ ---
152
+
153
+ ### Query Model Methods
154
+
155
+ Each model method sanitizes the incoming query/body against the role's permissions and returns an object ready to pass to the `easy-psql` engine.
156
+
157
+ ```typescript
158
+ rbac.findManyModel({ schema, table, user, query?, connection?, bypass? })
159
+ rbac.findOneModel({ schema, table, user, query?, connection?, bypass? })
160
+ rbac.aggregateModel({ schema, table, user, query?, connection?, bypass? })
161
+ rbac.createOneModel({ schema, table, user, body?, connection?, bypass? })
162
+ rbac.createManyModel({ schema, table, user, body?, connection?, bypass? })
163
+ rbac.updateOneModel({ schema, table, user, input?, connection?, bypass? })
164
+ rbac.updateManyModel({ schema, table, user, input?, connection?, bypass? })
165
+ rbac.deleteOneModel({ schema, table, user, query?, connection?, bypass? })
166
+ rbac.deleteManyModel({ schema, table, user, query?, connection?, bypass? })
167
+ ```
168
+
169
+ | Parameter | Type | Description |
170
+ |-----------|------|-------------|
171
+ | `schema` | `string` | PostgreSQL schema name |
172
+ | `table` | `string` | Table name |
173
+ | `user` | `User` | Authenticated user object with a `roleId` field |
174
+ | `bypass` | `boolean` | Skip all RBAC checks (use for internal/admin operations) |
175
+ | `connection` | any | Optional database connection override |
176
+
177
+ ---
178
+
179
+ ### `EntityPermissions` Shape
180
+
181
+ ```typescript
182
+ interface EntityPermissions {
183
+ columns?: string[];
184
+
185
+ ownership?: {
186
+ enabled?: boolean;
187
+ columns?: string[];
188
+ columnToUserFieldMapper?: Record<string, string>;
189
+ };
190
+
191
+ preConditions?: {
192
+ where?: Record<string, any>;
193
+ input?: Record<string, any>;
194
+ };
195
+ }
196
+ ```
197
+
198
+ ---
199
+
200
+ ### Errors
201
+
202
+ | Class | HTTP Status | Thrown When |
203
+ |-------|-------------|-------------|
204
+ | `ForbiddenError` | 403 | User has no role, role doesn't exist, or role lacks permission for the operation |
205
+ | `BadRequest` | 400 | Query contains columns or conditions the role is not allowed to use |
206
+
207
+ ```typescript
208
+ import { ForbiddenError, BadRequest } from "easy-psql-rbac";
209
+
210
+ try {
211
+ const model = rbac.findManyModel({ schema: "public", table: "users", user });
212
+ } catch (err) {
213
+ if (err instanceof ForbiddenError) {
214
+ // respond 403
215
+ }
216
+ if (err instanceof BadRequest) {
217
+ // respond 400
218
+ }
219
+ }
220
+ ```
221
+
222
+ ---
223
+
224
+ ## Advanced Usage
225
+
226
+ ### Multi-column Ownership
227
+
228
+ ```typescript
229
+ rbac.withRole("member", (role) =>
230
+ role.findMany("public", "documents", {
231
+ columns: ["id", "title", "org_id", "owner_id"],
232
+ ownership: {
233
+ enabled: true,
234
+ columns: ["owner_id"],
235
+ columnToUserFieldMapper: { owner_id: "userId" },
236
+ },
237
+ })
238
+ );
239
+ ```
240
+
241
+ ### Forced Server-side Values on Insert
242
+
243
+ ```typescript
244
+ rbac.withRole("tenant-user", (role) =>
245
+ role.createOne("public", "records", {
246
+ columns: ["name", "data", "tenant_id"],
247
+ preConditions: {
248
+ input: { tenant_id: "fixed-tenant-id" }, // client cannot override this
249
+ },
250
+ })
251
+ );
252
+ ```
253
+
254
+ ### Scoped Read with Pre-condition WHERE
255
+
256
+ ```typescript
257
+ rbac.withRole("moderator", (role) =>
258
+ role.findMany("public", "reports", {
259
+ columns: ["id", "content", "status"],
260
+ preConditions: {
261
+ where: { status: { _eq: "pending" } },
262
+ },
263
+ })
264
+ );
265
+ ```
266
+
267
+ ### Bypass for Internal Operations
268
+
269
+ ```typescript
270
+ const model = rbac.deleteOneModel({
271
+ schema: "public",
272
+ table: "sessions",
273
+ user: adminUser,
274
+ bypass: true,
275
+ query: { where: { expired: { _eq: true } } },
276
+ });
277
+ ```
278
+
279
+ ---
280
+
281
+ ## TypeScript
282
+
283
+ The package ships with full TypeScript declarations. All types are exported from the main entry point:
284
+
285
+ ```typescript
286
+ import {
287
+ EasyPSQLRBAC,
288
+ RoleRegistry,
289
+ RoleConfig,
290
+ ForbiddenError,
291
+ BadRequest,
292
+ AllowedEngineApiAccessTypes,
293
+ type EntityPermissions,
294
+ type RolePermissions,
295
+ type User,
296
+ } from "easy-psql-rbac";
297
+ ```
298
+
299
+ ---
300
+
301
+ ## License
302
+
303
+ ISC
@@ -0,0 +1,5 @@
1
+ export declare class BadRequest extends Error {
2
+ readonly status = 400;
3
+ constructor(message?: string);
4
+ }
5
+ //# sourceMappingURL=badrequest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"badrequest.d.ts","sourceRoot":"","sources":["../src/badrequest.ts"],"names":[],"mappings":"AAAA,qBAAa,UAAW,SAAQ,KAAK;IACnC,QAAQ,CAAC,MAAM,OAAO;gBACV,OAAO,CAAC,EAAE,MAAM;CAG7B"}
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BadRequest = void 0;
4
+ class BadRequest extends Error {
5
+ constructor(message) {
6
+ super(message);
7
+ this.status = 400;
8
+ }
9
+ }
10
+ exports.BadRequest = BadRequest;
11
+ //# sourceMappingURL=badrequest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"badrequest.js","sourceRoot":"","sources":["../src/badrequest.ts"],"names":[],"mappings":";;;AAAA,MAAa,UAAW,SAAQ,KAAK;IAEnC,YAAY,OAAgB;QAC1B,KAAK,CAAC,OAAO,CAAC,CAAC;QAFR,WAAM,GAAG,GAAG,CAAC;IAGtB,CAAC;CACF;AALD,gCAKC"}
@@ -0,0 +1,5 @@
1
+ export declare class ForbiddenError extends Error {
2
+ status: 403;
3
+ constructor(message?: string);
4
+ }
5
+ //# sourceMappingURL=forbidden.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"forbidden.d.ts","sourceRoot":"","sources":["../src/forbidden.ts"],"names":[],"mappings":"AAAA,qBAAa,cAAe,SAAQ,KAAK;IACvC,MAAM,EAAE,GAAG,CAAC;gBACA,OAAO,CAAC,EAAE,MAAM;CAI7B"}
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ForbiddenError = void 0;
4
+ class ForbiddenError extends Error {
5
+ constructor(message) {
6
+ super(message);
7
+ this.status = 403;
8
+ }
9
+ }
10
+ exports.ForbiddenError = ForbiddenError;
11
+ //# sourceMappingURL=forbidden.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"forbidden.js","sourceRoot":"","sources":["../src/forbidden.ts"],"names":[],"mappings":";;;AAAA,MAAa,cAAe,SAAQ,KAAK;IAEvC,YAAY,OAAgB;QAC1B,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,MAAM,GAAG,GAAG,CAAC;IACpB,CAAC;CACF;AAND,wCAMC"}
@@ -0,0 +1,6 @@
1
+ export * from "./rbac";
2
+ export * from "./badrequest";
3
+ export * from "./forbidden";
4
+ export * from "./types";
5
+ export * from "./roleRegistry";
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,QAAQ,CAAC;AACvB,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC;AAC5B,cAAc,SAAS,CAAC;AACxB,cAAc,gBAAgB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./rbac"), exports);
18
+ __exportStar(require("./badrequest"), exports);
19
+ __exportStar(require("./forbidden"), exports);
20
+ __exportStar(require("./types"), exports);
21
+ __exportStar(require("./roleRegistry"), exports);
22
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,yCAAuB;AACvB,+CAA6B;AAC7B,8CAA4B;AAC5B,0CAAwB;AACxB,iDAA+B"}
package/dist/rbac.d.ts ADDED
@@ -0,0 +1,152 @@
1
+ import { AllowedEngineApiAccessTypes, EntityPermissions, Model, RBACOptions, Relation, User } from "./types";
2
+ import { RoleRegistry } from "./roleRegistry";
3
+ export declare class EasyPSQLRBAC extends RoleRegistry {
4
+ options: RBACOptions;
5
+ constructor(options?: RBACOptions);
6
+ private assertUserIdentityKey;
7
+ private model;
8
+ roleBasedModel({ table, schema, connection, apiAccessType, bypass, input, user, }: {
9
+ table: string;
10
+ schema: string;
11
+ connection?: any;
12
+ apiAccessType: AllowedEngineApiAccessTypes;
13
+ bypass?: boolean;
14
+ input?: any;
15
+ user?: User;
16
+ }): any;
17
+ getUserRolePermissionsForEntity({ schema, table, user, apiAccessType, }: {
18
+ schema: string;
19
+ table: string;
20
+ user?: User;
21
+ apiAccessType: AllowedEngineApiAccessTypes;
22
+ }): EntityPermissions;
23
+ roleBasedSelectSanitization({ model, input, apiAccessType, user, entityPermissions, }: {
24
+ model: Model;
25
+ input: any;
26
+ apiAccessType: AllowedEngineApiAccessTypes;
27
+ user?: User;
28
+ entityPermissions: EntityPermissions;
29
+ }): void;
30
+ roleBasedInsertSanitization({ model, input, apiAccessType, user, entityPermissions, }: {
31
+ model: Model;
32
+ input: any;
33
+ apiAccessType: AllowedEngineApiAccessTypes;
34
+ user?: User;
35
+ entityPermissions: EntityPermissions;
36
+ }): void;
37
+ roleBasedUpdateSanitization({ model, input, apiAccessType, user, entityPermissions, }: {
38
+ model: Model;
39
+ input: any;
40
+ apiAccessType: AllowedEngineApiAccessTypes;
41
+ user?: User;
42
+ entityPermissions: EntityPermissions;
43
+ }): void;
44
+ roleBasedWhereSanitization({ model, where, apiAccessType, user, entityPermissions, }: {
45
+ model: Model;
46
+ where: any;
47
+ apiAccessType: AllowedEngineApiAccessTypes;
48
+ user?: User;
49
+ entityPermissions: EntityPermissions;
50
+ }): void;
51
+ roleBasedDeleteSanitization({ model, input, apiAccessType, user, entityPermissions, }: {
52
+ model: Model;
53
+ input: any;
54
+ apiAccessType: AllowedEngineApiAccessTypes;
55
+ user?: User;
56
+ entityPermissions: EntityPermissions;
57
+ }): void;
58
+ isColumnInRelationColumns({ relation, column, }: {
59
+ relation: Relation;
60
+ column: string;
61
+ }): boolean;
62
+ fastDeepClone<T = any>(obj: T): T;
63
+ mergeWhereWithPreConditions({ where, preConditions, }: {
64
+ where: any;
65
+ preConditions: any;
66
+ }): any;
67
+ findManyModel({ schema, table, connection, bypass, query, user, }: {
68
+ schema: string;
69
+ table: string;
70
+ connection?: any;
71
+ bypass?: boolean;
72
+ query?: any;
73
+ user?: User;
74
+ }): any;
75
+ findOneModel({ schema, table, connection, bypass, query, user, }: {
76
+ schema: string;
77
+ table: string;
78
+ connection?: any;
79
+ bypass?: boolean;
80
+ query?: any;
81
+ user?: User;
82
+ }): any;
83
+ aggregateModel({ schema, table, connection, bypass, query, user, }: {
84
+ schema: string;
85
+ table: string;
86
+ connection?: any;
87
+ bypass?: boolean;
88
+ query?: any;
89
+ user?: User;
90
+ }): any;
91
+ createManyModel({ schema, table, connection, bypass, body, user, }: {
92
+ schema: string;
93
+ table: string;
94
+ connection?: any;
95
+ bypass?: boolean;
96
+ body: any;
97
+ user?: User;
98
+ }): any;
99
+ createOneModel({ schema, table, connection, bypass, body, user, }: {
100
+ schema: string;
101
+ table: string;
102
+ connection?: any;
103
+ bypass?: boolean;
104
+ body: any;
105
+ user?: User;
106
+ }): any;
107
+ updateManyModel({ schema, table, connection, bypass, input, user, }: {
108
+ schema: string;
109
+ table: string;
110
+ connection?: any;
111
+ bypass?: boolean;
112
+ input: {
113
+ update: any;
114
+ where?: any;
115
+ };
116
+ user?: User;
117
+ }): any;
118
+ updateOneModel({ schema, table, connection, bypass, input, user, }: {
119
+ schema: string;
120
+ table: string;
121
+ connection?: any;
122
+ bypass?: boolean;
123
+ input: {
124
+ update: any;
125
+ where?: any;
126
+ };
127
+ user?: User;
128
+ }): any;
129
+ deleteManyModel({ schema, table, connection, bypass, input, user, }: {
130
+ schema: string;
131
+ table: string;
132
+ connection?: any;
133
+ bypass?: boolean;
134
+ input: {
135
+ where?: any;
136
+ };
137
+ user?: User;
138
+ }): any;
139
+ deleteOneModel({ schema, table, connection, bypass, input, user, }: {
140
+ schema: string;
141
+ table: string;
142
+ connection?: any;
143
+ bypass?: boolean;
144
+ input: {
145
+ where?: any;
146
+ };
147
+ user?: User;
148
+ }): any;
149
+ private toUserOwnershipColumnValueDefault;
150
+ private toUserOwnershipColumnNameMapper;
151
+ }
152
+ //# sourceMappingURL=rbac.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rbac.d.ts","sourceRoot":"","sources":["../src/rbac.ts"],"names":[],"mappings":"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"}