Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions src/CedarDotNet/CedarFunctions.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using CedarDotNet.Interop;
using CedarDotNet.Models;
using System.Runtime.InteropServices;
using System.Text.Json;

namespace CedarDotNet;

Expand Down Expand Up @@ -86,6 +88,30 @@ public static IAuthorizationAnswer IsAuthorized(
inputTypeInfo: CedarJsonSerializerContext.Default.AuthorizationCall,
outputTypeInfo: CedarJsonSerializerContext.Default.IAuthorizationAnswer);

/// <summary>
/// Parses a policy set into its constituent policies.
/// </summary>
/// <param name="policySetText">The policy set text.</param>
/// <returns>The answer.</returns>
public static IPolicySetTextToPartsAnswer PolicySetTextToParts(
string policySetText)
{
var result = CedarFfi.PolicySetTextToParts(policySetText);

try
{
var resultJson = Marshal.PtrToStringUTF8(result)!;

return JsonSerializer.Deserialize(
json: resultJson,
jsonTypeInfo: CedarJsonSerializerContext.Default.IPolicySetTextToPartsAnswer)!;
}
finally
{
CedarFfi.FreeString(result);
}
}

/// <summary>
/// Gets the language version.
/// </summary>
Expand Down
3 changes: 3 additions & 0 deletions src/CedarDotNet/Interop/CedarFfi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ internal static partial class CedarFfi
[LibraryImport(CedarNativeLibrary.Name, EntryPoint = "validate", StringMarshalling = StringMarshalling.Utf8)]
public static partial IntPtr Validate(string call);

[LibraryImport(CedarNativeLibrary.Name, EntryPoint = "policy_set_text_to_parts", StringMarshalling = StringMarshalling.Utf8)]
public static partial IntPtr PolicySetTextToParts(string call);

[LibraryImport(CedarNativeLibrary.Name, EntryPoint = "get_lang_version", StringMarshalling = StringMarshalling.Utf8)]
public static partial IntPtr GetLangVersion();

Expand Down
1 change: 1 addition & 0 deletions src/CedarDotNet/Models/CedarJsonSerializerContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ namespace CedarDotNet.Models;
[JsonSerializable(typeof(IAuthorizationAnswer))]
[JsonSerializable(typeof(ICheckParseAnswer))]
[JsonSerializable(typeof(IFormattingAnswer))]
[JsonSerializable(typeof(IPolicySetTextToPartsAnswer))]
[JsonSourceGenerationOptions(UseStringEnumConverter = true)]
internal sealed partial class CedarJsonSerializerContext
: JsonSerializerContext;
40 changes: 40 additions & 0 deletions src/CedarDotNet/Models/PolicySetTextToPartsAnswer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
using System.Text.Json.Serialization;

namespace CedarDotNet.Models;

/// <summary>
/// The marker interface for 'policy set text to parts' answer.
/// </summary>
[JsonPolymorphic(TypeDiscriminatorPropertyName = "type")]
[JsonDerivedType(typeof(PolicySetTextToPartsAnswerSuccess), "success")]
[JsonDerivedType(typeof(PolicySetTextToPartsAnswerFailure), "failure")]
public interface IPolicySetTextToPartsAnswer;

/// <summary>
/// A successful 'policy set text to parts' answer.
/// </summary>
public sealed record class PolicySetTextToPartsAnswerSuccess
: IPolicySetTextToPartsAnswer
{
/// <summary>
/// The policies.
/// </summary>
[JsonPropertyName("policies")]
public required IReadOnlyCollection<string> Policies { get; init; }

/// <summary>
/// The policy templates.
/// </summary>
[JsonPropertyName("policy_templates")]
public required IReadOnlyCollection<string> PolicyTemplates { get; init; }
}

