openidauthzen 0.1.0-alpha.1

OpenID AuthZEN Authorization API 1.0 — Policy Decision and Enforcement Points for Rust
Documentation
#![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";