mirror of
https://github.com/microsoft/regorus.git
synced 2026-08-05 02:16:11 +00:00
feat: Complete target system with C# bindings and resource inference (#458)
* feat: Add Schema Registry and Validation Framework This commit introduces a comprehensive schema registry and validation framework, providing schema-based validation of resources and policy effects. - Thread-safe, in-memory registry for schema storage and management - Global registry patterns for effects and resources - Concurrent access with proper error handling - Unicode schema names support - JSON Schema-compliant validation for all primitive types - Advanced constraint validation (patterns, ranges, length limits) - Discriminated union support with anyOf schemas - Detailed error reporting with nested validation paths - Discriminated subobject validation for polymorphic schemas - **Registry Tests**: All registry operations - **Effect Tests**: Policy effect validation - **Resource Tests**: Resource validation - **Validation Tests**: Core validation engine - Thread-safety, error handling, integration scenarios, edge cases - **Dependencies**: dashmap, once_cell, regex - **Thread Safety**: Minimal locking with Rc<Schema> sharing - **Error Types**: TypeMismatch, OutOfRange, PatternMismatch, etc. - Complete schema registry and validation subsystem - Comprehensive test coverage - Foundation for policy validation in Regorus Benchmarks: - Criterion benchmarks for basic types, effects and Azure resources - Performance range: 3.22ns (string) to 34.74µs (Azure VM resource schema validation) - String withs patterns validation: 30.2µs. Need to explore whether regex caching helps bring this down. - Azure policy effects: 188ns-1.4µs Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com> * feat: Complete target system with C# bindings and resource inference - Add comprehensive target system with TargetRegistry and target-aware compilation - Implement resource type inference from policy equality expressions - Create modular C# bindings with separate wrapper classes for each concept - Add thread-safe CompiledPolicy with reference counting for safe disposal - Enhance FFI with detailed error propagation and target functionality - Create TargetExampleApp demonstrating Azure Policy integration - Add CI/CD pipeline testing for all C# applications - Support target definitions with schema validation and resource selectors - Implement PolicyModule struct and target-aware compilation methods - Add comprehensive test coverage for target functionality Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com> --------- Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
This commit is contained in:
committed by
GitHub
parent
3c33d31d08
commit
cc917ea75d
@@ -0,0 +1,31 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#[cfg(feature = "custom_allocator")]
|
||||
extern "C" {
|
||||
fn regorus_aligned_alloc(alignment: usize, size: usize) -> *mut u8;
|
||||
fn regorus_free(ptr: *mut u8);
|
||||
}
|
||||
|
||||
#[cfg(feature = "custom_allocator")]
|
||||
mod allocator {
|
||||
use std::alloc::{GlobalAlloc, Layout};
|
||||
|
||||
struct RegorusAllocator {}
|
||||
|
||||
unsafe impl GlobalAlloc for RegorusAllocator {
|
||||
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
|
||||
let size = layout.size();
|
||||
let align = layout.align();
|
||||
|
||||
crate::allocator::regorus_aligned_alloc(align, size)
|
||||
}
|
||||
|
||||
unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) {
|
||||
crate::allocator::regorus_free(ptr)
|
||||
}
|
||||
}
|
||||
|
||||
#[global_allocator]
|
||||
static ALLOCATOR: RegorusAllocator = RegorusAllocator {};
|
||||
}
|
||||
@@ -0,0 +1,229 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
use anyhow::{anyhow, bail, Result};
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::os::raw::{c_char, c_longlong};
|
||||
|
||||
/// Status of a call on `RegorusEngine`.
|
||||
#[repr(C)]
|
||||
pub enum RegorusStatus {
|
||||
/// The operation was successful.
|
||||
Ok,
|
||||
|
||||
/// The operation was unsuccessful.
|
||||
Error,
|
||||
|
||||
/// Invalid data format provided.
|
||||
InvalidDataFormat,
|
||||
|
||||
/// Invalid entrypoint rule specified.
|
||||
InvalidEntrypoint,
|
||||
|
||||
/// Compilation failed.
|
||||
CompilationFailed,
|
||||
|
||||
/// Invalid argument provided.
|
||||
InvalidArgument,
|
||||
|
||||
/// Invalid module ID.
|
||||
InvalidModuleId,
|
||||
|
||||
/// Invalid policy content.
|
||||
InvalidPolicy,
|
||||
}
|
||||
|
||||
/// Type of data contained in RegorusResult
|
||||
#[repr(C)]
|
||||
#[allow(unused)]
|
||||
pub enum RegorusDataType {
|
||||
/// No data / void
|
||||
None,
|
||||
/// String data (output field is valid)
|
||||
String,
|
||||
/// Boolean data (bool_value field is valid)
|
||||
Boolean,
|
||||
/// Integer data (int_value field is valid)
|
||||
Integer,
|
||||
/// Pointer data (pointer_value field is valid)
|
||||
Pointer,
|
||||
}
|
||||
|
||||
/// Result of a call on `RegorusEngine`.
|
||||
///
|
||||
/// Must be freed using `regorus_result_drop`.
|
||||
#[repr(C)]
|
||||
pub struct RegorusResult {
|
||||
/// Status
|
||||
pub(crate) status: RegorusStatus,
|
||||
|
||||
/// Type of data contained in this result
|
||||
pub(crate) data_type: RegorusDataType,
|
||||
|
||||
/// String output produced by the call.
|
||||
/// Valid when data_type is String. Owned by Rust.
|
||||
pub(crate) output: *mut c_char,
|
||||
|
||||
/// Boolean value.
|
||||
/// Valid when data_type is Boolean.
|
||||
pub(crate) bool_value: bool,
|
||||
|
||||
/// Integer value.
|
||||
/// Valid when data_type is Integer.
|
||||
pub(crate) int_value: c_longlong,
|
||||
|
||||
/// Pointer value.
|
||||
/// Valid when data_type is Pointer.
|
||||
pub(crate) pointer_value: *mut std::os::raw::c_void,
|
||||
|
||||
/// Errors produced by the call.
|
||||
/// Owned by Rust.
|
||||
pub(crate) error_message: *mut c_char,
|
||||
}
|
||||
|
||||
impl RegorusResult {
|
||||
/// Create a successful result with no data.
|
||||
pub(crate) fn ok_void() -> Self {
|
||||
Self {
|
||||
status: RegorusStatus::Ok,
|
||||
data_type: RegorusDataType::None,
|
||||
output: std::ptr::null_mut(),
|
||||
bool_value: false,
|
||||
int_value: 0,
|
||||
pointer_value: std::ptr::null_mut(),
|
||||
error_message: std::ptr::null_mut(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a successful result with string output.
|
||||
pub(crate) fn ok_string(output: String) -> Self {
|
||||
Self {
|
||||
status: RegorusStatus::Ok,
|
||||
data_type: RegorusDataType::String,
|
||||
output: to_c_str(output),
|
||||
bool_value: false,
|
||||
int_value: 0,
|
||||
pointer_value: std::ptr::null_mut(),
|
||||
error_message: std::ptr::null_mut(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a successful result with boolean value.
|
||||
#[allow(unused)]
|
||||
pub(crate) fn ok_bool(value: bool) -> Self {
|
||||
Self {
|
||||
status: RegorusStatus::Ok,
|
||||
data_type: RegorusDataType::Boolean,
|
||||
output: std::ptr::null_mut(),
|
||||
bool_value: value,
|
||||
int_value: 0,
|
||||
pointer_value: std::ptr::null_mut(),
|
||||
error_message: std::ptr::null_mut(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a successful result with integer value.
|
||||
#[allow(unused)]
|
||||
pub(crate) fn ok_int(value: i64) -> Self {
|
||||
Self {
|
||||
status: RegorusStatus::Ok,
|
||||
data_type: RegorusDataType::Integer,
|
||||
output: std::ptr::null_mut(),
|
||||
bool_value: false,
|
||||
int_value: value as c_longlong,
|
||||
pointer_value: std::ptr::null_mut(),
|
||||
error_message: std::ptr::null_mut(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a successful result with pointer value.
|
||||
pub(crate) fn ok_pointer(pointer: *mut std::os::raw::c_void) -> Self {
|
||||
Self {
|
||||
status: RegorusStatus::Ok,
|
||||
data_type: RegorusDataType::Pointer,
|
||||
output: std::ptr::null_mut(),
|
||||
bool_value: false,
|
||||
int_value: 0,
|
||||
pointer_value: pointer,
|
||||
error_message: std::ptr::null_mut(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create an error result with specific status.
|
||||
pub(crate) fn err(status: RegorusStatus) -> Self {
|
||||
Self {
|
||||
status,
|
||||
data_type: RegorusDataType::None,
|
||||
output: std::ptr::null_mut(),
|
||||
bool_value: false,
|
||||
int_value: 0,
|
||||
pointer_value: std::ptr::null_mut(),
|
||||
error_message: std::ptr::null_mut(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create an error result with status and message.
|
||||
pub(crate) fn err_with_message(status: RegorusStatus, message: String) -> Self {
|
||||
Self {
|
||||
status,
|
||||
data_type: RegorusDataType::None,
|
||||
output: std::ptr::null_mut(),
|
||||
bool_value: false,
|
||||
int_value: 0,
|
||||
pointer_value: std::ptr::null_mut(),
|
||||
error_message: to_c_str(message),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn to_c_str(s: String) -> *mut c_char {
|
||||
match CString::new(s) {
|
||||
Ok(cs) => cs.into_raw(),
|
||||
_ => to_c_str("binding error: failed to create c-style string".to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn from_c_str(s: *const c_char) -> Result<String> {
|
||||
if s.is_null() {
|
||||
bail!("null pointer");
|
||||
}
|
||||
unsafe {
|
||||
CStr::from_ptr(s)
|
||||
.to_str()
|
||||
.map_err(|e| anyhow!("invalid utf8: {e}"))
|
||||
.map(|s| s.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn to_ref<'a, T>(t: *mut T) -> Result<&'a mut T> {
|
||||
unsafe { t.as_mut().ok_or_else(|| anyhow!("null pointer")) }
|
||||
}
|
||||
|
||||
pub(crate) fn to_regorus_result(r: Result<()>) -> RegorusResult {
|
||||
match r {
|
||||
Ok(()) => RegorusResult::ok_void(),
|
||||
Err(e) => RegorusResult::err_with_message(RegorusStatus::Error, format!("{e}")),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn to_regorus_string_result(r: Result<String>) -> RegorusResult {
|
||||
match r {
|
||||
Ok(s) => RegorusResult::ok_string(s),
|
||||
Err(e) => RegorusResult::err_with_message(RegorusStatus::Error, format!("{e}")),
|
||||
}
|
||||
}
|
||||
|
||||
/// Drop a `RegorusResult`.
|
||||
///
|
||||
/// `output` and `error_message` strings are not valid after drop.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn regorus_result_drop(r: RegorusResult) {
|
||||
unsafe {
|
||||
if !r.error_message.is_null() {
|
||||
let _ = CString::from_raw(r.error_message);
|
||||
}
|
||||
if !r.output.is_null() {
|
||||
let _ = CString::from_raw(r.output);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,208 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
use crate::common::{from_c_str, RegorusResult, RegorusStatus};
|
||||
use crate::compiled_policy::RegorusCompiledPolicy;
|
||||
use regorus::{compile_policy_with_entrypoint, PolicyModule, Value};
|
||||
|
||||
#[cfg(feature = "azure_policy")]
|
||||
use regorus::compile_policy_for_target;
|
||||
|
||||
use std::os::raw::c_char;
|
||||
|
||||
/// FFI wrapper for PolicyModule struct.
|
||||
#[repr(C)]
|
||||
pub struct RegorusPolicyModule {
|
||||
pub id: *const c_char,
|
||||
pub content: *const c_char,
|
||||
}
|
||||
|
||||
/// Compiles a policy from data and modules with a specific entry point rule.
|
||||
///
|
||||
/// This is a convenience function that wraps [`regorus::compile_policy_with_entrypoint`].
|
||||
/// It sets up an Engine internally and calls the appropriate compilation method.
|
||||
///
|
||||
/// # Parameters
|
||||
/// * `data_json` - JSON string containing static data for policy evaluation
|
||||
/// * `modules` - Array of policy modules to compile
|
||||
/// * `modules_len` - Number of modules in the array
|
||||
/// * `entry_point_rule` - The specific rule path to evaluate (e.g., "data.policy.allow")
|
||||
///
|
||||
/// # Returns
|
||||
/// Returns a RegorusResult containing a RegorusCompiledPolicy handle on success.
|
||||
///
|
||||
/// # Safety
|
||||
/// All string parameters must be valid null-terminated UTF-8 strings.
|
||||
/// The modules array must contain exactly `modules_len` valid elements.
|
||||
/// The caller must eventually call regorus_compiled_policy_drop on the returned handle.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn regorus_compile_policy_with_entrypoint(
|
||||
data_json: *const c_char,
|
||||
modules: *const RegorusPolicyModule,
|
||||
modules_len: usize,
|
||||
entry_point_rule: *const c_char,
|
||||
) -> RegorusResult {
|
||||
let data_str = match from_c_str(data_json) {
|
||||
Ok(s) => s,
|
||||
Err(e) => {
|
||||
return RegorusResult::err_with_message(
|
||||
RegorusStatus::InvalidDataFormat,
|
||||
format!("Invalid data JSON string: {e}"),
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
let entry_rule = match from_c_str(entry_point_rule) {
|
||||
Ok(s) => s,
|
||||
Err(e) => {
|
||||
return RegorusResult::err_with_message(
|
||||
RegorusStatus::InvalidEntrypoint,
|
||||
format!("Invalid entry point rule string: {e}"),
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
// Parse data JSON
|
||||
let data = match Value::from_json_str(&data_str) {
|
||||
Ok(data) => data,
|
||||
Err(e) => {
|
||||
return RegorusResult::err_with_message(
|
||||
RegorusStatus::InvalidDataFormat,
|
||||
format!("Failed to parse data JSON: {e}"),
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
// Convert C modules array to Rust Vec
|
||||
let policy_modules = match convert_c_modules_to_rust(modules, modules_len) {
|
||||
Ok(modules) => modules,
|
||||
Err(status) => return RegorusResult::err(status),
|
||||
};
|
||||
|
||||
// Call the convenience function
|
||||
match compile_policy_with_entrypoint(data, &policy_modules, entry_rule.into()) {
|
||||
Ok(compiled_policy) => {
|
||||
let wrapped_policy = RegorusCompiledPolicy { compiled_policy };
|
||||
let boxed_policy = Box::new(wrapped_policy);
|
||||
RegorusResult::ok_pointer(Box::into_raw(boxed_policy) as *mut std::os::raw::c_void)
|
||||
}
|
||||
Err(e) => RegorusResult::err_with_message(
|
||||
RegorusStatus::CompilationFailed,
|
||||
format!("Policy compilation failed: {e}"),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
/// Compiles a target-aware policy from data and modules.
|
||||
///
|
||||
/// This is a convenience function that wraps [`regorus::compile_policy_for_target`].
|
||||
/// It sets up an Engine internally and calls target-aware compilation.
|
||||
///
|
||||
/// # Parameters
|
||||
/// * `data_json` - JSON string containing static data for policy evaluation
|
||||
/// * `modules` - Array of policy modules to compile
|
||||
/// * `modules_len` - Number of modules in the array
|
||||
///
|
||||
/// # Returns
|
||||
/// Returns a RegorusResult containing a RegorusCompiledPolicy handle on success.
|
||||
///
|
||||
/// # Note
|
||||
/// This function is only available when the `azure_policy` feature is enabled.
|
||||
/// At least one module must contain a `__target__` declaration.
|
||||
///
|
||||
/// # Safety
|
||||
/// All string parameters must be valid null-terminated UTF-8 strings.
|
||||
/// The modules array must contain exactly `modules_len` valid elements.
|
||||
/// The caller must eventually call regorus_compiled_policy_drop on the returned handle.
|
||||
#[cfg(feature = "azure_policy")]
|
||||
#[no_mangle]
|
||||
pub extern "C" fn regorus_compile_policy_for_target(
|
||||
data_json: *const c_char,
|
||||
modules: *const RegorusPolicyModule,
|
||||
modules_len: usize,
|
||||
) -> RegorusResult {
|
||||
let data_str = match from_c_str(data_json) {
|
||||
Ok(s) => s,
|
||||
Err(e) => {
|
||||
return RegorusResult::err_with_message(
|
||||
RegorusStatus::InvalidDataFormat,
|
||||
format!("Invalid data JSON string: {e}"),
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
// Parse data JSON
|
||||
let data = match Value::from_json_str(&data_str) {
|
||||
Ok(data) => data,
|
||||
Err(e) => {
|
||||
return RegorusResult::err_with_message(
|
||||
RegorusStatus::InvalidDataFormat,
|
||||
format!("Failed to parse data JSON: {e}"),
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
// Convert C modules array to Rust Vec
|
||||
let policy_modules = match convert_c_modules_to_rust(modules, modules_len) {
|
||||
Ok(modules) => modules,
|
||||
Err(status) => return RegorusResult::err(status),
|
||||
};
|
||||
|
||||
// Call the convenience function
|
||||
match compile_policy_for_target(data, &policy_modules) {
|
||||
Ok(compiled_policy) => {
|
||||
let wrapped_policy = RegorusCompiledPolicy { compiled_policy };
|
||||
let boxed_policy = Box::new(wrapped_policy);
|
||||
RegorusResult::ok_pointer(Box::into_raw(boxed_policy) as *mut std::os::raw::c_void)
|
||||
}
|
||||
Err(e) => RegorusResult::err_with_message(
|
||||
RegorusStatus::CompilationFailed,
|
||||
format!("Target-aware policy compilation failed: {e}"),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper function to convert C module array to Rust Vec<PolicyModule>.
|
||||
fn convert_c_modules_to_rust(
|
||||
modules: *const RegorusPolicyModule,
|
||||
modules_len: usize,
|
||||
) -> Result<Vec<PolicyModule>, RegorusStatus> {
|
||||
if modules.is_null() && modules_len > 0 {
|
||||
return Err(RegorusStatus::InvalidArgument);
|
||||
}
|
||||
|
||||
let mut policy_modules = Vec::with_capacity(modules_len);
|
||||
|
||||
for i in 0..modules_len {
|
||||
unsafe {
|
||||
let module = modules.add(i);
|
||||
if module.is_null() {
|
||||
return Err(RegorusStatus::InvalidArgument);
|
||||
}
|
||||
|
||||
let module_ref = &*module;
|
||||
|
||||
let id = match from_c_str(module_ref.id) {
|
||||
Ok(s) => s,
|
||||
Err(e) => {
|
||||
eprintln!("Invalid module ID at index {}: {}", i, e);
|
||||
return Err(RegorusStatus::InvalidModuleId);
|
||||
}
|
||||
};
|
||||
|
||||
let content = match from_c_str(module_ref.content) {
|
||||
Ok(s) => s,
|
||||
Err(e) => {
|
||||
eprintln!("Invalid module content at index {}: {}", i, e);
|
||||
return Err(RegorusStatus::InvalidPolicy);
|
||||
}
|
||||
};
|
||||
|
||||
policy_modules.push(PolicyModule {
|
||||
id: id.into(),
|
||||
content: content.into(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Ok(policy_modules)
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
use crate::common::*;
|
||||
use anyhow::Result;
|
||||
use std::os::raw::c_char;
|
||||
|
||||
/// Wrapper for `regorus::CompiledPolicy`.
|
||||
#[derive(Clone)]
|
||||
pub struct RegorusCompiledPolicy {
|
||||
pub(crate) compiled_policy: regorus::CompiledPolicy,
|
||||
}
|
||||
|
||||
/// Drop a `RegorusCompiledPolicy`.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn regorus_compiled_policy_drop(compiled_policy: *mut RegorusCompiledPolicy) {
|
||||
if let Ok(cp) = to_ref(compiled_policy) {
|
||||
unsafe {
|
||||
let _ = Box::from_raw(std::ptr::from_mut(cp));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Evaluate the compiled policy with the given input.
|
||||
///
|
||||
/// For target policies, evaluates the target's effect rule.
|
||||
/// For regular policies, evaluates the originally compiled rule.
|
||||
///
|
||||
/// * `input`: JSON encoded input data (resource) to validate against the policy.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn regorus_compiled_policy_eval_with_input(
|
||||
compiled_policy: *mut RegorusCompiledPolicy,
|
||||
input: *const c_char,
|
||||
) -> RegorusResult {
|
||||
let output = || -> Result<String> {
|
||||
let input_value = regorus::Value::from_json_str(&from_c_str(input)?)?;
|
||||
let result = to_ref(compiled_policy)?
|
||||
.compiled_policy
|
||||
.eval_with_input(input_value)?;
|
||||
result.to_json_str()
|
||||
}();
|
||||
|
||||
match output {
|
||||
Ok(out) => RegorusResult::ok_string(out),
|
||||
Err(e) => to_regorus_result(Err(e)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get information about the compiled policy including metadata about modules,
|
||||
/// target configuration, and resource types.
|
||||
///
|
||||
/// Returns a JSON-encoded `PolicyInfo` struct containing comprehensive
|
||||
/// information about the compiled policy such as module IDs, target name,
|
||||
/// applicable resource types, entry point rule, and parameters.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn regorus_compiled_policy_get_policy_info(
|
||||
compiled_policy: *mut RegorusCompiledPolicy,
|
||||
) -> RegorusResult {
|
||||
let output = || -> Result<String> {
|
||||
let info = to_ref(compiled_policy)?.compiled_policy.get_policy_info()?;
|
||||
serde_json::to_string(&info)
|
||||
.map_err(|e| anyhow::anyhow!("Failed to serialize policy info: {}", e))
|
||||
}();
|
||||
|
||||
match output {
|
||||
Ok(out) => RegorusResult::ok_string(out),
|
||||
Err(e) => to_regorus_result(Err(e)),
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,175 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
//! Effect schema registry functions for FFI.
|
||||
//!
|
||||
//! These functions provide access to regorus's effect schema registry functionality,
|
||||
//! enabling registration and management of Azure Policy effect schemas.
|
||||
|
||||
#![cfg(feature = "azure_policy")]
|
||||
use crate::common::{from_c_str, RegorusResult, RegorusStatus};
|
||||
use regorus::{registry::schemas, Schema};
|
||||
|
||||
use std::os::raw::c_char;
|
||||
|
||||
/// Register an effect schema from JSON with a given name.
|
||||
///
|
||||
/// # Parameters
|
||||
/// * `name` - Name to register the schema under
|
||||
/// * `schema_json` - JSON string representing the schema
|
||||
///
|
||||
/// # Returns
|
||||
/// Returns a RegorusResult with success/error status.
|
||||
///
|
||||
/// # Safety
|
||||
/// All string parameters must be valid null-terminated UTF-8 strings.
|
||||
#[cfg(feature = "azure_policy")]
|
||||
#[no_mangle]
|
||||
pub extern "C" fn regorus_effect_schema_register(
|
||||
name: *const c_char,
|
||||
schema_json: *const c_char,
|
||||
) -> RegorusResult {
|
||||
let schema_name = match from_c_str(name) {
|
||||
Ok(s) => s,
|
||||
Err(e) => {
|
||||
return RegorusResult::err_with_message(
|
||||
RegorusStatus::InvalidArgument,
|
||||
format!("Invalid effect schema name string: {e}"),
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
let schema_str = match from_c_str(schema_json) {
|
||||
Ok(s) => s,
|
||||
Err(e) => {
|
||||
return RegorusResult::err_with_message(
|
||||
RegorusStatus::InvalidDataFormat,
|
||||
format!("Invalid effect schema JSON string: {e}"),
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
// Parse schema from JSON
|
||||
let schema = match Schema::from_json_str(&schema_str) {
|
||||
Ok(schema) => schema,
|
||||
Err(e) => {
|
||||
return RegorusResult::err_with_message(
|
||||
RegorusStatus::InvalidDataFormat,
|
||||
format!("Failed to parse effect schema JSON: {e}"),
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
// Register the schema
|
||||
match schemas::effect::register(schema_name, schema.into()) {
|
||||
Ok(()) => RegorusResult::ok_pointer(std::ptr::null_mut()),
|
||||
Err(e) => RegorusResult::err_with_message(
|
||||
RegorusStatus::Error,
|
||||
format!("Failed to register effect schema: {e}"),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if an effect schema with the given name exists.
|
||||
///
|
||||
/// # Parameters
|
||||
/// * `name` - Name of the schema to check
|
||||
///
|
||||
/// # Returns
|
||||
/// Returns a RegorusResult with "true" or "false" string output.
|
||||
///
|
||||
/// # Safety
|
||||
/// The name parameter must be a valid null-terminated UTF-8 string.
|
||||
#[cfg(feature = "azure_policy")]
|
||||
#[no_mangle]
|
||||
pub extern "C" fn regorus_effect_schema_contains(name: *const c_char) -> RegorusResult {
|
||||
let schema_name = match from_c_str(name) {
|
||||
Ok(s) => s,
|
||||
Err(e) => {
|
||||
return RegorusResult::err_with_message(
|
||||
RegorusStatus::InvalidArgument,
|
||||
format!("Invalid effect schema name string: {e}"),
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
let contains = schemas::effect::contains(&schema_name);
|
||||
RegorusResult::ok_bool(contains)
|
||||
}
|
||||
|
||||
/// Get the number of registered effect schemas.
|
||||
///
|
||||
/// # Returns
|
||||
/// Returns a RegorusResult with the count as a string.
|
||||
#[cfg(feature = "azure_policy")]
|
||||
#[no_mangle]
|
||||
pub extern "C" fn regorus_effect_schema_len() -> RegorusResult {
|
||||
let count = schemas::effect::len();
|
||||
RegorusResult::ok_int(count as i64)
|
||||
}
|
||||
|
||||
/// Check if the effect schema registry is empty.
|
||||
///
|
||||
/// # Returns
|
||||
/// Returns a RegorusResult with "true" or "false" string output.
|
||||
#[cfg(feature = "azure_policy")]
|
||||
#[no_mangle]
|
||||
pub extern "C" fn regorus_effect_schema_is_empty() -> RegorusResult {
|
||||
let is_empty = schemas::effect::is_empty();
|
||||
RegorusResult::ok_bool(is_empty)
|
||||
}
|
||||
|
||||
/// List all registered effect schema names as a JSON array.
|
||||
///
|
||||
/// # Returns
|
||||
/// Returns a RegorusResult with a JSON array of schema names.
|
||||
#[cfg(feature = "azure_policy")]
|
||||
#[no_mangle]
|
||||
pub extern "C" fn regorus_effect_schema_list_names() -> RegorusResult {
|
||||
let names = schemas::effect::list_names();
|
||||
match serde_json::to_string(&names) {
|
||||
Ok(json_str) => RegorusResult::ok_string(json_str),
|
||||
Err(e) => RegorusResult::err_with_message(
|
||||
RegorusStatus::Error,
|
||||
format!("Failed to serialize effect schema names to JSON: {e}"),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
/// Remove an effect schema by name.
|
||||
///
|
||||
/// # Parameters
|
||||
/// * `name` - Name of the schema to remove
|
||||
///
|
||||
/// # Returns
|
||||
/// Returns a RegorusResult with "true" if removed, "false" if not found.
|
||||
///
|
||||
/// # Safety
|
||||
/// The name parameter must be a valid null-terminated UTF-8 string.
|
||||
#[cfg(feature = "azure_policy")]
|
||||
#[no_mangle]
|
||||
pub extern "C" fn regorus_effect_schema_remove(name: *const c_char) -> RegorusResult {
|
||||
let schema_name = match from_c_str(name) {
|
||||
Ok(s) => s,
|
||||
Err(e) => {
|
||||
return RegorusResult::err_with_message(
|
||||
RegorusStatus::InvalidArgument,
|
||||
format!("Invalid effect schema name string: {e}"),
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
let removed = schemas::effect::remove(&schema_name).is_some();
|
||||
RegorusResult::ok_bool(removed)
|
||||
}
|
||||
|
||||
/// Clear all effect schemas from the registry.
|
||||
///
|
||||
/// # Returns
|
||||
/// Returns a RegorusResult with success status.
|
||||
#[cfg(feature = "azure_policy")]
|
||||
#[no_mangle]
|
||||
pub extern "C" fn regorus_effect_schema_clear() -> RegorusResult {
|
||||
schemas::effect::clear();
|
||||
RegorusResult::ok_pointer(std::ptr::null_mut())
|
||||
}
|
||||
@@ -0,0 +1,454 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
use crate::common::{
|
||||
from_c_str, to_ref, to_regorus_result, to_regorus_string_result, RegorusResult, RegorusStatus,
|
||||
};
|
||||
use crate::compiled_policy::RegorusCompiledPolicy;
|
||||
use anyhow::Result;
|
||||
use std::os::raw::c_char;
|
||||
|
||||
/// Wrapper for `regorus::Engine`.
|
||||
#[derive(Clone)]
|
||||
pub struct RegorusEngine {
|
||||
engine: ::regorus::Engine,
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
/// Construct a new Engine
|
||||
///
|
||||
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html
|
||||
pub extern "C" fn regorus_engine_new() -> *mut RegorusEngine {
|
||||
let mut engine = ::regorus::Engine::new();
|
||||
|
||||
// For more OPA compatibility out of the box, we ask builtins to return undefined
|
||||
// instead of raising errors in certain failure scenarios.
|
||||
engine.set_strict_builtin_errors(false);
|
||||
|
||||
Box::into_raw(Box::new(RegorusEngine { engine }))
|
||||
}
|
||||
|
||||
/// Clone a [`RegorusEngine`]
|
||||
///
|
||||
/// To avoid having to parse same policy again, the engine can be cloned
|
||||
/// after policies and data have been added.
|
||||
///
|
||||
#[no_mangle]
|
||||
pub extern "C" fn regorus_engine_clone(engine: *mut RegorusEngine) -> *mut RegorusEngine {
|
||||
match to_ref(engine) {
|
||||
Ok(e) => Box::into_raw(Box::new(e.clone())),
|
||||
_ => std::ptr::null_mut(),
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn regorus_engine_drop(engine: *mut RegorusEngine) {
|
||||
if let Ok(e) = to_ref(engine) {
|
||||
unsafe {
|
||||
let _ = Box::from_raw(std::ptr::from_mut(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 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.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn regorus_engine_add_policy(
|
||||
engine: *mut RegorusEngine,
|
||||
path: *const c_char,
|
||||
rego: *const c_char,
|
||||
) -> RegorusResult {
|
||||
to_regorus_string_result(|| -> Result<String> {
|
||||
to_ref(engine)?
|
||||
.engine
|
||||
.add_policy(from_c_str(path)?, from_c_str(rego)?)
|
||||
}())
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
#[no_mangle]
|
||||
pub extern "C" fn regorus_engine_add_policy_from_file(
|
||||
engine: *mut RegorusEngine,
|
||||
path: *const c_char,
|
||||
) -> RegorusResult {
|
||||
to_regorus_string_result(|| -> Result<String> {
|
||||
to_ref(engine)?
|
||||
.engine
|
||||
.add_policy_from_file(from_c_str(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.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn regorus_engine_add_data_json(
|
||||
engine: *mut RegorusEngine,
|
||||
data: *const c_char,
|
||||
) -> RegorusResult {
|
||||
to_regorus_result(|| -> Result<()> {
|
||||
to_ref(engine)?
|
||||
.engine
|
||||
.add_data(regorus::Value::from_json_str(&from_c_str(data)?)?)
|
||||
}())
|
||||
}
|
||||
|
||||
/// Get list of loaded Rego packages as JSON.
|
||||
///
|
||||
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.get_packages
|
||||
#[no_mangle]
|
||||
pub extern "C" fn regorus_engine_get_packages(engine: *mut RegorusEngine) -> RegorusResult {
|
||||
to_regorus_string_result(|| -> Result<String> {
|
||||
serde_json::to_string_pretty(&to_ref(engine)?.engine.get_packages()?)
|
||||
.map_err(anyhow::Error::msg)
|
||||
}())
|
||||
}
|
||||
|
||||
/// Get list of policies as JSON.
|
||||
///
|
||||
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.get_policies
|
||||
#[no_mangle]
|
||||
pub extern "C" fn regorus_engine_get_policies(engine: *mut RegorusEngine) -> RegorusResult {
|
||||
to_regorus_string_result(|| -> Result<String> {
|
||||
to_ref(engine)?.engine.get_policies_as_json()
|
||||
}())
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
#[no_mangle]
|
||||
pub extern "C" fn regorus_engine_add_data_from_json_file(
|
||||
engine: *mut RegorusEngine,
|
||||
path: *const c_char,
|
||||
) -> RegorusResult {
|
||||
to_regorus_result(|| -> Result<()> {
|
||||
to_ref(engine)?
|
||||
.engine
|
||||
.add_data(regorus::Value::from_json_file(from_c_str(path)?)?)
|
||||
}())
|
||||
}
|
||||
|
||||
/// Clear policy data.
|
||||
///
|
||||
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.clear_data
|
||||
#[no_mangle]
|
||||
pub extern "C" fn regorus_engine_clear_data(engine: *mut RegorusEngine) -> RegorusResult {
|
||||
to_regorus_result(|| -> Result<()> {
|
||||
to_ref(engine)?.engine.clear_data();
|
||||
Ok(())
|
||||
}())
|
||||
}
|
||||
|
||||
/// 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.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn regorus_engine_set_input_json(
|
||||
engine: *mut RegorusEngine,
|
||||
input: *const c_char,
|
||||
) -> RegorusResult {
|
||||
to_regorus_result(|| -> Result<()> {
|
||||
to_ref(engine)?
|
||||
.engine
|
||||
.set_input(regorus::Value::from_json_str(&from_c_str(input)?)?);
|
||||
Ok(())
|
||||
}())
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
#[no_mangle]
|
||||
pub extern "C" fn regorus_engine_set_input_from_json_file(
|
||||
engine: *mut RegorusEngine,
|
||||
path: *const c_char,
|
||||
) -> RegorusResult {
|
||||
to_regorus_result(|| -> Result<()> {
|
||||
to_ref(engine)?
|
||||
.engine
|
||||
.set_input(regorus::Value::from_json_file(from_c_str(path)?)?);
|
||||
Ok(())
|
||||
}())
|
||||
}
|
||||
|
||||
/// Evaluate query.
|
||||
///
|
||||
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.eval_query
|
||||
/// * `query`: Rego expression to be evaluate.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn regorus_engine_eval_query(
|
||||
engine: *mut RegorusEngine,
|
||||
query: *const c_char,
|
||||
) -> RegorusResult {
|
||||
let output = || -> Result<String> {
|
||||
let results = to_ref(engine)?
|
||||
.engine
|
||||
.eval_query(from_c_str(query)?, false)?;
|
||||
Ok(serde_json::to_string_pretty(&results)?)
|
||||
}();
|
||||
match output {
|
||||
Ok(out) => RegorusResult::ok_string(out),
|
||||
Err(e) => to_regorus_result(Err(e)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Evaluate specified rule.
|
||||
///
|
||||
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.eval_rule
|
||||
/// * `rule`: Path to the rule.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn regorus_engine_eval_rule(
|
||||
engine: *mut RegorusEngine,
|
||||
rule: *const c_char,
|
||||
) -> RegorusResult {
|
||||
let output = || -> Result<String> {
|
||||
to_ref(engine)?
|
||||
.engine
|
||||
.eval_rule(from_c_str(rule)?)?
|
||||
.to_json_str()
|
||||
}();
|
||||
match output {
|
||||
Ok(out) => RegorusResult::ok_string(out),
|
||||
Err(e) => to_regorus_result(Err(e)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Enable/disable coverage.
|
||||
///
|
||||
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.set_enable_coverage
|
||||
/// * `enable`: Whether to enable or disable coverage.
|
||||
#[no_mangle]
|
||||
#[cfg(feature = "coverage")]
|
||||
pub extern "C" fn regorus_engine_set_enable_coverage(
|
||||
engine: *mut RegorusEngine,
|
||||
enable: bool,
|
||||
) -> RegorusResult {
|
||||
to_regorus_result(|| -> Result<()> {
|
||||
to_ref(engine)?.engine.set_enable_coverage(enable);
|
||||
Ok(())
|
||||
}())
|
||||
}
|
||||
|
||||
/// Get coverage report.
|
||||
///
|
||||
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.get_coverage_report
|
||||
#[no_mangle]
|
||||
#[cfg(feature = "coverage")]
|
||||
pub extern "C" fn regorus_engine_get_coverage_report(engine: *mut RegorusEngine) -> RegorusResult {
|
||||
let output = || -> Result<String> {
|
||||
Ok(serde_json::to_string_pretty(
|
||||
&to_ref(engine)?.engine.get_coverage_report()?,
|
||||
)?)
|
||||
}();
|
||||
match output {
|
||||
Ok(out) => RegorusResult::ok_string(out),
|
||||
Err(e) => to_regorus_result(Err(e)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Enable/disable strict builtin errors.
|
||||
///
|
||||
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.set_strict_builtin_errors
|
||||
/// * `strict`: Whether to raise errors or return undefined on certain scenarios.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn regorus_engine_set_strict_builtin_errors(
|
||||
engine: *mut RegorusEngine,
|
||||
strict: bool,
|
||||
) -> RegorusResult {
|
||||
to_regorus_result(|| -> Result<()> {
|
||||
to_ref(engine)?.engine.set_strict_builtin_errors(strict);
|
||||
Ok(())
|
||||
}())
|
||||
}
|
||||
|
||||
/// Get pretty printed coverage report.
|
||||
///
|
||||
/// See https://docs.rs/regorus/latest/regorus/coverage/struct.Report.html#method.to_string_pretty
|
||||
#[no_mangle]
|
||||
#[cfg(feature = "coverage")]
|
||||
pub extern "C" fn regorus_engine_get_coverage_report_pretty(
|
||||
engine: *mut RegorusEngine,
|
||||
) -> RegorusResult {
|
||||
let output = || -> Result<String> {
|
||||
to_ref(engine)?
|
||||
.engine
|
||||
.get_coverage_report()?
|
||||
.to_string_pretty()
|
||||
}();
|
||||
match output {
|
||||
Ok(out) => RegorusResult::ok_string(out),
|
||||
Err(e) => to_regorus_result(Err(e)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Clear coverage data.
|
||||
///
|
||||
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.clear_coverage_data
|
||||
#[no_mangle]
|
||||
#[cfg(feature = "coverage")]
|
||||
pub extern "C" fn regorus_engine_clear_coverage_data(engine: *mut RegorusEngine) -> RegorusResult {
|
||||
to_regorus_result(|| -> Result<()> {
|
||||
to_ref(engine)?.engine.clear_coverage_data();
|
||||
Ok(())
|
||||
}())
|
||||
}
|
||||
|
||||
/// 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.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn regorus_engine_set_gather_prints(
|
||||
engine: *mut RegorusEngine,
|
||||
enable: bool,
|
||||
) -> RegorusResult {
|
||||
to_regorus_result(|| -> Result<()> {
|
||||
to_ref(engine)?.engine.set_gather_prints(enable);
|
||||
Ok(())
|
||||
}())
|
||||
}
|
||||
|
||||
/// Take all the gathered print statements.
|
||||
///
|
||||
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.take_prints
|
||||
#[no_mangle]
|
||||
pub extern "C" fn regorus_engine_take_prints(engine: *mut RegorusEngine) -> RegorusResult {
|
||||
let output = || -> Result<String> {
|
||||
Ok(serde_json::to_string_pretty(
|
||||
&to_ref(engine)?.engine.take_prints()?,
|
||||
)?)
|
||||
}();
|
||||
match output {
|
||||
Ok(out) => RegorusResult::ok_string(out),
|
||||
Err(e) => to_regorus_result(Err(e)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get AST of policies.
|
||||
///
|
||||
/// See https://docs.rs/regorus/latest/regorus/coverage/struct.Engine.html#method.get_ast_as_json
|
||||
#[no_mangle]
|
||||
#[cfg(feature = "ast")]
|
||||
pub extern "C" fn regorus_engine_get_ast_as_json(engine: *mut RegorusEngine) -> RegorusResult {
|
||||
let output = || -> Result<String> { to_ref(engine)?.engine.get_ast_as_json() }();
|
||||
match output {
|
||||
Ok(out) => RegorusResult::ok_string(out),
|
||||
Err(e) => to_regorus_result(Err(e)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the package names defined in each policy added to the engine.
|
||||
///
|
||||
/// See https://docs.rs/regorus/latest/regorus/coverage/struct.Engine.html#method.get_policy_package_names
|
||||
#[no_mangle]
|
||||
#[cfg(feature = "azure_policy")]
|
||||
pub extern "C" fn regorus_engine_get_policy_package_names(
|
||||
engine: *mut RegorusEngine,
|
||||
) -> RegorusResult {
|
||||
let output = || -> Result<String> {
|
||||
serde_json::to_string_pretty(&to_ref(engine)?.engine.get_policy_package_names()?)
|
||||
.map_err(anyhow::Error::msg)
|
||||
}();
|
||||
match output {
|
||||
Ok(out) => RegorusResult::ok_string(out),
|
||||
Err(e) => to_regorus_result(Err(e)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the parameters defined in each policy added to the engine.
|
||||
///
|
||||
/// See https://docs.rs/regorus/latest/regorus/coverage/struct.Engine.html#method.get_policy_parameters
|
||||
#[no_mangle]
|
||||
#[cfg(feature = "azure_policy")]
|
||||
pub extern "C" fn regorus_engine_get_policy_parameters(
|
||||
engine: *mut RegorusEngine,
|
||||
) -> RegorusResult {
|
||||
let output = || -> Result<String> {
|
||||
serde_json::to_string_pretty(&to_ref(engine)?.engine.get_policy_parameters()?)
|
||||
.map_err(anyhow::Error::msg)
|
||||
}();
|
||||
match output {
|
||||
Ok(out) => RegorusResult::ok_string(out),
|
||||
Err(e) => to_regorus_result(Err(e)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Enable/disable rego v1.
|
||||
///
|
||||
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.set_rego_v0
|
||||
#[no_mangle]
|
||||
pub extern "C" fn regorus_engine_set_rego_v0(
|
||||
engine: *mut RegorusEngine,
|
||||
enable: bool,
|
||||
) -> RegorusResult {
|
||||
let output = || -> Result<()> {
|
||||
to_ref(engine)?.engine.set_rego_v0(enable);
|
||||
Ok(())
|
||||
}();
|
||||
match output {
|
||||
Ok(()) => RegorusResult::ok_void(),
|
||||
Err(e) => to_regorus_result(Err(e)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Compile a target-aware policy from the current engine state.
|
||||
///
|
||||
/// This method creates a compiled policy that can work with Azure Policy targets,
|
||||
/// enabling resource type inference and target-specific evaluation.
|
||||
///
|
||||
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.compile_for_target
|
||||
#[no_mangle]
|
||||
#[cfg(feature = "azure_policy")]
|
||||
pub extern "C" fn regorus_engine_compile_for_target(engine: *mut RegorusEngine) -> RegorusResult {
|
||||
match to_ref(engine) {
|
||||
Ok(e) => match e.engine.compile_for_target() {
|
||||
Ok(compiled_policy) => {
|
||||
let wrapped_policy = RegorusCompiledPolicy { compiled_policy };
|
||||
let boxed_policy = Box::new(wrapped_policy);
|
||||
RegorusResult::ok_pointer(Box::into_raw(boxed_policy) as *mut std::os::raw::c_void)
|
||||
}
|
||||
Err(e) => RegorusResult::err_with_message(
|
||||
RegorusStatus::CompilationFailed,
|
||||
format!("Failed to compile for target: {e}"),
|
||||
),
|
||||
},
|
||||
Err(e) => RegorusResult::err_with_message(
|
||||
RegorusStatus::InvalidArgument,
|
||||
format!("Failed to get engine reference: {e}"),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
/// Compile a policy with a specific entry point rule.
|
||||
///
|
||||
/// This method creates a compiled policy that evaluates a specific rule as the entry point.
|
||||
///
|
||||
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.compile_with_entrypoint
|
||||
/// * `rule`: The specific rule path to evaluate (e.g., "data.policy.allow")
|
||||
#[no_mangle]
|
||||
pub extern "C" fn regorus_engine_compile_with_entrypoint(
|
||||
engine: *mut RegorusEngine,
|
||||
rule: *const c_char,
|
||||
) -> RegorusResult {
|
||||
let result = || -> Result<RegorusCompiledPolicy> {
|
||||
let rule_str = from_c_str(rule)?;
|
||||
let rule_rc: regorus::Rc<str> = rule_str.into();
|
||||
let compiled_policy = to_ref(engine)?.engine.compile_with_entrypoint(&rule_rc)?;
|
||||
Ok(RegorusCompiledPolicy { compiled_policy })
|
||||
}();
|
||||
|
||||
match result {
|
||||
Ok(wrapped_policy) => {
|
||||
let boxed_policy = Box::new(wrapped_policy);
|
||||
RegorusResult::ok_pointer(Box::into_raw(boxed_policy) as *mut std::os::raw::c_void)
|
||||
}
|
||||
Err(e) => RegorusResult::err_with_message(
|
||||
RegorusStatus::CompilationFailed,
|
||||
format!("Failed to compile with entrypoint: {e}"),
|
||||
),
|
||||
}
|
||||
}
|
||||
+8
-550
@@ -1,553 +1,11 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
use anyhow::{anyhow, bail, Result};
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::os::raw::c_char;
|
||||
|
||||
/// Status of a call on `RegorusEngine`.
|
||||
#[repr(C)]
|
||||
pub enum RegorusStatus {
|
||||
/// The operation was successful.
|
||||
RegorusStatusOk,
|
||||
|
||||
/// The operation was unsuccessful.
|
||||
RegorusStatusError,
|
||||
}
|
||||
|
||||
/// Result of a call on `RegorusEngine`.
|
||||
///
|
||||
/// Must be freed using `regorus_result_drop`.
|
||||
#[repr(C)]
|
||||
pub struct RegorusResult {
|
||||
/// Status
|
||||
status: RegorusStatus,
|
||||
|
||||
/// Output produced by the call.
|
||||
/// Owned by Rust.
|
||||
output: *mut c_char,
|
||||
|
||||
/// Errors produced by the call.
|
||||
/// Owned by Rust.
|
||||
error_message: *mut c_char,
|
||||
}
|
||||
|
||||
fn to_c_str(s: String) -> *mut c_char {
|
||||
match CString::new(s) {
|
||||
Ok(cs) => cs.into_raw(),
|
||||
_ => to_c_str("binding error: failed to create c-style string".to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
fn from_c_str(name: &str, s: *const c_char) -> Result<String> {
|
||||
if s.is_null() {
|
||||
bail!("null pointer");
|
||||
}
|
||||
unsafe {
|
||||
CStr::from_ptr(s)
|
||||
.to_str()
|
||||
.map_err(|e| anyhow!("`{name}`: invalid utf8.\n{e}"))
|
||||
.map(|s| s.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
fn to_ref<'a, T>(t: *mut T) -> Result<&'a mut T> {
|
||||
unsafe { t.as_mut().ok_or_else(|| anyhow!("null pointer")) }
|
||||
}
|
||||
|
||||
fn to_regorus_result(r: Result<()>) -> RegorusResult {
|
||||
match r {
|
||||
Ok(()) => RegorusResult {
|
||||
status: RegorusStatus::RegorusStatusOk,
|
||||
output: std::ptr::null_mut(),
|
||||
error_message: std::ptr::null_mut(),
|
||||
},
|
||||
Err(e) => RegorusResult {
|
||||
status: RegorusStatus::RegorusStatusError,
|
||||
output: std::ptr::null_mut(),
|
||||
error_message: to_c_str(format!("{e}")),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn to_regorus_string_result(r: Result<String>) -> RegorusResult {
|
||||
match r {
|
||||
Ok(s) => RegorusResult {
|
||||
status: RegorusStatus::RegorusStatusOk,
|
||||
output: to_c_str(s),
|
||||
error_message: std::ptr::null_mut(),
|
||||
},
|
||||
Err(e) => RegorusResult {
|
||||
status: RegorusStatus::RegorusStatusError,
|
||||
output: std::ptr::null_mut(),
|
||||
error_message: to_c_str(format!("{e}")),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Wrapper for `regorus::Engine`.
|
||||
#[derive(Clone)]
|
||||
pub struct RegorusEngine {
|
||||
engine: ::regorus::Engine,
|
||||
}
|
||||
|
||||
/// Drop a `RegorusResult`.
|
||||
///
|
||||
/// `output` and `error_message` strings are not valid after drop.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn regorus_result_drop(r: RegorusResult) {
|
||||
unsafe {
|
||||
if !r.error_message.is_null() {
|
||||
let _ = CString::from_raw(r.error_message);
|
||||
}
|
||||
if !r.output.is_null() {
|
||||
let _ = CString::from_raw(r.output);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
/// Construct a new Engine
|
||||
///
|
||||
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html
|
||||
pub extern "C" fn regorus_engine_new() -> *mut RegorusEngine {
|
||||
let mut engine = ::regorus::Engine::new();
|
||||
|
||||
// For more OPA compatibility out of the box, we ask builtins to return undefined
|
||||
// instead of raising errors in certain failure scenarios.
|
||||
engine.set_strict_builtin_errors(false);
|
||||
|
||||
Box::into_raw(Box::new(RegorusEngine { engine }))
|
||||
}
|
||||
|
||||
/// Clone a [`RegorusEngine`]
|
||||
///
|
||||
/// To avoid having to parse same policy again, the engine can be cloned
|
||||
/// after policies and data have been added.
|
||||
///
|
||||
#[no_mangle]
|
||||
pub extern "C" fn regorus_engine_clone(engine: *mut RegorusEngine) -> *mut RegorusEngine {
|
||||
match to_ref(engine) {
|
||||
Ok(e) => Box::into_raw(Box::new(e.clone())),
|
||||
_ => std::ptr::null_mut(),
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn regorus_engine_drop(engine: *mut RegorusEngine) {
|
||||
if let Ok(e) = to_ref(engine) {
|
||||
unsafe {
|
||||
let _ = Box::from_raw(std::ptr::from_mut(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 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.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn regorus_engine_add_policy(
|
||||
engine: *mut RegorusEngine,
|
||||
path: *const c_char,
|
||||
rego: *const c_char,
|
||||
) -> RegorusResult {
|
||||
to_regorus_string_result(|| -> Result<String> {
|
||||
to_ref(engine)?
|
||||
.engine
|
||||
.add_policy(from_c_str("path", path)?, from_c_str("rego", rego)?)
|
||||
}())
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
#[no_mangle]
|
||||
pub extern "C" fn regorus_engine_add_policy_from_file(
|
||||
engine: *mut RegorusEngine,
|
||||
path: *const c_char,
|
||||
) -> RegorusResult {
|
||||
to_regorus_string_result(|| -> Result<String> {
|
||||
to_ref(engine)?
|
||||
.engine
|
||||
.add_policy_from_file(from_c_str("path", 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.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn regorus_engine_add_data_json(
|
||||
engine: *mut RegorusEngine,
|
||||
data: *const c_char,
|
||||
) -> RegorusResult {
|
||||
to_regorus_result(|| -> Result<()> {
|
||||
to_ref(engine)?
|
||||
.engine
|
||||
.add_data(regorus::Value::from_json_str(&from_c_str("data", data)?)?)
|
||||
}())
|
||||
}
|
||||
|
||||
/// Get list of loaded Rego packages as JSON.
|
||||
///
|
||||
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.get_packages
|
||||
#[no_mangle]
|
||||
pub extern "C" fn regorus_engine_get_packages(engine: *mut RegorusEngine) -> RegorusResult {
|
||||
to_regorus_string_result(|| -> Result<String> {
|
||||
serde_json::to_string_pretty(&to_ref(engine)?.engine.get_packages()?)
|
||||
.map_err(anyhow::Error::msg)
|
||||
}())
|
||||
}
|
||||
|
||||
/// Get list of policies as JSON.
|
||||
///
|
||||
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.get_policies
|
||||
#[no_mangle]
|
||||
pub extern "C" fn regorus_engine_get_policies(engine: *mut RegorusEngine) -> RegorusResult {
|
||||
to_regorus_string_result(|| -> Result<String> {
|
||||
to_ref(engine)?.engine.get_policies_as_json()
|
||||
}())
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
#[no_mangle]
|
||||
pub extern "C" fn regorus_engine_add_data_from_json_file(
|
||||
engine: *mut RegorusEngine,
|
||||
path: *const c_char,
|
||||
) -> RegorusResult {
|
||||
to_regorus_result(|| -> Result<()> {
|
||||
to_ref(engine)?
|
||||
.engine
|
||||
.add_data(regorus::Value::from_json_file(from_c_str("path", path)?)?)
|
||||
}())
|
||||
}
|
||||
|
||||
/// Clear policy data.
|
||||
///
|
||||
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.clear_data
|
||||
#[no_mangle]
|
||||
pub extern "C" fn regorus_engine_clear_data(engine: *mut RegorusEngine) -> RegorusResult {
|
||||
to_regorus_result(|| -> Result<()> {
|
||||
to_ref(engine)?.engine.clear_data();
|
||||
Ok(())
|
||||
}())
|
||||
}
|
||||
|
||||
/// 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.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn regorus_engine_set_input_json(
|
||||
engine: *mut RegorusEngine,
|
||||
input: *const c_char,
|
||||
) -> RegorusResult {
|
||||
to_regorus_result(|| -> Result<()> {
|
||||
to_ref(engine)?
|
||||
.engine
|
||||
.set_input(regorus::Value::from_json_str(&from_c_str("input", input)?)?);
|
||||
Ok(())
|
||||
}())
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
#[no_mangle]
|
||||
pub extern "C" fn regorus_engine_set_input_from_json_file(
|
||||
engine: *mut RegorusEngine,
|
||||
path: *const c_char,
|
||||
) -> RegorusResult {
|
||||
to_regorus_result(|| -> Result<()> {
|
||||
to_ref(engine)?
|
||||
.engine
|
||||
.set_input(regorus::Value::from_json_file(from_c_str("path", path)?)?);
|
||||
Ok(())
|
||||
}())
|
||||
}
|
||||
|
||||
/// Evaluate query.
|
||||
///
|
||||
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.eval_query
|
||||
/// * `query`: Rego expression to be evaluate.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn regorus_engine_eval_query(
|
||||
engine: *mut RegorusEngine,
|
||||
query: *const c_char,
|
||||
) -> RegorusResult {
|
||||
let output = || -> Result<String> {
|
||||
let results = to_ref(engine)?
|
||||
.engine
|
||||
.eval_query(from_c_str("query", query)?, false)?;
|
||||
Ok(serde_json::to_string_pretty(&results)?)
|
||||
}();
|
||||
match output {
|
||||
Ok(out) => RegorusResult {
|
||||
status: RegorusStatus::RegorusStatusOk,
|
||||
output: to_c_str(out),
|
||||
error_message: std::ptr::null_mut(),
|
||||
},
|
||||
Err(e) => to_regorus_result(Err(e)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Evaluate specified rule.
|
||||
///
|
||||
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.eval_rule
|
||||
/// * `rule`: Path to the rule.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn regorus_engine_eval_rule(
|
||||
engine: *mut RegorusEngine,
|
||||
rule: *const c_char,
|
||||
) -> RegorusResult {
|
||||
let output = || -> Result<String> {
|
||||
to_ref(engine)?
|
||||
.engine
|
||||
.eval_rule(from_c_str("rule", rule)?)?
|
||||
.to_json_str()
|
||||
}();
|
||||
match output {
|
||||
Ok(out) => RegorusResult {
|
||||
status: RegorusStatus::RegorusStatusOk,
|
||||
output: to_c_str(out),
|
||||
error_message: std::ptr::null_mut(),
|
||||
},
|
||||
Err(e) => to_regorus_result(Err(e)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Enable/disable coverage.
|
||||
///
|
||||
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.set_enable_coverage
|
||||
/// * `enable`: Whether to enable or disable coverage.
|
||||
#[no_mangle]
|
||||
#[cfg(feature = "coverage")]
|
||||
pub extern "C" fn regorus_engine_set_enable_coverage(
|
||||
engine: *mut RegorusEngine,
|
||||
enable: bool,
|
||||
) -> RegorusResult {
|
||||
to_regorus_result(|| -> Result<()> {
|
||||
to_ref(engine)?.engine.set_enable_coverage(enable);
|
||||
Ok(())
|
||||
}())
|
||||
}
|
||||
|
||||
/// Get coverage report.
|
||||
///
|
||||
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.get_coverage_report
|
||||
#[no_mangle]
|
||||
#[cfg(feature = "coverage")]
|
||||
pub extern "C" fn regorus_engine_get_coverage_report(engine: *mut RegorusEngine) -> RegorusResult {
|
||||
let output = || -> Result<String> {
|
||||
Ok(serde_json::to_string_pretty(
|
||||
&to_ref(engine)?.engine.get_coverage_report()?,
|
||||
)?)
|
||||
}();
|
||||
match output {
|
||||
Ok(out) => RegorusResult {
|
||||
status: RegorusStatus::RegorusStatusOk,
|
||||
output: to_c_str(out),
|
||||
error_message: std::ptr::null_mut(),
|
||||
},
|
||||
Err(e) => to_regorus_result(Err(e)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Enable/disable strict builtin errors.
|
||||
///
|
||||
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.set_strict_builtin_errors
|
||||
/// * `strict`: Whether to raise errors or return undefined on certain scenarios.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn regorus_engine_set_strict_builtin_errors(
|
||||
engine: *mut RegorusEngine,
|
||||
strict: bool,
|
||||
) -> RegorusResult {
|
||||
to_regorus_result(|| -> Result<()> {
|
||||
to_ref(engine)?.engine.set_strict_builtin_errors(strict);
|
||||
Ok(())
|
||||
}())
|
||||
}
|
||||
|
||||
/// Get pretty printed coverage report.
|
||||
///
|
||||
/// See https://docs.rs/regorus/latest/regorus/coverage/struct.Report.html#method.to_string_pretty
|
||||
#[no_mangle]
|
||||
#[cfg(feature = "coverage")]
|
||||
pub extern "C" fn regorus_engine_get_coverage_report_pretty(
|
||||
engine: *mut RegorusEngine,
|
||||
) -> RegorusResult {
|
||||
let output = || -> Result<String> {
|
||||
to_ref(engine)?
|
||||
.engine
|
||||
.get_coverage_report()?
|
||||
.to_string_pretty()
|
||||
}();
|
||||
match output {
|
||||
Ok(out) => RegorusResult {
|
||||
status: RegorusStatus::RegorusStatusOk,
|
||||
output: to_c_str(out),
|
||||
error_message: std::ptr::null_mut(),
|
||||
},
|
||||
Err(e) => to_regorus_result(Err(e)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Clear coverage data.
|
||||
///
|
||||
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.clear_coverage_data
|
||||
#[no_mangle]
|
||||
#[cfg(feature = "coverage")]
|
||||
pub extern "C" fn regorus_engine_clear_coverage_data(engine: *mut RegorusEngine) -> RegorusResult {
|
||||
to_regorus_result(|| -> Result<()> {
|
||||
to_ref(engine)?.engine.clear_coverage_data();
|
||||
Ok(())
|
||||
}())
|
||||
}
|
||||
|
||||
/// 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.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn regorus_engine_set_gather_prints(
|
||||
engine: *mut RegorusEngine,
|
||||
enable: bool,
|
||||
) -> RegorusResult {
|
||||
to_regorus_result(|| -> Result<()> {
|
||||
to_ref(engine)?.engine.set_gather_prints(enable);
|
||||
Ok(())
|
||||
}())
|
||||
}
|
||||
|
||||
/// Take all the gathered print statements.
|
||||
///
|
||||
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.take_prints
|
||||
#[no_mangle]
|
||||
pub extern "C" fn regorus_engine_take_prints(engine: *mut RegorusEngine) -> RegorusResult {
|
||||
let output = || -> Result<String> {
|
||||
Ok(serde_json::to_string_pretty(
|
||||
&to_ref(engine)?.engine.take_prints()?,
|
||||
)?)
|
||||
}();
|
||||
match output {
|
||||
Ok(out) => RegorusResult {
|
||||
status: RegorusStatus::RegorusStatusOk,
|
||||
output: to_c_str(out),
|
||||
error_message: std::ptr::null_mut(),
|
||||
},
|
||||
Err(e) => to_regorus_result(Err(e)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get AST of policies.
|
||||
///
|
||||
/// See https://docs.rs/regorus/latest/regorus/coverage/struct.Engine.html#method.get_ast_as_json
|
||||
#[no_mangle]
|
||||
#[cfg(feature = "ast")]
|
||||
pub extern "C" fn regorus_engine_get_ast_as_json(engine: *mut RegorusEngine) -> RegorusResult {
|
||||
let output = || -> Result<String> { to_ref(engine)?.engine.get_ast_as_json() }();
|
||||
match output {
|
||||
Ok(out) => RegorusResult {
|
||||
status: RegorusStatus::RegorusStatusOk,
|
||||
output: to_c_str(out),
|
||||
error_message: std::ptr::null_mut(),
|
||||
},
|
||||
Err(e) => to_regorus_result(Err(e)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the package names defined in each policy added to the engine.
|
||||
///
|
||||
/// See https://docs.rs/regorus/latest/regorus/coverage/struct.Engine.html#method.get_policy_package_names
|
||||
#[no_mangle]
|
||||
#[cfg(feature = "azure_policy")]
|
||||
pub extern "C" fn regorus_engine_get_policy_package_names(
|
||||
engine: *mut RegorusEngine,
|
||||
) -> RegorusResult {
|
||||
let output = || -> Result<String> {
|
||||
serde_json::to_string_pretty(&to_ref(engine)?.engine.get_policy_package_names()?)
|
||||
.map_err(anyhow::Error::msg)
|
||||
}();
|
||||
match output {
|
||||
Ok(out) => RegorusResult {
|
||||
status: RegorusStatus::RegorusStatusOk,
|
||||
output: to_c_str(out),
|
||||
error_message: std::ptr::null_mut(),
|
||||
},
|
||||
Err(e) => to_regorus_result(Err(e)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the parameters defined in each policy added to the engine.
|
||||
///
|
||||
/// See https://docs.rs/regorus/latest/regorus/coverage/struct.Engine.html#method.get_policy_parameters
|
||||
#[no_mangle]
|
||||
#[cfg(feature = "azure_policy")]
|
||||
pub extern "C" fn regorus_engine_get_policy_parameters(
|
||||
engine: *mut RegorusEngine,
|
||||
) -> RegorusResult {
|
||||
let output = || -> Result<String> {
|
||||
serde_json::to_string_pretty(&to_ref(engine)?.engine.get_policy_parameters()?)
|
||||
.map_err(anyhow::Error::msg)
|
||||
}();
|
||||
match output {
|
||||
Ok(out) => RegorusResult {
|
||||
status: RegorusStatus::RegorusStatusOk,
|
||||
output: to_c_str(out),
|
||||
error_message: std::ptr::null_mut(),
|
||||
},
|
||||
Err(e) => to_regorus_result(Err(e)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Enable/disable rego v1.
|
||||
///
|
||||
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.set_rego_v0
|
||||
#[no_mangle]
|
||||
pub extern "C" fn regorus_engine_set_rego_v0(
|
||||
engine: *mut RegorusEngine,
|
||||
enable: bool,
|
||||
) -> RegorusResult {
|
||||
let output = || -> Result<()> {
|
||||
to_ref(engine)?.engine.set_rego_v0(enable);
|
||||
Ok(())
|
||||
}();
|
||||
match output {
|
||||
Ok(()) => RegorusResult {
|
||||
status: RegorusStatus::RegorusStatusOk,
|
||||
output: std::ptr::null_mut(),
|
||||
error_message: std::ptr::null_mut(),
|
||||
},
|
||||
Err(e) => to_regorus_result(Err(e)),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "custom_allocator")]
|
||||
extern "C" {
|
||||
fn regorus_aligned_alloc(alignment: usize, size: usize) -> *mut u8;
|
||||
fn regorus_free(ptr: *mut u8);
|
||||
}
|
||||
|
||||
#[cfg(feature = "custom_allocator")]
|
||||
mod allocator {
|
||||
use std::alloc::{GlobalAlloc, Layout};
|
||||
|
||||
struct RegorusAllocator {}
|
||||
|
||||
unsafe impl GlobalAlloc for RegorusAllocator {
|
||||
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
|
||||
let size = layout.size();
|
||||
let align = layout.align();
|
||||
|
||||
crate::regorus_aligned_alloc(align, size)
|
||||
}
|
||||
|
||||
unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) {
|
||||
crate::regorus_free(ptr)
|
||||
}
|
||||
}
|
||||
|
||||
#[global_allocator]
|
||||
static ALLOCATOR: RegorusAllocator = RegorusAllocator {};
|
||||
}
|
||||
mod allocator;
|
||||
mod common;
|
||||
mod compile;
|
||||
mod compiled_policy;
|
||||
mod effect_registry;
|
||||
mod engine;
|
||||
mod schema_registry;
|
||||
mod target_registry;
|
||||
|
||||
@@ -0,0 +1,178 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
//! Schema registry functions for FFI.
|
||||
//!
|
||||
//! These functions provide access to regorus's resource schema registry functionality,
|
||||
//! enabling registration and management of Azure Policy resource schemas.
|
||||
|
||||
#![cfg(feature = "azure_policy")]
|
||||
|
||||
use crate::common::{from_c_str, RegorusResult, RegorusStatus};
|
||||
use regorus::{registry::schemas, Schema};
|
||||
|
||||
use std::os::raw::c_char;
|
||||
|
||||
// Resource Schema Registry Functions
|
||||
|
||||
/// Register a resource schema from JSON with a given name.
|
||||
///
|
||||
/// # Parameters
|
||||
/// * `name` - Name to register the schema under
|
||||
/// * `schema_json` - JSON string representing the schema
|
||||
///
|
||||
/// # Returns
|
||||
/// Returns a RegorusResult with success/error status.
|
||||
///
|
||||
/// # Safety
|
||||
/// All string parameters must be valid null-terminated UTF-8 strings.
|
||||
#[cfg(feature = "azure_policy")]
|
||||
#[no_mangle]
|
||||
pub extern "C" fn regorus_resource_schema_register(
|
||||
name: *const c_char,
|
||||
schema_json: *const c_char,
|
||||
) -> RegorusResult {
|
||||
let schema_name = match from_c_str(name) {
|
||||
Ok(s) => s,
|
||||
Err(e) => {
|
||||
return RegorusResult::err_with_message(
|
||||
RegorusStatus::InvalidArgument,
|
||||
format!("Invalid schema name string: {e}"),
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
let schema_str = match from_c_str(schema_json) {
|
||||
Ok(s) => s,
|
||||
Err(e) => {
|
||||
return RegorusResult::err_with_message(
|
||||
RegorusStatus::InvalidDataFormat,
|
||||
format!("Invalid schema JSON string: {e}"),
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
// Parse schema from JSON
|
||||
let schema = match Schema::from_json_str(&schema_str) {
|
||||
Ok(schema) => schema,
|
||||
Err(e) => {
|
||||
return RegorusResult::err_with_message(
|
||||
RegorusStatus::InvalidDataFormat,
|
||||
format!("Failed to parse schema JSON: {e}"),
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
// Register the schema
|
||||
match schemas::resource::register(schema_name, schema.into()) {
|
||||
Ok(()) => RegorusResult::ok_pointer(std::ptr::null_mut()),
|
||||
Err(e) => RegorusResult::err_with_message(
|
||||
RegorusStatus::Error,
|
||||
format!("Failed to register schema: {e}"),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if a resource schema with the given name exists.
|
||||
///
|
||||
/// # Parameters
|
||||
/// * `name` - Name of the schema to check
|
||||
///
|
||||
/// # Returns
|
||||
/// Returns a RegorusResult with "true" or "false" string output.
|
||||
///
|
||||
/// # Safety
|
||||
/// The name parameter must be a valid null-terminated UTF-8 string.
|
||||
#[cfg(feature = "azure_policy")]
|
||||
#[no_mangle]
|
||||
pub extern "C" fn regorus_resource_schema_contains(name: *const c_char) -> RegorusResult {
|
||||
let schema_name = match from_c_str(name) {
|
||||
Ok(s) => s,
|
||||
Err(e) => {
|
||||
return RegorusResult::err_with_message(
|
||||
RegorusStatus::InvalidArgument,
|
||||
format!("Invalid schema name string: {e}"),
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
let contains = schemas::resource::contains(&schema_name);
|
||||
RegorusResult::ok_bool(contains)
|
||||
}
|
||||
|
||||
/// Get the number of registered resource schemas.
|
||||
///
|
||||
/// # Returns
|
||||
/// Returns a RegorusResult with the count as a string.
|
||||
#[cfg(feature = "azure_policy")]
|
||||
#[no_mangle]
|
||||
pub extern "C" fn regorus_resource_schema_len() -> RegorusResult {
|
||||
let count = schemas::resource::len();
|
||||
RegorusResult::ok_int(count as i64)
|
||||
}
|
||||
|
||||
/// Check if the resource schema registry is empty.
|
||||
///
|
||||
/// # Returns
|
||||
/// Returns a RegorusResult with "true" or "false" string output.
|
||||
#[cfg(feature = "azure_policy")]
|
||||
#[no_mangle]
|
||||
pub extern "C" fn regorus_resource_schema_is_empty() -> RegorusResult {
|
||||
let is_empty = schemas::resource::is_empty();
|
||||
RegorusResult::ok_bool(is_empty)
|
||||
}
|
||||
|
||||
/// List all registered resource schema names as a JSON array.
|
||||
///
|
||||
/// # Returns
|
||||
/// Returns a RegorusResult with a JSON array of schema names.
|
||||
#[cfg(feature = "azure_policy")]
|
||||
#[no_mangle]
|
||||
pub extern "C" fn regorus_resource_schema_list_names() -> RegorusResult {
|
||||
let names = schemas::resource::list_names();
|
||||
match serde_json::to_string(&names) {
|
||||
Ok(json_str) => RegorusResult::ok_string(json_str),
|
||||
Err(e) => RegorusResult::err_with_message(
|
||||
RegorusStatus::Error,
|
||||
format!("Failed to serialize schema names to JSON: {e}"),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
/// Remove a resource schema by name.
|
||||
///
|
||||
/// # Parameters
|
||||
/// * `name` - Name of the schema to remove
|
||||
///
|
||||
/// # Returns
|
||||
/// Returns a RegorusResult with "true" if removed, "false" if not found.
|
||||
///
|
||||
/// # Safety
|
||||
/// The name parameter must be a valid null-terminated UTF-8 string.
|
||||
#[cfg(feature = "azure_policy")]
|
||||
#[no_mangle]
|
||||
pub extern "C" fn regorus_resource_schema_remove(name: *const c_char) -> RegorusResult {
|
||||
let schema_name = match from_c_str(name) {
|
||||
Ok(s) => s,
|
||||
Err(e) => {
|
||||
return RegorusResult::err_with_message(
|
||||
RegorusStatus::InvalidArgument,
|
||||
format!("Invalid schema name string: {e}"),
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
let removed = schemas::resource::remove(&schema_name).is_some();
|
||||
RegorusResult::ok_bool(removed)
|
||||
}
|
||||
|
||||
/// Clear all resource schemas from the registry.
|
||||
///
|
||||
/// # Returns
|
||||
/// Returns a RegorusResult with success status.
|
||||
#[cfg(feature = "azure_policy")]
|
||||
#[no_mangle]
|
||||
pub extern "C" fn regorus_resource_schema_clear() -> RegorusResult {
|
||||
schemas::resource::clear();
|
||||
RegorusResult::ok_pointer(std::ptr::null_mut())
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#![cfg(feature = "azure_policy")]
|
||||
|
||||
use crate::common::*;
|
||||
use anyhow::Result;
|
||||
use std::os::raw::c_char;
|
||||
|
||||
/// Register a target from JSON definition.
|
||||
///
|
||||
/// The target JSON should follow the target schema format.
|
||||
/// Once registered, the target can be referenced in Rego policies using `__target__` rules.
|
||||
///
|
||||
/// * `target_json`: JSON encoded target definition
|
||||
#[no_mangle]
|
||||
#[cfg(feature = "azure_policy")]
|
||||
pub extern "C" fn regorus_register_target_from_json(target_json: *const c_char) -> RegorusResult {
|
||||
to_regorus_result(|| -> Result<()> {
|
||||
let target_str = from_c_str(target_json)?;
|
||||
let target = regorus::Target::from_json_str(&target_str)?;
|
||||
regorus::registry::targets::register(regorus::Rc::new(target))?;
|
||||
Ok(())
|
||||
}())
|
||||
}
|
||||
|
||||
/// Check if a target is registered.
|
||||
///
|
||||
/// # Parameters
|
||||
/// * `name` - Name of the target to check
|
||||
///
|
||||
/// # Returns
|
||||
/// Returns a RegorusResult with boolean value indicating if the target is registered.
|
||||
///
|
||||
/// # Safety
|
||||
/// The name parameter must be a valid null-terminated UTF-8 string.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn regorus_target_registry_contains(name: *const c_char) -> RegorusResult {
|
||||
let target_name = match from_c_str(name) {
|
||||
Ok(s) => s,
|
||||
Err(e) => {
|
||||
return RegorusResult::err_with_message(
|
||||
RegorusStatus::InvalidArgument,
|
||||
format!("Invalid target name string: {e}"),
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
let contains = regorus::registry::targets::contains(&target_name);
|
||||
RegorusResult::ok_bool(contains)
|
||||
}
|
||||
|
||||
/// Get a list of all registered target names as JSON array.
|
||||
#[no_mangle]
|
||||
#[cfg(feature = "azure_policy")]
|
||||
pub extern "C" fn regorus_target_registry_list_names() -> RegorusResult {
|
||||
let names = regorus::registry::targets::list_names();
|
||||
let output = serde_json::to_string_pretty(&names).map_err(anyhow::Error::msg);
|
||||
|
||||
match output {
|
||||
Ok(out) => RegorusResult::ok_string(out),
|
||||
Err(e) => to_regorus_result(Err(e)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Remove a target from the registry by name.
|
||||
///
|
||||
/// * `name`: The target name to remove
|
||||
#[no_mangle]
|
||||
#[cfg(feature = "azure_policy")]
|
||||
pub extern "C" fn regorus_target_registry_remove(name: *const c_char) -> RegorusResult {
|
||||
to_regorus_result(|| -> Result<()> {
|
||||
let name_str = from_c_str(name)?;
|
||||
regorus::registry::targets::remove(&name_str);
|
||||
Ok(())
|
||||
}())
|
||||
}
|
||||
|
||||
/// Clear all targets from the registry.
|
||||
#[no_mangle]
|
||||
#[cfg(feature = "azure_policy")]
|
||||
pub extern "C" fn regorus_target_registry_clear() -> RegorusResult {
|
||||
regorus::registry::targets::clear();
|
||||
RegorusResult::ok_void()
|
||||
}
|
||||
|
||||
/// Get the number of registered targets.
|
||||
///
|
||||
/// # Returns
|
||||
/// Returns a RegorusResult with the count as an integer value.
|
||||
#[no_mangle]
|
||||
#[cfg(feature = "azure_policy")]
|
||||
pub extern "C" fn regorus_target_registry_len() -> RegorusResult {
|
||||
let count = regorus::registry::targets::len();
|
||||
RegorusResult::ok_int(count as i64)
|
||||
}
|
||||
|
||||
/// Check if the target registry is empty.
|
||||
///
|
||||
/// # Returns
|
||||
/// Returns a RegorusResult with boolean value indicating if the registry is empty.
|
||||
#[no_mangle]
|
||||
#[cfg(feature = "azure_policy")]
|
||||
pub extern "C" fn regorus_target_registry_is_empty() -> RegorusResult {
|
||||
let is_empty = regorus::registry::targets::is_empty();
|
||||
RegorusResult::ok_bool(is_empty)
|
||||
}
|
||||
Reference in New Issue
Block a user