9
9
isInvocationExpr ,
10
10
isMemberAccessExpr ,
11
11
isReferenceExpr ,
12
+ isThisExpr ,
12
13
isUnaryExpr ,
13
14
MemberAccessExpr ,
14
15
Model ,
@@ -33,9 +34,10 @@ import path from 'path';
33
34
import { FunctionDeclaration , SourceFile , VariableDeclarationKind } from 'ts-morph' ;
34
35
import { name } from '.' ;
35
36
import { isFromStdlib } from '../../language-server/utils' ;
36
- import { getIdFields } from '../../utils/ast-utils' ;
37
+ import { getIdFields , isAuthInvocation } from '../../utils/ast-utils' ;
37
38
import { ALL_OPERATION_KINDS , getDefaultOutputFolder } from '../plugin-utils' ;
38
39
import { ExpressionWriter } from './expression-writer' ;
40
+ import TypeScriptExpressionTransformer from './typescript-expression-transformer' ;
39
41
import { isFutureExpr } from './utils' ;
40
42
import { ZodSchemaGenerator } from './zod-schema-generator' ;
41
43
@@ -332,18 +334,9 @@ export default class PolicyGenerator {
332
334
. addBody ( ) ;
333
335
334
336
// check if any allow or deny rule contains 'auth()' invocation
335
- let hasAuthRef = false ;
336
- for ( const node of [ ...denies , ...allows ] ) {
337
- for ( const child of streamAllContents ( node ) ) {
338
- if ( isInvocationExpr ( child ) && resolved ( child . function ) . name === 'auth' ) {
339
- hasAuthRef = true ;
340
- break ;
341
- }
342
- }
343
- if ( hasAuthRef ) {
344
- break ;
345
- }
346
- }
337
+ const hasAuthRef = [ ...denies , ...allows ] . some ( ( rule ) =>
338
+ streamAllContents ( rule ) . some ( ( child ) => isAuthInvocation ( child ) )
339
+ ) ;
347
340
348
341
if ( hasAuthRef ) {
349
342
const userModel = model . $container . declarations . find (
@@ -365,47 +358,73 @@ export default class PolicyGenerator {
365
358
) ;
366
359
}
367
360
368
- // r = <guard object>;
369
- func . addStatements ( ( writer ) => {
370
- writer . write ( 'return ' ) ;
371
- const exprWriter = new ExpressionWriter ( writer , kind === 'postUpdate' ) ;
372
- const writeDenies = ( ) => {
373
- writer . conditionalWrite ( denies . length > 1 , '{ AND: [' ) ;
374
- denies . forEach ( ( expr , i ) => {
375
- writer . inlineBlock ( ( ) => {
376
- writer . write ( 'NOT: ' ) ;
377
- exprWriter . write ( expr ) ;
378
- } ) ;
379
- writer . conditionalWrite ( i !== denies . length - 1 , ',' ) ;
361
+ const hasFieldAccess = [ ...denies , ...allows ] . some ( ( rule ) =>
362
+ streamAllContents ( rule ) . some (
363
+ ( child ) =>
364
+ // this.???
365
+ isThisExpr ( child ) ||
366
+ // future().???
367
+ isFutureExpr ( child ) ||
368
+ // field reference
369
+ ( isReferenceExpr ( child ) && isDataModelField ( child . target . ref ) )
370
+ )
371
+ ) ;
372
+
373
+ if ( ! hasFieldAccess ) {
374
+ // none of the rules reference model fields, we can compile down to a plain boolean
375
+ // function in this case (so we can skip doing SQL queries when validating)
376
+ func . addStatements ( ( writer ) => {
377
+ const transformer = new TypeScriptExpressionTransformer ( kind === 'postUpdate' ) ;
378
+ denies . forEach ( ( rule ) => {
379
+ writer . write ( `if (${ transformer . transform ( rule , false ) } ) { return false; }` ) ;
380
380
} ) ;
381
- writer . conditionalWrite ( denies . length > 1 , ']}' ) ;
382
- } ;
383
-
384
- const writeAllows = ( ) => {
385
- writer . conditionalWrite ( allows . length > 1 , '{ OR: [' ) ;
386
- allows . forEach ( ( expr , i ) => {
387
- exprWriter . write ( expr ) ;
388
- writer . conditionalWrite ( i !== allows . length - 1 , ',' ) ;
381
+ allows . forEach ( ( rule ) => {
382
+ writer . write ( `if (${ transformer . transform ( rule , false ) } ) { return true; }` ) ;
389
383
} ) ;
390
- writer . conditionalWrite ( allows . length > 1 , ']}' ) ;
391
- } ;
392
-
393
- if ( allows . length > 0 && denies . length > 0 ) {
394
- writer . write ( '{ AND: [' ) ;
395
- writeDenies ( ) ;
396
- writer . write ( ',' ) ;
397
- writeAllows ( ) ;
398
- writer . write ( ']}' ) ;
399
- } else if ( denies . length > 0 ) {
400
- writeDenies ( ) ;
401
- } else if ( allows . length > 0 ) {
402
- writeAllows ( ) ;
403
- } else {
404
- // disallow any operation
405
- writer . write ( `{ ${ GUARD_FIELD_NAME } : false }` ) ;
406
- }
407
- writer . write ( ';' ) ;
408
- } ) ;
384
+ writer . write ( 'return false;' ) ;
385
+ } ) ;
386
+ } else {
387
+ func . addStatements ( ( writer ) => {
388
+ writer . write ( 'return ' ) ;
389
+ const exprWriter = new ExpressionWriter ( writer , kind === 'postUpdate' ) ;
390
+ const writeDenies = ( ) => {
391
+ writer . conditionalWrite ( denies . length > 1 , '{ AND: [' ) ;
392
+ denies . forEach ( ( expr , i ) => {
393
+ writer . inlineBlock ( ( ) => {
394
+ writer . write ( 'NOT: ' ) ;
395
+ exprWriter . write ( expr ) ;
396
+ } ) ;
397
+ writer . conditionalWrite ( i !== denies . length - 1 , ',' ) ;
398
+ } ) ;
399
+ writer . conditionalWrite ( denies . length > 1 , ']}' ) ;
400
+ } ;
401
+
402
+ const writeAllows = ( ) => {
403
+ writer . conditionalWrite ( allows . length > 1 , '{ OR: [' ) ;
404
+ allows . forEach ( ( expr , i ) => {
405
+ exprWriter . write ( expr ) ;
406
+ writer . conditionalWrite ( i !== allows . length - 1 , ',' ) ;
407
+ } ) ;
408
+ writer . conditionalWrite ( allows . length > 1 , ']}' ) ;
409
+ } ;
410
+
411
+ if ( allows . length > 0 && denies . length > 0 ) {
412
+ writer . write ( '{ AND: [' ) ;
413
+ writeDenies ( ) ;
414
+ writer . write ( ',' ) ;
415
+ writeAllows ( ) ;
416
+ writer . write ( ']}' ) ;
417
+ } else if ( denies . length > 0 ) {
418
+ writeDenies ( ) ;
419
+ } else if ( allows . length > 0 ) {
420
+ writeAllows ( ) ;
421
+ } else {
422
+ // disallow any operation
423
+ writer . write ( `{ ${ GUARD_FIELD_NAME } : false }` ) ;
424
+ }
425
+ writer . write ( ';' ) ;
426
+ } ) ;
427
+ }
409
428
return func ;
410
429
}
411
430
}
0 commit comments