Skip to content

Commit 0c26f7d

Browse files
authored
Prep for v3.4.0 release (#1193)
2 parents 9aafdfe + e5aeccb commit 0c26f7d

File tree

13 files changed

+551
-54
lines changed

13 files changed

+551
-54
lines changed

cedar-policy-cli/Cargo.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ name = "cedar-policy-cli"
33
edition = "2021"
44
rust-version = "1.76.0" # minimum supported Rust version is currently 1.76.0 because `cedar-policy-core` requirement. Check with `cargo install cargo-msrv && cargo msrv --min 1.75.0`
55

6-
version = "3.3.0"
6+
version = "3.4.0"
77
license = "Apache-2.0"
88
categories = ["compilers", "config"]
99
description = "CLI interface for the Cedar Policy language."
@@ -12,8 +12,8 @@ homepage = "https://cedarpolicy.com"
1212
repository = "https://github.com/cedar-policy/cedar"
1313

1414
[dependencies]
15-
cedar-policy = { version = "=3.3.0", path = "../cedar-policy" }
16-
cedar-policy-formatter = { version = "=3.3.0", path = "../cedar-policy-formatter" }
15+
cedar-policy = { version = "=3.4.0", path = "../cedar-policy" }
16+
cedar-policy-formatter = { version = "=3.4.0", path = "../cedar-policy-formatter" }
1717
clap = { version = "4", features = ["derive", "env"] }
1818
serde = { version = "1.0", features = ["derive"] }
1919
serde_json = "1.0"

cedar-policy-core/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ edition = "2021"
44
rust-version = "1.76.0" # minimum supported Rust version is currently 1.76.0 because of use of `Arc::unwrap_or_clone()`. Check with `cargo install cargo-msrv && cargo msrv --min 1.75.0`
55
build = "build.rs"
66

7-
version = "3.3.0"
7+
version = "3.4.0"
88
license = "Apache-2.0"
99
categories = ["compilers", "config"]
1010
description = "Core implemenation of the Cedar Policy language."

cedar-policy-core/src/ast/entity.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,8 +287,17 @@ pub struct Entity {
287287
ancestors: HashSet<EntityUID>,
288288
}
289289

290+
impl std::hash::Hash for Entity {
291+
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
292+
self.uid.hash(state);
293+
}
294+
}
295+
290296
impl Entity {
291297
/// Create a new `Entity` with this UID, attributes, and ancestors
298+
///
299+
/// # Errors
300+
/// - Will error if any of the [`RestrictedExpr]`s in `attrs` error when evaluated
292301
pub fn new(
293302
uid: EntityUID,
294303
attrs: HashMap<SmolStr, RestrictedExpr>,
@@ -316,6 +325,13 @@ impl Entity {
316325
})
317326
}
318327

328+
/// Create a new `Entity` with this UID, ancestors, and an empty set of attributes
329+
///
330+
/// Since there are no attributes, this method does not error, and returns `Self` instead of `Result<Self>`
331+
pub fn new_empty_attrs(uid: EntityUID, ancestors: HashSet<EntityUID>) -> Self {
332+
Self::new_with_attr_partial_value(uid, HashMap::new(), ancestors)
333+
}
334+
319335
/// Create a new `Entity` with this UID, attributes, and ancestors.
320336
///
321337
/// Unlike in `Entity::new()`, in this constructor, attributes are expressed

