#![allow(dead_code)]
use std::collections::VecDeque;
use std::sync::Arc;
use std::time::Duration;
use openidauthzen::http::{HttpClient, HttpResponse, Method};
use openidauthzen::{
Action, AuthZenClient, Error, PdpConfiguration, Resource, Subject,
};
pub struct CapturedRequest {
pub method: String,
pub url: String,
pub headers: Vec<(String, String)>,
pub body: Option<Vec<u8>>,
}
pub struct MockHttpClient {
pub requests: Arc<tokio::sync::RwLock<Vec<CapturedRequest>>>,
pub responses: Arc<tokio::sync::RwLock<VecDeque<HttpResponse>>>,
}
impl MockHttpClient {
pub fn new(responses: Vec<HttpResponse>) -> Self {
Self {
requests: Arc::new(tokio::sync::RwLock::new(Vec::new())),
responses: Arc::new(tokio::sync::RwLock::new(VecDeque::from(responses))),
}
}
}
#[async_trait::async_trait]
impl HttpClient for MockHttpClient {
async fn request(
&self,
method: Method,
url: &str,
headers: &[(&str, &str)],
body: Option<Vec<u8>>,
) -> Result<HttpResponse, Error> {
let method_str = match method {
Method::Get => "GET".to_owned(),
Method::Post => "POST".to_owned(),
_ => "UNKNOWN".to_owned(),
};
let captured = CapturedRequest {
method: method_str,
url: url.to_owned(),
headers: headers.iter().map(|(k, v)| (k.to_string(), v.to_string())).collect(),
body,
};
self.requests.write().await.push(captured);
let resp = self
.responses
.write()
.await
.pop_front()
.expect("MockHttpClient: no more responses queued");
Ok(resp)
}
}
pub fn ok_response(body: &[u8]) -> HttpResponse {
HttpResponse {
status: 200,
headers: vec![],
body: body.to_vec(),
}
}
pub fn error_response(status: u16, body: &str) -> HttpResponse {
HttpResponse {
status,
headers: vec![],
body: body.as_bytes().to_vec(),
}
}
pub fn mock_pdp_config_json(pdp_id: &str) -> Vec<u8> {
serde_json::to_vec(&PdpConfiguration {
policy_decision_point: pdp_id.to_owned(),
access_evaluation_endpoint: format!("{}/access/v1/evaluation", pdp_id),
access_evaluations_endpoint: Some(format!("{}/access/v1/evaluations", pdp_id)),
search_subject_endpoint: Some(format!("{}/access/v1/search/subject", pdp_id)),
search_resource_endpoint: Some(format!("{}/access/v1/search/resource", pdp_id)),
search_action_endpoint: Some(format!("{}/access/v1/search/action", pdp_id)),
capabilities: None,
signed_metadata: None,
})
.unwrap()
}
pub fn mock_pdp_config_minimal_json(pdp_id: &str) -> Vec<u8> {
serde_json::to_vec(&PdpConfiguration {
policy_decision_point: pdp_id.to_owned(),
access_evaluation_endpoint: format!("{}/access/v1/evaluation", pdp_id),
access_evaluations_endpoint: None,
search_subject_endpoint: None,
search_resource_endpoint: None,
search_action_endpoint: None,
capabilities: None,
signed_metadata: None,
})
.unwrap()
}
pub fn make_client(responses: Vec<HttpResponse>) -> (AuthZenClient<MockHttpClient>, Arc<tokio::sync::RwLock<Vec<CapturedRequest>>>) {
let mock = MockHttpClient::new(responses);
let requests = Arc::clone(&mock.requests);
let client = AuthZenClient::new(mock, Duration::from_secs(300));
(client, requests)
}
pub fn make_client_with_ttl(responses: Vec<HttpResponse>, ttl: Duration) -> (AuthZenClient<MockHttpClient>, Arc<tokio::sync::RwLock<Vec<CapturedRequest>>>) {
let mock = MockHttpClient::new(responses);
let requests = Arc::clone(&mock.requests);
let client = AuthZenClient::new(mock, ttl);
(client, requests)
}
pub fn sample_subject() -> Subject {
Subject {
subject_type: "user".to_owned(),
id: Some("alice@example.com".to_owned()),
properties: None,
}
}
pub fn sample_resource() -> Resource {
Resource {
resource_type: "account".to_owned(),
id: Some("123".to_owned()),
properties: None,
}
}
pub fn sample_action() -> Action {
Action {
name: "can_read".to_owned(),
properties: None,
}
}
pub const PDP_ID: &str = "https://pdp.example.com";
pub const TOKEN: &str = "test-bearer-token";