Skip to content

Commit

Permalink
feat: Add stream adapter. (#299)
Browse files Browse the repository at this point in the history
  • Loading branch information
AsakusaRinne authored and sagilio committed Oct 4, 2022
1 parent 25e0b68 commit 7537539
Show file tree
Hide file tree
Showing 3 changed files with 293 additions and 1 deletion.
18 changes: 17 additions & 1 deletion Casbin.UnitTests/ModelTests/EnforcerTest.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using Casbin.Adapter.File;
using Casbin.Model;
Expand Down Expand Up @@ -1016,7 +1018,14 @@ public void TestSetAdapterFromFile()
FileAdapter a = new("examples/basic_policy.csv");
e.SetAdapter(a);
e.LoadPolicy();
TestEnforce(e, "alice", "data1", "read", true);
e.ClearPolicy();
e.ClearCache();

var policyBytes = Encoding.UTF8.GetBytes(File.ReadAllText(TestModelFixture.GetTestFile("basic_policy.csv")));
StreamAdapter b = new(new MemoryStream(policyBytes, false), new MemoryStream(policyBytes, true));
e.SetAdapter(b);
e.LoadPolicy();
TestEnforce(e, "alice", "data1", "read", true);
}

Expand All @@ -1030,7 +1039,14 @@ public async Task TestSetAdapterFromFileAsync()
FileAdapter a = new("examples/basic_policy_for_async_adapter_test.csv");
e.SetAdapter(a);
await e.LoadPolicyAsync();
await TestEnforceAsync(e, "alice", "data1", "read", true);
e.ClearPolicy();
e.ClearCache();

var policyBytes = Encoding.UTF8.GetBytes(File.ReadAllText(TestModelFixture.GetTestFile("basic_policy.csv")));
StreamAdapter b = new(new MemoryStream(policyBytes, false), new MemoryStream(policyBytes, true));
e.SetAdapter(b);
await e.LoadPolicyAsync();
await TestEnforceAsync(e, "alice", "data1", "read", true);
}

Expand Down
152 changes: 152 additions & 0 deletions Casbin/Adapter/Stream/StreamAdapter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Casbin.Model;
using Casbin.Persist;

namespace Casbin.Adapter.File
{
public class StreamAdapter : IEpochAdapter
{
protected readonly Stream _byteArrayInputStream;
protected readonly Stream _byteArrayOutputStream;
public StreamAdapter(Stream inputStream, Stream outputStream)
{
_byteArrayInputStream = inputStream;
_byteArrayOutputStream = outputStream;
}

public void LoadPolicy(IPolicyStore store)
{
try
{
var streamReader = new StreamReader(_byteArrayInputStream);
if (_byteArrayInputStream is not null)
{
LoadPolicyData(store, streamReader);
}
streamReader.Dispose();
}
catch (Exception)
{

}
}

public async Task LoadPolicyAsync(IPolicyStore store)
{
try
{
var streamReader = new StreamReader(_byteArrayInputStream);
if (_byteArrayInputStream is not null)
{
await LoadPolicyDataAsync(store, streamReader);
}
streamReader.Dispose();
}
catch (Exception)
{

}
}

public void SavePolicy(IPolicyStore store)
{
if (_byteArrayOutputStream is null)
{
throw new Exception("Store file can not write, because use outputStream has not been set.");
}

var policy = ConvertToPolicyStrings(store);
SavePolicyFile(string.Join("\n", policy));
}

public Task SavePolicyAsync(IPolicyStore store)
{
if (_byteArrayOutputStream is null)
{
throw new Exception("Store file can not write, because use outputStream has not been set.");
}

var policy = ConvertToPolicyStrings(store);
return SavePolicyFileAsync(string.Join("\n", policy));
}

private static IEnumerable<string> GetModelPolicy(IPolicyStore store, string section)
{
var policy = new List<string>();
foreach (var kv in store.GetPolicyAllType(section))
{
var key = kv.Key;
var value = kv.Value;
policy.AddRange(value.Select(p => $"{key}, {p.ToText()}"));
}

return policy;
}

private static void LoadPolicyData(IPolicyStore store, StreamReader inputStream)
{
if (inputStream.EndOfStream is true)
{
inputStream.BaseStream.Position = 0;
}
while (inputStream.EndOfStream is false)
{
string line = inputStream.ReadLine();
store.TryLoadPolicyLine(line);
}
}

private static async Task LoadPolicyDataAsync(IPolicyStore store, StreamReader inputStream)
{
if (inputStream.EndOfStream is true)
{
inputStream.BaseStream.Position = 0;
}
while (inputStream.EndOfStream is false)
{
string line = await inputStream.ReadLineAsync();
store.TryLoadPolicyLine(line);
}
}

private static IEnumerable<string> ConvertToPolicyStrings(IPolicyStore store)
{
var policy = new List<string>();
if (store.ContainsNodes(PermConstants.Section.PolicySection))
{
policy.AddRange(GetModelPolicy(store, PermConstants.Section.PolicySection));
}

if (store.ContainsNodes(PermConstants.Section.RoleSection))
{
policy.AddRange(GetModelPolicy(store, PermConstants.Section.RoleSection));
}

return policy;
}

private void SavePolicyFile(string text)
{
var streamWriter = new StreamWriter(_byteArrayOutputStream);
#if (NET6_0 || NET5_0 || NETCOREAPP3_1)
streamWriter.Write(text.AsSpan());
#else
streamWriter.Write(text.ToCharArray());
#endif
streamWriter.Dispose();
}

private async Task SavePolicyFileAsync(string text)
{
var streamWriter = new StreamWriter(_byteArrayOutputStream);
await streamWriter.WriteAsync(text);
streamWriter.Dispose();
}
}
}
124 changes: 124 additions & 0 deletions Casbin/Adapter/Stream/StreamFilteredAdapter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Casbin.Model;
using Casbin.Persist;

