From 5a50a2ab717ef4d57fa2005d9c02372e09f1f621 Mon Sep 17 00:00:00 2001 From: Shaobo He Date: Tue, 20 Feb 2024 22:02:44 -0800 Subject: [PATCH 1/9] Added a draft for human-readable schema format --- .../_schema/human-readable-schema-grammar.md | 50 ++ .../_schema/human-readable-schema.md | 226 ++++++ ...hema-grammar.md => json-schema-grammar.md} | 2 +- docs/collections/_schema/json-schema.md | 693 ++++++++++++++++++ docs/collections/_schema/schema.md | 686 +---------------- 5 files changed, 1005 insertions(+), 652 deletions(-) create mode 100644 docs/collections/_schema/human-readable-schema-grammar.md create mode 100644 docs/collections/_schema/human-readable-schema.md rename docs/collections/_schema/{schema-grammar.md => json-schema-grammar.md} (99%) create mode 100644 docs/collections/_schema/json-schema.md diff --git a/docs/collections/_schema/human-readable-schema-grammar.md b/docs/collections/_schema/human-readable-schema-grammar.md new file mode 100644 index 0000000..36857cd --- /dev/null +++ b/docs/collections/_schema/human-readable-schema-grammar.md @@ -0,0 +1,50 @@ +--- +layout: default +title: Human-Readable schema grammar +nav_order: 2 +--- + + +# Grammar specification for the human-readable schema format {#schema-grammar} +{: .no_toc } + +This topic describes the grammar specification for the human-readable schema format. For a more complete description, see [Schema format](../schema/human-readable-schema-format). + +The grammar applies the following the conventions. Capitalized words stand for grammar productions, and lexical tokens are given in all-caps. When productions or tokens match those in the Cedar policy grammar, we use the same names (e.g., `IDENT` and `Path`). + +For grammar productions it uses `|` for alternatives, `[]` for optional productions, `()` for grouping, and `{}` for repetition of a form zero or more times. + +Tokens are defined using regular expressions, where `[]` stands for character ranges; `|` stands for alternation; `*` , `+` , and `?` stand for zero or more, one or more, and zero or one occurrences, respectively; `~` stands for complement; and `-` stands for difference. The grammar ignores whitespace and comments. + +The grammar adopts the same string escaping rules as Cedar policy grammar. + +``` +Schema := {Namespace} +Namespace := ('namespace' Path '{' {Decl} '}') | Decl +Decl := Entity | Action | TypeDecl +Entity := 'entity' Idents ['in' EntOrTyps] [['='] RecType] ';' +Action := 'action' Names ['in' RefOrRefs] [AppliesTo]';' +TypeDecl := 'type' IDENT '=' Type ';' +Type := Path | SetType | RecType +EntType := Path +SetType := 'Set' '<' Type '>' +RecType := '{' [AttrDecls] '}' +AttrDecls := Name ['?'] ':' Type [',' | ',' AttrDecls] +AppliesTo := 'appliesTo' '{' AppDecls '}' +AppDecls := ('principal' | 'resource') ':' EntOrTyps [',' | ',' AppDecls] + | 'context' ':' RecType [',' | ',' AppDecls] +Path := IDENT {'::' IDENT} +Ref := Path '::' STR | Name +RefOrRefs := Ref | '[' [RefOrRefs] ']' +EntTypes := Path {',' Path} +EntOrTyps := EntType | '[' [EntTypes] ']' +Name := IDENT | STR +Names := Name {',' Name} +Idents := IDENT {',' IDENT} + +IDENT := ['_''a'-'z''A'-'Z']['_''a'-'z''A'-'Z''0'-'9']* +STR := Fully-escaped Unicode surrounded by '"'s +PRIMTYPE := 'Long' | 'String' | 'Bool' +WHITESPC := Unicode whitespace +COMMENT := '//' ~NEWLINE* NEWLINE +``` \ No newline at end of file diff --git a/docs/collections/_schema/human-readable-schema.md b/docs/collections/_schema/human-readable-schema.md new file mode 100644 index 0000000..f0b8f72 --- /dev/null +++ b/docs/collections/_schema/human-readable-schema.md @@ -0,0 +1,226 @@ +--- +layout: default +title: Human-Readable schema format +nav_order: 2 +--- + +# Human-Readable schema format {#schema} +{: .no_toc } + +
+ + Topics on this page + + {: .text-delta } +- TOC +{:toc} +
+ +This topic describes Cedar's human-readable schema format. + +## Schema format {#schema-format} + +A schema consists of zero or more namespaces, each of which contains declarations of three types --- *Entity Declaration*, *Action Declaration*, and *Common Type Declaration*. These declarations define entity types, actions, and common types used to define the former two, respectively. Declarations are delimited by `;`s. Note that unlike the JSON schema format, the human-readable schema format allows you to write Cedar-style comments. + +## NameSpace {#schema-namespace} + +You can group the declarations of a namespace by wrapping them with curly braces like `namespace Foo {...}`. A name must be given for a namespace specified by such syntax. An alternative way to declare a namespace is to “inline” declarations like `entity Bar; namespace Foo {...}`, where the entity declaration of `Bar` is under the same scope of namespace `Foo`. Names of these declarations are always referred as they are whereas declaration names of a `namespace` construct must be referred as their fully-qualified forms in other namespaces. + +Multiple `namespace` declarations with the same names are disallowed. This rule also applies to the inner declarations like entity type declarations. + +## Entity type {#schema-entityTypes} + +The following entity type declaration specifies an entity type `User` , whose parent entity type is `Group`. Entities of type `User` have three attributes, `personalGroup` of type `Group`, `delegate` of type `User`, and `blocked` of type `Set`, respectively. The attribute `delegate` is optional, which is specified by the `?` after the attribute name. + +``` +entity User in [Group] { + personalGroup: Group, + delegate?: User, + blocked: Set, +}; +``` + +Note that, unlike the JSON schema format, human-readable schema syntax allows you to declare multiple entity types that share the same definition using a single declaration. For example, `entity UserA, UserB, UserC ...` declares entity types `UserA`, `UserB`, and `UserC` that all have the same membership relations and shapes. + +### Membership relations {#schema-entitytypes-memberOf} + +The parent types are specified by `in ` after `entity `, where `EntityTypes` can be a list of entity type names surrounded by brackets and delimited by `,`, or an entity type name if there is only one parent type. For example, you can also declare entity type `User` like `entity User in Group ...`. The membership relation declaration is optional, whose absence means that the declared entity type does not have any parent entity types. + +### Shape {#schema-entitytypes-shape} + +You specify the shape of an entity type using the [record syntax](../policies/syntax-datatypes.html#datatype-record) of Cedar policies. That is, attribute declarations are enclosed by brackets, each of which is a `:` pair. Attribute names are either identifiers or strings. Such a declaration also defines a record type. We will visit schema type syntax later in the document. To make entity type declarations consistent with common type declarations, users can write a `=` before attribute declarations like `entity User = {...};`. + +Note that if you omit attribute declarations, then entities of this type do not have any attributes. This is equivalent to specifying an empty record (i.e., `{}`). + + +### Schema types {#schema-types} + +The corresponding type names of Cedar data types [Boolean](../policies/syntax-datatypes.html#datatype-boolean), [String](../policies/syntax-datatypes.html#datatype-string), [Long](../policies/syntax-datatypes.html#datatype-string) are `Bool`, `String`, `Long`, respectively. An entity type or an extension type is specified by its name. The entity type name is either an identifier or identifiers separated by `::`. For example, both `User` and `ExampleCo::User` are both valid entity type names. An extension type name is just an identifier, specifically, `ipaddr` or `decimal` as of now. + +We detail the declarations of composite data types as follows. + +#### Record {#schema-entitytypes-shape-record} +{: .no_toc } + +The specification of a record type is similar to that of a Cedar record, except that "values" of the former are types. For example, you can declare a record type as follows. + +``` +{ + name: String, + features: { + age: Long, + height: Long, + eyecolor: String + } +} +``` + +#### Set {#schema-entitytypes-shape-set} +{: .no_toc } + +A set type declaration consists of keyword `Set` and an element type surrounded by brackets (`<>`). For example, `Set` represents a set of `Long`s. + + +## Actions {#schema-actions} + +The following action declaration specifies an action `ViewDocument`. It is a child action of action group `ReadActions` and applies to principals of entity type `User` and `Public`, resources of entity type `Document` and contexts of record type `{ network: ipaddr, browser: String}`. + +``` +action ViewDocument in [ReadActions] appliesTo { + principal: [User,Public], + resource: Document, + context: { + network: ipaddr, + browser: String + } +}; +``` + +An action name is either an identifier or a string. The membership relation syntax of action declarations is akin to that of entity declarations. The difference is that action names could be strings but entity type names must be identifiers. + +The `appliesTo` construct specifies an action's applicability. It is a record of three optional keys: `principal`, `resource`, and `context` that specifies principals, resources, and contexts to which the action apply. Absence of the `appliesTo` construct means that the actions do not apply to any principals/resources/contexts. `principal` or `resource` keys, if given, must an entity type or a non-empty list of entity types. Absence of `principal` or `resource` keys means that the action applies to *unspecified* principals or resources, respectively. +The `context` value must be a record and its absence defaults to an empty record. + +## Common types {#schema-commonTypes} + +Like the JSON schema format, human-readable schema syntax allows for declarations of common types so that entity type declarations can use them to avoid error-prone duplication. The syntax of common type declarations is similar to defining type aliases in most programming languages: `type = ` . The right hand side of `=` is a schema type name except for common types to avoid definitional circularity. + +## Type name disambiguation + +Type names in the human-readable schema format can conflict with each other. For example, `ipaddr` is a valid unqualified common type name as well as an extension type name. `Foo::Bar` is a valid qualified common type name and an entity type name. We use the following rules to disambiguate type names. + +1. Primitive and extension type names cannot alias by design. +2. Type references are resolved in a priority order. +3. Reserve `__cedar` as a namespace to disambiguate extension/primitive types from others. For example, `__cedar::Long` uniquely refers to Cedar primitive type `Long`. + +We elaborate the second rule as the others are obvious. The priority order is common type > entity type > primitive/extension type. In other words, a type name is resolved by checking if it is declared as a common type, then entity type, and finally a primitive or extension type. The following example demonstrate this rule. + +``` +namespace Demo { + entity Host { + // the type of attribute `ip` is common type `ipaddr` + // instead of extension type `__cedar::ipaddr` + // because the former has a higher priority + ip: ipaddr, + // the type of attribute `bandwidth` is extension type `decimal` + // because there is not any common type or entity type + // that shares the same name + bandwidth: decimal, + }; + // An artificial entity type name that conflicts with + // primitive type `String` + entity String { + groups: Set<__cedar::String>, + }; + // A common type name that conflicts with extension + // type `ipaddr` + type ipaddr = { + // The type of attribute `repr` is the entity type + // `String` declared above instead of primitive type + // `__cedar::String` because the former has a higher + // priority + repr: String, + // The type of attribute `isV4` is the primitive type + // `Bool` because there is not any common type or + // entity type that shares the same name + isV4: Bool, + }; +} +``` + + Note that both common types and entity types can be qualified with namespaces. And the human-readable format allows "inline" declarations. So, there may be conflicts between type names declared within a namespace and those declared using "inline" declarations. The resolution rule for this scenario is akin to static scoping. That is, type names within the same namespace have higher priority. The following example demonstrates this rule. + + ``` +type id = { + group: String, + name: String, +}; + +type email_address = { + id: String, + domain: String, +}; + +namespace Demo { + entity User { + // The type of attribute `name` is the primitive type `String` + // because there is a common type declaration below. + name: id, + // The type of attribute `email` is the common type `email_address` + // declared above. + email: email_address; + }; + type id = String; +} +``` + +## Example schema {#schema-examples} + +The following schema is for the hypothetical application PhotoFlash. + +``` +namespace PhotoFlash { + entity User in UserGroup = { + "department": String, + "jobLevel": Long, + }; + entity UserGroup; + entity Album in Album = { + "account": Account, + "private": Bool, + }; + entity Account = { + "admins"?: Set, + "owner": User, + }; + entity Photo in Album = { + "account": Account, + "private": Bool, + }; + action "uploadPhoto" appliesTo { + principal: User, + resource: Album, + context: { + "authenticated": Bool, + "photo": { + "file_size": Long, + "file_type": String, + }, + } + }; + action "viewPhoto" appliesTo { + principal: User, + resource: Photo, + context: { + "authenticated": Bool, + } + }; + action "listAlbums" appliesTo { + principal: User, + resource: Account, + context: { + "authenticated": Bool, + } + }; +} +``` \ No newline at end of file diff --git a/docs/collections/_schema/schema-grammar.md b/docs/collections/_schema/json-schema-grammar.md similarity index 99% rename from docs/collections/_schema/schema-grammar.md rename to docs/collections/_schema/json-schema-grammar.md index 34caebc..68f07bc 100644 --- a/docs/collections/_schema/schema-grammar.md +++ b/docs/collections/_schema/json-schema-grammar.md @@ -1,6 +1,6 @@ --- layout: default -title: Schema grammar +title: JSON schema grammar nav_order: 2 --- diff --git a/docs/collections/_schema/json-schema.md b/docs/collections/_schema/json-schema.md new file mode 100644 index 0000000..5136dee --- /dev/null +++ b/docs/collections/_schema/json-schema.md @@ -0,0 +1,693 @@ +--- +layout: default +title: JSON schema format +nav_order: 2 +--- + +# JSON schema format {#schema} +{: .no_toc } + +
+ + Topics on this page + + {: .text-delta } +- TOC +{:toc} +
+ +This topic describes Cedar's JSON schema format. + +## Schema format {#schema-format} + +A schema contains a declaration of one or more namespaces, each of which contains two mandatory JSON objects, `entityTypes` and `actions`. A namespace declaration can optionally include a third object, `commonTypes`, which defines types that can be referenced by the other two objects. We consider the format of namespaces and these three objects next. + +## NameSpace {#schema-namespace} + +A [namespace](../overview/terminology.html#term-namespaces) declaration identifies and defines a scope for all entity types and actions declared within it. The namespace is a string that uses double colons \(`::`\) as separators between its elements, which must be identifiers. A namespace can be empty (i.e., the empty string). + +{: .important } +>The namespace name must be normalized and cannot include any embedded whitespace, such as spaces, newlines, control characters, or comments. + +A namespace declaration contains a comma-separated list of JSON objects within braces `{ }`. The following is an example of a namespace declaration: + +A namespace declaration must contain two child elements, and may contain a third, appearing in any order: + ++ [`entityTypes`](#schema-entityTypes) ++ [`actions`](#schema-actions) ++ [`commonTypes`](#schema-commonTypes) (optional) + +You define the types of your application's principal and resource entities within the `entityTypes` element, and the specific actions in the `actions` element. Principals and resources are separated from actions because actions are defined in the schema as individual discrete elements (each of which has type `Action`), whereas only the `principal` and `resource` entities' *types* are defined. In your entity store you create individual principal and resource entities that have these types. Optionally, you can define type names in `commonTypes` and reference those names as types in the `entityTypes` and `actions` elements of your schema. + +The declared namespace is automatically prepended to all types defined within the associated scope. For example, consider the following schema: + +```json +{ + "ExampleCo::Database": { + "entityTypes": { + "Table": { + ... + } + }, + "actions": { + "createTable": { + ... + } + } + } +} +``` + +Here, the schema is effectively defining the action entity `ExampleCo::Database::Action::"createTable"` and the entity type `ExampleCo::Database::Table`. + +You can reference entity types and actions defined in other namespaces of the same schema by using their fully qualified names. For example, here is a schema that declares two namespaces, `ExampleCo::Clients` and `ExampleCo::Furniture`, where the second namespace's entity type `Table` references the first's entity type `Manufacturer`. + +```json +{ + "ExampleCo::Clients": { + "entityTypes": { + "Manufacturer": { ... } + }, + "actions": { ... } + }, + "ExampleCo::Furniture": { + "entityTypes": { + "Table": { + "shape": { + "type": "Record", + "attributes": { + "manufacturer": { + "type": "Entity", + "name": "ExampleCo::Clients::Manufacturer" + } + } + } + } + }, + "actions": { ... } + } +} +``` + +If you change a declared namespace in your schema you will need to change the entity types appearing in your policies and/or in other namespaces declared in your schema to instead reference the changed namespace. + +## `entityTypes` {#schema-entityTypes} + +A collection of the `principal` and `resource` entity types supported by your application. The `entityTypes` element contains a comma-separated list of JSON objects. + +The high-level structure of an `entityTypes` entry looks like the following example. + +```json +"entityTypes": { + "EntityTypeName1": { + "memberOfTypes": [ "parentGroupTypeName1", "parentGroupTypeName2", … ], + "shape": { … } + }, + "EntityTypeName2": { + "memberOfTypes": [ "parentGroupTypeName1", "parentGroupTypeName2", … ], + "shape": { … } + } +} +``` + +Each entry in the `entityTypes` is a JSON object with the following properties. + +### Entity type name {#schema-entitytypes-name} + +Specifies the name of the entity type as a string. This type name must be an identifier, which is defined in the Cedar grammar as a sequence of alphanumeric characters, omitting any Cedar reserved words. + +{: .important } +>The entity type name must be normalized and cannot include any embedded whitespace, such as spaces, newlines, control characters, or comments. + +```json +"My::Name::Space": { + "entityTypes": { + "UserGroup": { ... } // New entity type name + } +} +``` + +This type name is qualified by its [namespace](#schema-namespace) to form a fully qualified entity type which must be used when referencing this type in a policy. + +```json +"My::Name::Space::UserGroup" +``` + +### `memberOfTypes` {#schema-entitytypes-memberOf} + +Specifies a list of entity types that can be direct parents of entities of this type. Values in this list must be valid entity type names declared in the schema. If the `memberOfTypes` element is empty or not defined, then entities of that entity type can't have any parents in the entity hierarchy. The following example shows that an entity of type `User` can have parents of type `UserGroup`. + +```json +"entityTypes": { + "UserGroup": { + … + }, + "User": { + "memberOfTypes": [ "UserGroup" ], + … + } +} +``` + +If the parent type is part of the same namespace as the child type, then you can reference simply the parent type's name. If the parent type is in a different namespace than the child type, then you must include the parent type's namespace as part of the reference. The following example references two parent types, both named `UserGroup`. The first `UserGroup` is part of the same namespace as the child entity type that this entry is part of. The second `UserGroup` is defined in the namespace `Aws::Cognito::UserPool_1`. + +```json +"memberOfTypes": [ + "UserGroup", + "Aws::Cognito::UserPool_1::UserGroup" +] +``` + +### `shape` {#schema-entitytypes-shape} + +Specifies the shape of the data stored in entities of this type. The `shape` element, if present, must have type `Record`, and be accompanied by a description of the entity's attributes. The following example shows a simple specification of the `User` entity type. + +```json +"User" : { + "shape" : { + "type" : "Record", + "attributes" : { + "name" : { + "type" : "String" + }, + "age" : { + "type" : "Long" + } + } + } +} +``` + +Each attribute in the `attributes` portion of the `shape` record must follow the format described next. + +Note that if the `shape` element is omitted, then entities of the type being defined are assumed to have no data stored with them. This is equivalent to specifying a `Record` with `{}` for its attributes. + +### Attribute specifications {#schema-attributes-specs} + +Each attribute in a `Record` is a JSON object that describes one attribute in the record associated with entities of this type. It has the form + +```json + "name" : { + "type" : "Type" + }, +``` + +where `name` is the name of the attribute, and `Type` is one of the [Cedar supported data types](../policies/syntax-datatypes.html), discussed in detail below. + +You can choose to specify whether an attribute is required or optional. By default, attributes that you define are required. This means that policies that reference this type can assume that the attribute is always present. You can make an attribute optional by adding `"required": false` to the attribute description. Here is an example: + +```json +"jobLevel": { + "type": "Long", + "required": false +}, +``` + +A policy should check for an optional attribute's presence by using the [`has`](../policies/syntax-operators.html#operator-has) operator before trying to access the attribute's value. If evaluation of a policy results in an attempt to access a non-existent attribute, evaluation fails with an error (which causes the policy to be ignored during authorization, and for a diagnostic to be generated). The validator will flag the potential for such errors to occur. + +You can choose to explicitly declare that an attribute is mandatory by including `"required": true` (but this is unnecessary as mandatory attributes are the default). + +### Attribute types {#schema-attributes-types} + +Attributes' `type` components can be `"String"`, `"Long"`, `"Boolean"`, `"Record"`, `"Set"`, `"Entity"`, or `"Extension"`. The first three require no further information to be specified. The latter four are described below. + +#### `Record` {#schema-entitytypes-shape-record} +{: .no_toc } + +A record attribute has the same JSON format as the [entity `shape`'s record's attributes](#schema-attributes-specs). As an example, the following refactors the `User` entity specification above to have a `features` attribute that is a `Record` containing some of the user's physical features. + +```json +"User" : { + "shape" : { + "type" : "Record", + "attributes" : { + "name" : { + "type" : "String" + }, + "features" : { + "type": "Record", + "attributes": { + "age" : { + "type" : "Long" + }, + "height": { + "type" : "Long" + }, + "eyecolor": { + "type": "String" + } + } + } + } + } +} +``` + +#### `Entity` {#schema-entitytypes-shape-entity} +{: .no_toc } + +For attributes of type `"Entity"`, you must also specify a `name` that identifies the entity type of this attribute. The entity type must be defined in the schema. For example, a resource entity might require an `Owner` element that specifies a `User`. + +```json +"Document" : { + "shape" : { + "type" : "Record", + "attributes" : { + "Owner": { + "type": "Entity", + "name": "User" + } + } + } +} +``` + +#### `Set` {#schema-entitytypes-shape-set} +{: .no_toc } + +For attributes with type `"Set"`, you must also specify an `element` that defines the properties of the members of the set. Each element is a JSON object that describes what each member of the set looks like. + +An `element` must contain a structure formatted according to the same rules as an attribute. As an example, consider the following `Admins` entry which could be part of the `shape` record of an `Account` entity type. This `Admins` element is a set of entities of type `User` and could be used to define which users have administrator permissions in the account. + +```json +"Group" : { + "shape" : { + "type" : "Record", + "attributes": { + "Admins": { + "type": "Set", + "element": { + "type": "Entity", + "name": "User" + } + } + } + } +} +``` + +#### `Extension` {#schema-entitytypes-shape-extension} +{: .no_toc } + +For attributes of type `"Extension"`, you must also specify the `name` of the specific extension type. +There are two extension types: `ipaddr` for IP address values, and `decimal` for decimal values. +For example, a `Network` entity may include the IP address of its gateway. + +```json +"Network": { + "shape": { + "type": "Record", + "attributes": { + "gateway": { + "type": "Extension", + "name": "ipaddr" + } + } + } +} +``` + +## `actions` {#schema-actions} + +A collection of the `Action` entities usable as actions in authorization requests submitted by your application. The `actions` element contains a comma-separated list of one or more JSON objects. + +The high-level structure of an `actions` entry looks like the following. + +```json +"actions": { + "ActionName1": { + "memberOf": ["ActionGroupName1",...], + "appliesTo": { + "principalTypes": ["PrincipalEntityType1",...], + "resourceTypes": ["ResourceEntityType1",...], + } + }, + "ActionName2": { ... }, + ... +} +``` + +You can add as many actions as your application requires. + +### Action name {#schema-actions-name} + +Specifies the identifier for the action entity, as a string. The name of the action isn't a value but the heading for its own JSON object. Since this is an [entity identifier](../policies/syntax-entity.html#entity-overview) (rather than an entity type, as in the `entityTypes` section) it can contain anything that would be valid inside a Cedar string. + +```json +"actions": { + "ViewPhoto": { + ... + }, + "ListPhotos": { + ... + }, + ... +} +``` + +You can then refer to these actions in your policies by using the following syntax. If the schema declares a namespace, then the entity type `Action` is qualified by that namespace. + +```cedar +MyApplicationNameSpace::Action::"ViewPhoto" +``` + +### `memberOf` {#schema-actions-memberOf} + +Specifies a list of action entity groups the action is a member of. The `memberOf` component is optional. If omitted, it means the action is a member of no action groups. + +The following schema snippet shows an action named `viewAlbum` that is a member of the action group called `viewImages`. + +```json +"actions": { + "viewAlbum": { + … + "memberOf": [ + { + "id": "viewImages", + "type": "PhotoFlash::Images::Action" + }, + ], + … + } +} +``` + +Action groups are themselves actions, and thus must also be defined in the schema in the `actions` section; we'll see an example of that below. + +### `appliesTo` {#schema-actions-appliesTo} + +Specifies a JSON object containing two lists, `principalTypes` and `resourceTypes`, which contain the principal and resources entity types, respectively, that can accompany the action in an authorization request. + ++ If either the `principalTypes` or `resourceTypes` components is given with an empty list `[]`, the associated action is not permitted in an authorization request with *any* entities of that category. This effectively means that the action will not be used in an authorization request at all. This makes sense for actions that act as groups for other actions. + ++ If the `principalTypes` component is omitted from the `appliesTo` element, then an authorization request with this action must have an [unspecified](../policies/syntax-entity.html#entity-overview) principal entity. The same is true for `resourceTypes`, for a request's resource component. If the `appliesTo` component is omitted entirely, it's the same as if it were present with both `principalTypes` and `resourceTypes` components omitted (i.e., a request must have both unspecified principal and resource entities). + +The following example `actions` snippet shows three actions. The first action, `read`, is an action group for the other two. It cannot appear in an authorization request because its `principalTypes` and `resourceTypes` components are `[]`. The second action, `viewPhoto`, is a member of the `read` action group, and expects that any request with this action will have a principal entity of type `User` and a resource entity of type `Photo`. The third action, `listAlbums`, also a member of the `read` group, expects that a request with that action will have a principal entity of type `User` and a resource entity of type `Account`. Notice that for both of the latter two actions, the group membership only requires giving the ID of the action -- `"read"` -- and not the type. This is because the validator knows that all action groups must have type `Action`, and by default the action will be within the current namespace. To declare membership in an action group in a different namespace you need to include `"type": "My::NameSpace::Action"` alongside the `"id"` portion, where `My::NameSpace` is the different namespace. + +```json +"actions": { + "read": { + "appliesTo": { + "principalTypes": [], + "resourceTypes": [] + } + }, + "viewPhoto": { + "memberOf": [ + { + "id": "read" + } + ], + "appliesTo": { + "principalTypes": [ "User" ], + "resourceTypes": [ "Photo" ] + } + }, + "listAlbums": { + "memberOf": [ + { + "id": "read" + } + ], + "appliesTo": { + "principalTypes": [ "User" ], + "resourceTypes": [ "Account" ] + } + } +} +``` + +### `context` {#schema-actions-context} + +Specifies a JSON object in the same format as an entity's `shape` property, which defines the attributes that can be present in the context record in authorization requests made with this action. Specifying a `context` enables Cedar to validate policies that attempt to reference the `context` record's attributes. + +Each `context` entry consists of `type` and `attributes` objects. The `type` object is always the type `Record`. The `attributes` object has the same JSON format as the [entity `shape`'s record's attributes](#schema-attributes-specs). For example, the following `context` snippet specifies that any request to perform the `SomeAction` action must include a `Boolean` attribute named `field1` and a `Long`attribute named `field2`. Optionally, the `context` may include a third attribute `field3` which, if present, is a `String`. The `context` entry is optional, and if excluded it is assumed to be an empty `Record` (in which case policies that try to access attributes in `context` will produce validation errors). + +```json +"actions": { + "SomeAction": { + "appliesTo": { + "principalTypes": [ ... ], + "resourceTypes": [ ... ], + "context": { + "type": "Record", + "attributes": { + "field1": { "type": "Boolean" }, + "field2": { "type": "Long" }, + "field3": { "type": "String", + "required": false } + } + } + } + } +} +``` + +## `commonTypes` {#schema-commonTypes} + +Your schema might define several entity types that share a lot of elements in common. Instead of redundantly entering those elements separately for each entity that needs them, you can define those elements once using a `commonTypes` construct with a name, and then reference that construct's name in each entity that requires them. You can use this anywhere you can define a Cedar type that includes a data type specification and a set of attributes. + +### Motivation + +Suppose your schema defines several entity types or action entities that share a lot of elements in common. For example, consider the following actions: both `view` and `upload` have identical `context` components. + +```json +"actions": { + "view": { + "appliesTo": { + "context": { + "type": "Record", + "attributes": { + "ip": { "type": "Extension", "name": "ipaddr" }, + "is_authenticated": { "type": "Boolean" }, + "timestamp": { "type": "Long" } + } + } + } + }, + "upload": { + "appliesTo": { + "context": { + "type": "Record", + "attributes": { + "ip": { "type": "Extension", "name": "ipaddr" }, + "is_authenticated": { "type": "Boolean" }, + "timestamp": { "type": "Long" } + } + } + } + } +} +``` + +Instead of redundantly entering common type elements separately for each action / entity type that needs them, you can define them once within `commonTypes`, and then refer to the definition in multiple places. + +### Structure + +Each JSON object within `commonTypes` consists of the name of a type being defined and its associated definition. The definition is specified just like an [attribute type specification](#schema-attributes-specs), i.e., + +```json + "TypeName": { + // attribute type specification + } +``` + +Returning to our motivating example, we can define a record type called `ReusedContext`, which is then referenced by the `view` and `upload` actions. + +```json +... +"commonTypes": { + "ReusedContext": { + "type": "Record", + "attributes": { + "ip": { "type": "Extension", "name": "ipaddr" }, + "is_authenticated": { "type": "Boolean" }, + "timestamp": { "type": "Long" } + } + } +}, +"actions": { + "view": { + "appliesTo": { + "context": { "type": "ReusedContext" } + } + }, + "upload": { + "appliesTo": { + "context": { "type": "ReusedContext" } + } + } +} +``` + +We can also use type names defined in `commonTypes` within definitions in the `entityTypes` section. As a simple example, here we define a type `name` as a `String`, and then use the type (twice) in the `User` entity type's `attributes` specifications: + +```json +... +"commonTypes": { + "name": { + "type": "String", + } +}, +"entityTypes": { + "User": { + "shape": { + "type": "Record", + "attributes": { + "firstName": { + "type": "name" + }, + "lastName": { + "type": "name" + } + } + } + } +} +``` + +As another example, we can use a defined record type for the `shape` of multiple entity types. In particular, we collect a set of attributes as a record named `Person` and use `Person` within the `Employee` and `Customer` entity type definitions. + +```json +... +"commonTypes": { + "Person": { + "type": "Record", + "attributes": { + "age": {"type": "Long"}, + "name": {"type": "String"} + } + } +}, +"entityTypes": { + "Employee": { "shape": { "type": "Person" } }, + "Customer": { "shape": { "type": "Person" } } +} +``` + +If you then send an `Employee` entity as the principal in an authorization request, you could evaluate the attributes of that principal by using syntax similar to this example: `principal.age`. + +Note that definitions of types appearing in `commonTypes` cannot refer to one another. For example, if both `name` and `Person` from the above example were in the same `commonTypes` section, you could not change `Person`'s define to refer to objects of type `name`. + +## Example schema {#schema-examples} + +The following schema is for a hypothetical application called PhotoFlash. + +The schema defines a `User` entity that can have a `department` and a `jobLevel`. The user can be a member of a `UserGroup`. + +The schema also defines the following three resource types: + ++ An `Account` can have one `owner` and zero or more `admins` that are all `User` entities. ++ An `Album` can be nested inside another `Album`, and has a Boolean `private` attribute, and a reference to an `Account`. ++ A `Photo` can be placed in an `Album`, and also has a `private` attribute and a reference to an `Account`. + +```json +{ + "PhotoFlash": { + "entityTypes": { + "User": { + "memberOfTypes": [ "UserGroup" ], + "shape": { + "type": "Record", + "attributes": { + "department": { "type": "String" }, + "jobLevel": { "type": "Long" } + } + } + }, + "UserGroup": { }, + "Photo": { + "memberOfTypes": [ "Album" ], + "shape": { + "type": "Record", + "attributes": { + "private": { "type": "Boolean" }, + "account": { + "type": "Entity", + "name": "Account" + } + } + } + }, + "Album": { + "memberOfTypes": [ "Album" ], + "shape": { + "type": "Record", + "attributes": { + "private": { "type": "Boolean" }, + "account": { + "type": "Entity", + "name": "Account" + } + } + } + }, + "Account": { + "memberOfTypes": [], + "shape": { + "type": "Record", + "attributes": { + "owner": { + "type": "Entity", + "name": "User" + }, + "admins": { + "required": false, + "type": "Set", + "element": { + "type": "Entity", + "name": "User" + } + } + } + } + } + }, + "actions": { + "viewPhoto": { + "appliesTo": { + "principalTypes": [ "User" ], + "resourceTypes": [ "Photo" ], + "context": { + "type": "Record", + "attributes": { + "authenticated": { "type": "Boolean" } + } + } + } + }, + "listAlbums": { + "appliesTo": { + "principalTypes": [ "User" ], + "resourceTypes": [ "Account" ], + "context": { + "type": "Record", + "attributes": { + "authenticated": { "type": "Boolean" } + } + } + } + }, + "uploadPhoto": { + "appliesTo": { + "principalTypes": [ "User" ], + "resourceTypes": [ "Album" ], + "context": { + "type": "Record", + "attributes": { + "authenticated": { "type": "Boolean" }, + "photo": { + "type": "Record", + "attributes": { + "file_size": { "type": "Long" }, + "file_type": { "type": "String" } + } + } + } + } + } + } + } + } +} +``` \ No newline at end of file diff --git a/docs/collections/_schema/schema.md b/docs/collections/_schema/schema.md index 8b4edad..432f276 100644 --- a/docs/collections/_schema/schema.md +++ b/docs/collections/_schema/schema.md @@ -4,7 +4,7 @@ title: Schema format nav_order: 1 --- -# Cedar schema format {#schema} +# Cedar schema {#schema} {: .no_toc }
@@ -16,10 +16,13 @@ nav_order: 1 {:toc}
-This topic describes the structure of a Cedar schema. To view the formal grammar, see [Schema grammar](../policies/schema-grammar.html). +This topic describes the structure of a schema. There are two schema formats --- human-readable and JSON. To view their formal grammars, see [JSON schema grammar](../schema/json-schema-grammar.html) and [human-readable schema grammar](../_schema/human-readable-schema-grammar.md). ## Overview {#schema-overview} -A schema is a declaration of the structure of the entity types that you want to support in your application and for which you want Cedar to provide authorization services. Cedar uses [JSON](https://json.org) to define a schema. It bears some resemblance to [JSON Schema](https://json-schema.org/), but unique aspects of the design of Cedar, such as its use of entity types, require some differences. After you define a schema, you can ask Cedar to validate your policies against it to ensure your policies reference the entities and their attributes correctly. +A schema is a declaration of the structure of the entity types that you want to support in your application and for which you want Cedar to provide authorization services. Cedar provides two formats (human-readable and JSON) to define a schema. The syntax of the human-readable schema format is very similar to that Cedar policy; hence the name. The JSON schema format bears some resemblance to [JSON Schema](https://json-schema.org/), but unique aspects of the design of Cedar, such as its use of entity types, require some differences. We recommend you to use the human-readable schema format for its simplicity and conciseness, although Cedar CLI provides a command to translate schemas written in both formats. + +After you define a schema, you can ask Cedar to validate your policies against it to ensure that your policies do not contain type errors, such as referencing the entities and their attributes incorrectly. + {: .warning } >If you change your schema, any policies that you validated before the change might no longer be valid. Those policies can then generate errors during authorization queries if you include entities that match the updated schema in your request. @@ -33,676 +36,57 @@ You can use a schema to define each of the following entities used by your appli Services that use Cedar can use the information provided in the schema to validate the policies you submit to the policy store. This helps prevent your policies from returning incorrect authorization decisions because of errors in policies like incorrectly typed attribute names. For more information about validating your policies, see [Cedar policy validation against schema](../policies/validation.html). -## Schema format {#schema-format} +Both schema formats implement the same ideas, which we detail as follows. We then present their the human-readable JSON realizations. -A schema contains a declaration of one or more namespaces, each of which contains two mandatory JSON objects, `entityTypes` and `actions`. A namespace declaration can optionally include a third object, `commonTypes`, which defines types that can be referenced by the other two objects. We consider the format of namespaces and these three objects next. +## Schema {#schema-format} + +A schema contains a declaration of one or more namespaces, each of which contains declarations of entity types, actions, and common types. A namespace has an optional name. ## NameSpace {#schema-namespace} -A [namespace](../overview/terminology.html#term-namespaces) declaration identifies and defines a scope for all entity types and actions declared within it. The namespace is a string that uses double colons \(`::`\) as separators between its elements, which must be identifiers. A namespace can be empty (i.e., the empty string). +A [namespace](../overview/terminology.html#term-namespaces) declaration identifies and defines a scope for all entity types, actions, and common types declared within it. The name of a namespace consists of identifiers separated by double colons (`::`). {: .important } >The namespace name must be normalized and cannot include any embedded whitespace, such as spaces, newlines, control characters, or comments. -A namespace declaration contains a comma-separated list of JSON objects within braces `{ }`. The following is an example of a namespace declaration: - -A namespace declaration must contain two child elements, and may contain a third, appearing in any order: - -+ [`entityTypes`](#schema-entityTypes) -+ [`actions`](#schema-actions) -+ [`commonTypes`](#schema-commonTypes) (optional) - -You define the types of your application's principal and resource entities within the `entityTypes` element, and the specific actions in the `actions` element. Principals and resources are separated from actions because actions are defined in the schema as individual discrete elements (each of which has type `Action`), whereas only the `principal` and `resource` entities' *types* are defined. In your entity store you create individual principal and resource entities that have these types. Optionally, you can define type names in `commonTypes` and reference those names as types in the `entityTypes` and `actions` elements of your schema. - -The declared namespace is automatically prepended to all types defined within the associated scope. For example, consider the following schema: - -```json -{ - "ExampleCo::Database": { - "entityTypes": { - "Table": { - ... - } - }, - "actions": { - "createTable": { - ... - } - } - } -} -``` - -Here, the schema is effectively defining the action entity `ExampleCo::Database::Action::"createTable"` and the entity type `ExampleCo::Database::Table`. - -You can reference entity types and actions defined in other namespaces of the same schema by using their fully qualified names. For example, here is a schema that declares two namespaces, `ExampleCo::Clients` and `ExampleCo::Furniture`, where the second namespace's entity type `Table` references the first's entity type `Manufacturer`. - -```json -{ - "ExampleCo::Clients": { - "entityTypes": { - "Manufacturer": { ... } - }, - "actions": { ... } - }, - "ExampleCo::Furniture": { - "entityTypes": { - "Table": { - "shape": { - "type": "Record", - "attributes": { - "manufacturer": { - "type": "Entity", - "name": "ExampleCo::Clients::Manufacturer" - } - } - } - } - }, - "actions": { ... } - } -} -``` +A namespace declaration contains three types of declarations, appearing in any order: -If you change a declared namespace in your schema you will need to change the entity types appearing in your policies and/or in other namespaces declared in your schema to instead reference the changed namespace. ++ [entity types](#schema-entityTypes) ++ [actions](#schema-actions) ++ [common types](#schema-commonTypes) (optional) -## `entityTypes` {#schema-entityTypes} +You define the types of your application's principal and resource entities via entity type declarations, and its actions via action declarations. Optionally, you can define type names in common type declarations and reference those names as types in other places in the schema. For example, a common practice is to name a shared record type in this way and refer it, e.g., in entity attribute declarations. -A collection of the `principal` and `resource` entity types supported by your application. The `entityTypes` element contains a comma-separated list of JSON objects. +Declarations (e.g, entity types) of a namespace must be qualified with its name to be referred in other namespaces. They can be referred in qualified or unqualified forms within the same namespace. For example, you can only refer the entity type `Table` declared in the namespace `ExampleCo::Database1` as `ExampleCo::Database1::Table` in the namespace `ExampleCo::Database2`. -The high-level structure of an `entityTypes` entry looks like the following example. - -```json -"entityTypes": { - "EntityTypeName1": { - "memberOfTypes": [ "parentGroupTypeName1", "parentGroupTypeName2", … ], - "shape": { … } - }, - "EntityTypeName2": { - "memberOfTypes": [ "parentGroupTypeName1", "parentGroupTypeName2", … ], - "shape": { … } - } -} -``` - -Each entry in the `entityTypes` is a JSON object with the following properties. +If you change a declared namespace in your schema you will need to change the entity types appearing in your policies and/or in other namespaces declared in your schema to instead reference the changed namespace. -### Entity type name {#schema-entitytypes-name} +## Entity types {#schema-entityTypes} -Specifies the name of the entity type as a string. This type name must be an identifier, which is defined in the Cedar grammar as a sequence of alphanumeric characters, omitting any Cedar reserved words. +A collection of the `principal` and `resource` entity types supported by your application. An entity type name is a Cedar identifier. An entity type declaration specifies its membership relations with other entity types and its shape. {: .important } ->The entity type name must be normalized and cannot include any embedded whitespace, such as spaces, newlines, control characters, or comments. - -```json -"My::Name::Space": { - "entityTypes": { - "UserGroup": { ... } // New entity type name - } -} -``` - -This type name is qualified by its [namespace](#schema-namespace) to form a fully qualified entity type which must be used when referencing this type in a policy. - -```json -"My::Name::Space::UserGroup" -``` - -### `memberOfTypes` {#schema-entitytypes-memberOf} - -Specifies a list of entity types that can be direct parents of entities of this type. Values in this list must be valid entity type names declared in the schema. If the `memberOfTypes` element is empty or not defined, then entities of that entity type can't have any parents in the entity hierarchy. The following example shows that an entity of type `User` can have parents of type `UserGroup`. - -```json -"entityTypes": { - "UserGroup": { - … - }, - "User": { - "memberOfTypes": [ "UserGroup" ], - … - } -} -``` - -If the parent type is part of the same namespace as the child type, then you can reference simply the parent type's name. If the parent type is in a different namespace than the child type, then you must include the parent type's namespace as part of the reference. The following example references two parent types, both named `UserGroup`. The first `UserGroup` is part of the same namespace as the child entity type that this entry is part of. The second `UserGroup` is defined in the namespace `Aws::Cognito::UserPool_1`. - -```json -"memberOfTypes": [ - "UserGroup", - "Aws::Cognito::UserPool_1::UserGroup" -] -``` - -### `shape` {#schema-entitytypes-shape} - -Specifies the shape of the data stored in entities of this type. The `shape` element, if present, must have type `Record`, and be accompanied by a description of the entity's attributes. The following example shows a simple specification of the `User` entity type. - -```json -"User" : { - "shape" : { - "type" : "Record", - "attributes" : { - "name" : { - "type" : "String" - }, - "age" : { - "type" : "Long" - } - } - } -} -``` - -Each attribute in the `attributes` portion of the `shape` record must follow the format described next. - -Note that if the `shape` element is omitted, then entities of the type being defined are assumed to have no data stored with them. This is equivalent to specifying a `Record` with `{}` for its attributes. - -### Attribute specifications {#schema-attributes-specs} - -Each attribute in a `Record` is a JSON object that describes one attribute in the record associated with entities of this type. It has the form - -```json - "name" : { - "type" : "Type" - }, -``` - -where `name` is the name of the attribute, and `Type` is one of the [Cedar supported data types](../policies/syntax-datatypes.html), discussed in detail below. - -You can choose to specify whether an attribute is required or optional. By default, attributes that you define are required. This means that policies that reference this type can assume that the attribute is always present. You can make an attribute optional by adding `"required": false` to the attribute description. Here is an example: - -```json -"jobLevel": { - "type": "Long", - "required": false -}, -``` +>The entity type name must be normalized and cannot include any embedded whitespace, such as spaces, newlines, control characters, or comments. -A policy should check for an optional attribute's presence by using the [`has`](../policies/syntax-operators.html#operator-has) operator before trying to access the attribute's value. If evaluation of a policy results in an attempt to access a non-existent attribute, evaluation fails with an error (which causes the policy to be ignored during authorization, and for a diagnostic to be generated). The validator will flag the potential for such errors to occur. +### Membership relation -You can choose to explicitly declare that an attribute is mandatory by including `"required": true` (but this is unnecessary as mandatory attributes are the default). +Specifies a list of entity types that can be direct parents of entities of this type. -### Attribute types {#schema-attributes-types} +### Shape -Attributes' `type` components can be `"String"`, `"Long"`, `"Boolean"`, `"Record"`, `"Set"`, `"Entity"`, or `"Extension"`. The first three require no further information to be specified. The latter four are described below. +Specifies the shape of the data stored in entities of this type. More precisely, it defines the attributes of an entity type --- their names, types, and optionality. +An attribute type is one of the [Cedar supported data types](../policies/syntax-datatypes.html). +You can choose to specify whether an attribute is required or optional. By default, attributes that you define are required. This means that policies that reference this type can assume that the attribute is always present. -#### `Record` {#schema-entitytypes-shape-record} -{: .no_toc } +A policy should check for an optional attribute's presence by using the [`has`](../policies/syntax-operators.html#operator-has) operator before trying to access the attribute's value. If evaluation of a policy results in an attempt to access a non-existent attribute, evaluation fails with an error (which causes the policy to be ignored during authorization, and for a diagnostic to be generated). The validator will flag the potential for such errors to occur. -A record attribute has the same JSON format as the [entity `shape`'s record's attributes](#schema-attributes-specs). As an example, the following refactors the `User` entity specification above to have a `features` attribute that is a `Record` containing some of the user's physical features. - -```json -"User" : { - "shape" : { - "type" : "Record", - "attributes" : { - "name" : { - "type" : "String" - }, - "features" : { - "type": "Record", - "attributes": { - "age" : { - "type" : "Long" - }, - "height": { - "type" : "Long" - }, - "eyecolor": { - "type": "String" - } - } - } - } - } -} -``` - -#### `Entity` {#schema-entitytypes-shape-entity} -{: .no_toc } +## Actions {#schema-actions} -For attributes of type `"Entity"`, you must also specify a `name` that identifies the entity type of this attribute. The entity type must be defined in the schema. For example, a resource entity might require an `Owner` element that specifies a `User`. - -```json -"Document" : { - "shape" : { - "type" : "Record", - "attributes" : { - "Owner": { - "type": "Entity", - "name": "User" - } - } - } -} -``` - -#### `Set` {#schema-entitytypes-shape-set} -{: .no_toc } +A collection of the `Action` entities usable as actions in authorization requests submitted by your application. The action name is an [entity identifier](../policies/syntax-entity.html#entity-overview) (rather than an entity type, as in the entity type section). +You should refer to actions in your policies by using the following syntax. If the schema declares a namespace, then the entity type `Action` is qualified by that namespace. -For attributes with type `"Set"`, you must also specify an `element` that defines the properties of the members of the set. Each element is a JSON object that describes what each member of the set looks like. - -An `element` must contain a structure formatted according to the same rules as an attribute. As an example, consider the following `Admins` entry which could be part of the `shape` record of an `Account` entity type. This `Admins` element is a set of entities of type `User` and could be used to define which users have administrator permissions in the account. - -```json -"Group" : { - "shape" : { - "type" : "Record", - "attributes": { - "Admins": { - "type": "Set", - "element": { - "type": "Entity", - "name": "User" - } - } - } - } -} -``` - -#### `Extension` {#schema-entitytypes-shape-extension} -{: .no_toc } +An action declaration specifies an action's membership relations with action groups, its applicability (with respect to principal and resource entity types, and the context shape). + +## Common type {#schema-commonTypes} -For attributes of type `"Extension"`, you must also specify the `name` of the specific extension type. -There are two extension types: `ipaddr` for IP address values, and `decimal` for decimal values. -For example, a `Network` entity may include the IP address of its gateway. - -```json -"Network": { - "shape": { - "type": "Record", - "attributes": { - "gateway": { - "type": "Extension", - "name": "ipaddr" - } - } - } -} -``` - -## `actions` {#schema-actions} - -A collection of the `Action` entities usable as actions in authorization requests submitted by your application. The `actions` element contains a comma-separated list of one or more JSON objects. - -The high-level structure of an `actions` entry looks like the following. - -```json -"actions": { - "ActionName1": { - "memberOf": ["ActionGroupName1",...], - "appliesTo": { - "principalTypes": ["PrincipalEntityType1",...], - "resourceTypes": ["ResourceEntityType1",...], - } - }, - "ActionName2": { ... }, - ... -} -``` - -You can add as many actions as your application requires. - -### Action name {#schema-actions-name} - -Specifies the identifier for the action entity, as a string. The name of the action isn't a value but the heading for its own JSON object. Since this is an [entity identifier](../policies/syntax-entity.html#entity-overview) (rather than an entity type, as in the `entityTypes` section) it can contain anything that would be valid inside a Cedar string. - -```json -"actions": { - "ViewPhoto": { - ... - }, - "ListPhotos": { - ... - }, - ... -} -``` - -You can then refer to these actions in your policies by using the following syntax. If the schema declares a namespace, then the entity type `Action` is qualified by that namespace. - -```cedar -MyApplicationNameSpace::Action::"ViewPhoto" -``` - -### `memberOf` {#schema-actions-memberOf} - -Specifies a list of action entity groups the action is a member of. The `memberOf` component is optional. If omitted, it means the action is a member of no action groups. - -The following schema snippet shows an action named `viewAlbum` that is a member of the action group called `viewImages`. - -```json -"actions": { - "viewAlbum": { - … - "memberOf": [ - { - "id": "viewImages", - "type": "PhotoFlash::Images::Action" - }, - ], - … - } -} -``` - -Action groups are themselves actions, and thus must also be defined in the schema in the `actions` section; we'll see an example of that below. - -### `appliesTo` {#schema-actions-appliesTo} - -Specifies a JSON object containing two lists, `principalTypes` and `resourceTypes`, which contain the principal and resources entity types, respectively, that can accompany the action in an authorization request. - -+ If either the `principalTypes` or `resourceTypes` components is given with an empty list `[]`, the associated action is not permitted in an authorization request with *any* entities of that category. This effectively means that the action will not be used in an authorization request at all. This makes sense for actions that act as groups for other actions. - -+ If the `principalTypes` component is omitted from the `appliesTo` element, then an authorization request with this action must have an [unspecified](../policies/syntax-entity.html#entity-overview) principal entity. The same is true for `resourceTypes`, for a request's resource component. If the `appliesTo` component is omitted entirely, it's the same as if it were present with both `principalTypes` and `resourceTypes` components omitted (i.e., a request must have both unspecified principal and resource entities). - -The following example `actions` snippet shows three actions. The first action, `read`, is an action group for the other two. It cannot appear in an authorization request because its `principalTypes` and `resourceTypes` components are `[]`. The second action, `viewPhoto`, is a member of the `read` action group, and expects that any request with this action will have a principal entity of type `User` and a resource entity of type `Photo`. The third action, `listAlbums`, also a member of the `read` group, expects that a request with that action will have a principal entity of type `User` and a resource entity of type `Account`. Notice that for both of the latter two actions, the group membership only requires giving the ID of the action -- `"read"` -- and not the type. This is because the validator knows that all action groups must have type `Action`, and by default the action will be within the current namespace. To declare membership in an action group in a different namespace you need to include `"type": "My::NameSpace::Action"` alongside the `"id"` portion, where `My::NameSpace` is the different namespace. - -```json -"actions": { - "read": { - "appliesTo": { - "principalTypes": [], - "resourceTypes": [] - } - }, - "viewPhoto": { - "memberOf": [ - { - "id": "read" - } - ], - "appliesTo": { - "principalTypes": [ "User" ], - "resourceTypes": [ "Photo" ] - } - }, - "listAlbums": { - "memberOf": [ - { - "id": "read" - } - ], - "appliesTo": { - "principalTypes": [ "User" ], - "resourceTypes": [ "Account" ] - } - } -} -``` - -### `context` {#schema-actions-context} - -Specifies a JSON object in the same format as an entity's `shape` property, which defines the attributes that can be present in the context record in authorization requests made with this action. Specifying a `context` enables Cedar to validate policies that attempt to reference the `context` record's attributes. - -Each `context` entry consists of `type` and `attributes` objects. The `type` object is always the type `Record`. The `attributes` object has the same JSON format as the [entity `shape`'s record's attributes](#schema-attributes-specs). For example, the following `context` snippet specifies that any request to perform the `SomeAction` action must include a `Boolean` attribute named `field1` and a `Long`attribute named `field2`. Optionally, the `context` may include a third attribute `field3` which, if present, is a `String`. The `context` entry is optional, and if excluded it is assumed to be an empty `Record` (in which case policies that try to access attributes in `context` will produce validation errors). - -```json -"actions": { - "SomeAction": { - "appliesTo": { - "principalTypes": [ ... ], - "resourceTypes": [ ... ], - "context": { - "type": "Record", - "attributes": { - "field1": { "type": "Boolean" }, - "field2": { "type": "Long" }, - "field3": { "type": "String", - "required": false } - } - } - } - } -} -``` - -## `commonTypes` {#schema-commonTypes} - -Your schema might define several entity types that share a lot of elements in common. Instead of redundantly entering those elements separately for each entity that needs them, you can define those elements once using a `commonTypes` construct with a name, and then reference that construct's name in each entity that requires them. You can use this anywhere you can define a Cedar type that includes a data type specification and a set of attributes. - -### Motivation - -Suppose your schema defines several entity types or action entities that share a lot of elements in common. For example, consider the following actions: both `view` and `upload` have identical `context` components. - -```json -"actions": { - "view": { - "appliesTo": { - "context": { - "type": "Record", - "attributes": { - "ip": { "type": "Extension", "name": "ipaddr" }, - "is_authenticated": { "type": "Boolean" }, - "timestamp": { "type": "Long" } - } - } - } - }, - "upload": { - "appliesTo": { - "context": { - "type": "Record", - "attributes": { - "ip": { "type": "Extension", "name": "ipaddr" }, - "is_authenticated": { "type": "Boolean" }, - "timestamp": { "type": "Long" } - } - } - } - } -} -``` - -Instead of redundantly entering common type elements separately for each action / entity type that needs them, you can define them once within `commonTypes`, and then refer to the definition in multiple places. - -### Structure - -Each JSON object within `commonTypes` consists of the name of a type being defined and its associated definition. The definition is specified just like an [attribute type specification](#schema-attributes-specs), i.e., - -```json - "TypeName": { - // attribute type specification - } -``` - -Returning to our motivating example, we can define a record type called `ReusedContext`, which is then referenced by the `view` and `upload` actions. - -```json -... -"commonTypes": { - "ReusedContext": { - "type": "Record", - "attributes": { - "ip": { "type": "Extension", "name": "ipaddr" }, - "is_authenticated": { "type": "Boolean" }, - "timestamp": { "type": "Long" } - } - } -}, -"actions": { - "view": { - "appliesTo": { - "context": { "type": "ReusedContext" } - } - }, - "upload": { - "appliesTo": { - "context": { "type": "ReusedContext" } - } - } -} -``` - -We can also use type names defined in `commonTypes` within definitions in the `entityTypes` section. As a simple example, here we define a type `name` as a `String`, and then use the type (twice) in the `User` entity type's `attributes` specifications: - -```json -... -"commonTypes": { - "name": { - "type": "String", - } -}, -"entityTypes": { - "User": { - "shape": { - "type": "Record", - "attributes": { - "firstName": { - "type": "name" - }, - "lastName": { - "type": "name" - } - } - } - } -} -``` - -As another example, we can use a defined record type for the `shape` of multiple entity types. In particular, we collect a set of attributes as a record named `Person` and use `Person` within the `Employee` and `Customer` entity type definitions. - -```json -... -"commonTypes": { - "Person": { - "type": "Record", - "attributes": { - "age": {"type": "Long"}, - "name": {"type": "String"} - } - } -}, -"entityTypes": { - "Employee": { "shape": { "type": "Person" } }, - "Customer": { "shape": { "type": "Person" } } -} -``` - -If you then send an `Employee` entity as the principal in an authorization request, you could evaluate the attributes of that principal by using syntax similar to this example: `principal.age`. - -Note that definitions of types appearing in `commonTypes` cannot refer to one another. For example, if both `name` and `Person` from the above example were in the same `commonTypes` section, you could not change `Person`'s define to refer to objects of type `name`. - -## Example schema {#schema-examples} - -The following schema is for a hypothetical application called PhotoFlash. - -The schema defines a `User` entity that can have a `department` and a `jobLevel`. The user can be a member of a `UserGroup`. - -The schema also defines the following three resource types: - -+ An `Account` can have one `owner` and zero or more `admins` that are all `User` entities. -+ An `Album` can be nested inside another `Album`, and has a Boolean `private` attribute, and a reference to an `Account`. -+ A `Photo` can be placed in an `Album`, and also has a `private` attribute and a reference to an `Account`. - -```json -{ - "PhotoFlash": { - "entityTypes": { - "User": { - "memberOfTypes": [ "UserGroup" ], - "shape": { - "type": "Record", - "attributes": { - "department": { "type": "String" }, - "jobLevel": { "type": "Long" } - } - } - }, - "UserGroup": { }, - "Photo": { - "memberOfTypes": [ "Album" ], - "shape": { - "type": "Record", - "attributes": { - "private": { "type": "Boolean" }, - "account": { - "type": "Entity", - "name": "Account" - } - } - } - }, - "Album": { - "memberOfTypes": [ "Album" ], - "shape": { - "type": "Record", - "attributes": { - "private": { "type": "Boolean" }, - "account": { - "type": "Entity", - "name": "Account" - } - } - } - }, - "Account": { - "memberOfTypes": [], - "shape": { - "type": "Record", - "attributes": { - "owner": { - "type": "Entity", - "name": "User" - }, - "admins": { - "required": false, - "type": "Set", - "element": { - "type": "Entity", - "name": "User" - } - } - } - } - } - }, - "actions": { - "viewPhoto": { - "appliesTo": { - "principalTypes": [ "User" ], - "resourceTypes": [ "Photo" ], - "context": { - "type": "Record", - "attributes": { - "authenticated": { "type": "Boolean" } - } - } - } - }, - "listAlbums": { - "appliesTo": { - "principalTypes": [ "User" ], - "resourceTypes": [ "Account" ], - "context": { - "type": "Record", - "attributes": { - "authenticated": { "type": "Boolean" } - } - } - } - }, - "uploadPhoto": { - "appliesTo": { - "principalTypes": [ "User" ], - "resourceTypes": [ "Album" ], - "context": { - "type": "Record", - "attributes": { - "authenticated": { "type": "Boolean" }, - "photo": { - "type": "Record", - "attributes": { - "file_size": { "type": "Long" }, - "file_type": { "type": "String" } - } - } - } - } - } - } - } - } -} -``` +Your schema might define several entity types that share a lot of elements in common. Instead of redundantly entering those elements separately for each entity that needs them, you can define those elements once using a common type construct with a name, and then reference that construct's name in each entity that requires them. You can use this anywhere you can define a Cedar type that includes a data type specification and a set of attributes. \ No newline at end of file From 13bdb334451e29c5047c8db0a801595bd4082977 Mon Sep 17 00:00:00 2001 From: Shaobo He Date: Wed, 21 Feb 2024 12:25:42 -0800 Subject: [PATCH 2/9] 1st pass --- docs/collections/_schema/json-schema.md | 7 ------- docs/collections/_schema/schema.md | 26 +++++++++++++++---------- 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/docs/collections/_schema/json-schema.md b/docs/collections/_schema/json-schema.md index 5136dee..2e6381a 100644 --- a/docs/collections/_schema/json-schema.md +++ b/docs/collections/_schema/json-schema.md @@ -24,11 +24,6 @@ A schema contains a declaration of one or more namespaces, each of which contain ## NameSpace {#schema-namespace} -A [namespace](../overview/terminology.html#term-namespaces) declaration identifies and defines a scope for all entity types and actions declared within it. The namespace is a string that uses double colons \(`::`\) as separators between its elements, which must be identifiers. A namespace can be empty (i.e., the empty string). - -{: .important } ->The namespace name must be normalized and cannot include any embedded whitespace, such as spaces, newlines, control characters, or comments. - A namespace declaration contains a comma-separated list of JSON objects within braces `{ }`. The following is an example of a namespace declaration: A namespace declaration must contain two child elements, and may contain a third, appearing in any order: @@ -203,8 +198,6 @@ You can choose to specify whether an attribute is required or optional. By defau }, ``` -A policy should check for an optional attribute's presence by using the [`has`](../policies/syntax-operators.html#operator-has) operator before trying to access the attribute's value. If evaluation of a policy results in an attempt to access a non-existent attribute, evaluation fails with an error (which causes the policy to be ignored during authorization, and for a diagnostic to be generated). The validator will flag the potential for such errors to occur. - You can choose to explicitly declare that an attribute is mandatory by including `"required": true` (but this is unnecessary as mandatory attributes are the default). ### Attribute types {#schema-attributes-types} diff --git a/docs/collections/_schema/schema.md b/docs/collections/_schema/schema.md index 432f276..a04a9c5 100644 --- a/docs/collections/_schema/schema.md +++ b/docs/collections/_schema/schema.md @@ -1,6 +1,6 @@ --- layout: default -title: Schema format +title: Schema formats nav_order: 1 --- @@ -16,13 +16,12 @@ nav_order: 1 {:toc} -This topic describes the structure of a schema. There are two schema formats --- human-readable and JSON. To view their formal grammars, see [JSON schema grammar](../schema/json-schema-grammar.html) and [human-readable schema grammar](../_schema/human-readable-schema-grammar.md). +This topic describes the structure of a Cedar schema. There are two schema formats --- human-readable and JSON. Cedar provides two formats (human-readable and JSON) to define a schema. The syntax of the human-readable schema format is very similar to that of Cedar policies; hence the name. The JSON schema format bears some resemblance to [JSON Schema](https://json-schema.org/), but unique aspects of the design of Cedar, such as its use of entity types, require some differences. The two formats are interchangeable and Cedar CLI provides a command to translate schemas in one format to the other. We encourage you to use the human-readable schema format for its simplicity and conciseness. To view their details, see [human-readable schema format](../schema/human-readable-schema.html) and [JSON schema format](../schema/json-schema.html). + ## Overview {#schema-overview} -A schema is a declaration of the structure of the entity types that you want to support in your application and for which you want Cedar to provide authorization services. Cedar provides two formats (human-readable and JSON) to define a schema. The syntax of the human-readable schema format is very similar to that Cedar policy; hence the name. The JSON schema format bears some resemblance to [JSON Schema](https://json-schema.org/), but unique aspects of the design of Cedar, such as its use of entity types, require some differences. We recommend you to use the human-readable schema format for its simplicity and conciseness, although Cedar CLI provides a command to translate schemas written in both formats. - -After you define a schema, you can ask Cedar to validate your policies against it to ensure that your policies do not contain type errors, such as referencing the entities and their attributes incorrectly. - +A schema is a declaration of the structure of the entity types that you want to support in your application and for which you want Cedar to provide authorization services. +After you define a schema, you can ask Cedar to [validate your policies](../policies/validation.html) against it to ensure that your policies do not contain type errors, such as referencing the entities and their attributes incorrectly. {: .warning } >If you change your schema, any policies that you validated before the change might no longer be valid. Those policies can then generate errors during authorization queries if you include entities that match the updated schema in your request. @@ -30,18 +29,22 @@ After you define a schema, you can ask Cedar to validate your policies against i You can use a schema to define each of the following entities used by your application: -+ **Principals** – The entities that represent the users of your application. In the schema for the example PhotoFlash application, the principals consist of the `user` and `group` entity types. You can define the properties of each principal, such as a name, age, address, or any other characteristic that is important to your application. -+ **Resources** – The entities that your principals can interact with. In the PhotoFlash application, resource entities could include the photo and the album resource types. These resource entities can also include the properties of each resource, such as a photo's name, location where taken, resolution, codec type, and so on. -+ **Actions** – The operations that principals can perform on your resources. These operations include specifying which resource types each action can apply to and which principal types can perform each action. In the PhotoFlash application, actions include viewing photos, sharing photos, and commenting on photos. ++ **Principals** – The entities that represent the users of your application. In the schema for the example [PhotoFlash](../schema/human-readable-schema.html#schema-examples) application, the principals consist of the `User` and `UserGroup` entity types. You can define the properties of each principal, such as a name, age, address, or any other characteristic that is important to your application. ++ **Resources** – The entities that your principals can interact with. In the [PhotoFlash](../schema/human-readable-schema.html#schema-examples) application, resource entities could include the `Photo` and the `Album` resource types. These resource entities can also include the properties of each resource, such as a photo's name, location where taken, resolution, codec type, and so on. ++ **Actions** – The operations that principals can perform on your resources. These operations include specifying which resource types each action can apply to and which principal types can perform each action. In the [PhotoFlash](../schema/human-readable-schema.html#schema-examples) application, actions include viewing photos, uploading photos, and listing albums. Services that use Cedar can use the information provided in the schema to validate the policies you submit to the policy store. This helps prevent your policies from returning incorrect authorization decisions because of errors in policies like incorrectly typed attribute names. For more information about validating your policies, see [Cedar policy validation against schema](../policies/validation.html). -Both schema formats implement the same ideas, which we detail as follows. We then present their the human-readable JSON realizations. +Both schema formats implement the same ideas, which we detail as follows. We then present their human-readable anb JSON realizations. ## Schema {#schema-format} A schema contains a declaration of one or more namespaces, each of which contains declarations of entity types, actions, and common types. A namespace has an optional name. +* [Human-Readable schema format](../schema/human-readable-schema.html#schema-format) +* [JSON schema format](../schema/json-schema.html#schema-format) + + ## NameSpace {#schema-namespace} A [namespace](../overview/terminology.html#term-namespaces) declaration identifies and defines a scope for all entity types, actions, and common types declared within it. The name of a namespace consists of identifiers separated by double colons (`::`). @@ -61,6 +64,9 @@ Declarations (e.g, entity types) of a namespace must be qualified with its name If you change a declared namespace in your schema you will need to change the entity types appearing in your policies and/or in other namespaces declared in your schema to instead reference the changed namespace. +* [Human-Readable schema format](../schema/human-readable-schema.html#schema-namespace) +* [JSON schema format](../schema/json-schema.html#schema-namespace) + ## Entity types {#schema-entityTypes} A collection of the `principal` and `resource` entity types supported by your application. An entity type name is a Cedar identifier. An entity type declaration specifies its membership relations with other entity types and its shape. From da4082007b573b594b799b60b3f534ed490f5da3 Mon Sep 17 00:00:00 2001 From: Shaobo He Date: Wed, 21 Feb 2024 14:15:33 -0800 Subject: [PATCH 3/9] 2nd pass --- .../_schema/human-readable-schema.md | 20 ++++---- docs/collections/_schema/json-schema.md | 37 -------------- docs/collections/_schema/schema.md | 51 ++++++++++++++++--- 3 files changed, 54 insertions(+), 54 deletions(-) diff --git a/docs/collections/_schema/human-readable-schema.md b/docs/collections/_schema/human-readable-schema.md index f0b8f72..23e23d0 100644 --- a/docs/collections/_schema/human-readable-schema.md +++ b/docs/collections/_schema/human-readable-schema.md @@ -20,7 +20,7 @@ This topic describes Cedar's human-readable schema format. ## Schema format {#schema-format} -A schema consists of zero or more namespaces, each of which contains declarations of three types --- *Entity Declaration*, *Action Declaration*, and *Common Type Declaration*. These declarations define entity types, actions, and common types used to define the former two, respectively. Declarations are delimited by `;`s. Note that unlike the JSON schema format, the human-readable schema format allows you to write Cedar-style comments. +A schema consists of zero or more namespaces, each of which contains declarations of three types --- *Entity Type Declaration*, *Action Declaration*, and *Common Type Declaration*. These declarations define entity types, actions, and common types used to define the former two, respectively. Declarations are delimited by `;`s. Note that unlike the JSON schema format, the human-readable schema format allows you to write Cedar-style comments. ## NameSpace {#schema-namespace} @@ -32,7 +32,7 @@ Multiple `namespace` declarations with the same names are disallowed. This rule The following entity type declaration specifies an entity type `User` , whose parent entity type is `Group`. Entities of type `User` have three attributes, `personalGroup` of type `Group`, `delegate` of type `User`, and `blocked` of type `Set`, respectively. The attribute `delegate` is optional, which is specified by the `?` after the attribute name. -``` +```cedar entity User in [Group] { personalGroup: Group, delegate?: User, @@ -48,14 +48,14 @@ The parent types are specified by `in ` after `entity ` ### Shape {#schema-entitytypes-shape} -You specify the shape of an entity type using the [record syntax](../policies/syntax-datatypes.html#datatype-record) of Cedar policies. That is, attribute declarations are enclosed by brackets, each of which is a `:` pair. Attribute names are either identifiers or strings. Such a declaration also defines a record type. We will visit schema type syntax later in the document. To make entity type declarations consistent with common type declarations, users can write a `=` before attribute declarations like `entity User = {...};`. +You specify the shape of an entity type using the [record syntax](../policies/syntax-datatypes.html#datatype-record) of Cedar policies. That is, attribute declarations are enclosed by brackets, each of which is a `:` pair. Attribute names are either identifiers or strings. Such a declaration also defines a [record schema type](#schema-entitytypes-shape-record), which we will visit later in the document. To make entity type declarations consistent with [common type declarations](#schema-commonTypes), users can write a `=` before attribute declarations like `entity User = {...};`. Note that if you omit attribute declarations, then entities of this type do not have any attributes. This is equivalent to specifying an empty record (i.e., `{}`). ### Schema types {#schema-types} -The corresponding type names of Cedar data types [Boolean](../policies/syntax-datatypes.html#datatype-boolean), [String](../policies/syntax-datatypes.html#datatype-string), [Long](../policies/syntax-datatypes.html#datatype-string) are `Bool`, `String`, `Long`, respectively. An entity type or an extension type is specified by its name. The entity type name is either an identifier or identifiers separated by `::`. For example, both `User` and `ExampleCo::User` are both valid entity type names. An extension type name is just an identifier, specifically, `ipaddr` or `decimal` as of now. +Schema types can be used as RHS of an attribute or common type declaration. Cedar data types have their corresponding schema types: The corresponding type names of Cedar primitive data types [Boolean](../policies/syntax-datatypes.html#datatype-boolean), [String](../policies/syntax-datatypes.html#datatype-string), [Long](../policies/syntax-datatypes.html#datatype-string) are `Bool`, `String`, `Long`, respectively. An entity type or an extension type is specified by its name: The entity type name is either an identifier or identifiers separated by `::`. For example, both `User` and `ExampleCo::User` are both valid entity type names. An extension type name is just an identifier, specifically, `ipaddr` or `decimal` as of now. Since Cedar verison 3.1, we reserve the namespace `__cedar` and allow fully-qualified type names for primitive and extension types with this namespace. For example, `__cedar::ipaddr` uniquely identifies the `ipaddr` extension type. We detail the declarations of composite data types as follows. @@ -64,7 +64,7 @@ We detail the declarations of composite data types as follows. The specification of a record type is similar to that of a Cedar record, except that "values" of the former are types. For example, you can declare a record type as follows. -``` +```cedar { name: String, features: { @@ -78,15 +78,15 @@ The specification of a record type is similar to that of a Cedar record, except #### Set {#schema-entitytypes-shape-set} {: .no_toc } -A set type declaration consists of keyword `Set` and an element type surrounded by brackets (`<>`). For example, `Set` represents a set of `Long`s. +A set type declaration consists of keyword `Set` and an element type surrounded by brackets (`<>`). For example, `Set` is the type of a set of `Long`s. ## Actions {#schema-actions} The following action declaration specifies an action `ViewDocument`. It is a child action of action group `ReadActions` and applies to principals of entity type `User` and `Public`, resources of entity type `Document` and contexts of record type `{ network: ipaddr, browser: String}`. -``` -action ViewDocument in [ReadActions] appliesTo { +```cedar +action ViewDocument in [ReadActions, ExampleNS::Action::"Write"] appliesTo { principal: [User,Public], resource: Document, context: { @@ -96,7 +96,7 @@ action ViewDocument in [ReadActions] appliesTo { }; ``` -An action name is either an identifier or a string. The membership relation syntax of action declarations is akin to that of entity declarations. The difference is that action names could be strings but entity type names must be identifiers. +An action name is either an identifier or a string. The membership relation syntax of action declarations is akin to that of entity declarations. The difference is that parent action names could be strings but entity type names must be identifiers. If a parent action is declared in another namespace, its name must be a *fully-qualified action entity name*. For example, the `Write` action declared in the namespace `ExampleNS` must be referred to as `ExampleNS::Action::"Write"` in the above action declaration. The `appliesTo` construct specifies an action's applicability. It is a record of three optional keys: `principal`, `resource`, and `context` that specifies principals, resources, and contexts to which the action apply. Absence of the `appliesTo` construct means that the actions do not apply to any principals/resources/contexts. `principal` or `resource` keys, if given, must an entity type or a non-empty list of entity types. Absence of `principal` or `resource` keys means that the action applies to *unspecified* principals or resources, respectively. The `context` value must be a record and its absence defaults to an empty record. @@ -115,7 +115,7 @@ Type names in the human-readable schema format can conflict with each other. For We elaborate the second rule as the others are obvious. The priority order is common type > entity type > primitive/extension type. In other words, a type name is resolved by checking if it is declared as a common type, then entity type, and finally a primitive or extension type. The following example demonstrate this rule. -``` +```cedar namespace Demo { entity Host { // the type of attribute `ip` is common type `ipaddr` diff --git a/docs/collections/_schema/json-schema.md b/docs/collections/_schema/json-schema.md index 2e6381a..ee335c2 100644 --- a/docs/collections/_schema/json-schema.md +++ b/docs/collections/_schema/json-schema.md @@ -438,43 +438,6 @@ Each `context` entry consists of `type` and `attributes` objects. The `type` obj ## `commonTypes` {#schema-commonTypes} -Your schema might define several entity types that share a lot of elements in common. Instead of redundantly entering those elements separately for each entity that needs them, you can define those elements once using a `commonTypes` construct with a name, and then reference that construct's name in each entity that requires them. You can use this anywhere you can define a Cedar type that includes a data type specification and a set of attributes. - -### Motivation - -Suppose your schema defines several entity types or action entities that share a lot of elements in common. For example, consider the following actions: both `view` and `upload` have identical `context` components. - -```json -"actions": { - "view": { - "appliesTo": { - "context": { - "type": "Record", - "attributes": { - "ip": { "type": "Extension", "name": "ipaddr" }, - "is_authenticated": { "type": "Boolean" }, - "timestamp": { "type": "Long" } - } - } - } - }, - "upload": { - "appliesTo": { - "context": { - "type": "Record", - "attributes": { - "ip": { "type": "Extension", "name": "ipaddr" }, - "is_authenticated": { "type": "Boolean" }, - "timestamp": { "type": "Long" } - } - } - } - } -} -``` - -Instead of redundantly entering common type elements separately for each action / entity type that needs them, you can define them once within `commonTypes`, and then refer to the definition in multiple places. - ### Structure Each JSON object within `commonTypes` consists of the name of a type being defined and its associated definition. The definition is specified just like an [attribute type specification](#schema-attributes-specs), i.e., diff --git a/docs/collections/_schema/schema.md b/docs/collections/_schema/schema.md index a04a9c5..2e6bdf1 100644 --- a/docs/collections/_schema/schema.md +++ b/docs/collections/_schema/schema.md @@ -69,30 +69,67 @@ If you change a declared namespace in your schema you will need to change the en ## Entity types {#schema-entityTypes} -A collection of the `principal` and `resource` entity types supported by your application. An entity type name is a Cedar identifier. An entity type declaration specifies its membership relations with other entity types and its shape. +A collection of the `principal` and `resource` entity types supported by your application. An entity type name is a Cedar identifier. An entity type declaration specifies its membership relations with other entity types and its shape/attributes. {: .important } >The entity type name must be normalized and cannot include any embedded whitespace, such as spaces, newlines, control characters, or comments. +* [Human-Readable schema format](../schema/human-readable-schema.html#schema-entityTypes) +* [JSON schema format](../schema/json-schema.html#schema-entityTypes) + ### Membership relation -Specifies a list of entity types that can be direct parents of entities of this type. +Specifies a list of entity types that can be *direct* parents of entities of this type. + +* [Human-Readable schema format](../schema/human-readable-schema.html#schema-entitytypes-memberOf) +* [JSON schema format](../schema/json-schema.html#schema-entitytypes-memberOf) -### Shape +### Shape/Attributes Specifies the shape of the data stored in entities of this type. More precisely, it defines the attributes of an entity type --- their names, types, and optionality. -An attribute type is one of the [Cedar supported data types](../policies/syntax-datatypes.html). +An attribute type is one of the [Cedar supported data types](../policies/syntax-datatypes.html) or a common type, which have different representations in the human-readable format and the JSON format. You can choose to specify whether an attribute is required or optional. By default, attributes that you define are required. This means that policies that reference this type can assume that the attribute is always present. A policy should check for an optional attribute's presence by using the [`has`](../policies/syntax-operators.html#operator-has) operator before trying to access the attribute's value. If evaluation of a policy results in an attempt to access a non-existent attribute, evaluation fails with an error (which causes the policy to be ignored during authorization, and for a diagnostic to be generated). The validator will flag the potential for such errors to occur. +* [Human-Readable schema format](../schema/human-readable-schema.html#schema-entitytypes-shape) +* [JSON schema format](../schema/json-schema.html#schema-entitytypes-shape) + ## Actions {#schema-actions} -A collection of the `Action` entities usable as actions in authorization requests submitted by your application. The action name is an [entity identifier](../policies/syntax-entity.html#entity-overview) (rather than an entity type, as in the entity type section). -You should refer to actions in your policies by using the following syntax. If the schema declares a namespace, then the entity type `Action` is qualified by that namespace. +A collection of the `Action` entities usable as actions in authorization requests submitted by your application. The action name is an [entity identifier (EID)](../policies/syntax-entity.html#entity-overview) (rather than an entity type, as in the entity type section). For example, the action entity name of action declaration `viewPhoto` of the PhotoFlash application is `PhotoFlash::Action::"viewPhoto"` because `PhotoFlash::Action` is the fully-qualified action entity type. An action declaration specifies an action's membership relations with action groups, its applicability (with respect to principal and resource entity types, and the context shape). +* [Human-Readable schema format](../schema/human-readable-schema.html#schema-actions) +* [JSON schema format](../schema/json-schema.html#schema-actions) + ## Common type {#schema-commonTypes} -Your schema might define several entity types that share a lot of elements in common. Instead of redundantly entering those elements separately for each entity that needs them, you can define those elements once using a common type construct with a name, and then reference that construct's name in each entity that requires them. You can use this anywhere you can define a Cedar type that includes a data type specification and a set of attributes. \ No newline at end of file +Your schema might define several entity types that share a lot of elements in common. Instead of redundantly entering those elements separately for each entity that needs them, you can define those elements once using a common type construct with a name, and then reference that construct's name in each entity that requires them. You can use this anywhere you can define a Cedar type that includes a data type specification and a set of attributes. + +### Motivating example + +Suppose your schema defines several entity types or action entities that share a lot of elements in common. For example, consider the following actions in the human-readable schema format: both `view` and `upload` have identical `context` components. + +```cedar +action view appliesTo { + context: { + ip: ipaddr, + is_authenticated: Bool, + timestamp: Long + } +} +action upload appliesTo { + context: { + ip: ipaddr, + is_authenticated: Bool, + timestamp: Long + } +} +``` + +Instead of redundantly entering common type elements separately for each action / entity type that needs them, you can define them once using a common type declaration, and then refer to the definition in multiple places. + +* [Human-Readable schema format](../schema/human-readable-schema.html#schema-commonTypes) +* [JSON schema format](../schema/json-schema.html#schema-commonTypes) From 67f004ef849d85baeec3c1b94a715893b45c6644 Mon Sep 17 00:00:00 2001 From: Shaobo He Date: Wed, 13 Mar 2024 14:01:13 -0700 Subject: [PATCH 4/9] Fixes Human-Readable Signed-off-by: Shaobo He --- .../_schema/human-readable-schema-grammar.md | 4 ++-- docs/collections/_schema/human-readable-schema.md | 6 +++--- docs/collections/_schema/schema.md | 14 +++++++------- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/docs/collections/_schema/human-readable-schema-grammar.md b/docs/collections/_schema/human-readable-schema-grammar.md index 36857cd..08b8073 100644 --- a/docs/collections/_schema/human-readable-schema-grammar.md +++ b/docs/collections/_schema/human-readable-schema-grammar.md @@ -1,6 +1,6 @@ --- layout: default -title: Human-Readable schema grammar +title: Human-readable schema grammar nav_order: 2 --- @@ -47,4 +47,4 @@ STR := Fully-escaped Unicode surrounded by '"'s PRIMTYPE := 'Long' | 'String' | 'Bool' WHITESPC := Unicode whitespace COMMENT := '//' ~NEWLINE* NEWLINE -``` \ No newline at end of file +``` diff --git a/docs/collections/_schema/human-readable-schema.md b/docs/collections/_schema/human-readable-schema.md index 23e23d0..97c3b2e 100644 --- a/docs/collections/_schema/human-readable-schema.md +++ b/docs/collections/_schema/human-readable-schema.md @@ -1,10 +1,10 @@ --- layout: default -title: Human-Readable schema format +title: Human-readable schema format nav_order: 2 --- -# Human-Readable schema format {#schema} +# Human-readable schema format {#schema} {: .no_toc }
@@ -223,4 +223,4 @@ namespace PhotoFlash { } }; } -``` \ No newline at end of file +``` diff --git a/docs/collections/_schema/schema.md b/docs/collections/_schema/schema.md index 2e6bdf1..a338325 100644 --- a/docs/collections/_schema/schema.md +++ b/docs/collections/_schema/schema.md @@ -41,7 +41,7 @@ Both schema formats implement the same ideas, which we detail as follows. We the A schema contains a declaration of one or more namespaces, each of which contains declarations of entity types, actions, and common types. A namespace has an optional name. -* [Human-Readable schema format](../schema/human-readable-schema.html#schema-format) +* [Human-readable schema format](../schema/human-readable-schema.html#schema-format) * [JSON schema format](../schema/json-schema.html#schema-format) @@ -64,7 +64,7 @@ Declarations (e.g, entity types) of a namespace must be qualified with its name If you change a declared namespace in your schema you will need to change the entity types appearing in your policies and/or in other namespaces declared in your schema to instead reference the changed namespace. -* [Human-Readable schema format](../schema/human-readable-schema.html#schema-namespace) +* [Human-readable schema format](../schema/human-readable-schema.html#schema-namespace) * [JSON schema format](../schema/json-schema.html#schema-namespace) ## Entity types {#schema-entityTypes} @@ -74,14 +74,14 @@ A collection of the `principal` and `resource` entity types supported by your ap {: .important } >The entity type name must be normalized and cannot include any embedded whitespace, such as spaces, newlines, control characters, or comments. -* [Human-Readable schema format](../schema/human-readable-schema.html#schema-entityTypes) +* [Human-readable schema format](../schema/human-readable-schema.html#schema-entityTypes) * [JSON schema format](../schema/json-schema.html#schema-entityTypes) ### Membership relation Specifies a list of entity types that can be *direct* parents of entities of this type. -* [Human-Readable schema format](../schema/human-readable-schema.html#schema-entitytypes-memberOf) +* [Human-readable schema format](../schema/human-readable-schema.html#schema-entitytypes-memberOf) * [JSON schema format](../schema/json-schema.html#schema-entitytypes-memberOf) ### Shape/Attributes @@ -92,7 +92,7 @@ You can choose to specify whether an attribute is required or optional. By defau A policy should check for an optional attribute's presence by using the [`has`](../policies/syntax-operators.html#operator-has) operator before trying to access the attribute's value. If evaluation of a policy results in an attempt to access a non-existent attribute, evaluation fails with an error (which causes the policy to be ignored during authorization, and for a diagnostic to be generated). The validator will flag the potential for such errors to occur. -* [Human-Readable schema format](../schema/human-readable-schema.html#schema-entitytypes-shape) +* [Human-readable schema format](../schema/human-readable-schema.html#schema-entitytypes-shape) * [JSON schema format](../schema/json-schema.html#schema-entitytypes-shape) ## Actions {#schema-actions} @@ -101,7 +101,7 @@ A collection of the `Action` entities usable as actions in authorization request An action declaration specifies an action's membership relations with action groups, its applicability (with respect to principal and resource entity types, and the context shape). -* [Human-Readable schema format](../schema/human-readable-schema.html#schema-actions) +* [Human-readable schema format](../schema/human-readable-schema.html#schema-actions) * [JSON schema format](../schema/json-schema.html#schema-actions) ## Common type {#schema-commonTypes} @@ -131,5 +131,5 @@ action upload appliesTo { Instead of redundantly entering common type elements separately for each action / entity type that needs them, you can define them once using a common type declaration, and then refer to the definition in multiple places. -* [Human-Readable schema format](../schema/human-readable-schema.html#schema-commonTypes) +* [Human-readable schema format](../schema/human-readable-schema.html#schema-commonTypes) * [JSON schema format](../schema/json-schema.html#schema-commonTypes) From 99d3385fc0e30dfbb3161e10a6c7fa6f14523612 Mon Sep 17 00:00:00 2001 From: shaobo-he-aws <130499339+shaobo-he-aws@users.noreply.github.com> Date: Wed, 13 Mar 2024 14:38:57 -0700 Subject: [PATCH 5/9] Update docs/collections/_schema/human-readable-schema-grammar.md Co-authored-by: Kesha Hietala --- docs/collections/_schema/human-readable-schema-grammar.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/collections/_schema/human-readable-schema-grammar.md b/docs/collections/_schema/human-readable-schema-grammar.md index 08b8073..beb30e3 100644 --- a/docs/collections/_schema/human-readable-schema-grammar.md +++ b/docs/collections/_schema/human-readable-schema-grammar.md @@ -16,7 +16,7 @@ For grammar productions it uses `|` for alternatives, `[]` for optional producti Tokens are defined using regular expressions, where `[]` stands for character ranges; `|` stands for alternation; `*` , `+` , and `?` stand for zero or more, one or more, and zero or one occurrences, respectively; `~` stands for complement; and `-` stands for difference. The grammar ignores whitespace and comments. -The grammar adopts the same string escaping rules as Cedar policy grammar. +The grammar adopts the same string escaping rules as the Cedar policy grammar. ``` Schema := {Namespace} From 817be5aa10c618d66b44cc99551000a34c1144ed Mon Sep 17 00:00:00 2001 From: Brandon Cotter Date: Wed, 13 Mar 2024 15:11:12 -0700 Subject: [PATCH 6/9] clarity edits and added redirect for schema-grammar --- .../_schema/human-readable-schema-grammar.md | 27 +++++++-- .../_schema/human-readable-schema.md | 60 +++++++++++++------ docs/collections/_schema/schema-grammar.md | 6 ++ docs/collections/_schema/schema.md | 23 ++++--- docs/schema-grammar.md | 2 +- 5 files changed, 84 insertions(+), 34 deletions(-) create mode 100644 docs/collections/_schema/schema-grammar.md diff --git a/docs/collections/_schema/human-readable-schema-grammar.md b/docs/collections/_schema/human-readable-schema-grammar.md index 36857cd..fd5b8ff 100644 --- a/docs/collections/_schema/human-readable-schema-grammar.md +++ b/docs/collections/_schema/human-readable-schema-grammar.md @@ -1,22 +1,37 @@ --- layout: default -title: Human-Readable schema grammar +title: Human-readable schema grammar nav_order: 2 --- -# Grammar specification for the human-readable schema format {#schema-grammar} +# Grammar specification for human-readable schema {#schema-grammar} {: .no_toc } This topic describes the grammar specification for the human-readable schema format. For a more complete description, see [Schema format](../schema/human-readable-schema-format). -The grammar applies the following the conventions. Capitalized words stand for grammar productions, and lexical tokens are given in all-caps. When productions or tokens match those in the Cedar policy grammar, we use the same names (e.g., `IDENT` and `Path`). +The grammar applies the following conventions. ++ Words with initial capital letters designate grammar constructs. ++ Words in all capital letters designate lexical tokens. -For grammar productions it uses `|` for alternatives, `[]` for optional productions, `()` for grouping, and `{}` for repetition of a form zero or more times. +When productions or tokens match those in the Cedar policy grammar, use the same names, for example `IDENT` and `Path. -Tokens are defined using regular expressions, where `[]` stands for character ranges; `|` stands for alternation; `*` , `+` , and `?` stand for zero or more, one or more, and zero or one occurrences, respectively; `~` stands for complement; and `-` stands for difference. The grammar ignores whitespace and comments. +Grammar productions use the following symbols: ++ `|` designates alternatives. ++ `[]` designates optional productions. ++ `()` designates grouping. ++ `{}` designates repetition of a form zero or more times. -The grammar adopts the same string escaping rules as Cedar policy grammar. +Tokens are defined using regular expressions: ++ `[]` designates character ranges. ++ `|` designates alternation. ++ `*` , `+` , and `?` designate zero or more, one or more, and zero or one occurrences, respectively. ++ `~` designates complement. ++ `-` designates difference. + +The human-readable grammar ignores whitespace and comments. + +The grammar adopts the same string escaping rules as [Cedar policy grammar](../policies/syntax-grammar.html). ``` Schema := {Namespace} diff --git a/docs/collections/_schema/human-readable-schema.md b/docs/collections/_schema/human-readable-schema.md index 23e23d0..567000b 100644 --- a/docs/collections/_schema/human-readable-schema.md +++ b/docs/collections/_schema/human-readable-schema.md @@ -4,7 +4,7 @@ title: Human-Readable schema format nav_order: 2 --- -# Human-Readable schema format {#schema} +# Human-readable schema format {#schema} {: .no_toc }
@@ -20,17 +20,24 @@ This topic describes Cedar's human-readable schema format. ## Schema format {#schema-format} -A schema consists of zero or more namespaces, each of which contains declarations of three types --- *Entity Type Declaration*, *Action Declaration*, and *Common Type Declaration*. These declarations define entity types, actions, and common types used to define the former two, respectively. Declarations are delimited by `;`s. Note that unlike the JSON schema format, the human-readable schema format allows you to write Cedar-style comments. +A schema consists of zero or more namespaces, each of which contains declarations of three types --- *Entity Type Declaration*, *Action Declaration*, and *Common Type Declaration*. These declarations define entity types and actions, and common types that define types that can be referenced by the Entity Type and Action Declarations. Declarations are delimited by `;` characters. Note that in the human-readable schema format, unlike in the JSON schema format, you can write Cedar-style comments. ## NameSpace {#schema-namespace} -You can group the declarations of a namespace by wrapping them with curly braces like `namespace Foo {...}`. A name must be given for a namespace specified by such syntax. An alternative way to declare a namespace is to “inline” declarations like `entity Bar; namespace Foo {...}`, where the entity declaration of `Bar` is under the same scope of namespace `Foo`. Names of these declarations are always referred as they are whereas declaration names of a `namespace` construct must be referred as their fully-qualified forms in other namespaces. +You can create a namespace that you want to associate with your declarations. Add the namespace key word, for example `namespace Foo { entity Bar; }`. The name of a namespace must be an identifier as specified in Cedar syntax. Anything declared in this namespace must be referred to in its fully-qualified form when referenced outside of the namespace, so the declared entity type would be `Foo::Bar`. + +Alternatively, you can create a declaration with a namespace, for example `entity Bar;`. The names of declarations that lack a namespace are always referred to without qualification, for example `Bar`. Multiple `namespace` declarations with the same names are disallowed. This rule also applies to the inner declarations like entity type declarations. ## Entity type {#schema-entityTypes} -The following entity type declaration specifies an entity type `User` , whose parent entity type is `Group`. Entities of type `User` have three attributes, `personalGroup` of type `Group`, `delegate` of type `User`, and `blocked` of type `Set`, respectively. The attribute `delegate` is optional, which is specified by the `?` after the attribute name. +The following entity type declaration specifies an entity type `User` , whose parent entity type is `Group`. Entities of type `User` have three attributes: ++ `personalGroup` of type `Group` ++ `delegate` of type `User` ++ `blocked` of type `Set` + +The attribute `delegate` is optional, as indicated by the `?` after the attribute name. ```cedar entity User in [Group] { @@ -40,29 +47,35 @@ entity User in [Group] { }; ``` -Note that, unlike the JSON schema format, human-readable schema syntax allows you to declare multiple entity types that share the same definition using a single declaration. For example, `entity UserA, UserB, UserC ...` declares entity types `UserA`, `UserB`, and `UserC` that all have the same membership relations and shapes. +Note that in the human-readable schema format, unlike in the JSON schema format, you can declare multiple entity types that share the same definition using a single declaration. For example, `entity UserA, UserB, UserC` declares entity types `UserA`, `UserB`, and `UserC` that all have the same membership relations and shapes. ### Membership relations {#schema-entitytypes-memberOf} -The parent types are specified by `in ` after `entity `, where `EntityTypes` can be a list of entity type names surrounded by brackets and delimited by `,`, or an entity type name if there is only one parent type. For example, you can also declare entity type `User` like `entity User in Group ...`. The membership relation declaration is optional, whose absence means that the declared entity type does not have any parent entity types. +The parent types are specified by `in ` after `entity `. `EntityTypes` can be a list of entity type names surrounded by brackets and delimited by `,`, or an entity type name if there is only one parent type. For example, you can also declare entity type `User` in the format `entity User in Group ...`. The membership relation declaration is optional. If you don't create this declaration, the declared entity type doesn't have any parent entity types. ### Shape {#schema-entitytypes-shape} -You specify the shape of an entity type using the [record syntax](../policies/syntax-datatypes.html#datatype-record) of Cedar policies. That is, attribute declarations are enclosed by brackets, each of which is a `:` pair. Attribute names are either identifiers or strings. Such a declaration also defines a [record schema type](#schema-entitytypes-shape-record), which we will visit later in the document. To make entity type declarations consistent with [common type declarations](#schema-commonTypes), users can write a `=` before attribute declarations like `entity User = {...};`. +Specify the shape of an entity type using the [record syntax](../policies/syntax-datatypes.html#datatype-record) of Cedar policies. Enclose attribute declarations in brackets, each of which is a `:` key-value pair. Attribute names are either identifiers or strings. Such a declaration also defines a [record schema type](#schema-entitytypes-shape-record). To make entity type declarations consistent with [common type declarations](#schema-commonTypes), you can prefix a `=` to attribute declarations, for example `entity User = {...};`. Note that if you omit attribute declarations, then entities of this type do not have any attributes. This is equivalent to specifying an empty record (i.e., `{}`). ### Schema types {#schema-types} -Schema types can be used as RHS of an attribute or common type declaration. Cedar data types have their corresponding schema types: The corresponding type names of Cedar primitive data types [Boolean](../policies/syntax-datatypes.html#datatype-boolean), [String](../policies/syntax-datatypes.html#datatype-string), [Long](../policies/syntax-datatypes.html#datatype-string) are `Bool`, `String`, `Long`, respectively. An entity type or an extension type is specified by its name: The entity type name is either an identifier or identifiers separated by `::`. For example, both `User` and `ExampleCo::User` are both valid entity type names. An extension type name is just an identifier, specifically, `ipaddr` or `decimal` as of now. Since Cedar verison 3.1, we reserve the namespace `__cedar` and allow fully-qualified type names for primitive and extension types with this namespace. For example, `__cedar::ipaddr` uniquely identifies the `ipaddr` extension type. +Schema types can be used as RHS of an attribute or common type declaration. + +Cedar data types have corresponding schema types. The corresponding type names of Cedar primitive data types [Boolean](../policies/syntax-datatypes.html#datatype-boolean), [String](../policies/syntax-datatypes.html#datatype-string), [Long](../policies/syntax-datatypes.html#datatype-string) are `Bool`, `String`, `Long`, respectively. + +An entity type or an extension type is specified by its name. The entity type name is an identifier or identifiers separated by `::`. For example, both `User` and `ExampleCo::User` are valid entity type names. -We detail the declarations of composite data types as follows. +An extension type name is an identifier. Currently, `ipaddr` and `decimal` are the only available extension type names. Since the release of version 3.1 of the Cedar language, the namespace `__cedar` is a reserved namespace. You can specify fully-qualified type names for primitive and extension types under the `__cedar` namespace. For example, `__cedar::ipaddr` uniquely identifies the `ipaddr` extension type. + +Format composite data type declarations as follows. #### Record {#schema-entitytypes-shape-record} {: .no_toc } -The specification of a record type is similar to that of a Cedar record, except that "values" of the former are types. For example, you can declare a record type as follows. +The specification of a record type is similar to that of a Cedar record, except that values of a record in the human-readable schema are types. For example, you can declare a record type as follows. ```cedar { @@ -78,12 +91,16 @@ The specification of a record type is similar to that of a Cedar record, except #### Set {#schema-entitytypes-shape-set} {: .no_toc } -A set type declaration consists of keyword `Set` and an element type surrounded by brackets (`<>`). For example, `Set` is the type of a set of `Long`s. +A set type declaration consists of keyword `Set` and an element type surrounded by angle brackets (`<>`). For example, `Set` is a set type made up of values of type `Long`. ## Actions {#schema-actions} -The following action declaration specifies an action `ViewDocument`. It is a child action of action group `ReadActions` and applies to principals of entity type `User` and `Public`, resources of entity type `Document` and contexts of record type `{ network: ipaddr, browser: String}`. +The following action declaration defines the action `ViewDocument`. It has the following characteristics: ++ It's a member of action group `ReadActions` ++ It applies to principals of entity type `User` and `Public` ++ It applies to resources of entity type `Document` ++ It applies to context of record types `network: ipaddr` and `browser: String`. ```cedar action ViewDocument in [ReadActions, ExampleNS::Action::"Write"] appliesTo { @@ -96,24 +113,28 @@ action ViewDocument in [ReadActions, ExampleNS::Action::"Write"] appliesTo { }; ``` -An action name is either an identifier or a string. The membership relation syntax of action declarations is akin to that of entity declarations. The difference is that parent action names could be strings but entity type names must be identifiers. If a parent action is declared in another namespace, its name must be a *fully-qualified action entity name*. For example, the `Write` action declared in the namespace `ExampleNS` must be referred to as `ExampleNS::Action::"Write"` in the above action declaration. +An action name is either an identifier or a string. The membership relation syntax of action declarations is like that of entity declarations, but parent action names can be strings, and entity type names must be identifiers. If a parent action is declared in another namespace, its name must be a *fully-qualified action entity name*. This is illustrated by the action `Write` in the example action declaration. It's declared in the namespace `ExampleNS` with the fully-qualified name `ExampleNS::Action::"Write"`. -The `appliesTo` construct specifies an action's applicability. It is a record of three optional keys: `principal`, `resource`, and `context` that specifies principals, resources, and contexts to which the action apply. Absence of the `appliesTo` construct means that the actions do not apply to any principals/resources/contexts. `principal` or `resource` keys, if given, must an entity type or a non-empty list of entity types. Absence of `principal` or `resource` keys means that the action applies to *unspecified* principals or resources, respectively. +The `appliesTo` construct specifies an action's applicability. It is a record of three optional keys: `principal`, `resource`, and `context` that the action applies to. Without the `appliesTo` construct in your schema, the actions do not apply to any principals, resources, or contexts. The `principal` and `resource` keys, if given, must an entity type or a non-empty list of entity types. Without `principal` or `resource` keys in your schema, the action applies to *unspecified* principals or resources, respectively. The `context` value must be a record and its absence defaults to an empty record. ## Common types {#schema-commonTypes} -Like the JSON schema format, human-readable schema syntax allows for declarations of common types so that entity type declarations can use them to avoid error-prone duplication. The syntax of common type declarations is similar to defining type aliases in most programming languages: `type = ` . The right hand side of `=` is a schema type name except for common types to avoid definitional circularity. +Like in the JSON schema format, human-readable schema syntax allows for declarations of common types so that entity type declarations can use them to avoid error-prone duplication. The syntax of common type declarations is similar to defining type aliases in most programming languages: `type = ` . The `Type` value is a schema type name, excluding common types to avoid definitional circularity. ## Type name disambiguation -Type names in the human-readable schema format can conflict with each other. For example, `ipaddr` is a valid unqualified common type name as well as an extension type name. `Foo::Bar` is a valid qualified common type name and an entity type name. We use the following rules to disambiguate type names. +Type names in the human-readable schema format can conflict with each other. For example, `ipaddr` is a valid unqualified common type name as well as an extension type name. `Foo::Bar` is a valid qualified common type name and an entity type name. Cedar uses the following rules to disambiguate type names. 1. Primitive and extension type names cannot alias by design. 2. Type references are resolved in a priority order. -3. Reserve `__cedar` as a namespace to disambiguate extension/primitive types from others. For example, `__cedar::Long` uniquely refers to Cedar primitive type `Long`. +3. To disambiguate extension and primitive types from others, the namespace `__cedar` is reserved. For example, `__cedar::Long` uniquely refers to Cedar primitive type `Long`. -We elaborate the second rule as the others are obvious. The priority order is common type > entity type > primitive/extension type. In other words, a type name is resolved by checking if it is declared as a common type, then entity type, and finally a primitive or extension type. The following example demonstrate this rule. +The priority order is +``` +common type > entity type > primitive/extension type +``` +A type name is resolved by checking if it is declared as a common type, then entity type, and finally a primitive or extension type. The following example demonstrates this rule. ```cedar namespace Demo { @@ -148,7 +169,8 @@ namespace Demo { } ``` - Note that both common types and entity types can be qualified with namespaces. And the human-readable format allows "inline" declarations. So, there may be conflicts between type names declared within a namespace and those declared using "inline" declarations. The resolution rule for this scenario is akin to static scoping. That is, type names within the same namespace have higher priority. The following example demonstrates this rule. + Common types and entity types can both be qualified with namespaces. + The human-readable format allows *inline* declarations. Because of this, there may be conflicts between type names declared within a namespace and those declared using inline declarations. The resolution rule for this scenario is like *static scoping*: type names within the same namespace have higher priority. The following example demonstrates this rule. ``` type id = { diff --git a/docs/collections/_schema/schema-grammar.md b/docs/collections/_schema/schema-grammar.md new file mode 100644 index 0000000..02c7d95 --- /dev/null +++ b/docs/collections/_schema/schema-grammar.md @@ -0,0 +1,6 @@ +--- +layout: forward +target: schema/json-schema-grammar.html +--- + + diff --git a/docs/collections/_schema/schema.md b/docs/collections/_schema/schema.md index 2e6bdf1..34248e0 100644 --- a/docs/collections/_schema/schema.md +++ b/docs/collections/_schema/schema.md @@ -16,7 +16,7 @@ nav_order: 1 {:toc}
-This topic describes the structure of a Cedar schema. There are two schema formats --- human-readable and JSON. Cedar provides two formats (human-readable and JSON) to define a schema. The syntax of the human-readable schema format is very similar to that of Cedar policies; hence the name. The JSON schema format bears some resemblance to [JSON Schema](https://json-schema.org/), but unique aspects of the design of Cedar, such as its use of entity types, require some differences. The two formats are interchangeable and Cedar CLI provides a command to translate schemas in one format to the other. We encourage you to use the human-readable schema format for its simplicity and conciseness. To view their details, see [human-readable schema format](../schema/human-readable-schema.html) and [JSON schema format](../schema/json-schema.html). +This topic describes the structure of a Cedar schema. Cedar has two schema formats: human-readable and JSON. The syntax of the human-readable schema format is very similar to that of Cedar policies. The JSON schema format is built from the [JSON Schema](https://json-schema.org/). Some unique aspects of the design of Cedar, like the use of entity types, differ from the base JSON specification. The two formats are interchangeable. The Cedar CLI can translate schemas in one format to the other. We encourage you to use the human-readable schema format for its simplicity and conciseness. For details, see [human-readable schema format](../schema/human-readable-schema.html) and [JSON schema format](../schema/json-schema.html). ## Overview {#schema-overview} @@ -41,7 +41,8 @@ Both schema formats implement the same ideas, which we detail as follows. We the A schema contains a declaration of one or more namespaces, each of which contains declarations of entity types, actions, and common types. A namespace has an optional name. -* [Human-Readable schema format](../schema/human-readable-schema.html#schema-format) +**Schema topics** +* [Human-readable schema format](../schema/human-readable-schema.html#schema-format) * [JSON schema format](../schema/json-schema.html#schema-format) @@ -64,7 +65,8 @@ Declarations (e.g, entity types) of a namespace must be qualified with its name If you change a declared namespace in your schema you will need to change the entity types appearing in your policies and/or in other namespaces declared in your schema to instead reference the changed namespace. -* [Human-Readable schema format](../schema/human-readable-schema.html#schema-namespace) +**Namespace topics** +* [Human-readable schema format](../schema/human-readable-schema.html#schema-namespace) * [JSON schema format](../schema/json-schema.html#schema-namespace) ## Entity types {#schema-entityTypes} @@ -74,14 +76,16 @@ A collection of the `principal` and `resource` entity types supported by your ap {: .important } >The entity type name must be normalized and cannot include any embedded whitespace, such as spaces, newlines, control characters, or comments. -* [Human-Readable schema format](../schema/human-readable-schema.html#schema-entityTypes) +**Entity type topics** +* [Human-readable schema format](../schema/human-readable-schema.html#schema-entityTypes) * [JSON schema format](../schema/json-schema.html#schema-entityTypes) ### Membership relation Specifies a list of entity types that can be *direct* parents of entities of this type. -* [Human-Readable schema format](../schema/human-readable-schema.html#schema-entitytypes-memberOf) +**Membership topics** +* [Human-readable schema format](../schema/human-readable-schema.html#schema-entitytypes-memberOf) * [JSON schema format](../schema/json-schema.html#schema-entitytypes-memberOf) ### Shape/Attributes @@ -92,7 +96,8 @@ You can choose to specify whether an attribute is required or optional. By defau A policy should check for an optional attribute's presence by using the [`has`](../policies/syntax-operators.html#operator-has) operator before trying to access the attribute's value. If evaluation of a policy results in an attempt to access a non-existent attribute, evaluation fails with an error (which causes the policy to be ignored during authorization, and for a diagnostic to be generated). The validator will flag the potential for such errors to occur. -* [Human-Readable schema format](../schema/human-readable-schema.html#schema-entitytypes-shape) +**Shape topics** +* [Human-readable schema format](../schema/human-readable-schema.html#schema-entitytypes-shape) * [JSON schema format](../schema/json-schema.html#schema-entitytypes-shape) ## Actions {#schema-actions} @@ -101,7 +106,8 @@ A collection of the `Action` entities usable as actions in authorization request An action declaration specifies an action's membership relations with action groups, its applicability (with respect to principal and resource entity types, and the context shape). -* [Human-Readable schema format](../schema/human-readable-schema.html#schema-actions) +**Actions topics** +* [Human-readable schema format](../schema/human-readable-schema.html#schema-actions) * [JSON schema format](../schema/json-schema.html#schema-actions) ## Common type {#schema-commonTypes} @@ -131,5 +137,6 @@ action upload appliesTo { Instead of redundantly entering common type elements separately for each action / entity type that needs them, you can define them once using a common type declaration, and then refer to the definition in multiple places. -* [Human-Readable schema format](../schema/human-readable-schema.html#schema-commonTypes) +**Common types topics** +* [Human-readable schema format](../schema/human-readable-schema.html#schema-commonTypes) * [JSON schema format](../schema/json-schema.html#schema-commonTypes) diff --git a/docs/schema-grammar.md b/docs/schema-grammar.md index 4ac65fb..02c7d95 100644 --- a/docs/schema-grammar.md +++ b/docs/schema-grammar.md @@ -1,6 +1,6 @@ --- layout: forward -target: schema/schema-grammar.html +target: schema/json-schema-grammar.html --- From ba864aeca4e2df281c6f1dab4a6d112789b78389 Mon Sep 17 00:00:00 2001 From: Brandon Cotter Date: Wed, 13 Mar 2024 15:22:45 -0700 Subject: [PATCH 7/9] minor additional clarity edits --- docs/collections/_schema/human-readable-schema-grammar.md | 6 +++--- docs/collections/_schema/human-readable-schema.md | 6 ++++-- docs/collections/_schema/json-schema-grammar.md | 2 +- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/docs/collections/_schema/human-readable-schema-grammar.md b/docs/collections/_schema/human-readable-schema-grammar.md index fd5b8ff..be8f9d4 100644 --- a/docs/collections/_schema/human-readable-schema-grammar.md +++ b/docs/collections/_schema/human-readable-schema-grammar.md @@ -5,7 +5,7 @@ nav_order: 2 --- -# Grammar specification for human-readable schema {#schema-grammar} +# Grammar specification for human-readable schemas {#schema-grammar} {: .no_toc } This topic describes the grammar specification for the human-readable schema format. For a more complete description, see [Schema format](../schema/human-readable-schema-format). @@ -14,9 +14,9 @@ The grammar applies the following conventions. + Words with initial capital letters designate grammar constructs. + Words in all capital letters designate lexical tokens. -When productions or tokens match those in the Cedar policy grammar, use the same names, for example `IDENT` and `Path. +When productions or tokens match those in the Cedar policy grammar, use the same names, for example `IDENT` and `Path`. -Grammar productions use the following symbols: +Grammar constructs use the following symbols: + `|` designates alternatives. + `[]` designates optional productions. + `()` designates grouping. diff --git a/docs/collections/_schema/human-readable-schema.md b/docs/collections/_schema/human-readable-schema.md index 567000b..aaab3e1 100644 --- a/docs/collections/_schema/human-readable-schema.md +++ b/docs/collections/_schema/human-readable-schema.md @@ -51,13 +51,15 @@ Note that in the human-readable schema format, unlike in the JSON schema format, ### Membership relations {#schema-entitytypes-memberOf} -The parent types are specified by `in ` after `entity `. `EntityTypes` can be a list of entity type names surrounded by brackets and delimited by `,`, or an entity type name if there is only one parent type. For example, you can also declare entity type `User` in the format `entity User in Group ...`. The membership relation declaration is optional. If you don't create this declaration, the declared entity type doesn't have any parent entity types. +Set an entity to be a member of another with `in ` after `entity `. The `EntityTypes` declaration can be a list of entity type names surrounded by brackets (`[]`) and delimited by commas `,`, for example `entity User in [UserGroup1, UserGroup2]`. Entities with one parent type don't require brackets, for example `entity User in UserGroup`. + +The membership relation declaration is optional. If you don't create this declaration, the declared entity type doesn't have any parent entity types. ### Shape {#schema-entitytypes-shape} Specify the shape of an entity type using the [record syntax](../policies/syntax-datatypes.html#datatype-record) of Cedar policies. Enclose attribute declarations in brackets, each of which is a `:` key-value pair. Attribute names are either identifiers or strings. Such a declaration also defines a [record schema type](#schema-entitytypes-shape-record). To make entity type declarations consistent with [common type declarations](#schema-commonTypes), you can prefix a `=` to attribute declarations, for example `entity User = {...};`. -Note that if you omit attribute declarations, then entities of this type do not have any attributes. This is equivalent to specifying an empty record (i.e., `{}`). +Note that if you omit attribute declarations, then entities of this type don't have any attributes. This is equivalent to specifying an empty record (i.e., `{}`). ### Schema types {#schema-types} diff --git a/docs/collections/_schema/json-schema-grammar.md b/docs/collections/_schema/json-schema-grammar.md index 68f07bc..a0caa78 100644 --- a/docs/collections/_schema/json-schema-grammar.md +++ b/docs/collections/_schema/json-schema-grammar.md @@ -5,7 +5,7 @@ nav_order: 2 --- -# Grammar specification for Cedar schema {#schema-grammar} +# Grammar specification for JSON schemas {#schema-grammar} {: .no_toc } This topic describes the grammar specification for the Cedar schema. For a more complete description, see [Schema format](../schema/schema.html). From bd5772b9e87105e8acfbde401226380e5910cdb0 Mon Sep 17 00:00:00 2001 From: Brandon Cotter Date: Wed, 13 Mar 2024 15:49:19 -0700 Subject: [PATCH 8/9] integrated comments --- docs/collections/_schema/human-readable-schema-grammar.md | 2 -- docs/collections/_schema/human-readable-schema.md | 2 +- docs/collections/_schema/schema.md | 2 +- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/docs/collections/_schema/human-readable-schema-grammar.md b/docs/collections/_schema/human-readable-schema-grammar.md index be8f9d4..d908ebf 100644 --- a/docs/collections/_schema/human-readable-schema-grammar.md +++ b/docs/collections/_schema/human-readable-schema-grammar.md @@ -29,8 +29,6 @@ Tokens are defined using regular expressions: + `~` designates complement. + `-` designates difference. -The human-readable grammar ignores whitespace and comments. - The grammar adopts the same string escaping rules as [Cedar policy grammar](../policies/syntax-grammar.html). ``` diff --git a/docs/collections/_schema/human-readable-schema.md b/docs/collections/_schema/human-readable-schema.md index aaab3e1..b3d6023 100644 --- a/docs/collections/_schema/human-readable-schema.md +++ b/docs/collections/_schema/human-readable-schema.md @@ -26,7 +26,7 @@ A schema consists of zero or more namespaces, each of which contains declaration You can create a namespace that you want to associate with your declarations. Add the namespace key word, for example `namespace Foo { entity Bar; }`. The name of a namespace must be an identifier as specified in Cedar syntax. Anything declared in this namespace must be referred to in its fully-qualified form when referenced outside of the namespace, so the declared entity type would be `Foo::Bar`. -Alternatively, you can create a declaration with a namespace, for example `entity Bar;`. The names of declarations that lack a namespace are always referred to without qualification, for example `Bar`. +Alternatively, you can create a declaration without a namespace, for example `entity Bar;`. The names of declarations that lack a namespace are always referred to without qualification, for example `Bar`. Multiple `namespace` declarations with the same names are disallowed. This rule also applies to the inner declarations like entity type declarations. diff --git a/docs/collections/_schema/schema.md b/docs/collections/_schema/schema.md index 34248e0..41f197f 100644 --- a/docs/collections/_schema/schema.md +++ b/docs/collections/_schema/schema.md @@ -16,7 +16,7 @@ nav_order: 1 {:toc}
-This topic describes the structure of a Cedar schema. Cedar has two schema formats: human-readable and JSON. The syntax of the human-readable schema format is very similar to that of Cedar policies. The JSON schema format is built from the [JSON Schema](https://json-schema.org/). Some unique aspects of the design of Cedar, like the use of entity types, differ from the base JSON specification. The two formats are interchangeable. The Cedar CLI can translate schemas in one format to the other. We encourage you to use the human-readable schema format for its simplicity and conciseness. For details, see [human-readable schema format](../schema/human-readable-schema.html) and [JSON schema format](../schema/json-schema.html). +This topic describes the structure of a Cedar schema. Cedar has two schema formats: human-readable and JSON. The syntax of the human-readable schema format is very similar to that of Cedar policies. The JSON schema format is built from the [JSON Schema](https://json-schema.org/). Some unique aspects of the design of Cedar, like the use of entity types, differ from the common JSON schema. The two formats are interchangeable. The Cedar CLI can translate schemas in one format to the other. We encourage you to use the human-readable schema format for its simplicity and conciseness. For details, see [human-readable schema format](../schema/human-readable-schema.html) and [JSON schema format](../schema/json-schema.html). ## Overview {#schema-overview} From c3458960f9aa393181f93d1a58165f71124d53b3 Mon Sep 17 00:00:00 2001 From: Brandon Cotter Date: Wed, 13 Mar 2024 16:00:32 -0700 Subject: [PATCH 9/9] RHS -> right-hand side --- docs/collections/_schema/human-readable-schema.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/collections/_schema/human-readable-schema.md b/docs/collections/_schema/human-readable-schema.md index 9c62884..20d4162 100644 --- a/docs/collections/_schema/human-readable-schema.md +++ b/docs/collections/_schema/human-readable-schema.md @@ -64,7 +64,7 @@ Note that if you omit attribute declarations, then entities of this type don't h ### Schema types {#schema-types} -Schema types can be used as RHS of an attribute or common type declaration. +Schema types can be used as right-hand side of an attribute or common type declaration. Cedar data types have corresponding schema types. The corresponding type names of Cedar primitive data types [Boolean](../policies/syntax-datatypes.html#datatype-boolean), [String](../policies/syntax-datatypes.html#datatype-string), [Long](../policies/syntax-datatypes.html#datatype-string) are `Bool`, `String`, `Long`, respectively.