Skip to content

Commit

Permalink
Easy change & clean ups
Browse files Browse the repository at this point in the history
  • Loading branch information
alexsnaps committed Jul 10, 2024
1 parent 2fe589e commit 682475c
Show file tree
Hide file tree
Showing 7 changed files with 178 additions and 289 deletions.
184 changes: 159 additions & 25 deletions src/configuration.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
use crate::glob::GlobPattern;
use crate::policy_index::PolicyIndex;
use cel_interpreter::Expression;
use log::warn;
use cel_interpreter::objects::ValueType;
use cel_interpreter::{Context, Expression, Value};
use cel_parser::{Atom, RelationOp};
use serde::Deserialize;
use std::cell::OnceCell;
use std::fmt::{Display, Formatter};
use std::sync::Arc;

#[derive(Deserialize, Debug, Clone)]
pub struct SelectorItem {
Expand Down Expand Up @@ -131,28 +132,6 @@ pub enum WhenConditionOperator {
Matches,
}

impl WhenConditionOperator {
pub fn eval(&self, value: &str, attr_value: &str) -> bool {
match *self {
WhenConditionOperator::Equal => value.eq(attr_value),
WhenConditionOperator::NotEqual => !value.eq(attr_value),
WhenConditionOperator::StartsWith => attr_value.starts_with(value),
WhenConditionOperator::EndsWith => attr_value.ends_with(value),
WhenConditionOperator::Matches => match GlobPattern::try_from(value) {
// TODO(eastizle): regexp being compiled and validated at request time.
// Validations and possibly regexp compilation should happen at boot time instead.
// In addition, if the regexp is not valid, the only consequence is that
// the current condition would not apply
Ok(glob_pattern) => glob_pattern.is_match(attr_value),
Err(e) => {
warn!("failed to parse regexp: {value}, error: {e:?}");
false
}
},
}
}
}

#[derive(Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub struct PatternExpression {
Expand Down Expand Up @@ -185,6 +164,161 @@ impl PatternExpression {
.get()
.expect("PatternExpression wasn't previously compiled!")
}

pub fn eval(&self, attribute_value: String) -> bool {
let cel_type = type_of(&self.selector);
let value = match cel_type {
ValueType::String => Value::String(attribute_value.into()),
ValueType::Int | ValueType::UInt => Value::Int(attribute_value.parse::<i64>().unwrap()),
_ => unimplemented!("Need support for {}", cel_type),
};
let mut ctx = Context::default();
ctx.add_variable("attribute", value).unwrap();
Value::resolve(self.compiled.get().unwrap(), &ctx)
.map(|v| {
if let Value::Bool(result) = v {
result
} else {
false
}
})
.unwrap_or(false)
}
}

impl TryFrom<&PatternExpression> for Expression {
type Error = ();

fn try_from(expression: &PatternExpression) -> Result<Self, Self::Error> {
let cel_type = type_of(&expression.selector);

let value = match cel_type {
ValueType::Map => match expression.operator {
WhenConditionOperator::Equal | WhenConditionOperator::NotEqual => {
match cel_parser::parse(&expression.value) {
Ok(exp) => {
if let Expression::Map(data) = exp {
Ok(Expression::Map(data))
} else {
Err(())
}
}
Err(_) => Err(()),
}
}
_ => Err(()),
},
ValueType::Int | ValueType::UInt => match expression.operator {
WhenConditionOperator::Equal | WhenConditionOperator::NotEqual => {
match cel_parser::parse(&expression.value) {
Ok(exp) => {
if let Expression::Atom(atom) = &exp {
match atom {
Atom::Int(_) | Atom::UInt(_) | Atom::Float(_) => Ok(exp),
_ => Err(()),
}
} else {
Err(())
}
}
Err(_) => Err(()),
}
}
_ => Err(()),
},
ValueType::String => match expression.operator {
WhenConditionOperator::Equal | WhenConditionOperator::NotEqual => Ok(
Expression::Atom(Atom::String(Arc::new(expression.value.clone()))),
),
// WhenConditionOperator::Matches => {}
_ => Ok(Expression::Atom(Atom::String(Arc::new(
expression.value.clone(),
)))),
},
// ValueType::Bytes => {}
// ValueType::Bool => {}
// ValueType::Timestamp => {}
_ => todo!("Still needs support for values of type `{cel_type}`"),
}?;

match expression.operator {
WhenConditionOperator::Equal => Ok(Expression::Relation(
Expression::Ident(Arc::new("attribute".to_string())).into(),
RelationOp::Equals,
value.into(),
)),
WhenConditionOperator::NotEqual => Ok(Expression::Relation(
Expression::Ident(Arc::new("attribute".to_string())).into(),
RelationOp::NotEquals,
value.into(),
)),
WhenConditionOperator::StartsWith => Ok(Expression::FunctionCall(
Expression::Ident(Arc::new("startsWith".to_string())).into(),
Some(Expression::Ident("attribute".to_string().into()).into()),
[value].to_vec(),
)),
WhenConditionOperator::EndsWith => Ok(Expression::FunctionCall(
Expression::Ident(Arc::new("endsWith".to_string())).into(),
Some(Expression::Ident("attribute".to_string().into()).into()),
[value].to_vec(),
)),
WhenConditionOperator::Matches => Ok(Expression::FunctionCall(
Expression::Ident(Arc::new("matches".to_string())).into(),
Some(Expression::Ident("attribute".to_string().into()).into()),
[value].to_vec(),
)),
}
}
}

