Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Upgraded cedar to 4.2.0 #83

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
268 changes: 129 additions & 139 deletions Cargo.lock

Large diffs are not rendered by default.

10 changes: 5 additions & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ bench = false
async-trait = "0.1.71"
chrono = "0.4.26"
derive_builder = "0.12.0"
futures = { version = "0.3.28", features = ["std"] }
futures = { version = "0.3.31", features = ["std"] }
fs2 = "0.4.3"
once_cell = "1.18.0"
rand = "0.8.5"
Expand All @@ -33,10 +33,10 @@ tracing-core = "0.1.31"
tracing-subscriber = "0.3.17"

# Cedar
cedar-policy = "3.1.0"
cedar-policy-core = "3.1.0"
cedar-policy-formatter = "3.1.0"
cedar-policy-validator = "3.1.0"
cedar-policy = "4.2.0"
cedar-policy-core = "4.2.0"
cedar-policy-formatter = "4.2.0"
cedar-policy-validator = "4.2.0"

[features]
# Experimental features.
Expand Down
2 changes: 1 addition & 1 deletion benches/data_gen/policy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ impl From<Entity> for EntityUidRepr {
fn from(value: Entity) -> Self {
EntityUidRepr {
type_name: value.uid().type_name().to_string(),
id: value.uid().id().to_string(),
id: value.uid().to_string(),
}
}
}
Expand Down
3 changes: 2 additions & 1 deletion benches/data_gen/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@ use rand::Rng;

/// Alphabet as an &str
pub const ALPHA: &str = "abcdefghijklmnopqrstuvwxyz";
#[allow(clippy::single_char_add_str)]
ShiromMakkad marked this conversation as resolved.
Show resolved Hide resolved
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
}
6 changes: 3 additions & 3 deletions benches/is_authorized.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ use std::sync::Arc;

