diff --git a/tinytodo/Cargo.toml b/tinytodo/Cargo.toml index a06851c..dc55b4e 100644 --- a/tinytodo/Cargo.toml +++ b/tinytodo/Cargo.toml @@ -21,6 +21,7 @@ notify = { version = "5.1.0", default-features = false, features = ["macos_kqueu use-templates = [] [dependencies.cedar-policy] +features = ["partial-eval"] version = "4.0.0" git = "https://github.com/cedar-policy/cedar" branch = "main" diff --git a/tinytodo/src/context.rs b/tinytodo/src/context.rs index 8017569..df1a651 100644 --- a/tinytodo/src/context.rs +++ b/tinytodo/src/context.rs @@ -16,12 +16,13 @@ use itertools::Itertools; use lazy_static::lazy_static; -use std::path::PathBuf; +use std::{collections::HashMap, path::PathBuf}; use tracing::{error, info, trace}; use cedar_policy::{ - Authorizer, Context, Decision, Diagnostics, HumanSchemaError, ParseErrors, PolicySet, - PolicySetError, Request, Schema, SchemaError, ValidationMode, Validator, + Authorizer, Context, Decision, Diagnostics, Entities, HumanSchemaError, ParseErrors, PolicySet, + PolicySetError, Request, RequestBuilder, RestrictedExpression, Schema, SchemaError, + ValidationMode, Validator, }; use thiserror::Error; @@ -45,8 +46,6 @@ use crate::{ use crate::{api::ShareRole, util::UserOrTeamUid}; #[cfg(feature = "use-templates")] use cedar_policy::{PolicyId, SlotId}; -#[cfg(feature = "use-templates")] -use std::collections::HashMap; // There's almost certainly a nicer way to do this than having separate `sender` fields @@ -512,17 +511,33 @@ impl AppContext { fn get_lists(&self, r: GetLists) -> Result { self.is_authorized(&r.uid, &*ACTION_GET_LISTS, &*APPLICATION_TINY_TODO)?; + let entities: Entities = self.entities.as_entities(&self.schema); + let partial_request = RequestBuilder::default() + .action(Some(ACTION_GET_LIST.as_ref().clone().into())) + .principal(Some(cedar_policy::EntityUid::from(EntityUid::from( + r.uid.clone(), + )))) + .build(); + let partial_response = + self.authorizer + .is_authorized_partial(&partial_request, &self.policies, &entities); + + let slice = self.entities.get_lists(); Ok(AppResponse::Lists( - self.entities - .get_lists() - .filter(|t| { - self.is_authorized(&r.uid, &*ACTION_GET_LIST, t.uid()) - .is_ok() - }) + slice + .filter(|t| matches!( + partial_response.reauthorize( + HashMap::from_iter( + std::iter::once( + ("resource".into(), RestrictedExpression::new_entity_uid(EntityUid::from(t.uid().clone()).into())) + ) + ), + &self.authorizer, + &entities), + Ok(r) if matches!(r.decision(), Some(Decision::Allow)))) .cloned() - .collect::>() - .into(), + .collect::>(), )) }