mirror of
https://github.com/microsoft/regorus.git
synced 2026-08-05 02:16:11 +00:00
feat: Implement methods to get package names and parameters (#425)
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -28,3 +28,6 @@ bindings/*/target
|
||||
# C# build folders
|
||||
**bin
|
||||
**obj
|
||||
|
||||
# Visual Studio folders
|
||||
**/*.vs
|
||||
@@ -23,6 +23,7 @@ default = ["full-opa", "arc"]
|
||||
|
||||
arc = ["scientific/arc"]
|
||||
ast = []
|
||||
azure_policy = []
|
||||
base64 = ["dep:data-encoding"]
|
||||
base64url = ["dep:data-encoding"]
|
||||
coverage = []
|
||||
|
||||
@@ -173,4 +173,43 @@ public class RegorusTests
|
||||
|
||||
Assert.IsTrue(JsonNode.DeepEquals(JsonNode.Parse(expected), JsonNode.Parse(result!)), $"Actual: {result}");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void GetPolicyPackageNames_succeeds()
|
||||
{
|
||||
using var engine = new Engine();
|
||||
engine.AddPolicy(
|
||||
"test.rego",
|
||||
"package test\nx = 1\nmessage = `Hello`");
|
||||
|
||||
engine.AddPolicy(
|
||||
"test.rego",
|
||||
"package test.nested.name\nx = 1\nmessage = `Hello`");
|
||||
|
||||
var result = engine.GetPolicyPackageNames();
|
||||
|
||||
var packageNames = JsonNode.Parse(result!);
|
||||
|
||||
Assert.AreEqual("test", packageNames![0]["package_name"].ToString());
|
||||
Assert.AreEqual("test.nested.name", packageNames![1]["package_name"].ToString());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void GetPolicyParameters_succeeds()
|
||||
{
|
||||
using var engine = new Engine();
|
||||
engine.AddPolicy(
|
||||
"test.rego",
|
||||
"package test\n default parameters.a = 5\nparameters.b = 10\nx = 1\nmessage = `Hello`");
|
||||
|
||||
var result = engine.GetPolicyParameters();
|
||||
|
||||
var parameters = JsonNode.Parse(result!);
|
||||
|
||||
Assert.AreEqual(1, parameters![0]["parameters"].AsArray().Count);
|
||||
Assert.AreEqual(1, parameters![0]["modifiers"].AsArray().Count);
|
||||
|
||||
Assert.AreEqual("a", parameters![0]["parameters"][0]["name"].ToString());
|
||||
Assert.AreEqual("b", parameters![0]["modifiers"][0]["name"].ToString());
|
||||
}
|
||||
}
|
||||
@@ -217,6 +217,16 @@ namespace Regorus
|
||||
return CheckAndDropResult(Regorus.Internal.API.regorus_engine_get_ast_as_json(E));
|
||||
}
|
||||
|
||||
public string? GetPolicyPackageNames()
|
||||
{
|
||||
return CheckAndDropResult(Regorus.Internal.API.regorus_engine_get_policy_package_names(E));
|
||||
}
|
||||
|
||||
public string? GetPolicyParameters()
|
||||
{
|
||||
return CheckAndDropResult(Regorus.Internal.API.regorus_engine_get_policy_parameters(E));
|
||||
}
|
||||
|
||||
string? StringFromUTF8(IntPtr ptr)
|
||||
{
|
||||
|
||||
|
||||
@@ -193,6 +193,22 @@ namespace Regorus.Internal
|
||||
[DllImport(__DllName, EntryPoint = "regorus_engine_get_ast_as_json", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
internal static extern RegorusResult regorus_engine_get_ast_as_json(RegorusEngine* engine);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the package names of policies added to the engine.
|
||||
///
|
||||
/// See https://docs.rs/regorus/latest/regorus/coverage/struct.Engine.html#method.get_policy_package_names
|
||||
/// </summary>
|
||||
[DllImport(__DllName, EntryPoint = "regorus_engine_get_policy_package_names", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
internal static extern RegorusResult regorus_engine_get_policy_package_names(RegorusEngine* engine);
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
[DllImport(__DllName, EntryPoint = "regorus_engine_get_policy_parameters", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
internal static extern RegorusResult regorus_engine_get_policy_parameters(RegorusEngine* engine);
|
||||
|
||||
/// <summary>
|
||||
/// Enable/disable rego v1.
|
||||
///
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
},
|
||||
"sdk": {
|
||||
"allowPrerelease": false,
|
||||
"version": "8.0.408",
|
||||
"version": "8.0.412",
|
||||
"rollForward": "disable"
|
||||
}
|
||||
}
|
||||
@@ -23,8 +23,9 @@ lto = true
|
||||
codegen-units = 1
|
||||
|
||||
[features]
|
||||
default = ["ast", "std", "coverage", "regorus/arc", "regorus/full-opa"]
|
||||
default = ["ast", "azure_policy", "std", "coverage", "regorus/arc", "regorus/full-opa"]
|
||||
ast = ["regorus/ast"]
|
||||
azure_policy = ["regorus/azure_policy"]
|
||||
std = ["regorus/std"]
|
||||
coverage = ["regorus/coverage"]
|
||||
custom_allocator = []
|
||||
|
||||
@@ -457,6 +457,40 @@ pub extern "C" fn regorus_engine_get_ast_as_json(engine: *mut RegorusEngine) ->
|
||||
}
|
||||
}
|
||||
|
||||
/// 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
|
||||
|
||||
132
src/engine.rs
132
src/engine.rs
@@ -23,6 +23,31 @@ pub struct Engine {
|
||||
rego_v1: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct PolicyPackageNameDefinition {
|
||||
pub source_file: String,
|
||||
pub package_name: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct PolicyParameter {
|
||||
pub name: String,
|
||||
pub modifiable: bool,
|
||||
pub required: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct PolicyModifier {
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct PolicyParameters {
|
||||
pub source_file: String,
|
||||
pub parameters: Vec<PolicyParameter>,
|
||||
pub modifiers: Vec<PolicyModifier>,
|
||||
}
|
||||
|
||||
/// Create a default engine.
|
||||
impl Default for Engine {
|
||||
fn default() -> Self {
|
||||
@@ -907,6 +932,113 @@ impl Engine {
|
||||
serde_json::to_string_pretty(&ast).map_err(anyhow::Error::msg)
|
||||
}
|
||||
|
||||
/// Get the package names of each policy added to the engine.
|
||||
///
|
||||
///
|
||||
/// ```rust
|
||||
/// # use regorus::*;
|
||||
/// # use anyhow::{bail, Result};
|
||||
/// # fn main() -> Result<()> {
|
||||
/// # let mut engine = Engine::new();
|
||||
/// engine.add_policy("test.rego".to_string(), "package test\n x := 1".to_string())?;
|
||||
/// engine.add_policy("test2.rego".to_string(), "package test.multi.segment\n x := 1".to_string())?;
|
||||
///
|
||||
/// let package_names = engine.get_policy_package_names()?;
|
||||
///
|
||||
/// assert_eq!("test", package_names[0].package_name);
|
||||
/// assert_eq!("test.multi.segment", package_names[1].package_name);
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
#[cfg(feature = "azure_policy")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "azure_policy")))]
|
||||
pub fn get_policy_package_names(&self) -> Result<Vec<PolicyPackageNameDefinition>> {
|
||||
let mut package_names = vec![];
|
||||
for m in &self.modules {
|
||||
let package_name = Interpreter::get_path_string(&m.package.refr, None)?;
|
||||
package_names.push(PolicyPackageNameDefinition {
|
||||
source_file: m.package.span.source.file().to_string(),
|
||||
package_name,
|
||||
});
|
||||
}
|
||||
|
||||
Ok(package_names)
|
||||
}
|
||||
|
||||
/// Get the parameters defined in each policy.
|
||||
///
|
||||
///
|
||||
/// ```rust
|
||||
/// # use regorus::*;
|
||||
/// # use anyhow::{bail, Result};
|
||||
/// # fn main() -> Result<()> {
|
||||
/// # let mut engine = Engine::new();
|
||||
/// engine.add_policy("test.rego".to_string(), "package test default parameters.a = 5 parameters.b = 10\n x := 1".to_string())?;
|
||||
///
|
||||
/// let parameters = engine.get_policy_parameters()?;
|
||||
///
|
||||
/// assert_eq!("a", parameters[0].parameters[0].name);
|
||||
/// assert_eq!("b", parameters[0].modifiers[0].name);
|
||||
///
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
#[cfg(feature = "azure_policy")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "azure_policy")))]
|
||||
pub fn get_policy_parameters(&self) -> Result<Vec<PolicyParameters>> {
|
||||
let mut policy_parameter_definitions = vec![];
|
||||
for m in &self.modules {
|
||||
let mut parameters = vec![];
|
||||
let mut modifiers = vec![];
|
||||
|
||||
for rule in &m.policy {
|
||||
// Extract parameter definitions from the policy rule
|
||||
// e.g. default parameters.a = 5
|
||||
if let Rule::Default { refr, .. } = rule.as_ref() {
|
||||
let path = Parser::get_path_ref_components(refr)?;
|
||||
let paths: Vec<&str> = path.iter().map(|s| s.text()).collect();
|
||||
|
||||
if paths.len() == 2 && paths[0] == "parameters" {
|
||||
// Todo: Fetch fields other than name from rego metadoc for the parameter
|
||||
parameters.push(PolicyParameter {
|
||||
name: paths[1].to_string(),
|
||||
modifiable: false,
|
||||
required: false,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Extract modifiers to the parameters from the policy rule
|
||||
// e.g. parameters.a = 5
|
||||
if let Rule::Spec { head, .. } = rule.as_ref() {
|
||||
match head {
|
||||
RuleHead::Compr { refr, .. } => {
|
||||
let path = Parser::get_path_ref_components(refr)?;
|
||||
let paths: Vec<&str> = path.iter().map(|s| s.text()).collect();
|
||||
|
||||
if paths.len() == 2 && paths[0] == "parameters" {
|
||||
// Todo: Fetch fields other than name from rego metadoc for the parameter
|
||||
modifiers.push(PolicyModifier {
|
||||
name: paths[1].to_string(),
|
||||
})
|
||||
}
|
||||
}
|
||||
RuleHead::Func { .. } => {}
|
||||
RuleHead::Set { .. } => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
policy_parameter_definitions.push(PolicyParameters {
|
||||
source_file: m.package.span.source.file().to_string(),
|
||||
parameters,
|
||||
modifiers,
|
||||
});
|
||||
}
|
||||
|
||||
Ok(policy_parameter_definitions)
|
||||
}
|
||||
|
||||
fn make_parser<'a>(&self, source: &'a Source) -> Result<Parser<'a>> {
|
||||
let mut parser = Parser::new(source)?;
|
||||
if self.rego_v1 {
|
||||
|
||||
@@ -101,3 +101,99 @@ fn extension_with_state() -> Result<()> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "azure_policy")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "azure_policy")))]
|
||||
fn get_policy_package_names() -> Result<()> {
|
||||
let mut engine = Engine::new();
|
||||
engine.add_policy(
|
||||
"testPolicy1".to_string(),
|
||||
r#"package test
|
||||
|
||||
deny if {
|
||||
1 == 2
|
||||
}
|
||||
"#
|
||||
.to_string(),
|
||||
)?;
|
||||
|
||||
engine.add_policy(
|
||||
"testPolicy2".to_string(),
|
||||
r#"package test.nested.name
|
||||
deny if {
|
||||
1 == 2
|
||||
}
|
||||
"#
|
||||
.to_string(),
|
||||
)?;
|
||||
|
||||
let package_names = engine.get_policy_package_names()?;
|
||||
|
||||
assert_eq!(2, package_names.len());
|
||||
assert_eq!("test", package_names[0].package_name);
|
||||
assert_eq!("testPolicy1", package_names[0].source_file);
|
||||
|
||||
assert_eq!("test.nested.name", package_names[1].package_name);
|
||||
assert_eq!("testPolicy2", package_names[1].source_file);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "azure_policy")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "azure_policy")))]
|
||||
fn get_policy_parameters() -> Result<()> {
|
||||
let mut engine = Engine::new();
|
||||
engine.add_policy(
|
||||
"testPolicy1".to_string(),
|
||||
r#"package test
|
||||
default parameters.a = 5
|
||||
default parameters.b = { asdf: 10}
|
||||
|
||||
parameters.c = 10
|
||||
|
||||
deny if {
|
||||
parameter.a == parameter.b.asdf
|
||||
}
|
||||
"#
|
||||
.to_string(),
|
||||
)?;
|
||||
|
||||
engine.add_policy(
|
||||
"testPolicy2".to_string(),
|
||||
r#"package test
|
||||
default parameters = {
|
||||
a: 5,
|
||||
b: { asdf: 10 }
|
||||
}
|
||||
|
||||
parameters.c = 5
|
||||
|
||||
deny if {
|
||||
parameters.a == parameters.b.asdf
|
||||
}
|
||||
"#
|
||||
.to_string(),
|
||||
)?;
|
||||
|
||||
let parameters = engine.get_policy_parameters()?;
|
||||
// let ast = engine.get_ast_as_json()?;
|
||||
// println!("ast: {}", ast);
|
||||
// let parameters = Value::from_json_str(&result)?;
|
||||
|
||||
assert_eq!(2, parameters.len());
|
||||
|
||||
let test_policy1_parameters = ¶meters[0];
|
||||
assert_eq!(2, test_policy1_parameters.parameters.len());
|
||||
assert_eq!("a", test_policy1_parameters.parameters[0].name);
|
||||
assert_eq!("b", test_policy1_parameters.parameters[1].name);
|
||||
|
||||
// We expect parameters to be defined separately, so the second policy does not have any parameters
|
||||
let test_policy2_parameters = ¶meters[1];
|
||||
assert_eq!(0, test_policy2_parameters.parameters.len());
|
||||
|
||||
assert_eq!(1, test_policy2_parameters.modifiers.len());
|
||||
assert_eq!("c", test_policy2_parameters.modifiers[0].name);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user