Skip to content

Commit

Permalink
Make non-json isAuthorized and validate public, rework wasm interfaces (
Browse files Browse the repository at this point in the history
#737)

Signed-off-by: Nick Szegheo <nicholas.szegheo@gmail.com>
  • Loading branch information
ncsze authored Apr 2, 2024
1 parent f4147b8 commit ea00969
Show file tree
Hide file tree
Showing 11 changed files with 125 additions and 58 deletions.
2 changes: 2 additions & 0 deletions cedar-policy-core/src/est/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ pub enum Expr {

/// Represent an element of a pattern literal
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
#[cfg_attr(feature = "wasm", tsify(into_wasm_abi, from_wasm_abi))]
pub enum PatternElem {
/// The wildcard asterisk
Wildcard,
Expand Down
3 changes: 2 additions & 1 deletion cedar-policy-validator/src/schema_file_format.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ extern crate tsify;
#[cfg_attr(feature = "wasm", tsify(into_wasm_abi, from_wasm_abi))]
pub struct SchemaFragment(
#[serde(deserialize_with = "deserialize_schema_fragment")]
pub HashMap<Option<Name>, NamespaceDefinition>,
#[cfg_attr(feature = "wasm", tsify(type = "Record<string, NamespaceDefinition>"))]
pub HashMap<Option<Name>, NamespaceDefinition>,
);

fn deserialize_hash_map<'de, D, K, V>(
Expand Down
1 change: 1 addition & 0 deletions cedar-policy/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Deprecated error `TypeErrorKind::ImpossiblePolicy` in favor of warning
`ValidationWarningKind::ImpossiblePolicy` so future improvements to Cedar
typing precision will not result in breaking changes. (resolving #539)
- Made `is_authorized` and `validate` functions in the frontend public, as well as their related structs: `AuthorizationAnswer`, `AuthorizationCall`, `ValidationCall`, `ValidationSettings`, `ValidationEnabled`, `ValidationError`, `ValidationWarning`, `ValidationAnswer`. (#737)

## [3.1.2] - 2024-03-29

Expand Down
26 changes: 15 additions & 11 deletions cedar-policy/src/frontend/is_authorized.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ thread_local!(
);

/// Construct and ask the authorizer the request.
fn is_authorized(call: AuthorizationCall) -> AuthorizationAnswer {
pub fn is_authorized(call: AuthorizationCall) -> AuthorizationAnswer {
match call.get_components() {
Ok((request, policies, entities)) => {
AUTHORIZER.with(|authorizer| AuthorizationAnswer::Success {
Expand Down Expand Up @@ -265,11 +265,17 @@ impl TryFrom<PartialResponse> for InterfaceResidualResponse {
#[serde(untagged)]
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
#[cfg_attr(feature = "wasm", tsify(into_wasm_abi, from_wasm_abi))]
enum AuthorizationAnswer {
/// Represents a failure to parse or call the authorizer
Failure { errors: Vec<String> },
pub enum AuthorizationAnswer {
/// Represents a failure to parse and call the authorizer
Failure {
/// Parsing errors
errors: Vec<String>,
},
/// Represents a successful authorization call
Success { response: InterfaceResponse },
Success {
/// Details of the authorization decision
response: InterfaceResponse,
},
}

#[cfg(feature = "partial-eval")]
Expand All @@ -286,7 +292,7 @@ enum PartialAuthorizationAnswer {
#[derive(Debug, Serialize, Deserialize)]
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
#[cfg_attr(feature = "wasm", tsify(into_wasm_abi, from_wasm_abi))]
struct AuthorizationCall {
pub struct AuthorizationCall {
/// The principal taking action
#[cfg_attr(feature = "wasm", tsify(type = "string|{type: string, id: string}"))]
principal: Option<JsonValueWithNoDuplicateKeys>,
Expand All @@ -298,17 +304,15 @@ struct AuthorizationCall {
resource: Option<JsonValueWithNoDuplicateKeys>,
/// The context details specific to the request
#[serde_as(as = "MapPreventDuplicates<_, _>")]
#[cfg_attr(
feature = "wasm",
tsify(optional, type = "Record<string, CedarValueJson>")
)]
#[cfg_attr(feature = "wasm", tsify(type = "Record<string, CedarValueJson>"))]
/// The context details specific to the request
context: HashMap<String, JsonValueWithNoDuplicateKeys>,
/// Optional schema in JSON format.
/// If present, this will inform the parsing: for instance, it will allow
/// `__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 = "Schema"))]
#[cfg_attr(feature = "wasm", tsify(optional, 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
39 changes: 28 additions & 11 deletions cedar-policy/src/frontend/validate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ use serde::{Deserialize, Serialize};
extern crate tsify;

/// Parse a policy set and optionally validate it against a provided schema
fn validate(call: ValidationCall) -> ValidationAnswer {
pub fn validate(call: ValidationCall) -> ValidationAnswer {
match call.get_components() {
Ok((policies, schema)) => {
let validator = Validator::new(schema);
Expand Down Expand Up @@ -66,14 +66,15 @@ pub fn json_validate(input: &str) -> InterfaceResult {
}

/// Struct containing the input data for validation
#[derive(Serialize, Deserialize)]
#[derive(Serialize, Deserialize, Debug)]
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
#[cfg_attr(feature = "wasm", tsify(into_wasm_abi, from_wasm_abi))]
struct ValidationCall {
pub struct ValidationCall {
#[serde(default)]
#[serde(rename = "validationSettings")]
validation_settings: ValidationSettings,
/// Schema in JSON format
#[cfg_attr(feature = "wasm", tsify(type = "Schema"))]
schema: JsonValueWithNoDuplicateKeys,
#[serde(rename = "policySet")]
policy_set: PolicySpecification,
Expand All @@ -92,20 +93,25 @@ impl ValidationCall {
}

/// Configuration for the validation call
#[derive(Default, Serialize, Deserialize)]
#[derive(Default, Serialize, Deserialize, Debug)]
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
#[cfg_attr(feature = "wasm", tsify(into_wasm_abi, from_wasm_abi))]
struct ValidationSettings {
/// Configuration for the validation call
pub struct ValidationSettings {
/// Whether validation is enabled
enabled: ValidationEnabled,
}

#[derive(Serialize, Deserialize)]
/// String enum for validation mode
#[derive(Serialize, Deserialize, Debug)]
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
#[cfg_attr(feature = "wasm", tsify(into_wasm_abi, from_wasm_abi))]
enum ValidationEnabled {
pub enum ValidationEnabled {
/// Setting for which policies will be validated against the schema
#[serde(rename = "on")]
#[serde(alias = "regular")]
On,
/// Setting for which no validation will be done
#[serde(rename = "off")]
Off,
}
Expand All @@ -116,15 +122,21 @@ impl Default for ValidationEnabled {
}
}

/// Error for a specified policy after validation
#[derive(Debug, Serialize, Deserialize)]
struct ValidationError {
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
#[cfg_attr(feature = "wasm", tsify(into_wasm_abi, from_wasm_abi))]
pub struct ValidationError {
#[serde(rename = "policyId")]
policy_id: String,
error: String,
}

/// Warning for a specified policy after validation
#[derive(Debug, Serialize, Deserialize)]
struct ValidationWarning {
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
#[cfg_attr(feature = "wasm", tsify(into_wasm_abi, from_wasm_abi))]
pub struct ValidationWarning {
#[serde(rename = "policyId")]
policy_id: String,
warning: String,
Expand All @@ -133,12 +145,17 @@ struct ValidationWarning {
/// Result struct for validation
#[derive(Debug, Serialize, Deserialize)]
#[serde(untagged)]
enum ValidationAnswer {
pub enum ValidationAnswer {
/// Represents a failure to parse or call the validator
Failure { errors: Vec<String> },
Failure {
/// Parsing errors
errors: Vec<String>,
},
/// Represents a successful validation call
Success {
/// Errors from any issues found during validation
validation_errors: Vec<ValidationError>,
/// Warnings from any issues found during validation
validation_warnings: Vec<ValidationWarning>,
},
}
Expand Down
2 changes: 2 additions & 0 deletions cedar-wasm/build-wasm.sh
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ 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 "type Name = string;" >> pkg/cedar_wasm.d.ts
echo "type Id = string;" >> pkg/cedar_wasm.d.ts
echo "export type TypeOfAttribute = SchemaType & { required?: boolean };" >> pkg/cedar_wasm.d.ts
echo "export type Context = Record<string, CedarValueJson>;" >> pkg/cedar_wasm.d.ts
echo "Finished post-processing types file"
24 changes: 19 additions & 5 deletions cedar-wasm/src/authorizer.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,23 @@
//! This module contains the entry point to the wasm isAuthorized functionality.
use cedar_policy::frontend::{is_authorized::json_is_authorized, utils::InterfaceResult};

use cedar_policy::frontend::is_authorized::{
is_authorized, AuthorizationAnswer, AuthorizationCall, InterfaceResponse,
};
use serde::{Deserialize, Serialize};
use tsify::Tsify;
use wasm_bindgen::prelude::*;

#[wasm_bindgen(js_name = isAuthorized)]
pub fn wasm_is_authorized(input: &str) -> InterfaceResult {
json_is_authorized(input)
#[derive(Tsify, Debug, Serialize, Deserialize)]
#[serde(untagged)]
#[tsify(into_wasm_abi, from_wasm_abi)]
pub enum AuthorizationResult {
Success { response: InterfaceResponse },
Error { errors: Vec<String> },
}

#[wasm_bindgen(js_name = "isAuthorized")]
pub fn wasm_is_authorized(call: AuthorizationCall) -> AuthorizationResult {
match is_authorized(call) {
AuthorizationAnswer::Success { response } => AuthorizationResult::Success { response },
AuthorizationAnswer::Failure { errors } => AuthorizationResult::Error { errors },
}
}
20 changes: 11 additions & 9 deletions cedar-wasm/src/formatter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,16 @@ use tsify::Tsify;
use wasm_bindgen::prelude::*;

#[derive(Tsify, Debug, Serialize, Deserialize)]
#[serde(tag = "success")]
#[serde(rename_all = "camelCase")]
#[serde(untagged)]
#[tsify(into_wasm_abi, from_wasm_abi)]
pub enum FormattingResult {
#[serde(rename = "true")]
Success { formatted_policy: String },
#[serde(rename = "false")]
Error { error: String },
#[serde(rename_all = "camelCase")]
Success {
formatted_policy: String,
},
Error {
errors: Vec<String>,
},
}

#[wasm_bindgen(js_name = "formatPolicies")]
Expand All @@ -25,15 +27,15 @@ pub fn wasm_format_policies(
Ok(width) => width,
Err(_) => {
return FormattingResult::Error {
error: "Input size error (line width)".to_string(),
errors: vec!["Input size error (line width)".to_string()],
}
}
};
let indent_width: isize = match indent_width.try_into() {
Ok(width) => width,
Err(_) => {
return FormattingResult::Error {
error: "Input size error (indent width)".to_string(),
errors: vec!["Input size error (indent width)".to_string()],
}
}
};
Expand All @@ -46,7 +48,7 @@ pub fn wasm_format_policies(
formatted_policy: prettified_policy,
},
Err(err) => FormattingResult::Error {
error: err.to_string(),
errors: vec![err.to_string()],
},
}
}
Expand Down
29 changes: 14 additions & 15 deletions cedar-wasm/src/policies_and_templates.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,17 @@ use tsify::Tsify;
use wasm_bindgen::prelude::*;

#[derive(Tsify, Debug, Serialize, Deserialize)]
#[serde(tag = "success")]
#[serde(untagged)]
#[serde(rename_all = "camelCase")]
#[tsify(into_wasm_abi, from_wasm_abi)]
pub enum JsonToPolicyResult {
#[serde(rename = "true")]
Success { policy_text: String },
#[serde(rename = "false")]
Error { errors: Vec<String> },
#[serde(rename_all = "camelCase")]
Success {
policy_text: String,
},
Error {
errors: Vec<String>,
},
}

#[wasm_bindgen(js_name = "policyTextFromJson")]
Expand All @@ -40,15 +43,15 @@ pub fn policy_text_from_json(json_str: &str) -> JsonToPolicyResult {
}

#[derive(Tsify, Debug, Serialize, Deserialize, Clone)]
#[serde(tag = "success")]
#[serde(untagged)]
#[tsify(into_wasm_abi, from_wasm_abi)]
pub enum PolicyToJsonResult {
#[serde(rename = "true")]
Success {
policy: cedar_policy_core::est::Policy,
},
#[serde(rename = "false")]
Error { errors: Vec<String> },
Error {
errors: Vec<String>,
},
}

#[wasm_bindgen(js_name = "policyTextToJson")]
Expand All @@ -62,14 +65,12 @@ pub fn policy_text_to_json(cedar_str: &str) -> PolicyToJsonResult {
}

#[derive(Tsify, Debug, Serialize, Deserialize)]
#[serde(tag = "success")]
#[serde(untagged)]
#[tsify(into_wasm_abi, from_wasm_abi)]
/// struct that defines the result for the syntax validation function
pub enum CheckParsePolicySetResult {
#[serde(rename = "true")]
/// represents successful syntax validation
Success { policies: i32, templates: i32 },
#[serde(rename = "false")]
/// represents a syntax error and encloses a vector of the errors
SyntaxError { errors: Vec<String> },
}
Expand Down Expand Up @@ -99,13 +100,11 @@ pub fn check_parse_policy_set(input_policies_str: &str) -> CheckParsePolicySetRe
}

#[derive(Tsify, Debug, Serialize, Deserialize)]
#[serde(tag = "success")]
#[serde(untagged)]
#[tsify(into_wasm_abi, from_wasm_abi)]
pub enum CheckParseTemplateResult {
#[serde(rename = "true")]
/// represents successful template validation
Success { slots: Vec<String> },
#[serde(rename = "false")]
/// represents errors in the template validation and encloses a vector of the errors
Error { errors: Vec<String> },
}
Expand Down
4 changes: 1 addition & 3 deletions cedar-wasm/src/schema_and_entities_and_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,12 @@ use tsify::Tsify;
use wasm_bindgen::prelude::*;

#[derive(Tsify, Debug, Serialize, Deserialize)]
#[serde(tag = "success")]
#[serde(untagged)]
#[tsify(into_wasm_abi, from_wasm_abi)]
/// struct that defines the result for the syntax validation function
pub enum CheckParseResult {
#[serde(rename = "true")]
/// represents successful syntax validation
Success,
#[serde(rename = "false")]
/// represents a syntax error and encloses a vector of the errors
SyntaxError { errors: Vec<String> },
}
Expand Down
Loading

0 comments on commit ea00969

Please sign in to comment.