4
4
DataModelAttribute ,
5
5
Expression ,
6
6
ExpressionType ,
7
+ isArrayExpr ,
7
8
isDataModel ,
8
9
isDataModelAttribute ,
9
10
isDataModelField ,
@@ -82,6 +83,8 @@ export default class ExpressionValidator implements AstValidator<Expression> {
82
83
node : expr . right ,
83
84
} ) ;
84
85
}
86
+
87
+ this . validateCrossModelFieldComparison ( expr , accept ) ;
85
88
break ;
86
89
}
87
90
@@ -137,6 +140,7 @@ export default class ExpressionValidator implements AstValidator<Expression> {
137
140
accept ( 'error' , 'incompatible operand types' , { node : expr } ) ;
138
141
}
139
142
143
+ this . validateCrossModelFieldComparison ( expr , accept ) ;
140
144
break ;
141
145
}
142
146
@@ -158,43 +162,8 @@ export default class ExpressionValidator implements AstValidator<Expression> {
158
162
break ;
159
163
}
160
164
161
- // not supported:
162
- // - foo.a == bar
163
- // - foo.user.id == userId
164
- // except:
165
- // - future().userId == userId
166
- if (
167
- ( isMemberAccessExpr ( expr . left ) &&
168
- isDataModelField ( expr . left . member . ref ) &&
169
- expr . left . member . ref . $container != getContainingDataModel ( expr ) ) ||
170
- ( isMemberAccessExpr ( expr . right ) &&
171
- isDataModelField ( expr . right . member . ref ) &&
172
- expr . right . member . ref . $container != getContainingDataModel ( expr ) )
173
- ) {
174
- // foo.user.id == auth().id
175
- // foo.user.id == "123"
176
- // foo.user.id == null
177
- // foo.user.id == EnumValue
178
- if ( ! ( this . isNotModelFieldExpr ( expr . left ) || this . isNotModelFieldExpr ( expr . right ) ) ) {
179
- const containingPolicyAttr = findUpAst (
180
- expr ,
181
- ( node ) => isDataModelAttribute ( node ) && [ '@@allow' , '@@deny' ] . includes ( node . decl . $refText )
182
- ) as DataModelAttribute | undefined ;
183
-
184
- if ( containingPolicyAttr ) {
185
- const operation = getAttributeArgLiteral < string > ( containingPolicyAttr , 'operation' ) ;
186
- if ( operation ?. split ( ',' ) . includes ( 'all' ) || operation ?. split ( ',' ) . includes ( 'read' ) ) {
187
- accept (
188
- 'error' ,
189
- 'comparison between fields of different models is not supported in model-level "read" rules' ,
190
- {
191
- node : expr ,
192
- }
193
- ) ;
194
- break ;
195
- }
196
- }
197
- }
165
+ if ( ! this . validateCrossModelFieldComparison ( expr , accept ) ) {
166
+ break ;
198
167
}
199
168
200
169
if (
@@ -262,6 +231,49 @@ export default class ExpressionValidator implements AstValidator<Expression> {
262
231
}
263
232
}
264
233
234
+ private validateCrossModelFieldComparison ( expr : BinaryExpr , accept : ValidationAcceptor ) {
235
+ // not supported in "read" rules:
236
+ // - foo.a == bar
237
+ // - foo.user.id == userId
238
+ // except:
239
+ // - future().userId == userId
240
+ if (
241
+ ( isMemberAccessExpr ( expr . left ) &&
242
+ isDataModelField ( expr . left . member . ref ) &&
243
+ expr . left . member . ref . $container != getContainingDataModel ( expr ) ) ||
244
+ ( isMemberAccessExpr ( expr . right ) &&
245
+ isDataModelField ( expr . right . member . ref ) &&
246
+ expr . right . member . ref . $container != getContainingDataModel ( expr ) )
247
+ ) {
248
+ // foo.user.id == auth().id
249
+ // foo.user.id == "123"
250
+ // foo.user.id == null
251
+ // foo.user.id == EnumValue
252
+ if ( ! ( this . isNotModelFieldExpr ( expr . left ) || this . isNotModelFieldExpr ( expr . right ) ) ) {
253
+ const containingPolicyAttr = findUpAst (
254
+ expr ,
255
+ ( node ) => isDataModelAttribute ( node ) && [ '@@allow' , '@@deny' ] . includes ( node . decl . $refText )
256
+ ) as DataModelAttribute | undefined ;
257
+
258
+ if ( containingPolicyAttr ) {
259
+ const operation = getAttributeArgLiteral < string > ( containingPolicyAttr , 'operation' ) ;
260
+ if ( operation ?. split ( ',' ) . includes ( 'all' ) || operation ?. split ( ',' ) . includes ( 'read' ) ) {
261
+ accept (
262
+ 'error' ,
263
+ 'comparison between fields of different models is not supported in model-level "read" rules' ,
264
+ {
265
+ node : expr ,
266
+ }
267
+ ) ;
268
+ return false ;
269
+ }
270
+ }
271
+ }
272
+ }
273
+
274
+ return true ;
275
+ }
276
+
265
277
private validateCollectionPredicate ( expr : BinaryExpr , accept : ValidationAcceptor ) {
266
278
if ( ! expr . $resolvedType ) {
267
279
accept ( 'error' , 'collection predicate can only be used on an array of model type' , { node : expr } ) ;
@@ -273,9 +285,18 @@ export default class ExpressionValidator implements AstValidator<Expression> {
273
285
return findUpAst ( node , ( n ) => isDataModelAttribute ( n ) && n . decl . $refText === '@@validate' ) ;
274
286
}
275
287
276
- private isNotModelFieldExpr ( expr : Expression ) {
288
+ private isNotModelFieldExpr ( expr : Expression ) : boolean {
277
289
return (
278
- isLiteralExpr ( expr ) || isEnumFieldReference ( expr ) || isNullExpr ( expr ) || this . isAuthOrAuthMemberAccess ( expr )
290
+ // literal
291
+ isLiteralExpr ( expr ) ||
292
+ // enum field
293
+ isEnumFieldReference ( expr ) ||
294
+ // null
295
+ isNullExpr ( expr ) ||
296
+ // `auth()` access
297
+ this . isAuthOrAuthMemberAccess ( expr ) ||
298
+ // array
299
+ ( isArrayExpr ( expr ) && expr . items . every ( ( item ) => this . isNotModelFieldExpr ( item ) ) )
279
300
) ;
280
301
}
281
302
0 commit comments