pub fn type_of(path: &str) -> ValueType {
match path {
"request.time" => ValueType::Timestamp,
"request.id" => ValueType::String,
"request.protocol" => ValueType::String,
"request.scheme" => ValueType::String,
"request.host" => ValueType::String,
"request.method" => ValueType::String,
"request.path" => ValueType::String,
"request.url_path" => ValueType::String,
"request.query" => ValueType::String,
"request.referer" => ValueType::String,
"request.useragent" => ValueType::String,
"request.body" => ValueType::String,
"source.address" => ValueType::String,
"source.service" => ValueType::String,
"source.principal" => ValueType::String,
"source.certificate" => ValueType::String,
"destination.address" => ValueType::String,
"destination.service" => ValueType::String,
"destination.principal" => ValueType::String,
"destination.certificate" => ValueType::String,
"connection.requested_server_name" => ValueType::String,
"connection.tls_session.sni" => ValueType::String,
"connection.tls_version" => ValueType::String,
"connection.subject_local_certificate" => ValueType::String,
"connection.subject_peer_certificate" => ValueType::String,
"connection.dns_san_local_certificate" => ValueType::String,
"connection.dns_san_peer_certificate" => ValueType::String,
"connection.uri_san_local_certificate" => ValueType::String,
"connection.uri_san_peer_certificate" => ValueType::String,
"connection.sha256_peer_certificate_digest" => ValueType::String,
"ratelimit.domain" => ValueType::String,
"request.size" => ValueType::Int,
"source.port" => ValueType::Int,
"destination.port" => ValueType::Int,
"connection.id" => ValueType::Int,
"ratelimit.hits_addend" => ValueType::Int,
"request.headers" => ValueType::Map,
"request.context_extensions" => ValueType::Map,
"source.labels" => ValueType::Map,
"destination.labels" => ValueType::Map,
"filter_state" => ValueType::Map,
"connection.mtls" => ValueType::Bool,
"request.raw_body" => ValueType::Bytes,
"auth.identity" => ValueType::Bytes,
_ => ValueType::Bytes,
}
}

#[derive(Deserialize, Debug, Clone)]
Expand Down
2 changes: 0 additions & 2 deletions src/envoy/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,6 @@ mod timestamp;
mod token_bucket;
mod value;

pub mod properties;

