diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index af9a906..6d47f48 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -27,6 +27,8 @@ jobs:
steps:
- name: 📥 Checkout code
uses: actions/checkout@v4
+ with:
+ submodules: true
- name: 🦀 Install Rust target
run: rustup target add ${{ matrix.target }}
@@ -79,7 +81,7 @@ jobs:
- name: ✅ Run .NET tests
run: dotnet test
- - name: 📦 Upload native binary
+ - name: 📤 Upload native binary
uses: actions/upload-artifact@v4
with:
name: rustlib-${{ matrix.rid }}
diff --git a/.gitignore b/.gitignore
index 9491a2f..eaa9902 100644
--- a/.gitignore
+++ b/.gitignore
@@ -360,4 +360,4 @@ MigrationBackup/
.ionide/
# Fody - auto-generated XML schema
-FodyWeavers.xsd
\ No newline at end of file
+FodyWeavers.xsd
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..6c18a08
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "cedar-integration-tests"]
+ path = cedar-integration-tests
+ url = https://github.com/cedar-policy/cedar-integration-tests
diff --git a/cedar-integration-tests b/cedar-integration-tests
new file mode 160000
index 0000000..858d8bd
--- /dev/null
+++ b/cedar-integration-tests
@@ -0,0 +1 @@
+Subproject commit 858d8bdc9ad4abd41020544f798a327bcf741b7e
diff --git a/examples/BasicExample/Program.cs b/examples/BasicExample/Program.cs
index 6be9bb3..46d2779 100644
--- a/examples/BasicExample/Program.cs
+++ b/examples/BasicExample/Program.cs
@@ -169,7 +169,7 @@ static void TestIsAuthorized(AuthorizationCall call)
static void TestIsAuthorizedPartial(PartialAuthorizationCall call)
{
- var answer = CedarFunctions.IsAuthorizedPartial(call);
+ var answer = CedarExperimentalFunctions.IsAuthorizedPartial(call);
WriteTitle("IsAuthorizedPartial");
WriteAnswer(answer);
diff --git a/src/CedarDotNet/CedarUtilities.cs b/src/CedarDotNet/CedarUtilities.cs
index 09ef25d..c95e99a 100644
--- a/src/CedarDotNet/CedarUtilities.cs
+++ b/src/CedarDotNet/CedarUtilities.cs
@@ -1,4 +1,7 @@
using CedarDotNet.Interop;
+using CedarDotNet.Models;
+using System.Runtime.InteropServices;
+using System.Text.Json;
namespace CedarDotNet;
@@ -28,4 +31,28 @@ public static string PolicyFormatJsonToText(
=> FfiUtilities.CallUnaryStr(
policyJson,
CedarFfi.PolicyFormatJsonToText);
+
+ ///
+ /// Loads a policy set from the given text.
+ ///
+ /// The policies text.
+ /// The policy collection.
+ public static IReadOnlyCollection LoadPolicySet(
+ string text)
+ {
+ var result = CedarFfi.LoadPolicySet(text);
+
+ try
+ {
+ var resultJson = Marshal.PtrToStringUTF8(result)!;
+
+ return JsonSerializer.Deserialize(
+ json: resultJson,
+ jsonTypeInfo: CedarJsonSerializerContext.Default.IReadOnlyCollectionString)!;
+ }
+ finally
+ {
+ CedarFfi.FreeString(result);
+ }
+ }
}
diff --git a/src/CedarDotNet/Interop/CedarFfi.Utilities.cs b/src/CedarDotNet/Interop/CedarFfi.Utilities.cs
index 38c2bb9..9d54302 100644
--- a/src/CedarDotNet/Interop/CedarFfi.Utilities.cs
+++ b/src/CedarDotNet/Interop/CedarFfi.Utilities.cs
@@ -9,4 +9,7 @@ internal static partial class CedarFfi
[LibraryImport(CedarNativeLibrary.Name, EntryPoint = "policy_format_json_to_text", StringMarshalling = StringMarshalling.Utf8)]
public static partial IntPtr PolicyFormatJsonToText(string call);
+
+ [LibraryImport(CedarNativeLibrary.Name, EntryPoint = "load_policy_set", StringMarshalling = StringMarshalling.Utf8)]
+ public static partial IntPtr LoadPolicySet(string text);
}
\ No newline at end of file
diff --git a/src/CedarDotNet/Models/Entity.cs b/src/CedarDotNet/Models/Entity.cs
index 14e336f..68265a9 100644
--- a/src/CedarDotNet/Models/Entity.cs
+++ b/src/CedarDotNet/Models/Entity.cs
@@ -31,6 +31,7 @@ public sealed record class Entity
/// The tags.
///
[JsonPropertyName("tags")]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public IReadOnlyDictionary Tags { get; init; } = FrozenDictionary.Empty;
}
diff --git a/src/CedarDotNet/Values/ValueJsonConverter.cs b/src/CedarDotNet/Values/ValueJsonConverter.cs
index c7d3c0b..2487093 100644
--- a/src/CedarDotNet/Values/ValueJsonConverter.cs
+++ b/src/CedarDotNet/Values/ValueJsonConverter.cs
@@ -71,7 +71,7 @@ private static Value ParseExtension(JsonElement extn)
"decimal" => DecimalValue.Decode(arg),
"datetime" => DateTimeValue.Decode(arg),
"duration" => DurationValue.Decode(arg),
- "ipaddr" => IpAddrValue.Decode(arg),
+ "ip" => IpAddrValue.Decode(arg),
_ => throw new NotSupportedException($"Unsupported extension type: {fn}")
};
}
@@ -127,7 +127,7 @@ public override void Write(Utf8JsonWriter writer, Value value, JsonSerializerOpt
WriteExtensionType(writer, "duration", durationValue.Encode());
break;
case IpAddrValue ipAddrValue:
- WriteExtensionType(writer, "ipaddr", ipAddrValue.Encode());
+ WriteExtensionType(writer, "ip", ipAddrValue.Encode());
break;
default:
throw new NotSupportedException($"Unsupported value type: {value.GetType()}");
diff --git a/src/CedarDotNetFfi/src/lib.rs b/src/CedarDotNetFfi/src/lib.rs
index f8acbf1..56e0a08 100644
--- a/src/CedarDotNetFfi/src/lib.rs
+++ b/src/CedarDotNetFfi/src/lib.rs
@@ -1,6 +1,11 @@
use std::ffi::{c_char, CString, CStr};
-use cedar_policy::Policy;
+use std::str::FromStr;
+
+use cedar_policy::{
+ Policy,
+ PolicySet
+};
use cedar_policy::ffi::{
check_parse_policy_set_json_str,
@@ -133,3 +138,19 @@ pub fn policy_format_json_to_text(json: *const c_char) -> *const c_char {
CString::new(policy.to_string()).unwrap().into_raw()
}
+
+#[unsafe(no_mangle)]
+pub fn load_policy_set(text: *const c_char) -> *const c_char {
+ let text_str = unsafe { CStr::from_ptr(text).to_str().unwrap() };
+
+ let policy_set = PolicySet::from_str(text_str).expect("Could not load policy set");
+
+ let arr = policy_set
+ .policies()
+ .map(|p| p.to_string())
+ .collect::>();
+
+ let result_json_str = serde_json::to_string(&arr).unwrap();
+
+ CString::new(result_json_str.to_string()).unwrap().into_raw()
+}
\ No newline at end of file
diff --git a/tests/CedarDotNet.UnitTests/CedarDotNet.UnitTests.csproj b/tests/CedarDotNet.UnitTests/CedarDotNet.UnitTests.csproj
index 68ff8e2..423ade6 100644
--- a/tests/CedarDotNet.UnitTests/CedarDotNet.UnitTests.csproj
+++ b/tests/CedarDotNet.UnitTests/CedarDotNet.UnitTests.csproj
@@ -1,4 +1,4 @@
-
+
net8.0
diff --git a/tests/CedarDotNet.UnitTests/IntegrationTests/Dtos/TestJsonSerializedContext.cs b/tests/CedarDotNet.UnitTests/IntegrationTests/Dtos/TestJsonSerializedContext.cs
new file mode 100644
index 0000000..8a8e851
--- /dev/null
+++ b/tests/CedarDotNet.UnitTests/IntegrationTests/Dtos/TestJsonSerializedContext.cs
@@ -0,0 +1,11 @@
+using CedarDotNet.Models;
+using System.Text.Json.Serialization;
+
+namespace CedarDotNet.UnitTests.IntegrationTests.Dtos;
+
+[JsonSerializable(typeof(TestScenarioDto))]
+[JsonSerializable(typeof(IReadOnlyCollection))]
+[JsonSourceGenerationOptions(
+ PropertyNamingPolicy = JsonKnownNamingPolicy.SnakeCaseLower,UseStringEnumConverter = true)]
+internal sealed partial class TestJsonSerializedContext
+ : JsonSerializerContext;
\ No newline at end of file
diff --git a/tests/CedarDotNet.UnitTests/IntegrationTests/Dtos/TestRequestDto.cs b/tests/CedarDotNet.UnitTests/IntegrationTests/Dtos/TestRequestDto.cs
new file mode 100644
index 0000000..3cc43f4
--- /dev/null
+++ b/tests/CedarDotNet.UnitTests/IntegrationTests/Dtos/TestRequestDto.cs
@@ -0,0 +1,32 @@
+using CedarDotNet.Models;
+using CedarDotNet.Values;
+using System.Text.Json.Serialization;
+
+namespace CedarDotNet.UnitTests.IntegrationTests.Dtos;
+
+public sealed class TestRequestDto
+{
+ [JsonPropertyName("description")]
+ public required string Description { get; init; }
+
+ [JsonPropertyName("principal")]
+ public required EntityUid Principal { get; init; }
+
+ [JsonPropertyName("action")]
+ public required EntityUid Action { get; init; }
+
+ [JsonPropertyName("resource")]
+ public required EntityUid Resource { get; init; }
+
+ [JsonPropertyName("context")]
+ public required IReadOnlyDictionary Context { get; init; }
+
+ [JsonPropertyName("decision")]
+ public required Decision Decision { get; init; }
+
+ [JsonPropertyName("reason")]
+ public required IReadOnlyCollection Reason { get; init; }
+
+ [JsonPropertyName("errors")]
+ public required IReadOnlyCollection Errors { get; init; }
+}
\ No newline at end of file
diff --git a/tests/CedarDotNet.UnitTests/IntegrationTests/Dtos/TestScenarioDto.cs b/tests/CedarDotNet.UnitTests/IntegrationTests/Dtos/TestScenarioDto.cs
new file mode 100644
index 0000000..e528a6c
--- /dev/null
+++ b/tests/CedarDotNet.UnitTests/IntegrationTests/Dtos/TestScenarioDto.cs
@@ -0,0 +1,21 @@
+using System.Text.Json.Serialization;
+
+namespace CedarDotNet.UnitTests.IntegrationTests.Dtos;
+
+public sealed class TestScenarioDto
+{
+ [JsonPropertyName("policies")]
+ public required string Policies { get; init; }
+
+ [JsonPropertyName("entities")]
+ public required string Entities { get; init; }
+
+ [JsonPropertyName("schema")]
+ public required string Schema { get; init; }
+
+ [JsonPropertyName("shouldValidate")]
+ public required bool ShouldValidate { get; init; }
+
+ [JsonPropertyName("requests")]
+ public required IReadOnlyCollection Requests { get; init; }
+}
diff --git a/tests/CedarDotNet.UnitTests/IntegrationTests/IntegrationTestData.cs b/tests/CedarDotNet.UnitTests/IntegrationTests/IntegrationTestData.cs
new file mode 100644
index 0000000..7a4a9ec
--- /dev/null
+++ b/tests/CedarDotNet.UnitTests/IntegrationTests/IntegrationTestData.cs
@@ -0,0 +1,69 @@
+using CedarDotNet.Models;
+using CedarDotNet.UnitTests.IntegrationTests.Dtos;
+using CedarDotNet.UnitTests.IntegrationTests.Models;
+using System.Collections;
+using System.Text.Json;
+
+namespace CedarDotNet.UnitTests.IntegrationTests;
+
+public sealed class IntegrationTestData
+ : IEnumerable