• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

casbin / node-casbin / 20025047050

08 Dec 2025 10:33AM UTC coverage: 78.172%. First build
20025047050

Pull #518

github

web-flow
Merge 755b02a13 into 7801732d5
Pull Request #518: Support multiple policy definitions (p, p2, p3) in enforcement

832 of 1163 branches covered (71.54%)

Branch coverage included in aggregate %.

64 of 72 new or added lines in 1 file covered. (88.89%)

1614 of 1966 relevant lines covered (82.1%)

353.71 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

76.94
/src/coreEnforcer.ts
1
// Copyright 2018 The Casbin Authors. All Rights Reserved.
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
//      http://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14

15
import { compile, compileAsync, addBinaryOp } from '@casbin/expression-eval';
32✔
16

17
import { DefaultEffector, Effect, Effector } from './effect';
32✔
18
import { FunctionMap, Model, newModelFromFile, PolicyOp } from './model';
32✔
19
import { Adapter, FilteredAdapter, Watcher, BatchAdapter, UpdatableAdapter, WatcherEx } from './persist';
20
import { DefaultRoleManager, RoleManager } from './rbac';
32✔
21
import { EnforceContext } from './enforceContext';
32✔
22

23
import {
32✔
24
  escapeAssertion,
25
  generateGFunction,
26
  generateSyncedGFunction,
27
  getEvalValue,
28
  hasEval,
29
  replaceEval,
30
  generatorRunSync,
31
  generatorRunAsync,
32
  customIn,
33
  bracketCompatible,
34
  removeComments,
35
} from './util';
36
import { getLogger, logPrint } from './log';
32✔
37
import { MatchingFunc } from './rbac';
38
import { FileSystem, getDefaultFileSystem } from './persist';
39

40
type Matcher = ((context: any) => Promise<any>) | ((context: any) => any);
41

42
type EnforceResult = Generator<(boolean | [boolean, string[]]) | Promise<boolean | [boolean, string[]]>>;
43

44
/**
45
 * CoreEnforcer defines the core functionality of an enforcer.
46
 */