pub use {
ratelimit::{RateLimitDescriptor, RateLimitDescriptor_Entry},
rls::{RateLimitRequest, RateLimitResponse, RateLimitResponse_Code},
Expand Down
4 changes: 0 additions & 4 deletions src/filter.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
use crate::envoy::properties::EnvoyTypeMapper;

mod http_context;
mod root_context;

Expand All @@ -25,7 +23,6 @@ extern "C" fn start() {
use proxy_wasm::traits::RootContext;
use proxy_wasm::types::LogLevel;
use root_context::FilterRoot;
use std::rc::Rc;

proxy_wasm::set_log_level(LogLevel::Trace);
std::panic::set_hook(Box::new(|panic_info| {
Expand All @@ -36,7 +33,6 @@ extern "C" fn start() {
Box::new(FilterRoot {
context_id,
config: Default::default(),
property_mapper: Rc::new(EnvoyTypeMapper::new()),
})
});
}
44 changes: 19 additions & 25 deletions src/filter/http_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ use crate::configuration::{
Condition, DataItem, DataType, FailureMode, FilterConfig, PatternExpression, RateLimitPolicy,
Rule,
};
use crate::envoy::properties::EnvoyTypeMapper;
use crate::envoy::{
RateLimitDescriptor, RateLimitDescriptor_Entry, RateLimitRequest, RateLimitResponse,
RateLimitResponse_Code,
Expand Down Expand Up @@ -43,7 +42,6 @@ pub struct Filter {
pub context_id: u32,
pub config: Rc<FilterConfig>,
pub response_headers_to_add: Vec<(String, String)>,
pub property_mapper: Rc<EnvoyTypeMapper>,
pub tracing_headers: Vec<(TracingHeader, Bytes)>,
}

Expand Down Expand Up @@ -136,32 +134,28 @@ impl Filter {

fn pattern_expression_applies(&self, p_e: &PatternExpression) -> bool {
let attribute_path = p_e.path();
// convert a Vec<String> to Vec<&str>
// attribute_path.iter().map(AsRef::as_ref).collect()
let attribute_value =
match self.get_property(attribute_path.iter().map(AsRef::as_ref).collect()) {
None => {
let attribute_value = match self.get_property(attribute_path) {
None => {
debug!(
"[context_id: {}]: selector not found: {}, defaulting to ``",
self.context_id, p_e.selector
);
"".to_string()
}
// TODO(eastizle): not all fields are strings
// https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/advanced/attributes
Some(attribute_bytes) => match String::from_utf8(attribute_bytes) {
Err(e) => {
debug!(
"[context_id: {}]: selector not found: {}, defaulting to ``",
self.context_id, p_e.selector
"[context_id: {}]: failed to parse selector value: {}, error: {}",
self.context_id, p_e.selector, e
);
"".to_string()
return false;
}
// TODO(eastizle): not all fields are strings
// https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/advanced/attributes
Some(attribute_bytes) => match String::from_utf8(attribute_bytes) {
Err(e) => {
debug!(
"[context_id: {}]: failed to parse selector value: {}, error: {}",
self.context_id, p_e.selector, e
);
return false;
}
Ok(attribute_value) => attribute_value,
},
};
p_e.operator
.eval(p_e.value.as_str(), attribute_value.as_str())
Ok(attribute_value) => attribute_value,
},
};
p_e.eval(attribute_value)
}

fn build_single_descriptor(&self, data_list: &[DataItem]) -> Option<RateLimitDescriptor> {
Expand Down
3 changes: 0 additions & 3 deletions src/filter/root_context.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use crate::configuration::{FilterConfig, PluginConfiguration};
use crate::envoy::properties::EnvoyTypeMapper;
use crate::filter::http_context::Filter;
use const_format::formatcp;
use log::{info, warn};
Expand All @@ -16,7 +15,6 @@ const WASM_SHIM_HEADER: &str = "Kuadrant wasm module";
pub struct FilterRoot {
pub context_id: u32,
pub config: Rc<FilterConfig>,
pub property_mapper: Rc<EnvoyTypeMapper>,
}

impl RootContext for FilterRoot {
Expand All @@ -42,7 +40,6 @@ impl RootContext for FilterRoot {
context_id,
config: Rc::clone(&self.config),
response_headers_to_add: Vec::default(),
property_mapper: Rc::clone(&self.property_mapper),
tracing_headers: Vec::default(),
}))
}
Expand Down
1 change: 0 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ mod envoy;
mod filter;
mod glob;
mod policy_index;
mod typing;

#[cfg(test)]
mod tests {
Expand Down
Loading

0 comments on commit 682475c

Please sign in to comment.