// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using System; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; using System.Text; #nullable enable namespace Regorus { /// /// Represents a policy module with an ID and content. /// public struct PolicyModule { /// /// Gets or sets the unique identifier for this policy module. /// public string Id { get; set; } /// /// Gets or sets the Rego policy content. /// public string Content { get; set; } /// /// Initializes a new instance of the PolicyModule struct. /// /// The unique identifier for this policy module /// The Rego policy content public PolicyModule(string id, string content) { Id = id; Content = content; } } /// /// Provides static methods for compiling policies into efficient compiled representations. /// These are convenience methods that create an engine internally and perform compilation. /// public static unsafe class Compiler { /// /// Compiles a policy from data and modules with a specific entry point rule. /// This is a convenience function that sets up an Engine internally and calls the appropriate compilation method. /// /// JSON string containing static data for policy evaluation /// List of policy modules to compile /// The specific rule path to evaluate (e.g., "data.policy.allow") /// A compiled policy that can be evaluated efficiently /// Thrown when compilation fails public static CompiledPolicy CompilePolicyWithEntrypoint(string dataJson, IEnumerable modules, string entryPointRule) { var dataBytes = Encoding.UTF8.GetBytes(dataJson + char.MinValue); var entryPointBytes = Encoding.UTF8.GetBytes(entryPointRule + char.MinValue); var modulesArray = modules.ToArray(); // Convert C# modules to native structs var nativeModules = new Internal.RegorusPolicyModule[modulesArray.Length]; var pinnedHandles = new List(); try { for (int i = 0; i < modulesArray.Length; i++) { var idBytes = Encoding.UTF8.GetBytes(modulesArray[i].Id + char.MinValue); var contentBytes = Encoding.UTF8.GetBytes(modulesArray[i].Content + char.MinValue); var idHandle = GCHandle.Alloc(idBytes, GCHandleType.Pinned); var contentHandle = GCHandle.Alloc(contentBytes, GCHandleType.Pinned); pinnedHandles.Add(idHandle); pinnedHandles.Add(contentHandle); nativeModules[i] = new Internal.RegorusPolicyModule { id = (byte*)idHandle.AddrOfPinnedObject(), content = (byte*)contentHandle.AddrOfPinnedObject() }; } fixed (byte* dataPtr = dataBytes) fixed (byte* entryPointPtr = entryPointBytes) fixed (Internal.RegorusPolicyModule* modulesPtr = nativeModules) { var result = Internal.API.regorus_compile_policy_with_entrypoint( dataPtr, modulesPtr, (UIntPtr)modulesArray.Length, entryPointPtr); var policy = GetCompiledPolicyResult(result); return policy; } } finally { foreach (var handle in pinnedHandles) { handle.Free(); } } } /// /// Compiles a target-aware policy from data and modules. /// This is a convenience function that sets up an Engine internally and calls target-aware compilation. /// At least one module must contain a `__target__` declaration. /// /// JSON string containing static data for policy evaluation /// List of policy modules to compile /// A compiled policy that can be evaluated efficiently /// Thrown when compilation fails public static CompiledPolicy CompilePolicyForTarget(string dataJson, IEnumerable modules) { var dataBytes = Encoding.UTF8.GetBytes(dataJson + char.MinValue); var modulesArray = modules.ToArray(); // Convert C# modules to native structs var nativeModules = new Internal.RegorusPolicyModule[modulesArray.Length]; var pinnedHandles = new List(); try { for (int i = 0; i < modulesArray.Length; i++) { var idBytes = Encoding.UTF8.GetBytes(modulesArray[i].Id + char.MinValue); var contentBytes = Encoding.UTF8.GetBytes(modulesArray[i].Content + char.MinValue); var idHandle = GCHandle.Alloc(idBytes, GCHandleType.Pinned); var contentHandle = GCHandle.Alloc(contentBytes, GCHandleType.Pinned); pinnedHandles.Add(idHandle); pinnedHandles.Add(contentHandle); nativeModules[i] = new Internal.RegorusPolicyModule { id = (byte*)idHandle.AddrOfPinnedObject(), content = (byte*)contentHandle.AddrOfPinnedObject() }; } fixed (byte* dataPtr = dataBytes) fixed (Internal.RegorusPolicyModule* modulesPtr = nativeModules) { var result = Internal.API.regorus_compile_policy_for_target( dataPtr, modulesPtr, (UIntPtr)modulesArray.Length); var policy = GetCompiledPolicyResult(result); return policy; } } finally { foreach (var handle in pinnedHandles) { handle.Free(); } } } private static string? StringFromUTF8(IntPtr ptr) { #if NETSTANDARD2_1 return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr); #else int len = 0; while (System.Runtime.InteropServices.Marshal.ReadByte(ptr, len) != 0) { ++len; } byte[] buffer = new byte[len]; System.Runtime.InteropServices.Marshal.Copy(ptr, buffer, 0, buffer.Length); return Encoding.UTF8.GetString(buffer); #endif } private static CompiledPolicy GetCompiledPolicyResult(Internal.RegorusResult result) { try { if (result.status != Internal.RegorusStatus.Ok) { var message = StringFromUTF8((IntPtr)result.error_message); throw new Exception(message ?? "Unknown compilation error occurred"); } if (result.data_type != Internal.RegorusDataType.Pointer || result.pointer_value == null) { throw new Exception("Expected compiled policy pointer but got different data type"); } return new CompiledPolicy((Internal.RegorusCompiledPolicy*)result.pointer_value); } finally { Internal.API.regorus_result_drop(result); } } } }