fn construct_request() -> Request {
Request::new(
Some("Principal::\"request\"".parse().unwrap()),
Some("Action::\"request\"".parse().unwrap()),
Some("Resource::\"request\"".parse().unwrap()),
"Principal::\"request\"".parse().unwrap(),
"Action::\"request\"".parse().unwrap(),
"Resource::\"request\"".parse().unwrap(),
Context::empty(),
None,
)
Expand Down
8 changes: 1 addition & 7 deletions deny.toml
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
# This file defines configuration for the cargo deny command
# Ref: https://github.com/EmbarkStudios/cargo-deny
[graph]
targets = []
l-kli marked this conversation as resolved.
Show resolved Hide resolved

[advisories]
vulnerability = "deny"
ShiromMakkad marked this conversation as resolved.
Show resolved Hide resolved
unmaintained = "deny"
notice = "deny"
unsound = "deny"
ignore = []

[bans]
Expand Down Expand Up @@ -37,9 +34,6 @@ unknown-registry = "deny"
unknown-git = "deny"

[licenses]
unlicensed = "deny"
allow-osi-fsf-free = "neither"
copyleft = "deny"
confidence-threshold = 0.93
allow = [
"Apache-2.0",
Expand Down
12 changes: 6 additions & 6 deletions src/public/file/entity_provider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use std::io::Error;
use std::sync::Arc;

use async_trait::async_trait;
use cedar_policy::{Entities, EntitiesError, Request, Schema};
use cedar_policy::{entities_errors::EntitiesError, Entities, Request, Schema};
use derive_builder::Builder;
use thiserror::Error;
use tokio::sync::RwLock;
Expand Down Expand Up @@ -172,7 +172,7 @@ impl EntityProvider {

let entities = if let Some(schema_path) = configuration.schema_path.as_ref() {
let schema_file = File::open(schema_path)?;
let schema = Schema::from_file(schema_file)
let schema = Schema::from_json_file(schema_file)
.map_err(|_schema_error| SchemaParseErrorWrapper::new(schema_path.clone()))?;
let res = Entities::from_json_file(entities_file, Some(&schema)).map_err(
|entities_error| {
Expand Down Expand Up @@ -227,7 +227,7 @@ impl UpdateProviderData for EntityProvider {
let schema_file = File::open(schema_path).map_err(|e| {
UpdateProviderDataError::General(Box::new(ProviderError::IOError(e)))
})?;
let schema = Schema::from_file(schema_file).map_err(|_| {
let schema = Schema::from_json_file(schema_file).map_err(|_| {
UpdateProviderDataError::General(Box::new(ProviderError::SchemaParseError(
schema_path.to_string(),
)))
Expand Down Expand Up @@ -335,9 +335,9 @@ mod test {
.unwrap()
.get_entities(
&Request::new(
Some(r#"User::"Eric""#.parse().unwrap()),
Some(r#"Action::"View""#.parse().unwrap()),
Some(r#"Box::"10""#.parse().unwrap()),
r#"User::"Eric""#.parse().unwrap(),
r#"Action::"View""#.parse().unwrap(),
r#"Box::"10""#.parse().unwrap(),
Context::empty(),
None,
)
Expand Down
6 changes: 3 additions & 3 deletions src/public/file/policy_set_provider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -269,9 +269,9 @@ mod test {
.unwrap()
.get_policy_set(
&Request::new(
Some(r#"User::"Adam""#.parse().unwrap()),
Some(r#"Action::"View""#.parse().unwrap()),
Some(r#"Box::"10""#.parse().unwrap()),
r#"User::"Adam""#.parse().unwrap(),
r#"Action::"View""#.parse().unwrap(),
r#"Box::"10""#.parse().unwrap(),
Context::empty(),
None,
)
Expand Down
2 changes: 1 addition & 1 deletion src/public/log/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,

Expand Down
94 changes: 57 additions & 37 deletions src/public/log/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,10 @@ const VENDOR_NAME: &str = "cedar::simple::authorizer";
const SECRET_STRING: &str = "Sensitive<REDACTED>";

/// 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.
///
/// <https://schema.ocsf.io/1.0.0/classes/entity_management?extensions=>
#[derive(Default, Builder, Serialize, Deserialize, Eq, PartialEq, Debug, Clone)]
#[builder(
Expand All @@ -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<String>,
Expand All @@ -69,7 +71,7 @@ pub struct OpenCyberSecurityFramework {
#[builder(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub count: Option<u64>,
/// 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<i64>,
Expand Down Expand Up @@ -105,7 +107,7 @@ pub struct OpenCyberSecurityFramework {
#[builder(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub raw_data: Option<String>,
/// 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<String>,
Expand All @@ -115,7 +117,7 @@ pub struct OpenCyberSecurityFramework {
#[builder(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub start_time: Option<i64>,
/// 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<String>,
Expand All @@ -137,9 +139,9 @@ pub struct OpenCyberSecurityFramework {
#[serde(skip_serializing_if = "Option::is_none")]
pub timezone_offset: Option<i32>,
/// 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<String>,
Expand Down Expand Up @@ -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
l-kli marked this conversation as resolved.
Show resolved Hide resolved
EmailAddress = 5,
/// Uniform Resource Locator (URL) string
URLString = 6,
Expand All @@ -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/)
Expand Down Expand Up @@ -691,7 +693,7 @@ pub struct Reputation {
#[builder(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub provider: Option<String>,
/// 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")]
Expand Down Expand Up @@ -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.
///
/// <https://schema.ocsf.io/1.0.0/objects/feature?extensions=>
#[derive(Default, Serialize, Deserialize, Builder, Eq, PartialEq, Debug, Clone)]
#[builder(setter(into))]
Expand Down Expand Up @@ -882,20 +885,20 @@ 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,
}

impl Display for EntityComponent {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Self::Concrete(euid) => {
write!(f, "{}", euid.id())
write!(f, "{}", euid.id().escaped())
}
Self::None => {
write!(f, "{SECRET_STRING}")
Expand All @@ -920,7 +923,7 @@ impl EntityComponent {
/// Gets the Id of the component.
pub fn get_id(&self) -> String {
match self {
Self::Concrete(euid) => euid.id().to_string(),
Self::Concrete(euid) => euid.to_string(),
Self::None => SECRET_STRING.to_string(),
Self::Unspecified => "*".to_string(),
}
Expand All @@ -939,10 +942,9 @@ mod test {
use std::str::FromStr;

use cedar_policy::{
AuthorizationError, Context, Entities, EntityId, EntityTypeName, EntityUid,
EvaluationError, PolicyId, Request, Response,
AuthorizationError, Authorizer, Context, Entities, EntityId, EntityTypeName, EntityUid,
PolicyId, PolicySet, Request, Response,
};
use cedar_policy_core::ast::{PolicyID, RestrictedExprError, Value};
use cedar_policy_core::authorizer::Decision;
use serde_json::{from_str, to_string, to_value, Map};

Expand All @@ -957,6 +959,8 @@ mod test {
};
use crate::public::log::{FieldLevel, FieldSet, FieldSetBuilder};

use super::build_ocsf_severity;

fn generate_metadata() -> MetaData {
return MetaDataBuilder::default()
.version("1.0.0")
Expand Down Expand Up @@ -1011,9 +1015,9 @@ mod test {
}

fn generate_mock_request(principal_name: &str) -> Request {
let principal = Some(generate_entity_uid(principal_name));
let action = Some(generate_entity_uid("read"));
let resource = Some(generate_entity_uid("Box"));
let principal = generate_entity_uid(principal_name);
let action = generate_entity_uid("read");
let resource = generate_entity_uid("Box");

Request::new(principal, action, resource, Context::empty(), None).unwrap()
}
Expand Down Expand Up @@ -1075,15 +1079,29 @@ mod test {
policy_ids.insert(PolicyId::from_str("policy1").unwrap());
policy_ids.insert(PolicyId::from_str("policy2").unwrap());
ShiromMakkad marked this conversation as resolved.
Show resolved Hide resolved

let errors = (0..num_of_error)
.map(|i| AuthorizationError::PolicyEvaluationError {
id: PolicyID::from_string(format!("policy{i}")),
error: EvaluationError::from(RestrictedExprError::InvalidRestrictedExpression {
feature: Default::default(),
expr: Value::from(true).into(),
}),
})
.collect();
let authorizer = Authorizer::new();
let policy_set = PolicySet::from_str(
r"permit(
principal,
action,
resource
) when {
resource.admins.contains(principal)
};",
)
.unwrap();

let euid_type = EntityTypeName::from_str("Veris::User").unwrap();
let euid_id = EntityId::from_str("test").unwrap();
let euid = EntityUid::from_type_name_and_id(euid_type, euid_id);

let request =
Request::new(euid.clone(), euid.clone(), euid, Context::empty(), None).unwrap();

let auth_res = authorizer.is_authorized(&request, &policy_set, &Entities::empty());
let auth_err = auth_res.diagnostics().errors().next().unwrap();

let errors: Vec<AuthorizationError> = (0..num_of_error).map(|_| auth_err.clone()).collect();
l-kli marked this conversation as resolved.
Show resolved Hide resolved

Response::new(decision, policy_ids, errors)
}
Expand Down Expand Up @@ -1140,6 +1158,15 @@ mod test {
assert_eq!(ocsf_log.status_code.unwrap(), "Deny".to_string());
}

#[test]
fn build_ocsf_severity_multiple_errors() {
ShiromMakkad marked this conversation as resolved.
Show resolved Hide resolved
assert_eq!(build_ocsf_severity(1), (SeverityId::Low, "Low".to_string()));
assert_eq!(
build_ocsf_severity(4),
(SeverityId::Medium, "Medium".to_string())
);
}

#[test]
fn activity_id_conversion() {
assert_eq!(ActivityId::from("update".to_string()), ActivityId::Update);
Expand Down Expand Up @@ -1419,14 +1446,7 @@ mod test {
EntityTypeName::from_str("Photo").unwrap(),
EntityId::from_str("vacation.jpg").unwrap(),
);
Request::new(
Some(principal),
Some(action),
Some(resource),
Context::empty(),
None,
)
.unwrap()
Request::new(principal, action, resource, Context::empty(), None).unwrap()
}

fn create_mock_entities() -> Entities {
Expand Down
Loading
Loading