Skip to content

Commit 9fce2cc

Browse files
authored
feat: Implement methods to get package names and parameters (#425)
1 parent 3b802c1 commit 9fce2cc

10 files changed

Lines changed: 335 additions & 3 deletions

File tree

.gitignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,7 @@ bindings/*/target
2727

2828
# C# build folders
2929
**bin
30-
**obj
30+
**obj
31+
32+
# Visual Studio folders
33+
**/*.vs

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ default = ["full-opa", "arc"]
2323

2424
arc = ["scientific/arc"]
2525
ast = []
26+
azure_policy = []
2627
base64 = ["dep:data-encoding"]
2728
base64url = ["dep:data-encoding"]
2829
coverage = []

bindings/csharp/Regorus.Tests/RegorusTests.cs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,4 +173,43 @@ public void Evaluation_using_file_policies_succeeds()
173173

174174
Assert.IsTrue(JsonNode.DeepEquals(JsonNode.Parse(expected), JsonNode.Parse(result!)), $"Actual: {result}");
175175
}
176+
177+
[TestMethod]
178+
public void GetPolicyPackageNames_succeeds()
179+
{
180+
using var engine = new Engine();
181+
engine.AddPolicy(
182+
"test.rego",
183+
"package test\nx = 1\nmessage = `Hello`");
184+
185+
engine.AddPolicy(
186+
"test.rego",
187+
"package test.nested.name\nx = 1\nmessage = `Hello`");
188+
189+
var result = engine.GetPolicyPackageNames();
190+
191+
var packageNames = JsonNode.Parse(result!);
192+
193+
Assert.AreEqual("test", packageNames![0]["package_name"].ToString());
194+
Assert.AreEqual("test.nested.name", packageNames![1]["package_name"].ToString());
195+
}
196+
197+
[TestMethod]
198+
public void GetPolicyParameters_succeeds()
199+
{
200+
using var engine = new Engine();
201+
engine.AddPolicy(
202+
"test.rego",
203+
"package test\n default parameters.a = 5\nparameters.b = 10\nx = 1\nmessage = `Hello`");
204+
205+
var result = engine.GetPolicyParameters();
206+
207+
var parameters = JsonNode.Parse(result!);
208+
209+
Assert.AreEqual(1, parameters![0]["parameters"].AsArray().Count);
210+
Assert.AreEqual(1, parameters![0]["modifiers"].AsArray().Count);
211+
212+
Assert.AreEqual("a", parameters![0]["parameters"][0]["name"].ToString());
213+
Assert.AreEqual("b", parameters![0]["modifiers"][0]["name"].ToString());
214+
}
176215
}

bindings/csharp/Regorus/Regorus.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,16 @@ public void SetGatherPrints(bool enable)
217217
return CheckAndDropResult(Regorus.Internal.API.regorus_engine_get_ast_as_json(E));
218218
}
219219

