use std::{net::IpAddr, time::SystemTime};
use crate::pg_wap::{
crpc::model::{
AuthorizeTargetsError, AuthorizeTargetsRequest, AuthorizeTargetsResponse,
ControlServiceAPIHandler,
},
grants::{AuthorizeError, GrantManager},
};
pub struct WapControlHandler {
grant: GrantManager,
wap_id: String,
data_plane_port: u16,
}
impl WapControlHandler {
pub fn new(grant: GrantManager, wap_id: String, data_plane_port: u16) -> Self {
Self {
grant,
wap_id,
data_plane_port,
}
}
}
impl ControlServiceAPIHandler for WapControlHandler {
fn authorize_targets(
&self,
client_ip: IpAddr,
request: AuthorizeTargetsRequest,
now: SystemTime,
) -> Result<AuthorizeTargetsResponse, AuthorizeTargetsError> {
let expiry_time = self
.grant
.authorize(client_ip, request.targets, now)
.map_err(|err| {
let message = err.to_string();
match err {
AuthorizeError::TooManySegmentsInRequest { .. }
| AuthorizeError::TooManyTargetsInRequest { .. }
| AuthorizeError::TooManySegmentsForTarget { .. }
| AuthorizeError::TooManyTargetsForIp { .. } => {
AuthorizeTargetsError::InvalidRequest(message)
}
AuthorizeError::TargetGrantLimitReached { .. }
| AuthorizeError::IpTargetLimitReached { .. } => {
AuthorizeTargetsError::LimitReached(message)
}
}
})?;
Ok(AuthorizeTargetsResponse {
client_ip,
wap_id: self.wap_id.clone(),
data_plane_port: self.data_plane_port,
expiry_time,
})
}
}
#[cfg(test)]
mod tests {
use std::{
collections::BTreeMap,
net::Ipv4Addr,
time::{Duration, UNIX_EPOCH},
};
use super::*;
use crate::pg_wap::{
crpc::model::AuthSegments, grants::GrantManagerConfig, sni::CustomerDomain,
};
const GRANT_DURATION: Duration = Duration::from_secs(100);
const WAP_ID: &str = "wap-1";
const DATA_PLANE_PORT: u16 = 8443;
fn handler(max_targets: usize) -> WapControlHandler {
let grant = GrantManager::new(GrantManagerConfig {
grant_duration: GRANT_DURATION,
max_targets_per_request: max_targets,
..GrantManagerConfig::default()
})
.expect("a valid GrantManagerConfig");
WapControlHandler::new(grant, WAP_ID.to_owned(), DATA_PLANE_PORT)
}
fn request(count: usize) -> AuthorizeTargetsRequest {
AuthorizeTargetsRequest {
targets: (0..count)
.map(|i| {
(
CustomerDomain::new(format!("target{i}.example.com"))
.expect("a valid domain"),
AuthSegments::default(),
)
})
.collect::<BTreeMap<_, _>>(),
}
}
#[test]
fn response_carries_the_wap_and_the_granted_expiry() {
let client_ip = Ipv4Addr::new(192, 0, 2, 1).into();
let now = UNIX_EPOCH + Duration::from_secs(1_000);
let response = handler(10)
.authorize_targets(client_ip, request(1), now)
.expect("the grant fits");
assert_eq!(response.client_ip, client_ip);
assert_eq!(response.wap_id, WAP_ID);
assert_eq!(response.data_plane_port, DATA_PLANE_PORT);
assert_eq!(response.expiry_time, now + GRANT_DURATION);
}
#[test]
fn a_request_over_a_limit_is_invalid() {
let err = handler(1)
.authorize_targets(
Ipv4Addr::new(192, 0, 2, 1).into(),
request(2),
SystemTime::now(),
)
.expect_err("two targets do not fit");
assert!(matches!(err, AuthorizeTargetsError::InvalidRequest(_)));
}
}