treetop-core 0.1.0

Core library for Treetop, a Cedar policy engine implementation.
Documentation
//! Usage example:
//!
//! Here we declare a policy that allows "alice" to create a host if and only if the following conditions are met:
//! - The host's nameLabel set contains "example_domain". This is created via regular expressions on the name from
//!   the `initialize_host_patterns` function.
//! - The host's IP address is within the network "10.0.0.0/24".
//! - The host's name contains the letter 'n'
//!
//! Note that we do not require the host to have the nameLabel "webserver" for "alice" to create it.
//!
//! ```rust
//! use regex::Regex;
//! use std::sync::Arc;
//! use treetop_core::{LabelTarget, Action, AttrValue, PolicyEngine, Request, User, Principal, Resource, RegexLabeler, LabelRegistryBuilder};
//! use sha2::{Digest, Sha256};
//!
//! let policies = r#"
//! permit (
//!    principal == User::"alice",
//!    action == Action::"create_host",
//!    resource is Host
//! ) when {
//!     resource.nameLabels.contains("in_domain") &&
//!     resource.ip.isInRange(ip("10.0.0.0/24")) &&
//!     resource.name like "*n*"
//! };
//! "#;
//!
//! // Used to create attributes for hosts based on their names.
//! let patterns = vec![
//!     ("in_domain".to_string(), Regex::new(r"example\.com$").unwrap()),
//!     ("webserver".to_string(), Regex::new(r"^web-\d+").unwrap()),
//! ];
//! let label_registry = LabelRegistryBuilder::new()
//!     .add_labeler(Arc::new(RegexLabeler::new(LabelTarget::new("Host", "nameLabels").unwrap(), "name",
//!         patterns.into_iter().collect(),
//!     ).unwrap()))
//!     .build()
//!     .unwrap();
//!
//! let engine = PolicyEngine::new_from_str(&policies).unwrap()
//!     .with_label_registry(label_registry);
//!
//! let request = Request {
//!    principal: Principal::User(User::new("alice", None, None).unwrap()), // No groups, no namespace
//!    action: Action::new("create_host", None).unwrap(), // Action is not in a namespace
//!    resource: Resource::new("Host", "hostname.example.com").unwrap()
//!     .with_attr("name", AttrValue::String("hostname.example.com".into()))
//!     .with_attr("ip", AttrValue::ip("10.0.0.1").unwrap())
//! };
//!
//! let decision = engine.evaluate(&request).unwrap();
//! assert!(decision.is_allowed());
//!
//! // List all of alice's policies
//! let alice_policies = engine.list_policies_for_user("alice", &[], &[]).unwrap();
//! // This value is also seralizable to JSON
//! let json = serde_json::to_string(&alice_policies).unwrap();
//!
//! // Check that the policy running is the expected version
//! let expected_hash = Sha256::digest(policies)
//!     .iter()
//!     .fold(String::with_capacity(64), |mut s, b| {
//!         use std::fmt::Write;
//!         write!(s, "{b:02x}").unwrap();
//!         s
//!     });
//! assert_eq!(engine.current_version().hash.as_ref(), expected_hash);
//!
//! ```
//!
//! ## Thread-Safe Sharing
//!
//! For multithreaded applications, wrap `PolicyEngine` in `Arc` to share it across threads:
//!
//! ```rust,no_run
//! use std::sync::Arc;
//! use std::thread;
//! # use treetop_core::{PolicyEngine, Request, Principal, User, Action, Resource, Decision};
//! # let engine_base = PolicyEngine::new_from_str("permit(principal,action,resource);").unwrap();
//!
//! let engine = Arc::new(engine_base);
//! let engine_clone = Arc::clone(&engine);
//!
//! let handle = thread::spawn(move || {
//!     // Evaluate policies in a background thread
//!     let request = Request {
//!         principal: Principal::User(User::new("user", None, None).unwrap()),
//!         action: Action::new("read", None).unwrap(),
//!         resource: Resource::new("Document", "doc1").unwrap(),
//!     };
//!     let _decision = engine_clone.evaluate(&request);
//! });
//!
//! handle.join().unwrap();
//! ```
//!

pub use build_info::{BuildInfo, GitInfo, build_info};
pub use cedar_policy::Schema;
pub use engine::{EvaluationSession, PolicyEngine, SchemaEnforcing, SchemaFree, ValidationMode};
pub use error::PolicyError;
pub use labels::{
    LabelRegistry, LabelRegistryBuilder, LabelSetVersion, LabelTarget, Labeler, LabelerApply,
    RegexLabeler,
};
pub use loader::{compile_policy, compile_policy_with_schema};
pub use policy_store::{
    POLICY_STORE_ANNOTATION, PolicyStoreConfig, PolicyStoreId, PolicyStoreLayout,
};
pub use types::{
    Action, AttrValue, CedarIp, CedarType, Decision, DecisionDiagnostics, DecisionDto, Group,
    Groups, PermitPolicies, PermitPolicy, PolicyCandidates, PolicyEffectFilter, PolicyMatch,
    PolicyMatchReason, PolicyVersion, Principal, Request, RequestContext, Resource, User,
    action_entity_uid, group_entity_uid, namespace_segments, resource_entity_uid, user_entity_uid,
};

#[cfg(feature = "observability")]
pub use metrics::{
    EvaluationObservation, EvaluationPhases, EvaluationStats, MetricsSink, ReloadStats, set_sink,
};
#[cfg(feature = "bench-internal")]
pub mod bench_helpers;
mod build_info;
mod engine;
mod error;
mod labels;
mod loader;
#[cfg(feature = "observability")]
pub mod metrics;
#[cfg(all(not(feature = "observability"), feature = "bench-internal"))]
mod metrics;
mod policy_match;
mod policy_store;
mod query;
#[cfg(test)]
mod tests;
mod timers;
mod traits;
pub mod types;