1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
#![cfg_attr(all(doc, CHANNEL_NIGHTLY), feature(doc_auto_cfg))]
#![feature(associated_type_defaults)]

#[macro_use]
extern crate async_backtrace;
#[macro_use]
extern crate async_trait;
#[macro_use]
extern crate cfg_if;
#[macro_use]
extern crate derivative;
#[macro_use]
extern crate derive_more;
#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate serde_with;
#[macro_use]
extern crate tracing;
#[macro_use]
extern crate typed_builder;

mod endpoints;

pub use endpoints::*;

use authzen_service_util::*;
use hyper::{client::HttpConnector, header::HeaderMap, Body, Request, Response, StatusCode};
use hyper_rustls::{HttpsConnector, HttpsConnectorBuilder};
use std::{ops::Deref, sync::Arc, time::Duration};
use tokio::time::timeout;

pub const DEFAULT_TIMEOUT_SECONDS: u64 = 30;

lazy_static! {
    pub(crate) static ref OPA_DEBUG: bool = std::env::var("OPA_DEBUG").map(|s| s == "true").unwrap_or_default();
    pub(crate) static ref OPA_EXPLAIN: Option<String> = std::env::var("OPA_EXPLAIN").ok();
    pub(crate) static ref OPA_PRETTY: bool = std::env::var("OPA_PRETTY").map(|s| s == "true").unwrap_or_default();
}

#[derive(Clone, Debug)]
pub struct OPAClient<Connector = HttpsConnector<HttpConnector>>(Arc<_OPAClient<Connector>>);

#[doc(hidden)]
#[derive(Clone, Debug)]
pub struct _OPAClient<Connector = HttpsConnector<HttpConnector>> {
    base_uri: String,
    client: hyper::client::Client<Connector>,
    headers: HeaderMap,
    timeout: Duration,
    pub data_path: String,
    pub query: String,
}

impl Deref for OPAClient {
    type Target = _OPAClient;
    fn deref(&self) -> &Self::Target {
        self.0.deref()
    }
}

#[async_trait]
impl Client for OPAClient {
    type Error = Error;

    fn headers(&self) -> &HeaderMap {
        &self.deref().headers
    }

    #[framed]
    async fn rest(&self, request: Request<Body>) -> Result<Response<Body>, Self::Error> {
        let response = timeout(self.timeout, self.client.request(request))
            .await
            .map_err(|_| Error::new(StatusCode::REQUEST_TIMEOUT))?
            .map_err(Error::default_details)?;
        cfg_if! {
            if #[cfg(feature = "debug")] {
                let (parts, body) = response.into_parts();
                let bytes = hyper::body::to_bytes(body).await.unwrap();
                println!("{}", String::from_utf8_lossy(bytes.as_ref()));
                Ok(Response::from_parts(parts, hyper::Body::from(bytes)))
            } else {
                Ok(response)
            }
        }
    }
}

impl ClientBaseUri for OPAClient {
    fn base_uri(&self) -> &str {
        &self.deref().base_uri
    }
}

impl OPAClient {
    pub fn new(
        scheme: &str,
        host: &str,
        port: &Option<u16>,
        data_path: impl ToString,
        query: impl ToString,
    ) -> Result<Self, anyhow::Error> {
        let client: hyper::Client<HttpsConnector<HttpConnector>> = hyper::Client::builder().build::<_, hyper::Body>(
            HttpsConnectorBuilder::new()
                .with_webpki_roots()
                .https_or_http()
                .enable_http1()
                .build(),
        );

        let headers = HeaderMap::new();
        let port = port.map(|x| format!(":{x}")).unwrap_or_default();
        let base_uri = format!("{scheme}://{host}{port}");

        Ok(Self(Arc::new(_OPAClient {
            timeout: Duration::from_secs(DEFAULT_TIMEOUT_SECONDS),
            base_uri,
            client,
            headers,
            data_path: data_path.to_string(),
            query: query.to_string(),
        })))
    }
    pub fn timeout(self, timeout: Duration) -> Self {
        let mut _opa_client = Arc::try_unwrap(self.0).unwrap();
        _opa_client.timeout = timeout;
        Self(Arc::new(_opa_client))
    }
}