diff --git a/cedar-rust-hello-world/Cargo.toml b/cedar-rust-hello-world/Cargo.toml index af0cc5b..c560167 100644 --- a/cedar-rust-hello-world/Cargo.toml +++ b/cedar-rust-hello-world/Cargo.toml @@ -9,7 +9,7 @@ publish = false serde_json = "1.0" [dependencies.cedar-policy] -version = "4.0.0" +version = "*" git = "https://github.com/cedar-policy/cedar" branch = "main" #Do not add any lines below this. CI relies on the previous line being the second-to-last line in the file diff --git a/cedar-wasm-example/package-lock.json b/cedar-wasm-example/package-lock.json index 7a03515..71566eb 100644 --- a/cedar-wasm-example/package-lock.json +++ b/cedar-wasm-example/package-lock.json @@ -9,7 +9,7 @@ "version": "1.0.0", "license": "Apache-2.0", "dependencies": { - "@cedar-policy/cedar-wasm": "3.2.3" + "@cedar-policy/cedar-wasm": "4.1.0" }, "devDependencies": { "@types/jest": "^29.5.12", @@ -589,9 +589,9 @@ "dev": true }, "node_modules/@cedar-policy/cedar-wasm": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/@cedar-policy/cedar-wasm/-/cedar-wasm-3.2.3.tgz", - "integrity": "sha512-DPtI9SRSFYdrpkL8yKtcseQkZFfd0G0nihSQXMKT63AbENKnMIT/GZE0kiFudl9UT2DsfAyru9ZPfP8y4FK36g==" + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@cedar-policy/cedar-wasm/-/cedar-wasm-4.1.0.tgz", + "integrity": "sha512-QqPPb605DFB5PeJe6FMBfMtKFIfPhWAsO9ynbLToNjRhDbYFqbE8+a0MX9norYJL6/g7ycX1GAMw4mZD6H13Mw==" }, "node_modules/@cspotcode/source-map-support": { "version": "0.8.1", diff --git a/cedar-wasm-example/package.json b/cedar-wasm-example/package.json index fe3a45a..61eda0d 100644 --- a/cedar-wasm-example/package.json +++ b/cedar-wasm-example/package.json @@ -11,7 +11,7 @@ "author": "", "license": "Apache-2.0", "dependencies": { - "@cedar-policy/cedar-wasm": "3.2.3" + "@cedar-policy/cedar-wasm": "4.1.0" }, "devDependencies": { "@types/jest": "^29.5.12", diff --git a/oopsla2024-benchmarks/Cargo.toml b/oopsla2024-benchmarks/Cargo.toml index 77429df..5250718 100644 --- a/oopsla2024-benchmarks/Cargo.toml +++ b/oopsla2024-benchmarks/Cargo.toml @@ -7,9 +7,9 @@ publish = false [dependencies] arbitrary = "1.3" average = "0.14" -cedar-policy-core = { git = "https://github.com/cedar-policy/cedar-spec", branch = "release/3.0.x", version = "=3.0.1" } -cedar-policy-generators = { git = "https://github.com/cedar-policy/cedar-spec", branch = "release/3.0.x", version = "=3.0.1" } -cedar-policy-validator = { git = "https://github.com/cedar-policy/cedar-spec", branch = "release/3.0.x", version = "=3.0.1" } +cedar-policy-core = { path = "cedar-spec/cedar/cedar-policy-core", version = "*" } +cedar-policy-generators = { path = "cedar-spec/cedar-policy-generators", version = "*" } +cedar-policy-validator = { path = "cedar-spec/cedar/cedar-policy-validator", version = "*" } clap = { version = "4.3", features = ["derive"] } env_logger = "0.11" itertools = "0.12" @@ -19,7 +19,7 @@ rand = "0.8.5" reqwest = { version = "0.11", features = ["json", "blocking"] } serde = { version = "1", features = ["derive"] } serde_json = "1" -smol_str = { version = "0.2", features = ["arbitrary"] } +smol_str = { version = "0.3", features = ["arbitrary"] } [features] default = [] diff --git a/oopsla2024-benchmarks/Dockerfile b/oopsla2024-benchmarks/Dockerfile index 5643dab..284c38e 100644 --- a/oopsla2024-benchmarks/Dockerfile +++ b/oopsla2024-benchmarks/Dockerfile @@ -1,4 +1,4 @@ -FROM rust:1.74-bookworm +FROM rust:1.81-bookworm RUN echo 'deb http://deb.debian.org/debian bookworm-backports main' >> /etc/apt/sources.list RUN apt-get update && apt-get install -y git python3-matplotlib python3-pandas golang-1.21-go openjdk-17-jdk vim && \ rm -rf /var/lib/apt/lists/* @@ -17,8 +17,12 @@ ENV PATH="${PATH}:${HOME}/openfga" ## run an openfga server for 100ms and then kill it. For some reason this reduces flakiness later on RUN timeout 0.1 openfga run --playground-enabled=false --log-level=warn || true +## Clone cedar and cedar-spec +RUN git clone https://github.com/cedar-policy/cedar-spec cedar-benchmarks/cedar-spec -b main +RUN git clone https://github.com/cedar-policy/cedar cedar-benchmarks/cedar-spec/cedar -b main + ## Install Cedar CLI so it is usable from command line -RUN cargo install cedar-policy-cli@3.0.1 +RUN cargo install cedar-policy-cli --path cedar-benchmarks/cedar-spec/cedar/cedar-policy-cli ## Get cedar-benchmarks (benchmark harness and example app code) # unfortunately, `COPY src cedar-benchmarks/` doesn't do what we want, it copies diff --git a/oopsla2024-benchmarks/src/apps.rs b/oopsla2024-benchmarks/src/apps.rs index 046f72f..f65e7c4 100644 --- a/oopsla2024-benchmarks/src/apps.rs +++ b/oopsla2024-benchmarks/src/apps.rs @@ -1,11 +1,11 @@ use arbitrary::Unstructured; use cedar_policy_core::{ - ast::{Entity, EntityUID, Id, PolicyID, PolicySet}, - entities::{EntityUidJson, JsonDeserializationErrorContext}, + ast::{AnyId, Entity, EntityUID, PolicyID, PolicySet}, + entities::{json::err::JsonDeserializationErrorContext, EntityUidJson}, parser::parse_policyset, }; use cedar_policy_generators::{schema::Schema as GeneratorSchema, settings::ABACSettings}; -use cedar_policy_validator::{SchemaFragment, ValidatorSchema}; +use cedar_policy_validator::{json_schema::Fragment, ValidatorSchema}; use std::{fs::File, path::Path, process::Command}; /// Everything we need for an example application used as a benchmark @@ -54,8 +54,7 @@ impl ExampleApp { let policies_path = Path::new("./benches/github/cedar").join("policies.cedar"); Self::load_policies(policies_path) }, - openfga_authz_model_filename: - "./benches/github/openfga/authorization-model.json", + openfga_authz_model_filename: "./benches/github/openfga/authorization-model.json", convert_euid: Box::new(convert_github_euid), bespoke_generator: separate_process_bespoke_generator( "./generators/github_entity_generator.py", @@ -86,8 +85,7 @@ impl ExampleApp { Path::new("./benches/github-templates/cedar").join("policies.cedar"); Self::load_policies(policies_path) }, - openfga_authz_model_filename: - "./benches/github/openfga/authorization-model.json", + openfga_authz_model_filename: "./benches/github/openfga/authorization-model.json", convert_euid: Box::new(convert_github_euid), bespoke_generator: separate_process_bespoke_generator( "./generators/github_templates_entity_generator.py", @@ -117,8 +115,7 @@ impl ExampleApp { let policies_path = Path::new("./benches/gdrive/cedar").join("policies.cedar"); Self::load_policies(policies_path) }, - openfga_authz_model_filename: - "./benches/gdrive/openfga/authorization-model.json", + openfga_authz_model_filename: "./benches/gdrive/openfga/authorization-model.json", convert_euid: Box::new(convert_gdrive_euid), bespoke_generator: separate_process_bespoke_generator( "./generators/gdrive_entity_generator.py", @@ -149,8 +146,7 @@ impl ExampleApp { Path::new("./benches/gdrive-templates/cedar").join("policies.cedar"); Self::load_policies(policies_path) }, - openfga_authz_model_filename: - "./benches/gdrive/openfga/authorization-model.json", + openfga_authz_model_filename: "./benches/gdrive/openfga/authorization-model.json", convert_euid: Box::new(convert_gdrive_euid), bespoke_generator: separate_process_bespoke_generator( "./generators/gdrive_templates_entity_generator.py", @@ -172,7 +168,8 @@ impl ExampleApp { Self { name: "tinytodo", schema: { - let schema_path = Path::new("benches/tinytodo/cedar").join("tinytodo.cedarschema.json"); + let schema_path = + Path::new("benches/tinytodo/cedar").join("tinytodo.cedarschema.json"); Self::load_schema(schema_path, u) }, static_policies: { @@ -211,17 +208,17 @@ impl ExampleApp { // but policies' and templates' ids will be renamed to match // their "id" annotation, if present let mut new_pset = PolicySet::new(); - let id_key = Id::from_str("id").unwrap(); + let id_key = AnyId::from_str("id").unwrap(); let templates = pset.templates().map(|t| match t.annotation(&id_key) { None => t.clone(), - Some(anno) => t.new_id(PolicyID::from_smolstr(anno.clone())), + Some(anno) => t.new_id(PolicyID::from_smolstr(anno.val.clone())), }); for template in templates { new_pset.add_template(template).unwrap(); } let policies = pset.policies().map(|p| match p.annotation(&id_key) { None => p.clone(), - Some(anno) => p.new_id(PolicyID::from_smolstr(anno.clone())), + Some(anno) => p.new_id(PolicyID::from_smolstr(anno.val.clone())), }); for policy in policies { new_pset.add(policy).unwrap(); @@ -231,7 +228,7 @@ impl ExampleApp { /// Create a `GeneratorSchema` from the given filepath pub fn load_schema(path: impl AsRef, u: &mut Unstructured<'_>) -> GeneratorSchema { - let schema = SchemaFragment::from_file(File::open(path.as_ref()).unwrap_or_else(|e| { + let schema = Fragment::from_json_file(File::open(path.as_ref()).unwrap_or_else(|e| { panic!( "failed to open schema file {}: {e}", path.as_ref().display() @@ -251,7 +248,8 @@ impl ExampleApp { enable_unspecified_apply_spec: false, enable_action_in_constraints: false, }; - GeneratorSchema::from_schemafrag(schema, settings, u).expect("failed to generate schema") + GeneratorSchema::from_raw_schemafrag(schema, settings, u) + .expect("failed to generate schema") } /// Get the `ValidatorSchema` for this `ExampleApp` diff --git a/oopsla2024-benchmarks/src/cedar_engine.rs b/oopsla2024-benchmarks/src/cedar_engine.rs index bc5bdb1..c6c6475 100644 --- a/oopsla2024-benchmarks/src/cedar_engine.rs +++ b/oopsla2024-benchmarks/src/cedar_engine.rs @@ -58,7 +58,8 @@ impl CedarEngine { pub fn execute(&self, request: Request) -> SingleExecutionReport { let num_context_attrs = request .context() - .map(|ctx| ctx.iter().map(|it| it.count()).unwrap_or(0)) + .cloned() + .map(|ctx| ctx.into_iter().count()) .unwrap_or(0); let start = Instant::now(); let response = self @@ -128,7 +129,8 @@ impl CedarOptEngine { pub fn execute(&self, request: Request, slicer: &Slicer) -> SingleExecutionReport { let num_context_attrs = request .context() - .map(|ctx| ctx.iter().map(|it| it.count()).unwrap_or(0)) + .cloned() + .map(|ctx| ctx.into_iter().count()) .unwrap_or(0); let start = Instant::now(); let sliced = slicer diff --git a/oopsla2024-benchmarks/src/lib.rs b/oopsla2024-benchmarks/src/lib.rs index edc55e1..f987bd3 100644 --- a/oopsla2024-benchmarks/src/lib.rs +++ b/oopsla2024-benchmarks/src/lib.rs @@ -110,14 +110,18 @@ impl MultiExecutionReport { /// Add a data point to the report pub fn add(&mut self, single_report: SingleExecutionReport) { - self.mean_dur_micros - .add(f64::try_from(u32::try_from(single_report.dur.as_micros()).unwrap()).unwrap()); - self.median_dur_micros - .add(f64::try_from(u32::try_from(single_report.dur.as_micros()).unwrap()).unwrap()); - self.p90_dur_micros - .add(f64::try_from(u32::try_from(single_report.dur.as_micros()).unwrap()).unwrap()); - self.p99_dur_micros - .add(f64::try_from(u32::try_from(single_report.dur.as_micros()).unwrap()).unwrap()); + self.mean_dur_micros.add(f64::from( + u32::try_from(single_report.dur.as_micros()).unwrap(), + )); + self.median_dur_micros.add(f64::from( + u32::try_from(single_report.dur.as_micros()).unwrap(), + )); + self.p90_dur_micros.add(f64::from( + u32::try_from(single_report.dur.as_micros()).unwrap(), + )); + self.p99_dur_micros.add(f64::from( + u32::try_from(single_report.dur.as_micros()).unwrap(), + )); match single_report.decision { Decision::Allow => { self.allows.add(1.0); @@ -128,16 +132,18 @@ impl MultiExecutionReport { self.denies.add(1.0); } } - self.mean_num_errors - .add(f64::try_from(u32::try_from(single_report.errors.len()).unwrap()).unwrap()); + self.mean_num_errors.add(f64::from( + u32::try_from(single_report.errors.len()).unwrap(), + )); match &self.err { Some(_) => {} None => { - self.err = single_report.errors.iter().next().cloned(); + self.err = single_report.errors.first().cloned(); } } - self.mean_context_attrs - .add(f64::try_from(u32::try_from(single_report.context_attrs).unwrap()).unwrap()); + self.mean_context_attrs.add(f64::from( + u32::try_from(single_report.context_attrs).unwrap(), + )); } /// Print the report's contents to the provided stream (e.g. stdout) @@ -222,11 +228,13 @@ impl HierarchyStats { for entity in hierarchy.iter() { self.mean_num_parents .entry(entity.uid().entity_type().clone()) - .or_insert_with(|| Mean::new()) - .add(f64::try_from(u32::try_from(entity.ancestors().count()).unwrap()).unwrap()); + .or_default() + .add(f64::from( + u32::try_from(entity.ancestors().count()).unwrap(), + )); } self.mean_openfga_tuples - .add(f64::try_from(u32::try_from(openfga_tuples).unwrap()).unwrap()); + .add(f64::from(u32::try_from(openfga_tuples).unwrap())); } /// Print the CSV header row that corresponds to the format in diff --git a/oopsla2024-benchmarks/src/openfga_engine.rs b/oopsla2024-benchmarks/src/openfga_engine.rs index 1a53edc..3368935 100644 --- a/oopsla2024-benchmarks/src/openfga_engine.rs +++ b/oopsla2024-benchmarks/src/openfga_engine.rs @@ -71,16 +71,16 @@ impl<'a> OpenFgaEngine<'a> { pub fn execute(&self, request: Request) -> SingleExecutionReport { let tuple = OpenFgaTuple { user: match request.principal() { - EntityUIDEntry::Known(p) => (self.app.convert_euid)(p), - EntityUIDEntry::Unknown => panic!("can't handle requests with Unknown"), + EntityUIDEntry::Known { euid, .. } => (self.app.convert_euid)(euid), + EntityUIDEntry::Unknown { .. } => panic!("can't handle requests with Unknown"), }, relation: match request.action() { - EntityUIDEntry::Known(a) => (self.app.convert_euid)(a), - EntityUIDEntry::Unknown => panic!("can't handle requests with Unknown"), + EntityUIDEntry::Known { euid, .. } => (self.app.convert_euid)(euid), + EntityUIDEntry::Unknown { .. } => panic!("can't handle requests with Unknown"), }, object: match request.resource() { - EntityUIDEntry::Known(r) => (self.app.convert_euid)(r), - EntityUIDEntry::Unknown => panic!("can't handle requests with Unknown"), + EntityUIDEntry::Known { euid, .. } => (self.app.convert_euid)(euid), + EntityUIDEntry::Unknown { .. } => panic!("can't handle requests with Unknown"), }, }; let allowed = self.client.check(&tuple); @@ -103,7 +103,8 @@ impl<'a> OpenFgaEngine<'a> { errors: vec![], // all errors in OpenFGA are currently panics context_attrs: request .context() - .map(|ctx| ctx.iter().map(|it| it.count()).unwrap_or(0)) + .cloned() + .map(|ctx| ctx.into_iter().count()) .unwrap_or(0), } } @@ -168,11 +169,11 @@ impl<'a> GithubConverter<'a> { if ancestor.entity_type() == &self.ghtypes.repopermission || ancestor.entity_type() == &self.ghtypes.orgpermission { - self.repo_org_permission_to_tuple(ancestor, &entity.uid()) + self.repo_org_permission_to_tuple(ancestor, entity.uid()) } else if ancestor.entity_type() == &self.ghtypes.team || ancestor.entity_type() == &self.ghtypes.org { - self.parent_to_tuple(&entity.uid(), ancestor) + self.parent_to_tuple(entity.uid(), ancestor) } else { panic!("unexpected ancestor of User: {ancestor}") } @@ -183,9 +184,9 @@ impl<'a> GithubConverter<'a> { .ancestors() .map(|ancestor| { if ancestor.entity_type() == &self.ghtypes.repopermission { - self.repo_org_permission_to_tuple(ancestor, &entity.uid()) + self.repo_org_permission_to_tuple(ancestor, entity.uid()) } else if ancestor.entity_type() == &self.ghtypes.team { - self.parent_to_tuple(&entity.uid(), ancestor) + self.parent_to_tuple(entity.uid(), ancestor) } else { panic!("unexpected ancestor of Team: {ancestor}") } @@ -198,14 +199,14 @@ impl<'a> GithubConverter<'a> { vec![OpenFgaTuple { user: (self.app.convert_euid)(&owner), relation: "owner".to_string(), - object: (self.app.convert_euid)(&entity.uid()), + object: (self.app.convert_euid)(entity.uid()), }] } else if entity.uid().entity_type() == &self.ghtypes.org { entity .ancestors() .map(|ancestor| { if ancestor.entity_type() == &self.ghtypes.orgpermission { - self.repo_org_permission_to_tuple(ancestor, &entity.uid()) + self.repo_org_permission_to_tuple(ancestor, entity.uid()) } else { panic!("unexpected ancestor of Org: {ancestor}") } @@ -281,7 +282,7 @@ impl<'a> GithubConverter<'a> { OpenFgaTuple { user, relation: if perm.entity_type() == &self.ghtypes.repopermission { - format!("{relation}") + relation.to_string() } else { format!("repo_{relation}") // orgs have relations like "repo_reader" etc, while repos have "reader" etc. I didn't make the rules }, @@ -303,7 +304,7 @@ impl<'a> GithubConverter<'a> { OpenFgaTuple { user, relation: "member".to_string(), - object: (self.app.convert_euid)(&parent), + object: (self.app.convert_euid)(parent), } } } @@ -354,19 +355,19 @@ impl<'a> GdriveConverter<'a> { if entity.uid().entity_type() == &self.gdtypes.user { let groups = entity .ancestors() - .map(|ancestor| self.parent_to_tuple(&entity.uid(), ancestor)); + .map(|ancestor| self.parent_to_tuple(entity.uid(), ancestor)); let owned_documents = utils::pv_expect_set_euids( entity .get("ownedDocuments") .expect("user should have .ownedDocuments"), ) - .map(|owned_doc| self.owner_to_tuple(&entity.uid(), &owned_doc)); + .map(|owned_doc| self.owner_to_tuple(entity.uid(), &owned_doc)); let owned_folders = utils::pv_expect_set_euids( entity .get("ownedFolders") .expect("user should have .ownedFolders"), ) - .map(|owned_folder| self.owner_to_tuple(&entity.uid(), &owned_folder)); + .map(|owned_folder| self.owner_to_tuple(entity.uid(), &owned_folder)); groups .chain(owned_documents) .chain(owned_folders) @@ -376,9 +377,9 @@ impl<'a> GdriveConverter<'a> { .ancestors() .map(|ancestor| { if ancestor.entity_type() == &self.gdtypes.folder { - self.parent_folder(&entity.uid(), ancestor) + self.parent_folder(entity.uid(), ancestor) } else if ancestor.entity_type() == &self.gdtypes.view { - self.view_to_tuple(ancestor, &entity.uid()) + self.view_to_tuple(ancestor, entity.uid()) } else { panic!("Expected all ancestors of a Folder to be either Folder or View") } @@ -389,9 +390,9 @@ impl<'a> GdriveConverter<'a> { .ancestors() .map(|ancestor| { if ancestor.entity_type() == &self.gdtypes.folder { - self.parent_folder(&entity.uid(), ancestor) + self.parent_folder(entity.uid(), ancestor) } else if ancestor.entity_type() == &self.gdtypes.view { - self.view_to_tuple(ancestor, &entity.uid()) + self.view_to_tuple(ancestor, entity.uid()) } else { panic!( "Expected all ancestors of a Document to be either Folder or View" @@ -404,7 +405,7 @@ impl<'a> GdriveConverter<'a> { .get("isPublic") .expect("Document should have .isPublic"), ) { - tuples.push(self.public_view(&entity.uid())); + tuples.push(self.public_view(entity.uid())); } tuples } else { @@ -552,17 +553,17 @@ impl<'a> TinyTodoConverter<'a> { vec![OpenFgaTuple { user: "user:*".to_string(), relation: "member".to_string(), - object: (self.app.convert_euid)(&entity.uid()), + object: (self.app.convert_euid)(entity.uid()), }] } else if entity.uid().entity_type() == &self.types.user { entity .ancestors() - .filter_map(|ancestor| self.parent_to_tuple(&entity.uid(), ancestor)) + .filter_map(|ancestor| self.parent_to_tuple(entity.uid(), ancestor)) .collect() } else if entity.uid().entity_type() == &self.types.team { entity .ancestors() - .filter_map(|ancestor| self.parent_to_tuple(&entity.uid(), ancestor)) + .filter_map(|ancestor| self.parent_to_tuple(entity.uid(), ancestor)) .collect() } else if entity.uid().entity_type() == &self.types.list { let owner = utils::pv_expect_euid( @@ -572,7 +573,7 @@ impl<'a> TinyTodoConverter<'a> { OpenFgaTuple { user: (self.app.convert_euid)(&owner), relation: "owner".to_string(), // read this as "is the owner of" - object: (self.app.convert_euid)(&entity.uid()), + object: (self.app.convert_euid)(entity.uid()), } }; let reader_tuple = { @@ -581,7 +582,7 @@ impl<'a> TinyTodoConverter<'a> { .get("readers") .expect("List entity should have .readers"), ); - self.reader_editor_to_tuple(&reader_team, &entity.uid(), false) + self.reader_editor_to_tuple(&reader_team, entity.uid(), false) }; let editor_tuple = { let editor_team = utils::pv_expect_euid( @@ -589,7 +590,7 @@ impl<'a> TinyTodoConverter<'a> { .get("editors") .expect("List entity should have .editors"), ); - self.reader_editor_to_tuple(&editor_team, &entity.uid(), true) + self.reader_editor_to_tuple(&editor_team, entity.uid(), true) }; vec![owner_tuple, reader_tuple, editor_tuple] } else { diff --git a/oopsla2024-benchmarks/src/rego_requests/gdrive.rs b/oopsla2024-benchmarks/src/rego_requests/gdrive.rs index f682f0a..b9a594f 100644 --- a/oopsla2024-benchmarks/src/rego_requests/gdrive.rs +++ b/oopsla2024-benchmarks/src/rego_requests/gdrive.rs @@ -120,7 +120,7 @@ impl Resource { fn build_file_graph(es: impl IntoIterator) -> FileGraph { es.into_iter() - .filter(|e| is_document_or_folder(&&e.uid())) + .filter(|e| is_document_or_folder(&e.uid())) .map(|e| { ( e.uid().to_string(), diff --git a/oopsla2024-benchmarks/src/slicing.rs b/oopsla2024-benchmarks/src/slicing.rs index f1f8616..e845056 100644 --- a/oopsla2024-benchmarks/src/slicing.rs +++ b/oopsla2024-benchmarks/src/slicing.rs @@ -1,7 +1,7 @@ //! Policy slicing functionality use cedar_policy_core::ast::{ - EntityReference, EntityType, EntityUID, EntityUIDEntry, Policy, PolicySet, PolicySetError, + EntityReference, EntityUID, EntityUIDEntry, Policy, PolicySet, PolicySetError, PrincipalOrResourceConstraint, Request, }; use cedar_policy_core::entities::{Dereference, Entities}; @@ -102,52 +102,19 @@ impl<'p> Slicer<'p> { }) }; match (request.principal(), request.resource()) { - (EntityUIDEntry::Known(principal), EntityUIDEntry::Known(resource)) => { - match (principal.entity_type(), resource.entity_type()) { - (EntityType::Specified(_), EntityType::Specified(_)) => self - .all_policies_with_keys( - get_all_ancestors(principal) - .cartesian_product(get_all_ancestors(resource)) - .map(|(principal, resource)| SliceKey { - principal, - resource, - }), - ), - (EntityType::Specified(_), EntityType::Unspecified) => { - // for requests with unspecified resource, the only matching - // policies are the ones that do not constrain `resource` - self.all_policies_with_keys( - get_all_ancestors(principal) - .cartesian_product(std::iter::once(None)) - .map(|(principal, resource)| SliceKey { - principal, - resource, - }), - ) - } - (EntityType::Unspecified, EntityType::Specified(_)) => { - // for requests with unspecified principal, the only matching - // policies are the ones that do not constrain `principal` - self.all_policies_with_keys( - std::iter::once(None) - .cartesian_product(get_all_ancestors(resource)) - .map(|(principal, resource)| SliceKey { - principal, - resource, - }), - ) - } - (EntityType::Unspecified, EntityType::Unspecified) => { - // for requests with unspecified principal _and_ resource, the - // only matching policies are the ones that do not constrain - // `principal` _or_ `resource` - self.all_policies_with_keys([SliceKey { - principal: None, - resource: None, - }]) - } - } - } + ( + EntityUIDEntry::Known { + euid: principal, .. + }, + EntityUIDEntry::Known { euid: resource, .. }, + ) => self.all_policies_with_keys( + get_all_ancestors(principal) + .cartesian_product(get_all_ancestors(resource)) + .map(|(principal, resource)| SliceKey { + principal, + resource, + }), + ), _ => unimplemented!("slicing with partial evaluation"), } } diff --git a/oopsla2024-benchmarks/src/tinytodo_generator/cedar.rs b/oopsla2024-benchmarks/src/tinytodo_generator/cedar.rs index 6729ac7..58583a8 100644 --- a/oopsla2024-benchmarks/src/tinytodo_generator/cedar.rs +++ b/oopsla2024-benchmarks/src/tinytodo_generator/cedar.rs @@ -129,9 +129,9 @@ pub struct Request { impl Request { pub fn to_cedar_request(&self) -> cedar_policy_core::ast::Request { cedar_policy_core::ast::Request::new( - self.principal.to_euid(), - self.action.to_euid(), - self.resource.to_euid(), + (self.principal.to_euid(), None), + (self.action.to_euid(), None), + (self.resource.to_euid(), None), Context::empty(), None::<&RequestSchemaAllPass>, Extensions::all_available(), @@ -143,8 +143,8 @@ impl Request { impl From for Request { fn from(r: cedar_policy_core::ast::Request) -> Self { let expect_concrete = |uidentry: &cedar_policy_core::ast::EntityUIDEntry| -> Arc { match uidentry { - cedar_policy_core::ast::EntityUIDEntry::Known(euid) => Arc::clone(euid), - cedar_policy_core::ast::EntityUIDEntry::Unknown => panic!("expected concrete entry"), + cedar_policy_core::ast::EntityUIDEntry::Known{euid, loc:_} => Arc::clone(euid), + cedar_policy_core::ast::EntityUIDEntry::Unknown{ .. } => panic!("expected concrete entry"), }}; let principal = expect_concrete(r.principal()); let action = expect_concrete(r.action()); diff --git a/oopsla2024-benchmarks/src/tinytodo_generator/common.rs b/oopsla2024-benchmarks/src/tinytodo_generator/common.rs index 6cdf93f..e1cbef1 100644 --- a/oopsla2024-benchmarks/src/tinytodo_generator/common.rs +++ b/oopsla2024-benchmarks/src/tinytodo_generator/common.rs @@ -17,7 +17,7 @@ impl Entity { pub fn to_cedar_entity(&self) -> cedar_policy_core::ast::Entity { cedar_policy_core::ast::Entity::new_with_attr_partial_value( self.euid.to_euid(), - HashMap::default(), + HashMap::new(), self.parents.iter().map(|uid| uid.to_euid()).collect(), ) } diff --git a/oopsla2024-benchmarks/src/tinytodo_generator/entities.rs b/oopsla2024-benchmarks/src/tinytodo_generator/entities.rs index 0cf3b28..e20510d 100644 --- a/oopsla2024-benchmarks/src/tinytodo_generator/entities.rs +++ b/oopsla2024-benchmarks/src/tinytodo_generator/entities.rs @@ -24,52 +24,52 @@ lazy_static! { pub static ref ACTIONS: Vec = vec![ cedar_policy_core::ast::Entity::new_with_attr_partial_value( r#"Action::"CreateList""#.parse().unwrap(), - Default::default(), + HashMap::new(), Default::default() ), cedar_policy_core::ast::Entity::new_with_attr_partial_value( r#"Action::"GetLists""#.parse().unwrap(), - Default::default(), + HashMap::new(), Default::default() ), cedar_policy_core::ast::Entity::new_with_attr_partial_value( r#"Action::"GetList""#.parse().unwrap(), - Default::default(), + HashMap::new(), Default::default() ), cedar_policy_core::ast::Entity::new_with_attr_partial_value( r#"Action::"UpdateList""#.parse().unwrap(), - Default::default(), + HashMap::new(), Default::default() ), cedar_policy_core::ast::Entity::new_with_attr_partial_value( r#"Action::"DeleteList""#.parse().unwrap(), - Default::default(), + HashMap::new(), Default::default() ), cedar_policy_core::ast::Entity::new_with_attr_partial_value( r#"Action::"CreateTask""#.parse().unwrap(), - Default::default(), + HashMap::new(), Default::default() ), cedar_policy_core::ast::Entity::new_with_attr_partial_value( r#"Action::"UpdateTask""#.parse().unwrap(), - Default::default(), + HashMap::new(), Default::default() ), cedar_policy_core::ast::Entity::new_with_attr_partial_value( r#"Action::"DeleteTask""#.parse().unwrap(), - Default::default(), + HashMap::new(), Default::default() ), cedar_policy_core::ast::Entity::new_with_attr_partial_value( r#"Action::"EditShares""#.parse().unwrap(), - Default::default(), + HashMap::new(), Default::default() ), cedar_policy_core::ast::Entity::new_with_attr_partial_value( r#"Application::"TinyTodo""#.parse().unwrap(), - Default::default(), + HashMap::new(), Default::default() ), ]; @@ -123,15 +123,15 @@ impl Entities { pub fn from_cedar_entities( entities: impl IntoIterator, ) -> Self { - let user_entity_type = cedar_policy_core::ast::EntityType::Specified( - cedar_policy_core::ast::Name::parse_unqualified_name("User").unwrap(), - ); - let team_entity_type = cedar_policy_core::ast::EntityType::Specified( - cedar_policy_core::ast::Name::parse_unqualified_name("Team").unwrap(), - ); - let list_entity_type = cedar_policy_core::ast::EntityType::Specified( - cedar_policy_core::ast::Name::parse_unqualified_name("List").unwrap(), - ); + let user_entity_type = cedar_policy_core::ast::Name::parse_unqualified_name("User") + .unwrap() + .into(); + let team_entity_type = cedar_policy_core::ast::Name::parse_unqualified_name("Team") + .unwrap() + .into(); + let list_entity_type = cedar_policy_core::ast::Name::parse_unqualified_name("List") + .unwrap() + .into(); let mut users = HashMap::new(); let mut teams = HashMap::new(); let mut lists = HashMap::new(); diff --git a/oopsla2024-benchmarks/src/tinytodo_generator/list.rs b/oopsla2024-benchmarks/src/tinytodo_generator/list.rs index 75bea2a..dc051b7 100644 --- a/oopsla2024-benchmarks/src/tinytodo_generator/list.rs +++ b/oopsla2024-benchmarks/src/tinytodo_generator/list.rs @@ -1,5 +1,4 @@ use std::collections::{HashMap, HashSet}; -use std::sync::Arc; use arbitrary::{Arbitrary, Unstructured}; use cedar_policy_core::ast::{EntityUID, PartialValue, Value}; @@ -38,7 +37,10 @@ impl List { ); attrs.insert( "tasks".into(), - PartialValue::Value(Value::set(self.tasks.iter().map(|task| task.to_value()))), + PartialValue::Value(Value::set( + self.tasks.iter().map(|task| task.to_value()), + None, + )), ); cedar_policy_core::ast::Entity::new_with_attr_partial_value( self.euid.to_euid(), @@ -87,12 +89,12 @@ struct Task { impl Task { pub fn to_value(&self) -> Value { - let attrs = [ + let attrs: [(SmolStr, _); 3] = [ ("id".into(), Value::from(self.id)), ("name".into(), Value::from(self.name.clone())), ("state".into(), Value::from(format!("{:?}", self.state))), ]; - Value::Record(Arc::new(attrs.into_iter().collect())) + Value::record(attrs, None) } pub fn from_cedar_record(rec: &Value) -> Self { diff --git a/oopsla2024-benchmarks/src/utils.rs b/oopsla2024-benchmarks/src/utils.rs index 6a917e9..b99639a 100644 --- a/oopsla2024-benchmarks/src/utils.rs +++ b/oopsla2024-benchmarks/src/utils.rs @@ -1,6 +1,8 @@ #![allow(dead_code)] // this is a utilities file, some utilities are included for completeness even if currently unused -use cedar_policy_core::ast::{EntityType, EntityUID, Literal, Name, PartialValue, Value}; +use cedar_policy_core::ast::{ + EntityType, EntityUID, Literal, Name, PartialValue, Value, ValueKind, +}; use smol_str::SmolStr; use std::collections::HashMap; use std::sync::Arc; @@ -8,7 +10,7 @@ use std::sync::Arc; /// Convert &str to EntityType. Assumes the EntityType is supposed to be /// unqualified and the &str doesn't contain namespaces pub fn entity_type(s: &str) -> EntityType { - EntityType::Specified(Name::parse_unqualified_name(s).unwrap()) + Name::parse_unqualified_name(s).unwrap().into() } /// Given a `PartialValue` that we expect to contain no unknowns, @@ -23,8 +25,8 @@ pub fn pv_expect_known(v: &PartialValue) -> &Value { /// Given a `Value` that we expect to be a boolean, /// get the boolean value, panicking if it's not actually a boolean pub fn expect_bool(v: &Value) -> bool { - match v { - Value::Lit(Literal::Bool(b)) => *b, + match &v.value { + ValueKind::Lit(Literal::Bool(b)) => *b, v => panic!("expected a boolean; got: {v:?}"), } } @@ -38,8 +40,8 @@ pub fn pv_expect_bool(v: &PartialValue) -> bool { /// Given a `Value` that we expect to be an int, /// get the int value, panicking if it's not actually an int pub fn expect_int(v: &Value) -> i64 { - match v { - Value::Lit(Literal::Long(i)) => *i, + match &v.value { + ValueKind::Lit(Literal::Long(i)) => *i, v => panic!("expected an int: got: {v:?}"), } } @@ -53,8 +55,8 @@ pub fn pv_expect_int(v: &PartialValue) -> i64 { /// Given a `Value` that we expect to be a string, /// get the string value, panicking if it's not actually a string pub fn expect_string(v: &Value) -> SmolStr { - match v { - Value::Lit(Literal::String(s)) => s.clone(), + match &v.value { + ValueKind::Lit(Literal::String(s)) => s.clone(), v => panic!("expected a string; got: {v:?}"), } } @@ -68,8 +70,8 @@ pub fn pv_expect_string(v: &PartialValue) -> SmolStr { /// Given a `Value` that we expect to be an EntityUID, /// get the EntityUID value, panicking if it's not actually an EntityUID pub fn expect_euid(v: &Value) -> Arc { - match v { - Value::Lit(Literal::EntityUID(euid)) => Arc::clone(euid), + match &v.value { + ValueKind::Lit(Literal::EntityUID(euid)) => Arc::clone(euid), v => panic!("expected an euid; got: {v:?}"), } } @@ -83,8 +85,8 @@ pub fn pv_expect_euid(v: &PartialValue) -> Arc { /// Given a `Value` that we expect to be a record, /// get the map of (key, value) pairs in the record, panicking if it's not actually a record pub fn expect_record(v: &Value) -> HashMap { - match v { - Value::Record(record) => record.iter().map(|(k, v)| (k.clone(), v)).collect(), + match &v.value { + ValueKind::Record(record) => record.iter().map(|(k, v)| (k.clone(), v)).collect(), v => panic!("expected a record; got: {v:?}"), } } @@ -97,16 +99,16 @@ pub fn pv_expect_record(v: &PartialValue) -> HashMap { /// Given a `Value` that we expect to be a set, /// iterate over the values in the set, panicking if it's not actually a set -pub fn expect_set<'a>(v: &'a Value) -> impl Iterator + 'a { - match v { - Value::Set(s) => s.iter(), +pub fn expect_set(v: &Value) -> impl Iterator { + match &v.value { + ValueKind::Set(s) => s.iter(), v => panic!("expected a set; got: {v:?}"), } } /// Given an attribute value (`PartialValue`) that we expect to be a set, /// iterate over the values in the set, panicking if it's not actually a set (or is unknown) -pub fn pv_expect_set<'a>(v: &'a PartialValue) -> impl Iterator + 'a { +pub fn pv_expect_set(v: &PartialValue) -> impl Iterator { expect_set(pv_expect_known(v)) } diff --git a/oopsla2024-benchmarks/validate_all_benchmarks.sh b/oopsla2024-benchmarks/validate_all_benchmarks.sh index 23b5e0c..95d2840 100755 --- a/oopsla2024-benchmarks/validate_all_benchmarks.sh +++ b/oopsla2024-benchmarks/validate_all_benchmarks.sh @@ -1,16 +1,16 @@ BENCHES="${BENCHES:-benches}" # gdrive benchmark (static policies) -cedar validate --policies $BENCHES/gdrive/cedar/policies.cedar --schema $BENCHES/gdrive/cedar/gdrive.cedarschema.json +cedar validate --policies $BENCHES/gdrive/cedar/policies.cedar --schema $BENCHES/gdrive/cedar/gdrive.cedarschema.json --schema-format json # gdrive benchmark (templates) -cedar validate --policies $BENCHES/gdrive-templates/cedar/policies.cedar --schema $BENCHES/gdrive-templates/cedar/gdrive-templates.cedarschema.json +cedar validate --policies $BENCHES/gdrive-templates/cedar/policies.cedar --schema $BENCHES/gdrive-templates/cedar/gdrive-templates.cedarschema.json --schema-format json # gdrive benchmark (static policies) -cedar validate --policies $BENCHES/github/cedar/policies.cedar --schema $BENCHES/github/cedar/github.cedarschema.json +cedar validate --policies $BENCHES/github/cedar/policies.cedar --schema $BENCHES/github/cedar/github.cedarschema.json --schema-format json # gdrive benchmark (templates) -cedar validate --policies $BENCHES/github-templates/cedar/policies.cedar --schema $BENCHES/github-templates/cedar/github-templates.cedarschema.json +cedar validate --policies $BENCHES/github-templates/cedar/policies.cedar --schema $BENCHES/github-templates/cedar/github-templates.cedarschema.json --schema-format json # tinytodo benchmark -cedar validate --policies $BENCHES/tinytodo/cedar/tinytodo.cedar --schema $BENCHES/tinytodo/cedar/tinytodo.cedarschema.json +cedar validate --policies $BENCHES/tinytodo/cedar/tinytodo.cedar --schema $BENCHES/tinytodo/cedar/tinytodo.cedarschema.json --schema-format json