Cedarling Rust Developer Guide#
Requirements#
Rust Installation#
If you haven't installed Rust yet, follow the official installation guide:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
After installation, restart your terminal or run:
source $HOME/.cargo/env
Verify the installation:
rustc --version
cargo --version
Installation#
The Cedarling Rust library is not yet available on crates.io, so you need to include it directly from the source in your project.
Adding to Your Project#
1. Clone the Jans repository
git clone https://github.com/JanssenProject/jans.git
cd jans/jans-cedarling/cedarling
2. Add to your Cargo.toml
[dependencies]
cedarling = { path = "path/to/jans/jans-cedarling/cedarling" }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
tokio = { version = "1.0", features = ["full"] }
3. Alternative: Using Git Dependency
[dependencies]
cedarling = { git = "https://github.com/JanssenProject/jans", package = "cedarling" }
Building#
Building from Source#
1. Clone the repository
git clone https://github.com/JanssenProject/jans.git
cd jans/jans-cedarling
2. Build the library
cargo build --release
3. Run tests
cargo test --workspace
4. Generate documentation
cargo doc -p cedarling --no-deps --open
Building Examples#
The Cedarling project includes several examples that demonstrate different use cases:
# Run JWT validation example
cargo run -p cedarling --example authorize_with_jwt_validation
# Run unsigned authorization example
cargo run -p cedarling --example authorize_unsigned
# Run logging example
cargo run -p cedarling --example log_init -- stdout
Usage#
Initialization#
The first step is to initialize Cedarling with your configuration:
use cedarling::*;
use std::collections::HashSet;
use jsonwebtoken::Algorithm;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Configure JWT validation settings
let jwt_config = JwtConfig {
jwks: None,
jwt_sig_validation: true,
jwt_status_validation: false,
signature_algorithms_supported: HashSet::from_iter([
Algorithm::HS256,
Algorithm::RS256
]),
};
// Initialize Cedarling with configuration
let cedarling = Cedarling::new(&BootstrapConfig {
application_name: "my_app".to_string(),
log_config: LogConfig {
log_type: LogTypeConfig::StdOut,
log_level: LogLevel::INFO,
},
policy_store_config: PolicyStoreConfig {
source: PolicyStoreSource::Yaml(POLICY_STORE_YAML.to_string()),
},
jwt_config,
authorization_config: AuthorizationConfig {
use_user_principal: true,
use_workload_principal: true,
principal_bool_operator: JsonRule::new(serde_json::json!({
"and": [
{"===": [{"var": "Jans::Workload"}, "ALLOW"]},
{"===": [{"var": "Jans::User"}, "ALLOW"]}
]
})).unwrap(),
..Default::default()
},
entity_builder_config: EntityBuilderConfig::default()
.with_user()
.with_workload(),
lock_config: None,
}).await?;
Ok(())
}
Token-Based Authorization#
Token-based authorization uses JWT tokens to extract principal information:
use cedarling::*;
use std::collections::HashMap;
use serde_json::json;
async fn perform_token_authorization(cedarling: &Cedarling) -> Result<(), Box<dyn std::error::Error>> {
// 1. Prepare JWT tokens
let access_token = "your_access_token_here".to_string();
let id_token = "your_id_token_here".to_string();
let userinfo_token = "your_userinfo_token_here".to_string();
// 2. Define the resource
let resource = EntityData {
entity_type: "Jans::Application".to_string(),
id: "app_id_001".to_string(),
attributes: HashMap::from_iter([
("protocol".to_string(), json!("https")),
("host".to_string(), json!("example.com")),
("path".to_string(), json!("/admin-dashboard")),
]),
};
// 3. Define the action
let action = r#"Jans::Action::"Read""#.to_string();
// 4. Define context
let context = json!({
"current_time": std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_millis(),
"device_health": ["Healthy"],
"fraud_indicators": ["Allowed"],
"geolocation": ["America"],
"network": "127.0.0.1",
"network_type": "Local",
"operating_system": "Linux",
"user_agent": "Linux"
});
// 5. Build the request
let request = Request {
tokens: HashMap::from([
("access_token".to_string(), access_token),
("id_token".to_string(), id_token),
("userinfo_token".to_string(), userinfo_token),
]),
action,
resource,
context,
};
// 6. Perform authorization
let result = cedarling.authorize(request).await?;
match result.decision {
true => println!("Access granted"),
false => println!("Access denied: {:?}", result.diagnostics),
}
Ok(())
}
Unsigned Authorization#
Unsigned authorization allows you to pass principals directly without JWT tokens:
use cedarling::*;
use std::collections::HashMap;
use serde_json::json;
async fn perform_unsigned_authorization(cedarling: &Cedarling) -> Result<(), Box<dyn std::error::Error>> {
// 1. Define principals directly
let principals = vec![
EntityData {
entity_type: "Jans::Workload".to_string(),
id: "some_workload_id".to_string(),
attributes: HashMap::from_iter([
("client_id".to_string(), json!("some_client_id")),
]),
},
EntityData {
entity_type: "Jans::User".to_string(),
id: "random_user_id".to_string(),
attributes: HashMap::from_iter([
("roles".to_string(), json!(["admin", "manager"])),
("email".to_string(), json!("user@example.com")),
]),
},
];
// 2. Define the resource
let resource = EntityData {
entity_type: "Jans::Application".to_string(),
id: "app_id_001".to_string(),
attributes: HashMap::from_iter([
("protocol".to_string(), json!("https")),
("host".to_string(), json!("example.com")),
("path".to_string(), json!("/admin-dashboard")),
]),
};
// 3. Define the action
let action = r#"Jans::Action::"Update""#.to_string();
// 4. Define context
let context = json!({
"current_time": std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_millis(),
"device_health": ["Healthy"],
"fraud_indicators": ["Allowed"],
"geolocation": ["America"],
"network": "127.0.0.1",
"network_type": "Local",
"operating_system": "Linux",
"user_agent": "Linux"
});
// 5. Build the unsigned request
let request = RequestUnsigned {
principals,
action,
resource,
context,
};
// 6. Perform authorization
let result = cedarling.authorize_unsigned(request).await?;
match result.decision {
true => println!("Access granted"),
false => println!("Access denied: {:?}", result.diagnostics),
}
Ok(())
}
Log Retrieval#
Cedarling provides comprehensive logging capabilities:
use cedarling::*;
async fn retrieve_logs(cedarling: &Cedarling) -> Result<(), Box<dyn std::error::Error>> {
// Get all log IDs
let log_ids = cedarling.get_log_ids();
println!("Available log IDs: {:?}", log_ids);
// Get specific log by ID
for id in &log_ids {
if let Some(log_entry) = cedarling.get_log_by_id(id) {
println!("Log entry {}: {:?}", id, log_entry);
}
}
// Pop all logs (retrieves and clears the buffer)
let logs = cedarling.pop_logs();
println!("Retrieved {} log entries", logs.len());
for (i, log) in logs.iter().enumerate() {
println!("Log {}: {:?}", i, log);
}
Ok(())
}
Complete Example#
Here's a complete example showing initialization, authorization, and logging:
use cedarling::*;
use std::collections::{HashMap, HashSet};
use jsonwebtoken::Algorithm;
use serde_json::json;
static POLICY_STORE_YAML: &str = include_str!("../../test_files/policy-store_ok.yaml");
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Initialize Cedarling
let cedarling = Cedarling::new(&BootstrapConfig {
application_name: "example_app".to_string(),
log_config: LogConfig {
log_type: LogTypeConfig::Memory(MemoryLogConfig {
log_ttl: 3600, // 1 hour
max_item_size: None,
max_items: None,
}),
log_level: LogLevel::INFO,
},
policy_store_config: PolicyStoreConfig {
source: PolicyStoreSource::Yaml(POLICY_STORE_YAML.to_string()),
},
jwt_config: JwtConfig {
jwks: None,
jwt_sig_validation: false,
jwt_status_validation: false,
signature_algorithms_supported: HashSet::new(),
}.allow_all_algorithms(),
authorization_config: AuthorizationConfig {
use_user_principal: true,
use_workload_principal: true,
principal_bool_operator: JsonRule::new(serde_json::json!(
{"===": [{"var": "Jans::User"}, "ALLOW"]}
)).unwrap(),
..Default::default()
},
entity_builder_config: EntityBuilderConfig::default()
.with_user()
.with_workload(),
lock_config: None,
}).await?;
// Perform unsigned authorization
let principals = vec![EntityData {
entity_type: "Jans::User".to_string(),
id: "test_user".to_string(),
attributes: HashMap::from_iter([
("roles".to_string(), json!(["admin"])),
("email".to_string(), json!("admin@example.com")),
]),
}];
let resource = EntityData {
entity_type: "Jans::Application".to_string(),
id: "admin_panel".to_string(),
attributes: HashMap::from_iter([
("permission_level".to_string(), json!("admin")),
]),
};
let request = RequestUnsigned {
principals,
action: r#"Jans::Action::"Read""#.to_string(),
resource,
context: json!({}),
};
let result = cedarling.authorize_unsigned(request).await?;
println!("Authorization result: {}", result.decision);
// Retrieve logs
let logs = cedarling.pop_logs();
println!("Retrieved {} log entries", logs.len());
Ok(())
}
API Reference#
Core Types#
Cedarling
#
The main struct for interacting with Cedarling.
pub struct Cedarling {
// Implementation details
}
BootstrapConfig
#
Configuration for initializing Cedarling.
pub struct BootstrapConfig {
pub application_name: String,
pub log_config: LogConfig,
pub policy_store_config: PolicyStoreConfig,
pub jwt_config: JwtConfig,
pub authorization_config: AuthorizationConfig,
pub entity_builder_config: EntityBuilderConfig,
pub lock_config: Option<LockConfig>,
}
Request
#
Token-based authorization request.
pub struct Request {
pub tokens: HashMap<String, String>,
pub action: String,
pub resource: EntityData,
pub context: serde_json::Value,
}
RequestUnsigned
#
Unsigned authorization request.
pub struct RequestUnsigned {
pub principals: Vec<EntityData>,
pub action: String,
pub resource: EntityData,
pub context: serde_json::Value,
}
EntityData
#
Represents an entity in the authorization system.
pub struct EntityData {
pub entity_type: String,
pub id: String,
pub attributes: HashMap<String, serde_json::Value>,
}
Main Methods#
Cedarling::new()
#
Initialize a new Cedarling instance.
pub async fn new(config: &BootstrapConfig) -> Result<Self, CedarlingError>
authorize()
#
Perform token-based authorization.
pub async fn authorize(&self, request: Request) -> Result<AuthorizeResult, CedarlingError>
authorize_unsigned()
#
Perform unsigned authorization.
pub async fn authorize_unsigned(&self, request: RequestUnsigned) -> Result<AuthorizeResult, CedarlingError>
pop_logs()
#
Retrieve and clear all logs.
pub fn pop_logs(&self) -> Vec<LogEntry>
get_log_ids()
#
Get all available log IDs.
pub fn get_log_ids(&self) -> Vec<String>
get_log_by_id()
#
Get a specific log entry by ID.
pub fn get_log_by_id(&self, id: &str) -> Option<LogEntry>
Configuration#
Bootstrap Properties#
Cedarling can be configured using bootstrap properties. See the bootstrap properties documentation for complete configuration options.
Environment Variables#
You can also configure Cedarling using environment variables:
export CEDARLING_APPLICATION_NAME="my_app"
export CEDARLING_LOG_TYPE="stdout"
export CEDARLING_LOG_LEVEL="INFO"
export CEDARLING_POLICY_STORE_LOCAL_FN="/path/to/policy-store.yaml"
Configuration Loading#
use cedarling::*;
// Load from environment variables
let config = BootstrapConfig::from_env();
// Load from JSON string
let config = BootstrapConfig::from_json(json_string)?;
// Load from file
let config = BootstrapConfig::from_file("config.json")?;
Examples#
The Cedarling project includes several examples in the examples/
directory:
JWT Validation Example#
cargo run -p cedarling --example authorize_with_jwt_validation
Unsigned Authorization Example#
cargo run -p cedarling --example authorize_unsigned
Logging Examples#
# Stdout logging
cargo run -p cedarling --example log_init -- stdout
# Memory logging with TTL
cargo run -p cedarling --example log_init -- memory 3600
# Disable logging
cargo run -p cedarling --example log_init -- off
Lock Server Integration#
cargo run -p cedarling --example lock_integration
Testing and Debugging#
Running Tests#
# Run all tests
cargo test --workspace
# Run specific test
cargo test -p cedarling test_name
# Run tests with output
cargo test -- --nocapture
Running Benchmarks#
# Run all benchmarks
cargo bench -p cedarling
# Run specific benchmark
cargo bench -p cedarling benchmark_name
Code Coverage#
# Install coverage tool
cargo install cargo-llvm-cov
# Generate coverage report
cargo llvm-cov --html --open
Profiling#
# Run profiling example
cargo run --example profiling
Debugging#
Enable debug logging:
let config = BootstrapConfig {
log_config: LogConfig {
log_level: LogLevel::DEBUG,
..Default::default()
},
// ... other config
};