Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(jans-cedarling): add WASM bindings for Cedarling #10542

Open
wants to merge 47 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 43 commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
5089a64
chore(jans-cedarling): rebase to main
olehbozhok Jan 2, 2025
94d9c0e
chore(jans-cedarling): fix monotonic uuid creation
olehbozhok Jan 2, 2025
f899d4a
chore(jans-cedarling): fix generating uuid7 using generator defined i…
olehbozhok Jan 2, 2025
15fc408
chore(jans-cedarling): update `LogStorage` trait to return `serde_jso…
olehbozhok Jan 2, 2025
03e85af
feat(jans-cedarling): add std log write log to browser console.
olehbozhok Jan 2, 2025
b07f267
chore(jans-cedarling): apply cargo fmt
olehbozhok Jan 2, 2025
c53d49b
test(jans-cedarling): add WASM test to run cedarling
olehbozhok Jan 2, 2025
54f6291
feat(jans-cedarling): add memory log to WASM API
olehbozhok Jan 2, 2025
0a3f195
chore(jans-cedarling): remove conditional compiling for WASM,
olehbozhok Jan 2, 2025
1b39cc3
chore(jans-cedarling): improve doc message for memory log interface
olehbozhok Jan 2, 2025
dcce0b2
test(jans-cedarling): add `test_memory_log_interface`
olehbozhok Jan 2, 2025
6d0761b
feat(jans-cedarling): implement html page with cedarling usage
olehbozhok Jan 2, 2025
d7a04f8
chore(jans-cedarling): remove `println`
olehbozhok Jan 2, 2025
8ee1915
chore(jans-cedarling): fix clippy issue
olehbozhok Jan 2, 2025
328da64
chore(jans-cedarling): add to `Cargo.toml` license and description
olehbozhok Jan 2, 2025
03ee0a5
test(jans-cedarling): add run WASM tests on CI
olehbozhok Jan 2, 2025
9e5199e
chore(jans-cedarling): remove `dynamic = ["version"]` from `pyproject…
olehbozhok Jan 2, 2025
f7968fb
chore(jans-cedarling): remove unused WASM code
olehbozhok Jan 2, 2025
09e0582
chore(jans-cedarling): fix clippy issues
olehbozhok Jan 3, 2025
0b3ef5f
docs(jans-cedarling): remove unnecessary part about optimization wasm.
olehbozhok Jan 3, 2025
9e54440
Merge commit '1951312d5bc426078f4ed193d5ccc00df6101dfa' into jans-ced…
olehbozhok Jan 3, 2025
2d5652d
test(jans-cedarling): fix unit tests
olehbozhok Jan 3, 2025
a06ee3b
chore(jans-cedarling): remove comment that enable blocking `cedarling…
olehbozhok Jan 3, 2025
984094c
chore(jans-cedarling): make comment with license more correct
olehbozhok Jan 3, 2025
5a83f86
chore(jans-cedarling): update description for `cedarling` package
olehbozhok Jan 3, 2025
939c5c4
chore(jans-cedarling): add `dynamic = ["version"]` to python bindings
olehbozhok Jan 3, 2025
2c6380b
chore(jans-cedarling): remove double comments
olehbozhok Jan 3, 2025
f72d234
chore(jans-cedarling): clone cedarling html app to another html app
olehbozhok Jan 6, 2025
cba3487
chore(jans-cedarling): add simplified example to the index.html
olehbozhok Jan 6, 2025
458e642
chore(jans-cedarling): refactor examples, move hardcoded data to `exa…
olehbozhok Jan 6, 2025
9f771cc
chore(jans-cedarling): add scrolling to result when request is executed
olehbozhok Jan 6, 2025
68702df
chore(jans-cedarling): add tokens key for request
olehbozhok Jan 7, 2025
d2b37db
chore(jans-cedarling): fix serializing json logs
olehbozhok Jan 7, 2025
98233fd
chore(jans-cedarling): add licence header
olehbozhok Jan 7, 2025
87356f6
chore(jans-cedarling): update bootstrap to latest
olehbozhok Jan 7, 2025
010a8fd
chore(jans-cedarling): add graceful error handling when init blocking…
olehbozhok Jan 7, 2025
814e745
chore(jans-cedarling): fix test case, rename key `kkk` to `key` to av…
olehbozhok Jan 8, 2025
784a627
chore(jans-cedarling): update documentation
olehbozhok Jan 9, 2025
cc35bf4
Merge branch 'main' into jans-cedaling-issue-10013
olehbozhok Jan 9, 2025
904b3b7
ci: use working-directory
moabu Jan 9, 2025
5c3c3ce
Merge branch 'main' into jans-cedaling-issue-10013
olehbozhok Jan 9, 2025
efdd675
chore(jans-cedarling): increase duration in test to avoid failure on …
olehbozhok Jan 10, 2025
c1ac642
chore(jans-cedarling): fix mistype bootsrap -> bootstrap
olehbozhok Jan 10, 2025
606331b
chore(jans-cedarling): specified toolchain
olehbozhok Jan 10, 2025
4510b9c
chore(jans-cedarling): rename toolchain file to be more correct
olehbozhok Jan 10, 2025
536fa05
chore(jans-cedarling): align `dtolnay/rust-toolchain` to use in WASM …
olehbozhok Jan 10, 2025
767319d
Merge branch 'main' into jans-cedaling-issue-10013
olehbozhok Jan 10, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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@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
Loading