pub struct Engine { /* private fields */ }
Expand description
The Rego evaluation engine.
Implementations§
source§impl Engine
impl Engine
sourcepub fn add_policy(&mut self, path: String, rego: String) -> Result<()>
pub fn add_policy(&mut self, path: String, rego: String) -> Result<()>
Add a policy.
The policy file will be parsed and converted to AST representation. Multiple policy files may be added to the engine.
path
: A filename to be associated with the policy.rego
: The rego policy code.
let mut engine = Engine::new();
engine.add_policy(
"test.rego".to_string(),
r#"
package test
allow = input.user == "root"
"#.to_string())?;
sourcepub fn add_policy_from_file<P: AsRef<Path>>(&mut self, path: P) -> Result<()>
pub fn add_policy_from_file<P: AsRef<Path>>(&mut self, path: P) -> Result<()>
Add a policy from a given file.
The policy file will be parsed and converted to AST representation. Multiple policy files may be added to the engine.
path
: Path to the policy file (.rego).
let mut engine = Engine::new();
engine.add_policy_from_file("tests/aci/framework.rego")?;
sourcepub fn set_input(&mut self, input: Value)
pub fn set_input(&mut self, input: Value)
Set the input document.
input
: Input documented. Typically this Value is constructed from JSON or YAML.
let mut engine = Engine::new();
let input = Value::from_json_str(r#"
{
"role" : "admin",
"action": "delete"
}"#)?;
engine.set_input(input);
pub fn set_input_json(&mut self, input_json: &str) -> Result<()>
sourcepub fn clear_data(&mut self)
pub fn clear_data(&mut self)
Clear the data document.
The data document will be reset to an empty object.
let mut engine = Engine::new();
engine.clear_data();
// Evaluate data.
let results = engine.eval_query("data".to_string(), false)?;
// Assert that it is empty object.
assert_eq!(results.result.len(), 1);
assert_eq!(results.result[0].expressions.len(), 1);
assert_eq!(results.result[0].expressions[0].value, Value::new_object());
sourcepub fn add_data(&mut self, data: Value) -> Result<()>
pub fn add_data(&mut self, data: Value) -> Result<()>
Add data document.
The specified data document is merged into existing data document.
let mut engine = Engine::new();
// Only objects can be added.
assert!(engine.add_data(Value::from_json_str("[]")?).is_err());
// Merge { "x" : 1, "y" : {} }
assert!(engine.add_data(Value::from_json_str(r#"{ "x" : 1, "y" : {}}"#)?).is_ok());
// Merge { "z" : 2 }
assert!(engine.add_data(Value::from_json_str(r#"{ "z" : 2 }"#)?).is_ok());
// Merge { "z" : 3 }. Conflict error.
assert!(engine.add_data(Value::from_json_str(r#"{ "z" : 3 }"#)?).is_err());
assert_eq!(
engine.eval_query("data".to_string(), false)?.result[0].expressions[0].value,
Value::from_json_str(r#"{ "x": 1, "y": {}, "z": 2}"#)?
);
pub fn add_data_json(&mut self, data_json: &str) -> Result<()>
sourcepub fn set_strict_builtin_errors(&mut self, b: bool)
pub fn set_strict_builtin_errors(&mut self, b: bool)
Set whether builtins should raise errors strictly or not.
Regorus differs from OPA in that by default builtins will raise errors instead of returning Undefined.
§NOTE: Currently not all builtins honor this flag and will always strictly raise errors.
sourcepub fn eval_rule(&mut self, path: String) -> Result<Value>
pub fn eval_rule(&mut self, path: String) -> Result<Value>
Evaluate rule(s) at given path.
Engine::eval_rule
is often faster than Engine::eval_query
and should be preferred if
OPA style QueryResults
are not needed.
let mut engine = Engine::new();
// Add policy
engine.add_policy(
"policy.rego".to_string(),
r#"
package example
import rego.v1
x = [1, 2]
y := 5 if input.a > 2
"#.to_string())?;
// Evaluate rule.
let v = engine.eval_rule("data.example.x".to_string())?;
assert_eq!(v, Value::from(vec![Value::from(1), Value::from(2)]));
// y evaluates to undefined.
let v = engine.eval_rule("data.example.y".to_string())?;
assert_eq!(v, Value::Undefined);
// Evaluating a non-existent rule is an error.
let r = engine.eval_rule("data.exaample.x".to_string());
assert!(r.is_err());
// Path must be valid rule paths.
assert!( engine.eval_rule("data".to_string()).is_err());
assert!( engine.eval_rule("data.example".to_string()).is_err());
sourcepub fn eval_query(
&mut self,
query: String,
enable_tracing: bool
) -> Result<QueryResults>
pub fn eval_query( &mut self, query: String, enable_tracing: bool ) -> Result<QueryResults>
Evaluate a Rego query.
let mut engine = Engine::new();
// Add policies
engine.add_policy_from_file("tests/aci/framework.rego")?;
engine.add_policy_from_file("tests/aci/api.rego")?;
engine.add_policy_from_file("tests/aci/policy.rego")?;
// Add data document (if any).
// If multiple data documents can be added, they will be merged together.
engine.add_data(Value::from_json_file("tests/aci/data.json")?)?;
// At this point the policies and data have been loaded.
// Either the same engine can be used to make multiple queries or the engine
// can be cloned to avoid having the reload the policies and data.
let _clone = engine.clone();
// Evaluate a query.
// Load input and make query.
engine.set_input(Value::new_object());
let results = engine.eval_query("data.framework.mount_overlay.allowed".to_string(), false)?;
assert_eq!(results.result[0].expressions[0].value, Value::from(false));
// Evaluate query with different inputs.
engine.set_input(Value::from_json_file("tests/aci/input.json")?);
let results = engine.eval_query("data.framework.mount_overlay.allowed".to_string(), false)?;
assert_eq!(results.result[0].expressions[0].value, Value::from(true));
sourcepub fn eval_bool_query(
&mut self,
query: String,
enable_tracing: bool
) -> Result<bool>
pub fn eval_bool_query( &mut self, query: String, enable_tracing: bool ) -> Result<bool>
Evaluate a Rego query that produces a boolean value.
This function should be preferred over Engine::eval_query
if just a true
/false
value is desired instead of QueryResults
.
let enable_tracing = false;
assert_eq!(engine.eval_bool_query("1 > 2".to_string(), enable_tracing)?, false);
assert_eq!(engine.eval_bool_query("1 < 2".to_string(), enable_tracing)?, true);
// Non boolean queries will raise an error.
assert!(engine.eval_bool_query("1+1".to_string(), enable_tracing).is_err());
// Queries producing multiple values will raise an error.
assert!(engine.eval_bool_query("true; true".to_string(), enable_tracing).is_err());
// Queries producing no values will raise an error.
assert!(engine.eval_bool_query("true; false; true".to_string(), enable_tracing).is_err());
sourcepub fn eval_allow_query(&mut self, query: String, enable_tracing: bool) -> bool
pub fn eval_allow_query(&mut self, query: String, enable_tracing: bool) -> bool
Evaluate an allow
query.
This is a wrapper over Engine::eval_bool_query
that returns true only if the
boolean query succeed and produced a true
value.
let enable_tracing = false;
assert_eq!(engine.eval_allow_query("1 > 2".to_string(), enable_tracing), false);
assert_eq!(engine.eval_allow_query("1 < 2".to_string(), enable_tracing), true);
assert_eq!(engine.eval_allow_query("1+1".to_string(), enable_tracing), false);
assert_eq!(engine.eval_allow_query("true; true".to_string(), enable_tracing), false);
assert_eq!(engine.eval_allow_query("true; false; true".to_string(), enable_tracing), false);
sourcepub fn eval_deny_query(&mut self, query: String, enable_tracing: bool) -> bool
pub fn eval_deny_query(&mut self, query: String, enable_tracing: bool) -> bool
Evaluate a deny
query.
This is a wrapper over Engine::eval_bool_query
that returns false only if the
boolean query succeed and produced a false
value.
let enable_tracing = false;
assert_eq!(engine.eval_deny_query("1 > 2".to_string(), enable_tracing), false);
assert_eq!(engine.eval_deny_query("1 < 2".to_string(), enable_tracing), true);
assert_eq!(engine.eval_deny_query("1+1".to_string(), enable_tracing), true);
assert_eq!(engine.eval_deny_query("true; true".to_string(), enable_tracing), true);
assert_eq!(engine.eval_deny_query("true; false; true".to_string(), enable_tracing), true);
sourcepub fn add_extension(
&mut self,
path: String,
nargs: u8,
extension: Box<dyn Extension>
) -> Result<()>
pub fn add_extension( &mut self, path: String, nargs: u8, extension: Box<dyn Extension> ) -> Result<()>
Add a custom builtin (extension).
path
: The fully qualified path of the builtin.nargs
: The number of arguments the builtin takes.extension
: TheExtension
instance.
let mut engine = Engine::new();
// Policy uses `do_magic` custom builtin.
engine.add_policy(
"test.rego".to_string(),
r#"package test
x = do_magic(1)
"#.to_string(),
)?;
// Evaluating fails since `do_magic` is not defined.
assert!(engine.eval_query("data.test.x".to_string(), false).is_err());
// Add extension to implement `do_magic`. The extension can be stateful.
let mut magic = 8;
engine.add_extension("do_magic".to_string(), 1 , Box::new(move | mut params: Vec<Value> | {
// params is mut and therefore individual values can be removed from it and modified.
// The number of parameters (1) has already been validated.
match ¶ms[0].as_i64() {
Ok(i) => {
// Compute value
let v = *i + magic;
// Update extension state.
magic += 1;
Ok(Value::from(v))
}
// Extensions can raise errors. Regorus will add location information to
// the error.
_ => bail!("do_magic expects i64 value")
}
}))?;
// Evaluation will now succeed.
let r = engine.eval_query("data.test.x".to_string(), false)?;
assert_eq!(r.result[0].expressions[0].value.as_i64()?, 9);
// Cloning the engine will also clone the extension.
let mut engine1 = engine.clone();
// Evaluating again will return a different value since the extension is stateful.
let r = engine.eval_query("data.test.x".to_string(), false)?;
assert_eq!(r.result[0].expressions[0].value.as_i64()?, 10);
// The second engine has a clone of the extension.
let r = engine1.eval_query("data.test.x".to_string(), false)?;
assert_eq!(r.result[0].expressions[0].value.as_i64()?, 10);
// Once added, the extension cannot be replaced or removed.
assert!(engine.add_extension("do_magic".to_string(), 1, Box::new(|_:Vec<Value>| {
Ok(Value::Undefined)
})).is_err());
// Extensions don't support out-parameter syntax.
engine.add_policy(
"policy.rego".to_string(),
r#"package invalid
x = y {
do_magic(2, y) # y is supplied as an out parameter.
}
"#.to_string()
)?;
// Evaluation fails since rule x calls an extension with out parameter.
assert!(engine.eval_query("data.invalid.x".to_string(), false).is_err());
sourcepub fn get_coverage_report(&self) -> Result<Report>
pub fn get_coverage_report(&self) -> Result<Report>
Get the coverage report.
let mut engine = Engine::new();
engine.add_policy(
"policy.rego".to_string(),
r#"
package test # Line 2
x = y { # Line 4
input.a > 2 # Line 5
y = 5 # Line 6
}
"#.to_string()
)?;
// Enable coverage.
engine.set_enable_coverage(true);
engine.eval_query("data".to_string(), false)?;
let report = engine.get_coverage_report()?;
assert_eq!(report.files[0].path, "policy.rego");
// Only line 5 is evaluated.
assert_eq!(report.files[0].covered.iter().cloned().collect::<Vec<u32>>(), vec![5]);
// Line 4 and 6 are not evaluated.
assert_eq!(report.files[0].not_covered.iter().cloned().collect::<Vec<u32>>(), vec![4, 6]);
sourcepub fn set_enable_coverage(&mut self, enable: bool)
pub fn set_enable_coverage(&mut self, enable: bool)
Enable/disable policy coverage.
If enable
is different from the current value, then any existing coverage
information will be cleared.
sourcepub fn clear_coverage_data(&mut self)
pub fn clear_coverage_data(&mut self)
Clear the gathered policy coverage data.
sourcepub fn set_gather_prints(&mut self, b: bool)
pub fn set_gather_prints(&mut self, b: bool)
Gather output from print statements instead of emiting to stderr.
See Engine::take_prints
.
sourcepub fn take_prints(&mut self) -> Result<Vec<String>>
pub fn take_prints(&mut self) -> Result<Vec<String>>
Take the gathered output of print statements.
let mut engine = Engine::new();
// Print to stderr.
engine.eval_query("print(\"Hello\")".to_string(), false)?;
// Configure gathering print statements.
engine.set_gather_prints(true);
// Execute query.
engine.eval_query("print(\"Hello\")".to_string(), false)?;
// Take and clear prints.
let prints = engine.take_prints()?;
assert_eq!(prints.len(), 1);
assert!(prints[0].contains("Hello"));
for p in prints {
println!("{p}");
}
Trait Implementations§
Auto Trait Implementations§
impl Freeze for Engine
impl !RefUnwindSafe for Engine
impl Send for Engine
impl Sync for Engine
impl Unpin for Engine
impl !UnwindSafe for Engine
Blanket Implementations§
source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
source§impl<T> IntoEither for T
impl<T> IntoEither for T
source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self
into a Left
variant of Either<Self, Self>
if into_left
is true
.
Converts self
into a Right
variant of Either<Self, Self>
otherwise. Read moresource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self
into a Left
variant of Either<Self, Self>
if into_left(&self)
returns true
.
Converts self
into a Right
variant of Either<Self, Self>
otherwise. Read more