cedar-policy-core/src/entities.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,13 @@ impl Entities {
154154
///
155155
/// If you pass `TCComputation::AssumeAlreadyComputed`, then the caller is
156156
/// responsible for ensuring that TC and DAG hold before calling this method.
157+
///
158+
/// # Errors
159+
/// - [`EntitiesError::Duplicate`] if there are any duplicate entities in `entities`
160+
/// - [`EntitiesError::TransitiveClosureError`] if `tc_computation ==
161+
/// TCComputation::EnforceAlreadyComputed` and the entities are not transitivly closed
162+
/// - [`EntitiesError::InvalidEntity`] if `schema` is not none and any entities do not conform
163+
/// to the schema
157164
pub fn from_entities(
158165
entities: impl IntoIterator<Item = Entity>,
159166
schema: Option<&impl Schema>,
@@ -809,7 +816,7 @@ mod json_parsing_tests {
809816
parser.from_json_value(json).expect("JSON is correct")
810817
}
811818

812-
/// Ensure the initial conditions of the entiites still hold
819+
/// Ensure the initial conditions of the entities still hold
813820
fn simple_entities_still_sane(e: &Entities) {
814821
let bob = r#"Test::"bob""#.parse().unwrap();
815822
let alice = e.entity(&r#"Test::"alice""#.parse().unwrap()).unwrap();

cedar-policy-core/src/entities/conformance.rs

Lines changed: 64 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
* limitations under the License.
1515
*/
1616

17+
use std::collections::BTreeMap;
18+
1719
use super::{
1820
schematype_of_restricted_expr, EntityTypeDescription, GetSchemaTypeError,
1921
HeterogeneousSetError, Schema, SchemaType, TypeMismatchError,
@@ -291,6 +293,60 @@ pub fn typecheck_value_against_schematype(
291293
}
292294
}
293295

296+
/// Check whether the given `RestrictedExpr` is a valid instance of `SchemaType`
297+
pub fn does_restricted_expr_implement_schematype(
298+
expr: BorrowedRestrictedExpr<'_>,
299+
expected_ty: &SchemaType,
300+
) -> bool {
301+
use SchemaType::*;
302+
303+
match expected_ty {
304+
Bool => expr.as_bool().is_some(),
305+
Long => expr.as_long().is_some(),
306+
String => expr.as_string().is_some(),
307+
EmptySet => expr.as_set_elements().is_some_and(|e| e.count() == 0),
308+
Set { .. } if expr.as_set_elements().is_some_and(|e| e.count() == 0) => true,
309+
Set { element_ty: elty } => match expr.as_set_elements() {
310+
Some(mut els) => els.all(|e| does_restricted_expr_implement_schematype(e, elty)),
311+
None => false,
312+
},
313+
Record { attrs, open_attrs } => match expr.as_record_pairs() {
314+
Some(pairs) => {
315+
let pairs_map: BTreeMap<&SmolStr, BorrowedRestrictedExpr<'_>> = pairs.collect();
316+
let all_req_schema_attrs_in_record = attrs.iter().all(|(k, v)| {
317+
!v.required
318+
|| match pairs_map.get(k) {
319+
Some(inner_e) => {
320+
does_restricted_expr_implement_schematype(*inner_e, &v.attr_type)
321+
}
322+
None => false,
323+
}
324+
});
325+
let all_rec_attrs_match_schema =
326+
pairs_map.iter().all(|(k, inner_e)| match attrs.get(*k) {
327+
Some(sch_ty) => {
328+
does_restricted_expr_implement_schematype(*inner_e, &sch_ty.attr_type)
329+
}
330+
None => *open_attrs,
331+
});
332+
all_rec_attrs_match_schema && all_req_schema_attrs_in_record
333+
}
334+
None => false,
335+
},
336+
Extension { name } => match expr.as_extn_fn_call() {
337+
Some((actual_name, _)) => match name.id.as_ref() {
338+
"ipaddr" => actual_name.id.as_ref() == "ip",
339+
_ => name == actual_name,
340+
},
341+
None => false,
342+
},
343+
Entity { ty } => match expr.as_euid() {
344+
Some(actual_euid) => actual_euid.entity_type() == ty,
345+
None => false,
346+
},
347+
}
348+
}
349+
294350
/// Check whether the given `RestrictedExpr` typechecks with the given `SchemaType`.
295351
/// If the typecheck passes, return `Ok(())`.
296352
/// If the typecheck fails, return an appropriate `Err`.
@@ -299,23 +355,15 @@ pub fn typecheck_restricted_expr_against_schematype(
299355
expected_ty: &SchemaType,
300356
extensions: Extensions<'_>,
301357
) -> Result<(), TypecheckError> {
302-
// TODO(#440): instead of computing the `SchemaType` of `expr` and then
303-
// checking whether the schematypes are "consistent", wouldn't it be less
304-
// confusing, more efficient, and maybe even more precise to just typecheck
305-
// directly?
358+
if does_restricted_expr_implement_schematype(expr, expected_ty) {
359+
return Ok(());
360+
}
306361
match schematype_of_restricted_expr(expr, extensions) {
307-
Ok(actual_ty) => {
308-
if actual_ty.is_consistent_with(expected_ty) {
309-
// typecheck passes
310-
Ok(())
311-
} else {
312-
Err(TypecheckError::TypeMismatch(TypeMismatchError {
313-
expected: Box::new(expected_ty.clone()),
314-
actual_ty: Some(Box::new(actual_ty)),
315-
actual_val: Either::Right(Box::new(expr.to_owned())),
316-
}))
317-
}
318-
}
362+
Ok(actual_ty) => Err(TypecheckError::TypeMismatch(TypeMismatchError {
363+
expected: Box::new(expected_ty.clone()),
364+
actual_ty: Some(Box::new(actual_ty)),
365+
actual_val: Either::Right(Box::new(expr.to_owned())),
366+
})),
319367
Err(GetSchemaTypeError::UnknownInsufficientTypeInfo { .. }) => {
320368
// in this case we just don't have the information to know whether
321369
// the attribute value (an unknown) matches the expected type.

cedar-policy-core/src/entities/json/schema_types.rs

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -67,9 +67,9 @@ pub enum SchemaType {
6767
#[derive(Debug, PartialEq, Eq, Clone)]
6868
pub struct AttributeType {
6969
/// Type of the attribute
70-
attr_type: SchemaType,
70+
pub(crate) attr_type: SchemaType,
7171
/// Is the attribute required
72-
required: bool,
72+
pub(crate) required: bool,
7373
}
7474

7575
impl SchemaType {
@@ -313,11 +313,8 @@ pub struct HeterogeneousSetError {
313313
/// required or optional.
314314
/// This function, when given a record that has keys A, B, and C, will return a
315315
/// `SchemaType` where A, B, and C are all marked as optional attributes, but no
316-
/// other attributes are possible.
317-
/// That is, this assumes that all existing attributes are optional, but that no
318-
/// other optional attributes are possible.
319-
/// Compared to marking A, B, and C as required, this allows the returned
320-
/// `SchemaType` to `is_consistent_with()` more types.
316+
/// other attributes are possible. This maximized flexibility while avoiding
317+
/// heterogeneous sets.
321318
///
322319
/// This function may return `GetSchemaTypeError`, but should never return
323320
/// `NontrivialResidual`, because `RestrictedExpr`s can't contain nontrivial
@@ -341,9 +338,8 @@ pub fn schematype_of_restricted_expr(
341338
BorrowedRestrictedExpr::new_unchecked(v), // assuming the invariant holds for the record as a whole, it will also hold for each attribute value
342339
extensions,
343340
)?;
344-
// we can't know if the attribute is required or optional,
345-
// but marking it optional is more flexible -- allows the
346-
// attribute type to `is_consistent_with()` more types
341+
// We can't know if the attribute is required or optional.
342+
// Keep as optional to minimize heterogeneous sets.
347343
Ok((k.clone(), AttributeType::optional(attr_type)))
348344
}).collect::<Result<HashMap<_,_>, GetSchemaTypeError>>()?,
349345
open_attrs: false,

cedar-policy-formatter/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "cedar-policy-formatter"
3-
version = "3.3.0"
3+
version = "3.4.0"
44
edition = "2021"
55
rust-version = "1.76.0" # minimum supported Rust version is currently 1.76.0 because `cedar-policy-core` requirement. Check with `cargo install cargo-msrv && cargo msrv --min 1.75.0`
66
license = "Apache-2.0"
@@ -11,7 +11,7 @@ homepage = "https://cedarpolicy.com"
1111
repository = "https://github.com/cedar-policy/cedar"
1212

1313
[dependencies]
14-
cedar-policy-core = { version = "=3.3.0", path = "../cedar-policy-core" }
14+
cedar-policy-core = { version = "=3.4.0", path = "../cedar-policy-core" }
1515
pretty = "0.12.1"
1616
logos = "0.14.0"
1717
itertools = "0.12"

cedar-policy-validator/Cargo.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ name = "cedar-policy-validator"
33
edition = "2021"
44
rust-version = "1.76.0" # minimum supported Rust version is currently 1.76.0 because `cedar-policy-core` requirement. Check with `cargo install cargo-msrv && cargo msrv --min 1.75.0`
55

6-
version = "3.3.0"
6+
version = "3.4.0"
77
license = "Apache-2.0"
88
categories = ["compilers", "config"]
99
description = "Validator for the Cedar Policy language."
@@ -12,7 +12,7 @@ homepage = "https://cedarpolicy.com"
1212
repository = "https://github.com/cedar-policy/cedar"
1313

1414
[dependencies]
15-
cedar-policy-core = { version = "=3.3.0", path = "../cedar-policy-core" }
15+
cedar-policy-core = { version = "=3.4.0", path = "../cedar-policy-core" }
1616
serde = { version = "1.0", features = ["derive"] }
1717
serde_json = { version = "1.0", features = ["preserve_order"] }
1818
serde_with = "3.0"
@@ -49,7 +49,7 @@ wasm = ["serde-wasm-bindgen", "tsify", "wasm-bindgen"]
4949

5050
[dev-dependencies]
5151
cool_asserts = "2.0"
52-
cedar-policy-core = { version = "=3.3.0", path = "../cedar-policy-core", features = [
52+
cedar-policy-core = { version = "=3.4.0", path = "../cedar-policy-core", features = [
5353
"test-util",
5454
] }
5555

cedar-policy/Cargo.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ name = "cedar-policy"
33
edition = "2021"
44
rust-version = "1.76.0" # minimum supported Rust version is currently 1.76.0 because `cedar-policy-core` requirement. Check with `cargo install cargo-msrv && cargo msrv --min 1.75.0`
55

6-
version = "3.3.0"
6+
version = "3.4.0"
77
license = "Apache-2.0"
88
categories = ["compilers", "config"]
99
description = "Cedar is a language for defining permissions as policies, which describe who should have access to what."
@@ -12,8 +12,8 @@ homepage = "https://cedarpolicy.com"
1212
repository = "https://github.com/cedar-policy/cedar"
1313

1414
[dependencies]
15-
cedar-policy-core = { version = "=3.3.0", features = ["test-util"], path = "../cedar-policy-core" }
16-
cedar-policy-validator = { version = "=3.3.0", path = "../cedar-policy-validator" }
15+
cedar-policy-core = { version = "=3.4.0", features = ["test-util"], path = "../cedar-policy-core" }
16+
cedar-policy-validator = { version = "=3.4.0", path = "../cedar-policy-validator" }
1717
ref-cast = "1.0"
1818
serde = { version = "1.0", features = ["derive", "rc"] }
1919
serde_json = "1.0"

0 commit comments

Comments
 (0)