Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Proposal: Generic type's policy and request #229

Open
4 tasks
sagilio opened this issue Nov 21, 2021 · 1 comment
Open
4 tasks

Proposal: Generic type's policy and request #229

sagilio opened this issue Nov 21, 2021 · 1 comment
Assignees
Labels
enhancement Enhancement the exist feature
Milestone

Comments

@sagilio
Copy link
Member

sagilio commented Nov 21, 2021

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: casbin/casbin#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:

// golang
func (key1 string, key2 string) bool
// csharp
Func<string, string, bool>

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

Changes:

1. Generic request and policy values

Request and policy value is like a read-only tuple. We can use the record Request<T1, T2 ...> 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:

public record Policy<T1, T2, T3>(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<string> GetEnumerator() => new PolicyEnumerator<Policy<T1, T2, T3>>(this);
    IEnumerator IEnumerable.GetEnumerator() => new PolicyEnumerator<Policy<T1, T2, T3>>(this);
};

Request sample:

public readonly record struct Request<T1, T2, T3>(T1 Value1, T2 Value2, T3 Value3) : IRequestValues
{
    public int Count => 3;

    public static implicit operator Request<T1, T2, T3>((T1, T2, T3) tuple)
    {
        return Request.Create(tuple.Item1, tuple.Item2, tuple.Item3);
    }

    public static implicit operator (T1, T2, T3)(Request<T1, T2, T3> 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:

internal static string EscapeAssertion(string str)
{
if (str.StartsWith(PermConstants.DefaultRequestType, StringComparison.Ordinal)
|| str.StartsWith(PermConstants.DefaultPolicyType, StringComparison.Ordinal))
{
str = str.ReplaceFirst('.', '_');
}
str = s_escapeRegex.Replace(str, m => m.Value.ReplaceFirst('.', '_')) ;
return str;
}

Sample test:

[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")));
}
@sagilio sagilio added the enhancement Enhancement the exist feature label Nov 21, 2021
@sagilio sagilio added this to the Future milestone Nov 21, 2021
@sagilio sagilio self-assigned this Nov 21, 2021
@casbin-bot
Copy link
Member

@sagilio @xcaptain @huazhikui

@casbin-bot casbin-bot added the question Further information is requested label Nov 21, 2021
@sagilio sagilio removed the question Further information is requested label May 12, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement Enhancement the exist feature
Projects
Development

No branches or pull requests

3 participants