Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make non-json isAuthorized and validate public, rework wasm interfaces #737

Merged
merged 1 commit into from
Apr 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading