Skip to content

Conversation

alanwang67
Copy link
Contributor

@alanwang67 alanwang67 commented Jul 31, 2025

Description of changes

Introduce internal data structures for generalized templates. The main changes are having a policy now store a map from slots to types. The differences in the changes between Allow principal resource slots in condition and this PR are here.

Issue #, if available

Checklist for requesting a review

The change in this PR is (choose one, and delete the other options):

  • A breaking change requiring a major version bump to cedar-policy (e.g., changes to the signature of an existing API).
  • A backwards-compatible change requiring a minor version bump to cedar-policy (e.g., addition of a new API).
  • A bug fix or other functionality change requiring a patch to cedar-policy.
  • A change "invisible" to users (e.g., documentation, changes to "internal" crates like cedar-policy-core, cedar-validator, etc.)
  • A change (breaking or otherwise) that only impacts unreleased or experimental code.

I confirm that this PR (choose one, and delete the other options):

  • Updates the "Unreleased" section of the CHANGELOG with a description of my change (required for major/minor version bumps).
  • Does not update the CHANGELOG because my change does not significantly impact released code.

I confirm that cedar-spec (choose one, and delete the other options):

  • Does not require updates because my change does not impact the Cedar formal model or DRT infrastructure.
  • Requires updates, and I have made / will make these updates myself. (Please include in your description a timeline or link to the relevant PR in cedar-spec, and how you have tested that your updates are correct.)
  • Requires updates, but I do not plan to make them in the near future. (Make sure that your changes are hidden behind a feature flag to mark them as experimental.)
  • I'm not sure how my change impacts cedar-spec. (Post your PR anyways, and we'll discuss in the comments.)

I confirm that docs.cedarpolicy.com (choose one, and delete the other options):

  • Does not require updates because my change does not impact the Cedar language specification.
  • Requires updates, and I have made / will make these updates myself. (Please include in your description a timeline or link to the relevant PR in cedar-docs. PRs should be targeted at a staging-X.Y branch, not main.)
  • I'm not sure how my change impacts the documentation. (Post your PR anyways, and we'll discuss in the comments.)

},

/// Value provided for a slot is not of the type annotated
#[error("value provided `{value}` for `{slot}` is not of type annotated `{ty}`")]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
#[error("value provided `{value}` for `{slot}` is not of type annotated `{ty}`")]
#[error("value provided `{value}` for `{slot}` is not of declared type `{ty}`")]

Comment on lines 898 to 901
/// Values of the slots
values: SlotEnv,
/// Generalized values of the slots
generalized_values: GeneralizedSlotEnv,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be simpler to store the values of all slots in a GeneralizedSlotEnv? EntityUIDs could be stored as a RestrictedExpr

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I spent some more time thinking about this and I think one of the consequences would be that now every time we have code involving templates we would have to handle the regular templates and the generalized case differently since the values of their slots appear in different fields and we would also have to enforce the invariant that only one of these HashMaps can have values in it. I was considering making the default value field of type HashMap<SlotId, RestrictedExpr> however, that would cause issues with the EST format.

One alternative solution in terms of making it easier for user's of the API not needing to supply values in two HashMaps and the potentially confusing error messages is to have the API handle the translation to our internal data structures.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I understand correctly, you think it would be better to have a single map for internal data structures, two maps for the EST, and handle the conversion during translation. That sounds fine to me.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you update this comment thread based on our out-of-band discussion?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After implementing a version of this PR that used a single HashMap to store all slot values, there were issues involved with need to introduce many unwrap calls to translate between RestrictedExpr and EntityUID. Therefore, the choice ultimately made is to internally represent values of templates using two HashMap's and for user convenience at the API level to expose a single HashMap from SlotId to RestrictedExpr. Internally, we will do this conversion to use two HashMap's and so therefore we have to maintain the invariant that in value there are only bindings for SlotId's ?principal and ?resource and in generalized_values there are only bindings for generalized slots.

Signed-off-by: Alan Wang <[email protected]>
Signed-off-by: Alan Wang <[email protected]>
Copy link
Contributor

@john-h-kastner-aws john-h-kastner-aws left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you say a little more about the relation between this and #1722. And I guess lay out more the implementation plan so I know what to expect coming in future PRs.

Signed-off-by: Alan Wang <[email protected]>
@alanwang67
Copy link
Contributor Author

alanwang67 commented Aug 1, 2025

Can you say a little more about the relation between this and #1722. And I guess lay out more the implementation plan so I know what to expect coming in future PRs.

The changes in #1722 only allow the current ?principal and ?resource slots to appear in the condition of the template. The changes there more or less don't touch any of the other stuff with user defined slots. The other parts of the PR are here. The changes there are to the typechecker (the typechecker struct stores a map from slot id's to validator types and uses that when typechecking slots), changes to the parser, EST, and evaluator (whenever we evaluate we carry around another the generalized_values map).

Comment on lines 898 to 901
/// Values of the slots
values: SlotEnv,
/// Generalized values of the slots
generalized_values: GeneralizedSlotEnv,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you update this comment thread based on our out-of-band discussion?

Copy link
Contributor

@john-h-kastner-aws john-h-kastner-aws left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a couple questions, but looks good


/// Struct storing the pairs of SlotId's and their corresponding type
#[derive(Clone, Eq, PartialEq, PartialOrd, Ord, Debug, Hash, Serialize, Deserialize)]
pub struct GeneralizedSlotsDeclaration(BTreeMap<SlotId, JSONSchemaType<RawName>>);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On a first pass it's not clear why we need this and ValidatorGeneralizedSlotsDeclaration. Probably worth a comment explaining this design

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gotcha, yeah will include a comment. The reason why we need both is that JSONSchemaType<RawName> can't be used by the typechecker and needs to be converted to a validator type by combing information from the Schema.

}

/// Construct a [`Type`] from [`json_schema::Type<RawName>`]
pub fn json_schema_type_to_validator_type(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this function not already exist somewhere else? I feel like it must

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the other place where a similar conversion occurs is the function from_schema_fragments in cedar-policy-core/src/validator/schema.rs but it's basically inlined within it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants