Skip to content

Commit a0a6424

Browse files
committed
Revert "fix(policy): skip m2m update-policy check for newly created side on connect"
This reverts commit 59d302d.
1 parent 59d302d commit a0a6424

File tree

3 files changed

+7
-126
lines changed

3 files changed

+7
-126
lines changed

packages/orm/src/client/crud/operations/base.ts

Lines changed: 4 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -554,8 +554,7 @@ export abstract class BaseOperationHandler<Schema extends SchemaDef> {
554554
}
555555

556556
if (fromRelation && m2m) {
557-
// connect many-to-many relation; mark the newly created model so the
558-
// policy plugin can skip the circular update-policy check on it
557+
// connect many-to-many relation
559558
await this.handleManyToManyRelation(
560559
kysely,
561560
'connect',
@@ -566,7 +565,6 @@ export abstract class BaseOperationHandler<Schema extends SchemaDef> {
566565
m2m.otherField,
567566
[createdEntity],
568567
m2m.joinTable,
569-
m2m.otherModel,
570568
);
571569
}
572570

@@ -649,10 +647,6 @@ export abstract class BaseOperationHandler<Schema extends SchemaDef> {
649647
return parentFkFields;
650648
}
651649

652-
// Marker embedded in the INSERT end modifier to signal to the policy plugin
653-
// that one side of the join was just created in this operation.
654-
static readonly M2M_CREATED_MODEL_MARKER_PREFIX = '/*ZS:M2M_CREATED:';
655-
656650
private async handleManyToManyRelation<Action extends 'connect' | 'disconnect'>(
657651
kysely: AnyKysely,
658652
action: Action,
@@ -663,7 +657,6 @@ export abstract class BaseOperationHandler<Schema extends SchemaDef> {
663657
rightField: string,
664658
rightEntities: any[],
665659
joinTable: string,
666-
newlyCreatedModel?: string,
667660
): Promise<void> {
668661
if (rightEntities.length === 0) {
669662
return;
@@ -701,14 +694,6 @@ export abstract class BaseOperationHandler<Schema extends SchemaDef> {
701694
)
702695
// case for `INSERT IGNORE` syntax
703696
.$if(this.dialect.insertIgnoreMethod === 'ignore', (qb) => qb.ignore())
704-
// embed a marker so the policy plugin knows which model was just created
705-
.$if(newlyCreatedModel !== undefined, (qb) =>
706-
qb.modifyEnd(
707-
sql.raw(
708-
`${BaseOperationHandler.M2M_CREATED_MODEL_MARKER_PREFIX}${newlyCreatedModel}*/`,
709-
),
710-
),
711-
)
712697
.execute();
713698
} else {
714699
const eb = expressionBuilder<any, any>();
@@ -844,9 +829,7 @@ export abstract class BaseOperationHandler<Schema extends SchemaDef> {
844829
}
845830

846831
case 'connect': {
847-
// contextModel was just created; pass it so the policy plugin can
848-
// skip the circular update-policy check on the newly created side
849-
await this.connectRelation(kysely, relationModel, subPayload, fromRelationContext, contextModel);
832+
await this.connectRelation(kysely, relationModel, subPayload, fromRelationContext);
850833
break;
851834
}
852835

@@ -856,7 +839,7 @@ export abstract class BaseOperationHandler<Schema extends SchemaDef> {
856839
if (!found) {
857840
await this.create(kysely, relationModel, item.create, fromRelationContext);
858841
} else {
859-
await this.connectRelation(kysely, relationModel, found, fromRelationContext, contextModel);
842+
await this.connectRelation(kysely, relationModel, found, fromRelationContext);
860843
}
861844
}
862845
break;
@@ -1927,13 +1910,7 @@ export abstract class BaseOperationHandler<Schema extends SchemaDef> {
19271910

19281911
// #region relation manipulation
19291912

1930-
protected async connectRelation(
1931-
kysely: AnyKysely,
1932-
model: string,
1933-
data: any,
1934-
fromRelation: FromRelationContext,
1935-
newlyCreatedModel?: string,
1936-
) {
1913+
protected async connectRelation(kysely: AnyKysely, model: string, data: any, fromRelation: FromRelationContext) {
19371914
const _data = this.normalizeRelationManipulationInput(model, data);
19381915
if (_data.length === 0) {
19391916
return;
@@ -1956,7 +1933,6 @@ export abstract class BaseOperationHandler<Schema extends SchemaDef> {
19561933
m2m.otherField!,
19571934
allIds,
19581935
m2m.joinTable,
1959-
newlyCreatedModel,
19601936
);
19611937
} else {
19621938
const { ownedByModel, keyPairs } = getRelationForeignKeyFieldPairs(

packages/plugins/policy/src/policy-handler.ts

Lines changed: 3 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -784,7 +784,6 @@ export class PolicyHandler<Schema extends SchemaDef> extends OperationNodeTransf
784784
for (const values of valueRows) {
785785
if (isManyToManyJoinTable) {
786786
await this.enforcePreCreatePolicyForManyToManyJoinTable(
787-
node,
788787
mutationModel,
789788
fields,
790789
values.map((v) => v.node),
@@ -802,7 +801,6 @@ export class PolicyHandler<Schema extends SchemaDef> extends OperationNodeTransf
802801
}
803802

804803
private async enforcePreCreatePolicyForManyToManyJoinTable(
805-
node: InsertQueryNode,
806804
tableName: string,
807805
fields: string[],
808806
values: OperationNode[],
@@ -825,24 +823,15 @@ export class PolicyHandler<Schema extends SchemaDef> extends OperationNodeTransf
825823
invariant(aValue !== null && aValue !== undefined, 'A value cannot be null or undefined');
826824
invariant(bValue !== null && bValue !== undefined, 'B value cannot be null or undefined');
827825

828-
// If one side was just created in this operation, its update-policy check is
829-
// inherently circular (it has no relations yet), so we skip it. The create
830-
// policy for that side was already verified earlier in the operation.
831-
const newlyCreatedModel = this.extractNewlyCreatedModel(node);
832-
833826
const eb = expressionBuilder<any, any>();
834827

835-
const filterA = newlyCreatedModel === m2m.firstModel
836-
? trueNode(this.dialect)
837-
: this.buildPolicyFilter(m2m.firstModel, undefined, 'update');
828+
const filterA = this.buildPolicyFilter(m2m.firstModel, undefined, 'update');
838829
const queryA = eb
839830
.selectFrom(m2m.firstModel)
840831
.where(eb(eb.ref(`${m2m.firstModel}.${m2m.firstIdField}`), '=', aValue))
841832
.select(() => new ExpressionWrapper(filterA).as('_'));
842833

843-
const filterB = newlyCreatedModel === m2m.secondModel
844-
? trueNode(this.dialect)
845-
: this.buildPolicyFilter(m2m.secondModel, undefined, 'update');
834+
const filterB = this.buildPolicyFilter(m2m.secondModel, undefined, 'update');
846835
const queryB = eb
847836
.selectFrom(m2m.secondModel)
848837
.where(eb(eb.ref(`${m2m.secondModel}.${m2m.secondIdField}`), '=', bValue))
@@ -861,7 +850,7 @@ export class PolicyHandler<Schema extends SchemaDef> extends OperationNodeTransf
861850
if (!result.rows[0]?.$conditionA) {
862851
throw createRejectedByPolicyError(
863852
m2m.firstModel,
864-
RejectedByPolicyReason.NO_ACCESS,
853+
RejectedByPolicyReason.CANNOT_READ_BACK,
865854
`many-to-many relation participant model "${m2m.firstModel}" not updatable`,
866855
);
867856
}
@@ -874,19 +863,6 @@ export class PolicyHandler<Schema extends SchemaDef> extends OperationNodeTransf
874863
}
875864
}
876865

877-
private extractNewlyCreatedModel(node: InsertQueryNode): string | undefined {
878-
const prefix = '/*ZS:M2M_CREATED:';
879-
for (const modifier of node.endModifiers ?? []) {
880-
if (RawNode.is(modifier)) {
881-
const fragment = modifier.sqlFragments[0];
882-
if (fragment?.startsWith(prefix)) {
883-
return fragment.slice(prefix.length, -2); // strip prefix and trailing */
884-
}
885-
}
886-
}
887-
return undefined;
888-
}
889-
890866
private async enforcePreCreatePolicyForOne(
891867
model: string,
892868
fields: string[],

tests/regression/test/issue-2531.test.ts

Lines changed: 0 additions & 71 deletions
This file was deleted.

0 commit comments

Comments
 (0)