Skip to content

Commit

Permalink
tsify ValidateCall and schema types for cedar-wasm, add wasm build to…
Browse files Browse the repository at this point in the history
… CI (#692)

Signed-off-by: Nick Szegheo <[email protected]>
  • Loading branch information
ncsze authored Mar 5, 2024
1 parent 75c94d1 commit 95b126d
Show file tree
Hide file tree
Showing 8 changed files with 55 additions and 2 deletions.
11 changes: 11 additions & 0 deletions .github/workflows/build_and_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -70,3 +70,14 @@ jobs:
steps:
- uses: actions/checkout@v3
- uses: EmbarkStudios/cargo-deny-action@v1

wasm-build:
name: run wasm build script
runs-on: ubuntu-latest
strategy:
matrix:
toolchain:
- stable
steps:
- uses: actions/checkout@v3
- run: cd ./cedar-wasm && cargo install wasm-pack && ./build-wasm.sh
6 changes: 6 additions & 0 deletions cedar-policy-validator/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ lalrpop-util = { version = "0.20.0", features = ["lexer", "unicode"] }
lazy_static = "1.4.0"
nonempty = "0.10.0"

# wasm dependencies
serde-wasm-bindgen = { version = "0.6", optional = true }
tsify = { version = "0.4.5", optional = true }
wasm-bindgen = { version = "0.2.82", optional = true }

[features]
# by default, enable all Cedar extensions
default = ["ipaddr", "decimal"]
Expand All @@ -38,6 +43,7 @@ arbitrary = ["dep:arbitrary"]

# Experimental features.
partial-validate = []
wasm = ["serde-wasm-bindgen", "tsify", "wasm-bindgen"]

[dev-dependencies]
cool_asserts = "2.0"
Expand Down
23 changes: 22 additions & 1 deletion cedar-policy-validator/src/schema_file_format.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,18 @@ use crate::{
HumanSchemaError, Result,
};

#[cfg(feature = "wasm")]
extern crate tsify;

/// A SchemaFragment describe the types for a given instance of Cedar.
/// SchemaFragments are composed of Entity Types and Action Types. The
/// schema fragment is split into multiple namespace definitions, eac including
/// a namespace name which is applied to all entity types (and the implicit
/// `Action` entity type for all actions) in the schema.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(transparent)]
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
#[cfg_attr(feature = "wasm", tsify(into_wasm_abi, from_wasm_abi))]
pub struct SchemaFragment(
#[serde(with = "::serde_with::rust::maps_duplicate_key_is_error")]
pub HashMap<SmolStr, NamespaceDefinition>,
Expand Down Expand Up @@ -83,6 +88,8 @@ impl SchemaFragment {
#[serde_as]
#[serde(deny_unknown_fields)]
#[doc(hidden)]
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
#[cfg_attr(feature = "wasm", tsify(into_wasm_abi, from_wasm_abi))]
pub struct NamespaceDefinition {
#[serde(default)]
#[serde(with = "::serde_with::rust::maps_duplicate_key_is_error")]
Expand Down Expand Up @@ -113,6 +120,8 @@ impl NamespaceDefinition {
/// can/should be included on entities of each type.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
#[cfg_attr(feature = "wasm", tsify(into_wasm_abi, from_wasm_abi))]
pub struct EntityType {
#[serde(default)]
#[serde(rename = "memberOfTypes")]
Expand All @@ -123,6 +132,8 @@ pub struct EntityType {

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(transparent)]
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
#[cfg_attr(feature = "wasm", tsify(into_wasm_abi, from_wasm_abi))]
pub struct AttributesOrContext(
// We use the usual `SchemaType` deserialization, but it will ultimately
// need to be a `Record` or type def which resolves to a `Record`.
Expand All @@ -148,6 +159,8 @@ impl Default for AttributesOrContext {
/// kinds of entities it can be used on.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
#[cfg_attr(feature = "wasm", tsify(into_wasm_abi, from_wasm_abi))]
pub struct ActionType {
/// This maps attribute names to
/// `cedar_policy_core::entities::json::value::CedarValueJson` which is the
Expand All @@ -172,6 +185,8 @@ pub struct ActionType {
/// applies to.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
#[cfg_attr(feature = "wasm", tsify(into_wasm_abi, from_wasm_abi))]
pub struct ApplySpec {
#[serde(default)]
#[serde(rename = "resourceTypes")]
Expand All @@ -185,6 +200,8 @@ pub struct ApplySpec {

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
#[cfg_attr(feature = "wasm", tsify(into_wasm_abi, from_wasm_abi))]
pub struct ActionEntityUID {
pub id: SmolStr,

Expand Down Expand Up @@ -218,6 +235,8 @@ impl std::fmt::Display for ActionEntityUID {
// then, have catch-all variant for any unrecognized tag in the same enum that
// captures the name of the unrecognized tag.
#[serde(untagged)]
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
#[cfg_attr(feature = "wasm", tsify(into_wasm_abi, from_wasm_abi))]
pub enum SchemaType {
Type(SchemaTypeVariant),
TypeDef {
Expand Down Expand Up @@ -492,8 +511,10 @@ impl From<SchemaTypeVariant> for SchemaType {
}
}

#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize)]
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
#[serde(tag = "type")]
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
#[cfg_attr(feature = "wasm", tsify(into_wasm_abi, from_wasm_abi))]
pub enum SchemaTypeVariant {
String,
Long,
Expand Down
2 changes: 1 addition & 1 deletion cedar-policy/src/frontend/is_authorized.rs
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,7 @@ struct AuthorizationCall {
/// `__entity` and `__extn` escapes to be implicit, and it will error if
/// attributes have the wrong types (e.g., string instead of integer).
#[serde(rename = "schema")]
#[cfg_attr(feature = "wasm", tsify(type = "Record<string, any>"))]
#[cfg_attr(feature = "wasm", tsify(type = "Schema"))]
schema: Option<JsonValueWithNoDuplicateKeys>,
/// If this is `true` and a schema is provided, perform request validation.
/// If this is `false`, the schema will only be used for schema-based
Expand Down
9 changes: 9 additions & 0 deletions cedar-policy/src/frontend/validate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ use cedar_policy_core::{
use cedar_policy_validator::Validator;
use serde::{Deserialize, Serialize};

#[cfg(feature = "wasm")]
extern crate tsify;

fn validate(call: &ValidateCall) -> Result<ValidateAnswer, String> {
let mut policy_set = PolicySet::new();
let mut parse_errors: Vec<String> = vec![];
Expand Down Expand Up @@ -99,6 +102,8 @@ pub fn json_validate(input: &str) -> InterfaceResult {
}

#[derive(Serialize, Deserialize)]
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
#[cfg_attr(feature = "wasm", tsify(into_wasm_abi, from_wasm_abi))]
struct ValidateCall {
#[serde(default)]
#[serde(rename = "validationSettings")]
Expand All @@ -109,11 +114,15 @@ struct ValidateCall {
}

#[derive(Default, Serialize, Deserialize)]
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
#[cfg_attr(feature = "wasm", tsify(into_wasm_abi, from_wasm_abi))]
struct ValidationSettings {
mode: ValidationMode,
}

#[derive(Serialize, Deserialize)]
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
#[cfg_attr(feature = "wasm", tsify(into_wasm_abi, from_wasm_abi))]
enum ValidationMode {
#[serde(rename = "regular")]
Regular,
Expand Down
1 change: 1 addition & 0 deletions cedar-wasm/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
`policy_text_to_json`, and `policy_text_from_json`. (#616)
- Exposed cedar-wasm functionality for authorization and validation: `wasm_is_authorized`
and `wasm_validate`. (#657)
- Exposed types through `tsify` for `ValidateCall` and the schema. (#692)
1 change: 1 addition & 0 deletions cedar-wasm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ exclude = ['/build']
cedar-policy = { version = "=3.0.0", path = "../cedar-policy", features = ["wasm"] }
cedar-policy-core = { version = "=3.0.0", path = "../cedar-policy-core", features = ["wasm"] }
cedar-policy-formatter = { version = "=3.0.0", path = "../cedar-policy-formatter" }
cedar-policy-validator = {version = "=3.0.0", path = "../cedar-policy-validator", features = ["wasm"]}

serde = { version = "1.0", features = ["derive", "rc"] }
serde-wasm-bindgen = "0.6"
Expand Down
4 changes: 4 additions & 0 deletions cedar-wasm/build-wasm.sh
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
#!/bin/bash
# This script calls wasm-pack build and post-processes the generated TS types to fix them.
# Without this, the built wasm still works, but the Typescript definitions made by tsify don't.
set -e
cargo build
wasm-pack build --scope amzn --target web
Expand All @@ -17,6 +19,8 @@ sed -i "s/[{]\s*-: /{ \"-\": /g" pkg/cedar_wasm.d.ts
sed -i "s/[{]\s*[*]: /{ \"*\": /g" pkg/cedar_wasm.d.ts
sed -i "s/[{]\s*\.: /{ \".\": /g" pkg/cedar_wasm.d.ts
sed -i "s/ | __skip//g" pkg/cedar_wasm.d.ts
sed -i "s/SchemaFragment/Schema/g" pkg/cedar_wasm.d.ts

echo "type SmolStr = string;" >> pkg/cedar_wasm.d.ts
echo "export type TypeOfAttribute = SchemaType & { required?: boolean };" >> pkg/cedar_wasm.d.ts
echo "Finished post-processing types file"

0 comments on commit 95b126d

Please sign in to comment.