From e06224c053232b0648dd87e48e7cdd068ec4ae4e Mon Sep 17 00:00:00 2001 From: Craig Disselkoen Date: Thu, 15 Jun 2023 15:35:37 +0000 Subject: [PATCH 01/10] RFC 3 --- 0003-placeholders-in-conditions.md | 63 ++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 0003-placeholders-in-conditions.md diff --git a/0003-placeholders-in-conditions.md b/0003-placeholders-in-conditions.md new file mode 100644 index 00000000..d80cd012 --- /dev/null +++ b/0003-placeholders-in-conditions.md @@ -0,0 +1,63 @@ +- Start Date: 2023-06-15 +- Target Major Version: 2.x +- Reference Issues: https://github.com/cedar-policy/cedar/issues/81 +- Implementation PR: + +# Summary + +__Support template placeholders in conditions__ + +This RFC proposes to allow the `?principal` and `?resource` placeholders to appear in the `when`/`unless` conditions of a policy, not just in the policy scope. + +NB: This RFC was migrated from https://github.com/cedar-policy/cedar/issues/81 as our RFC process was introduced and formalized. + +# Basic example + +Example 1, from @WanderingStar + +``` +@id("Admin") +permit( + principal == ?principal, + action, + resource) +when { + // take any action in the scope that you're admin on + resource in ?resource + + // allow access up the tree for navigation + || (action == Action::"Navigate" && ?resource in resource) +}; +``` + +Example 2 + +``` +permit(principal, action, resource) +when { + ?principal has currentAccessLevel + && ?principal.currentAccessLevel >= 2 +}; +``` + +# Motivation + +With this change, we can write policies like the above that perform multiple checks on `?principal` or `?resource`. +We can also write policies that perform checks other than `in` or `==` on `?principal` or `?resource`, such as checking attributes of `?principal` or `?resource`. +In Example 1 above, this feature allows us to write this as a single template, rather than as two separate templates that we'd have to make sure are always instantiated together (which is an additional maintenance burden). +In Example 2, this feature allows us to write this as a template, where without this feature we'd be unable to use a template for this purpose. + +# Drawbacks + +- It becomes possible to write more complex Cedar policies, that may be harder for humans to read and reason about. + +## Non-drawbacks + +- Implementation cost in Cedar should be low. +- This is not a breaking change, either for Cedar syntax (all existing Cedar policies remain valid) or for Cedar APIs. + +# Alternatives + +In https://github.com/cedar-policy/cedar/issues/81, @mwhicks1 suggested that Example 1 could be accomplished with "template groups" instead. +With that solution, we would have two separate templates, but Cedar would provide a mechanism ensuring that they are always instantiated together, with the same arguments. +It's more difficult to see how template groups would solve the use-case in Example 2. From c49bfa59fb423f7ef9e1cd221de2632e227d5ba6 Mon Sep 17 00:00:00 2001 From: Craig Disselkoen Date: Fri, 16 Jun 2023 14:35:42 +0000 Subject: [PATCH 02/10] updates --- 0003-placeholders-in-conditions.md | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/0003-placeholders-in-conditions.md b/0003-placeholders-in-conditions.md index d80cd012..c5af66e2 100644 --- a/0003-placeholders-in-conditions.md +++ b/0003-placeholders-in-conditions.md @@ -47,6 +47,24 @@ We can also write policies that perform checks other than `in` or `==` on `?prin In Example 1 above, this feature allows us to write this as a single template, rather than as two separate templates that we'd have to make sure are always instantiated together (which is an additional maintenance burden). In Example 2, this feature allows us to write this as a template, where without this feature we'd be unable to use a template for this purpose. +# Non-proposals + +This RFC is _not_ proposing any change to what syntax is valid in the policy scope. For instance, the following remains _invalid_: +``` +permit( + principal, + action, + ?resource in resource +); +``` +The only valid uses of `?resource` in the policy scope are `resource == ?resource` and `resource in ?resource`, and this RFC is not proposing to change that. + +Of course, the above policy could be legally written as the following, in this RFC's proposal: +``` +permit(principal, action, resource) +when { ?resource in resource }; +``` + # Drawbacks - It becomes possible to write more complex Cedar policies, that may be harder for humans to read and reason about. @@ -60,4 +78,5 @@ In Example 2, this feature allows us to write this as a template, where without In https://github.com/cedar-policy/cedar/issues/81, @mwhicks1 suggested that Example 1 could be accomplished with "template groups" instead. With that solution, we would have two separate templates, but Cedar would provide a mechanism ensuring that they are always instantiated together, with the same arguments. -It's more difficult to see how template groups would solve the use-case in Example 2. +Unfortunately (as @mwhicks1 also pointed out, in a comment on this RFC), "template groups" would not alone be sufficient for Example 1, if the current restrictions on placeholders and the policy scope remain, because there would be no way to express the `?resource in resource` part. +Likewise, it's difficult to see how template groups would solve the use-case in Example 2. From a51fef9db9aa55c5ed7a78703144fea27794c24e Mon Sep 17 00:00:00 2001 From: Craig Disselkoen Date: Fri, 16 Jun 2023 16:02:41 +0000 Subject: [PATCH 03/10] move rfc into text/ folder --- .../0003-placeholders-in-conditions.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename 0003-placeholders-in-conditions.md => text/0003-placeholders-in-conditions.md (100%) diff --git a/0003-placeholders-in-conditions.md b/text/0003-placeholders-in-conditions.md similarity index 100% rename from 0003-placeholders-in-conditions.md rename to text/0003-placeholders-in-conditions.md From 9ddb3eeb66f684266eb39e6c3b235a834ef9e2a6 Mon Sep 17 00:00:00 2001 From: Mike Hicks Date: Fri, 23 Jun 2023 12:20:49 -0400 Subject: [PATCH 04/10] updated design to respond to feedback thus far --- text/0003-placeholders-in-conditions.md | 125 +++++++++++++++++------- 1 file changed, 88 insertions(+), 37 deletions(-) diff --git a/text/0003-placeholders-in-conditions.md b/text/0003-placeholders-in-conditions.md index c5af66e2..9f6305ae 100644 --- a/text/0003-placeholders-in-conditions.md +++ b/text/0003-placeholders-in-conditions.md @@ -1,55 +1,73 @@ -- Start Date: 2023-06-15 -- Target Major Version: 2.x +# Support template placeholders in conditions + +## Related issues and PRs + - Reference Issues: https://github.com/cedar-policy/cedar/issues/81 -- Implementation PR: +- Implementation PR(s): + +## Timeline + +- Start Date: 2023-06-15 +- Date Entered FCP: +- Date Accepted: +- Date Landed: # Summary -__Support template placeholders in conditions__ +This RFC proposes to generalize templates in two ways: -This RFC proposes to allow the `?principal` and `?resource` placeholders to appear in the `when`/`unless` conditions of a policy, not just in the policy scope. +1. The `?principal` and `?resource` placeholders may appear in the `when`/`unless` conditions of a policy when they also appear in the policy scope. -NB: This RFC was migrated from https://github.com/cedar-policy/cedar/issues/81 as our RFC process was introduced and formalized. +2. Placeholder variables are not limited to `?principal` and `?resource`. A general variable binding mechanism permits introducing additional placeholder variables in the policy, which can appear in the `when`/`unless` conditions. -# Basic example +# Basic examples -Example 1, from @WanderingStar +### Example 1: + +``` +permit( + principal in ?principal, + action, + resource) +when { + ?principal has currentAccessLevel + && ?principal.currentAccessLevel >= 2 +}; +``` +This template uses `?principal`, an ancestor of `principal`, to be used in the `when` clause (the condition) of the policy. Notice that `?principal` appears in the scope; this is required in order for it to also appear in the condition. + +### Example 2 ``` @id("Admin") +template ?bound permit( principal == ?principal, action, resource) when { // take any action in the scope that you're admin on - resource in ?resource + resource in ?bound // allow access up the tree for navigation - || (action == Action::"Navigate" && ?resource in resource) + || (action == Action::"Navigate" && ?bound in resource) }; ``` +This example introduces a fresh placeholder variable `?bound` which it uses in the condition. It is adapted from @WanderingStar [#81](https://github.com/cedar-policy/cedar/issues/81), in which the placeholder `?resource` is used instead of `?bound`. Using the `?resource` placholder is not allowed since it does not (and cannot) also appear in the scope. -Example 2 +# Motivation -``` -permit(principal, action, resource) -when { - ?principal has currentAccessLevel - && ?principal.currentAccessLevel >= 2 -}; -``` +This change expands the space of expressible templates. Applications can build more policies by template linking rather than by constructing the policies directly, e.g., by concatenating strings. There are two benefits of template-based policy construction: -# Motivation +1. It is easier to comprehensively analyze, whether manually or automatically, an application's permissions. If all policies are either static or linked templates, then an auditor or tool need only look at the policy store to explore the state of an application's possible permissions. If policies could be created on the fly, then an auditor or tool would have to _also_ analyze the application code. -With this change, we can write policies like the above that perform multiple checks on `?principal` or `?resource`. -We can also write policies that perform checks other than `in` or `==` on `?principal` or `?resource`, such as checking attributes of `?principal` or `?resource`. -In Example 1 above, this feature allows us to write this as a single template, rather than as two separate templates that we'd have to make sure are always instantiated together (which is an additional maintenance burden). -In Example 2, this feature allows us to write this as a template, where without this feature we'd be unable to use a template for this purpose. +2. Template-based policy construction prevents code injection attacks (i.e., in the style of SQL injection). Templates act like prepared statements, where placeholders can only be instantiated with data values; they cannot be instantiated with strings that could be reinterpreted as arbitrary policy code. Using templates whenever possible is an important protection for applications that add policies as a result of user actions. -# Non-proposals +This RFC was motivated by a particular customer use-case, i.e., [#81](https://github.com/cedar-policy/cedar/issues/81). -This RFC is _not_ proposing any change to what syntax is valid in the policy scope. For instance, the following remains _invalid_: +# Detailed design + +This RFC does not propose any change to what syntax is valid in the policy scope. For instance, the following remains _invalid_: ``` permit( principal, @@ -57,26 +75,59 @@ permit( ?resource in resource ); ``` -The only valid uses of `?resource` in the policy scope are `resource == ?resource` and `resource in ?resource`, and this RFC is not proposing to change that. +The only valid uses of `?resource` in the policy scope are `resource == ?resource` and `resource in ?resource`, and this RFC is not proposing to change that. This RFC also disallows the use of `?principal` and `?resource` in the condition if they don't _also_ appear in the scope. + +Both restrictions are in place because these two placeholder variables have special meaning for indexing policies in a policy store. In particular, looking _only_ at the linking information (i.e., to what `?principal` and/or `?resource` are bound) is sufficient to precisely select linked policies for particular requests. If the variables could only be bound in the condition or in other positions in the scope, and used in arbitrary contexts, it would lead to selecting superfluous policies. -Of course, the above policy could be legally written as the following, in this RFC's proposal: +Given these restrictions, the above policy would be impossible to write without support for placeholder variables beyond `?resource` and `?principal`. With their support, we can write: ``` +template ?lowerbound permit(principal, action, resource) -when { ?resource in resource }; +when { ?lowerbound in resource }; ``` +Here, the `template ?lowerbound` part _introduces_ a fresh placeholder variable which can be used legally within the policy template that follows. Eliding the `template ?lowerbound` prefix would be illegal, signaling an unbound variable. -# Drawbacks +Multiple variables can be introduced together, separated by commas, e.g., +``` +template ?user, ?resourcebound +permit(principal, action, resource) +when { + ?resourcebound in resource + && principal in ?user.delegates +}; +``` + +All introduced placeholder variables presented so far are assumed to be entities (just like `?principal` and `?resource`). The particular _type_ of the entity is not given, for simplicity. Eliding the type poses a problem for validating templates on their own; we would have to wait to validate a template each time it is linked. However, link-time validation makes it straightforward to allow placeholder variables to have any type (`string`, `bool`, etc.), not just entity types. + +Link-time validation has the benefit that it is lower overhead to write the templates, and potentially more flexible, since the particulars of the types don't need to be spelled out. New types added after the template is created that work with the template don't require changing the template. + +Link-time validation has three drawbacks, though. -- It becomes possible to write more complex Cedar policies, that may be harder for humans to read and reason about. +1. Fundamental errors in a template are not discovered until the template is first linked. E.g., `permit(principal in ?principal,action,resource) when { 1 < "hello" >}` will _always_ be invalid when linked, and it would be nice to know that as early as possible. -## Non-drawbacks +2. It may be more difficult to analyze the possible impact of a template by considering all of its possible safe linkages. Providing the types essentially puts useful bounds on how templates can be linked safely in the future. -- Implementation cost in Cedar should be low. -- This is not a breaking change, either for Cedar syntax (all existing Cedar policies remain valid) or for Cedar APIs. +3. Validation is more expensive at linkage time. If we provided types for placeholders, we could mostly validate the template when it is added to the store, and then just check at link time that the linkages to variables have the correct types. + +# Drawbacks + +This adds new syntax to the Cedar language. + +It relegates validation to link time, which has the drawbacks mentioned in the previous section. # Alternatives -In https://github.com/cedar-policy/cedar/issues/81, @mwhicks1 suggested that Example 1 could be accomplished with "template groups" instead. -With that solution, we would have two separate templates, but Cedar would provide a mechanism ensuring that they are always instantiated together, with the same arguments. -Unfortunately (as @mwhicks1 also pointed out, in a comment on this RFC), "template groups" would not alone be sufficient for Example 1, if the current restrictions on placeholders and the policy scope remain, because there would be no way to express the `?resource in resource` part. -Likewise, it's difficult to see how template groups would solve the use-case in Example 2. +One obvious alternative would be to annotate placeholder variables with types so that templates can be validated without knowing particular linkages. For example, we might rewrite the example above to be: +``` +template ?user:User, ?resourcebound:DeviceGroup +permit(principal, action, resource) +when { + ?resourcebound in resource + && principal in ?user.delegates +}; +``` +Here we have added `:User` and `:DeviceGroup` type annotations to the introduced variables. The validator can treat `?user` and `resourcebound` as respectively having these types when considering this template, e.g., to make sure that `?user` truly does have a `delegates` attribute. + +We could also allow the appearance of `?principal` and `?resource` in the list of variables, in order to give them types as well. As of now, this is probably not necessary because the types of these placeholder variables can be inferred from the schema (based on entity type given to `principal` and `resource` during "cross product" validation, and respectively the `memberOfTypes` portions of those entity types). + +This alternative is more work to implement and more effort for users, but mitigates the drawbacks mentioned above. \ No newline at end of file From 925d8a84270d72cb355a5df494ca968f64a1d4f4 Mon Sep 17 00:00:00 2001 From: Mike Hicks Date: Mon, 26 Jun 2023 10:36:25 -0400 Subject: [PATCH 05/10] alternative syntax --- text/0003-placeholders-in-conditions.md | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/text/0003-placeholders-in-conditions.md b/text/0003-placeholders-in-conditions.md index 9f6305ae..261384fb 100644 --- a/text/0003-placeholders-in-conditions.md +++ b/text/0003-placeholders-in-conditions.md @@ -81,15 +81,15 @@ Both restrictions are in place because these two placeholder variables have spec Given these restrictions, the above policy would be impossible to write without support for placeholder variables beyond `?resource` and `?principal`. With their support, we can write: ``` -template ?lowerbound +template (?lowerbound) permit(principal, action, resource) when { ?lowerbound in resource }; ``` -Here, the `template ?lowerbound` part _introduces_ a fresh placeholder variable which can be used legally within the policy template that follows. Eliding the `template ?lowerbound` prefix would be illegal, signaling an unbound variable. +Here, the `template (?lowerbound)` part _introduces_ a fresh placeholder variable which can be used legally within the policy template that follows. Eliding the `template (?lowerbound)` prefix would be illegal, signaling an unbound variable. Multiple variables can be introduced together, separated by commas, e.g., ``` -template ?user, ?resourcebound +template (?user, ?resourcebound) permit(principal, action, resource) when { ?resourcebound in resource @@ -117,17 +117,25 @@ It relegates validation to link time, which has the drawbacks mentioned in the p # Alternatives +## Type annotations + One obvious alternative would be to annotate placeholder variables with types so that templates can be validated without knowing particular linkages. For example, we might rewrite the example above to be: ``` -template ?user:User, ?resourcebound:DeviceGroup +template (?user:User, ?resourcebound:DeviceGroup) permit(principal, action, resource) when { ?resourcebound in resource && principal in ?user.delegates }; ``` -Here we have added `:User` and `:DeviceGroup` type annotations to the introduced variables. The validator can treat `?user` and `resourcebound` as respectively having these types when considering this template, e.g., to make sure that `?user` truly does have a `delegates` attribute. +Here we have added `:User` and `:DeviceGroup` type annotations to the introduced variables. The validator can treat `?user` and `?resourcebound` as respectively having these types when considering this template, e.g., to make sure that `?user` truly does have a `delegates` attribute. + +We could also allow the appearance of `?principal` and `?resource` in the list of variables, in order to give them types as well. Doing so is probably not necessary, however, because the types of these placeholder variables can be inferred from the schema (based on entity type given to `principal` and `resource` during "cross product" validation, and respectively the `memberOfTypes` portions of those entity types). + +This alternative is more work to implement and more effort for users, but mitigates the drawbacks mentioned above. + +## Use type parameter syntax -We could also allow the appearance of `?principal` and `?resource` in the list of variables, in order to give them types as well. As of now, this is probably not necessary because the types of these placeholder variables can be inferred from the schema (based on entity type given to `principal` and `resource` during "cross product" validation, and respectively the `memberOfTypes` portions of those entity types). +Rather than write `template (?user, ?resourcebound) permit ( principal, ...)` an alternative would be to write `permit( principal, ...)`. Using the `<...>` notation resembles parameterization on types used in other languages, as with C++ templates and Java generics. -This alternative is more work to implement and more effort for users, but mitigates the drawbacks mentioned above. \ No newline at end of file +The benefit of this syntax is that (1) it does not introduce a new keyword (`template`); (2) a normal policy looks closer to a "template" without parameters; and (3) it follows conventions similar to previous languages. I don't know if the parser would be any harder to write with this alternative syntax. \ No newline at end of file From 51eabca8730abf88b9e9a97e786da72912b3afda Mon Sep 17 00:00:00 2001 From: Mike Hicks Date: Mon, 26 Jun 2023 14:01:18 -0400 Subject: [PATCH 06/10] generalize title and description to match body --- text/0003-placeholders-in-conditions.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/text/0003-placeholders-in-conditions.md b/text/0003-placeholders-in-conditions.md index 261384fb..fa477964 100644 --- a/text/0003-placeholders-in-conditions.md +++ b/text/0003-placeholders-in-conditions.md @@ -1,4 +1,4 @@ -# Support template placeholders in conditions +# Generalizing template placeholders ## Related issues and PRs @@ -14,11 +14,11 @@ # Summary -This RFC proposes to generalize templates in two ways: +This RFC proposes to generalize templates in three ways: 1. The `?principal` and `?resource` placeholders may appear in the `when`/`unless` conditions of a policy when they also appear in the policy scope. -2. Placeholder variables are not limited to `?principal` and `?resource`. A general variable binding mechanism permits introducing additional placeholder variables in the policy, which can appear in the `when`/`unless` conditions. +2. Placeholder variables are not limited to `?principal` and `?resource`. A general variable binding mechanism permits introducing additional placeholder variables in the policy, which can appear in the `when`/`unless` conditions. Moreover, these additional variables need not be linked to values of entity type only; any Cedar type is acceptable. # Basic examples @@ -138,4 +138,4 @@ This alternative is more work to implement and more effort for users, but mitiga Rather than write `template (?user, ?resourcebound) permit ( principal, ...)` an alternative would be to write `permit( principal, ...)`. Using the `<...>` notation resembles parameterization on types used in other languages, as with C++ templates and Java generics. -The benefit of this syntax is that (1) it does not introduce a new keyword (`template`); (2) a normal policy looks closer to a "template" without parameters; and (3) it follows conventions similar to previous languages. I don't know if the parser would be any harder to write with this alternative syntax. \ No newline at end of file +The benefit of this syntax is that (1) it does not introduce a new keyword (`template`); (2) a normal policy looks closer to a "template" without parameters; and (3) it follows conventions similar to previous languages. I don't know if the parser would be any harder to write with this alternative syntax. From 461ecf86a8b835e0a848aa47347e4015151db623 Mon Sep 17 00:00:00 2001 From: Mike Hicks Date: Tue, 27 Jun 2023 12:56:19 -0400 Subject: [PATCH 07/10] added missing parens --- text/0003-placeholders-in-conditions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0003-placeholders-in-conditions.md b/text/0003-placeholders-in-conditions.md index fa477964..5227891a 100644 --- a/text/0003-placeholders-in-conditions.md +++ b/text/0003-placeholders-in-conditions.md @@ -40,7 +40,7 @@ This template uses `?principal`, an ancestor of `principal`, to be used in the ` ``` @id("Admin") -template ?bound +template (?bound) permit( principal == ?principal, action, From 673705a3e23d9e238413188dce09d1a2fd5b36dd Mon Sep 17 00:00:00 2001 From: Mike Hicks Date: Tue, 27 Jun 2023 13:03:37 -0400 Subject: [PATCH 08/10] including ?principal/?resource in list is optional --- text/0003-placeholders-in-conditions.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/text/0003-placeholders-in-conditions.md b/text/0003-placeholders-in-conditions.md index 5227891a..95dc05b3 100644 --- a/text/0003-placeholders-in-conditions.md +++ b/text/0003-placeholders-in-conditions.md @@ -18,7 +18,7 @@ This RFC proposes to generalize templates in three ways: 1. The `?principal` and `?resource` placeholders may appear in the `when`/`unless` conditions of a policy when they also appear in the policy scope. -2. Placeholder variables are not limited to `?principal` and `?resource`. A general variable binding mechanism permits introducing additional placeholder variables in the policy, which can appear in the `when`/`unless` conditions. Moreover, these additional variables need not be linked to values of entity type only; any Cedar type is acceptable. +2. Placeholder variables are not limited to `?principal` and `?resource`. A general variable binding mechanism permits introducing additional placeholder variables in the policy, which can appear in the `when`/`unless` conditions. Moreover, these additional variables need not be linked to values of entity type only; any Cedar type is acceptable. Including `?principal` and/or `?resource` in this list is optional; restrictions on their use are enforced either way. # Basic examples @@ -97,6 +97,10 @@ when { }; ``` +Policy writers can choose to include `?principal` and/or `?resource` in the list of template variables if they like, but doing so is not required. These two variables retain their special status. + +If a variable is included in the list that does not appear in the actual template, we can issue a warning. + All introduced placeholder variables presented so far are assumed to be entities (just like `?principal` and `?resource`). The particular _type_ of the entity is not given, for simplicity. Eliding the type poses a problem for validating templates on their own; we would have to wait to validate a template each time it is linked. However, link-time validation makes it straightforward to allow placeholder variables to have any type (`string`, `bool`, etc.), not just entity types. Link-time validation has the benefit that it is lower overhead to write the templates, and potentially more flexible, since the particulars of the types don't need to be spelled out. New types added after the template is created that work with the template don't require changing the template. From b541d4f98978431cfb07da0f7718ba487bb2565b Mon Sep 17 00:00:00 2001 From: Mike Hicks Date: Wed, 28 Jun 2023 09:26:02 -0400 Subject: [PATCH 09/10] clarified that link-time validation is a breaking change --- text/0003-placeholders-in-conditions.md | 30 +++++++++++++++++-------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/text/0003-placeholders-in-conditions.md b/text/0003-placeholders-in-conditions.md index 95dc05b3..db95c388 100644 --- a/text/0003-placeholders-in-conditions.md +++ b/text/0003-placeholders-in-conditions.md @@ -14,12 +14,14 @@ # Summary -This RFC proposes to generalize templates in three ways: +This RFC proposes to generalize templates in two ways: -1. The `?principal` and `?resource` placeholders may appear in the `when`/`unless` conditions of a policy when they also appear in the policy scope. +1. The `?principal` and `?resource` placeholders may appear in the `when`/`unless` conditions of a policy when they also appear in the policy scope. 2. Placeholder variables are not limited to `?principal` and `?resource`. A general variable binding mechanism permits introducing additional placeholder variables in the policy, which can appear in the `when`/`unless` conditions. Moreover, these additional variables need not be linked to values of entity type only; any Cedar type is acceptable. Including `?principal` and/or `?resource` in this list is optional; restrictions on their use are enforced either way. +Because the types of placeholder variables (other than `?principal` and `?resource`) are not known, any validation of templates cannot occur until _link time_. Currently, templates are mostly validated when created, with only a small check carried out at link time. + # Basic examples ### Example 1: @@ -75,17 +77,19 @@ permit( ?resource in resource ); ``` -The only valid uses of `?resource` in the policy scope are `resource == ?resource` and `resource in ?resource`, and this RFC is not proposing to change that. This RFC also disallows the use of `?principal` and `?resource` in the condition if they don't _also_ appear in the scope. +The only valid uses of `?resource` in the policy scope are `resource == ?resource` and `resource in ?resource`, and this RFC is not proposing to change that. This RFC also disallows the use of `?principal` and `?resource` in the condition if they don't _also_ appear in the scope. + +Both restrictions are in place because these two placeholder variables have special meaning for indexing policies in a policy store. In particular, looking _only_ at the linking information (i.e., to what `?principal` and/or `?resource` are bound) is sufficient to precisely select linked policies for particular requests. If the variables could only be bound in the condition or in other positions in the scope, and used in arbitrary contexts, it would lead to selecting superfluous policies. -Both restrictions are in place because these two placeholder variables have special meaning for indexing policies in a policy store. In particular, looking _only_ at the linking information (i.e., to what `?principal` and/or `?resource` are bound) is sufficient to precisely select linked policies for particular requests. If the variables could only be bound in the condition or in other positions in the scope, and used in arbitrary contexts, it would lead to selecting superfluous policies. +## Fresh placeholder variables -Given these restrictions, the above policy would be impossible to write without support for placeholder variables beyond `?resource` and `?principal`. With their support, we can write: +To support a policy like the one above, this RFC proposes support for placeholder variables beyond `?resource` and `?principal`. With their support, we can write: ``` template (?lowerbound) permit(principal, action, resource) when { ?lowerbound in resource }; ``` -Here, the `template (?lowerbound)` part _introduces_ a fresh placeholder variable which can be used legally within the policy template that follows. Eliding the `template (?lowerbound)` prefix would be illegal, signaling an unbound variable. +Here, the `template (?lowerbound)` part _introduces_ a fresh placeholder variable which can be used legally within the policy template that follows. Eliding the `template (?lowerbound)` prefix would be illegal, signaling an unbound variable. Multiple variables can be introduced together, separated by commas, e.g., ``` @@ -101,9 +105,15 @@ Policy writers can choose to include `?principal` and/or `?resource` in the list If a variable is included in the list that does not appear in the actual template, we can issue a warning. -All introduced placeholder variables presented so far are assumed to be entities (just like `?principal` and `?resource`). The particular _type_ of the entity is not given, for simplicity. Eliding the type poses a problem for validating templates on their own; we would have to wait to validate a template each time it is linked. However, link-time validation makes it straightforward to allow placeholder variables to have any type (`string`, `bool`, etc.), not just entity types. +While all introduced placeholder variables presented so far are assumed to be entities (just like `?principal` and `?resource`), there is no reason why they cannot have arbitrary type. The particular type of the placeholder need not be given, for simplicity. + +## Validation -Link-time validation has the benefit that it is lower overhead to write the templates, and potentially more flexible, since the particulars of the types don't need to be spelled out. New types added after the template is created that work with the template don't require changing the template. +Eliding the type of a placeholder variable poses a problem for validating templates when they are created, without considering particular linkages. This is because we cannot validate an expression like `?bound.name` without knowing the type of `?bound`. + +Instead, validation of templates involving variables beyond `?principal` and `?resource` can be deferred to link time. At that point, we know the exact type of the variable, based on the value it is linked to. This is true whether the variable is an entity or a primitive like `string`, `bool`, etc. + +Link-time validation has the benefit that it is lower overhead to write the templates, and potentially more flexible, since the particulars of the types don't need to be spelled out, and could even be different for two linkages of the same template. New entity types created after the template don't necessarily require changing the template to add a type annotation. Link-time validation has three drawbacks, though. @@ -113,11 +123,13 @@ Link-time validation has three drawbacks, though. 3. Validation is more expensive at linkage time. If we provided types for placeholders, we could mostly validate the template when it is added to the store, and then just check at link time that the linkages to variables have the correct types. +To avoid link-time validation being a breaking change, we can still do creation-time validation when the only variables are `?principal` and `?resource`. + # Drawbacks This adds new syntax to the Cedar language. -It relegates validation to link time, which has the drawbacks mentioned in the previous section. +It relegates (most) validation to link time, which has the drawbacks mentioned in the previous section. # Alternatives From 4e27108e1425480ed8ace9b7db66526a99c9a5d9 Mon Sep 17 00:00:00 2001 From: Craig Disselkoen Date: Wed, 28 Jun 2023 15:58:50 +0000 Subject: [PATCH 10/10] add FCP date --- text/0003-placeholders-in-conditions.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/text/0003-placeholders-in-conditions.md b/text/0003-placeholders-in-conditions.md index db95c388..db7efeb8 100644 --- a/text/0003-placeholders-in-conditions.md +++ b/text/0003-placeholders-in-conditions.md @@ -3,12 +3,12 @@ ## Related issues and PRs - Reference Issues: https://github.com/cedar-policy/cedar/issues/81 -- Implementation PR(s): +- Implementation PR(s): ## Timeline - Start Date: 2023-06-15 -- Date Entered FCP: +- Date Entered FCP: 2023-06-28 - Date Accepted: - Date Landed: @@ -28,8 +28,8 @@ Because the types of placeholder variables (other than `?principal` and `?resour ``` permit( - principal in ?principal, - action, + principal in ?principal, + action, resource) when { ?principal has currentAccessLevel @@ -95,8 +95,8 @@ Multiple variables can be introduced together, separated by commas, e.g., ``` template (?user, ?resourcebound) permit(principal, action, resource) -when { - ?resourcebound in resource +when { + ?resourcebound in resource && principal in ?user.delegates }; ``` @@ -139,8 +139,8 @@ One obvious alternative would be to annotate placeholder variables with types so ``` template (?user:User, ?resourcebound:DeviceGroup) permit(principal, action, resource) -when { - ?resourcebound in resource +when { + ?resourcebound in resource && principal in ?user.delegates }; ``` @@ -152,6 +152,6 @@ This alternative is more work to implement and more effort for users, but mitiga ## Use type parameter syntax -Rather than write `template (?user, ?resourcebound) permit ( principal, ...)` an alternative would be to write `permit( principal, ...)`. Using the `<...>` notation resembles parameterization on types used in other languages, as with C++ templates and Java generics. +Rather than write `template (?user, ?resourcebound) permit ( principal, ...)` an alternative would be to write `permit( principal, ...)`. Using the `<...>` notation resembles parameterization on types used in other languages, as with C++ templates and Java generics. The benefit of this syntax is that (1) it does not introduce a new keyword (`template`); (2) a normal policy looks closer to a "template" without parameters; and (3) it follows conventions similar to previous languages. I don't know if the parser would be any harder to write with this alternative syntax.