Skip to content

Commit

Permalink
fixup! wasm configuration v2
Browse files Browse the repository at this point in the history
  • Loading branch information
eguzki committed Jul 11, 2023
1 parent fe49a2c commit 3583833
Show file tree
Hide file tree
Showing 2 changed files with 99 additions and 111 deletions.
137 changes: 62 additions & 75 deletions src/configuration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,9 @@ pub struct StaticItem {

#[derive(Deserialize, Debug, Clone)]
pub struct DataItem {
#[serde(rename = "static")]
#[serde(default, rename = "static")]
pub static_item: Option<StaticItem>,
#[serde(default)]
pub selector: Option<SelectorItem>,
}

Expand All @@ -42,6 +43,16 @@ enum WhenConditionOperator {
MatchesOperator,
}

impl WhenConditionOperator {
pub fn eval(&self, value: String, attr_value: String) -> bool {
match *self {
WhenConditionOperator::EqualOperator => value.eq(attr_value.as_str()),
// TODO(eastizle): implement other operators
_ => true,
}
}
}

#[derive(Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub struct PatternExpression {
Expand Down Expand Up @@ -79,32 +90,32 @@ impl RateLimitPolicy {
#[cfg(test)]
pub fn new(
name: String,
rate_limit_domain: String,
upstream_cluster: String,
domain: String,
service: String,
hostnames: Vec<String>,
gateway_actions: Vec<GatewayAction>,
rules: Vec<Rule>,
) -> Self {
RateLimitPolicy {
name,
rate_limit_domain,
upstream_cluster,
domain,
service,
hostnames,
gateway_actions,
rules,
}
}
}

pub struct FilterConfig {
pub index: PolicyIndex,
// Deny request when faced with an irrecoverable failure.
pub failure_mode_deny: bool,
// Deny/Allow request when faced with an irrecoverable failure.
pub failure_mode: FailureMode,
}

impl FilterConfig {
pub fn new() -> Self {
Self {
index: PolicyIndex::new(),
failure_mode_deny: true,
failure_mode: FailureMode::Deny,
}
}

Expand All @@ -119,7 +130,7 @@ impl FilterConfig {

Self {
index,
failure_mode_deny: config.failure_mode_deny,
failure_mode: config.failure_mode,
}
}
}
Expand Down Expand Up @@ -147,56 +158,45 @@ mod test {
"failureMode": "deny",
"rateLimitPolicies": [
{
"name": "some-name",
"rate_limit_domain": "RLS-domain",
"upstream_cluster": "limitador-cluster",
"name": "rlp-ns-A/rlp-name-A",
"domain": "rlp-ns-A/rlp-name-A",
"service": "limitador-cluster",
"hostnames": ["*.toystore.com", "example.com"],
"gateway_actions": [
"rules": [
{
"rules": [
"conditions": [
{
"paths": ["/admin/toy"],
"hosts": ["cars.toystore.com"],
"methods": ["POST"]
}],
"configurations": [
{
"actions": [
"allOf": [
{
"generic_key": {
"descriptor_key": "admin",
"descriptor_value": "1"
}
"selector": "request.path",
"operator": "equal",
"value": "/admin/toy"
},
{
"metadata": {
"descriptor_key": "user-id",
"default_value": "no-user",
"metadata_key": {
"key": "envoy.filters.http.ext_authz",
"path": [
{
"segment": {
"key": "ext_auth_data"
}
},
{
"segment": {
"key": "user_id"
}
}
]
},
"source": "DYNAMIC"
}
"selector": "request.method",
"operator": "equal",
"value": "POST"
},
{
"selector": "request.host",
"operator": "equal",
"value": "cars.toystore.com"
}]
},
"data": [
{
"static": {
"key": "rlp-ns-A/rlp-name-A",
"value": "1"
}
]
}
]
}
]
}
]
},
{
"selector": {
"selector": "auth.metadata.username",
}
}]
}]
}]
}"#;

#[test]
Expand All @@ -210,30 +210,17 @@ mod test {
let filter_config = res.unwrap();
assert_eq!(filter_config.rate_limit_policies.len(), 1);

let gateway_actions = &filter_config.rate_limit_policies[0].gateway_actions;
assert_eq!(gateway_actions.len(), 1);
let rules = &filter_config.rate_limit_policies[0].rules;
assert_eq!(rules.len(), 1);

let configurations = &gateway_actions[0].configurations;
assert_eq!(configurations.len(), 1);
let conditions = &rules[0].conditions;
assert_eq!(conditions.len(), 1);

let actions = &configurations[0].actions;
assert_eq!(actions.len(), 2);
assert!(std::matches!(
actions[0],
RLA_action_specifier::generic_key(_)
));
let all_of_conditions = &conditions[0].all_of;
assert_eq!(all_of_conditions.len(), 3);

if let RLA_action_specifier::metadata(ref metadata_action) = actions[1] {
let metadata_key = metadata_action.get_metadata_key();
assert_eq!(metadata_key.get_key(), "envoy.filters.http.ext_authz");

let metadata_path = metadata_key.get_path();
assert_eq!(metadata_path.len(), 2);
assert_eq!(metadata_path[0].get_key(), "ext_auth_data");
assert_eq!(metadata_path[1].get_key(), "user_id");
} else {
panic!("wrong action type: expected metadata type");
}
let data_items = &rules[0].data;
assert_eq!(data_items.len(), 2);
}

