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/dist/rbac.js ADDED
@@ -0,0 +1,745 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.EasyPSQLRBAC = void 0;
7
+ const badrequest_1 = require("./badrequest");
8
+ const forbidden_1 = require("./forbidden");
9
+ const types_1 = require("./types");
10
+ const easy_validation_service_1 = __importDefault(require("easy-validation-service"));
11
+ const easy_psql_1 = require("easy-psql");
12
+ const roleRegistry_1 = require("./roleRegistry");
13
+ class EasyPSQLRBAC extends roleRegistry_1.RoleRegistry {
14
+ constructor(options) {
15
+ super();
16
+ this.options = options || {};
17
+ if (!this.options.userIdentityKey) {
18
+ this.options.userIdentityKey = "id";
19
+ }
20
+ this.assertUserIdentityKey();
21
+ }
22
+ assertUserIdentityKey() {
23
+ if (typeof this.options.userIdentityKey !== "string" ||
24
+ !this.options.userIdentityKey.trim().length) {
25
+ throw new Error(`Please provide a userIdentityKey in options`);
26
+ }
27
+ }
28
+ model({ schema, table, connection, }) {
29
+ return new easy_psql_1.DB.modelFactory[schema][table](connection);
30
+ }
31
+ roleBasedModel({ table, schema, connection, apiAccessType, bypass, input, user, }) {
32
+ if (bypass) {
33
+ return this.model({ schema, table, connection });
34
+ }
35
+ const entityPermissions = this.getRolePermissions(user === null || user === void 0 ? void 0 : user.roleId)
36
+ .schema(schema)
37
+ .table(table)[apiAccessType]();
38
+ const { columns = [] } = entityPermissions;
39
+ if (!(columns === null || columns === void 0 ? void 0 : columns.length)) {
40
+ throw new forbidden_1.ForbiddenError(`${schema}.${table}: operation ${apiAccessType} is not allowed`);
41
+ }
42
+ const model = this.model({ table, schema, connection });
43
+ switch (apiAccessType) {
44
+ case types_1.AllowedEngineApiAccessTypes.findMany:
45
+ case types_1.AllowedEngineApiAccessTypes.findOne:
46
+ case types_1.AllowedEngineApiAccessTypes.aggregate:
47
+ this.roleBasedSelectSanitization({
48
+ user,
49
+ model,
50
+ input,
51
+ entityPermissions,
52
+ apiAccessType,
53
+ });
54
+ break;
55
+ case types_1.AllowedEngineApiAccessTypes.createOne:
56
+ case types_1.AllowedEngineApiAccessTypes.createMany:
57
+ this.roleBasedInsertSanitization({
58
+ user,
59
+ model,
60
+ input,
61
+ apiAccessType,
62
+ entityPermissions,
63
+ });
64
+ break;
65
+ case types_1.AllowedEngineApiAccessTypes.updateOne:
66
+ case types_1.AllowedEngineApiAccessTypes.updateMany:
67
+ this.roleBasedUpdateSanitization({
68
+ user,
69
+ model,
70
+ input,
71
+ apiAccessType,
72
+ entityPermissions,
73
+ });
74
+ break;
75
+ case types_1.AllowedEngineApiAccessTypes.deleteOne:
76
+ case types_1.AllowedEngineApiAccessTypes.deleteMany:
77
+ this.roleBasedDeleteSanitization({
78
+ user,
79
+ model,
80
+ input,
81
+ apiAccessType,
82
+ entityPermissions,
83
+ });
84
+ break;
85
+ default:
86
+ throw new forbidden_1.ForbiddenError(`${schema}.${table}: operation ${apiAccessType} is not allowed`);
87
+ }
88
+ return model;
89
+ }
90
+ getUserRolePermissionsForEntity({ schema, table, user, apiAccessType, }) {
91
+ return this.getRolePermissions(user === null || user === void 0 ? void 0 : user.roleId)
92
+ .schema(schema)
93
+ .table(table)[apiAccessType]();
94
+ }
95
+ roleBasedSelectSanitization({ model, input, apiAccessType, user, entityPermissions, }) {
96
+ var _a, _b, _c;
97
+ if (!easy_validation_service_1.default.isObject(input)) {
98
+ throw new badrequest_1.BadRequest(`query must be an object for operation ${apiAccessType}`);
99
+ }
100
+ const { columns, ownership, preConditions } = entityPermissions;
101
+ if (!Array.isArray(columns) || !columns.length) {
102
+ throw new forbidden_1.ForbiddenError(`${model.schema}.${model.table}: operation ${apiAccessType} is not allowed`);
103
+ }
104
+ if (easy_validation_service_1.default.isObject(input.select) &&
105
+ Object.keys(input.select).length > 0) {
106
+ for (const key of Object.keys(input.select)) {
107
+ if (!input.select[key] || !columns.includes(key)) {
108
+ delete input.select[key];
109
+ }
110
+ }
111
+ }
112
+ else {
113
+ input.select = (columns || []).reduce((acc, col) => {
114
+ acc[col] = true;
115
+ return acc;
116
+ }, {});
117
+ }
118
+ //sanitize order by
119
+ if (easy_validation_service_1.default.isObject(input.orderBy)) {
120
+ const iter = Object.entries(input.orderBy);
121
+ for (const [key, value] of iter) {
122
+ if (model.columns[key] && !columns.includes(key)) {
123
+ throw new forbidden_1.ForbiddenError(`Operation orderBy.${key} is not allowed`);
124
+ }
125
+ else if (easy_psql_1.DB.getIsAggregate(key)) {
126
+ const relation = model.relations[easy_psql_1.DB.getRelationNameWithoutAggregate(key)];
127
+ if (!relation ||
128
+ !columns.some((col) => this.isColumnInRelationColumns({ relation, column: col }))) {
129
+ throw new forbidden_1.ForbiddenError(`operation orderBy.${key} is not allowed`);
130
+ }
131
+ const relatedModel = easy_psql_1.DB.getRelatedModel(relation);
132
+ if (!relatedModel) {
133
+ throw new forbidden_1.ForbiddenError(`operation ${apiAccessType} is not allowed`);
134
+ }
135
+ const relatedEntityPermissions = this.getUserRolePermissionsForEntity({
136
+ schema: relatedModel.schema || easy_psql_1.DB.database,
137
+ table: relatedModel.table,
138
+ user,
139
+ apiAccessType,
140
+ });
141
+ if (!((_a = relatedEntityPermissions.columns) === null || _a === void 0 ? void 0 : _a.length)) {
142
+ throw new forbidden_1.ForbiddenError(`${relatedModel.schema}.${relatedModel.table}: operation ${apiAccessType} is not allowed`);
143
+ }
144
+ for (const [aggregationKey, config] of Object.entries(value)) {
145
+ if (aggregationKey === "_count") {
146
+ continue;
147
+ }
148
+ if (!easy_validation_service_1.default.isObject(config)) {
149
+ throw new badrequest_1.BadRequest(`invalid aggregation at orderBy.${key}.${aggregationKey}`);
150
+ }
151
+ let column;
152
+ if (!Object.keys(config).every((col) => {
153
+ var _a, _b;
154
+ const exists = (_b = (_a = relatedEntityPermissions.columns) === null || _a === void 0 ? void 0 : _a.includes) === null || _b === void 0 ? void 0 : _b.call(_a, col);
155
+ if (!exists) {
156
+ column = col;
157
+ }
158
+ return exists;
159
+ })) {
160
+ throw new forbidden_1.ForbiddenError(`invalid aggregation at orderBy.${key}.${aggregationKey}.${column}`);
161
+ }
162
+ }
163
+ }
164
+ }
165
+ }
166
+ if (!input.where) {
167
+ input.where = {};
168
+ }
169
+ input.where = this.mergeWhereWithPreConditions({
170
+ where: input.where || {},
171
+ preConditions: preConditions || {},
172
+ });
173
+ // sanitize where
174
+ this.roleBasedWhereSanitization({
175
+ model,
176
+ where: input.where,
177
+ user,
178
+ apiAccessType,
179
+ entityPermissions,
180
+ });
181
+ if ((ownership === null || ownership === void 0 ? void 0 : ownership.enabled) &&
182
+ Array.isArray(ownership === null || ownership === void 0 ? void 0 : ownership.columns) &&
183
+ ownership.columns.length) {
184
+ if (!user) {
185
+ throw new forbidden_1.ForbiddenError(`${model.schema}.${model.table}: operation ${apiAccessType} is not allowed`);
186
+ }
187
+ if (!(ownership === null || ownership === void 0 ? void 0 : ownership.columns.every((x) => columns.includes(x)))) {
188
+ throw new forbidden_1.ForbiddenError(`${model.schema}.${model.table}: operation ${apiAccessType} is not allowed`);
189
+ }
190
+ if (!easy_validation_service_1.default.isObject(input.where)) {
191
+ input.where = {};
192
+ }
193
+ input.where = {
194
+ _and: Object.entries(input.where)
195
+ .map(([key, value]) => ({
196
+ [key]: value,
197
+ }))
198
+ .concat(ownership === null || ownership === void 0 ? void 0 : ownership.columns.map((col) => {
199
+ return this.toUserOwnershipColumnNameMapper(user, col, ownership.columnToUserFieldMapper);
200
+ }, {})),
201
+ };
202
+ }
203
+ //sanitize relations
204
+ const include = input.include || {};
205
+ const iter = Object.entries(include);
206
+ for (let [alias, relationConfig] of iter) {
207
+ if (!easy_validation_service_1.default.isObject(relationConfig)) {
208
+ include[alias] = {};
209
+ relationConfig = include[alias];
210
+ }
211
+ if (easy_psql_1.DB.getIsAggregate(alias)) {
212
+ const relation = model.relations[easy_psql_1.DB.getRelationNameWithoutAggregate(alias)];
213
+ if (!relation ||
214
+ (!Array.isArray(relation.from_column) &&
215
+ !input.select[relation.from_column]) ||
216
+ (Array.isArray(relation.from_column) &&
217
+ !relation.from_column.some((col) => input.select[col]))) {
218
+ throw new forbidden_1.ForbiddenError(`operation ${model.schema}.${model.table}.${alias} is not allowed`);
219
+ }
220
+ const relatedModel = easy_psql_1.DB.getRelatedModel(relation);
221
+ if (!relatedModel) {
222
+ throw new forbidden_1.ForbiddenError(`aggregate operation ${apiAccessType} is not allowed`);
223
+ }
224
+ const relatedEntityPermissions = this.getUserRolePermissionsForEntity({
225
+ schema: relatedModel.schema || easy_psql_1.DB.database,
226
+ table: relatedModel.table,
227
+ user,
228
+ apiAccessType,
229
+ });
230
+ if (!((_b = relatedEntityPermissions.columns) === null || _b === void 0 ? void 0 : _b.length)) {
231
+ throw new forbidden_1.ForbiddenError(`${relatedModel.schema}.${relatedModel.table}: aggregate operation ${apiAccessType} is not allowed`);
232
+ }
233
+ const { where, limit, offset, orderBy, groupBy, distinct, ...rest } = relationConfig;
234
+ for (const [aggregationKey, config] of Object.entries(rest)) {
235
+ if (aggregationKey === "_count") {
236
+ continue;
237
+ }
238
+ if (!easy_validation_service_1.default.isObject(config)) {
239
+ throw new badrequest_1.BadRequest(`invalid aggregation at ${relatedModel.schema}.${relatedModel.table}.${alias}.${aggregationKey}`);
240
+ }
241
+ let column;
242
+ if (!Object.keys(config).every((col) => {
243
+ var _a, _b;
244
+ const exists = (_b = (_a = relatedEntityPermissions.columns) === null || _a === void 0 ? void 0 : _a.includes) === null || _b === void 0 ? void 0 : _b.call(_a, col);
245
+ if (!exists) {
246
+ column = col;
247
+ }
248
+ return exists;
249
+ })) {
250
+ throw new forbidden_1.ForbiddenError(`aggregation at ${relatedModel.schema}.${relatedModel.table}.${alias}.${aggregationKey}.${column} is not allowed`);
251
+ }
252
+ }
253
+ continue;
254
+ }
255
+ const relation = (_c = model.relations) === null || _c === void 0 ? void 0 : _c[alias];
256
+ // e.g relation is from id -> product_id. If id is excluded from the current model the join will fail, so force it to fail...
257
+ if (!relation ||
258
+ (!Array.isArray(relation.from_column) &&
259
+ !input.select[relation.from_column]) ||
260
+ (Array.isArray(relation.from_column) &&
261
+ !relation.from_column.some((col) => input.select[col]))) {
262
+ throw new forbidden_1.ForbiddenError(`${model.schema}.${model.table}.${alias}: operation ${apiAccessType} is not allowed`);
263
+ }
264
+ const relatedModel = easy_psql_1.DB.getRelatedModel(relation);
265
+ if (!relatedModel) {
266
+ throw new forbidden_1.ForbiddenError(`operation ${apiAccessType} is not allowed`);
267
+ }
268
+ const relatedEntityPermissions = this.getUserRolePermissionsForEntity({
269
+ schema: relatedModel.schema || easy_psql_1.DB.database,
270
+ table: relatedModel.table,
271
+ user,
272
+ apiAccessType,
273
+ });
274
+ this.roleBasedSelectSanitization({
275
+ user,
276
+ model: relatedModel,
277
+ input: relationConfig,
278
+ apiAccessType,
279
+ entityPermissions: relatedEntityPermissions,
280
+ });
281
+ }
282
+ // sanitize group by
283
+ if (Array.isArray(input.groupBy) && input.groupBy.length > 0) {
284
+ let column;
285
+ if (!input.groupBy.every((col) => {
286
+ const exists = columns.some((x) => x === col);
287
+ if (!exists) {
288
+ column = col;
289
+ }
290
+ return exists;
291
+ })) {
292
+ throw new forbidden_1.ForbiddenError(`${model.schema}.${model.table}.groupBy.${column}: operation ${apiAccessType} is not allowed`);
293
+ }
294
+ }
295
+ // sanitize distinct
296
+ if (Array.isArray(input.distinct) && input.distinct.length > 0) {
297
+ let column;
298
+ if (!input.distinct.every((col) => {
299
+ const exists = columns.some((x) => x === col);
300
+ if (!exists) {
301
+ column = col;
302
+ }
303
+ return exists;
304
+ })) {
305
+ throw new forbidden_1.ForbiddenError(`${model.schema}.${model.table}.distinct.${column}: operation ${apiAccessType} is not allowed`);
306
+ }
307
+ }
308
+ }
309
+ roleBasedInsertSanitization({ model, input, apiAccessType, user, entityPermissions, }) {
310
+ var _a;
311
+ const { columns, ownership, preConditions } = entityPermissions;
312
+ if (!Array.isArray(columns) || !columns.length) {
313
+ throw new forbidden_1.ForbiddenError();
314
+ }
315
+ const allowedColumnsMap = columns.reduce((acc, col) => {
316
+ acc[col] = true;
317
+ return acc;
318
+ }, {});
319
+ if (input == null) {
320
+ throw new badrequest_1.BadRequest(`body must not be null for operation ${apiAccessType}`);
321
+ }
322
+ const iter = Array.isArray(input) ? input : [input];
323
+ for (let i = 0; i < iter.length; i++) {
324
+ if (!easy_validation_service_1.default.isObject(iter[i])) {
325
+ throw new badrequest_1.BadRequest(`each row must be an object for operation ${apiAccessType}`);
326
+ }
327
+ Object.assign(iter[i], this.fastDeepClone(preConditions === null || preConditions === void 0 ? void 0 : preConditions.input));
328
+ let entry = iter[i];
329
+ const entryPropsIter = Object.keys(entry);
330
+ for (const key of entryPropsIter) {
331
+ if (key === "onConflict") {
332
+ continue;
333
+ }
334
+ else if ((_a = model.relations) === null || _a === void 0 ? void 0 : _a[key]) {
335
+ const relatedModel = easy_psql_1.DB.getRelatedModel(model.relations[key]);
336
+ const relatedModelEntityPermissions = this.getUserRolePermissionsForEntity({
337
+ schema: relatedModel.schema || easy_psql_1.DB.database,
338
+ table: relatedModel.table,
339
+ user,
340
+ apiAccessType,
341
+ });
342
+ this.roleBasedInsertSanitization({
343
+ model: relatedModel,
344
+ input: entry[key],
345
+ apiAccessType,
346
+ user,
347
+ entityPermissions: relatedModelEntityPermissions,
348
+ });
349
+ }
350
+ else {
351
+ if (!allowedColumnsMap[key]) {
352
+ throw new forbidden_1.ForbiddenError(`column ${key} is not allowed for operation ${apiAccessType}`);
353
+ }
354
+ }
355
+ }
356
+ if ((ownership === null || ownership === void 0 ? void 0 : ownership.enabled) &&
357
+ Array.isArray(ownership.columns) &&
358
+ ownership.columns.length) {
359
+ if (!user) {
360
+ throw new forbidden_1.ForbiddenError();
361
+ }
362
+ if (!(ownership === null || ownership === void 0 ? void 0 : ownership.columns.every((x) => allowedColumnsMap[x]))) {
363
+ throw new forbidden_1.ForbiddenError();
364
+ }
365
+ for (const col of ownership.columns) {
366
+ Object.assign(entry, this.toUserOwnershipColumnNameMapper(user, col, ownership.columnToUserFieldMapper, false));
367
+ }
368
+ }
369
+ }
370
+ }
371
+ roleBasedUpdateSanitization({ model, input, apiAccessType, user, entityPermissions, }) {
372
+ const { columns, ownership, preConditions } = entityPermissions;
373
+ if (!Array.isArray(columns) || !columns.length) {
374
+ throw new forbidden_1.ForbiddenError();
375
+ }
376
+ if (!easy_validation_service_1.default.isObject(input === null || input === void 0 ? void 0 : input.update)) {
377
+ throw new badrequest_1.BadRequest(`input.update must be an object for operation ${apiAccessType}`);
378
+ }
379
+ if (preConditions === null || preConditions === void 0 ? void 0 : preConditions.input) {
380
+ Object.assign(input.update, this.fastDeepClone(preConditions.input));
381
+ }
382
+ for (const column of Object.keys(input.update)) {
383
+ if (!columns.some((x) => x === column)) {
384
+ throw new forbidden_1.ForbiddenError(`column ${column} is not allowed for operation ${apiAccessType}`);
385
+ }
386
+ }
387
+ if (!input.where) {
388
+ input.where = {};
389
+ }
390
+ input.where = this.mergeWhereWithPreConditions({
391
+ where: input.where || {},
392
+ preConditions: preConditions || {},
393
+ });
394
+ // sanitize where
395
+ this.roleBasedWhereSanitization({
396
+ model,
397
+ where: input.where,
398
+ user,
399
+ apiAccessType,
400
+ entityPermissions,
401
+ });
402
+ if ((ownership === null || ownership === void 0 ? void 0 : ownership.enabled) &&
403
+ Array.isArray(ownership === null || ownership === void 0 ? void 0 : ownership.columns) &&
404
+ ownership.columns.length) {
405
+ if (!user) {
406
+ throw new forbidden_1.ForbiddenError();
407
+ }
408
+ if (!(ownership === null || ownership === void 0 ? void 0 : ownership.columns.every((x) => columns.includes(x)))) {
409
+ throw new forbidden_1.ForbiddenError();
410
+ }
411
+ if (!easy_validation_service_1.default.isObject(input.where)) {
412
+ input.where = {};
413
+ }
414
+ input.where = {
415
+ _and: Object.entries(input.where)
416
+ .map(([key, value]) => ({
417
+ [key]: value,
418
+ }))
419
+ .concat(ownership === null || ownership === void 0 ? void 0 : ownership.columns.map((col) => {
420
+ return this.toUserOwnershipColumnNameMapper(user, col, ownership.columnToUserFieldMapper);
421
+ }, {})),
422
+ };
423
+ }
424
+ }
425
+ roleBasedWhereSanitization({ model, where, apiAccessType, user, entityPermissions, }) {
426
+ var _a, _b;
427
+ if (!Array.isArray(entityPermissions === null || entityPermissions === void 0 ? void 0 : entityPermissions.columns) ||
428
+ !((_a = entityPermissions === null || entityPermissions === void 0 ? void 0 : entityPermissions.columns) === null || _a === void 0 ? void 0 : _a.length)) {
429
+ throw new forbidden_1.ForbiddenError();
430
+ }
431
+ if (!easy_validation_service_1.default.isObject(where) && !Array.isArray(where)) {
432
+ return;
433
+ }
434
+ if (Array.isArray(where)) {
435
+ for (const operation of where) {
436
+ this.roleBasedWhereSanitization({
437
+ model,
438
+ where: operation,
439
+ apiAccessType,
440
+ user,
441
+ entityPermissions,
442
+ });
443
+ }
444
+ return;
445
+ }
446
+ const iter = Object.entries(where);
447
+ for (const [key, value] of iter) {
448
+ if (key === "_and" || key === "_or") {
449
+ if (!Array.isArray(value)) {
450
+ throw new badrequest_1.BadRequest(`invalid value for operation ${key}`);
451
+ }
452
+ this.roleBasedWhereSanitization({
453
+ model,
454
+ where: value,
455
+ apiAccessType,
456
+ user,
457
+ entityPermissions,
458
+ });
459
+ }
460
+ else if (model.columns[key]) {
461
+ if (!entityPermissions.columns.some((c) => c === key)) {
462
+ throw new forbidden_1.ForbiddenError();
463
+ }
464
+ }
465
+ else if (easy_psql_1.DB.getIsAggregate(key)) {
466
+ const relationKey = easy_psql_1.DB.getRelationNameWithoutAggregate(key);
467
+ const relation = model.relations[relationKey];
468
+ if (!relation ||
469
+ !entityPermissions.columns.some((col) => this.isColumnInRelationColumns({ relation, column: col }))) {
470
+ throw new forbidden_1.ForbiddenError();
471
+ }
472
+ const relatedModel = easy_psql_1.DB.getRelatedModel(relation);
473
+ if (!relatedModel) {
474
+ throw new forbidden_1.ForbiddenError();
475
+ }
476
+ const relatedEntityPermissions = this.getUserRolePermissionsForEntity({
477
+ schema: relatedModel.schema || easy_psql_1.DB.database,
478
+ table: relatedModel.table,
479
+ user,
480
+ apiAccessType,
481
+ });
482
+ if (!((_b = relatedEntityPermissions.columns) === null || _b === void 0 ? void 0 : _b.length)) {
483
+ throw new forbidden_1.ForbiddenError();
484
+ }
485
+ const valueEntries = easy_validation_service_1.default.isObject(value)
486
+ ? Object.entries(value)
487
+ : [];
488
+ const [_, config] = valueEntries[0] || [null, {}];
489
+ const configEntries = easy_validation_service_1.default.isObject(config)
490
+ ? Object.entries(config)
491
+ : [];
492
+ const [aggregationKey, aggregationConfig] = configEntries[0] || [
493
+ "",
494
+ {},
495
+ ];
496
+ if (!aggregationKey) {
497
+ throw new badrequest_1.BadRequest();
498
+ }
499
+ if (aggregationKey === "_count") {
500
+ continue;
501
+ }
502
+ else {
503
+ const aggregationColumns = Object.keys(aggregationConfig);
504
+ if (!aggregationColumns.every((aggColumn) => { var _a, _b; return (_b = (_a = relatedEntityPermissions === null || relatedEntityPermissions === void 0 ? void 0 : relatedEntityPermissions.columns) === null || _a === void 0 ? void 0 : _a.includes) === null || _b === void 0 ? void 0 : _b.call(_a, aggColumn); })) {
505
+ throw new forbidden_1.ForbiddenError();
506
+ }
507
+ }
508
+ }
509
+ else if (model.relations[key]) {
510
+ const relation = model.relations[key];
511
+ if (!relation ||
512
+ !entityPermissions.columns.some((col) => this.isColumnInRelationColumns({ relation, column: col }))) {
513
+ throw new forbidden_1.ForbiddenError();
514
+ }
515
+ const relatedModel = easy_psql_1.DB.getRelatedModel(relation);
516
+ if (!relatedModel) {
517
+ throw new forbidden_1.ForbiddenError();
518
+ }
519
+ const relatedEntityPermissions = this.getUserRolePermissionsForEntity({
520
+ schema: relatedModel.schema || easy_psql_1.DB.database,
521
+ table: relatedModel.table,
522
+ user,
523
+ apiAccessType,
524
+ });
525
+ where[key] = this.mergeWhereWithPreConditions({
526
+ where: value || {},
527
+ preConditions: relatedEntityPermissions === null || relatedEntityPermissions === void 0 ? void 0 : relatedEntityPermissions.preConditions,
528
+ });
529
+ this.roleBasedWhereSanitization({
530
+ user,
531
+ model: relatedModel,
532
+ entityPermissions: relatedEntityPermissions,
533
+ where: where[key],
534
+ apiAccessType,
535
+ });
536
+ }
537
+ }
538
+ }
539
+ roleBasedDeleteSanitization({ model, input, apiAccessType, user, entityPermissions, }) {
540
+ const { columns, ownership, preConditions } = entityPermissions;
541
+ if (!Array.isArray(columns) || !columns.length) {
542
+ throw new forbidden_1.ForbiddenError();
543
+ }
544
+ if (!input.where) {
545
+ input.where = {};
546
+ }
547
+ input.where = this.mergeWhereWithPreConditions({
548
+ where: input.where || {},
549
+ preConditions: preConditions || {},
550
+ });
551
+ // sanitize where
552
+ this.roleBasedWhereSanitization({
553
+ model,
554
+ where: input.where,
555
+ user,
556
+ apiAccessType,
557
+ entityPermissions,
558
+ });
559
+ if (easy_validation_service_1.default.isObject(ownership) &&
560
+ (ownership === null || ownership === void 0 ? void 0 : ownership.enabled) &&
561
+ Array.isArray(ownership === null || ownership === void 0 ? void 0 : ownership.columns) &&
562
+ (ownership === null || ownership === void 0 ? void 0 : ownership.columns.length)) {
563
+ if (!user) {
564
+ throw new forbidden_1.ForbiddenError();
565
+ }
566
+ if (!ownership.columns.every((x) => columns.includes(x))) {
567
+ throw new forbidden_1.ForbiddenError();
568
+ }
569
+ input.where = {
570
+ _and: Object.entries(input.where)
571
+ .map(([key, value]) => ({
572
+ [key]: value,
573
+ }))
574
+ .concat(ownership.columns.map((col) => {
575
+ return this.toUserOwnershipColumnNameMapper(user, col, ownership.columnToUserFieldMapper);
576
+ }, {})),
577
+ };
578
+ }
579
+ }
580
+ isColumnInRelationColumns({ relation, column, }) {
581
+ if (Array.isArray(relation.from_column)) {
582
+ return relation.from_column.includes(column);
583
+ }
584
+ else {
585
+ return relation.from_column === column;
586
+ }
587
+ }
588
+ fastDeepClone(obj) {
589
+ if (obj === null || typeof obj !== "object") {
590
+ return obj;
591
+ }
592
+ if (Array.isArray(obj)) {
593
+ const copy = new Array(obj.length);
594
+ for (let i = 0; i < obj.length; i++) {
595
+ copy[i] = this.fastDeepClone(obj[i]);
596
+ }
597
+ return copy;
598
+ }
599
+ const copy = {};
600
+ for (const key in obj) {
601
+ if (Object.prototype.hasOwnProperty.call(obj, key)) {
602
+ copy[key] = this.fastDeepClone(obj[key]);
603
+ }
604
+ }
605
+ return copy;
606
+ }
607
+ mergeWhereWithPreConditions({ where, preConditions, }) {
608
+ if (!easy_validation_service_1.default.isObject(where)) {
609
+ where = {};
610
+ }
611
+ const conditionsToMerge = preConditions === null || preConditions === void 0 ? void 0 : preConditions.where;
612
+ if (easy_validation_service_1.default.isObject(conditionsToMerge)) {
613
+ const conditions = this.fastDeepClone(conditionsToMerge);
614
+ const { _and, _or, ...rest } = conditions;
615
+ where = { ...where, ...rest };
616
+ if (Array.isArray(_and)) {
617
+ where._and = [...(where._and || []), ..._and];
618
+ }
619
+ if (Array.isArray(_or)) {
620
+ where._or = [...(where._or || []), ..._or];
621
+ }
622
+ }
623
+ return where;
624
+ }
625
+ findManyModel({ schema, table, connection, bypass, query, user, }) {
626
+ const model = this.roleBasedModel({
627
+ schema,
628
+ table,
629
+ connection,
630
+ apiAccessType: types_1.AllowedEngineApiAccessTypes.findMany,
631
+ bypass,
632
+ input: query,
633
+ user,
634
+ });
635
+ return model;
636
+ }
637
+ findOneModel({ schema, table, connection, bypass, query, user, }) {
638
+ const model = this.roleBasedModel({
639
+ schema,
640
+ table,
641
+ connection,
642
+ apiAccessType: types_1.AllowedEngineApiAccessTypes.findOne,
643
+ bypass,
644
+ input: query,
645
+ user,
646
+ });
647
+ return model;
648
+ }
649
+ aggregateModel({ schema, table, connection, bypass, query, user, }) {
650
+ const model = this.roleBasedModel({
651
+ schema,
652
+ table,
653
+ connection,
654
+ apiAccessType: types_1.AllowedEngineApiAccessTypes.aggregate,
655
+ bypass,
656
+ input: query,
657
+ user,
658
+ });
659
+ return model;
660
+ }
661
+ createManyModel({ schema, table, connection, bypass, body, user, }) {
662
+ const model = this.roleBasedModel({
663
+ schema,
664
+ table,
665
+ connection,
666
+ apiAccessType: types_1.AllowedEngineApiAccessTypes.createMany,
667
+ bypass,
668
+ input: body,
669
+ user,
670
+ });
671
+ return model;
672
+ }
673
+ createOneModel({ schema, table, connection, bypass, body, user, }) {
674
+ const model = this.roleBasedModel({
675
+ schema,
676
+ table,
677
+ connection,
678
+ apiAccessType: types_1.AllowedEngineApiAccessTypes.createOne,
679
+ bypass,
680
+ input: body,
681
+ user,
682
+ });
683
+ return model;
684
+ }
685
+ updateManyModel({ schema, table, connection, bypass, input, user, }) {
686
+ const model = this.roleBasedModel({
687
+ schema,
688
+ table,
689
+ connection,
690
+ apiAccessType: types_1.AllowedEngineApiAccessTypes.updateMany,
691
+ bypass,
692
+ input,
693
+ user,
694
+ });
695
+ return model;
696
+ }
697
+ updateOneModel({ schema, table, connection, bypass, input, user, }) {
698
+ const model = this.roleBasedModel({
699
+ schema,
700
+ table,
701
+ connection,
702
+ apiAccessType: types_1.AllowedEngineApiAccessTypes.updateOne,
703
+ bypass,
704
+ input,
705
+ user,
706
+ });
707
+ return model;
708
+ }
709
+ deleteManyModel({ schema, table, connection, bypass, input, user, }) {
710
+ const model = this.roleBasedModel({
711
+ schema,
712
+ table,
713
+ connection,
714
+ apiAccessType: types_1.AllowedEngineApiAccessTypes.deleteMany,
715
+ bypass,
716
+ input,
717
+ user,
718
+ });
719
+ return model;
720
+ }
721
+ deleteOneModel({ schema, table, connection, bypass, input, user, }) {
722
+ const model = this.roleBasedModel({
723
+ schema,
724
+ table,
725
+ connection,
726
+ apiAccessType: types_1.AllowedEngineApiAccessTypes.deleteOne,
727
+ bypass,
728
+ input,
729
+ user,
730
+ });
731
+ return model;
732
+ }
733
+ toUserOwnershipColumnValueDefault(user) {
734
+ return user === null || user === void 0 ? void 0 : user[this.options.userIdentityKey || "id"];
735
+ }
736
+ toUserOwnershipColumnNameMapper(user, col, columnToUserFieldMapper, isComparison = true) {
737
+ const userField = columnToUserFieldMapper === null || columnToUserFieldMapper === void 0 ? void 0 : columnToUserFieldMapper[col];
738
+ const value = userField && user && userField in user
739
+ ? user[userField]
740
+ : this.toUserOwnershipColumnValueDefault(user);
741
+ return isComparison ? { [col]: { _eq: value } } : { [col]: value };
742
+ }
743
+ }
744
+ exports.EasyPSQLRBAC = EasyPSQLRBAC;
745
+ //# sourceMappingURL=rbac.js.map