Compare commits

...

16 Commits

Author SHA1 Message Date
Anand Krishnamoorthi
dff65f0329 chore: release (#298)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-08-16 08:45:52 -07:00
dependabot[bot]
6bf40c7394 Update cbindgen requirement from 0.26.0 to 0.27.0 (#296)
Updates the requirements on [cbindgen](https://github.com/mozilla/cbindgen) to permit the latest version.
- [Release notes](https://github.com/mozilla/cbindgen/releases)
- [Changelog](https://github.com/mozilla/cbindgen/blob/master/CHANGES)
- [Commits](https://github.com/mozilla/cbindgen/compare/0.26.0...v0.27.0)

---
updated-dependencies:
- dependency-name: cbindgen
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-12 09:09:53 -07:00
Anand Krishnamoorthi
a488a84969 fix: Match OPA behavior for split (#295)
In case of empty delimiter, Rust's split returns leading and trailing
empty strings whereas Golang's doesn't.
Change behavior to match Golang/OPA.

fixes #291

Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
2024-08-08 14:53:01 -07:00
dependabot[bot]
e4a58ad1dc Bump rexml in /bindings/ruby in the bundler group across 1 directory (#294)
Bumps the bundler group with 1 update in the /bindings/ruby directory: [rexml](https://github.com/ruby/rexml).


Updates `rexml` from 3.3.2 to 3.3.3
- [Release notes](https://github.com/ruby/rexml/releases)
- [Changelog](https://github.com/ruby/rexml/blob/master/NEWS.md)
- [Commits](https://github.com/ruby/rexml/compare/v3.3.2...v3.3.3)

---
updated-dependencies:
- dependency-name: rexml
  dependency-type: indirect
  dependency-group: bundler
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-07 13:08:09 -07:00
dependabot[bot]
430a453fde Update csbindgen requirement from =1.9.0 to =1.9.3 (#292)
Updates the requirements on [csbindgen](https://github.com/Cysharp/csbindgen) to permit the latest version.
- [Release notes](https://github.com/Cysharp/csbindgen/releases)
- [Commits](https://github.com/Cysharp/csbindgen/compare/1.9.0...1.9.3)

---
updated-dependencies:
- dependency-name: csbindgen
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-07 11:39:56 -07:00
Anand Krishnamoorthi
ef549a6528 fix: Merge data to init document (#293)
Init document is the aggregated data documen that the user has
specified using multiple `add_data` calls. Each query evaluation
starts of by initializing the current data to the init document.

Previously `add_data` was incorrectly added to the current document,
causing the added data to be lost if the addition happened after query
evaluation.

With this fix, scenarios where data addition may be interspersed with
query evaluation calls are supported.

Also provide a get_data method to obtain the (init) data document.

Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
2024-08-07 11:39:23 -07:00
Anand Krishnamoorthi
52afcbe5c5 chore: release (#289)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-07-28 13:19:09 +05:30
Anand Krishnamoorthi
f0576cef77 Update readme (#288)
Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
2024-07-28 13:04:47 +05:30
Anand Krishnamoorthi
20eece58ed Update binding versions (#287)
Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
2024-07-28 12:32:39 +05:30
Anand Krishnamoorthi
6599ce6001 feat: Update to opa v0.67.0 (#286)
Implement strings.count builtin

Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
2024-07-28 11:46:07 +05:30
Anand Krishnamoorthi
7095e269b7 fix: Handle aliases in scheduler (#285)
Earlier scheduler only recognized rules and would raise an
`unsafe var` error on alias.

Register alias var names to fix this.

fixes #284

Also fix clippy warning treated as error

Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
2024-07-27 23:26:53 +05:30
Stuart Neivandt
6e1f8cdb36 build.rs create hooks dir if not exists (#283) 2024-07-21 10:27:51 +05:30
Jie Yang
fb5151e0e4 add extension_list example (#281)
- Created an example of extension policy
- Added C# binding support of .NET framework 4.0 and created a Nuget
  spec for it.
- Added a pytest in python bindings to test the extension policy and the
  python binding
- Restructured the example and Csharp binding directories due to above
  changes.
- Added copyrights.
- Added a Windows workflow for .NET 4.0 build and test.
2024-07-16 11:08:37 +05:30
Anand Krishnamoorthi
37d283cb38 Fix build break (#278)
- Fix warning due to use of deprecated function.
  This was causing a build issue in the hava and csharp bindings
- Lock use of csbindgen@1.9.0
  The newer version 1.9.2  causes a "type of namespace C could not be fond" error
  In the generated code, struct inherits from C instead of uint

Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
2024-07-12 05:28:25 +05:30
dependabot[bot]
25dbd27d82 Update pyo3 requirement from 0.21.0 to 0.22.0 (#275)
Updates the requirements on [pyo3](https://github.com/pyo3/pyo3) to permit the latest version.
- [Release notes](https://github.com/pyo3/pyo3/releases)
- [Changelog](https://github.com/PyO3/pyo3/blob/main/CHANGELOG.md)
- [Commits](https://github.com/pyo3/pyo3/compare/v0.21.0...v0.22.0)

---
updated-dependencies:
- dependency-name: pyo3
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-01 09:51:50 +05:30
Anand Krishnamoorthi
292948a694 Update to OPA v0.66.0 (#274)
Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
2024-06-28 08:57:03 +05:30
41 changed files with 1024 additions and 56 deletions

View File

@@ -19,8 +19,8 @@ jobs:
- name: Build
run: dotnet build
working-directory: ./bindings/csharp
working-directory: ./bindings/csharp/net8.0
- name: Run
run: LD_LIBRARY_PATH=. dotnet run
working-directory: ./bindings/csharp
working-directory: ./bindings/csharp/net8.0

28
.github/workflows/test-csharp40.yml vendored Normal file
View File

@@ -0,0 +1,28 @@
name: bindings/csharp40
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
jobs:
test:
runs-on: windows-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-dotnet@v1
with:
dotnet-version: "5.0.x"
- name: Build
run: dotnet build
working-directory: ./bindings/csharp/net40
- name: Run
run: dotnet run
working-directory: ./bindings/csharp/net40

View File

@@ -32,6 +32,6 @@ jobs:
- name: Test jar
run: |
javac -cp target/regorus-java-0.1.5.jar Test.java
java -Djava.library.path=../../target/release -cp target/regorus-java-0.1.5.jar:. Test
javac -cp target/regorus-java-0.2.2.jar Test.java
java -Djava.library.path=../../target/release -cp target/regorus-java-0.2.2.jar:. Test
working-directory: ./bindings/java

2
.gitignore vendored
View File

@@ -1,6 +1,8 @@
# Generated by Cargo
# will have compiled files and executables
/target/
**/wheels/
**/__pycache__/
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html

View File

@@ -6,6 +6,34 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
## [0.2.3](https://github.com/microsoft/regorus/compare/regorus-v0.2.2...regorus-v0.2.3) - 2024-08-16
### Fixed
- Match OPA behavior for split ([#295](https://github.com/microsoft/regorus/pull/295))
- Merge data to init document ([#293](https://github.com/microsoft/regorus/pull/293))
### Other
- Update cbindgen requirement from 0.26.0 to 0.27.0 ([#296](https://github.com/microsoft/regorus/pull/296))
- Bump rexml in /bindings/ruby in the bundler group across 1 directory ([#294](https://github.com/microsoft/regorus/pull/294))
- Update csbindgen requirement from =1.9.0 to =1.9.3 ([#292](https://github.com/microsoft/regorus/pull/292))
## [0.2.2](https://github.com/microsoft/regorus/compare/regorus-v0.2.1...regorus-v0.2.2) - 2024-07-28
### Added
- Update to opa v0.67.0 ([#286](https://github.com/microsoft/regorus/pull/286))
### Fixed
- Handle aliases in scheduler ([#285](https://github.com/microsoft/regorus/pull/285))
### Other
- Update readme ([#288](https://github.com/microsoft/regorus/pull/288))
- Update binding versions ([#287](https://github.com/microsoft/regorus/pull/287))
- build.rs create hooks dir if not exists ([#283](https://github.com/microsoft/regorus/pull/283))
- add extension_list example ([#281](https://github.com/microsoft/regorus/pull/281))
- Fix build break ([#278](https://github.com/microsoft/regorus/pull/278))
- Update pyo3 requirement from 0.21.0 to 0.22.0 ([#275](https://github.com/microsoft/regorus/pull/275))
- Update to OPA v0.66.0 ([#274](https://github.com/microsoft/regorus/pull/274))
## [0.2.1](https://github.com/microsoft/regorus/compare/regorus-v0.2.0...regorus-v0.2.1) - 2024-06-19
### Added
@@ -354,3 +382,4 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- LICENSE committed
- CODE_OF_CONDUCT.md committed
- Initial commit

View File

@@ -12,7 +12,7 @@ members = [
[package]
name = "regorus"
description = "A fast, lightweight Rego (OPA policy language) interpreter"
version = "0.2.1"
version = "0.2.3"
edition = "2021"
license-file = "LICENSE"
repository = "https://github.com/microsoft/regorus"

View File

@@ -10,7 +10,7 @@ Regorus is also
- *cross-platform* - Written in platform-agnostic Rust.
- *no_std compatible* - Regorus can be used in `no_std` environments too. Most of the builtins are supported.
- *current* - We strive to keep Regorus up to date with latest OPA release. Regorus supports `import rego.v1`.
- *compliant* - Regorus is mostly compliant with the latest [OPA release v0.64.0](https://github.com/open-policy-agent/opa/releases/tag/v0.64.0). See [OPA Conformance](#opa-conformance) for details. Note that while we behaviorally produce the same results, we don't yet support all the builtins.
- *compliant* - Regorus is mostly compliant with the latest [OPA release v0.67.0](https://github.com/open-policy-agent/opa/releases/tag/v0.67.0). See [OPA Conformance](#opa-conformance) for details. Note that while we behaviorally produce the same results, we don't yet support all the builtins.
- *extensible* - Extend the Rego language by implementing custom stateful builtins in Rust.
See [add_extension](https://github.com/microsoft/regorus/blob/fc68bf9c8bea36427dae9401a7d1f6ada771f7ab/src/engine.rs#L352).
Support for extensibility using other languages coming soon.
@@ -99,7 +99,7 @@ $ cargo build -r --example regorus --no-default-features; strip target/release/e
-rwxr-xr-x 1 anand staff 1.9M May 11 22:04 target/release/examples/regorus*
```
Regorus passes the [OPA v0.64.0 test-suite](https://www.openpolicyagent.org/docs/latest/ir/#test-suite) barring a few
Regorus passes the [OPA v0.67.0 test-suite](https://www.openpolicyagent.org/docs/latest/ir/#test-suite) barring a few
builtins. See [OPA Conformance](#opa-conformance) below.
## Bindings
@@ -276,7 +276,7 @@ Benchmark 1: opa eval -b tests/aci -d tests/aci/data.json -i tests/aci/input.jso
```
## OPA Conformance
Regorus has been verified to be compliant with [OPA v0.64.0](https://github.com/open-policy-agent/opa/releases/tag/v0.64.0)
Regorus has been verified to be compliant with [OPA v0.67.0](https://github.com/open-policy-agent/opa/releases/tag/v0.67.0)
using a [test driver](https://github.com/microsoft/regorus/blob/main/tests/opa.rs) that loads and runs the OPA testsuite using Regorus, and verifies that expected outputs are produced.
The test driver can be invoked by running:

View File

@@ -0,0 +1,65 @@
//-----------------------------------------------------------------------
// <copyright file="Program.cs" company="Microsoft">
// Copyright (c)2012 Microsoft. All rights reserved.
// </copyright>
// <summary>
// Contains code to test the Regorus Policy Engine base class for C#
// and .NET4.0 bindings. It can be built and tested in Windows only.
// </summary>
//-----------------------------------------------------------------------
using System;
using System.Text;
using System.Diagnostics;
using Microsoft.WindowsAzure.Regorus.IaaS;
namespace regoregorus_test
{
class Program
{
static void Main(string[] args)
{
long nanosecPerTick = (1000L * 1000L * 1000L) / Stopwatch.Frequency;
var w = new Stopwatch();
w.Restart();
var engine = new RegorusPolicyEngine();
w.Stop();
var newEngineTicks = w.ElapsedTicks;
w.Restart();
// Load policies and data.
engine.AddPolicyFromFile("../../../examples/extension_list/agent_extension_policy.rego");
engine.AddDataFromJsonFile("../../../examples/extension_list/agent-extension-data-allow-only.json");
w.Stop();
var loadPoliciesTicks = w.ElapsedTicks;
w.Restart();
// Set input and eval query.
engine.SetInputFromJsonFile("../../../examples/extension_list/agent-extension-input.json");
var results = engine.EvalQuery("data.agent_extension_policy.extensions_to_download=x");
Console.WriteLine("Download query test: \n {0}", results);
results = engine.EvalQuery("data.agent_extension_policy.extensions_validated");
Console.WriteLine("Signing validation test: \n {0}", results);
engine.Dispose();
w.Stop();
var evalTicks = w.ElapsedTicks;
Console.WriteLine("Engine creation took {0} msecs", (newEngineTicks * nanosecPerTick) / (1000.0 * 1000.0));
Console.WriteLine("Load policies and data took {0} msecs", (loadPoliciesTicks * nanosecPerTick) / (1000.0 * 1000.0));
Console.WriteLine("EvalQuery and print results took {0} msecs", (evalTicks * nanosecPerTick) / (1000.0 * 1000.0));
}
}
}

View File

@@ -0,0 +1,4 @@
The Regorus C# binding library can be built via command "dotnet build". We can use the Regorus C# binding library built from this
directory to create a Nuget. This Nuget will contain the Regorus C# binding library with definitions that
work for .NET framework 4.0 (net40) and above. Note the Nuget can only be created after the binding library has been built.
RegorusCsharp-Lib-x64.nuspec is built for x64 architecture.

View File

@@ -0,0 +1,203 @@
//-----------------------------------------------------------------------
// <copyright file="Regorus.cs" company="Microsoft">
// Copyright (c)2012 Microsoft. All rights reserved.
// </copyright>
// <summary>
// Contains code for the Regorus Policy Engine base class for C# and
// .NET4.0 bindings. Currently this base class is not thread-safe. Make
// sure we use it in a signle-threaded environment or add additional
// protection when using it.
// </summary>
//-----------------------------------------------------------------------
using System;
using System.Text;
using System.IO;
using System.Threading;
namespace Microsoft.WindowsAzure.Regorus.IaaS
{
public class RegorusPolicyEngine : ICloneable, IDisposable
{
unsafe private RegorusFFI.RegorusEngine* E;
public RegorusPolicyEngine()
{
unsafe
{
E = RegorusFFI.API.regorus_engine_new();
}
}
public void Dispose()
{
unsafe
{
if (E != null)
{
RegorusFFI.API.regorus_engine_drop(E);
// to avoid Dispose() being called multiple times by mistake.
E = null;
}
}
}
public object Clone()
{
var clone = (RegorusPolicyEngine)this.MemberwiseClone();
unsafe
{
clone.E = RegorusFFI.API.regorus_engine_clone(E);
}
return clone;
}
public void AddPolicy(string path, string rego)
{
var pathBytes = Encoding.UTF8.GetBytes(path);
var regoBytes = Encoding.UTF8.GetBytes(rego);
unsafe
{
fixed (byte* pathPtr = pathBytes)
{
fixed(byte* regoPtr = regoBytes)
{
CheckAndDropResult(RegorusFFI.API.regorus_engine_add_policy(E, pathPtr, regoPtr));
}
}
}
}
public void AddPolicyFromFile(string path)
{
var pathBytes = Encoding.UTF8.GetBytes(path);
unsafe
{
fixed (byte* pathPtr = pathBytes)
{
CheckAndDropResult(RegorusFFI.API.regorus_engine_add_policy_from_file(E, pathPtr));
}
}
}
public void AddPolicyFromPath(string path)
{
if (!Directory.Exists(path))
{
return;
}
string[] regoFiles = Directory.GetFiles(path, "*.rego", SearchOption.AllDirectories);
foreach (string file in regoFiles)
{
AddPolicyFromFile(file);
}
}
public void AddDataJson(string data)
{
var dataBytes = Encoding.UTF8.GetBytes(data);
unsafe
{
fixed (byte* dataPtr = dataBytes)
{
CheckAndDropResult(RegorusFFI.API.regorus_engine_add_data_json(E, dataPtr));
}
}
}
public void AddDataFromJsonFile(string path)
{
var pathBytes = Encoding.UTF8.GetBytes(path);
unsafe
{
fixed (byte* pathPtr = pathBytes)
{
CheckAndDropResult(RegorusFFI.API.regorus_engine_add_data_from_json_file(E, pathPtr));
}
}
}
public void SetInputJson(string input)
{
var inputBytes = Encoding.UTF8.GetBytes(input);
unsafe
{
fixed (byte* inputPtr = inputBytes)
{
CheckAndDropResult(RegorusFFI.API.regorus_engine_set_input_json(E, inputPtr));
}
}
}
public void SetInputFromJsonFile(string path)
{
var pathBytes = Encoding.UTF8.GetBytes(path);
unsafe
{
fixed (byte* pathPtr = pathBytes)
{
CheckAndDropResult(RegorusFFI.API.regorus_engine_set_input_from_json_file(E, pathPtr));
}
}
}
public string EvalQuery(string query)
{
var queryBytes = Encoding.UTF8.GetBytes(query);
var resultJson = "";
unsafe
{
fixed (byte* queryPtr = queryBytes)
{
var result = RegorusFFI.API.regorus_engine_eval_query(E, queryPtr);
if (result.status == RegorusFFI.RegorusStatus.RegorusStatusOk) {
if (result.output != null) {
resultJson = System.Runtime.InteropServices.Marshal.PtrToStringAnsi((IntPtr)result.output);
}
RegorusFFI.API.regorus_result_drop(result);
} else {
CheckAndDropResult(result);
}
}
}
if (resultJson != null) {
return resultJson;
} else {
return "";
}
}
void CheckAndDropResult(RegorusFFI.RegorusResult result)
{
if (result.status != RegorusFFI.RegorusStatus.RegorusStatusOk) {
unsafe {
var message = System.Runtime.InteropServices.Marshal.PtrToStringAnsi((IntPtr)result.error_message);
var ex = new Exception(message);
RegorusFFI.API.regorus_result_drop(result);
throw ex;
}
}
RegorusFFI.API.regorus_result_drop(result);
}
}
}

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8" ?>
<package>
<metadata>
<id>RegorusCsharp-Lib-x64</id>
<version>0.2.1</version>
<title>RegorusCsharp-Lib-x64</title>
<authors>yangjie@microsoft.com</authors>
<owners>yangjie@microsoft.com</owners>
<projectUrl>https://www.microsoft.com</projectUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>Regorus C# library for x64</description>
<releaseNotes>remove Regorus.cs from Nuget</releaseNotes>
<copyright>Copyright (C) Microsoft Corp</copyright>
<summary></summary>
</metadata>
<files>
<file src="RegorusFFI.g.cs" target="RegorusFFI.g.cs"/>
<file src="regorus_ffi.dll" target="lib\regorusc.dll" />
<file src="README" target="README" />
<file src="..\..\..\LICENSE" target="LICENSE" />
</files>
</package>

View File

@@ -0,0 +1,24 @@
<Project Sdk="Microsoft.NET.Sdk" InitialTargets="BuildRegorusFFI">
<Target Name="BuildRegorusFFI">
<Exec Command="cargo build -r --manifest-path ../../ffi/Cargo.toml" />
<Copy SourceFiles="../../ffi/RegorusFFI.g.cs" DestinationFolder="." />
<ItemGroup>
<RegorusDylib Include="..\..\..\target\release\*regorus_ffi*" />
</ItemGroup>
<Copy SourceFiles="@(RegorusDylib)" DestinationFolder="." />
</Target>
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net40</TargetFramework>
<RootNamespace>regorus_test</RootNamespace>
<StartupObject>regoregorus_test.Program</StartupObject>
<ImplicitUsings>enable</ImplicitUsings>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
</Project>

View File

@@ -1,4 +1,14 @@
using System.Diagnostics;
//-----------------------------------------------------------------------
// <copyright file="Program.cs" company="Microsoft">
// Copyright (c)2012 Microsoft. All rights reserved.
// </copyright>
// <summary>
// Contains code to test the Regorus class for C#
// and .NET 8.0 bindings.
// </summary>
//-----------------------------------------------------------------------
using System.Diagnostics;
long nanosecPerTick = (1000L*1000L*1000L) / Stopwatch.Frequency;
var w = new Stopwatch();
@@ -21,10 +31,10 @@ var newEngineTicks = w.ElapsedTicks;
w.Restart();
// Load policies and data.
engine.AddPolicyFromFile("../../tests/aci/framework.rego");
engine.AddPolicyFromFile("../../tests/aci/api.rego");
engine.AddPolicyFromFile("../../tests/aci/policy.rego");
engine.AddDataFromJsonFile("../../tests/aci/data.json");
engine.AddPolicyFromFile("../../../tests/aci/framework.rego");
engine.AddPolicyFromFile("../../../tests/aci/api.rego");
engine.AddPolicyFromFile("../../../tests/aci/policy.rego");
engine.AddDataFromJsonFile("../../../tests/aci/data.json");
w.Stop();
@@ -34,7 +44,7 @@ var loadPoliciesTicks = w.ElapsedTicks;
w.Restart();
// Set input and eval rule.
engine.SetInputFromJsonFile("../../tests/aci/input.json");
engine.SetInputFromJsonFile("../../../tests/aci/input.json");
var value = engine.EvalQuery("data.framework.mount_overlay");
var valueDoc = System.Text.Json.JsonDocument.Parse(value);

View File

@@ -1,10 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk" InitialTargets="BuildRegorusFFI">
<Target Name="BuildRegorusFFI">
<Exec Command="cargo build -r --manifest-path ../ffi/Cargo.toml" />
<Copy SourceFiles="../ffi/RegorusFFI.g.cs" DestinationFolder="." />
<Exec Command="cargo build -r --manifest-path ../../ffi/Cargo.toml" />
<Copy SourceFiles="../../ffi/RegorusFFI.g.cs" DestinationFolder="." />
<ItemGroup>
<RegorusDylib Include="..\..\target\release\*regorus_ffi*" />
<RegorusDylib Include="..\..\..\target\release\*regorus_ffi*" />
</ItemGroup>
<Copy SourceFiles="@(RegorusDylib)" DestinationFolder="." />
</Target>

View File

@@ -1,6 +1,6 @@
[package]
name = "regorus-ffi"
version = "0.2.1"
version = "0.2.2"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -20,5 +20,5 @@ coverage = ["regorus/coverage"]
custom_allocator = []
[build-dependencies]
cbindgen = "0.26.0"
csbindgen = "1.9.0"
cbindgen = "0.27.0"
csbindgen = "=1.9.3"

View File

@@ -1,6 +1,6 @@
[package]
name = "regorus-java"
version = "0.2.1"
version = "0.2.2"
edition = "2021"
repository = "https://github.com/microsoft/regorus/bindings/java"
description = "Java bindings for Regorus - a fast, lightweight Rego interpreter written in Rust"

View File

@@ -9,7 +9,7 @@
<groupId>com.microsoft.regorus</groupId>
<artifactId>regorus-java</artifactId>
<version>0.1.5</version>
<version>0.2.2</version>
<name>Regorus Java</name>
<description>Java bindings for Regorus - a fast, lightweight Rego interpreter written in Rust</description>

View File

@@ -1,6 +1,6 @@
[package]
name = "regoruspy"
version = "0.2.1"
version = "0.2.2"
edition = "2021"
repository = "https://github.com/microsoft/regorus/bindings/python"
description = "Python bindings for Regorus - a fast, lightweight Rego interpreter written in Rust"
@@ -19,7 +19,7 @@ coverage = ["regorus/coverage"]
[dependencies]
anyhow = "1.0"
ordered-float = "4.2.0"
pyo3 = {version = "0.21.0", features = ["anyhow", "extension-module"] }
pyo3 = {version = "0.22.0", features = ["anyhow", "extension-module"] }
regorus = { path = "../..", default-features = false, features = ["arc"] }
serde_json = "1.0.112"

View File

@@ -0,0 +1,214 @@
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
import json
import pytest
import regorus
TEST_EXT_NAME = "Microsoft.Azure.ActiveDirectory.AADSSHLoginForLinux"
@pytest.fixture(name="engine", scope="function")
def engine_fixture():
"""
Fixture to handle creation and cleanup of a default policy engine.
New engine is created for each test case.
"""
engine = regorus.Engine()
engine.add_policy_from_file('../../examples/extension_list/agent_extension_policy.rego')
yield engine
@pytest.fixture(name="input_data")
def input_data_fixture():
"""
Fixture to handle creation and cleanup of a default input data.
New input data is created for each test case.
"""
input_data = {
"extensions": {
TEST_EXT_NAME: {
"signingInfo": {
"extensionSigned": False
}
}
}
}
input_json = json.dumps(input_data)
yield input_json
@pytest.fixture(name="default_data")
def default_data_fixture():
"""Fixture for default data"""
data_json = {
"azureGuestAgentPolicy": {
"policyVersion": "0.1.0",
"signingRules": {
"extensionSigned": False
},
"allowListOnly": False
}
}
data_json = json.dumps(data_json)
yield data_json
def test_default_data_json(engine, input_data):
"""Test the default data in json format for extension policy."""
data_json = {
"azureGuestAgentPolicy": {
"policyVersion": "0.1.0",
"signingRules": {
"extensionSigned": False
},
"allowListOnly": False
}
}
data_json = json.dumps(data_json)
engine.add_data_json(data_json)
engine.set_input_json(input_data)
# Eval query
results = engine.eval_query('data.agent_extension_policy')
assert results['result'][0]['expressions'][0]['value']['extensions_to_download'][TEST_EXT_NAME]['downloadAllowed']
def test_default_data_file(engine, input_data):
"""Test the default data in file format for extension policy."""
data_default_path = "../../examples/extension_list/agent-extension-default-data.json"
engine.add_data_from_json_file(data_default_path)
engine.set_input_json(input_data)
# Eval query
results = engine.eval_query('data.agent_extension_policy')
assert results['result'][0]['expressions'][0]['value']['extensions_to_download'][TEST_EXT_NAME]['downloadAllowed']
def test_allow_all(engine, input_data):
"""Test the policy engine with allow all policy."""
data_json = {
"azureGuestAgentPolicy": {
"policyVersion": "0.1.0",
"signingRules": {
"extensionSigned": False
},
"allowListOnly": False
}
}
data_json = json.dumps(data_json)
engine.add_data_json(data_json)
engine.set_input_json(input_data)
# Eval query
results = engine.eval_query('data.agent_extension_policy')
assert results['result'][0]['expressions'][0]['value']['extensions_to_download'][TEST_EXT_NAME]['downloadAllowed']
def test_name_only_input(engine, default_data):
"""Test input with only the extension name."""
input_data = {
"extensions": {
TEST_EXT_NAME: {
}
}
}
input_json = json.dumps(input_data)
engine.add_data_json(default_data)
engine.set_input_json(input_json)
# Eval query
results = engine.eval_query('data.agent_extension_policy')
assert results['result'][0]['expressions'][0]['value']['extensions_to_download'][TEST_EXT_NAME]['downloadAllowed']
@pytest.mark.parametrize("input_signed, extension_signed", [
(True, True),
(True, False),
(False, True),
(False, False)
])
def test_extension_signed_rule(engine, input_signed, extension_signed):
"""
Test extension signing rule. Engine should be able to handle
both signed and unsigned extensions, with extensionSigned rule set
to either true or false.
"""
data_json = {
"azureGuestAgentPolicy": {
"policyVersion": "0.1.0",
"signingRules": {
"extensionSigned": extension_signed
},
"allowListOnly": False
}
}
input_data = {
"extensions": {
TEST_EXT_NAME: {
"signingInfo": {
"extensionSigned": input_signed
}
}
}
}
data_json = json.dumps(data_json)
input_data = json.dumps(input_data)
engine.add_data_json(data_json)
engine.set_input_json(input_data)
# Eval query
results = engine.eval_query('data.agent_extension_policy')
# assert results
if extension_signed:
assert results['result'][0]['expressions'][0]['value']['extensions_validated'][TEST_EXT_NAME]['signingValidated'] == input_signed
else:
assert results['result'][0]['expressions'][0]['value']['extensions_validated'][TEST_EXT_NAME]['signingValidated']
assert results['result'][0]['expressions'][0]['value']['extensions_to_download'][TEST_EXT_NAME]['downloadAllowed']
@pytest.mark.parametrize("ext_allowed, allow_rule", [
(True, True),
(True, False),
(False, True),
(False, False)
])
def test_allowlist_rule(engine, ext_allowed, allow_rule):
"""
Test allowListOnly rule. Engine should be able to handle
both allowed and disallowed extensions, with allowListOnly rule
set to either true or false.
"""
if ext_allowed:
ext_name = TEST_EXT_NAME
else:
ext_name = "random_disallowed_extension"
input_json = {
"extensions": {
ext_name: {
"signingInfo": {
"extensionSigned": False
}
}
}
}
data_json = {
"azureGuestAgentPolicy": {
"signingRules": {
"extensionSigned": False
},
"allowListOnly": allow_rule
},
"azureGuestExtensionsPolicy": {
"Microsoft.CPlat.Core.RunCommandLinux": {
},
TEST_EXT_NAME: {
}
}
}
input_json = json.dumps(input_json)
data_json = json.dumps(data_json)
engine.add_data_json(data_json)
engine.set_input_json(input_json)
# Eval query
results = engine.eval_query('data.agent_extension_policy')
if allow_rule:
assert results['result'][0]['expressions'][0]['value']['extensions_to_download'][ext_name]['downloadAllowed'] == ext_allowed
else:
assert results['result'][0]['expressions'][0]['value']['extensions_to_download'][ext_name]['downloadAllowed']

View File

@@ -10,9 +10,9 @@ GEM
ast (2.4.2)
json (2.7.2)
language_server-protocol (3.17.0.3)
minitest (5.24.0)
minitest (5.24.1)
parallel (1.25.1)
parser (3.3.3.0)
parser (3.3.4.0)
ast (~> 2.4.1)
racc
racc (1.8.0)
@@ -21,24 +21,24 @@ GEM
rake-compiler (1.2.7)
rake
rake-compiler-dock (1.5.1)
rb_sys (0.9.97)
rb_sys (0.9.99)
regexp_parser (2.9.2)
rexml (3.3.0)
rexml (3.3.3)
strscan
rubocop (1.64.1)
rubocop (1.65.0)
json (~> 2.3)
language_server-protocol (>= 3.17.0)
parallel (~> 1.10)
parser (>= 3.3.0.2)
rainbow (>= 2.2.2, < 4.0)
regexp_parser (>= 1.8, < 3.0)
regexp_parser (>= 2.4, < 3.0)
rexml (>= 3.2.5, < 4.0)
rubocop-ast (>= 1.31.1, < 2.0)
ruby-progressbar (~> 1.7)
unicode-display_width (>= 2.4.0, < 3.0)
rubocop-ast (1.31.3)
parser (>= 3.3.1.0)
rubocop-minitest (0.35.0)
rubocop-minitest (0.35.1)
rubocop (>= 1.61, < 2.0)
rubocop-ast (>= 1.31.1, < 2.0)
rubocop-rake (0.6.0)

View File

@@ -1,6 +1,6 @@
[package]
name = "regorusrb"
version = "0.12.0"
version = "0.2.2"
edition = "2021"
description = "Ruby bindings for Regorus - a fast, lightweight Rego interpreter written in Rust"
publish = false

View File

@@ -1,6 +1,6 @@
[package]
name = "regorusjs"
version = "0.2.1"
version = "0.2.2"
edition = "2021"
repository = "https://github.com/microsoft/regorus/bindings/wasm"
description = "WASM bindings for Regorus - a fast, lightweight Rego interpreter written in Rust"

View File

@@ -8,6 +8,9 @@ fn main() -> Result<()> {
// Copy hooks to appropriate location so that git will run them.
// In git worktrees, .git is a symlink and the following commands fail.
if Path::new(".git").is_dir() {
if !Path::new("./.git/hooks").exists() {
std::fs::create_dir_all("./.git/hooks")?;
}
std::fs::copy("./scripts/pre-commit", "./.git/hooks/pre-commit")?;
std::fs::copy("./scripts/pre-push", "./.git/hooks/pre-push")?;
}

View File

@@ -0,0 +1,19 @@
{
"azureGuestAgentPolicy": {
"signingRules": {
"extensionSigned": true
},
"allowListOnly": true
},
"azureGuestExtensionsPolicy": {
"test3": {
"runtimeRules": {}
},
"test2": {
"signingRules": {
"extensionSigned": false
},
"runtimeRules": {}
}
}
}

View File

@@ -0,0 +1,9 @@
{
"azureGuestAgentPolicy": {
"policyVersion": "0.1.0",
"signingRules": {
"extensionSigned": false
},
"allowListOnly": false
}
}

View File

@@ -0,0 +1,28 @@
{
"extensions": {
"Microsoft.Azure.ActiveDirectory.AADSSHLoginForLinux": {
"signingInfo": {
"extensionSigned": false
}
},
"test2": {
"signingInfo": {
"extensionSigned": true
}
},
"test3": {
"signingInfo": {
"extensionSigned": false
}
},
"test1": {
"signingInfo": {
"extensionSigned": false
}
},
"test4": {}
}
}

View File

@@ -0,0 +1,125 @@
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
package agent_extension_policy
import rego.v1
policy_version := "0.1.0"
default default_global_rules := {
"allowListOnly": false,
"signingRules": {
"extensionSigned": false,
"signingDetails": {},
},
"updateAllowed": true,
"uninstallAllowed": true,
}
default global_rules := {
"allowListOnly": false,
"signingRules": {
"extensionSigned": false,
"signingDetails": {},
},
"updateAllowed": true,
"uninstallAllowed": true,
}
global_rules := object.union(default_global_rules, data.azureGuestAgentPolicy) if {
data.azureGuestAgentPolicy
}
default any_extension_allowed := true
any_extension_allowed := false if {
global_rules.allowListOnly
}
default default_signing_info := {"signingInfo": {}}
# Download rule 1: if the extension is in the list and download rule satisfied: download allowed
extensions_to_download[name] := extension if {
some name, input_extension in input.extensions
data.azureGuestExtensionsPolicy[name]
download_rule_validated(input_extension, data.azureGuestExtensionsPolicy[name])
extension := object.union(input_extension, {"downloadAllowed": true})
}
# Download rule 2: if the extension is in the list and download rule not satisfied: download denied
extensions_to_download[name] := extension if {
some name, input_extension in input.extensions
data.azureGuestExtensionsPolicy[name]
not download_rule_validated(input_extension, data.azureGuestExtensionsPolicy[name])
extension := object.union(input_extension, {"downloadAllowed": false})
}
# Download rule 3: if the extension is not in the list: depending on allowListOnly on or off
extensions_to_download[name] := extension if {
some name, input_extension in input.extensions
not data.azureGuestExtensionsPolicy[name]
extension := object.union(input_extension, {"downloadAllowed": any_extension_allowed})
}
# Validate rule 1: if individual signing rule exists, signing rule validated according to the rules
extensions_validated[name] := extension if {
some name, input_extension in input.extensions
data.azureGuestExtensionsPolicy[name]
extension_global_rules := object.union(global_rules, data.azureGuestExtensionsPolicy[name])
extension_signing_info := object.union(extension_global_rules, default_signing_info)
output := object.union(input_extension, extension_signing_info)
signing_validated(output.signingInfo, output.signingRules)
extension := object.union(output, {"signingValidated": true})
}
# Validate rule 2: if indivual signing rule exists, signing rule not validated according to the rules
extensions_validated[name] := extension if {
some name, input_extension in input.extensions
data.azureGuestExtensionsPolicy[name]
extension_global_rules := object.union(global_rules, data.azureGuestExtensionsPolicy[name])
extension_signing_info := object.union(extension_global_rules, default_signing_info)
output := object.union(input_extension, extension_signing_info)
not signing_validated(output.signingInfo, output.signingRules)
extension := object.union(output, {"signingValidated": false})
}
# Validate rule 3: if individual signing rule doesn't exist, signing rule validated according to global signing rule
extensions_validated[name] := extension if {
some name, input_extension in input.extensions
not data.azureGuestExtensionsPolicy[name]
extension_global_rules := object.union(input_extension, global_rules)
output := object.union(extension_global_rules, default_signing_info)
signing_validated(output.signingInfo, output.signingRules)
extension := object.union(output, {"signingValidated": true})
}
# Validate rule 4: if individual signing rule doesn't exist, signing rule not validated according to the global rules
extensions_validated[name] := extension if {
some name, input_extension in input.extensions
not data.azureGuestExtensionsPolicy[name]
extension_global_rules := object.union(input_extension, global_rules)
output := object.union(extension_global_rules, default_signing_info)
not signing_validated(output.signingInfo, output.signingRules)
extension := object.union(output, {"signingValidated": false})
}
# Currently if download rules doesn't exist, allow the extension because its name is in the list.
# In the future additional rules can be checked with downloadRules present.
download_rule_validated(_, rules) if {
not rules.downloadRules
}
# Signing is validated if input comes with extension signed, or the input of signing information is matching the
# rules in data.
signing_validated(signingInfo, signingRules) if {
signingInfo
signingRules
signingInfo.extensionSigned
} else if {
signingInfo
signingRules
signingInfo.extensionSigned == signingRules.extensionSigned
}

View File

@@ -28,6 +28,7 @@ pub fn register(m: &mut builtins::BuiltinsMap<&'static str, builtins::BuiltinFcn
m.insert("startswith", (startswith, 2));
m.insert("strings.any_prefix_match", (any_prefix_match, 2));
m.insert("strings.any_suffix_match", (any_suffix_match, 2));
m.insert("strings.count", (strings_count, 2));
m.insert("strings.replace_n", (replace_n, 2));
m.insert("strings.reverse", (reverse, 1));
m.insert("substring", (substring, 3));
@@ -145,11 +146,18 @@ fn split(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Re
let s = ensure_string(name, &params[0], &args[0])?;
let delimiter = ensure_string(name, &params[1], &args[1])?;
Ok(Value::from_array(
// Handle https://github.com/microsoft/regorus/issues/291
let parts: Vec<Value> = if delimiter.as_ref() == "" {
// If delimiter is "", str::split returns a leading and trailing "" whereas Golang's split doesn't.
// Therefore avoid str::split and instead return each char as a Value::String.
s.chars().map(|c| Value::from(c.to_string())).collect()
} else {
s.split(delimiter.as_ref())
.map(|s| Value::String(s.into()))
.collect(),
))
.collect()
};
Ok(Value::from(parts))
}
fn to_string(v: &Value, unescape: bool) -> String {
@@ -512,6 +520,27 @@ fn any_suffix_match(
))
}
fn strings_count(
span: &Span,
params: &[Ref<Expr>],
args: &[Value],
_strict: bool,
) -> Result<Value> {
let name = "strings.count";
ensure_args_count(span, name, params, args, 2)?;
let search = ensure_string(name, &params[0], &args[0])?;
let substring = ensure_string(name, &params[0], &args[1])?;
Ok(Value::from(
search
.as_bytes()
.windows(substring.len())
.filter(|&w| w == substring.as_bytes())
.count(),
))
}
fn startswith(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
let name = "startswith";
ensure_args_count(span, name, params, args, 2)?;

View File

@@ -121,6 +121,7 @@ fn timestamp(uuid: &Uuid) -> Option<Timestamp> {
// https://github.com/uuid-rs/uuid/blob/94ecea893fadac93248f1bd6f47673c09cec5912/src/lib.rs#L900-L904
if uuid.get_version_num() == 2 {
let (ticks, counter) = decode_rfc4122_timestamp(uuid);
#[allow(deprecated)]
return Some(Timestamp::from_rfc4122(ticks, counter));
}

View File

@@ -239,7 +239,7 @@ impl Engine {
/// # }
/// ```
pub fn clear_data(&mut self) {
self.interpreter.set_data(Value::new_object());
self.interpreter.set_init_data(Value::new_object());
self.prepared = false;
}
@@ -276,7 +276,40 @@ impl Engine {
bail!("data must be object");
}
self.prepared = false;
self.interpreter.get_data_mut().merge(data)
self.interpreter.get_init_data_mut().merge(data)
}
/// Get the data document.
///
/// The returned value is the data document that has been constructed using
/// one or more calls to [`Engine::add_data`]. The values of policy rules are
/// not included in the returned document.
///
///
/// ```
/// # use regorus::*;
/// # fn main() -> anyhow::Result<()> {
/// let mut engine = Engine::new();
///
/// // If not set, data document is empty.
/// assert_eq!(engine.get_data(), Value::new_object());
///
/// // 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());
///
/// let data = engine.get_data();
/// assert_eq!(data["x"], Value::from(1));
/// assert_eq!(data["y"], Value::new_object());
/// assert_eq!(data["z"], Value::from(2));
///
/// # Ok(())
/// # }
/// ```
pub fn get_data(&self) -> Value {
self.interpreter.get_init_data().clone()
}
pub fn add_data_json(&mut self, data_json: &str) -> Result<()> {
@@ -537,11 +570,7 @@ impl Engine {
self.interpreter.set_modules(&self.modules);
self.interpreter.clear_builtins_cache();
// when the interpreter is prepared the initial data is saved
// the data will be reset to init_data each time clean_internal_evaluation_state is called
let init_data = self.interpreter.get_data_mut().clone();
self.interpreter.set_init_data(init_data);
// clean_internal_evaluation_state will set data to an efficient clont of use supplied init_data
// Initialize the with-document with initial data values.
// with-modifiers will be applied to this document.
self.interpreter.init_with_document()?;

View File

@@ -216,18 +216,22 @@ impl Interpreter {
self.modules = modules.to_vec();
}
pub fn set_init_data(&mut self, init_data: Value) {
self.init_data = init_data;
}
pub fn set_data(&mut self, data: Value) {
self.data = data;
}
pub fn get_data_mut(&mut self) -> &mut Value {
&mut self.data
}
pub fn set_init_data(&mut self, data: Value) {
self.init_data = data;
}
pub fn get_init_data(&self) -> &Value {
&self.init_data
}
pub fn get_init_data_mut(&mut self) -> &mut Value {
&mut self.init_data
}
pub fn set_traces(&mut self, enable_tracing: bool) {
self.traces = match enable_tracing {
true => Some(vec![]),

View File

@@ -1,6 +1,8 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
#![allow(unknown_lints)]
#![allow(clippy::doc_lazy_continuation)]
// Use README.md as crate documentation.
#![doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/README.md"))]
// We'll default to building for no_std - use core, alloc instead of std.

View File

@@ -386,6 +386,7 @@ pub struct Analyzer {
}
#[derive(Debug, Clone)]
#[allow(dead_code)]
pub struct Schedule {
#[allow(unused)]
pub scopes: BTreeMap<Ref<Query>, Scope>,
@@ -411,7 +412,7 @@ impl Analyzer {
}
pub fn analyze(mut self, modules: &[Ref<Module>]) -> Result<Schedule> {
self.add_rules(modules)?;
self.add_rules_and_aliases(modules)?;
self.functions = gather_functions(modules)?;
for m in modules {
@@ -429,7 +430,7 @@ impl Analyzer {
modules: &[Ref<Module>],
query: &Ref<Query>,
) -> Result<Schedule> {
self.add_rules(modules)?;
self.add_rules_and_aliases(modules)?;
self.analyze_query(None, None, query, Scope::default())?;
Ok(Schedule {
@@ -438,7 +439,7 @@ impl Analyzer {
})
}
fn add_rules(&mut self, modules: &[Ref<Module>]) -> Result<()> {
fn add_rules_and_aliases(&mut self, modules: &[Ref<Module>]) -> Result<()> {
for m in modules {
let path = get_path_string(&m.package.refr, Some("data"))?;
let scope: &mut Scope = self.packages.entry(path).or_default();
@@ -455,6 +456,12 @@ impl Analyzer {
};
scope.unscoped.insert(var);
}
for import in &m.imports {
if let Some(var) = &import.r#as {
scope.unscoped.insert(var.source_str());
}
}
}
Ok(())

View File

@@ -422,3 +422,32 @@ fn one_yaml() -> Result<()> {
fn run(path: &str) {
yaml_test(path).unwrap()
}
#[test]
fn test_get_data() -> Result<()> {
let mut engine = Engine::new();
// Merge { "x" : 1, "y" : {} }
engine.add_data(Value::from_json_str(r#"{ "x" : 1, "y" : {}}"#)?)?;
// Merge { "z" : 2 }
engine.add_data(Value::from_json_str(r#"{ "z" : 2 }"#)?)?;
// Add a policy
engine.add_policy("policy.rego".to_string(), "package a".to_string())?;
// Evaluate virtual data document. The virtual document includes all rules as well.
let v_data = engine.eval_query("data".to_string(), false)?.result[0].expressions[0]
.value
.clone();
// There must be an empty package.
assert_eq!(v_data["a"], Value::new_object());
// Get the data document.
let data = engine.get_data();
// There must NOT be any value of `a`.
assert_eq!(data["a"], Value::Undefined);
Ok(())
}

View File

@@ -0,0 +1,17 @@
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
cases:
- note: empty separator
data: {}
modules: []
query: "x := split(\"test\", \"\")"
want_result:
x: ["t", "e", "s", "t"]
- note: empty separator, empty string
data: {}
modules: []
query: "x := split(\"\", \"\")"
want_result:
x: []

View File

@@ -34,6 +34,7 @@ cases:
- |
package b
import rego.v1
# Both the following imports are overridden by rules
#import data.a.b as a
#import data.a.b
@@ -43,6 +44,10 @@ cases:
a = 10
c = C + b
r if {
some v in [C]
}
query: data
want_result:
a:
@@ -50,6 +55,7 @@ cases:
b:
a: 10
c: 22
r: true
- note: import overridden by rule
modules:

View File

@@ -172,6 +172,64 @@ struct Cli {
generate: bool,
}
fn stateful_policy_test() -> Result<()> {
// Create an engine for evaluating Rego policies.
let mut engine = regorus::Engine::new();
let policy = String::from(
r#"
package example
import rego.v1
default allow := false
allow if {
print("data.allowed_actions = ", data.allowed_actions)
input.action in data.allowed_actions["user1"]
print("This rule should be allowed")
}
"#,
);
// Add policy to the engine.
engine.add_policy(String::from("policy.rego"), policy)?;
// Evaluate first input. Expect to evaluate to false, since state is not set
engine.set_input(regorus::Value::from_json_str(
r#"{
"action": "write"
}"#,
)?);
let r = engine.eval_bool_query(String::from("data.example.allow"), false)?;
println!("Received result: {:?}", r);
assert_eq!(r, false);
// Add data to engine. Set state
engine.add_data(regorus::Value::from_json_str(
r#"{
"allowed_actions": {
"user1" : ["read", "write"]
}}"#,
)?)?;
// Evaluate second input. Expect to evaluate to true, since state has been set now
engine.set_input(regorus::Value::from_json_str(
r#"{
"action": "write"
}"#,
)?);
let r = engine.eval_bool_query(String::from("data.example.allow"), false)?;
println!("Received result: {:?}", r);
assert_eq!(
r, true,
"expect result to be true since rule evaluates to true after state has been updated, per rego logs"
);
Ok(())
}
fn main() -> Result<()> {
let cli = Cli::parse();
run_kata_tests(
@@ -179,5 +237,6 @@ fn main() -> Result<()> {
&cli.name,
cli.coverage,
cli.generate,
)
)?;
stateful_policy_test()
}

View File

@@ -13,7 +13,7 @@ use serde::{Deserialize, Serialize};
use walkdir::WalkDir;
const OPA_REPO: &str = "https://github.com/open-policy-agent/opa";
const OPA_BRANCH: &str = "v0.65.0";
const OPA_BRANCH: &str = "v0.67.0";
#[derive(Serialize, Deserialize, PartialEq, Debug)]
#[serde(deny_unknown_fields)]