Skip to content
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
63 changes: 43 additions & 20 deletions wren-core/core/src/logical_plan/analyze/access_control.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ pub fn validate_rlac_rule(rule: &RowLevelAccessControl, model: &Model) -> Result
let RowLevelAccessControl {
condition,
required_properties,
..
name,
} = rule;
let (_, session_properties) = collect_condition(model, condition)?;

Expand All @@ -98,12 +98,13 @@ pub fn validate_rlac_rule(rule: &RowLevelAccessControl, model: &Model) -> Result
.collect();
if !missed_properties.is_empty() {
return plan_err!(
"The session property {} is used, but not found in the session properties",
"The session property {} is used for `{}` rule, but not found in the session properties",
missed_properties
.iter()
.map(|property| format!("@{property}"))
.collect::<Vec<_>>()
.join(", ")
.join(", "),
name
);
}
Ok(())
Expand Down Expand Up @@ -143,24 +144,27 @@ pub fn build_filter_expression(
.next()
}) else {
error = Some(plan_err!(
"The session property {} is not found in the session properties",
property_name
"The session property {} is required for `{}` rule but not found in the session properties",
property_name,
rule.name
));
return ControlFlow::Break(());
};

let Some(property_value) = property_value else {
error = Some(plan_err!(
"The session property {} should not be null",
property_name
"The session property {} is required for `{}` rule and should not be null",
property_name,
rule.name
));
return ControlFlow::Break(());
};