#[test]
Expand Down
73 changes: 37 additions & 36 deletions src/filter/http_context.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::configuration::{Configuration, FilterConfig, RateLimitPolicy, Rule};
use crate::configuration::{Condition, FilterConfig, PatternExpression, RateLimitPolicy, Rule};

Check warning on line 1 in src/filter/http_context.rs

View workflow job for this annotation

GitHub Actions / Check

unused import: `Rule`

Check warning on line 1 in src/filter/http_context.rs

View workflow job for this annotation

GitHub Actions / Test Suite

unused import: `Rule`

Check warning on line 1 in src/filter/http_context.rs

View workflow job for this annotation

GitHub Actions / Rustfmt

unused import: `Rule`

Check failure on line 1 in src/filter/http_context.rs

View workflow job for this annotation

GitHub Actions / Clippy

unused import: `Rule`
use crate::envoy::{
RLA_action_specifier, RateLimitDescriptor, RateLimitDescriptor_Entry, RateLimitRequest,
RateLimitResponse, RateLimitResponse_Code,
Expand Down Expand Up @@ -63,14 +63,14 @@ impl Filter {
}

let mut rl_req = RateLimitRequest::new();
rl_req.set_domain(rlp.rate_limit_domain.clone());
rl_req.set_domain(rlp.domain.clone());
rl_req.set_hits_addend(1);
rl_req.set_descriptors(descriptors);

let rl_req_serialized = Message::write_to_bytes(&rl_req).unwrap(); // TODO(rahulanand16nov): Error Handling

match self.dispatch_grpc_call(
rlp.upstream_cluster.as_str(),
rlp.service.as_str(),
RATELIMIT_SERVICE_NAME,
RATELIMIT_METHOD_NAME,
Vec::new(),
Expand All @@ -91,54 +91,55 @@ impl Filter {
rlp: &RateLimitPolicy,
) -> protobuf::RepeatedField<RateLimitDescriptor> {
//::protobuf::RepeatedField::default()
rlp.gateway_actions
rlp.rules
.iter()
.filter(|ga| self.filter_configurations_by_rules(&ga.rules))
.filter(|rule| self.filter_rule_by_conditions(&rule.conditions))
// flatten the vec<vec<Configurations> to vec<Configuration>
.flat_map(|ga| &ga.configurations)

Check failure on line 98 in src/filter/http_context.rs

View workflow job for this annotation

GitHub Actions / Check

no field `configurations` on type `&Rule`

Check failure on line 98 in src/filter/http_context.rs

View workflow job for this annotation

GitHub Actions / Test Suite

no field `configurations` on type `&Rule`

Check failure on line 98 in src/filter/http_context.rs

View workflow job for this annotation

GitHub Actions / Rustfmt

no field `configurations` on type `&Rule`

Check failure on line 98 in src/filter/http_context.rs

View workflow job for this annotation

GitHub Actions / Clippy

no field `configurations` on type `&configuration::Rule`
// All actions cannot be flatten! each vec of actions defines one potential descriptor
.flat_map(|configuration| self.build_descriptor(configuration))
.collect()
}

fn filter_configurations_by_rules(&self, rules: &[Rule]) -> bool {
if rules.is_empty() {
// no rules is equivalent to matching all the requests.
fn filter_rule_by_conditions(&self, conditions: &[Condition]) -> bool {
if conditions.is_empty() {
// no conditions is equivalent to matching all the requests.
return true;
}

rules.iter().any(|rule| self.rule_applies(rule))
conditions
.iter()
.any(|condition| self.condition_applies(condition))
}

fn rule_applies(&self, rule: &Rule) -> bool {
if !rule.paths.is_empty()
&& !rule
.paths
.iter()
.any(|path| path_match(path, self.request_path().as_str()))
{
return false;
}

if !rule.methods.is_empty()
&& !rule
.methods
.iter()
.any(|method| self.request_method().eq(method))
{
return false;
}
fn condition_applies(&self, condition: &Condition) -> bool {
condition
.all_of
.iter()
.all(|pattern_expression| self.pattern_expression_applies(pattern_expression))
}

if !rule.hosts.is_empty()
&& !rule
.hosts
.iter()
.any(|subdomain| subdomain_match(subdomain, self.request_authority().as_str()))
{
return false;
fn pattern_expression_applies(&self, p_e: &PatternExpression) -> bool {
let attribute_path = p_e.selector.split(".").collect();
match self.get_property(attribute_path) {
None => {
debug!(
"[context_id: {}]: selector not found: {}",
self.context_id, p_e.selector
);
false
}
Some(attribute_bytes) => match String::from_utf8(attribute_bytes) {
Err(e) => {
debug!(
"[context_id: {}]: failed to parse selector value: {}, ",
self.context_id, p_e.selector
);
false
}
Ok(attribute_value) => p_e.operator.eval(p_e.value, attribute_value),
},
}

true
}

fn build_descriptor(&self, configuration: &Configuration) -> Option<RateLimitDescriptor> {

Check failure on line 145 in src/filter/http_context.rs

View workflow job for this annotation

GitHub Actions / Check

cannot find type `Configuration` in this scope

Check failure on line 145 in src/filter/http_context.rs

View workflow job for this annotation

GitHub Actions / Test Suite

cannot find type `Configuration` in this scope

Check failure on line 145 in src/filter/http_context.rs

View workflow job for this annotation

GitHub Actions / Rustfmt

cannot find type `Configuration` in this scope

Check failure on line 145 in src/filter/http_context.rs

View workflow job for this annotation

GitHub Actions / Clippy

cannot find type `Configuration` in this scope
Expand Down

0 comments on commit 3583833

Please sign in to comment.