cedarling 0.0.5

The Cedarling: a high-performance local authorization service powered by the Rust Cedar Engine.
Documentation
// This software is available under the Apache-2.0 license.
// See https://www.apache.org/licenses/LICENSE-2.0.txt for full text.
//
// Copyright (c) 2024, Gluu, Inc.

// run this example using `cargo run --example lock_integration`

use cedarling::log_config::StdOutLoggerMode;
use cedarling::{
    AuthorizationConfig, BootstrapConfig, CedarEntityMapping, Cedarling, DataStoreConfig,
    EntityData, HttpClientConfig, JwtConfig, LockServiceConfig, LockTransport, LogConfig, LogLevel,
    LogTypeConfig, PolicyStoreConfig, PolicyStoreSource, RequestUnsigned,
};
use serde_json::json;
use std::collections::{HashMap, HashSet};
use std::time::Duration;
use tokio::time::sleep;

static POLICY_STORE_RAW: &str = include_str!("../../test_files/policy-store_ok.yaml");

// NOTE: make sure you replace this with your own SSA
const SSA_JWT: &str = "eyJraWQiOiJzc2FfMmRmMGNkZDUtNTU2Yi00ZDRlLTkzNjItNjc2Mjk1NjEzMzMxX3NpZ19yczI1NiIsInR5cCI6IkpXVCIsImFsZyI6IlJTMjU2In0.eyJzb2Z0d2FyZV9pZCI6ImNlZGFybGluZyIsImdyYW50X3R5cGVzIjpbImNsaWVudF9jcmVkZW50aWFscyJdLCJvcmdfaWQiOiJteV9vcmciLCJpc3MiOiJodHRwczovL2RlbW9leGFtcGxlLmphbnMuaW8iLCJsaWZldGltZSI6MTU3Njc5OTk5OSwic29mdHdhcmVfcm9sZXMiOlsiaHR0cHM6Ly9qYW5zLmlvL29hdXRoL2xvY2svbG9nLndyaXRlIiwiaHR0cHM6Ly9qYW5zLmlvL29hdXRoL2xvY2svaGVhbHRoLndyaXRlIiwiaHR0cHM6Ly9qYW5zLmlvL29hdXRoL2xvY2svdGVsZW1ldHJ5LndyaXRlIl0sImV4cCI6MzMyMzA4NjI3NSwiaWF0IjoxNzQ2Mjg2Mjc2LCJqdGkiOiJkM2EwODI1Yi1kZjFhLTQ3ZTYtYmQ5MC0yMTk2NTkyYTVlNGQifQ.KxMQyxDZ3zsZDHj5OjZdAWi3J8fuYdxMbl2NC3fzS0e308Zd_t8CvtFX0F3edYAwvy3mdnva_MxKkxgSsXGniv2UfiFj7p8gKaqybYB4ngb1mX1BZCJZ27M0K5g9H3pa4g3csKp_UHjmV2LBHePkr3RA343E9ezDtR-4WwxQwJ6Lq_dmdXvtMW5iQLE9SsDT0f6rPNs2jt1mx-_PjT3mpOo2NG7mMB1TPX_runu53PqnJ844QoZNa1yjjIhJUGxLzE-DH8t6pjwiatnd1kDS7jDhfAn41l-t29IraIpYKTmPEGNxKd6EkIr-j7Si54HPIJZoZXY8E-UEnLHwDNo7hQ";

#[tokio::main(flavor = "current_thread")]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // This configuration is specifically for the lock server interaction
    let lock_config = LockServiceConfig {
        log_level: LogLevel::TRACE,
        config_uri: "https://demoexample.jans.io/.well-known/lock-server-configuration"
            .parse()
            .unwrap(),
        dynamic_config: false,
        ssa_jwt: Some(SSA_JWT.to_string()),
        log_interval: Some(Duration::from_secs(3)), // send logs every 3 secs
        health_interval: None,                      // don't send healthchecks
        telemetry_interval: None,                   // don't send telemetry
        listen_sse: false,
        accept_invalid_certs: true,
        transport: LockTransport::Rest,
        ..Default::default()
    };

    let cedarling = Cedarling::new(&BootstrapConfig {
        application_name: "test_app".to_string(),
        log_config: LogConfig {
            log_type: LogTypeConfig::StdOut(StdOutLoggerMode::Immediate),
            log_level: LogLevel::INFO,
        },
        policy_store_config: PolicyStoreConfig {
            source: PolicyStoreSource::Yaml(POLICY_STORE_RAW.to_string()),
            ..Default::default()
        },
        jwt_config: JwtConfig {
            jwks: None,
            jwt_sig_validation: false,
            jwt_status_validation: false,
            signature_algorithms_supported: HashSet::new(),
            ..Default::default()
        }
        .allow_all_algorithms(),
        authorization_config: AuthorizationConfig {
            decision_log_default_jwt_id: "jti".to_string(),
            strict_schema_validation: true,
        },
        lock_config: Some(lock_config),
        max_default_entities: None,
        max_base64_size: None,
        data_store_config: DataStoreConfig::default(),
        http_client_config: HttpClientConfig::default(),
    })
    .await?;

    let principal = EntityData {
        cedar_mapping: CedarEntityMapping {
            entity_type: "Jans::User".to_string(),
            id: "some_user".to_string(),
        },
        attributes: HashMap::from([
            ("sub".to_string(), json!("some_sub")),
            ("email".to_string(), json!("email@email.com")),
            ("username".to_string(), json!("some_username")),
            ("country".to_string(), json!("US")),
            ("role".to_string(), json!("SuperUser")),
        ]),
    };

    let result = cedarling
        .authorize_unsigned(RequestUnsigned {
            principal: Some(principal),
            action: "Jans::Action::\"Update\"".to_string(),
            context: serde_json::json!({}),
            resource: EntityData {
                cedar_mapping: CedarEntityMapping {
                    entity_type: "Jans::Issue".to_string(),
                    id: "random_id".to_string(),
                },
                attributes: HashMap::from_iter([
                    (
                        "org_id".to_string(),
                        serde_json::Value::String("some_long_id".to_string()),
                    ),
                    (
                        "country".to_string(),
                        serde_json::Value::String("US".to_string()),
                    ),
                ]),
            },
        })
        .await;

    match result {
        Ok(result) => {
            println!("\n\nis allowed: {}", result.decision);
        },
        Err(e) => eprintln!("Error while authorizing: {e}\n {e:?}\n\n"),
    }

    // we sleep for a bit so we don't exit before any logs are sent
    println!("sleeping for 5 secs to give time for Cedarling's Lock service to send the logs");
    sleep(Duration::from_secs(5)).await;

    println!("logs should be sent to the lock server by now!");

    // make sure to call shut_down() to flush any remaining logs
    cedarling.shut_down().await;
    println!("exiting");

    Ok(())
}