@@ -6,7 +6,16 @@ import { QueryFragment } from '~/core/database/query';
6
6
import { withoutScope } from '../../dto' ;
7
7
import { RoleCondition } from '../../policies/conditions/role.condition' ;
8
8
import { Permission } from '../builder/perm-granter' ;
9
- import { all , any , CalculatedCondition , OrConditions } from '../conditions' ;
9
+ import {
10
+ AggregateConditions ,
11
+ all ,
12
+ AndConditions ,
13
+ any ,
14
+ CalculatedCondition ,
15
+ Condition ,
16
+ OrConditions ,
17
+ } from '../conditions' ;
18
+ import { visitCondition } from '../conditions/condition-visitor' ;
10
19
import { PolicyFactory } from '../policy.factory' ;
11
20
import { ConditionOptimizer } from './condition-optimizer' ;
12
21
@@ -17,6 +26,10 @@ export interface ResolveParams {
17
26
prop ?: string ;
18
27
calculatedAsCondition ?: boolean ;
19
28
optimizeConditions ?: boolean ;
29
+ /**
30
+ * A function to partially resolve conditions.
31
+ */
32
+ conditionResolver ?: ( condition : Condition ) => boolean | undefined ;
20
33
}
21
34
22
35
export interface FilterOptions {
@@ -38,6 +51,7 @@ export class PolicyExecutor {
38
51
prop,
39
52
calculatedAsCondition,
40
53
optimizeConditions = false ,
54
+ conditionResolver,
41
55
} : ResolveParams ) : Permission {
42
56
if ( action !== 'read' ) {
43
57
if ( prop ) {
@@ -80,12 +94,20 @@ export class PolicyExecutor {
80
94
if ( conditions . length === 0 ) {
81
95
return false ;
82
96
}
83
- const merged = OrConditions . fromAll ( conditions , {
97
+ let condition = OrConditions . fromAll ( conditions , {
84
98
optimize : optimizeConditions ,
85
99
} ) ;
86
- return optimizeConditions
87
- ? this . conditionOptimizer . optimize ( merged )
88
- : merged ;
100
+ if ( conditionResolver ) {
101
+ const resolved = this . partialResolve ( condition , conditionResolver ) ;
102
+ if ( typeof resolved === 'boolean' ) {
103
+ return resolved ;
104
+ }
105
+ condition = resolved ;
106
+ }
107
+ if ( optimizeConditions ) {
108
+ condition = this . conditionOptimizer . optimize ( condition ) ;
109
+ }
110
+ return condition ;
89
111
}
90
112
91
113
forEdgeDB ( {
@@ -178,6 +200,54 @@ export class PolicyExecutor {
178
200
} ;
179
201
}
180
202
203
+ private partialResolve (
204
+ condition : Condition ,
205
+ resolver : ( condition : Condition ) => boolean | undefined ,
206
+ ) : Permission {
207
+ const partialResolutions = new Map < Condition , boolean > ( ) ;
208
+ visitCondition ( condition , ( cc ) => {
209
+ const result = resolver ( cc ) ;
210
+ if ( result != null ) {
211
+ partialResolutions . set ( cc , result ) ;
212
+ }
213
+ } ) ;
214
+
215
+ const walkAndReform = ( c : Condition ) : Permission => {
216
+ if ( partialResolutions . has ( c ) ) {
217
+ return partialResolutions . get ( c ) ! ;
218
+ }
219
+
220
+ if ( ! ( c instanceof AggregateConditions ) ) {
221
+ return c ;
222
+ }
223
+
224
+ let changed = false ;
225
+ const children = c . conditions . map ( ( sub ) => {
226
+ const next = walkAndReform ( sub ) ;
227
+ if ( next !== sub ) {
228
+ changed = true ;
229
+ }
230
+ return next ;
231
+ } ) ;
232
+ // Only change aggregate identity if children have changed
233
+ if ( ! changed ) {
234
+ return c ;
235
+ }
236
+ if ( c instanceof AndConditions ) {
237
+ return children . some ( ( perm ) => perm === false )
238
+ ? false
239
+ : all ( ...children . filter ( ( perm ) : perm is Condition => perm !== true ) ) ;
240
+ } else {
241
+ return children . some ( ( perm ) => perm === true )
242
+ ? true
243
+ : any (
244
+ ...children . filter ( ( perm ) : perm is Condition => perm !== false ) ,
245
+ ) ;
246
+ }
247
+ } ;
248
+ return walkAndReform ( condition ) ;
249
+ }
250
+
181
251
@CachedByArg ( { weak : true } )
182
252
getPolicies ( session : Session ) {
183
253
const policies = this . policyFactory . getPolicies ( ) . filter ( ( policy ) => {
0 commit comments