/// <summary>
/// A failed 'policy set text to parts' answer.
/// </summary>
public sealed record class PolicySetTextToPartsAnswerFailure
: IPolicySetTextToPartsAnswer
{
[JsonPropertyName("errors")]
public required IReadOnlyCollection<DetailedError> Errors { get; init; }

Check warning on line 39 in src/CedarDotNet/Models/PolicySetTextToPartsAnswer.cs

View workflow job for this annotation

GitHub Actions / 🛠️ Build & Test (ubuntu-latest-x86_64-unknown-linux-gnu)

Missing XML comment for publicly visible type or member 'PolicySetTextToPartsAnswerFailure.Errors'

Check warning on line 39 in src/CedarDotNet/Models/PolicySetTextToPartsAnswer.cs

View workflow job for this annotation

GitHub Actions / 🛠️ Build & Test (windows-latest-x86_64-pc-windows-msvc)

Missing XML comment for publicly visible type or member 'PolicySetTextToPartsAnswerFailure.Errors'
}
93 changes: 51 additions & 42 deletions src/CedarDotNetFfi/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions src/CedarDotNetFfi/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ edition = "2024"
crate-type=["cdylib"]

[dependencies]
serde_json = "1.0.140"
serde_json = "1.0.141"

[dependencies.cedar-policy]
version = "4.4.0"
version = "4.5.0"
git = "https://github.com/cedar-policy/cedar"
branch = "main"
features = ["partial-eval"]
14 changes: 13 additions & 1 deletion src/CedarDotNetFfi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ use cedar_policy::ffi::{
is_authorized_partial_json_str,
validate_json_str,
get_lang_version as internal_get_lang_version,
get_sdk_version as internal_get_sdk_version
get_sdk_version as internal_get_sdk_version,
policy_set_text_to_parts as internal_policy_set_text_to_parts
};

#[unsafe(no_mangle)]
Expand Down Expand Up @@ -153,4 +154,15 @@ pub fn load_policy_set(text: *const c_char) -> *const c_char {
let result_json_str = serde_json::to_string(&arr).unwrap();

CString::new(result_json_str.to_string()).unwrap().into_raw()
}

#[unsafe(no_mangle)]
pub fn policy_set_text_to_parts(text: *const c_char) -> *const c_char {
let text_str = unsafe { CStr::from_ptr(text).to_str().unwrap() };

let parts = internal_policy_set_text_to_parts(text_str);

let result_json_str = serde_json::to_string(&parts).expect("Could not serialize parts to JSON");

CString::new(result_json_str).unwrap().into_raw()
}
4 changes: 2 additions & 2 deletions tests/CedarDotNet.UnitTests/CedarDotNet.UnitTests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.13.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
<PackageReference Include="Shouldly" Version="4.3.0" />
<PackageReference Include="xunit" Version="2.9.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="3.0.2">
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.3">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
using CedarDotNet.Models;

namespace CedarDotNet.UnitTests;

public partial class CedarFunctionTests
{
[Fact]
public void PolicySetTextToParts_ValidPolicySet_ReturnsPoliciesAndTemplates()
{
// Arrange
var policySetText = """
permit(principal, action, resource)
when { principal has "Email" && principal.Email == "a@a.com" };

permit(principal in UserGroup::"DeathRowRecords", action == Action::"pop", resource);

permit(principal in ?principal, action, resource);
""";

// Act
var result = CedarFunctions.PolicySetTextToParts(policySetText);

// Assert
Assert.IsType<PolicySetTextToPartsAnswerSuccess>(result);

var success = (PolicySetTextToPartsAnswerSuccess)result;

Assert.Equal(2, success.Policies.Count);
Assert.Single(success.PolicyTemplates);
}

[Fact]
public void PolicySetTextToParts_InvalidPolicySet_ReturnsFailure()
{
// Arrange
var policySetText = """
invalid
""";

// Act
var result = CedarFunctions.PolicySetTextToParts(policySetText);

// Assert
Assert.IsType<PolicySetTextToPartsAnswerFailure>(result);
}
}
Loading