diff --git a/.github/workflows/test-csharp.yml b/.github/workflows/test-csharp.yml index ce566e6..ff258e8 100644 --- a/.github/workflows/test-csharp.yml +++ b/.github/workflows/test-csharp.yml @@ -6,9 +6,101 @@ on: pull_request: branches: [ "main" ] -jobs: - test: +jobs: + build-ffi: + name: 'Build Regorus FFI: (${{ matrix.runtime.target }})' + runs-on: ${{ matrix.runtime.os }} + strategy: + # let us get failures from other jobs even if one fails + fail-fast: false + matrix: + runtime: + - os: windows-latest + target: x86_64-pc-windows-msvc + libpath: | + **/release/regorus_ffi.dll + **/release/regorus_ffi.pdb + - os: ubuntu-latest + target: x86_64-unknown-linux-gnu + libpath: | + **/release/libregorus_ffi.so + # Disabled for now + #- os: macos-latest + # target: aarch64-apple-darwin + # libpath: | + # **/release/libregorus_ffi.dylib + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Build Regorus binding + run: cargo build -r --target ${{ matrix.runtime.target }} + working-directory: ./bindings/ffi + + - name: Upload regorus ffi shared library + uses: actions/upload-artifact@v4 + with: + name: regorus-ffi-artifacts-${{ matrix.runtime.target }} + # Note: The full path of each artifact relative to . is preserved. + path: ${{ matrix.runtime.libpath }} + if-no-files-found: error + retention-days: 1 + + build-nuget: + name: 'Build Regorus nuget' runs-on: ubuntu-latest + needs: build-ffi + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - uses: actions/setup-dotnet@v4 + + - name: Download regorus ffi shared libraries + uses: actions/download-artifact@v4 + with: + pattern: regorus-ffi-artifacts-* + merge-multiple: true + path: ./bindings/csharp/Regorus/tmp + + - name: Display regorus ffi artifacts + run: ls -R ./bindings/csharp/Regorus/tmp + + # Note that we need to supply the target folder within the folder where artifacts are downloaded. + - name: Build Regorus binding + run: dotnet build /p:Configuration=Release /p:RegorusFFIArtifactsDir=./tmp/bindings/ffi/target + working-directory: ./bindings/csharp/Regorus + + - name: Pack + run: dotnet pack /p:RegorusFFIArtifactsDir=./tmp/bindings/ffi/target + working-directory: ./bindings/csharp/Regorus + + - name: Upload Regorus nuget + uses: actions/upload-artifact@v4 + with: + name: regorus-nuget + path: bindings/csharp/Regorus/bin/Release/Regorus*.nupkg + if-no-files-found: error + retention-days: 1 + + test-nuget: + name: 'Test Regorus Nuget: (${{ matrix.runtime.target }})' + needs: build-nuget + runs-on: ${{ matrix.runtime.os }} + strategy: + # let us get failures from other jobs even if one fails + fail-fast: false + matrix: + runtime: + - os: windows-latest + target: x86_64-pc-windows-msvc + - os: ubuntu-latest + target: x86_64-unknown-linux-gnu + #- os: macos-latest + # target: aarch64-apple-darwin steps: - name: Checkout repository uses: actions/checkout@v4 @@ -17,10 +109,21 @@ jobs: - uses: actions/setup-dotnet@v4 - - name: Build - run: dotnet build - working-directory: ./bindings/csharp/net8.0 + - name: Download regorus nuget + uses: actions/download-artifact@v4 + with: + name: regorus-nuget + path: ./bindings/csharp/TestApp/regorus-nuget/ + + - name: Restore Test App + run: dotnet restore /p:RestoreSources=./regorus-nuget/ + working-directory: ./bindings/csharp/TestApp - - name: Run - run: LD_LIBRARY_PATH=. dotnet run - working-directory: ./bindings/csharp/net8.0 + - name: Build Test App 8.0 + run: dotnet build --framework net8.0 + working-directory: ./bindings/csharp/TestApp + + - name: Run Test App 8.0 + run: dotnet run --framework net8.0 + working-directory: ./bindings/csharp/TestApp + \ No newline at end of file diff --git a/.github/workflows/test-csharp40.yml b/.github/workflows/test-csharp40.yml deleted file mode 100644 index bb06ac9..0000000 --- a/.github/workflows/test-csharp40.yml +++ /dev/null @@ -1,28 +0,0 @@ -name: bindings/csharp40 - -on: - push: - branches: [ "main" ] - pull_request: - branches: [ "main" ] - -jobs: - test: - runs-on: windows-latest - steps: - - name: Checkout repository - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - uses: actions/setup-dotnet@v1 - with: - dotnet-version: "5.0.x" - - - name: Build - run: dotnet build - working-directory: ./bindings/csharp/net40 - - - name: Run - run: dotnet run - working-directory: ./bindings/csharp/net40 diff --git a/.gitignore b/.gitignore index 51f0ed6..4b69e48 100644 --- a/.gitignore +++ b/.gitignore @@ -27,4 +27,9 @@ worktrees/ bindings/ffi/regorus.h bindings/ffi/regorus.ffi.hpp -bindings/*/target \ No newline at end of file +bindings/*/target + +# C# build folders +**bin +**obj +*.sln \ No newline at end of file diff --git a/bindings/csharp/Regorus/Regorus.cs b/bindings/csharp/Regorus/Regorus.cs new file mode 100644 index 0000000..69c5e9a --- /dev/null +++ b/bindings/csharp/Regorus/Regorus.cs @@ -0,0 +1,240 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Runtime.InteropServices; +using System.Text; + + +#nullable enable +namespace Regorus +{ + public unsafe sealed class Engine : System.IDisposable + { + private Regorus.Internal.RegorusEngine* E; + // Detect redundant Dispose() calls in a thread-safe manner. + // _isDisposed == 0 means Dispose(bool) has not been called yet. + // _isDisposed == 1 means Dispose(bool) has been already called. + private int isDisposed; + + public Engine() + { + E = Regorus.Internal.API.regorus_engine_new(); + } + + public void Dispose() + { + Dispose(disposing: true); + + // This object will be cleaned up by the Dispose method. + // Therefore, call GC.SuppressFinalize to + // take this object off the finalization queue + // and prevent finalization code for this object + // from executing a second time. + GC.SuppressFinalize(this); + } + + // Dispose(bool disposing) executes in two distinct scenarios. + // If disposing equals true, the method has been called directly + // or indirectly by a user's code. Managed and unmanaged resources + // can be disposed. + // If disposing equals false, the method has been called by the + // runtime from inside the finalizer and you should not reference + // other objects. Only unmanaged resources can be disposed. + void Dispose(bool disposing) + { + // In case _isDisposed is 0, atomically set it to 1. + // Enter the branch only if the original value is 0. + if (System.Threading.Interlocked.CompareExchange(ref isDisposed, 1, 0) == 0) + { + // If disposing equals true, dispose all managed + // and unmanaged resources. + if (disposing) + { + // No managed resource to dispose. + } + + // Call the appropriate methods to clean up + // unmanaged resources here. + // If disposing is false, + // only the following code is executed. + if (E != null) + { + Regorus.Internal.API.regorus_engine_drop(E); + E = null; + } + + } + } + + // Use C# finalizer syntax for finalization code. + // This finalizer will run only if the Dispose method + // does not get called. + ~Engine() => Dispose(disposing: false); + + // Helper for implementing Clone + private Engine(Internal.RegorusEngine* engine) + { + this.E = engine; + } + + public Engine Clone() => new(Internal.API.regorus_engine_clone(E)); + + byte[] NullTerminatedUTF8Bytes(string s) + { + return Encoding.UTF8.GetBytes(s + char.MinValue); + } + + public string? AddPolicy(string path, string rego) + { + var pathBytes = NullTerminatedUTF8Bytes(path); + var regoBytes = NullTerminatedUTF8Bytes(rego); + + + fixed (byte* pathPtr = pathBytes) + { + fixed (byte* regoPtr = regoBytes) + { + return CheckAndDropResult(Regorus.Internal.API.regorus_engine_add_policy(E, pathPtr, regoPtr)); + } + } + + } + + public void SetRegoV0(bool enable) + { + CheckAndDropResult(Regorus.Internal.API.regorus_engine_set_rego_v0(E, enable)); + } + + public string? AddPolicyFromFile(string path) + { + var pathBytes = NullTerminatedUTF8Bytes(path); + fixed (byte* pathPtr = pathBytes) + { + return CheckAndDropResult(Regorus.Internal.API.regorus_engine_add_policy_from_file(E, pathPtr)); + } + + } + + public void AddDataJson(string data) + { + var dataBytes = NullTerminatedUTF8Bytes(data); + fixed (byte* dataPtr = dataBytes) + { + CheckAndDropResult(Regorus.Internal.API.regorus_engine_add_data_json(E, dataPtr)); + } + + } + + public void AddDataFromJsonFile(string path) + { + var pathBytes = NullTerminatedUTF8Bytes(path); + fixed (byte* pathPtr = pathBytes) + { + CheckAndDropResult(Regorus.Internal.API.regorus_engine_add_data_from_json_file(E, pathPtr)); + } + + } + + public void SetInputJson(string input) + { + var inputBytes = NullTerminatedUTF8Bytes(input); + fixed (byte* inputPtr = inputBytes) + { + CheckAndDropResult(Regorus.Internal.API.regorus_engine_set_input_json(E, inputPtr)); + } + } + + public void SetInputFromJsonFile(string path) + { + var pathBytes = NullTerminatedUTF8Bytes(path); + fixed (byte* pathPtr = pathBytes) + { + CheckAndDropResult(Regorus.Internal.API.regorus_engine_set_input_from_json_file(E, pathPtr)); + } + } + + public string? EvalQuery(string query) + { + var queryBytes = NullTerminatedUTF8Bytes(query); + fixed (byte* queryPtr = queryBytes) + { + return CheckAndDropResult(Regorus.Internal.API.regorus_engine_eval_query(E, queryPtr)); + } + } + + public string? EvalRule(string rule) + { + var ruleBytes = NullTerminatedUTF8Bytes(rule); + fixed (byte* rulePtr = ruleBytes) + { + return CheckAndDropResult(Regorus.Internal.API.regorus_engine_eval_query(E, rulePtr)); + } + } + + public void SetEnableCoverage(bool enable) + { + CheckAndDropResult(Regorus.Internal.API.regorus_engine_set_enable_coverage(E, enable)); + } + + public void ClearCoverageData() + { + CheckAndDropResult(Regorus.Internal.API.regorus_engine_clear_coverage_data(E)); + } + + public string? GetCoverageReport() + { + return CheckAndDropResult(Regorus.Internal.API.regorus_engine_get_coverage_report(E)); + } + + public string? GetCoverageReportPretty() + { + return CheckAndDropResult(Regorus.Internal.API.regorus_engine_get_coverage_report_pretty(E)); + } + + public void SetGatherPrints(bool enable) + { + CheckAndDropResult(Regorus.Internal.API.regorus_engine_set_gather_prints(E, enable)); + } + + public string? TakePrints() + { + return CheckAndDropResult(Regorus.Internal.API.regorus_engine_take_prints(E)); + } + + + + string? StringFromUTF8(IntPtr ptr) + { + +#if NETSTANDARD2_1 + return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr); +#else + int len = 0; + while (Marshal.ReadByte(ptr, len) != 0) { ++len; } + byte[] buffer = new byte[len]; + Marshal.Copy(ptr, buffer, 0, buffer.Length); + return Encoding.UTF8.GetString(buffer); +#endif + } + + string? CheckAndDropResult(Regorus.Internal.RegorusResult result) + { + if (result.status != Regorus.Internal.RegorusStatus.RegorusStatusOk) + { + var message = StringFromUTF8((IntPtr)result.error_message); + var ex = new Exception(message); + Regorus.Internal.API.regorus_result_drop(result); + throw ex; + } + + var resultString = ""; + if (result.output is not null) + { + resultString = StringFromUTF8((IntPtr)result.output); + } + Regorus.Internal.API.regorus_result_drop(result); + return resultString; + } + } +} diff --git a/bindings/csharp/Regorus/Regorus.csproj b/bindings/csharp/Regorus/Regorus.csproj new file mode 100644 index 0000000..39686cb --- /dev/null +++ b/bindings/csharp/Regorus/Regorus.csproj @@ -0,0 +1,48 @@ + + + + Library + Microsoft.Regorus + netstandard2.0;netstandard2.1 + true + 0.4.0-preview.1 + 10.0 + README.md + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/bindings/csharp/Regorus/RegorusFFI.cs b/bindings/csharp/Regorus/RegorusFFI.cs new file mode 100644 index 0000000..796c6cb --- /dev/null +++ b/bindings/csharp/Regorus/RegorusFFI.cs @@ -0,0 +1,219 @@ +// +// This code is generated by csbindgen. +// DON'T CHANGE THIS DIRECTLY. +// +#pragma warning disable CS8500 +#pragma warning disable CS8981 +using System; +using System.Runtime.InteropServices; + + +namespace Regorus.Internal +{ + internal static unsafe partial class API + { + const string __DllName = "regorus_ffi"; + + + + /// + /// Drop a `RegorusResult`. + /// + /// `output` and `error_message` strings are not valid after drop. + /// + [DllImport(__DllName, EntryPoint = "regorus_result_drop", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] + internal static extern void regorus_result_drop(RegorusResult r); + + /// + /// Construct a new Engine + /// + /// See https://docs.rs/regorus/latest/regorus/struct.Engine.html + /// + [DllImport(__DllName, EntryPoint = "regorus_engine_new", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] + internal static extern RegorusEngine* regorus_engine_new(); + + /// + /// Clone a [`RegorusEngine`] + /// + /// To avoid having to parse same policy again, the engine can be cloned + /// after policies and data have been added. + /// + /// + [DllImport(__DllName, EntryPoint = "regorus_engine_clone", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] + internal static extern RegorusEngine* regorus_engine_clone(RegorusEngine* engine); + + [DllImport(__DllName, EntryPoint = "regorus_engine_drop", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] + internal static extern void regorus_engine_drop(RegorusEngine* engine); + + /// + /// Add a policy + /// + /// The policy is parsed into AST. + /// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.add_policy + /// + /// * `path`: A filename to be associated with the policy. + /// * `rego`: Rego policy. + /// + [DllImport(__DllName, EntryPoint = "regorus_engine_add_policy", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] + internal static extern RegorusResult regorus_engine_add_policy(RegorusEngine* engine, byte* path, byte* rego); + + [DllImport(__DllName, EntryPoint = "regorus_engine_add_policy_from_file", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] + internal static extern RegorusResult regorus_engine_add_policy_from_file(RegorusEngine* engine, byte* path); + + /// + /// Add policy data. + /// + /// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.add_data + /// * `data`: JSON encoded value to be used as policy data. + /// + [DllImport(__DllName, EntryPoint = "regorus_engine_add_data_json", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] + internal static extern RegorusResult regorus_engine_add_data_json(RegorusEngine* engine, byte* data); + + /// + /// Get list of loaded Rego packages as JSON. + /// + /// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.get_packages + /// + [DllImport(__DllName, EntryPoint = "regorus_engine_get_packages", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] + internal static extern RegorusResult regorus_engine_get_packages(RegorusEngine* engine); + + /// + /// Get list of policies as JSON. + /// + /// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.get_policies + /// + [DllImport(__DllName, EntryPoint = "regorus_engine_get_policies", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] + internal static extern RegorusResult regorus_engine_get_policies(RegorusEngine* engine); + + [DllImport(__DllName, EntryPoint = "regorus_engine_add_data_from_json_file", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] + internal static extern RegorusResult regorus_engine_add_data_from_json_file(RegorusEngine* engine, byte* path); + + /// + /// Clear policy data. + /// + /// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.clear_data + /// + [DllImport(__DllName, EntryPoint = "regorus_engine_clear_data", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] + internal static extern RegorusResult regorus_engine_clear_data(RegorusEngine* engine); + + /// + /// Set input. + /// + /// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.set_input + /// * `input`: JSON encoded value to be used as input to query. + /// + [DllImport(__DllName, EntryPoint = "regorus_engine_set_input_json", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] + internal static extern RegorusResult regorus_engine_set_input_json(RegorusEngine* engine, byte* input); + + [DllImport(__DllName, EntryPoint = "regorus_engine_set_input_from_json_file", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] + internal static extern RegorusResult regorus_engine_set_input_from_json_file(RegorusEngine* engine, byte* path); + + /// + /// Evaluate query. + /// + /// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.eval_query + /// * `query`: Rego expression to be evaluate. + /// + [DllImport(__DllName, EntryPoint = "regorus_engine_eval_query", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] + internal static extern RegorusResult regorus_engine_eval_query(RegorusEngine* engine, byte* query); + + /// + /// Evaluate specified rule. + /// + /// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.eval_rule + /// * `rule`: Path to the rule. + /// + [DllImport(__DllName, EntryPoint = "regorus_engine_eval_rule", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] + internal static extern RegorusResult regorus_engine_eval_rule(RegorusEngine* engine, byte* rule); + + /// + /// Enable/disable coverage. + /// + /// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.set_enable_coverage + /// * `enable`: Whether to enable or disable coverage. + /// + [DllImport(__DllName, EntryPoint = "regorus_engine_set_enable_coverage", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] + internal static extern RegorusResult regorus_engine_set_enable_coverage(RegorusEngine* engine, [MarshalAs(UnmanagedType.U1)] bool enable); + + /// + /// Get coverage report. + /// + /// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.get_coverage_report + /// + [DllImport(__DllName, EntryPoint = "regorus_engine_get_coverage_report", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] + internal static extern RegorusResult regorus_engine_get_coverage_report(RegorusEngine* engine); + + /// + /// Get pretty printed coverage report. + /// + /// See https://docs.rs/regorus/latest/regorus/coverage/struct.Report.html#method.to_string_pretty + /// + [DllImport(__DllName, EntryPoint = "regorus_engine_get_coverage_report_pretty", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] + internal static extern RegorusResult regorus_engine_get_coverage_report_pretty(RegorusEngine* engine); + + /// + /// Clear coverage data. + /// + /// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.clear_coverage_data + /// + [DllImport(__DllName, EntryPoint = "regorus_engine_clear_coverage_data", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] + internal static extern RegorusResult regorus_engine_clear_coverage_data(RegorusEngine* engine); + + /// + /// Whether to gather output of print statements. + /// + /// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.set_gather_prints + /// * `enable`: Whether to enable or disable gathering print statements. + /// + [DllImport(__DllName, EntryPoint = "regorus_engine_set_gather_prints", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] + internal static extern RegorusResult regorus_engine_set_gather_prints(RegorusEngine* engine, [MarshalAs(UnmanagedType.U1)] bool enable); + + /// + /// Take all the gathered print statements. + /// + /// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.take_prints + /// + [DllImport(__DllName, EntryPoint = "regorus_engine_take_prints", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] + internal static extern RegorusResult regorus_engine_take_prints(RegorusEngine* engine); + + /// + /// Get AST of policies. + /// + /// See https://docs.rs/regorus/latest/regorus/coverage/struct.Engine.html#method.get_ast_as_json + /// + [DllImport(__DllName, EntryPoint = "regorus_engine_get_ast_as_json", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] + internal static extern RegorusResult regorus_engine_get_ast_as_json(RegorusEngine* engine); + + /// + /// Enable/disable rego v1. + /// + /// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.set_rego_v0 + /// + [DllImport(__DllName, EntryPoint = "regorus_engine_set_rego_v0", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] + internal static extern RegorusResult regorus_engine_set_rego_v0(RegorusEngine* engine, [MarshalAs(UnmanagedType.U1)] bool enable); + + + } + + [StructLayout(LayoutKind.Sequential)] + internal unsafe partial struct RegorusResult + { + public RegorusStatus status; + public byte* output; + public byte* error_message; + } + + [StructLayout(LayoutKind.Sequential)] + internal unsafe partial struct RegorusEngine + { + } + + + internal enum RegorusStatus : uint + { + RegorusStatusOk, + RegorusStatusError, + } + + +} diff --git a/bindings/csharp/Regorus/docs/README.md b/bindings/csharp/Regorus/docs/README.md new file mode 100644 index 0000000..28c2dd0 --- /dev/null +++ b/bindings/csharp/Regorus/docs/README.md @@ -0,0 +1 @@ +C# Bindings for Regorus \ No newline at end of file diff --git a/bindings/csharp/net8.0/Program.cs b/bindings/csharp/TestApp/Program.cs similarity index 65% rename from bindings/csharp/net8.0/Program.cs rename to bindings/csharp/TestApp/Program.cs index fff52a9..565470b 100644 --- a/bindings/csharp/net8.0/Program.cs +++ b/bindings/csharp/TestApp/Program.cs @@ -1,69 +1,71 @@ -//----------------------------------------------------------------------- -// -// Copyright (c)2012 Microsoft. All rights reserved. -// -// -// Contains code to test the Regorus class for C# -// and .NET 8.0 bindings. -// -//----------------------------------------------------------------------- - -using System.Diagnostics; - -long nanosecPerTick = (1000L*1000L*1000L) / Stopwatch.Frequency; -var w = new Stopwatch(); - - -// Force load of modules. -{ - var _e = new Regorus.Engine(); - var _j = System.Text.Json.JsonDocument.Parse("{}"); -} - -w.Restart(); - -var engine = new Regorus.Engine(); -engine.SetRegoV0(true); - -w.Stop(); -var newEngineTicks = w.ElapsedTicks; - - -w.Restart(); - -// Load policies and data. -engine.AddPolicyFromFile("../../../tests/aci/framework.rego"); -engine.AddPolicyFromFile("../../../tests/aci/api.rego"); -engine.AddPolicyFromFile("../../../tests/aci/policy.rego"); -engine.AddDataFromJsonFile("../../../tests/aci/data.json"); - - -w.Stop(); -var loadPoliciesTicks = w.ElapsedTicks; - - -w.Restart(); - -// Set input and eval rule. -engine.SetInputFromJsonFile("../../../tests/aci/input.json"); -var value = engine.EvalQuery("data.framework.mount_overlay"); -var valueDoc = System.Text.Json.JsonDocument.Parse(value); - -w.Stop(); -var evalTicks = w.ElapsedTicks; - -Console.WriteLine("{0}", valueDoc); - - -Console.WriteLine("Engine creation took {0} msecs", (newEngineTicks*nanosecPerTick)/(1000.0*1000.0)); -Console.WriteLine("Load policies and data took {0} msecs", (loadPoliciesTicks*nanosecPerTick)/(1000.0*1000.0)); -Console.WriteLine("EvalQuery took {0} msecs", (evalTicks*nanosecPerTick)/(1000.0*1000.0)); - -engine = new Regorus.Engine(); -engine.AddPolicy( - "test.rego", - "package test\nx = 1\nmessage = `Hello`"); - -engine.SetEnableCoverage(true); -Console.WriteLine("{0}", engine.EvalRule("data.test.message")); -Console.WriteLine("{0}", engine.GetCoverageReportPretty()); +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.Diagnostics; + + +long nanosecPerTick = (1000L * 1000L * 1000L) / Stopwatch.Frequency; +var w = new Stopwatch(); + + +// Force load of modules. +{ + var _e = new Regorus.Engine(); +#if NET8_0_OR_GREATER + var _j = System.Text.Json.JsonDocument.Parse("{}"); +#endif +} + +w.Restart(); + +var engine = new Regorus.Engine(); +engine.SetRegoV0(true); + +w.Stop(); +var newEngineTicks = w.ElapsedTicks; + + +w.Restart(); + +// Load policies and data. +engine.AddPolicyFromFile("../../../tests/aci/framework.rego"); +engine.AddPolicyFromFile("../../../tests/aci/api.rego"); +engine.AddPolicyFromFile("../../../tests/aci/policy.rego"); +engine.AddDataFromJsonFile("../../../tests/aci/data.json"); + + +w.Stop(); +var loadPoliciesTicks = w.ElapsedTicks; + + +w.Restart(); + +// Set input and eval rule. +engine.SetInputFromJsonFile("../../../tests/aci/input.json"); +var value = engine.EvalQuery("data.framework.mount_overlay"); + +#if NET8_0_OR_GREATER +var valueDoc = System.Text.Json.JsonDocument.Parse(value); + +w.Stop(); +var evalTicks = w.ElapsedTicks; + +Console.WriteLine("{0}", valueDoc); +#else +w.Stop(); +var evalTicks = w.ElapsedTicks; +#endif + + +Console.WriteLine("Engine creation took {0} msecs", (newEngineTicks * nanosecPerTick) / (1000.0 * 1000.0)); +Console.WriteLine("Load policies and data took {0} msecs", (loadPoliciesTicks * nanosecPerTick) / (1000.0 * 1000.0)); +Console.WriteLine("EvalQuery took {0} msecs", (evalTicks * nanosecPerTick) / (1000.0 * 1000.0)); + +engine = new Regorus.Engine(); +engine.AddPolicy( + "test.rego", + "package test\nx = 1\nmessage = `Hello`"); + +engine.SetEnableCoverage(true); +Console.WriteLine("{0}", engine.EvalRule("data.test.message")); +Console.WriteLine("{0}", engine.GetCoverageReportPretty()); diff --git a/bindings/csharp/TestApp/TestApp.csproj b/bindings/csharp/TestApp/TestApp.csproj new file mode 100644 index 0000000..4f8ddc0 --- /dev/null +++ b/bindings/csharp/TestApp/TestApp.csproj @@ -0,0 +1,16 @@ + + + + Exe + net8.0 + TestApp + enable + enable + true + 10.0 + + + + + + diff --git a/bindings/csharp/net40/Program.cs b/bindings/csharp/net40/Program.cs deleted file mode 100644 index 6236564..0000000 --- a/bindings/csharp/net40/Program.cs +++ /dev/null @@ -1,65 +0,0 @@ -//----------------------------------------------------------------------- -// -// Copyright (c)2012 Microsoft. All rights reserved. -// -// -// Contains code to test the Regorus Policy Engine base class for C# -// and .NET4.0 bindings. It can be built and tested in Windows only. -// -//----------------------------------------------------------------------- -using System; -using System.Text; - -using System.Diagnostics; -using Microsoft.WindowsAzure.Regorus.IaaS; - -namespace regoregorus_test -{ - class Program - { - static void Main(string[] args) - { - long nanosecPerTick = (1000L * 1000L * 1000L) / Stopwatch.Frequency; - var w = new Stopwatch(); - w.Restart(); - - var engine = new RegorusPolicyEngine(); - - w.Stop(); - var newEngineTicks = w.ElapsedTicks; - - - w.Restart(); - - // Load policies and data. - engine.AddPolicyFromFile("../../../examples/extension_list/agent_extension_policy.rego"); - engine.AddDataFromJsonFile("../../../examples/extension_list/agent-extension-data-allow-only.json"); - - - w.Stop(); - var loadPoliciesTicks = w.ElapsedTicks; - - - w.Restart(); - - // Set input and eval query. - engine.SetInputFromJsonFile("../../../examples/extension_list/agent-extension-input.json"); - var results = engine.EvalQuery("data.agent_extension_policy.extensions_to_download=x"); - Console.WriteLine("Download query test: \n {0}", results); - - results = engine.EvalQuery("data.agent_extension_policy.extensions_validated"); - - Console.WriteLine("Signing validation test: \n {0}", results); - - engine.Dispose(); - - w.Stop(); - var evalTicks = w.ElapsedTicks; - - Console.WriteLine("Engine creation took {0} msecs", (newEngineTicks * nanosecPerTick) / (1000.0 * 1000.0)); - Console.WriteLine("Load policies and data took {0} msecs", (loadPoliciesTicks * nanosecPerTick) / (1000.0 * 1000.0)); - Console.WriteLine("EvalQuery and print results took {0} msecs", (evalTicks * nanosecPerTick) / (1000.0 * 1000.0)); - } - } -} - diff --git a/bindings/csharp/net40/README b/bindings/csharp/net40/README deleted file mode 100644 index 68bf4d3..0000000 --- a/bindings/csharp/net40/README +++ /dev/null @@ -1,4 +0,0 @@ -The Regorus C# binding library can be built via command "dotnet build". We can use the Regorus C# binding library built from this -directory to create a Nuget. This Nuget will contain the Regorus C# binding library with definitions that -work for .NET framework 4.0 (net40) and above. Note the Nuget can only be created after the binding library has been built. -RegorusCsharp-Lib-x64.nuspec is built for x64 architecture. \ No newline at end of file diff --git a/bindings/csharp/net40/Regorus.cs b/bindings/csharp/net40/Regorus.cs deleted file mode 100644 index c995e63..0000000 --- a/bindings/csharp/net40/Regorus.cs +++ /dev/null @@ -1,220 +0,0 @@ -//----------------------------------------------------------------------- -// -// Copyright (c)2012 Microsoft. All rights reserved. -// -// -// Contains code for the Regorus Policy Engine base class for C# and -// .NET4.0 bindings. Currently this base class is not thread-safe. Make -// sure we use it in a signle-threaded environment or add additional -// protection when using it. -// -//----------------------------------------------------------------------- - - -using System; -using System.Text; -using System.IO; -using System.Threading; - -namespace Microsoft.WindowsAzure.Regorus.IaaS -{ - - public class RegorusPolicyEngine : ICloneable, IDisposable - { - unsafe private RegorusFFI.RegorusEngine* E; - - public RegorusPolicyEngine() - { - unsafe - { - E = RegorusFFI.API.regorus_engine_new(); - } - } - - - public void Dispose() - { - unsafe - { - if (E != null) - { - RegorusFFI.API.regorus_engine_drop(E); - // to avoid Dispose() being called multiple times by mistake. - E = null; - } - - } - - } - - public object Clone() - { - var clone = (RegorusPolicyEngine)this.MemberwiseClone(); - unsafe - { - clone.E = RegorusFFI.API.regorus_engine_clone(E); - } - return clone; - - } - - byte[] NullTerminatedUTF8Bytes(string s) - { - return Encoding.UTF8.GetBytes(s + char.MinValue); - } - public void SetRegoV0(bool enable) - { - unsafe - { - CheckAndDropResult(RegorusFFI.API.regorus_engine_set_rego_v0(E, enable)); - } - } - - public void AddPolicy(string path, string rego) - { - var pathBytes = NullTerminatedUTF8Bytes(path); - var regoBytes = NullTerminatedUTF8Bytes(rego); - - unsafe - { - fixed (byte* pathPtr = pathBytes) - { - fixed (byte* regoPtr = regoBytes) - { - CheckAndDropResult(RegorusFFI.API.regorus_engine_add_policy(E, pathPtr, regoPtr)); - } - } - } - } - - public void AddPolicyFromFile(string path) - { - var pathBytes = NullTerminatedUTF8Bytes(path); - - unsafe - { - fixed (byte* pathPtr = pathBytes) - { - CheckAndDropResult(RegorusFFI.API.regorus_engine_add_policy_from_file(E, pathPtr)); - } - } - } - - public void AddPolicyFromPath(string path) - { - if (!Directory.Exists(path)) - { - return; - } - - string[] regoFiles = Directory.GetFiles(path, "*.rego", SearchOption.AllDirectories); - foreach (string file in regoFiles) - { - AddPolicyFromFile(file); - } - } - - public void AddDataJson(string data) - { - var dataBytes = NullTerminatedUTF8Bytes(data); - - unsafe - { - fixed (byte* dataPtr = dataBytes) - { - CheckAndDropResult(RegorusFFI.API.regorus_engine_add_data_json(E, dataPtr)); - } - } - } - - public void AddDataFromJsonFile(string path) - { - var pathBytes = NullTerminatedUTF8Bytes(path); - - unsafe - { - fixed (byte* pathPtr = pathBytes) - { - CheckAndDropResult(RegorusFFI.API.regorus_engine_add_data_from_json_file(E, pathPtr)); - - } - } - } - - public void SetInputJson(string input) - { - var inputBytes = NullTerminatedUTF8Bytes(input); - - unsafe - { - fixed (byte* inputPtr = inputBytes) - { - CheckAndDropResult(RegorusFFI.API.regorus_engine_set_input_json(E, inputPtr)); - } - } - } - - public void SetInputFromJsonFile(string path) - { - var pathBytes = NullTerminatedUTF8Bytes(path); - - unsafe - { - fixed (byte* pathPtr = pathBytes) - { - CheckAndDropResult(RegorusFFI.API.regorus_engine_set_input_from_json_file(E, pathPtr)); - } - } - } - - public string EvalQuery(string query) - { - var queryBytes = NullTerminatedUTF8Bytes(query); - - var resultJson = ""; - unsafe - { - fixed (byte* queryPtr = queryBytes) - { - var result = RegorusFFI.API.regorus_engine_eval_query(E, queryPtr); - if (result.status == RegorusFFI.RegorusStatus.RegorusStatusOk) - { - if (result.output != null) - { - resultJson = System.Runtime.InteropServices.Marshal.PtrToStringAnsi((IntPtr)result.output); - } - RegorusFFI.API.regorus_result_drop(result); - } - else - { - CheckAndDropResult(result); - } - } - } - if (resultJson != null) - { - return resultJson; - } - else - { - return ""; - } - } - - void CheckAndDropResult(RegorusFFI.RegorusResult result) - { - if (result.status != RegorusFFI.RegorusStatus.RegorusStatusOk) - { - unsafe - { - var message = System.Runtime.InteropServices.Marshal.PtrToStringAnsi((IntPtr)result.error_message); - var ex = new Exception(message); - RegorusFFI.API.regorus_result_drop(result); - throw ex; - } - } - RegorusFFI.API.regorus_result_drop(result); - } - - } -} diff --git a/bindings/csharp/net40/RegorusCsharp-Lib-x64.nuspec b/bindings/csharp/net40/RegorusCsharp-Lib-x64.nuspec deleted file mode 100644 index 8f8956c..0000000 --- a/bindings/csharp/net40/RegorusCsharp-Lib-x64.nuspec +++ /dev/null @@ -1,22 +0,0 @@ - - - - RegorusCsharp-Lib-x64 - 0.2.1 - RegorusCsharp-Lib-x64 - yangjie@microsoft.com - yangjie@microsoft.com - https://www.microsoft.com - false - Regorus C# library for x64 - remove Regorus.cs from Nuget - Copyright (C) Microsoft Corp - - - - - - - - - \ No newline at end of file diff --git a/bindings/csharp/net40/regorus-test.csproj b/bindings/csharp/net40/regorus-test.csproj deleted file mode 100644 index 3e06e3d..0000000 --- a/bindings/csharp/net40/regorus-test.csproj +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - - - - - Exe - net40 - regorus_test - regoregorus_test.Program - enable - true - - - - - diff --git a/bindings/csharp/net8.0/Regorus.cs b/bindings/csharp/net8.0/Regorus.cs deleted file mode 100644 index 51aa0da..0000000 --- a/bindings/csharp/net8.0/Regorus.cs +++ /dev/null @@ -1,236 +0,0 @@ -using System.Text; - -namespace Regorus -{ - public class Exception : System.Exception - { - public Exception(string? message) : base(message) { } - } - - public class Engine : ICloneable - { - unsafe private RegorusFFI.RegorusEngine* E; - public Engine() - { - unsafe - { - E = RegorusFFI.API.regorus_engine_new(); - } - } - - public object Clone() - { - var clone = (Engine)this.MemberwiseClone(); - unsafe - { - clone.E = RegorusFFI.API.regorus_engine_clone(E); - } - return clone; - - } - - byte[] NullTerminatedUTF8Bytes(string s) - { - return Encoding.UTF8.GetBytes(s + char.MinValue); - } - - public string AddPolicy(string path, string rego) - { - var pathBytes = NullTerminatedUTF8Bytes(path); - var regoBytes = NullTerminatedUTF8Bytes(rego); - - unsafe - { - fixed (byte* pathPtr = pathBytes) - { - fixed (byte* regoPtr = regoBytes) - { - return CheckAndDropResult(RegorusFFI.API.regorus_engine_add_policy(E, pathPtr, regoPtr)); - } - } - } - } - - public void SetRegoV0(bool enable) - { - unsafe - { - CheckAndDropResult(RegorusFFI.API.regorus_engine_set_rego_v0(E, enable)); - } - } - - public string AddPolicyFromFile(string path) - { - var pathBytes = NullTerminatedUTF8Bytes(path); - - unsafe - { - fixed (byte* pathPtr = pathBytes) - { - return CheckAndDropResult(RegorusFFI.API.regorus_engine_add_policy_from_file(E, pathPtr)); - } - } - } - - public void AddDataJson(string data) - { - var dataBytes = NullTerminatedUTF8Bytes(data); - - unsafe - { - fixed (byte* dataPtr = dataBytes) - { - CheckAndDropResult(RegorusFFI.API.regorus_engine_add_data_json(E, dataPtr)); - } - } - } - - public void AddDataFromJsonFile(string path) - { - var pathBytes = NullTerminatedUTF8Bytes(path); - - unsafe - { - fixed (byte* pathPtr = pathBytes) - { - CheckAndDropResult(RegorusFFI.API.regorus_engine_add_data_from_json_file(E, pathPtr)); - } - } - } - - public void SetInputJson(string input) - { - var inputBytes = NullTerminatedUTF8Bytes(input); - - unsafe - { - fixed (byte* inputPtr = inputBytes) - { - CheckAndDropResult(RegorusFFI.API.regorus_engine_set_input_json(E, inputPtr)); - } - } - } - - public void SetInputFromJsonFile(string path) - { - var pathBytes = NullTerminatedUTF8Bytes(path); - - unsafe - { - fixed (byte* pathPtr = pathBytes) - { - CheckAndDropResult(RegorusFFI.API.regorus_engine_set_input_from_json_file(E, pathPtr)); - } - } - } - - public string EvalQuery(string query) - { - var queryBytes = NullTerminatedUTF8Bytes(query); - - unsafe - { - fixed (byte* queryPtr = queryBytes) - { - return CheckAndDropResult(RegorusFFI.API.regorus_engine_eval_query(E, queryPtr)); - } - } - } - - public string EvalRule(string rule) - { - var ruleBytes = NullTerminatedUTF8Bytes(rule); - - unsafe - { - fixed (byte* rulePtr = ruleBytes) - { - return CheckAndDropResult(RegorusFFI.API.regorus_engine_eval_query(E, rulePtr)); - } - } - } - - public void SetEnableCoverage(bool enable) - { - unsafe - { - CheckAndDropResult(RegorusFFI.API.regorus_engine_set_enable_coverage(E, enable)); - } - } - - public void ClearCoverageData() - { - unsafe - { - CheckAndDropResult(RegorusFFI.API.regorus_engine_clear_coverage_data(E)); - } - } - - public string GetCoverageReport() - { - unsafe - { - return CheckAndDropResult(RegorusFFI.API.regorus_engine_get_coverage_report(E)); - } - } - - public string GetCoverageReportPretty() - { - unsafe - { - return CheckAndDropResult(RegorusFFI.API.regorus_engine_get_coverage_report_pretty(E)); - } - } - - public void SetGatherPrints(bool enable) - { - unsafe - { - CheckAndDropResult(RegorusFFI.API.regorus_engine_set_gather_prints(E, enable)); - } - } - - public string TakePrints() - { - unsafe - { - return CheckAndDropResult(RegorusFFI.API.regorus_engine_take_prints(E)); - } - } - - ~Engine() - { - unsafe - { - RegorusFFI.API.regorus_engine_drop(E); - } - } - - - string CheckAndDropResult(RegorusFFI.RegorusResult result) - { - if (result.status != RegorusFFI.RegorusStatus.RegorusStatusOk) - { - unsafe - { - var message = System.Runtime.InteropServices.Marshal.PtrToStringUTF8((IntPtr)result.error_message); - var ex = new Exception(message); - RegorusFFI.API.regorus_result_drop(result); - throw ex; - } - } - - var resultString = ""; - unsafe - { - if (result.output is not null) - { - resultString = System.Runtime.InteropServices.Marshal.PtrToStringUTF8((IntPtr)result.output); - } - RegorusFFI.API.regorus_result_drop(result); - } - return resultString; - } - - } -} diff --git a/bindings/csharp/net8.0/regorus-test.csproj b/bindings/csharp/net8.0/regorus-test.csproj deleted file mode 100644 index 05fec4b..0000000 --- a/bindings/csharp/net8.0/regorus-test.csproj +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - - - - - Exe - net8.0 - regorus_test - enable - enable - true - - - - - diff --git a/bindings/ffi/build.rs b/bindings/ffi/build.rs index 5c0ca06..ac20a59 100644 --- a/bindings/ffi/build.rs +++ b/bindings/ffi/build.rs @@ -26,7 +26,7 @@ fn main() { .input_extern_file("src/lib.rs") .csharp_dll_name("regorus_ffi") .csharp_class_name("API") - .csharp_namespace("RegorusFFI") + .csharp_namespace("Regorus.Internal") .generate_csharp_file("./RegorusFFI.g.cs") .unwrap(); }