if property_value.trim().is_empty() {
error = Some(plan_err!(
"The session property {} should not be empty",
property_name
"The session property {} is required for `{}` rule and should not be empty",
property_name,
rule.name
));
return ControlFlow::Break(());
}
Expand All @@ -171,8 +175,9 @@ pub fn build_filter_expression(
}
Err(e) => {
error = Some(plan_err!(
"The session property {} is not valid: {}",
"The session property {} is required for `{}` rule but not valid: {}",
property_name,
rule.name,
e
));
return ControlFlow::Break(());
Expand Down Expand Up @@ -237,6 +242,7 @@ fn prevent_invalid_expr(expr: &ast::Expr) -> Result<()> {
/// If the optional property is not found in the headers but has a default value, return true.
/// If the optional property is not found in the headers and has no default value, return false.
pub fn validate_rule(
name: &str,
required_properties: &[SessionProperty],
headers: &HashMap<String, Option<String>>,
) -> Result<bool> {
Expand All @@ -246,8 +252,9 @@ pub fn validate_rule(
if property.required {
if !is_property_present(headers, &property.name) {
return plan_err!(
"session property {} is required, but not found in headers",
property.name
"session property {} is required for `{}` rule but not found in headers",
property.name,
name
);
}
Ok(true)
Expand Down Expand Up @@ -278,7 +285,7 @@ pub(crate) fn validate_clac_rule(
return Ok(true);
};

if !validate_rule(&clac.required_properties, properties)? {
if !validate_rule(&clac.name, &clac.required_properties, properties)? {
return Ok(true);
}

Expand Down Expand Up @@ -385,42 +392,47 @@ mod test {
pub fn test_validate_rule() -> Result<()> {
// required property
assert!(validate_rule(
"test",
&[SessionProperty::new_required("session_id")],
&build_headers(&[("session_id".to_string(), Some("1".to_string()))])
)?);

match validate_rule(
"test",
&[SessionProperty::new_required("session_id")],
&build_headers(&[("session_id".to_string(), None)]),
) {
Err(error) => {
assert_snapshot!(error.message(), @"session property session_id is required, but not found in headers");
assert_snapshot!(error.message(), @"session property session_id is required for `test` rule but not found in headers");
}
_ => panic!("should be error"),
}

match validate_rule(
"test",
&[SessionProperty::new_required("session_id")],
&build_headers(&[("session_id".to_string(), Some("".to_string()))]),
) {
Err(error) => {
assert_snapshot!(error.message(), @"session property session_id is required, but not found in headers");
assert_snapshot!(error.message(), @"session property session_id is required for `test` rule but not found in headers");
}
_ => panic!("should be error"),
}

match validate_rule(
"test",
&[SessionProperty::new_required("session_id")],
&build_headers(&[]),
) {
Err(error) => {
assert_snapshot!(error.message(), @"session property session_id is required, but not found in headers");
assert_snapshot!(error.message(), @"session property session_id is required for `test` rule but not found in headers");
}
_ => panic!("should be error"),
}

// optional property with default
assert!(validate_rule(
"test",
&[SessionProperty::new_optional(
"session_id",
Some("1".to_string())
Expand All @@ -429,6 +441,7 @@ mod test {
)?);

assert!(validate_rule(
"test",
&[SessionProperty::new_optional(
"session_id",
Some("1".to_string())
Expand All @@ -437,6 +450,7 @@ mod test {
)?);

assert!(validate_rule(
"test",
&[SessionProperty::new_optional(
"session_id",
Some("1".to_string())
Expand All @@ -445,6 +459,7 @@ mod test {
)?);

assert!(validate_rule(
"test",
&[SessionProperty::new_optional(
"session_id",
Some("1".to_string())
Expand All @@ -454,29 +469,34 @@ mod test {

// optional property without default
assert!(validate_rule(
"test",
&[SessionProperty::new_optional("session_id", None)],
&build_headers(&[("session_id".to_string(), Some("2".to_string()))])
)?);

// expected false
assert!(!validate_rule(
"test",
&[SessionProperty::new_optional("session_id", None)],
&build_headers(&[("session_id".to_string(), None)])
)?);

// expected false
assert!(!validate_rule(
"test",
&[SessionProperty::new_optional("session_id", None)],
&build_headers(&[("session_id".to_string(), Some("".to_string()))])
)?);

// expected false
assert!(!validate_rule(
"test",
&[SessionProperty::new_optional("session_id", None)],
&build_headers(&[])
)?);

assert!(validate_rule(
"test",
&[
SessionProperty::new_required("session_id"),
SessionProperty::new_optional("session_id_1", None),
Expand All @@ -491,6 +511,7 @@ mod test {

// expected false
assert!(!validate_rule(
"test",
&[
SessionProperty::new_required("session_id"),
SessionProperty::new_optional("session_id_1", None),
Expand All @@ -504,6 +525,7 @@ mod test {
)?);

assert!(validate_rule(
"test",
&[
SessionProperty::new_required("session_id"),
SessionProperty::new_optional("session_id_1", None),
Expand All @@ -517,6 +539,7 @@ mod test {
)?);

match validate_rule(
"test",
&[
SessionProperty::new_required("session_id"),
SessionProperty::new_optional("session_id_1", None),
Expand All @@ -529,7 +552,7 @@ mod test {
]),
) {
Err(error) => {
assert_snapshot!(error.message(), @"session property session_id is required, but not found in headers");
assert_snapshot!(error.message(), @"session property session_id is required for `test` rule but not found in headers");
}
_ => panic!("should be error"),
}
Expand Down Expand Up @@ -584,7 +607,7 @@ mod test {

match build_filter_expression(&state, Arc::clone(&model), &headers, &rule) {
Err(error) => {
assert_snapshot!(error.to_string(), @"Error during planning: The session property not_found is not found in the session properties");
assert_snapshot!(error.to_string(), @"Error during planning: The session property not_found is required for `test` rule but not found in the session properties");
}
_ => panic!("should be error"),
}
Expand All @@ -604,7 +627,7 @@ mod test {
)]));
match build_filter_expression(&state, Arc::clone(&model), &headers, &rule) {
Err(error) => {
assert_snapshot!(error.to_string(), @"Error during planning: The session property session_name is not found in the session properties");
assert_snapshot!(error.to_string(), @"Error during planning: The session property session_name is required for `test` rule but not found in the session properties");
}
_ => panic!("should be error"),
}
Expand Down Expand Up @@ -763,7 +786,7 @@ mod test {

match validate_rlac_rule(&rule, &model) {
Err(error) => {
assert_snapshot!(error.message(), @"The session property @session_name is used, but not found in the session properties");
assert_snapshot!(error.message(), @"The session property @session_name is used for `test` rule, but not found in the session properties");
}
_ => panic!("should be error"),
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,7 @@ impl ModelGenerationRule {
model: Arc<Model>,
rule: &RowLevelAccessControl,
) -> Result<Option<Expr>> {
if validate_rule(&rule.required_properties, &self.properties)? {
if validate_rule(&rule.name, &rule.required_properties, &self.properties)? {
let filter = build_filter_expression(
&self.session_state,
model,
Expand Down
3 changes: 2 additions & 1 deletion wren-core/core/src/logical_plan/analyze/plan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -406,7 +406,8 @@ impl ModelPlanNodeBuilder {
.row_level_access_controls()
.iter()
.try_for_each(|rule| {
if validate_rule(&rule.required_properties, &self.properties)? {
if validate_rule(&rule.name, &rule.required_properties, &self.properties)?
{
required_fields.extend(collect_condition(model, &rule.condition)?.0);
}
Ok::<_, DataFusionError>(())
Expand Down
8 changes: 4 additions & 4 deletions wren-core/core/src/mdl/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1935,7 +1935,7 @@ mod test {
@r"
ModelAnalyzeRule
caused by
Error during planning: session property session_nation is required, but not found in headers
Error during planning: session property session_nation is required for `nation` rule but not found in headers
"
)
}
Expand Down Expand Up @@ -2024,7 +2024,7 @@ mod test {
@r"
ModelAnalyzeRule
caused by
Error during planning: session property session_user is required, but not found in headers
Error during planning: session property session_user is required for `name` rule but not found in headers
"
)
}
Expand Down Expand Up @@ -2089,7 +2089,7 @@ mod test {
@r"
ModelAnalyzeRule
caused by
Error during planning: session property session_nation is required, but not found in headers
Error during planning: session property session_nation is required for `nation` rule but not found in headers
"
)
}
Expand Down Expand Up @@ -2582,7 +2582,7 @@ mod test {
Err(e) => {
assert_snapshot!(
e.to_string(),
@"Error during planning: session property session_level is required, but not found in headers"
@"Error during planning: session property session_level is required for `cls rule` rule but not found in headers"
)
}
_ => panic!("Expected error"),
Expand Down
Loading