@zenstackhq/plugin-policy 3.0.0-beta.19 → 3.0.0-beta.21
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/index.cjs +73 -61
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +31 -19
- package/dist/index.js.map +1 -1
- package/package.json +7 -7
package/dist/index.cjs
CHANGED
|
@@ -27,12 +27,12 @@ module.exports = __toCommonJS(src_exports);
|
|
|
27
27
|
|
|
28
28
|
// src/functions.ts
|
|
29
29
|
var import_common_helpers4 = require("@zenstackhq/common-helpers");
|
|
30
|
-
var
|
|
30
|
+
var import_orm5 = require("@zenstackhq/orm");
|
|
31
31
|
var import_kysely4 = require("kysely");
|
|
32
32
|
|
|
33
33
|
// src/policy-handler.ts
|
|
34
34
|
var import_common_helpers3 = require("@zenstackhq/common-helpers");
|
|
35
|
-
var
|
|
35
|
+
var import_orm4 = require("@zenstackhq/orm");
|
|
36
36
|
var import_schema4 = require("@zenstackhq/orm/schema");
|
|
37
37
|
var import_kysely3 = require("kysely");
|
|
38
38
|
var import_ts_pattern3 = require("ts-pattern");
|
|
@@ -58,7 +58,7 @@ var ColumnCollector = class extends import_orm.KyselyUtils.DefaultOperationNodeV
|
|
|
58
58
|
|
|
59
59
|
// src/expression-transformer.ts
|
|
60
60
|
var import_common_helpers2 = require("@zenstackhq/common-helpers");
|
|
61
|
-
var
|
|
61
|
+
var import_orm3 = require("@zenstackhq/orm");
|
|
62
62
|
var import_schema3 = require("@zenstackhq/orm/schema");
|
|
63
63
|
var import_kysely2 = require("kysely");
|
|
64
64
|
var import_ts_pattern2 = require("ts-pattern");
|
|
@@ -135,6 +135,7 @@ var ExpressionEvaluator = class {
|
|
|
135
135
|
};
|
|
136
136
|
|
|
137
137
|
// src/utils.ts
|
|
138
|
+
var import_orm2 = require("@zenstackhq/orm");
|
|
138
139
|
var import_schema2 = require("@zenstackhq/orm/schema");
|
|
139
140
|
var import_kysely = require("kysely");
|
|
140
141
|
function trueNode(dialect) {
|
|
@@ -236,6 +237,17 @@ function isBeforeInvocation(expr2) {
|
|
|
236
237
|
return import_schema2.ExpressionUtils.isCall(expr2) && expr2.function === "before";
|
|
237
238
|
}
|
|
238
239
|
__name(isBeforeInvocation, "isBeforeInvocation");
|
|
240
|
+
function createRejectedByPolicyError(model, reason, message) {
|
|
241
|
+
const err = new import_orm2.ORMError(import_orm2.ORMErrorReason.REJECTED_BY_POLICY, message ?? "operation is rejected by access policies");
|
|
242
|
+
err.rejectedByPolicyReason = reason;
|
|
243
|
+
err.model = model;
|
|
244
|
+
return err;
|
|
245
|
+
}
|
|
246
|
+
__name(createRejectedByPolicyError, "createRejectedByPolicyError");
|
|
247
|
+
function createUnsupportedError(message) {
|
|
248
|
+
return new import_orm2.ORMError(import_orm2.ORMErrorReason.NOT_SUPPORTED, message);
|
|
249
|
+
}
|
|
250
|
+
__name(createUnsupportedError, "createUnsupportedError");
|
|
239
251
|
|
|
240
252
|
// src/expression-transformer.ts
|
|
241
253
|
function _ts_decorate(decorators, target, key, desc) {
|
|
@@ -267,7 +279,7 @@ var ExpressionTransformer = class {
|
|
|
267
279
|
dialect;
|
|
268
280
|
constructor(client) {
|
|
269
281
|
this.client = client;
|
|
270
|
-
this.dialect = (0,
|
|
282
|
+
this.dialect = (0, import_orm3.getCrudDialect)(this.schema, this.clientOptions);
|
|
271
283
|
}
|
|
272
284
|
get schema() {
|
|
273
285
|
return this.client.$schema;
|
|
@@ -280,7 +292,7 @@ var ExpressionTransformer = class {
|
|
|
280
292
|
}
|
|
281
293
|
get authType() {
|
|
282
294
|
if (!this.schema.authType) {
|
|
283
|
-
|
|
295
|
+
(0, import_common_helpers2.invariant)(false, 'Schema does not have an "authType" specified');
|
|
284
296
|
}
|
|
285
297
|
return this.schema.authType;
|
|
286
298
|
}
|
|
@@ -298,7 +310,7 @@ var ExpressionTransformer = class {
|
|
|
298
310
|
return import_kysely2.ValueListNode.create(expr2.items.map((item) => this.transform(item, context)));
|
|
299
311
|
}
|
|
300
312
|
_field(expr2, context) {
|
|
301
|
-
const fieldDef =
|
|
313
|
+
const fieldDef = import_orm3.QueryUtils.requireField(this.schema, context.model, expr2.field);
|
|
302
314
|
if (!fieldDef.relation) {
|
|
303
315
|
return this.createColumnRef(expr2.field, context);
|
|
304
316
|
} else {
|
|
@@ -389,7 +401,7 @@ var ExpressionTransformer = class {
|
|
|
389
401
|
(0, import_common_helpers2.invariant)(import_schema3.ExpressionUtils.isNull(expr2.right), "only null comparison is supported for relation field");
|
|
390
402
|
const leftRelDef = this.getFieldDefFromFieldRef(expr2.left, context.model);
|
|
391
403
|
(0, import_common_helpers2.invariant)(leftRelDef, "failed to get relation field definition");
|
|
392
|
-
const idFields =
|
|
404
|
+
const idFields = import_orm3.QueryUtils.requireIdFields(this.schema, leftRelDef.type);
|
|
393
405
|
normalizedLeft = this.makeOrAppendMember(normalizedLeft, idFields[0]);
|
|
394
406
|
}
|
|
395
407
|
let normalizedRight = expr2.right;
|
|
@@ -397,7 +409,7 @@ var ExpressionTransformer = class {
|
|
|
397
409
|
(0, import_common_helpers2.invariant)(import_schema3.ExpressionUtils.isNull(expr2.left), "only null comparison is supported for relation field");
|
|
398
410
|
const rightRelDef = this.getFieldDefFromFieldRef(expr2.right, context.model);
|
|
399
411
|
(0, import_common_helpers2.invariant)(rightRelDef, "failed to get relation field definition");
|
|
400
|
-
const idFields =
|
|
412
|
+
const idFields = import_orm3.QueryUtils.requireIdFields(this.schema, rightRelDef.type);
|
|
401
413
|
normalizedRight = this.makeOrAppendMember(normalizedRight, idFields[0]);
|
|
402
414
|
}
|
|
403
415
|
return {
|
|
@@ -421,10 +433,10 @@ var ExpressionTransformer = class {
|
|
|
421
433
|
newContextModel = fieldDef.type;
|
|
422
434
|
} else {
|
|
423
435
|
(0, import_common_helpers2.invariant)(import_schema3.ExpressionUtils.isMember(expr2.left) && import_schema3.ExpressionUtils.isField(expr2.left.receiver), "left operand must be member access with field receiver");
|
|
424
|
-
const fieldDef2 =
|
|
436
|
+
const fieldDef2 = import_orm3.QueryUtils.requireField(this.schema, context.model, expr2.left.receiver.field);
|
|
425
437
|
newContextModel = fieldDef2.type;
|
|
426
438
|
for (const member of expr2.left.members) {
|
|
427
|
-
const memberDef =
|
|
439
|
+
const memberDef = import_orm3.QueryUtils.requireField(this.schema, newContextModel, member);
|
|
428
440
|
newContextModel = memberDef.type;
|
|
429
441
|
}
|
|
430
442
|
}
|
|
@@ -448,7 +460,7 @@ var ExpressionTransformer = class {
|
|
|
448
460
|
}
|
|
449
461
|
transformAuthBinary(expr2, context) {
|
|
450
462
|
if (expr2.op !== "==" && expr2.op !== "!=") {
|
|
451
|
-
throw
|
|
463
|
+
throw createUnsupportedError(`Unsupported operator for \`auth()\` in policy of model "${context.model}": ${expr2.op}`);
|
|
452
464
|
}
|
|
453
465
|
let authExpr;
|
|
454
466
|
let other;
|
|
@@ -462,9 +474,9 @@ var ExpressionTransformer = class {
|
|
|
462
474
|
if (import_schema3.ExpressionUtils.isNull(other)) {
|
|
463
475
|
return this.transformValue(expr2.op === "==" ? !this.auth : !!this.auth, "Boolean");
|
|
464
476
|
} else {
|
|
465
|
-
const authModel =
|
|
477
|
+
const authModel = import_orm3.QueryUtils.getModel(this.schema, this.authType);
|
|
466
478
|
if (!authModel) {
|
|
467
|
-
throw
|
|
479
|
+
throw createUnsupportedError(`Unsupported use of \`auth()\` in policy of model "${context.model}", comparing with \`auth()\` is only possible when auth type is a model`);
|
|
468
480
|
}
|
|
469
481
|
const idFields = Object.values(authModel.fields).filter((f) => f.id).map((f) => f.name);
|
|
470
482
|
(0, import_common_helpers2.invariant)(idFields.length > 0, "auth type model must have at least one id field");
|
|
@@ -514,7 +526,7 @@ var ExpressionTransformer = class {
|
|
|
514
526
|
transformCall(expr2, context) {
|
|
515
527
|
const func = this.getFunctionImpl(expr2.function);
|
|
516
528
|
if (!func) {
|
|
517
|
-
throw
|
|
529
|
+
throw createUnsupportedError(`Function not implemented: ${expr2.function}`);
|
|
518
530
|
}
|
|
519
531
|
const eb = (0, import_kysely2.expressionBuilder)();
|
|
520
532
|
return func(eb, (expr2.args ?? []).map((arg) => this.transformCallArg(eb, arg, context)), {
|
|
@@ -551,7 +563,7 @@ var ExpressionTransformer = class {
|
|
|
551
563
|
const valNode = this.valueMemberAccess(this.auth, arg, this.authType);
|
|
552
564
|
return valNode ? eb.val(valNode.value) : eb.val(null);
|
|
553
565
|
}
|
|
554
|
-
throw
|
|
566
|
+
throw createUnsupportedError(`Unsupported argument expression: ${arg.kind}`);
|
|
555
567
|
}
|
|
556
568
|
_member(expr2, context) {
|
|
557
569
|
if (this.isAuthCall(expr2.receiver)) {
|
|
@@ -570,7 +582,7 @@ var ExpressionTransformer = class {
|
|
|
570
582
|
if (expr2.members.length === 1) {
|
|
571
583
|
return this._field(import_schema3.ExpressionUtils.field(expr2.members[0]), context);
|
|
572
584
|
} else {
|
|
573
|
-
const firstMemberFieldDef =
|
|
585
|
+
const firstMemberFieldDef = import_orm3.QueryUtils.requireField(this.schema, context.model, expr2.members[0]);
|
|
574
586
|
receiver = this.transformRelationAccess(expr2.members[0], firstMemberFieldDef.type, restContext);
|
|
575
587
|
members = expr2.members.slice(1);
|
|
576
588
|
}
|
|
@@ -580,7 +592,7 @@ var ExpressionTransformer = class {
|
|
|
580
592
|
(0, import_common_helpers2.invariant)(import_kysely2.SelectQueryNode.is(receiver), "expected receiver to be select query");
|
|
581
593
|
let startType;
|
|
582
594
|
if (import_schema3.ExpressionUtils.isField(expr2.receiver)) {
|
|
583
|
-
const receiverField =
|
|
595
|
+
const receiverField = import_orm3.QueryUtils.requireField(this.schema, context.model, expr2.receiver.field);
|
|
584
596
|
startType = receiverField.type;
|
|
585
597
|
} else {
|
|
586
598
|
startType = context.model;
|
|
@@ -588,7 +600,7 @@ var ExpressionTransformer = class {
|
|
|
588
600
|
const memberFields = [];
|
|
589
601
|
let currType = startType;
|
|
590
602
|
for (const member of members) {
|
|
591
|
-
const fieldDef =
|
|
603
|
+
const fieldDef = import_orm3.QueryUtils.requireField(this.schema, currType, member);
|
|
592
604
|
memberFields.push({
|
|
593
605
|
fieldDef,
|
|
594
606
|
fromModel: currType
|
|
@@ -642,18 +654,18 @@ var ExpressionTransformer = class {
|
|
|
642
654
|
throw new Error(`Only single member access is supported`);
|
|
643
655
|
}
|
|
644
656
|
const field = expr2.members[0];
|
|
645
|
-
const fieldDef =
|
|
657
|
+
const fieldDef = import_orm3.QueryUtils.requireField(this.schema, receiverType, field);
|
|
646
658
|
const fieldValue = receiver[field] ?? null;
|
|
647
659
|
return this.transformValue(fieldValue, fieldDef.type);
|
|
648
660
|
}
|
|
649
661
|
transformRelationAccess(field, relationModel, context) {
|
|
650
|
-
const m2m =
|
|
662
|
+
const m2m = import_orm3.QueryUtils.getManyToManyRelation(this.schema, context.model, field);
|
|
651
663
|
if (m2m) {
|
|
652
664
|
return this.transformManyToManyRelationAccess(m2m, context);
|
|
653
665
|
}
|
|
654
666
|
const fromModel = context.model;
|
|
655
|
-
const relationFieldDef =
|
|
656
|
-
const { keyPairs, ownedByModel } =
|
|
667
|
+
const relationFieldDef = import_orm3.QueryUtils.requireField(this.schema, fromModel, field);
|
|
668
|
+
const { keyPairs, ownedByModel } = import_orm3.QueryUtils.getRelationForeignKeyFieldPairs(this.schema, fromModel, field);
|
|
657
669
|
let condition;
|
|
658
670
|
if (ownedByModel) {
|
|
659
671
|
condition = conjunction(this.dialect, keyPairs.map(({ fk, pk }) => {
|
|
@@ -684,14 +696,14 @@ var ExpressionTransformer = class {
|
|
|
684
696
|
if (context.operation === "create") {
|
|
685
697
|
return import_kysely2.ReferenceNode.create(import_kysely2.ColumnNode.create(column), import_kysely2.TableNode.create(tableName));
|
|
686
698
|
}
|
|
687
|
-
const fieldDef =
|
|
699
|
+
const fieldDef = import_orm3.QueryUtils.requireField(this.schema, context.model, column);
|
|
688
700
|
if (!fieldDef.originModel || fieldDef.originModel === context.model) {
|
|
689
701
|
return import_kysely2.ReferenceNode.create(import_kysely2.ColumnNode.create(column), import_kysely2.TableNode.create(tableName));
|
|
690
702
|
}
|
|
691
703
|
return this.buildDelegateBaseFieldSelect(context.model, tableName, column, fieldDef.originModel);
|
|
692
704
|
}
|
|
693
705
|
buildDelegateBaseFieldSelect(model, modelAlias, field, baseModel) {
|
|
694
|
-
const idFields =
|
|
706
|
+
const idFields = import_orm3.QueryUtils.requireIdFields(this.client.$schema, model);
|
|
695
707
|
return {
|
|
696
708
|
kind: "SelectQueryNode",
|
|
697
709
|
from: import_kysely2.FromNode.create([
|
|
@@ -730,9 +742,9 @@ var ExpressionTransformer = class {
|
|
|
730
742
|
}
|
|
731
743
|
getFieldDefFromFieldRef(expr2, model) {
|
|
732
744
|
if (import_schema3.ExpressionUtils.isField(expr2)) {
|
|
733
|
-
return
|
|
745
|
+
return import_orm3.QueryUtils.requireField(this.schema, model, expr2.field);
|
|
734
746
|
} else if (import_schema3.ExpressionUtils.isMember(expr2) && expr2.members.length === 1 && import_schema3.ExpressionUtils.isThis(expr2.receiver)) {
|
|
735
|
-
return
|
|
747
|
+
return import_orm3.QueryUtils.requireField(this.schema, model, expr2.members[0]);
|
|
736
748
|
} else {
|
|
737
749
|
return void 0;
|
|
738
750
|
}
|
|
@@ -816,14 +828,14 @@ var PolicyHandler = class extends import_kysely3.OperationNodeTransformer {
|
|
|
816
828
|
dialect;
|
|
817
829
|
constructor(client) {
|
|
818
830
|
super(), this.client = client;
|
|
819
|
-
this.dialect = (0,
|
|
831
|
+
this.dialect = (0, import_orm4.getCrudDialect)(this.client.$schema, this.client.$options);
|
|
820
832
|
}
|
|
821
833
|
get kysely() {
|
|
822
834
|
return this.client.$qb;
|
|
823
835
|
}
|
|
824
836
|
async handle(node, proceed) {
|
|
825
837
|
if (!this.isCrudQueryNode(node)) {
|
|
826
|
-
throw
|
|
838
|
+
throw createRejectedByPolicyError(void 0, import_orm4.RejectedByPolicyReason.OTHER, "non-CRUD queries are not allowed");
|
|
827
839
|
}
|
|
828
840
|
if (!this.isMutationQueryNode(node)) {
|
|
829
841
|
return proceed(this.transformNode(node));
|
|
@@ -837,7 +849,7 @@ var PolicyHandler = class extends import_kysely3.OperationNodeTransformer {
|
|
|
837
849
|
if (constCondition === true) {
|
|
838
850
|
needCheckPreCreate = false;
|
|
839
851
|
} else if (constCondition === false) {
|
|
840
|
-
throw
|
|
852
|
+
throw createRejectedByPolicyError(mutationModel, import_orm4.RejectedByPolicyReason.NO_ACCESS);
|
|
841
853
|
}
|
|
842
854
|
}
|
|
843
855
|
if (needCheckPreCreate) {
|
|
@@ -853,11 +865,11 @@ var PolicyHandler = class extends import_kysely3.OperationNodeTransformer {
|
|
|
853
865
|
if (hasPostUpdatePolicies && result.rows.length > 0) {
|
|
854
866
|
if (beforeUpdateInfo) {
|
|
855
867
|
(0, import_common_helpers3.invariant)(beforeUpdateInfo.rows.length === result.rows.length);
|
|
856
|
-
const idFields =
|
|
868
|
+
const idFields = import_orm4.QueryUtils.requireIdFields(this.client.$schema, mutationModel);
|
|
857
869
|
for (const postRow of result.rows) {
|
|
858
870
|
const beforeRow = beforeUpdateInfo.rows.find((r) => idFields.every((f) => r[f] === postRow[f]));
|
|
859
871
|
if (!beforeRow) {
|
|
860
|
-
throw
|
|
872
|
+
throw createRejectedByPolicyError(mutationModel, import_orm4.RejectedByPolicyReason.OTHER, "Before-update and after-update rows do not match by id. If you have post-update policies on a model, updating id fields is not supported.");
|
|
861
873
|
}
|
|
862
874
|
}
|
|
863
875
|
}
|
|
@@ -870,7 +882,7 @@ var PolicyHandler = class extends import_kysely3.OperationNodeTransformer {
|
|
|
870
882
|
import_kysely3.ParensNode.create(import_kysely3.ValuesNode.create(beforeUpdateInfo.rows.map((r) => import_kysely3.PrimitiveValueListNode.create(beforeUpdateInfo.fields.map((f) => r[f])))))
|
|
871
883
|
]),
|
|
872
884
|
selections: beforeUpdateInfo.fields.map((name, index) => {
|
|
873
|
-
const def =
|
|
885
|
+
const def = import_orm4.QueryUtils.requireField(this.client.$schema, mutationModel, name);
|
|
874
886
|
const castedColumnRef = import_kysely3.sql`CAST(${eb.ref(`column${index + 1}`)} as ${import_kysely3.sql.raw(this.dialect.getFieldSqlType(def))})`.as(name);
|
|
875
887
|
return import_kysely3.SelectionNode.create(castedColumnRef.toOperationNode());
|
|
876
888
|
})
|
|
@@ -883,12 +895,12 @@ var PolicyHandler = class extends import_kysely3.OperationNodeTransformer {
|
|
|
883
895
|
idConditions,
|
|
884
896
|
postUpdateFilter
|
|
885
897
|
]))).$if(!!beforeUpdateInfo, (qb) => qb.leftJoin(() => new import_kysely3.ExpressionWrapper(beforeUpdateTable).as("$before"), (join) => {
|
|
886
|
-
const idFields =
|
|
898
|
+
const idFields = import_orm4.QueryUtils.requireIdFields(this.client.$schema, mutationModel);
|
|
887
899
|
return idFields.reduce((acc, f) => acc.onRef(`${mutationModel}.${f}`, "=", `$before.${f}`), join);
|
|
888
900
|
}));
|
|
889
901
|
const postUpdateResult = await proceed(postUpdateQuery.toOperationNode());
|
|
890
902
|
if (!postUpdateResult.rows[0]?.$condition) {
|
|
891
|
-
throw
|
|
903
|
+
throw createRejectedByPolicyError(mutationModel, import_orm4.RejectedByPolicyReason.NO_ACCESS, "some or all updated rows failed to pass post-update policy check");
|
|
892
904
|
}
|
|
893
905
|
}
|
|
894
906
|
if (!node.returning || this.onlyReturningId(node)) {
|
|
@@ -896,7 +908,7 @@ var PolicyHandler = class extends import_kysely3.OperationNodeTransformer {
|
|
|
896
908
|
} else {
|
|
897
909
|
const readBackResult = await this.processReadBack(node, result, proceed);
|
|
898
910
|
if (readBackResult.rows.length !== result.rows.length) {
|
|
899
|
-
throw
|
|
911
|
+
throw createRejectedByPolicyError(mutationModel, import_orm4.RejectedByPolicyReason.CANNOT_READ_BACK, "result is not allowed to be read back");
|
|
900
912
|
}
|
|
901
913
|
return readBackResult;
|
|
902
914
|
}
|
|
@@ -950,7 +962,7 @@ var PolicyHandler = class extends import_kysely3.OperationNodeTransformer {
|
|
|
950
962
|
return void 0;
|
|
951
963
|
}
|
|
952
964
|
const fields = /* @__PURE__ */ new Set();
|
|
953
|
-
const fieldCollector = new class extends
|
|
965
|
+
const fieldCollector = new class extends import_orm4.SchemaUtils.ExpressionVisitor {
|
|
954
966
|
visitMember(e) {
|
|
955
967
|
if (isBeforeInvocation(e.receiver)) {
|
|
956
968
|
(0, import_common_helpers3.invariant)(e.members.length === 1, "before() can only be followed by a scalar field access");
|
|
@@ -965,7 +977,7 @@ var PolicyHandler = class extends import_kysely3.OperationNodeTransformer {
|
|
|
965
977
|
if (fields.size === 0) {
|
|
966
978
|
return void 0;
|
|
967
979
|
}
|
|
968
|
-
|
|
980
|
+
import_orm4.QueryUtils.requireIdFields(this.client.$schema, model).forEach((f) => fields.add(f));
|
|
969
981
|
return Array.from(fields).sort();
|
|
970
982
|
}
|
|
971
983
|
// #region overrides
|
|
@@ -1039,7 +1051,7 @@ var PolicyHandler = class extends import_kysely3.OperationNodeTransformer {
|
|
|
1039
1051
|
let returning = result.returning;
|
|
1040
1052
|
if (returning) {
|
|
1041
1053
|
const { mutationModel } = this.getMutationModel(node);
|
|
1042
|
-
const idFields =
|
|
1054
|
+
const idFields = import_orm4.QueryUtils.requireIdFields(this.client.$schema, mutationModel);
|
|
1043
1055
|
returning = import_kysely3.ReturningNode.create(idFields.map((f) => import_kysely3.SelectionNode.create(import_kysely3.ColumnNode.create(f))));
|
|
1044
1056
|
}
|
|
1045
1057
|
return {
|
|
@@ -1062,7 +1074,7 @@ var PolicyHandler = class extends import_kysely3.OperationNodeTransformer {
|
|
|
1062
1074
|
}
|
|
1063
1075
|
let returning = result.returning;
|
|
1064
1076
|
if (returning || this.hasPostUpdatePolicies(mutationModel)) {
|
|
1065
|
-
const idFields =
|
|
1077
|
+
const idFields = import_orm4.QueryUtils.requireIdFields(this.client.$schema, mutationModel);
|
|
1066
1078
|
returning = import_kysely3.ReturningNode.create(idFields.map((f) => import_kysely3.SelectionNode.create(import_kysely3.ColumnNode.create(f))));
|
|
1067
1079
|
}
|
|
1068
1080
|
return {
|
|
@@ -1102,9 +1114,9 @@ var PolicyHandler = class extends import_kysely3.OperationNodeTransformer {
|
|
|
1102
1114
|
return true;
|
|
1103
1115
|
}
|
|
1104
1116
|
const { mutationModel } = this.getMutationModel(node);
|
|
1105
|
-
const idFields =
|
|
1117
|
+
const idFields = import_orm4.QueryUtils.requireIdFields(this.client.$schema, mutationModel);
|
|
1106
1118
|
if (node.returning.selections.some((s) => import_kysely3.SelectAllNode.is(s.selection))) {
|
|
1107
|
-
const modelDef =
|
|
1119
|
+
const modelDef = import_orm4.QueryUtils.requireModel(this.client.$schema, mutationModel);
|
|
1108
1120
|
if (Object.keys(modelDef.fields).some((f) => !idFields.includes(f))) {
|
|
1109
1121
|
return false;
|
|
1110
1122
|
} else {
|
|
@@ -1155,14 +1167,14 @@ var PolicyHandler = class extends import_kysely3.OperationNodeTransformer {
|
|
|
1155
1167
|
};
|
|
1156
1168
|
const result = await proceed(queryNode);
|
|
1157
1169
|
if (!result.rows[0]?.$conditionA) {
|
|
1158
|
-
throw
|
|
1170
|
+
throw createRejectedByPolicyError(m2m.firstModel, import_orm4.RejectedByPolicyReason.CANNOT_READ_BACK, `many-to-many relation participant model "${m2m.firstModel}" not updatable`);
|
|
1159
1171
|
}
|
|
1160
1172
|
if (!result.rows[0]?.$conditionB) {
|
|
1161
|
-
throw
|
|
1173
|
+
throw createRejectedByPolicyError(m2m.secondModel, import_orm4.RejectedByPolicyReason.NO_ACCESS, `many-to-many relation participant model "${m2m.secondModel}" not updatable`);
|
|
1162
1174
|
}
|
|
1163
1175
|
}
|
|
1164
1176
|
async enforcePreCreatePolicyForOne(model, fields, values, proceed) {
|
|
1165
|
-
const allFields = Object.entries(
|
|
1177
|
+
const allFields = Object.entries(import_orm4.QueryUtils.requireModel(this.client.$schema, model).fields).filter(([, def]) => !def.relation);
|
|
1166
1178
|
const allValues = [];
|
|
1167
1179
|
for (const [name, _def] of allFields) {
|
|
1168
1180
|
const index = fields.indexOf(name);
|
|
@@ -1200,7 +1212,7 @@ var PolicyHandler = class extends import_kysely3.OperationNodeTransformer {
|
|
|
1200
1212
|
};
|
|
1201
1213
|
const result = await proceed(preCreateCheck);
|
|
1202
1214
|
if (!result.rows[0]?.$condition) {
|
|
1203
|
-
throw
|
|
1215
|
+
throw createRejectedByPolicyError(model, import_orm4.RejectedByPolicyReason.NO_ACCESS);
|
|
1204
1216
|
}
|
|
1205
1217
|
}
|
|
1206
1218
|
unwrapCreateValueRows(node, model, fields, isManyToManyJoinTable) {
|
|
@@ -1211,7 +1223,7 @@ var PolicyHandler = class extends import_kysely3.OperationNodeTransformer {
|
|
|
1211
1223
|
this.unwrapCreateValueRow(node.values, model, fields, isManyToManyJoinTable)
|
|
1212
1224
|
];
|
|
1213
1225
|
} else {
|
|
1214
|
-
|
|
1226
|
+
(0, import_common_helpers3.invariant)(false, `Unexpected node kind: ${node.kind} for unwrapping create values`);
|
|
1215
1227
|
}
|
|
1216
1228
|
}
|
|
1217
1229
|
unwrapCreateValueRow(data, model, fields, isImplicitManyToManyJoinTable) {
|
|
@@ -1220,7 +1232,7 @@ var PolicyHandler = class extends import_kysely3.OperationNodeTransformer {
|
|
|
1220
1232
|
for (let i = 0; i < data.length; i++) {
|
|
1221
1233
|
const item = data[i];
|
|
1222
1234
|
if (typeof item === "object" && item && "kind" in item) {
|
|
1223
|
-
const fieldDef =
|
|
1235
|
+
const fieldDef = import_orm4.QueryUtils.requireField(this.client.$schema, model, fields[i]);
|
|
1224
1236
|
(0, import_common_helpers3.invariant)(item.kind === "ValueNode", "expecting a ValueNode");
|
|
1225
1237
|
result.push({
|
|
1226
1238
|
node: import_kysely3.ValueNode.create(this.dialect.transformPrimitive(item.value, fieldDef.type, !!fieldDef.array)),
|
|
@@ -1229,7 +1241,7 @@ var PolicyHandler = class extends import_kysely3.OperationNodeTransformer {
|
|
|
1229
1241
|
} else {
|
|
1230
1242
|
let value = item;
|
|
1231
1243
|
if (!isImplicitManyToManyJoinTable) {
|
|
1232
|
-
const fieldDef =
|
|
1244
|
+
const fieldDef = import_orm4.QueryUtils.requireField(this.client.$schema, model, fields[i]);
|
|
1233
1245
|
value = this.dialect.transformPrimitive(item, fieldDef.type, !!fieldDef.array);
|
|
1234
1246
|
}
|
|
1235
1247
|
if (Array.isArray(value)) {
|
|
@@ -1293,7 +1305,7 @@ var PolicyHandler = class extends import_kysely3.OperationNodeTransformer {
|
|
|
1293
1305
|
return selectResult;
|
|
1294
1306
|
}
|
|
1295
1307
|
buildIdConditions(table, rows) {
|
|
1296
|
-
const idFields =
|
|
1308
|
+
const idFields = import_orm4.QueryUtils.requireIdFields(this.client.$schema, table);
|
|
1297
1309
|
return disjunction(this.dialect, rows.map((row) => conjunction(this.dialect, idFields.map((field) => import_kysely3.BinaryOperationNode.create(import_kysely3.ReferenceNode.create(import_kysely3.ColumnNode.create(field), import_kysely3.TableNode.create(table)), import_kysely3.OperatorNode.create("="), import_kysely3.ValueNode.create(row[field]))))));
|
|
1298
1310
|
}
|
|
1299
1311
|
getMutationModel(node) {
|
|
@@ -1302,7 +1314,7 @@ var PolicyHandler = class extends import_kysely3.OperationNodeTransformer {
|
|
|
1302
1314
|
alias: void 0
|
|
1303
1315
|
})).when(import_kysely3.UpdateQueryNode.is, (node2) => {
|
|
1304
1316
|
if (!node2.table) {
|
|
1305
|
-
|
|
1317
|
+
(0, import_common_helpers3.invariant)(false, "Update query must have a table");
|
|
1306
1318
|
}
|
|
1307
1319
|
const r2 = this.extractTableName(node2.table);
|
|
1308
1320
|
return r2 ? {
|
|
@@ -1311,7 +1323,7 @@ var PolicyHandler = class extends import_kysely3.OperationNodeTransformer {
|
|
|
1311
1323
|
} : void 0;
|
|
1312
1324
|
}).when(import_kysely3.DeleteQueryNode.is, (node2) => {
|
|
1313
1325
|
if (node2.from.froms.length !== 1) {
|
|
1314
|
-
throw
|
|
1326
|
+
throw createUnsupportedError("Only one from table is supported for delete");
|
|
1315
1327
|
}
|
|
1316
1328
|
const r2 = this.extractTableName(node2.from.froms[0]);
|
|
1317
1329
|
return r2 ? {
|
|
@@ -1320,7 +1332,7 @@ var PolicyHandler = class extends import_kysely3.OperationNodeTransformer {
|
|
|
1320
1332
|
} : void 0;
|
|
1321
1333
|
}).exhaustive();
|
|
1322
1334
|
if (!r) {
|
|
1323
|
-
|
|
1335
|
+
(0, import_common_helpers3.invariant)(false, `Unable to get table name for query node: ${node}`);
|
|
1324
1336
|
}
|
|
1325
1337
|
return r;
|
|
1326
1338
|
}
|
|
@@ -1404,7 +1416,7 @@ var PolicyHandler = class extends import_kysely3.OperationNodeTransformer {
|
|
|
1404
1416
|
});
|
|
1405
1417
|
}
|
|
1406
1418
|
getModelPolicies(model, operation) {
|
|
1407
|
-
const modelDef =
|
|
1419
|
+
const modelDef = import_orm4.QueryUtils.requireModel(this.client.$schema, model);
|
|
1408
1420
|
const result = [];
|
|
1409
1421
|
const extractOperations = /* @__PURE__ */ __name((expr2) => {
|
|
1410
1422
|
(0, import_common_helpers3.invariant)(import_schema4.ExpressionUtils.isLiteral(expr2), "expecting a literal");
|
|
@@ -1423,7 +1435,7 @@ var PolicyHandler = class extends import_kysely3.OperationNodeTransformer {
|
|
|
1423
1435
|
resolveManyToManyJoinTable(tableName) {
|
|
1424
1436
|
for (const model of Object.values(this.client.$schema.models)) {
|
|
1425
1437
|
for (const field of Object.values(model.fields)) {
|
|
1426
|
-
const m2m =
|
|
1438
|
+
const m2m = import_orm4.QueryUtils.getManyToManyRelation(this.client.$schema, model.name, field.name);
|
|
1427
1439
|
if (m2m?.joinTable === tableName) {
|
|
1428
1440
|
const sortedRecord = [
|
|
1429
1441
|
{
|
|
@@ -1435,8 +1447,8 @@ var PolicyHandler = class extends import_kysely3.OperationNodeTransformer {
|
|
|
1435
1447
|
field: m2m.otherField
|
|
1436
1448
|
}
|
|
1437
1449
|
].sort(this.manyToManySorter);
|
|
1438
|
-
const firstIdFields =
|
|
1439
|
-
const secondIdFields =
|
|
1450
|
+
const firstIdFields = import_orm4.QueryUtils.requireIdFields(this.client.$schema, sortedRecord[0].model);
|
|
1451
|
+
const secondIdFields = import_orm4.QueryUtils.requireIdFields(this.client.$schema, sortedRecord[1].model);
|
|
1440
1452
|
(0, import_common_helpers3.invariant)(firstIdFields.length === 1 && secondIdFields.length === 1, "only single-field id is supported for implicit many-to-many join table");
|
|
1441
1453
|
return {
|
|
1442
1454
|
firstModel: sortedRecord[0].model,
|
|
@@ -1481,17 +1493,17 @@ var check = /* @__PURE__ */ __name((eb, args, { client, model, modelAlias, opera
|
|
|
1481
1493
|
const arg2Node = args.length === 2 ? args[1].toOperationNode() : void 0;
|
|
1482
1494
|
if (arg2Node) {
|
|
1483
1495
|
(0, import_common_helpers4.invariant)(import_kysely4.ValueNode.is(arg2Node) && typeof arg2Node.value === "string", '"operation" parameter must be a string literal when provided');
|
|
1484
|
-
(0, import_common_helpers4.invariant)(
|
|
1496
|
+
(0, import_common_helpers4.invariant)(import_orm5.CRUD.includes(arg2Node.value), '"operation" parameter must be one of "create", "read", "update", "delete"');
|
|
1485
1497
|
}
|
|
1486
|
-
const fieldName =
|
|
1498
|
+
const fieldName = import_orm5.QueryUtils.extractFieldName(arg1Node);
|
|
1487
1499
|
(0, import_common_helpers4.invariant)(fieldName, 'Failed to extract field name from the first argument of "check" function');
|
|
1488
|
-
const fieldDef =
|
|
1500
|
+
const fieldDef = import_orm5.QueryUtils.requireField(client.$schema, model, fieldName);
|
|
1489
1501
|
(0, import_common_helpers4.invariant)(fieldDef.relation, `Field "${fieldName}" is not a relation field in model "${model}"`);
|
|
1490
1502
|
(0, import_common_helpers4.invariant)(!fieldDef.array, `Field "${fieldName}" is a to-many relation, which is not supported by "check"`);
|
|
1491
1503
|
const relationModel = fieldDef.type;
|
|
1492
1504
|
const joinConditions = [];
|
|
1493
|
-
const fkInfo =
|
|
1494
|
-
const idFields =
|
|
1505
|
+
const fkInfo = import_orm5.QueryUtils.getRelationForeignKeyFieldPairs(client.$schema, model, fieldName);
|
|
1506
|
+
const idFields = import_orm5.QueryUtils.requireIdFields(client.$schema, model);
|
|
1495
1507
|
const buildBaseSelect = /* @__PURE__ */ __name((baseModel, field) => {
|
|
1496
1508
|
return eb.selectFrom(baseModel).select(field).where(eb.and(idFields.map((idField) => eb(eb.ref(`${fieldDef.originModel}.${idField}`), "=", eb.ref(`${modelAlias}.${idField}`)))));
|
|
1497
1509
|
}, "buildBaseSelect");
|