cedar-policy/cedar

Converted JSON schemas use "EntityOrCommon"

Opened this issue · 3 comments

Describe the improvement you'd like to request

Due to recent changes introduced on main (which are currently unreleased, but will be released as part of 4.0), converting a Cedar schema to JSON will now introduce "EntityOrCommon" types. These seems like an internal detail that is not useful to expose to users. Is there a way to hide this detail when returning JSON data from SchemaFragment::to_json_string?

As an example, here's a snippet from the translation of the TinyTodo schema:

{
  "": {
    "commonTypes": {
      "Task": {
        "type": "Record",
        "attributes": {
          "id": {
            "type": "EntityOrCommon",
            "name": "Long"
          },
          "name": {
            "type": "EntityOrCommon",
            "name": "String"
          },
          "state": {
            "type": "EntityOrCommon",
            "name": "String"
          }
        }
      },
      "Tasks": {
        "type": "Set",
        "element": {
          "type": "EntityOrCommon",
          "name": "Task"
        }
      }
    },
    "entityTypes": {
      "Team": {
        "memberOfTypes": [
          "Team",
          "Application"
        ]
      },
      "User": {
        "memberOfTypes": [
          "Team",
          "Application"
        ],
        "shape": {
          "type": "Record",
          "attributes": {
            "joblevel": {
              "type": "EntityOrCommon",
              "name": "Long"
            },
            "location": {
              "type": "EntityOrCommon",
              "name": "String"
            }
          }
        }
      },
...

The more natural way for a user to write this would be:

{
  "": {
    "commonTypes": {
      "Task": {
        "type": "Record",
        "attributes": {
          "id": {
            "type": "Long"
          },
          "name": {
            "type": "String"
          },
          "state": {
            "type": "String"
          }
        }
      },
      "Tasks": {
        "type": "Set",
        "element": {
          "type": "Task"
        }
      }
    },
    "entityTypes": {
      "Team": {
        "memberOfTypes": [
          "Team",
          "Application"
        ]
      },
      "User": {
        "memberOfTypes": [
          "Team",
          "Application"
        ],
        "shape": {
          "type": "Record",
          "attributes": {
            "joblevel": {
              "type": "Long"
            },
            "location": {
              "type": "String"
            }
          }
        }
      },
...

Describe alternatives you've considered

No response

Additional context

Related: the Display trait for the Cedar schema format is also not as pretty as it could be. See #682.

Is this something that you'd be interested in working on?

  • 👋 I may be able to implement this internal improvement
  • ⚠️ This feature might incur a breaking change

some context: in some ways, EntityOrCommon is more correct than the previous status quo. The Cedar syntax Long will actually refer to an entity type Long if it exists, in preference to the builtin type Long, as specified by RFC 24. (This is why RFC 24 introduced the __cedar::Long notation in the first place.) This remains true after #1150, which only introduced restrictions on the names of common types, not entity types. So, translating Cedar syntax Long to JSON EntityOrCommon is an accurate reflection of the semantics of the Cedar syntax.

To resolve this issue, we would have to actually resolve the typenames in the Cedar syntax (e.g., to know that Long does indeed refer to the builtin type Long) in order to write a more accurate JSON-syntax typename.

I attempted to add a common type with type String = __cedar::String; but cedar validate returns an error:

error parsing schema: this uses a reserved schema keyword: `String`
 2 │     type String = __cedar::String;
  help: Keywords such as `entity`, `extension`, `set` and `record` cannot be used as common type names

On the other hand, I am able to create an Entity with the name String—that just earns me a warning:

  ⚠ The name `String` shadows a builtin Cedar name. You'll have to refer to the builtin as `__cedar::String`.

My own personal issue with using EntityOrCommon for built-in primitive types is that Amazon Verified Permissions does not work with JSON-formatted cedar schema like this. It doesn't seem to understand EntityOrCommon at all, so I can't use cedar translate-schema to convert my nice schema into JSON form to hand over to Amazon Verified Permissions without post-processing to remove all the EntityOrCommon references. I guess that's technically an AWS problem, not a Cedar problem, but... it's extra friction in the ecosystem.

If you want to use your schema with AVP, it should work if you convert the schema with the latest 3.x CLI release. This release won't insert EntityOrCommon, so it should work