add support for auth and other improvements
This commit is contained in:
24
examples/end_to_end/README.md
Normal file
24
examples/end_to_end/README.md
Normal file
@@ -0,0 +1,24 @@
|
||||
# End-to-End Authorization Demo
|
||||
|
||||
This example demonstrates an end-to-end scenario involving a custom Rhai engine, `rhailib_worker`, and `rhai_client` to showcase how authorization based on `CALLER_PUBLIC_KEY` can be implemented.
|
||||
|
||||
## Overview
|
||||
|
||||
1. **Custom Rhai Engine**: A Rhai engine is created, and a custom function `check_permission(caller_pk: String)` is registered. This function returns different messages based on the `caller_pk` provided.
|
||||
2. **Rhai Worker (`rhailib_worker`)**: A worker is spawned with this custom engine. The worker is configured with its own `CIRCLE_PUBLIC_KEY` (e.g., "auth_worker_circle").
|
||||
3. **Rhai Client (`rhai_client`)**: The client is used to submit a script (`auth_script.rhai`) to the worker.
|
||||
4. **Authorization Script (`auth_script.rhai`)**: This script calls the `check_permission` function, passing the `CALLER_PUBLIC_KEY` (which is automatically injected into the script's scope by the worker based on the client's submission).
|
||||
5. **Demonstration**: The `main.rs` program submits the script twice, using two different `CALLER_PUBLIC_KEY`s ("admin_pk" and "user_pk"), and shows that the script produces different results based on the authorization logic in `check_permission`.
|
||||
|
||||
This example illustrates how the `rhailib` components can work together to build systems where script execution is controlled and authorized based on the identity of the calling client.
|
||||
|
||||
## Running the Example
|
||||
|
||||
Assuming you have Redis running and accessible at `redis://127.0.0.1/`:
|
||||
|
||||
Run the example from the `rhailib` root directory:
|
||||
```bash
|
||||
cargo run --example end_to_end_auth_demo
|
||||
```
|
||||
|
||||
You should see output indicating the results of the script execution for both the "admin_pk" and "user_pk" callers.
|
6
examples/end_to_end/auth_script.rhai
Normal file
6
examples/end_to_end/auth_script.rhai
Normal file
@@ -0,0 +1,6 @@
|
||||
// auth_script.rhai
|
||||
// This script calls a custom registered function 'check_permission'
|
||||
// and passes the CALLER_PUBLIC_KEY to it.
|
||||
// CALLER_PUBLIC_KEY is injected into the script's scope by the rhailib_worker.
|
||||
|
||||
check_permission(CALLER_PUBLIC_KEY)
|
136
examples/end_to_end/main.rs
Normal file
136
examples/end_to_end/main.rs
Normal file
@@ -0,0 +1,136 @@
|
||||
use rhai::{Engine, EvalAltResult};
|
||||
use rhai_client::RhaiClient;
|
||||
use rhailib_worker::spawn_rhai_worker;
|
||||
use std::{fs, path::Path, time::Duration};
|
||||
use tokio::sync::mpsc;
|
||||
use uuid::Uuid;
|
||||
|
||||
// Custom Rhai function for authorization
|
||||
// It takes the caller's public key as an argument.
|
||||
fn check_permission(caller_pk: String) -> Result<String, Box<EvalAltResult>> {
|
||||
log::info!("check_permission called with PK: {}", caller_pk);
|
||||
if caller_pk == "admin_pk" {
|
||||
Ok("Access Granted: Welcome Admin!".to_string())
|
||||
} else if caller_pk == "user_pk" {
|
||||
Ok("Limited Access: Welcome User!".to_string())
|
||||
} else {
|
||||
Ok(format!("Access Denied: Unknown public key '{}'", caller_pk))
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info")).init();
|
||||
|
||||
let redis_url = "redis://127.0.0.1/";
|
||||
let worker_circle_pk = "auth_worker_circle".to_string();
|
||||
|
||||
// 1. Create a Rhai engine and register custom functionality
|
||||
let mut engine = Engine::new();
|
||||
engine.register_fn("check_permission", check_permission);
|
||||
log::info!("Custom 'check_permission' function registered with Rhai engine.");
|
||||
|
||||
// 2. Spawn the Rhai worker
|
||||
let (shutdown_tx, shutdown_rx) = mpsc::channel(1);
|
||||
let worker_handle = tokio::spawn(spawn_rhai_worker(
|
||||
0, // worker_id
|
||||
worker_circle_pk.clone(),
|
||||
engine,
|
||||
redis_url.to_string(),
|
||||
shutdown_rx,
|
||||
false, // use_sentinel
|
||||
));
|
||||
log::info!("Rhai worker spawned for circle: {}", worker_circle_pk);
|
||||
|
||||
// Give the worker a moment to start up
|
||||
tokio::time::sleep(Duration::from_secs(1)).await;
|
||||
|
||||
// 3. Create a Rhai client
|
||||
let client = RhaiClient::new(redis_url)?;
|
||||
log::info!("Rhai client created.");
|
||||
|
||||
// 4. Load the Rhai script content
|
||||
let script_path_str = "examples/end_to_end/auth_script.rhai"; // Relative to Cargo.toml / rhailib root
|
||||
let script_content = match fs::read_to_string(script_path_str) {
|
||||
Ok(content) => content,
|
||||
Err(e) => {
|
||||
log::error!("Failed to read script file '{}': {}", script_path_str, e);
|
||||
// Attempt to read from an alternative path if run via `cargo run --example`
|
||||
// where current dir might be the crate root.
|
||||
let alt_script_path = Path::new(file!()).parent().unwrap().join("auth_script.rhai");
|
||||
log::info!("Attempting alternative script path: {:?}", alt_script_path);
|
||||
fs::read_to_string(&alt_script_path)?
|
||||
}
|
||||
};
|
||||
log::info!("Loaded script content from '{}'", script_path_str);
|
||||
|
||||
// Define different caller public keys
|
||||
let admin_caller_pk = "admin_pk".to_string();
|
||||
let user_caller_pk = "user_pk".to_string();
|
||||
let unknown_caller_pk = "unknown_pk".to_string();
|
||||
|
||||
let callers = vec![
|
||||
("Admin", admin_caller_pk),
|
||||
("User", user_caller_pk),
|
||||
("Unknown", unknown_caller_pk),
|
||||
];
|
||||
|
||||
for (caller_name, caller_pk) in callers {
|
||||
let task_id = Uuid::new_v4().to_string();
|
||||
log::info!(
|
||||
"Submitting script for caller '{}' (PK: {}) with task_id: {}",
|
||||
caller_name,
|
||||
caller_pk,
|
||||
task_id
|
||||
);
|
||||
|
||||
match client
|
||||
.submit_script_and_await_result(
|
||||
&worker_circle_pk,
|
||||
task_id.clone(), // task_id (UUID) first
|
||||
script_content.clone(), // script_content second
|
||||
Duration::from_secs(10),
|
||||
Some(caller_pk.clone()), // This is the CALLER_PUBLIC_KEY
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(details) => {
|
||||
log::info!(
|
||||
"Task {} for caller '{}' (PK: {}) completed. Status: {}, Output: {:?}, Error: {:?}",
|
||||
task_id,
|
||||
caller_name,
|
||||
caller_pk,
|
||||
details.status,
|
||||
details.output,
|
||||
details.error
|
||||
);
|
||||
// Basic assertion for expected output
|
||||
if caller_pk == "admin_pk" {
|
||||
assert_eq!(details.output, Some("Access Granted: Welcome Admin!".to_string()));
|
||||
} else if caller_pk == "user_pk" {
|
||||
assert_eq!(details.output, Some("Limited Access: Welcome User!".to_string()));
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!(
|
||||
"Task {} for caller '{}' (PK: {}) failed: {}",
|
||||
task_id,
|
||||
caller_name,
|
||||
caller_pk,
|
||||
e
|
||||
);
|
||||
}
|
||||
}
|
||||
tokio::time::sleep(Duration::from_millis(100)).await; // Small delay between submissions
|
||||
}
|
||||
|
||||
// 5. Shutdown the worker (optional, could also let it run until program exits)
|
||||
log::info!("Signaling worker to shutdown...");
|
||||
let _ = shutdown_tx.send(()).await;
|
||||
if let Err(e) = worker_handle.await {
|
||||
log::error!("Worker task panicked or encountered an error: {:?}", e);
|
||||
}
|
||||
log::info!("Worker shutdown complete.");
|
||||
|
||||
Ok(())
|
||||
}
|
Reference in New Issue
Block a user