apache / apache/casbin-Casbin.NET
Proposal: Generic type's policy and request
- Dominant language
- C#
- Stars
- 1.3k
- Forks
- 128
- PR merge metrics
- No merged PRs in 30d
Description
### **Context and purposes**
Casbin uses a string array to store all policies, so we have ensured the request is always a string. In some situations, we need additional operations to "parse" request values: https://github.com/casbin/casbin/issues/868.
However, this does bring some benefits, all as strings can greatly reduce the complexity of development. like we can provide a clear signature for the custom function:
```csharp
// golang
func (key1 string, key2 string) bool
// csharp
Func
```
So this proposal does not completely make the parameters strongly typed, currently only for the following purposes :
- Validate matcher when loading model.
- Lower memory allocation and boxed conversion.
- Provider better ABAC support, like higher performance.
- Prepare for future semantic expansion, like auto-type parse.
### **Roadmap**
- [x] https://github.com/casbin/Casbin.NET/issues/94 **(Will be included to v2.0)**
- [x] https://github.com/casbin/Casbin.NET/issues/226
- [x] https://github.com/casbin/Casbin.NET/issues/227
- [ ] https://github.com/casbin/Casbin.NET/issues/228
### Changes:
#### 1. Generic request and policy values
Request and policy value is like a read-only tuple. We can use the `record Request` type to define them. In order to deal with the different numbers and usage scenarios of policies and requests (the request will be much more than policy), so the request should be lower allocated than the policy instance and try best to avoid boxed-transform.
**Policy Sample:**
```csharp
public record Policy(T1 Value1, T2 Value2, T3 Value3) : IPolicyValues
{
public string this[int index] => index switch
{
0 => Policy.ToString(Value1),
1 => Policy.ToString(Value2),
2 => Policy.ToString(Value3),
_ => throw new ArgumentOutOfRangeException(nameof(index))
};
public int Count => 3;
public IEnumerator GetEnumerator() => new PolicyEnumerator>(this);
IEnumerator IEnumerable.GetEnumerator() => new PolicyEnumerator>(this);
};
```
**Request sample:**
```csharp
public readonly record struct Request(T1 Value1, T2 Value2, T3 Value3) : IRequestValues
{
public int Count => 3;
public static implicit operator Request((T1, T2, T3) tuple)
{
return Request.Create(tuple.Item1, tuple.Item2, tuple.Item3);
}
public static implicit operator (T1, T2, T3)(Request r)
{
return (r.Value1, r.Value2, r.Value3);
}
}
```
#### 2. Expression and matcher
These will help implement a “real matcher”, we do not need the escaping:
https://github.com/casbin/Casbin.NET/blob/bb8b51b43ed2c49f6e8ca70929f609a71cef2f74/NetCasbin/Util/StringUtil.cs#L18-L27
**Sample test:**
```csharp
[Fact]
public void TestGenericEnforce()
{
var r = Request.Create("A", 1);
var p = Policy.Create("A", 1);
var func1 = Compile("r.Value1 == p.Value1 && r.Value2 == p.Value2",
nameof(r), in r, nameof(p), in p);
Assert.True(func1(Request.Create("A", 1), Policy.Create("A", 1)));
Assert.False(func1(Request.Create("A", 1), Policy.Create("A", 2)));
Assert.False(func1(Request.Create("B", 1), Policy.Create("B", 2)));
var r2 = Request.Create("A", 1, "read");
var p2 = Policy.Create("A", 1, "read");
var func2 = Compile("r2.Value1 == p2.Value1 && r2.Value2 == p2.Value2 && r2.Value3 == p2.Value3",
nameof(r2), in r2, nameof(p2), in p2);
Assert.True(func2(Request.Create("A", 1, "read"), Policy.Create("A", 1, "read")));
Assert.False(func2(Request.Create("A", 1, "read"), Policy.Create("A", 2, "read")));
Assert.False(func2(Request.Create("B", 1, "read"), Policy.Create("B", 2, "read")));
}
```
Contributor guide
No contributing guide indexed for this repository
Assessment
This issue has not been assessed yet.