220+
public string? GetPolicyPackageNames()
221+
{
222+
return CheckAndDropResult(Regorus.Internal.API.regorus_engine_get_policy_package_names(E));
223+
}
224+
225+
public string? GetPolicyParameters()
226+
{
227+
return CheckAndDropResult(Regorus.Internal.API.regorus_engine_get_policy_parameters(E));
228+
}
229+
220230
string? StringFromUTF8(IntPtr ptr)
221231
{
222232

bindings/csharp/Regorus/RegorusFFI.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,22 @@ internal static unsafe partial class API
193193
[DllImport(__DllName, EntryPoint = "regorus_engine_get_ast_as_json", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
194194
internal static extern RegorusResult regorus_engine_get_ast_as_json(RegorusEngine* engine);
195195

196+
/// <summary>
197+
/// Gets the package names of policies added to the engine.
198+
///
199+
/// See https://docs.rs/regorus/latest/regorus/coverage/struct.Engine.html#method.get_policy_package_names
200+
/// </summary>
201+
[DllImport(__DllName, EntryPoint = "regorus_engine_get_policy_package_names", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
202+
internal static extern RegorusResult regorus_engine_get_policy_package_names(RegorusEngine* engine);
203+
204+
/// <summary>
205+
/// Gets the parameters defined in each policy added to the engine
206+
///
207+
/// See https://docs.rs/regorus/latest/regorus/coverage/struct.Engine.html#method.get_policy_parameters
208+
/// </summary>
209+
[DllImport(__DllName, EntryPoint = "regorus_engine_get_policy_parameters", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
210+
internal static extern RegorusResult regorus_engine_get_policy_parameters(RegorusEngine* engine);
211+
196212
/// <summary>
197213
/// Enable/disable rego v1.
198214
///

bindings/csharp/global.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
},
55
"sdk": {
66
"allowPrerelease": false,
7-
"version": "8.0.408",
7+
"version": "8.0.412",
88
"rollForward": "disable"
99
}
1010
}

bindings/ffi/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,9 @@ lto = true
2323
codegen-units = 1
2424

2525
[features]
26-
default = ["ast", "std", "coverage", "regorus/arc", "regorus/full-opa"]
26+
default = ["ast", "azure_policy", "std", "coverage", "regorus/arc", "regorus/full-opa"]
2727
ast = ["regorus/ast"]
28+
azure_policy = ["regorus/azure_policy"]
2829
std = ["regorus/std"]
2930
coverage = ["regorus/coverage"]
3031
custom_allocator = []

bindings/ffi/src/lib.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -457,6 +457,40 @@ pub extern "C" fn regorus_engine_get_ast_as_json(engine: *mut RegorusEngine) ->
457457
}
458458
}
459459

460+
/// Gets the package names defined in each policy added to the engine.
461+
///
462+
/// See https://docs.rs/regorus/latest/regorus/coverage/struct.Engine.html#method.get_policy_package_names
463+
#[no_mangle]
464+
#[cfg(feature = "azure_policy")]
465+
pub extern "C" fn regorus_engine_get_policy_package_names(engine: *mut RegorusEngine) -> RegorusResult {
466+
let output = || -> Result<String> { serde_json::to_string_pretty(&to_ref(engine)?.engine.get_policy_package_names()?).map_err(anyhow::Error::msg) }();
467+
match output {
468+
Ok(out) => RegorusResult {
469+
status: RegorusStatus::RegorusStatusOk,
470+
output: to_c_str(out),
471+
error_message: std::ptr::null_mut(),
472+
},
473+
Err(e) => to_regorus_result(Err(e)),
474+
}
475+
}
476+
477+
/// Gets the parameters defined in each policy added to the engine.
478+
///
479+
/// See https://docs.rs/regorus/latest/regorus/coverage/struct.Engine.html#method.get_policy_parameters
480+
#[no_mangle]
481+
#[cfg(feature = "azure_policy")]
482+
pub extern "C" fn regorus_engine_get_policy_parameters(engine: *mut RegorusEngine) -> RegorusResult {
483+
let output = || -> Result<String> { serde_json::to_string_pretty(&to_ref(engine)?.engine.get_policy_parameters()?).map_err(anyhow::Error::msg) }();
484+
match output {
485+
Ok(out) => RegorusResult {
486+
status: RegorusStatus::RegorusStatusOk,
487+
output: to_c_str(out),
488+
error_message: std::ptr::null_mut(),
489+
},
490+
Err(e) => to_regorus_result(Err(e)),
491+
}
492+
}
493+
460494
/// Enable/disable rego v1.
461495
///
462496
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.set_rego_v0

src/engine.rs

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,31 @@ pub struct Engine {
2323
rego_v1: bool,
2424
}
2525

26+
#[derive(Debug, Clone, Serialize)]
27+
pub struct PolicyPackageNameDefinition {
28+
pub source_file: String,
29+
pub package_name: String,
30+
}
31+
32+
#[derive(Debug, Clone, Serialize)]
33+
pub struct PolicyParameter {
34+
pub name: String,
35+
pub modifiable: bool,
36+
pub required: bool,
37+
}
38+
39+
#[derive(Debug, Clone, Serialize)]
40+
pub struct PolicyModifier {
41+
pub name: String,
42+
}
43+
44+
#[derive(Debug, Clone, Serialize)]
45+
pub struct PolicyParameters {
46+
pub source_file: String,
47+
pub parameters: Vec<PolicyParameter>,
48+
pub modifiers: Vec<PolicyModifier>,
49+
}
50+
2651
/// Create a default engine.
2752
impl Default for Engine {
2853
fn default() -> Self {
@@ -907,6 +932,113 @@ impl Engine {
907932
serde_json::to_string_pretty(&ast).map_err(anyhow::Error::msg)
908933
}
909934

935+
/// Get the package names of each policy added to the engine.
936+
///
937+
///
938+
/// ```rust
939+
/// # use regorus::*;
940+
/// # use anyhow::{bail, Result};
941+
/// # fn main() -> Result<()> {
942+
/// # let mut engine = Engine::new();
943+
/// engine.add_policy("test.rego".to_string(), "package test\n x := 1".to_string())?;
944+
/// engine.add_policy("test2.rego".to_string(), "package test.multi.segment\n x := 1".to_string())?;
945+
///
946+
/// let package_names = engine.get_policy_package_names()?;
947+
///
948+
/// assert_eq!("test", package_names[0].package_name);
949+
/// assert_eq!("test.multi.segment", package_names[1].package_name);
950+
/// # Ok(())
951+
/// # }
952+
/// ```
953+
#[cfg(feature = "azure_policy")]
954+
#[cfg_attr(docsrs, doc(cfg(feature = "azure_policy")))]
955+
pub fn get_policy_package_names(&self) -> Result<Vec<PolicyPackageNameDefinition>> {
956+
let mut package_names = vec![];
957+
for m in &self.modules {
958+
let package_name = Interpreter::get_path_string(&m.package.refr, None)?;
959+
package_names.push(PolicyPackageNameDefinition {
960+
source_file: m.package.span.source.file().to_string(),
961+
package_name,
962+
});
963+
}
964+
965+
Ok(package_names)
966+
}
967+
968+
/// Get the parameters defined in each policy.
969+
///
970+
///
971+
/// ```rust
972+
/// # use regorus::*;
973+
/// # use anyhow::{bail, Result};
974+
/// # fn main() -> Result<()> {
975+
/// # let mut engine = Engine::new();
976+
/// engine.add_policy("test.rego".to_string(), "package test default parameters.a = 5 parameters.b = 10\n x := 1".to_string())?;
977+
///
978+
/// let parameters = engine.get_policy_parameters()?;
979+
///
980+
/// assert_eq!("a", parameters[0].parameters[0].name);
981+
/// assert_eq!("b", parameters[0].modifiers[0].name);
982+
///
983+
/// # Ok(())
984+
/// # }
985+
/// ```
986+
#[cfg(feature = "azure_policy")]
987+
#[cfg_attr(docsrs, doc(cfg(feature = "azure_policy")))]
988+
pub fn get_policy_parameters(&self) -> Result<Vec<PolicyParameters>> {
989+
let mut policy_parameter_definitions = vec![];
990+
for m in &self.modules {
991+
let mut parameters = vec![];
992+
let mut modifiers = vec![];
993+
994+
for rule in &m.policy {
995+
// Extract parameter definitions from the policy rule
996+
// e.g. default parameters.a = 5
997+
if let Rule::Default { refr, .. } = rule.as_ref() {
998+
let path = Parser::get_path_ref_components(refr)?;
999+
let paths: Vec<&str> = path.iter().map(|s| s.text()).collect();
1000+
1001+
if paths.len() == 2 && paths[0] == "parameters" {
1002+
// Todo: Fetch fields other than name from rego metadoc for the parameter
1003+
parameters.push(PolicyParameter {
1004+
name: paths[1].to_string(),
1005+
modifiable: false,
1006+
required: false,
1007+
})
1008+
}
1009+
}
1010+
1011+
// Extract modifiers to the parameters from the policy rule
1012+
// e.g. parameters.a = 5
1013+
if let Rule::Spec { head, .. } = rule.as_ref() {
1014+
match head {
1015+
RuleHead::Compr { refr, .. } => {
1016+
let path = Parser::get_path_ref_components(refr)?;
1017+
let paths: Vec<&str> = path.iter().map(|s| s.text()).collect();
1018+
1019+
if paths.len() == 2 && paths[0] == "parameters" {
1020+
// Todo: Fetch fields other than name from rego metadoc for the parameter
1021+
modifiers.push(PolicyModifier {
1022+
name: paths[1].to_string(),
1023+
})
1024+
}
1025+
}
1026+
RuleHead::Func { .. } => {}
1027+
RuleHead::Set { .. } => {}
1028+
}
1029+
}
1030+
}
1031+
1032+
policy_parameter_definitions.push(PolicyParameters {
1033+
source_file: m.package.span.source.file().to_string(),
1034+
parameters,
1035+
modifiers,
1036+
});
1037+
}
1038+
1039+
Ok(policy_parameter_definitions)
1040+
}
1041+
9101042
fn make_parser<'a>(&self, source: &'a Source) -> Result<Parser<'a>> {
9111043
let mut parser = Parser::new(source)?;
9121044
if self.rego_v1 {

0 commit comments

Comments
 (0)