hidingmyname opened a new issue, #402:
URL: https://github.com/apache/casbin-Casbin.NET/issues/402

   **Location** (`apache/casbin-Casbin.NET` @ `771066d`):
   
   - 
[`Casbin/Extensions/Model/ModelExtension.cs:13-17`](https://github.com/apache/casbin-Casbin.NET/blob/771066d0e94df76f84e10c7fffbf648c4b926151/Casbin/Extensions/Model/ModelExtension.cs#L13-L17):
 `SortPolicy` always reads the `policy_effect` of the default type `e`.
   - 
[`Casbin/Model/DefaultSections.cs:91-102`](https://github.com/apache/casbin-Casbin.NET/blob/771066d0e94df76f84e10c7fffbf648c4b926151/Casbin/Model/DefaultSections.cs#L91-L102):
 `GetAssertion` throws `ArgumentOutOfRangeException` when the type does not 
exist.
   - 
[`Casbin/Enforcer.cs:60`](https://github.com/apache/casbin-Casbin.NET/blob/771066d0e94df76f84e10c7fffbf648c4b926151/Casbin/Enforcer.cs#L60):
 the constructor calls `model.SortPolicy()` unconditionally; 
[`Casbin/Extensions/Enforcer/EnforcerExtension.cs:190`](https://github.com/apache/casbin-Casbin.NET/blob/771066d0e94df76f84e10c7fffbf648c4b926151/Casbin/Extensions/Enforcer/EnforcerExtension.cs#L190)
 does the same in `LoadPolicy`.
   
   ```csharp
   public static bool SortPolicy(this IModel model)
   {
       string value = 
model.Sections.GetValue(PermConstants.Section.PolicyEffectSection,
           PermConstants.DefaultPolicyEffectType);
       switch (value)
       {
   ```
   
   **Problem**
   
   Casbin.NET supports several section types through `EnforceContext` 
(`CreateContext("r2", "p2", "e2", "m2")`). A model that only uses such types, 
without the defaults `r`/`p`/`e`/`m`, cannot be used, because the enforcer 
cannot even be constructed: `SortPolicy` looks up the `e` policy effect with 
`GetValue`, which throws when `e` is missing. The same call runs from 
`LoadPolicy`/`LoadPolicyAsync`.
   
   Scope:
   
   - Only models without a default `e` policy effect are affected. Models that 
define `e` and additional types already work.
   - Priority sorting is still driven by the default `e` and applied to `p` 
only. This report is about the crash, not about sorting for `e2`.
   
   **Reproduction**
   
   ```csharp
   using Casbin.Model;
   using Xunit;
   
   namespace Casbin.UnitTests.ModelTests;
   
   public class MultipleSectionsTypeTest
   {
       private static IModel BuildModel()
       {
           IModel model = DefaultModel.Create();
           model.AddDef("r", "r2", "sub, obj, act");
           model.AddDef("p", "p2", "sub, obj, act");
           model.AddDef("g", "g", "_, _");
           model.AddDef("e", "e2", "some(where (p.eft == allow))");
           model.AddDef("m", "m2", "g(r2.sub, p2.sub) && r2.obj == p2.obj && 
r2.act == p2.act");
           return model;
       }
   
       [Fact]
       public void TestEnforceWithOnlyMultipleSectionsType()
       {
           IModel model = BuildModel();
           Enforcer e = new(model);
           e.AddNamedPolicy(PermConstants.PolicyType2, "alice", "data1", 
"read");
           e.AddNamedPolicy(PermConstants.PolicyType2, "bob", "data2", "write");
           e.BuildRoleLinks();
   
           EnforceContext context = e.CreateContext(
               PermConstants.RequestType2,
               PermConstants.PolicyType2,
               PermConstants.PolicyEffectType2,
               PermConstants.MatcherType2);
   
           Assert.True(e.Enforce(context, "alice", "data1", "read"));
           Assert.True(e.Enforce(context, "bob", "data2", "write"));
       }
   }
   ```
   
   `dotnet test Casbin.UnitTests/Casbin.UnitTests.csproj -f net9.0`:
   
   ```text
     Failed 
Casbin.UnitTests.ModelTests.MultipleSectionsTypeTest.TestEnforceWithOnlyMultipleSectionsType
 [50 ms]
     Error Message:
      System.ArgumentOutOfRangeException : Specified argument was out of the 
range of valid values. (Parameter 'type')
   Actual value was e.
     Stack Trace:
        at Casbin.Model.DefaultSections.GetAssertion[T](String section, String 
type) in .../Casbin/Model/DefaultSections.cs:line 101
      at Casbin.Model.SectionsExtension.GetValue(ISections sections, String 
section, String type) in .../Casbin/Extensions/Model/SectionsExtension.cs:line 9
      at Casbin.Model.ModelExtension.SortPolicy(IModel model) in 
.../Casbin/Extensions/Model/ModelExtension.cs:line 15
      at Casbin.Enforcer..ctor(IModel model, IReadOnlyAdapter adapter, 
EnforcerOptions options) in .../Casbin/Enforcer.cs:line 60
      at 
Casbin.UnitTests.ModelTests.MultipleSectionsTypeTest.TestEnforceWithOnlyMultipleSectionsType()
 in .../Casbin.UnitTests/ModelTests/MultipleSectionsTypeTest.cs:line 23
   
   Failed!  - Failed:     1, Passed:   399, Skipped:     0, Total:   400, 
Duration: 39 s - Casbin.UnitTests.dll (net9.0)
   ```
   
   Environment:
   - Commit: 
[`771066d0e94df76f84e10c7fffbf648c4b926151`](https://github.com/apache/casbin-Casbin.NET/commit/771066d0e94df76f84e10c7fffbf648c4b926151)
 (`master`, current HEAD)
   - Toolchain: .NET SDK 11.0.100-rc.2.26504.105 (selected by `global.json` 
roll-forward), test target net9.0, Linux x64
   
   **Already fixed in `apache/casbin-node-casbin`**
   
   PR: https://github.com/apache/casbin-node-casbin/pull/442 ("feat: Multiple 
sections type", merged 2023-03-26T16:37:35Z, merge commit `48b0d8fbbf3`). It 
added `EnforceContext` to node-casbin, looks up the `r`/`p`/`e`/`m` assertions 
by the context's types, and tests an enforcer built from a model that only has 
`r2`/`p2`/`e2`/`m2`. The same model works in node-casbin.
   
   **How node-casbin fixed it** (relevant hunks, verbatim):
   
   ```diff
   --- a/src/coreEnforcer.ts
   +++ b/src/coreEnforcer.ts
   @@ -430,23 +438,23 @@ export class CoreEnforcer {
          functions[key] = asyncCompile ? generateGFunction(rm) : 
generateSyncedGFunction(rm);
        });
    
   -    const expString = this.model.model.get('m')?.get('m')?.value;
   +    const expString = 
this.model.model.get('m')?.get(enforceContext.mType)?.value;
        if (!expString) {
          throw new Error('Unable to find matchers in model');
        }
    
   -    const effectExpr = this.model.model.get('e')?.get('e')?.value;
   +    const effectExpr = 
this.model.model.get('e')?.get(enforceContext.eType)?.value;
        if (!effectExpr) {
          throw new Error('Unable to find policy_effect in model');
        }
   ...
   ```
   
   ```diff
   --- a/test/enforcer.test.ts
   +++ b/test/enforcer.test.ts
   @@ -753,3 +753,71 @@ test('TestEnforcerWithScopeFileSystem', async () => {
      await testEnforce(e, 'bob', 'data2', 'write', true);
      await testEnforce(e, 'bob', 'data2', 'read', false);
    });
   +
   +test('TestEnforce Multiple policies config', async () => {
   +  const m = newModel();
   +  m.addDef('r', 'r2', 'sub, obj, act');
   +  m.addDef('p', 'p2', 'sub, obj, act');
   +  m.addDef('g', 'g', '_, _');
   +  m.addDef('e', 'e2', 'some(where (p.eft == allow))');
   +  m.addDef('m', 'm2', 'g(r2.sub, p2.sub) && r2.obj == p2.obj && r2.act == 
p2.act');
   +  const a = new FileAdapter('examples/mulitple_policy.csv');
   +
   +  const e = await newEnforcer(m, a);
   +
   +  //const e = await getEnforcerWithPath(m);
   +  const enforceContext = new EnforceContext('r2', 'p2', 'e2', 'm2');
   +  await expect(e.enforce(enforceContext, 'alice', 'data1', 
'read')).resolves.toStrictEqual(true);
   +  await expect(e.enforce(enforceContext, 'bob', 'data2', 
'write')).resolves.toStrictEqual(true);
   +});
   ...
   ```
   
   **Suggested fix**
   
   Treat a missing default `e` like any effect that needs no sorting, and 
return `false` instead of throwing:
   
   ```diff
   diff --git a/Casbin/Extensions/Model/ModelExtension.cs 
b/Casbin/Extensions/Model/ModelExtension.cs
   --- a/Casbin/Extensions/Model/ModelExtension.cs
   +++ b/Casbin/Extensions/Model/ModelExtension.cs
   @@ -12,9 +12,14 @@ namespace Casbin.Model
        {
            public static bool SortPolicy(this IModel model)
            {
   -            string value = 
model.Sections.GetValue(PermConstants.Section.PolicyEffectSection,
   -                PermConstants.DefaultPolicyEffectType);
   -            switch (value)
   +            // A model may define only non-default section types (e.g. 
"e2") and use them through an EnforceContext.
   +            if 
(model.Sections.TryGetAssertion(PermConstants.Section.PolicyEffectSection,
   +                    PermConstants.DefaultPolicyEffectType, out 
PolicyEffectAssertion assertion) is false)
   +            {
   +                return false;
   +            }
   +
   +            switch (assertion.Value)
                {
                    case PermConstants.PolicyEffect.Priority:
                    case PermConstants.PolicyEffect.PriorityDenyOverride:
   ```
   
   Verified on `771066d` (.NET SDK 11.0.100-rc.2.26504.105, net9.0): the test 
above fails without this patch and passes with it, and both `Enforce` calls 
through the `e2` context return `true`; no other test results change (399 
existing tests in `Casbin.UnitTests`).
   
   I can submit a PR for this.
   
   Checked: #216 (a second matcher next to the default `m`), #329 (named role 
types) and #346 (`ArgumentOutOfRangeException` under concurrent writes) are 
different problems; searches for `SortPolicy`, `EnforceContext` and `e2 
policy_effect` found nothing about models without the default section types.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to