Skip to content

Commit

Permalink
feat(jans-cedarling): add WASM bindings for Cedarling (#10542)
Browse files Browse the repository at this point in the history
* chore(jans-cedarling): rebase to main

Signed-off-by: Oleh Bohzok <[email protected]>

* chore(jans-cedarling): fix monotonic uuid creation

Signed-off-by: Oleh Bohzok <[email protected]>

* chore(jans-cedarling): fix generating uuid7 using generator defined in the static variable

Signed-off-by: Oleh Bohzok <[email protected]>

* chore(jans-cedarling): update `LogStorage` trait to return `serde_json::Value` as result

Signed-off-by: Oleh Bohzok <[email protected]>

* feat(jans-cedarling): add std log write log to browser console.

Signed-off-by: Oleh Bohzok <[email protected]>

* chore(jans-cedarling): apply cargo fmt

Signed-off-by: Oleh Bohzok <[email protected]>

* test(jans-cedarling): add WASM test to run cedarling

Signed-off-by: Oleh Bohzok <[email protected]>

* feat(jans-cedarling): add memory log to WASM API

Signed-off-by: Oleh Bohzok <[email protected]>

* chore(jans-cedarling): remove conditional compiling for WASM,

it allows to catch some errors when made changes on different platforms

Signed-off-by: Oleh Bohzok <[email protected]>

* chore(jans-cedarling): improve doc message for memory log interface

Signed-off-by: Oleh Bohzok <[email protected]>

* test(jans-cedarling): add `test_memory_log_interface`

Signed-off-by: Oleh Bohzok <[email protected]>

* feat(jans-cedarling): implement html page with cedarling usage

Signed-off-by: Oleh Bohzok <[email protected]>

* chore(jans-cedarling): remove `println`

Signed-off-by: Oleh Bohzok <[email protected]>

* chore(jans-cedarling): fix clippy issue

Signed-off-by: Oleh Bohzok <[email protected]>

* chore(jans-cedarling): add to `Cargo.toml` license and description

Signed-off-by: Oleh Bohzok <[email protected]>

* test(jans-cedarling): add run WASM tests on CI

Signed-off-by: Oleh Bohzok <[email protected]>

* chore(jans-cedarling): remove `dynamic = ["version"]` from `pyproject.toml` because it cause fail CI check

Signed-off-by: Oleh Bohzok <[email protected]>

* chore(jans-cedarling): remove unused WASM code

Signed-off-by: Oleh Bohzok <[email protected]>

* chore(jans-cedarling): fix clippy issues

Signed-off-by: Oleh Bohzok <[email protected]>

* docs(jans-cedarling): remove unnecessary part about optimization wasm.

It is already optimized if used `--release` on build

Signed-off-by: Oleh Bohzok <[email protected]>

* test(jans-cedarling): fix unit tests

Signed-off-by: Oleh Bohzok <[email protected]>

* chore(jans-cedarling): remove comment that enable blocking `cedarling` by default

Signed-off-by: Oleh Bohzok <[email protected]>

* chore(jans-cedarling): make comment with license more correct

Signed-off-by: Oleh Bohzok <[email protected]>

* chore(jans-cedarling): update description for `cedarling` package

Signed-off-by: Oleh Bohzok <[email protected]>

* chore(jans-cedarling): add `dynamic = ["version"]` to python bindings

Signed-off-by: Oleh Bohzok <[email protected]>

* chore(jans-cedarling): remove double comments

Signed-off-by: Oleh Bohzok <[email protected]>

* chore(jans-cedarling): clone cedarling html app to another html app

Signed-off-by: Oleh Bohzok <[email protected]>

* chore(jans-cedarling): add simplified example to the index.html

Signed-off-by: Oleh Bohzok <[email protected]>

* chore(jans-cedarling): refactor examples, move hardcoded data to `example_data.js`

Signed-off-by: Oleh Bohzok <[email protected]>

* chore(jans-cedarling): add scrolling to result when request is executed

Signed-off-by: Oleh Bohzok <[email protected]>

* chore(jans-cedarling): add tokens key for request

Signed-off-by: Oleh Bohzok <[email protected]>

* chore(jans-cedarling): fix serializing json logs

Signed-off-by: Oleh Bohzok <[email protected]>

* chore(jans-cedarling): add licence header

Signed-off-by: Oleh Bohzok <[email protected]>

* chore(jans-cedarling): update bootstrap to latest

Signed-off-by: Oleh Bohzok <[email protected]>
Signed-off-by: Oleh Bohzok <[email protected]>
Signed-off-by: Oleh Bohzok <[email protected]>

* chore(jans-cedarling): add graceful error handling when init blocking client

Signed-off-by: Oleh Bohzok <[email protected]>
Signed-off-by: Oleh Bohzok <[email protected]>
Signed-off-by: Oleh Bohzok <[email protected]>

* chore(jans-cedarling): fix test case, rename key `kkk` to `key` to avoid misinterpreted

Signed-off-by: Oleh Bohzok <[email protected]>
Signed-off-by: Oleh Bohzok <[email protected]>
Signed-off-by: Oleh Bohzok <[email protected]>
Signed-off-by: Oleh Bohzok <[email protected]>

* chore(jans-cedarling): update documentation

Signed-off-by: Oleh Bohzok <[email protected]>
Signed-off-by: Oleh Bohzok <[email protected]>
Signed-off-by: Oleh Bohzok <[email protected]>
Signed-off-by: Oleh Bohzok <[email protected]>

* ci: use working-directory

Signed-off-by: moabu <[email protected]>

---------

Signed-off-by: Oleh Bohzok <[email protected]>
Signed-off-by: Oleh Bohzok <[email protected]>
Signed-off-by: moabu <[email protected]>
Co-authored-by: moabu <[email protected]>
  • Loading branch information
olehbozhok and moabu authored Jan 13, 2025
1 parent 856f9fe commit ec7c7e1
Show file tree
Hide file tree
Showing 78 changed files with 2,818 additions and 827 deletions.
32 changes: 28 additions & 4 deletions .github/workflows/test-cedarling.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,37 @@ jobs:
- name: Install Rust
uses: dtolnay/rust-toolchain@1ff72ee08e3cb84d84adba594e0a297990fc1ed3 # stable
- name: Run Tests
working-directory: jans-cedarling
run: |
cd ./jans-cedarling
cargo test --workspace
- name: Run Clippy
- name: Run Clippy on native target
working-directory: jans-cedarling
run: |
cd ./jans-cedarling
cargo clippy -- -Dwarnings
wasm_tests:
runs-on: ubuntu-latest
steps:
- name: Harden Runner
uses: step-security/harden-runner@a4aa98b93cab29d9b1101a6143fb8bce00e2eac4 # v2.7.1
with:
egress-policy: audit
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- name: Install Rust
uses: dtolnay/rust-toolchain@1ff72ee08e3cb84d84adba594e0a297990fc1ed3 # stable
- name: Install WASM dependencies
run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
- name: Run WASM tests using chrome
working-directory: jans-cedarling/bindings/cedarling_wasm
run: |
wasm-pack test --headless --chrome
- name: Run WASM tests using firefox
working-directory: jans-cedarling/bindings/cedarling_wasm
run: |
wasm-pack test --headless --firefox
- name: Run Clippy on WASM target
working-directory: jans-cedarling
run: |
cargo clippy --target wasm32-unknown-unknown -- -Dwarnings
python_tests:
runs-on: ubuntu-latest
strategy:
Expand All @@ -48,6 +72,6 @@ jobs:
python3 -m pip install --upgrade pip || echo "Failed to upgrade pip"
python3 -m pip install tox
- name: Test with pytest
working-directory: jans-cedarling/bindings/cedarling_python
run: |
cd ./jans-cedarling/bindings/cedarling_python
tox
6 changes: 4 additions & 2 deletions docs/cedarling/cedarling-authz.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,9 @@ Action, Resource and Context is sent by the application in the authorization req
this is a sample request from a hypothetical application:

```js
input = {
const bootstrap_config = {...};
const cedarling = await init(bootstrap_config);
let input = {
"tokens": {
"access_token": "eyJhbGc....",
"id_token": "eyJjbGc...",
Expand All @@ -76,7 +78,7 @@ input = {
}
}

decision_result = authz(input)
decision_result = await cedarling(input)
```

## Automatically Adding Entity References to the Context
Expand Down
228 changes: 228 additions & 0 deletions docs/cedarling/wasm/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
---
tags:
- cedarling
- wasm
---

# WASM for Cedarling

Cedarling provides a binding for JavaScript programs via the `wasm-pack` tool. This allows browser developers to use the cedarling crate in their code directly.

## Requirements

- Rust 1.63 or greater
- Installed `wasm-pack` via `cargo`
- clang with `wasm` target support

## Building

- Install `wasm-pack` by:

```sh
cargo install wasm-pack
```

- Build cedarling `wasm` in release:

```bash
wasm-pack build --release --target web
```

`wasm-pack` automatically make optimization of `wasm` binary file, using `wasm-opt`.
- Get result in the `pkg` folder.

## Including in projects

For using result files in browser project you need make result `pkg` folder accessible for loading in the browser so that you can later import the corresponding file from the browser.

Here is example of code snippet:

```html
<script type="module">
import initWasm, { init } from "/pkg/cedarling_wasm.js";
async function main() {
await initWasm(); // Initialize the WebAssembly module
// init cedarling with `BOOTSTRAP` config
let instance = await init({
"CEDARLING_APPLICATION_NAME": "My App",
"CEDARLING_POLICY_STORE_URI": "https://example.com/policy-store.json",
"CEDARLING_LOG_TYPE": "memory",
"CEDARLING_LOG_LEVEL": "INFO",
"CEDARLING_LOG_TTL": 120,
"CEDARLING_DECISION_LOG_USER_CLAIMS ": ["aud", "sub", "email", "username"],
"CEDARLING_DECISION_LOG_WORKLOAD_CLAIMS ": ["aud", "client_id", "rp_id"],
"CEDARLING_USER_AUTHZ": "enabled",
"CEDARLING_WORKLOAD_AUTHZ": "enabled",
"CEDARLING_USER_WORKLOAD_BOOLEAN_OPERATION": "AND",
});
// make authorize request
let result = await instance.authorize({
"tokens": {
"access_token": "...",
"id_token": "...",
"userinfo_token": "...",
},
"action": 'Jans::Action::"Read"',
"resource": {
"type": "Jans::Application",
"id": "some_id",
"app_id": "application_id",
"name": "Some Application",
"url": {
"host": "jans.test",
"path": "/protected-endpoint",
"protocol": "http"
}
},
"context": {
"current_time": Math.floor(Date.now() / 1000),
"device_health": ["Healthy"],
"fraud_indicators": ["Allowed"],
"geolocation": ["America"],
"network": "127.0.0.1",
"network_type": "Local",
"operating_system": "Linux",
"user_agent": "Linux"
},
});
console.log("result:", result);
}
main().catch(console.error);
</script>
```

## Usage

Before usage make sure that you have completed `Building` steps.
You can find usage examples in the following locations:

- `jans-cedarling/bindings/cedarling_wasm/index.html`: A simple example demonstrating basic usage.
- `jans-cedarling/bindings/cedarling_wasm/cedarling_app.html`: A fully featured `Cedarling` browser app where you can test and validate your configuration.

### Defined API

```ts
/**
* Create a new instance of the Cedarling application.
* This function can take as config parameter the eather `Map` other `Object`
*/
export function init(config: any): Promise<Cedarling>;

/**
* The instance of the Cedarling application.
*/
export class Cedarling {
/**
* Create a new instance of the Cedarling application.
* Assume that config is `Object`
*/
static new(config: object): Promise<Cedarling>;
/**
* Create a new instance of the Cedarling application.
* Assume that config is `Map`
*/
static new_from_map(config: Map<any, any>): Promise<Cedarling>;
/**
* Authorize request
* makes authorization decision based on the [`Request`]
*/
authorize(request: any): Promise<AuthorizeResult>;
/**
* Get logs and remove them from the storage.
* Returns `Array` of `Map`
*/
pop_logs(): Array<any>;
/**
* Get specific log entry.
* Returns `Map` with values or `null`.
*/
get_log_by_id(id: string): any;
/**
* Returns a list of all log ids.
* Returns `Array` of `String`
*/
get_log_ids(): Array<any>;
}

/**
* A WASM wrapper for the Rust `cedarling::AuthorizeResult` struct.
* Represents the result of an authorization request.
*/
export class AuthorizeResult {
/**
* Convert `AuthorizeResult` to json string value
*/
json_string(): string;
/**
* Result of authorization where principal is `Jans::Workload`
*/
workload?: AuthorizeResultResponse;
/**
* Result of authorization where principal is `Jans::User`
*/
person?: AuthorizeResultResponse;
/**
* Result of authorization
* true means `ALLOW`
* false means `Deny`
*
* this field is [`bool`] type to be compatible with [authzen Access Evaluation Decision](https://openid.github.io/authzen/#section-6.2.1).
*/
decision: boolean;
}

/**
* A WASM wrapper for the Rust `cedar_policy::Response` struct.
* Represents the result of an authorization request.
*/
export class AuthorizeResultResponse {
/**
* Authorization decision
*/
readonly decision: boolean;
/**
* Diagnostics providing more information on how this decision was reached
*/
readonly diagnostics: Diagnostics;
}

/**
* Diagnostics
* ===========
*
* Provides detailed information about how a policy decision was made, including policies that contributed to the decision and any errors encountered during evaluation.
*/
export class Diagnostics {
/**
* `PolicyId`s of the policies that contributed to the decision.
* If no policies applied to the request, this set will be empty.
*
* The ids should be treated as unordered,
*/
readonly reason: (string)[];
/**
* Errors that occurred during authorization. The errors should be
* treated as unordered, since policies may be evaluated in any order.
*/
readonly errors: (PolicyEvaluationError)[];
}

/**
* PolicyEvaluationError
* =====================
*
* Represents an error that occurred when evaluating a Cedar policy.
*/
export class PolicyEvaluationError {
/**
* Id of the policy with an error
*/
readonly id: string;
/**
* Underlying evaluation error string representation
*/
readonly error: string;
}
```
2 changes: 2 additions & 0 deletions jans-cedarling/.cargo/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[target.wasm32-unknown-unknown]
runner = 'wasm-bindgen-test-runner'
9 changes: 8 additions & 1 deletion jans-cedarling/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,20 @@ serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
thiserror = "1.0"
sparkv = { path = "sparkv" }
jsonwebtoken = "9.3.0"
jsonwebkey = "0.3.5"
chrono = "0.4"
cedarling = { path = "cedarling" }
test_utils = { path = "test_utils" }
wasm-bindgen = "0.2"
wasm-bindgen-futures = "0.4"
web-sys = "0.3"
serde-wasm-bindgen = "0.6"


[profile.release]
strip = "symbols"
debug-assertions = true
debug-assertions = false
lto = "thin"
opt-level = "s"
codegen-units = 1
8 changes: 5 additions & 3 deletions jans-cedarling/bindings/cedarling_python/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,17 @@
name = "cedarling_python"
version = "0.0.0"
edition = "2021"
description = "Python binding to Cedarling"
license = "Apache-2.0"

[lib]
name = "cedarling_python"
crate-type = ["cdylib"]

[dependencies]
# dependency for NOT wasm target
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
pyo3 = { version = "0.22.5", features = ["extension-module", "gil-refs"] }
cedarling = { workspace = true }
cedarling = { workspace = true, features = ["blocking"] }
serde = { workspace = true }
serde_json = { workspace = true }
serde-pyobject = "0.4.0"
jsonwebtoken = "9.3.0"
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ pub struct AuthorizeResult {
impl AuthorizeResult {
/// Returns true if request is allowed
fn is_allowed(&self) -> bool {
self.inner.is_allowed()
self.inner.decision
}

/// Get the decision value for workload
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
*
* Copyright (c) 2024, Gluu, Inc.
*/
use pyo3::prelude::*;
use pyo3::Bound;
use pyo3::prelude::*;

pub(crate) mod authorize_result;
mod authorize_result_response;
Expand Down
6 changes: 3 additions & 3 deletions jans-cedarling/bindings/cedarling_python/src/cedarling.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,14 +62,14 @@ use serde_pyobject::to_pyobject;
#[derive(Clone)]
#[pyclass]
pub struct Cedarling {
inner: cedarling::Cedarling,
inner: cedarling::blocking::Cedarling,
}

#[pymethods]
impl Cedarling {
#[new]
fn new(config: &BootstrapConfig) -> PyResult<Self> {
let inner = cedarling::Cedarling::new(config.inner())
let inner = cedarling::blocking::Cedarling::new(config.inner())
.map_err(|err| PyValueError::new_err(err.to_string()))?;
Ok(Self { inner })
}
Expand Down Expand Up @@ -112,7 +112,7 @@ impl Cedarling {
}
}

fn log_entry_to_py(gil: Python, entry: &cedarling::bindings::LogEntry) -> PyResult<PyObject> {
fn log_entry_to_py(gil: Python, entry: &serde_json::Value) -> PyResult<PyObject> {
to_pyobject(gil, entry)
.map(|v| v.unbind())
.map_err(|err| err.0)
Expand Down
2 changes: 1 addition & 1 deletion jans-cedarling/bindings/cedarling_python/src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
*
* Copyright (c) 2024, Gluu, Inc.
*/
use pyo3::prelude::*;
use pyo3::Bound;
use pyo3::prelude::*;

pub(crate) mod bootstrap_config;

Expand Down
Loading

0 comments on commit ec7c7e1

Please sign in to comment.