Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 24 additions & 1 deletion cedar-policy-core/src/ast/policy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,11 @@ use miette::Diagnostic;
use nonempty::{nonempty, NonEmpty};
use serde::{Deserialize, Serialize};
use smol_str::SmolStr;
use std::{collections::HashMap, sync::Arc};
use std::{
collections::{HashMap, HashSet},
str::FromStr,
sync::Arc,
};
use thiserror::Error;

#[cfg(feature = "wasm")]
Expand Down Expand Up @@ -581,6 +585,25 @@ impl Policy {
pub fn is_static(&self) -> bool {
self.link.is_none()
}

/// Returns all the unknown entities in the policy during evaluation
pub fn unknown_entities(&self) -> HashSet<EntityUID> {
self.condition()
.unknowns()
.filter_map(
|Unknown {
name,
type_annotation,
}| {
if matches!(type_annotation, Some(Type::Entity { .. })) {
EntityUID::from_str(name.as_str()).ok()
} else {
None
}
},
)
.collect()
}
}

impl std::fmt::Display for Policy {
Expand Down
1 change: 1 addition & 0 deletions cedar-policy/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ Cedar Language Version: TBD
edge cases, policies that previously failed to validate under strict validation
will now pass validation, probably with an `ImpossiblePolicy` warning. (#1355,
resolving #638)
- Added `PartialResponse::unknown_entities` method (#1557)

### Added

Expand Down
26 changes: 12 additions & 14 deletions cedar-policy/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1059,6 +1059,15 @@ impl PartialResponse {
self.0.all_residuals().map(Policy::from_ast)
}

/// Returns all unknown entities during the evaluation of the response
pub fn unknown_entities(&self) -> HashSet<EntityUid> {
let mut entity_uids = HashSet::new();
for policy in self.0.all_residuals() {
entity_uids.extend(policy.unknown_entities().into_iter().map(Into::into));
}
entity_uids
}

/// Return the residual for a given [`PolicyId`], if it exists in the response
pub fn get(&self, id: &PolicyId) -> Option<Policy> {
self.0.get(id.as_ref()).map(Policy::from_ast)
Expand Down Expand Up @@ -3569,20 +3578,9 @@ impl Policy {
#[cfg(feature = "partial-eval")]
pub fn unknown_entities(&self) -> HashSet<EntityUid> {
self.ast
.condition()
.unknowns()
.filter_map(
|ast::Unknown {
name,
type_annotation,
}| {
if matches!(type_annotation, Some(ast::Type::Entity { .. })) {
EntityUid::from_str(name.as_str()).ok()
} else {
None
}
},
)
.unknown_entities()
.into_iter()
.map(Into::into)
.collect()
}

Expand Down
30 changes: 30 additions & 0 deletions cedar-policy/src/test/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -810,6 +810,36 @@ mod policy_set_tests {
.contains(&"test_entity_type::\"unknown\"".parse().unwrap()));
}

#[cfg(feature = "partial-eval")]
#[test]
fn partial_response_unknown_entities() {
let authorizer = Authorizer::new();
let request = Request::new(
EntityUid::from_strs("Test", "test"),
EntityUid::from_strs("Action", "a"),
EntityUid::from_strs("Resource", "b"),
Context::empty(),
None,
)
.unwrap();

let entities = Entities::default().partial();

let mut pset = PolicySet::new();
let static_policy = Policy::parse(
Some(PolicyId::new("id")),
"permit(principal,action,resource) when {principal.foo == 1};",
)
.expect("Failed to parse");
pset.add(static_policy).expect("Failed to add");

let response = authorizer.is_authorized_partial(&request, &pset, &entities);
assert_eq!(response.unknown_entities().len(), 1);
assert!(response
.unknown_entities()
.contains(&"Test::\"test\"".parse().unwrap()));
}

#[test]
fn unlink_linked_policy() {
let template = Template::parse(
Expand Down