namespace Casbin.Adapter.File
{
public class StreamFilteredAdapter : StreamAdapter, IFilteredAdapter
{
public bool IsFiltered { get; private set; }

public StreamFilteredAdapter(Stream inputStream, Stream outputStream) : base(inputStream, outputStream)
{
}

public void LoadFilteredPolicy(IPolicyStore store, Filter filter)
{
if (filter is null)
{
LoadPolicy(store);
return;
}

LoadFilteredPolicyFile(store, filter);
}

public Task LoadFilteredPolicyAsync(IPolicyStore store, Filter filter)
{
if (filter is null)
{
return LoadPolicyAsync(store);
}

return LoadFilteredPolicyFileAsync(store, filter);
}

private void LoadFilteredPolicyFile(IPolicyStore store, Filter filter)
{
var reader = new StreamReader(_byteArrayInputStream);
while (reader.EndOfStream is false)
{
string line = reader.ReadLine()?.Trim();
if (string.IsNullOrWhiteSpace(line) || FilterLine(line, filter))
{
return;
}
store.TryLoadPolicyLine(line);
}
IsFiltered = true;
}

private async Task LoadFilteredPolicyFileAsync(IPolicyStore store, Filter filter)
{
var reader = new StreamReader(_byteArrayInputStream);
while (reader.EndOfStream is false)
{
string line = (await reader.ReadLineAsync())?.Trim();
if (string.IsNullOrWhiteSpace(line) || FilterLine(line, filter))
{
return;
}
store.TryLoadPolicyLine(line);
}
IsFiltered = true;
}

private static bool FilterLine(string line, Filter filter)
{
if (filter == null)
{
return false;
}

string[] p = line.Split(',');
if (p.Length == 0)
{
return true;
}

IEnumerable<string> filterSlice = new List<string>();
switch (p[0].Trim())
{
case PermConstants.DefaultPolicyType:
filterSlice = filter.P;
break;
case PermConstants.DefaultGroupingPolicyType:
filterSlice = filter.G;
break;
}

return FilterWords(p, filterSlice);
}

private static bool FilterWords(string[] line, IEnumerable<string> filter)
{
string[] filterArray = filter.ToArray();
int length = filterArray.Length;

if (line.Length < length + 1)
{
return true;
}

bool skipLine = false;
for (int i = 0; i < length; i++)
{
string current = filterArray.ElementAt(i).Trim();
string next = filterArray.ElementAt(i + 1);

if (string.IsNullOrEmpty(current) || current == next)
{
continue;
}

skipLine = true;
break;
}
return skipLine;
}
}
}

0 comments on commit 7537539

Please # to comment.