Skip to content
Closed
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
c5a7f04
updated slot types and included id within entity reference
alanwang67 Jun 23, 2025
cf5f7a1
make imports consistent
alanwang67 Jun 23, 2025
92db238
added comment
alanwang67 Jun 23, 2025
6e439ce
updates to proto policy.rs, currently doesn't account for generalized…
alanwang67 Jun 24, 2025
0a9a000
added comments & edited function to return a pointer
alanwang67 Jun 24, 2025
6dc4a83
changed to using as_ref
alanwang67 Jun 24, 2025
687984e
updates to Cedar's templates & policy structures to now store general…
alanwang67 Jun 27, 2025
ba8691f
removed comment
alanwang67 Jun 27, 2025
9878233
clippy lint fixes
alanwang67 Jun 27, 2025
381ed42
changed into to use from
alanwang67 Jun 27, 2025
697df9b
changed unwrap to expect
alanwang67 Jun 27, 2025
b446dfd
generalized templates with schemas
alanwang67 Jul 8, 2025
620d0e2
updates to validator generalized slots annotation structure
alanwang67 Jul 8, 2025
ff3a4bd
fixed some feature flag errors & refactored typechecker
alanwang67 Jul 8, 2025
2863d73
updates to protobuf formats
alanwang67 Jul 9, 2025
5aac5e5
Added test for typechecking of types that shadow primitive types
alanwang67 Jul 9, 2025
ca8b85c
fixed loop
alanwang67 Jul 9, 2025
d16a6e7
fixed test that failed because of schema change
alanwang67 Jul 9, 2025
40c69e6
put back function that is used in cedar symcc
alanwang67 Jul 16, 2025
bcbec32
updates to remove generalized slots from appearing in the scope of th…
alanwang67 Jul 28, 2025
9e2073e
added some more test cases for namespaces and included schema for lin…
alanwang67 Jul 28, 2025
564a027
updates to error handling to reflect new changes
alanwang67 Jul 28, 2025
6880e82
link time type checking updates
alanwang67 Jul 29, 2025
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
4 changes: 2 additions & 2 deletions cedar-policy-core/src/ast/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,7 @@ impl<T> Expr<T> {
self.subexpressions()
.filter_map(|exp| match &exp.expr_kind {
ExprKind::Slot(slotid) => Some(Slot {
id: *slotid,
id: slotid.clone(),
loc: exp.source_loc().into_maybe_loc(),
}),
_ => None,
Expand Down Expand Up @@ -1842,7 +1842,7 @@ mod test {
let e = Expr::slot(SlotId::principal());
let p = SlotId::principal();
let r = SlotId::resource();
let set: HashSet<SlotId> = HashSet::from_iter([p]);
let set: HashSet<SlotId> = HashSet::from_iter([p.clone()]);
assert_eq!(set, e.slots().map(|slot| slot.id).collect::<HashSet<_>>());
let e = Expr::or(
Expr::slot(SlotId::principal()),
Expand Down
2 changes: 1 addition & 1 deletion cedar-policy-core/src/ast/expr_visitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ pub trait ExprVisitor {
match expr.expr_kind() {
ExprKind::Lit(lit) => self.visit_literal(lit, loc),
ExprKind::Var(var) => self.visit_var(*var, loc),
ExprKind::Slot(slot) => self.visit_slot(*slot, loc),
ExprKind::Slot(slot) => self.visit_slot(slot.clone(), loc),
ExprKind::Unknown(unknown) => self.visit_unknown(unknown, loc),
ExprKind::If {
test_expr,
Expand Down
26 changes: 23 additions & 3 deletions cedar-policy-core/src/ast/name.rs
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,7 @@ impl<'de> Deserialize<'de> for InternalName {
/// Clone is O(1).
// This simply wraps a separate enum -- currently [`ValidSlotId`] -- in case we
// want to generalize later
#[derive(Debug, Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
#[serde(transparent)]
pub struct SlotId(pub(crate) ValidSlotId);

Expand All @@ -298,6 +298,11 @@ impl SlotId {
Self(ValidSlotId::Resource)
}

/// Create a `generalized slot`
pub fn generalized_slot(id: Id) -> Self {
Self(ValidSlotId::GeneralizedSlot(id))
}

/// Check if a slot represents a principal
pub fn is_principal(&self) -> bool {
matches!(self, Self(ValidSlotId::Principal))
Expand All @@ -307,6 +312,19 @@ impl SlotId {
pub fn is_resource(&self) -> bool {
matches!(self, Self(ValidSlotId::Resource))
}

/// Check if a slot represents a generalized slot
pub fn is_generalized_slot(&self) -> bool {
matches!(self, Self(ValidSlotId::GeneralizedSlot(_)))
}

/// Returns the id if the slot is a `generalized slot`
pub fn extract_id_out_of_generalized_slot(&self) -> Option<&Id> {
match self {
Self(ValidSlotId::GeneralizedSlot(id)) => Some(id),
_ => None,
}
}
}

impl From<PrincipalOrResource> for SlotId {
Expand All @@ -324,20 +342,22 @@ impl std::fmt::Display for SlotId {
}
}

/// Two possible variants for Slots
#[derive(Debug, Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
/// Three possible variants for Slots
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub(crate) enum ValidSlotId {
#[serde(rename = "?principal")]
Principal,
#[serde(rename = "?resource")]
Resource,
GeneralizedSlot(Id), // Slots for generalized templates, for more info see [RFC 98](https://github.com/cedar-policy/rfcs/pull/98).
}

impl std::fmt::Display for ValidSlotId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let s = match self {
ValidSlotId::Principal => "principal",
ValidSlotId::Resource => "resource",
ValidSlotId::GeneralizedSlot(id) => id.as_ref(),
};
write!(f, "?{s}")
}
Expand Down
95 changes: 54 additions & 41 deletions cedar-policy-core/src/ast/policy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -293,8 +293,8 @@ impl Template {
Ok(())
} else {
Err(LinkingError::from_unbound_and_extras(
unbound.into_iter().map(|slot| slot.id),
extra.into_iter().copied(),
unbound.into_iter().map(|slot| slot.id.clone()),
extra.into_iter().cloned(),
))
}
}
Expand Down Expand Up @@ -1331,9 +1331,9 @@ impl PrincipalConstraint {
}

/// Constrained to be equal to a slot
pub fn is_eq_slot() -> Self {
pub fn is_eq_slot(id: Option<Id>) -> Self {
Self {
constraint: PrincipalOrResourceConstraint::is_eq_slot(),
constraint: PrincipalOrResourceConstraint::is_eq_slot(id),
}
}

Expand All @@ -1345,16 +1345,16 @@ impl PrincipalConstraint {
}

/// Hierarchical constraint to Slot
pub fn is_in_slot() -> Self {
pub fn is_in_slot(id: Option<Id>) -> Self {
Self {
constraint: PrincipalOrResourceConstraint::is_in_slot(),
constraint: PrincipalOrResourceConstraint::is_in_slot(id),
}
}

/// Type constraint additionally constrained to be in a slot.
pub fn is_entity_type_in_slot(entity_type: Arc<EntityType>) -> Self {
pub fn is_entity_type_in_slot(entity_type: Arc<EntityType>, id: Option<Id>) -> Self {
Self {
constraint: PrincipalOrResourceConstraint::is_entity_type_in_slot(entity_type),
constraint: PrincipalOrResourceConstraint::is_entity_type_in_slot(entity_type, id),
}
}

Expand All @@ -1375,10 +1375,10 @@ impl PrincipalConstraint {
/// Fill in the Slot, if any, with the given EUID
pub fn with_filled_slot(self, euid: Arc<EntityUID>) -> Self {
match self.constraint {
PrincipalOrResourceConstraint::Eq(EntityReference::Slot(_)) => Self {
PrincipalOrResourceConstraint::Eq(EntityReference::Slot(_, _)) => Self {
constraint: PrincipalOrResourceConstraint::Eq(EntityReference::EUID(euid)),
},
PrincipalOrResourceConstraint::In(EntityReference::Slot(_)) => Self {
PrincipalOrResourceConstraint::In(EntityReference::Slot(_, _)) => Self {
constraint: PrincipalOrResourceConstraint::In(EntityReference::EUID(euid)),
},
_ => self,
Expand Down Expand Up @@ -1438,16 +1438,16 @@ impl ResourceConstraint {
}

/// Constrained to equal a slot.
pub fn is_eq_slot() -> Self {
pub fn is_eq_slot(id: Option<Id>) -> Self {
Self {
constraint: PrincipalOrResourceConstraint::is_eq_slot(),
constraint: PrincipalOrResourceConstraint::is_eq_slot(id),
}
}

/// Constrained to be in a slot
pub fn is_in_slot() -> Self {
pub fn is_in_slot(id: Option<Id>) -> Self {
Self {
constraint: PrincipalOrResourceConstraint::is_in_slot(),
constraint: PrincipalOrResourceConstraint::is_in_slot(id),
}
}

Expand All @@ -1459,9 +1459,9 @@ impl ResourceConstraint {
}

/// Type constraint additionally constrained to be in a slot.
pub fn is_entity_type_in_slot(entity_type: Arc<EntityType>) -> Self {
pub fn is_entity_type_in_slot(entity_type: Arc<EntityType>, id: Option<Id>) -> Self {
Self {
constraint: PrincipalOrResourceConstraint::is_entity_type_in_slot(entity_type),
constraint: PrincipalOrResourceConstraint::is_entity_type_in_slot(entity_type, id),
}
}

Expand All @@ -1482,10 +1482,10 @@ impl ResourceConstraint {
/// Fill in the Slot, if any, with the given EUID
pub fn with_filled_slot(self, euid: Arc<EntityUID>) -> Self {
match self.constraint {
PrincipalOrResourceConstraint::Eq(EntityReference::Slot(_)) => Self {
PrincipalOrResourceConstraint::Eq(EntityReference::Slot(_, _)) => Self {
constraint: PrincipalOrResourceConstraint::Eq(EntityReference::EUID(euid)),
},
PrincipalOrResourceConstraint::In(EntityReference::Slot(_)) => Self {
PrincipalOrResourceConstraint::In(EntityReference::Slot(_, _)) => Self {
constraint: PrincipalOrResourceConstraint::In(EntityReference::EUID(euid)),
},
_ => self,
Expand All @@ -1511,6 +1511,7 @@ pub enum EntityReference {
EUID(Arc<EntityUID>),
/// Template Slot
Slot(
Option<Id>, // If a slot stores None then it is a principal/resource, otherwise it is a generalized slot
#[educe(PartialEq(ignore))]
#[educe(PartialOrd(ignore))]
#[educe(Hash(ignore))]
Expand All @@ -1532,7 +1533,13 @@ impl EntityReference {
pub fn into_expr(&self, slot: SlotId) -> Expr {
match self {
EntityReference::EUID(euid) => Expr::val(euid.clone()),
EntityReference::Slot(loc) => Expr::slot(slot).with_maybe_source_loc(loc.clone()),
EntityReference::Slot(id, loc) => {
let slot = match id {
Some(id) => SlotId::generalized_slot(id.clone()),
None => slot,
};
Expr::slot(slot).with_maybe_source_loc(loc.clone())
}
}
}
}
Expand Down Expand Up @@ -1628,13 +1635,13 @@ impl PrincipalOrResourceConstraint {
}

/// Constrained to equal a slot
pub fn is_eq_slot() -> Self {
PrincipalOrResourceConstraint::Eq(EntityReference::Slot(None))
pub fn is_eq_slot(id: Option<Id>) -> Self {
PrincipalOrResourceConstraint::Eq(EntityReference::Slot(id, None))
}

/// Constrained to be in a slot
pub fn is_in_slot() -> Self {
PrincipalOrResourceConstraint::In(EntityReference::Slot(None))
pub fn is_in_slot(id: Option<Id>) -> Self {
PrincipalOrResourceConstraint::In(EntityReference::Slot(id, None))
}

/// Hierarchical constraint.
Expand All @@ -1643,8 +1650,8 @@ impl PrincipalOrResourceConstraint {
}

/// Type constraint additionally constrained to be in a slot.
pub fn is_entity_type_in_slot(entity_type: Arc<EntityType>) -> Self {
PrincipalOrResourceConstraint::IsIn(entity_type, EntityReference::Slot(None))
pub fn is_entity_type_in_slot(entity_type: Arc<EntityType>, id: Option<Id>) -> Self {
PrincipalOrResourceConstraint::IsIn(entity_type, EntityReference::Slot(id, None))
}

/// Type constraint with a hierarchical constraint.
Expand Down Expand Up @@ -1705,11 +1712,11 @@ impl PrincipalOrResourceConstraint {
match self {
PrincipalOrResourceConstraint::Any => None,
PrincipalOrResourceConstraint::In(EntityReference::EUID(euid)) => Some(euid),
PrincipalOrResourceConstraint::In(EntityReference::Slot(_)) => None,
PrincipalOrResourceConstraint::In(EntityReference::Slot(_, _)) => None,
PrincipalOrResourceConstraint::Eq(EntityReference::EUID(euid)) => Some(euid),
PrincipalOrResourceConstraint::Eq(EntityReference::Slot(_)) => None,
PrincipalOrResourceConstraint::Eq(EntityReference::Slot(_, _)) => None,
PrincipalOrResourceConstraint::IsIn(_, EntityReference::EUID(euid)) => Some(euid),
PrincipalOrResourceConstraint::IsIn(_, EntityReference::Slot(_)) => None,
PrincipalOrResourceConstraint::IsIn(_, EntityReference::Slot(_, _)) => None,
PrincipalOrResourceConstraint::Is(_) => None,
}
}
Expand Down Expand Up @@ -1966,9 +1973,9 @@ pub(crate) mod test_generators {
let v = vec![
PrincipalOrResourceConstraint::any(),
PrincipalOrResourceConstraint::is_eq(euid.clone()),
PrincipalOrResourceConstraint::Eq(EntityReference::Slot(None)),
PrincipalOrResourceConstraint::Eq(EntityReference::Slot(None, None)),
PrincipalOrResourceConstraint::is_in(euid),
PrincipalOrResourceConstraint::In(EntityReference::Slot(None)),
PrincipalOrResourceConstraint::In(EntityReference::Slot(None, None)),
];

v.into_iter()
Expand Down Expand Up @@ -2057,7 +2064,7 @@ mod test {
let t = Arc::new(template);
let env = t
.slots()
.map(|slot| (slot.id, EntityUID::with_eid("eid")))
.map(|slot| (slot.id.clone(), EntityUID::with_eid("eid")))
.collect();
let _ = Template::link(t, PolicyID::from_string("id"), env).expect("Linking failed");
}
Expand Down Expand Up @@ -2110,7 +2117,7 @@ mod test {
None,
Annotations::new(),
Effect::Forbid,
PrincipalConstraint::is_eq_slot(),
PrincipalConstraint::is_eq_slot(None),
ActionConstraint::Any,
ResourceConstraint::any(),
Expr::val(true),
Expand All @@ -2132,9 +2139,9 @@ mod test {
None,
Annotations::new(),
Effect::Forbid,
PrincipalConstraint::is_eq_slot(),
PrincipalConstraint::is_eq_slot(None),
ActionConstraint::Any,
ResourceConstraint::is_in_slot(),
ResourceConstraint::is_in_slot(None),
Expr::val(true),
));
assert_matches!(Template::link(t.clone(), iid.clone(), HashMap::new()), Err(LinkingError::ArityError { unbound_values, extra_values }) => {
Expand All @@ -2158,9 +2165,9 @@ mod test {
None,
Annotations::new(),
Effect::Permit,
PrincipalConstraint::is_in_slot(),
PrincipalConstraint::is_in_slot(None),
ActionConstraint::any(),
ResourceConstraint::is_eq_slot(),
ResourceConstraint::is_eq_slot(None),
Expr::val(true),
));

Expand Down Expand Up @@ -2231,15 +2238,15 @@ mod test {
Some(&e)
);
assert_eq!(
PrincipalOrResourceConstraint::In(EntityReference::Slot(None)).get_euid(),
PrincipalOrResourceConstraint::In(EntityReference::Slot(None, None)).get_euid(),
None
);
assert_eq!(
PrincipalOrResourceConstraint::Eq(EntityReference::EUID(e.clone())).get_euid(),
Some(&e)
);
assert_eq!(
PrincipalOrResourceConstraint::Eq(EntityReference::Slot(None)).get_euid(),
PrincipalOrResourceConstraint::Eq(EntityReference::Slot(None, None)).get_euid(),
None
);
assert_eq!(
Expand All @@ -2257,7 +2264,7 @@ mod test {
assert_eq!(
PrincipalOrResourceConstraint::IsIn(
Arc::new("T".parse().unwrap()),
EntityReference::Slot(None)
EntityReference::Slot(None, None)
)
.get_euid(),
None
Expand Down Expand Up @@ -2318,7 +2325,7 @@ mod test {

#[test]
fn euid_into_expr() {
let e = EntityReference::Slot(None);
let e = EntityReference::Slot(None, None);
assert_eq!(
e.into_expr(SlotId::principal()),
Expr::slot(SlotId::principal())
Expand All @@ -2332,14 +2339,20 @@ mod test {

#[test]
fn por_constraint_display() {
let t = PrincipalOrResourceConstraint::Eq(EntityReference::Slot(None));
let t = PrincipalOrResourceConstraint::Eq(EntityReference::Slot(None, None));
let s = t.display(PrincipalOrResource::Principal);
assert_eq!(s, "principal == ?principal");
let t = PrincipalOrResourceConstraint::Eq(EntityReference::euid(Arc::new(
EntityUID::with_eid("test"),
)));
let s = t.display(PrincipalOrResource::Principal);
assert_eq!(s, "principal == test_entity_type::\"test\"");
let t = PrincipalOrResourceConstraint::Eq(EntityReference::Slot(
"generalized".parse().ok(),
None,
));
let s = t.display(PrincipalOrResource::Principal);
assert_eq!(s, "principal == ?generalized");
}

#[test]
Expand Down
Loading
Loading