diff --git a/benches/data_gen/utils.rs b/benches/data_gen/utils.rs index 361b69a..4f314ee 100644 --- a/benches/data_gen/utils.rs +++ b/benches/data_gen/utils.rs @@ -2,13 +2,14 @@ use rand::Rng; /// Alphabet as an &str pub const ALPHA: &str = "abcdefghijklmnopqrstuvwxyz"; +#[allow(clippy::single_char_add_str)] pub fn random_string(n: u32, charset: &str) -> String { let mut rng = rand::thread_rng(); let mut res = "".to_string(); for _i in 0..n as usize { let random_index: usize = rng.gen_range(0..charset.len()); - res.push_str(&charset.chars().nth(random_index).unwrap().to_string()); + res.push(charset.chars().nth(random_index).unwrap()); } res } diff --git a/src/public/log/mod.rs b/src/public/log/mod.rs index 7a37bbb..b82ead0 100644 --- a/src/public/log/mod.rs +++ b/src/public/log/mod.rs @@ -16,7 +16,7 @@ pub const DEFAULT_REQUESTER_NAME: &str = "cedar::simple::authorizer"; #[builder(setter(into))] pub struct Config { /// `format` is used to specify the log rotation format. - /// By default the log rotation format is OpenCyberSecurityFramework (OCSF). + /// By default the log rotation format is `OpenCyberSecurityFramework` (OCSF). #[builder(default)] pub format: Format, diff --git a/src/public/log/schema.rs b/src/public/log/schema.rs index 4f013b9..9af6143 100644 --- a/src/public/log/schema.rs +++ b/src/public/log/schema.rs @@ -31,8 +31,10 @@ const VENDOR_NAME: &str = "cedar::simple::authorizer"; const SECRET_STRING: &str = "Sensitive"; /// A basic Open Cyber Security Framework structure +/// /// Entity Management events report activity. The activity can be a /// create, read, update, and delete operation on a managed entity. +/// /// #[derive(Default, Builder, Serialize, Deserialize, Eq, PartialEq, Debug, Clone)] #[builder( @@ -53,7 +55,7 @@ pub struct OpenCyberSecurityFramework { /// The category unique identifier of the event. The authorization log will always be 3 #[builder(default = "3u8")] pub category_uid: u8, - /// The event class name, as defined by class_uid value: `Entity Management` + /// The event class name, as defined by `class_uid` value: `Entity Management` #[builder(default = "Some(\"Entity Management\".to_string())")] #[serde(skip_serializing_if = "Option::is_none")] pub class_name: Option, @@ -69,7 +71,7 @@ pub struct OpenCyberSecurityFramework { #[builder(default)] #[serde(skip_serializing_if = "Option::is_none")] pub count: Option, - /// The event duration or aggregate time, the amount of time the event covers from start_time to end_time in milliseconds + /// The event duration or aggregate time, the amount of time the event covers from `start_time` to `end_time` in milliseconds #[builder(default)] #[serde(skip_serializing_if = "Option::is_none")] pub duration: Option, @@ -105,7 +107,7 @@ pub struct OpenCyberSecurityFramework { #[builder(default)] #[serde(skip_serializing_if = "Option::is_none")] pub raw_data: Option, - /// The event severity, normalized to the caption of the severity_id value + /// The event severity, normalized to the caption of the `severity_id` value #[builder(default)] #[serde(skip_serializing_if = "Option::is_none")] pub severity: Option, @@ -115,7 +117,7 @@ pub struct OpenCyberSecurityFramework { #[builder(default)] #[serde(skip_serializing_if = "Option::is_none")] pub start_time: Option, - /// The event status, normalized to the caption of the status_id value + /// The event status, normalized to the caption of the `status_id` value #[builder(default)] #[serde(skip_serializing_if = "Option::is_none")] pub status: Option, @@ -137,9 +139,9 @@ pub struct OpenCyberSecurityFramework { #[serde(skip_serializing_if = "Option::is_none")] pub timezone_offset: Option, /// The event type ID. It identifies the event's semantics and structure. - /// the value is calculated by the logging system as: class_uid * 100 + activity_id + /// the value is calculated by the logging system as: `class_uid` * 100 + `activity_id` pub type_uid: TypeUid, - /// The event type name, as defined by the type_uid + /// The event type name, as defined by the `type_uid` #[builder(default)] #[serde(skip_serializing_if = "Option::is_none")] pub type_name: Option, @@ -548,9 +550,9 @@ pub enum ObservableTypeId { IPAddress = 2, /// Media Access Control (MAC) address. For example: 18:36:F3:98:4F:9A MACAddress = 3, - /// User name. For example: john_doe + /// User name. For example: `john_doe` UserName = 4, - /// Email address. For example: john_doe@example.com + /// Email address. For example: `john_doe@example.com` EmailAddress = 5, /// Uniform Resource Locator (URL) string URLString = 6, @@ -564,7 +566,7 @@ pub enum ObservableTypeId { ResourceUID = 10, /// Endpoints, whether physical or virtual, connect to and interact with computer networks. /// Examples include mobile devices, computers, virtual machines, embedded devices, servers, - /// and IoT devices like cameras and smart speakers + /// and `IoT` devices like cameras and smart speakers Endpoint = 20, /// The User object describes the characteristics of a user/person or a security principal. /// Defined by D3FEND [d3f:UserAccount](https://d3fend.mitre.org/dao/artifact/d3f:UserAccount/) @@ -691,7 +693,7 @@ pub struct Reputation { #[builder(default)] #[serde(skip_serializing_if = "Option::is_none")] pub provider: Option, - /// The reputation score, normalized to the caption of the score_id value. In the case of 'Other', + /// The reputation score, normalized to the caption of the `score_id` value. In the case of 'Other', /// it is defined by the event source #[builder(default)] #[serde(skip_serializing_if = "Option::is_none")] @@ -835,6 +837,7 @@ pub struct Product { /// Encompasses details related to the capabilities, components, user interface (UI) design, /// and performance upgrades associated with the feature. +/// /// #[derive(Default, Serialize, Deserialize, Builder, Eq, PartialEq, Debug, Clone)] #[builder(setter(into))] @@ -882,12 +885,12 @@ struct FilteredRequest { /// authorization decision. #[derive(Default, Debug, Clone, PartialEq, Eq)] pub(crate) enum EntityComponent { - /// A concrete EntityUID + /// A concrete `EntityUID` Concrete(EntityUid), /// An entity that is not specified / concrete. Unspecified, #[default] - /// No EntityUID because it was filtered out. + /// No `EntityUID` because it was filtered out. None, } @@ -935,15 +938,12 @@ impl From> for EntityComponent { #[cfg(test)] mod test { - use core::num; use std::collections::{HashMap, HashSet}; use std::str::FromStr; use cedar_policy::{ - AuthorizationError, Authorizer, Context, Entities, EntityId, EntityTypeName, EntityUid, - EvaluationError, PolicyId, PolicySet, Request, Response, + Context, Entities, EntityId, EntityTypeName, EntityUid, PolicyId, Request, Response, }; - use cedar_policy_core::ast::{PolicyID, RestrictedExpr, Value}; use cedar_policy_core::authorizer::Decision; use serde_json::{from_str, to_string, to_value, Map}; @@ -1088,7 +1088,7 @@ mod test { // }), // }) // .collect(); - + println!("Number of errors needed: {}", { num_of_error }); // Uses a empty vector now instead of giving num_of_error errors. Tests have been changed to reflect this // Leads to problems in test coverage Response::new(decision, policy_ids, vec![]) diff --git a/src/public/simple.rs b/src/public/simple.rs index 4952cd6..7952d9e 100644 --- a/src/public/simple.rs +++ b/src/public/simple.rs @@ -2,7 +2,7 @@ use std::sync::Arc; #[cfg(feature = "partial-eval")] -use cedar_policy::PartialResponse; +use cedar_policy::{Diagnostics, PartialResponse}; use cedar_policy::{Entities, Request, Response}; use derive_builder::Builder; use thiserror::Error; @@ -214,26 +214,31 @@ where ); info!("Fetched Authorization data from Policy Set Provider and Entity Provider"); + let concrete_response = partial_response.clone().concretize(); + // Skip logging for now info!("Generated OCSF log record."); - match &partial_response { - Concrete(response) => self.log(request, response, entities), - Residual(residual_response) => self.log_residual(request, residual_response, entities), - }; + // match &partial_response { + // Concrete(response) => self.log(request, response, entities), + // Residual(residual_response) => self.log_residual(request, residual_response, entities), + // }; + match partial_response.decision() { + Some(_) => self.log(request, &concrete_response, entities), + None => self.log_residual( + request, + concrete_response.diagnostics(), + &partial_response, + entities, + ), + } info!( - "Is_authorized_partial completed: response_decision={}", - match &partial_response { - Concrete(response) => format!("{:?}", response.decision()), - Residual(residual_response) => format!("{:?}", residual_response.residuals()), - } + "Is_authorized_partial completed: response_decision={:?}", + partial_response.decision() ); debug!( "This decision was reached because: response_diagnostics={:?}", - match &partial_response { - Concrete(response) => response.diagnostics(), - Residual(residual_response) => residual_response.diagnostics(), - } + partial_response.clone().concretize().diagnostics() ); Ok(partial_response) @@ -244,19 +249,24 @@ where fn log_residual( &self, request: &Request, - residual_response: &ResidualResponse, + // residual_response: &ResidualResponse, + diagnostics: &Diagnostics, + policies: &PartialResponse, entities: &Entities, ) { event!(target: "cedar::simple::authorizer", Level::INFO, "{}", serde_json::to_string( &OpenCyberSecurityFramework::create_generic( request, - residual_response.diagnostics(), - residual_response.residuals().policies() + //residual_response.diagnostics(), + diagnostics, + // residual_response.residuals().policies() + policies.all_residuals() .map(|policy| format!("{}", policy.id())) .collect::>() .join(", ") .as_str(), + String::from("Residuals"), entities, &self.log_config.field_set, @@ -423,7 +433,7 @@ mod test_partial { use std::sync::Arc; use async_trait::async_trait; - use cedar_policy::{Context, Entities, PartialResponse, Policy, PolicySet, Request}; + use cedar_policy::{Context, Entities, Policy, PolicyId, PolicySet, Request}; use cedar_policy_core::authorizer::Decision; use cool_asserts::assert_matches; @@ -445,8 +455,8 @@ mod test_partial { let result = authorizer .is_authorized_partial( &Request::builder() - .principal(Some(r#"User::"Mike""#.parse().unwrap())) - .action(Some(r#"Action::"View""#.parse().unwrap())) + .principal(r#"User::"Mike""#.parse().unwrap()) + .action(r#"Action::"View""#.parse().unwrap()) .context(Context::empty()) .build(), &Entities::empty(), @@ -454,9 +464,7 @@ mod test_partial { .await; assert_matches!(result, Ok(partial_response) => - assert_matches!(partial_response, PartialResponse::Concrete(response) => - assert_eq!(response.decision(), Decision::Deny) - ) + assert_eq!(partial_response.decision(), Some(Decision::Deny)) ); assert_eq!(authorizer.log_config.requester, DEFAULT_REQUESTER_NAME); assert!(!authorizer.log_config.field_set.principal); @@ -475,8 +483,8 @@ mod test_partial { let result = authorizer .is_authorized_partial( &Request::builder() - .action(Some(r#"Action::"View""#.parse().unwrap())) - .resource(Some(r#"Box::"10""#.parse().unwrap())) + .action(r#"Action::"View""#.parse().unwrap()) + .resource(r#"Box::"10""#.parse().unwrap()) .context(Context::empty()) .build(), &Entities::empty(), @@ -484,9 +492,7 @@ mod test_partial { .await; assert_matches!(result, Ok(partial_response) => - assert_matches!(partial_response, PartialResponse::Concrete(response) => - assert_eq!(response.decision(), Decision::Deny) - ) + assert_eq!(partial_response.decision(), Some(Decision::Deny)) ); assert_eq!(authorizer.log_config.requester, DEFAULT_REQUESTER_NAME); assert!(!authorizer.log_config.field_set.principal); @@ -502,7 +508,7 @@ mod test_partial { _: &Request, ) -> Result, PolicySetProviderError> { let policy = Policy::parse( - Some("test".into()), + Some(PolicyId::new("test")), r#"permit(principal == User::"Mike", action, resource == Box::"10");"#, ) .expect("Failed to parse"); @@ -526,8 +532,8 @@ mod test_partial { let result = authorizer .is_authorized_partial( &Request::builder() - .action(Some(r#"Action::"View""#.parse().unwrap())) - .resource(Some(r#"Box::"10""#.parse().unwrap())) + .action(r#"Action::"View""#.parse().unwrap()) + .resource(r#"Box::"10""#.parse().unwrap()) .context(Context::empty()) .build(), &Entities::empty(), @@ -535,9 +541,7 @@ mod test_partial { .await; assert_matches!(result, Ok(partial_response) => - assert_matches!(partial_response, PartialResponse::Residual(residual_response) => { - assert_eq!(residual_response.residuals().policies().count(), 1); - }) + assert_eq!(partial_response.all_residuals().count(), 1) ); assert_eq!(authorizer.log_config.requester, DEFAULT_REQUESTER_NAME); assert!(!authorizer.log_config.field_set.principal);