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
Signed-off-by: Nick Szegheo <nicholas.szegheo@gmail.com>
  • Loading branch information
ncsze committed Mar 19, 2024
1 parent d6d5e98 commit 5a75480
Show file tree
Hide file tree
Showing 9 changed files with 121 additions and 56 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
1 change: 1 addition & 0 deletions cedar-policy/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Validation error messages render types in the new, more readable, schema
syntax. (#708, resolving #242)
- Removed unnecessary lifetimes from some validation related structs (#715)
- Made `isAuthorized` and `validate` functions in the frontend public, as well as their related structs: `AuthorizationAnswer`, `AuthorizationCall`, `ValidateCall`, `ValidationSettings`, `ValidationMode`, `ValidateAnswer`, `ValidationNote`. (#737)

## [3.1.1] - 2024-03-14

Expand Down
32 changes: 22 additions & 10 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 @@ -262,13 +262,22 @@ impl TryFrom<PartialResponse> for InterfaceResidualResponse {
}
}

/// Answer struct from authorization call
#[derive(Debug, Serialize, Deserialize)]
#[serde(untagged)]
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
#[cfg_attr(feature = "wasm", tsify(into_wasm_abi, from_wasm_abi))]
enum AuthorizationAnswer {
ParseFailed { errors: Vec<String> },
Success { response: InterfaceResponse },
pub enum AuthorizationAnswer {
/// Represents a failure to parse and call the authorizer
ParseFailed {
/// Parsing errors
errors: Vec<String>,
},
/// Represents a successful authorization call
Success {
/// Details of the authorization decision
response: InterfaceResponse,
},
}

#[cfg(feature = "partial-eval")]
Expand All @@ -280,36 +289,39 @@ enum PartialAuthorizationAnswer {
Residuals { response: InterfaceResidualResponse },
}

/// Struct containing the input data for authorization
#[serde_as]
#[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>,
/// The action the principal is taking
#[cfg_attr(feature = "wasm", tsify(type = "string|{type: string, id: string}"))]
action: JsonValueWithNoDuplicateKeys,
/// The resource being acted on by the principal
#[cfg_attr(feature = "wasm", tsify(type = "string|{type: string, id: string}"))]
resource: Option<JsonValueWithNoDuplicateKeys>,
#[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
/// parsing of `context`, and not for request validation.
/// If a schema is not provided, this option has no effect.
#[serde(default = "constant_true")]
enable_request_validation: bool,
/// The slice containing entities and policies
slice: RecvdSlice,
}

Expand Down
41 changes: 30 additions & 11 deletions cedar-policy/src/frontend/validate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ use serde::{Deserialize, Serialize};
#[cfg(feature = "wasm")]
extern crate tsify;

fn validate(call: &ValidateCall) -> Result<ValidateAnswer, String> {
/// Parse a policy set and optionally validate it against a provided schema
pub fn validate(call: &ValidateCall) -> Result<ValidateAnswer, String> {
let mut policy_set = PolicySet::new();
let mut parse_errors: Vec<String> = vec![];

Expand Down Expand Up @@ -101,10 +102,11 @@ pub fn json_validate(input: &str) -> InterfaceResult {
)
}

#[derive(Serialize, Deserialize)]
/// Struct containing the input data for validation
#[derive(Serialize, Deserialize, Debug)]
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
#[cfg_attr(feature = "wasm", tsify(into_wasm_abi, from_wasm_abi))]
struct ValidateCall {
pub struct ValidateCall {
#[serde(default)]
#[serde(rename = "validationSettings")]
validation_settings: ValidationSettings,
Expand All @@ -113,19 +115,24 @@ struct ValidateCall {
policy_set: PolicySpecification,
}

#[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 {
/// The selected validation mode
mode: ValidationMode,
}

#[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 ValidationMode {
pub enum ValidationMode {
/// Mode for which policies will be validated against the schema
#[serde(rename = "regular")]
Regular,
/// Mode for which no validation will be done
#[serde(rename = "off")]
Off,
}
Expand All @@ -136,18 +143,30 @@ impl Default for ValidationMode {
}
}

/// Note of issue with a specified policy after validation
#[derive(Debug, Serialize, Deserialize)]
struct ValidationNote {
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
#[cfg_attr(feature = "wasm", tsify(into_wasm_abi, from_wasm_abi))]
pub struct ValidationNote {
#[serde(rename = "policyId")]
policy_id: String,
note: String,
}

/// Result struct for validation
#[derive(Debug, Serialize, Deserialize)]
#[serde(untagged)]
enum ValidateAnswer {
ParseFailed { errors: Vec<String> },
Success { notes: Vec<ValidationNote> },
pub enum ValidateAnswer {
/// Represents unsuccessful parsing
ParseFailed {
/// Parsing errors
errors: Vec<String>,
},
/// Represents successful parsing
Success {
/// Notes from any issues found during validation
notes: Vec<ValidationNote>,
},
}

// PANIC SAFETY unit tests
Expand Down
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::ParseFailed { 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
24 changes: 21 additions & 3 deletions cedar-wasm/src/validator.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,25 @@
use cedar_policy::frontend::{utils::InterfaceResult, validate::json_validate};
use cedar_policy::frontend::validate::{validate, ValidateAnswer, ValidateCall, ValidationNote};
use serde::{Deserialize, Serialize};
use tsify::Tsify;
use wasm_bindgen::prelude::*;

#[derive(Tsify, Debug, Serialize, Deserialize)]
#[serde(untagged)]
#[tsify(into_wasm_abi, from_wasm_abi)]
pub enum ValidateResult {
Success { notes: Vec<ValidationNote> },
Error { errors: Vec<String> },
}

#[wasm_bindgen(js_name = "validate")]
pub fn wasm_validate(input: &str) -> InterfaceResult {
json_validate(input)
pub fn wasm_validate(call: ValidateCall) -> ValidateResult {
match validate(&call) {
Ok(answer) => match answer {
ValidateAnswer::Success { notes } => ValidateResult::Success { notes },
ValidateAnswer::ParseFailed { errors } => ValidateResult::Error { errors },
},
Err(err) => ValidateResult::Error {
errors: vec![err.to_string()],
},
}
}

0 comments on commit 5a75480

Please sign in to comment.