47
export class CoreEnforcer {
32✔
48
  protected modelPath: string;
49
  protected model: Model;
50
  protected fm: FunctionMap = FunctionMap.loadFunctionMap();
298✔
51
  protected eft: Effector = new DefaultEffector();
298✔
52
  private matcherMap: Map<string, Matcher> = new Map();
298✔
53
  private defaultEnforceContext: EnforceContext = new EnforceContext('r', 'p', 'e', 'm');
298✔
54

55
  protected adapter: UpdatableAdapter | FilteredAdapter | Adapter | BatchAdapter;
56
  protected watcher: Watcher | null = null;
298✔
57
  protected watcherEx: WatcherEx | null = null;
298✔
58
  protected rmMap: Map<string, RoleManager>;
59

60
  protected enabled = true;
298✔
61
  protected autoSave = true;
298✔
62
  protected autoBuildRoleLinks = true;
298✔
63
  protected autoNotifyWatcher = true;
298✔
64
  protected acceptJsonRequest = false;
298✔
65
  protected fs?: FileSystem;
66

67
  /**
68
   * setFileSystem sets a file system to read the model file or the policy file.
69
   * @param fs {@link FileSystem}
70
   */
71
  public setFileSystem(fs: FileSystem): void {
72
    this.fs = fs;
2✔
73
  }
74

75
  /**
76
   * getFileSystem gets the file system,
77
   */
78
  public getFileSystem(): FileSystem | undefined {
79
    return this.fs;
2✔
80
  }
81

82
  private getExpression(asyncCompile: boolean, exp: string): Matcher {
83
    const matcherKey = `${asyncCompile ? 'ASYNC[' : 'SYNC['}${exp}]`;
1,144✔
84

85
    addBinaryOp('in', 1, customIn);
1,144✔
86

87
    let expression = this.matcherMap.get(matcherKey);
1,144✔
88
    if (!expression) {
1,144✔
89
      exp = bracketCompatible(exp);
166✔
90
      expression = asyncCompile ? compileAsync(exp) : compile(exp);
166✔
91
      this.matcherMap.set(matcherKey, expression);
166✔
92
    }
93
    return expression;
1,144✔
94
  }
95

96
  /**
97
   * loadModel reloads the model from the model CONF file.
98
   * Because the policy is attached to a model,
99
   * so the policy is invalidated and needs to be reloaded by calling LoadPolicy().
100
   */
101
  public loadModel(): void {
102
    this.model = newModelFromFile(this.modelPath, this.fs);
×
103
    this.model.printModel();
×
104
  }
105

106
  /**
107
   * getModel gets the current model.
108
   *
109
   * @return the model of the enforcer.
110
   */
111
  public getModel(): Model {
112
    return this.model;
12✔
113
  }
114

115
  /**
116
   * setModel sets the current model.
117
   *
118
   * @param m the model.
119
   */
120
  public setModel(m: Model): void {
121
    this.model = m;
6✔
122
  }
123

124
  /**
125
   * getAdapter gets the current adapter.
126
   *
127
   * @return the adapter of the enforcer.
128
   */
129
  public getAdapter(): Adapter {
130
    return this.adapter;
2✔
131
  }
132

133
  /**
134
   * setAdapter sets the current adapter.
135
   *
136
   * @param adapter the adapter.
137
   */
138
  public setAdapter(adapter: Adapter): void {
139
    this.adapter = adapter;
34✔
140
  }
141

142
  /**
143
   * setWatcher sets the current watcher.
144
   *
145
   * @param watcher the watcher.
146
   */
147
  public setWatcher(watcher: Watcher): void {
148
    this.watcher = watcher;
×
149
    watcher.setUpdateCallback(async () => await this.loadPolicy());
×
150
  }
151

152
  /**
153
   * setWatcherEx sets the current watcherEx.
154
   *
155
   * @param watcherEx the watcherEx.
156
   */
157
  public setWatcherEx(watcherEx: WatcherEx): void {
158
    this.watcherEx = watcherEx;
×
159
  }
160

161
  /**
162
   * setRoleManager sets the current role manager.
163
   *
164
   * @param rm the role manager.
165
   */
166
  public setRoleManager(rm: RoleManager): void {
167
    this.rmMap.set('g', rm);
×
168
  }
169

170
  /**
171
   * setRoleManager sets the role manager for the named policy.
172
   *
173
   * @param ptype the named policy.
174
   * @param rm the role manager.
175
   */
176
  public setNamedRoleManager(ptype: string, rm: RoleManager): void {
177
    this.rmMap.set(ptype, rm);
×
178
  }
179

180
  /**
181
   * getRoleManager gets the current role manager.
182
   */
183
  public getRoleManager(): RoleManager {
184
    return <RoleManager>this.rmMap.get('g');
8✔
185
  }
186

187
  /**
188
   * getNamedRoleManager gets role manager by name.
189
   */
190
  public getNamedRoleManager(name: string): RoleManager | undefined {
191
    return this.rmMap.get(name);
2✔
192
  }
193

194
  /**
195
   * setEffector sets the current effector.
196
   *
197
   * @param eft the effector.
198
   */
199
  public setEffector(eft: Effector): void {
200
    this.eft = eft;
×
201
  }
202

203
  /**
204
   * clearPolicy clears all policy.
205
   */
206
  public clearPolicy(): void {
207
    this.model.clearPolicy();
4✔
208
  }
209

210
  public initRmMap(): void {
211
    this.rmMap = new Map<string, RoleManager>();
300✔
212
    const rm = this.model.model.get('g');
300✔
213
    if (rm) {
300✔
214
      for (const ptype of rm.keys()) {
194✔
215
        this.rmMap.set(ptype, new DefaultRoleManager(10));
204✔
216
      }
217
    }
218
  }
219

220
  public sortPolicies(): void {
221
    this.model.model.get('p')?.forEach((value, key) => {
292✔
222
      const policy = value.policy;
298✔
223
      const tokens = value.tokens;
298✔
224
      if (policy && tokens) {
298!
225
        const priorityIndex = tokens.indexOf(`${key}_priority`);
298✔
226
        if (priorityIndex !== -1) {
298✔
227
          policy.sort((a, b) => {
6✔
228
            return parseInt(a[priorityIndex], 10) - parseInt(b[priorityIndex], 10);
84✔
229
          });
230
        }
231
      }
232
    });
233
  }
234

235
  /**
236
   * loadPolicy reloads the policy from file/database.
237
   */
238
  public async loadPolicy(): Promise<void> {
239
    this.model.clearPolicy();
292✔
240
    await this.adapter.loadPolicy(this.model);
292✔
241

242
    this.sortPolicies();
292✔
243
    this.model.sortPoliciesBySubjectHierarchy();
292✔
244

245
    if (this.autoBuildRoleLinks) {
292!
246
      await this.buildRoleLinksInternal();
292✔
247
    }
248
  }
249

250
  /**
251
   * loadFilteredPolicy reloads a filtered policy from file/database.
252
   *
253
   * @param filter the filter used to specify which type of policy should be loaded.
254
   */
255
  // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
256
  public async loadFilteredPolicy(filter: any): Promise<boolean> {
257
    this.model.clearPolicy();
×
258

259
    this.sortPolicies();
×
260
    this.model.sortPoliciesBySubjectHierarchy();
×
261

262
    return this.loadIncrementalFilteredPolicy(filter);
×
263
  }
264

265
  /**
266
   * LoadIncrementalFilteredPolicy append a filtered policy from file/database.
267
   *
268
   * @param filter the filter used to specify which type of policy should be appended.
269
   */
270
  // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
271
  public async loadIncrementalFilteredPolicy(filter: any): Promise<boolean> {
272
    if ('isFiltered' in this.adapter) {
×
273
      await this.adapter.loadFilteredPolicy(this.model, filter);
×
274
    } else {
275
      throw new Error('filtered policies are not supported by this adapter');
×
276
    }
277

278
    this.sortPolicies();
×
279

280
    if (this.autoBuildRoleLinks) {
×
281
      await this.buildRoleLinksInternal();
×
282
    }
283
    return true;
×
284
  }
285

286
  /**
287
   * isFiltered returns true if the loaded policy has been filtered.
288
   *
289
   * @return if the loaded policy has been filtered.
290
   */
291
  public isFiltered(): boolean {
292
    if ('isFiltered' in this.adapter) {
2!
293
      return this.adapter.isFiltered();
×
294
    }
295
    return false;
2✔
296
  }
297

298
  /**
299
   * savePolicy saves the current policy (usually after changed with
300
   * Casbin API) back to file/database.
301
   */
302
  public async savePolicy(): Promise<boolean> {
303
    if (this.isFiltered()) {
2!
304
      throw new Error('Cannot save a filtered policy');
×
305
    }
306
    const flag = await this.adapter.savePolicy(this.model);
2✔
307
    if (!flag) {
2!
308
      return false;
×
309
    }
310
    if (this.watcherEx) {
2!
311
      return await this.watcherEx.updateForSavePolicy(this.model);
×
312
    } else if (this.watcher) {
2!
313
      return await this.watcher.update();
×
314
    }
315
    return true;
2✔
316
  }
317

318
  /**
319
   * enableEnforce changes the enforcing state of Casbin, when Casbin is
320
   * disabled, all access will be allowed by the enforce() function.
321
   *
322
   * @param enable whether to enable the enforcer.
323
   */
324
  public enableEnforce(enable: boolean): void {
325
    this.enabled = enable;
4✔
326
  }
327

328
  /**
329
   * enableLog changes whether to print Casbin log to the standard output.
330
   *
331
   * @param enable whether to enable Casbin's log.
332
   */
333
  public enableLog(enable: boolean): void {
334
    getLogger().enableLog(enable);
2✔
335
  }
336

337
  /**
338
   * enableAutoSave controls whether to save a policy rule automatically to
339
   * the adapter when it is added or removed.
340
   *
341
   * @param autoSave whether to enable the AutoSave feature.
342
   */
343
  public enableAutoSave(autoSave: boolean): void {
344
    this.autoSave = autoSave;
4✔
345
  }
346

347
  /**
348
   * enableAutoNotifyWatcher controls whether to save a policy rule automatically notify the Watcher when it is added or removed.
349
   * @param enable whether to enable the AutoNotifyWatcher feature.
350
   */
351
  public enableAutoNotifyWatcher(enable: boolean): void {
352
    this.autoNotifyWatcher = enable;
×
353
  }
354

355
  /**
356
   * enableAcceptJsonRequest determines whether to attempt parsing request args as JSON
357
   *
358
   * @param enable whether to attempt parsing request args as JSON
359
   */
360
  public enableAcceptJsonRequest(enable: boolean): void {
361
    this.acceptJsonRequest = enable;
4✔
362
  }
363

364
  /**
365
   * enableAutoBuildRoleLinks controls whether to save a policy rule
366
   * automatically to the adapter when it is added or removed.
367
   *
368
   * @param autoBuildRoleLinks whether to automatically build the role links.
369
   */
370
  public enableAutoBuildRoleLinks(autoBuildRoleLinks: boolean): void {
371
    this.autoBuildRoleLinks = autoBuildRoleLinks;
2✔
372
  }
373

374
  /**
375
   * add matching function to RoleManager by ptype
376
   * @param ptype g
377
   * @param fn the function will be added
378
   */
379
  public async addNamedMatchingFunc(ptype: string, fn: MatchingFunc): Promise<void> {
380
    const rm = this.rmMap.get(ptype);
4✔
381
    if (rm) {
4!
382
      return await (<DefaultRoleManager>rm).addMatchingFunc(fn);
4✔
383
    }
384

385
    throw Error('Target ptype not found.');
×
386
  }
387

388
  /**
389
   * add domain matching function to RoleManager by ptype
390
   * @param ptype g
391
   * @param fn the function will be added
392
   */
393
  public async addNamedDomainMatchingFunc(ptype: string, fn: MatchingFunc): Promise<void> {
394
    const rm = this.rmMap.get(ptype);
×
395
    if (rm) {
×
396
      return await (<DefaultRoleManager>rm).addDomainMatchingFunc(fn);
×
397
    }
398
  }
399

400
  /**
401
   * buildRoleLinks manually rebuild the role inheritance relations.
402
   */
403
  public async buildRoleLinks(): Promise<void> {
404
    return this.buildRoleLinksInternal();
4✔
405
  }
406

407
  /**
408
   * buildIncrementalRoleLinks provides incremental build the role inheritance relations.
409
   * @param op policy operation
410
   * @param ptype g
411
   * @param rules policies
412
   */
413
  public async buildIncrementalRoleLinks(op: PolicyOp, ptype: string, rules: string[][]): Promise<void> {
414
    let rm = this.rmMap.get(ptype);
56✔
415
    if (!rm) {
56!
416
      rm = new DefaultRoleManager(10);
×
417
      this.rmMap.set(ptype, rm);
×
418
    }
419
    await this.model.buildIncrementalRoleLinks(rm, op, 'g', ptype, rules);
56✔
420
  }
421

422
  protected async buildRoleLinksInternal(): Promise<void> {
423
    for (const rm of this.rmMap.values()) {
296✔
424
      await rm.clear();
192✔
425
      await this.model.buildRoleLinks(this.rmMap);
192✔
426
    }
427
  }
428

429
  private *privateEnforce(
430
    asyncCompile = true,
×
431
    explain = false,
×
432
    matcher: string,
433
    enforceContext: EnforceContext = new EnforceContext('r', 'p', 'e', 'm'),
×
434
    ...rvals: any[]
435
  ): EnforceResult {
436
    if (!this.enabled) {
1,110✔
437
      return true;
16✔
438
    }
439

440
    let explainIndex = -1;
1,094✔
441
    let explainPtype = '';
1,094✔
442

443
    const functions: { [key: string]: any } = {};
1,094✔
444
    this.fm.getFunctions().forEach((value: any, key: string) => {
1,094✔
445
      functions[key] = value;
10,944✔
446
    });
447

448
    const astMap = this.model.model.get('g');
1,094✔
449

450
    astMap?.forEach((value, key) => {
1,094✔
451
      const rm = value.rm;
636✔
452
      functions[key] = asyncCompile ? generateGFunction(rm) : generateSyncedGFunction(rm);
636✔
453
    });
454

455
    let expString;
456

457
    if (!matcher) {
1,094✔
458
      expString = this.model.model.get('m')?.get(enforceContext.mType)?.value;
1,084!
459
    } else {
460
      expString = removeComments(escapeAssertion(matcher));
10✔
461
    }
462

463
    if (!expString) {
1,094!
464
      throw new Error('Unable to find matchers in model');
×
465
    }
466

467
    const effectExpr = this.model.model.get('e')?.get(enforceContext.eType)?.value;
1,094!
468
    if (!effectExpr) {
1,094!
469
      throw new Error('Unable to find policy_effect in model');
×
470
    }
471

472
    const HasEval: boolean = hasEval(expString);
1,094✔
473
    let expression: Matcher | undefined = undefined;
1,094✔
474

475
    const policyMap = this.model.model.get('p');
1,094✔
476
    const rTokens = this.model.model.get('r')?.get(enforceContext.rType)?.tokens;
1,094!
477
    const rTokensLen = rTokens?.length;
1,094!
478

479
    const effectStream = this.eft.newStream(effectExpr);
1,094✔
480

481
    // Get all policy types from the 'p' section
482
    const policyTypes: string[] = policyMap ? Array.from(policyMap.keys()) : [];
1,094!
483

484
    // Check if we have any policies to evaluate
485
    const hasPolicies = policyTypes.some((ptype) => {
1,094✔
486
      const policyDef = policyMap?.get(ptype);
1,094!
487
      return policyDef && policyDef.policy && policyDef.policy.length > 0;
1,094✔
488
    });
489

490
    let effectDone = false;
1,094✔
491
    if (hasPolicies) {
1,094✔
492
      // Iterate through all policy types (p, p2, p3, etc.)
493
      for (const ptype of policyTypes) {
1,014✔
494
        if (effectDone) {
1,054✔
495
          break;
12✔
496
        }
497

498
        const policyDef = policyMap?.get(ptype);
1,042!
499
        if (!policyDef || !policyDef.policy) {
1,042!
NEW
500
          continue;
×
501
        }
502

503
        const policyLen = policyDef.policy.length;
1,042✔
504

505
        // Iterate through all policies of this type
506
        for (let i = 0; i < policyLen; i++) {
1,042✔
507
          const parameters: { [key: string]: any } = {};
3,002✔
508

509
          if (rTokens?.length !== rvals.length) {
3,002!
NEW
510
            throw new Error(`invalid request size: expected ${rTokensLen}, got ${rvals.length}, rvals: ${rvals}"`);
×
511
          }
512

513
          if (this.acceptJsonRequest) {
3,002✔
514
            // Attempt to parse each request parameter as JSON; continue with string if failed
515
            rTokens.forEach((token, j) => {
44✔
516
              try {
132✔
517
                parameters[token] = JSON.parse(rvals[j]);
132✔
518
              } catch {
519
                parameters[token] = rvals[j];
88✔
520
              }
521
            });
522
          } else {
523
            rTokens.forEach((token, j) => {
2,958✔
524
              parameters[token] = rvals[j];
9,300✔
525
            });
526
          }
527

528
          // Add tokens for the current policy type
529
          policyDef.tokens.forEach((token, j) => {
3,002✔
530
            parameters[token] = policyDef.policy[i][j];
10,038✔
531
          });
532

533
          if (HasEval) {
3,002✔
534
            const ruleNames: string[] = getEvalValue(expString);
124✔
535
            let expWithRule = expString;
124✔
536
            for (const ruleName of ruleNames) {
124✔
537
              if (ruleName in parameters) {
130!
538
                const rule = escapeAssertion(parameters[ruleName]);
130✔
539
                expWithRule = replaceEval(expWithRule, ruleName, rule);
130✔
540
              } else {
NEW
541
                throw new Error(`${ruleName} not in ${parameters}`);
×
542
              }
543
            }
544
            expression = this.getExpression(asyncCompile, expWithRule);
124✔
545
          } else {
546
            if (expression === undefined) {
2,878✔
547
              expression = this.getExpression(asyncCompile, expString);
940✔
548
            }
549
          }
550

551
          const context = { ...parameters, ...functions };
3,002✔
552
          const result = asyncCompile ? yield expression(context) : expression(context);
3,002✔
553

554
          let eftRes: Effect;
555
          switch (typeof result) {
3,002✔
556
            case 'boolean':
3,002!
557
              eftRes = result ? Effect.Allow : Effect.Indeterminate;
2,996✔
558
              break;
2,996✔
559
            case 'number':
NEW
560
              if (result === 0) {
×
NEW
561
                eftRes = Effect.Indeterminate;
×
562
              } else {
NEW
563
                eftRes = result;
×
564
              }
NEW
565
              break;
×
566
            case 'string':
567
              if (result === '') {
6✔
568
                eftRes = Effect.Indeterminate;
4✔
569
              } else {
570
                eftRes = Effect.Allow;
2✔
571
              }
572
              break;
6✔
573
            default:
NEW
574
              throw new Error('matcher result should only be of type boolean, number, or string');
×
575
          }
576

577
          const eft = parameters[`${ptype}_eft`];
3,002✔
578
          if (eft && eftRes === Effect.Allow) {
3,002✔
579
            if (eft === 'allow') {
88✔
580
              eftRes = Effect.Allow;
56✔
581
            } else if (eft === 'deny') {
32✔
582
              eftRes = Effect.Deny;
30✔
583
            } else {
584
              eftRes = Effect.Indeterminate;
2✔
585
            }
586
          }
587

588
          const [res, rec, done] = effectStream.pushEffect(eftRes);
3,002✔
589

590
          if (rec) {
3,002✔
591
            explainIndex = i;
492✔
592
            explainPtype = ptype;
492✔
593
          }
594

595
          if (done) {
3,002✔
596
            effectDone = true;
478✔
597
            break;
478✔
598
          }
599
        }
600
      }
601
    } else {
602
      explainIndex = 0;
80✔
603

604
      const parameters: { [key: string]: any } = {};
80✔
605

606
      rTokens?.forEach((token, j): void => {
80!
607
        parameters[token] = rvals[j];
238✔
608
      });
609

610
      // Add empty tokens for all policy types
611
      if (policyMap) {
80!
612
        for (const ptype of policyTypes) {
80✔
613
          const policyDef = policyMap.get(ptype);
80✔
614
          policyDef?.tokens?.forEach((token) => {
80!
615
            parameters[token] = '';
240✔
616
          });
617
        }
618
      }
619

620
      expression = this.getExpression(asyncCompile, expString);
80✔
621
      const context = { ...parameters, ...functions };
80✔
622
      const result = asyncCompile ? yield expression(context) : expression(context);
80!
623

624
      if (result) {
80✔
625
        effectStream.pushEffect(Effect.Allow);
18✔
626
      } else {
627
        effectStream.pushEffect(Effect.Indeterminate);
62✔
628
      }
629
    }
630

631
    const res = effectStream.current();
1,094✔
632

633
    // only generate the request --> result string if the message
634
    // is going to be logged.
635
    if (getLogger().isEnable()) {
1,094✔
636
      let reqStr = 'Request: ';
16✔
637
      for (let i = 0; i < rvals.length; i++) {
16✔
638
        if (i !== rvals.length - 1) {
48✔
639
          reqStr += `${rvals[i]}, `;
32✔
640
        } else {
641
          reqStr += rvals[i];
16✔
642
        }
643
      }
644
      reqStr += ` ---> ${res}`;
16✔
645
      logPrint(reqStr);
16✔
646
    }
647

648
    if (explain) {
1,094✔
649
      if (explainIndex === -1) {
58✔
650
        return [res, []];
14✔
651
      }
652
      const explainPolicy = policyMap?.get(explainPtype);
44!
653
      return [res, explainPolicy?.policy?.[explainIndex] || []];
44!
654
    }
655

656
    return res;
1,036✔
657
  }
658

659
  /**
660
   * If the matchers does not contain an asynchronous method, call it faster.
661
   *
662
   * enforceSync decides whether a "subject" can access a "object" with
663
   * the operation "action", input parameters are usually: (sub, obj, act).
664
   *
665
   * @param rvals the request needs to be mediated, usually an array
666
   *              of strings, can be class instances if ABAC is used.
667
   * @return whether to allow the request.
668
   */
669
  public enforceSync(...rvals: any[]): boolean {
670
    if (rvals[0] instanceof EnforceContext) {
126!
671
      const enforceContext: EnforceContext = rvals.shift();
×
672
      return generatorRunSync(this.privateEnforce(false, false, '', enforceContext, ...rvals));
×
673
    }
674
    return generatorRunSync(this.privateEnforce(false, false, '', this.defaultEnforceContext, ...rvals));
126✔
675
  }
676

677
  /**
678
   * If the matchers does not contain an asynchronous method, call it faster.
679
   *
680
   * enforceSync decides whether a "subject" can access a "object" with
681
   * the operation "action", input parameters are usually: (sub, obj, act).
682
   *
683
   * @param rvals the request needs to be mediated, usually an array
684
   *              of strings, can be class instances if ABAC is used.
685
   * @return whether to allow the request and the reason rule.
686
   */
687
  public enforceExSync(...rvals: any[]): [boolean, string[]] {
688
    if (rvals[0] instanceof EnforceContext) {
4!
689
      const enforceContext: EnforceContext = rvals.shift();
×
690
      return generatorRunSync(this.privateEnforce(false, true, '', enforceContext, ...rvals));
×
691
    }
692
    return generatorRunSync(this.privateEnforce(false, true, '', this.defaultEnforceContext, ...rvals));
4✔
693
  }
694

695
  /**
696
   * Same as enforceSync. To be removed.
697
   */
698
  public enforceWithSyncCompile(...rvals: any[]): boolean {
699
    return this.enforceSync(...rvals);
×
700
  }
701

702
  /**
703
   * enforce decides whether a "subject" can access a "object" with
704
   * the operation "action", input parameters are usually: (sub, obj, act).
705
   *
706
   * @param rvals the request needs to be mediated, usually an array
707
   *              of strings, can be class instances if ABAC is used.
708
   * @return whether to allow the request.
709
   */
710
  public async enforce(...rvals: any[]): Promise<boolean> {
711
    if (rvals[0] instanceof EnforceContext) {
916✔
712
      const enforceContext: EnforceContext = rvals.shift();
8✔
713
      return generatorRunAsync(this.privateEnforce(true, false, '', enforceContext, ...rvals));
8✔
714
    }
715
    return generatorRunAsync(this.privateEnforce(true, false, '', this.defaultEnforceContext, ...rvals));
908✔
716
  }
717

718
  /**
719
   * enforceWithMatcher decides whether a "subject" can access a "object" with
720
   * the operation "action" but with the matcher passed,
721
   * input parameters are usually: (matcher, sub, obj, act).
722
   *
723
   * @param matcher matcher string.
724
   * @param rvals the request needs to be mediated, usually an array
725
   *              of strings, can be class instances if ABAC is used.
726
   * @return whether to allow the request.
727
   */
728
  public async enforceWithMatcher(matcher: string, ...rvals: any[]): Promise<boolean> {
729
    if (rvals[0] instanceof EnforceContext) {
10!
730
      const enforceContext: EnforceContext = rvals.shift();
×
731
      return generatorRunAsync(this.privateEnforce(true, false, matcher, enforceContext, ...rvals));
×
732
    }
733
    return generatorRunAsync(this.privateEnforce(true, false, matcher, this.defaultEnforceContext, ...rvals));
10✔
734
  }
735

736
  /**
737
   * enforce decides whether a "subject" can access a "object" with
738
   * the operation "action", input parameters are usually: (sub, obj, act).
739
   *
740
   * @param rvals the request needs to be mediated, usually an array
741
   *              of strings, can be class instances if ABAC is used.
742
   * @return whether to allow the request and the reason rule.
743
   */
744
  public async enforceEx(...rvals: any[]): Promise<[boolean, string[]]> {
745
    if (rvals[0] instanceof EnforceContext) {
54✔
746
      const enforceContext: EnforceContext = rvals.shift();
8✔
747
      return generatorRunAsync(this.privateEnforce(true, true, '', enforceContext, ...rvals));
8✔
748
    }
749
    return generatorRunAsync(this.privateEnforce(true, true, '', this.defaultEnforceContext, ...rvals));
46✔
750
  }
751

752
  /**
753
   * enforceExWithMatcher decides whether a "subject" can access a "object" with
754
   * the operation "action" but with the matcher passed,
755
   *  input parameters are usually: (matcher, sub, obj, act).
756
   *
757
   * @param matcher matcher string.
758
   * @param rvals the request needs to be mediated, usually an array
759
   *              of strings, can be class instances if ABAC is used.
760
   * @return whether to allow the request and the reason rule.
761
   */
762
  public async enforceExWithMatcher(matcher: string, ...rvals: any[]): Promise<[boolean, string[]]> {
763
    if (rvals[0] instanceof EnforceContext) {
×
764
      const enforceContext: EnforceContext = rvals.shift();
×
765
      return generatorRunAsync(this.privateEnforce(true, true, matcher, enforceContext, ...rvals));
×
766
    }
767
    return generatorRunAsync(this.privateEnforce(true, true, matcher, this.defaultEnforceContext, ...rvals));
×
768
  }
769

770
  /**
771
   * batchEnforce enforces each request and returns result in a bool array.
772
   * @param rvals the request need to be mediated, usually an array
773
   *              of array of strings, can be class instances if ABAC is used.
774
   * @returns whether to allow the requests.
775
   */
776
  public async batchEnforce(rvals: any[]): Promise<boolean[]> {
777
    return await Promise.all(rvals.map((rval) => this.enforce(...rval)));
4✔
778
  }
779
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc