envoy_types/ext_authz/
mod.rs

1pub mod v3 {
2    /// Re-exporting selected pbs for more convenient imports when implementing
3    /// an ExtAuthz server.
4    pub mod pb {
5        pub use crate::pb::envoy::config::core::v3::{
6            address::Address, header_value_option::HeaderAppendAction, HeaderValue,
7            HeaderValueOption, QueryParameter,
8        };
9        pub use crate::pb::envoy::r#type::v3::{HttpStatus, StatusCode as HttpStatusCode};
10        pub use crate::pb::envoy::service::auth::v3::{
11            authorization_server::{Authorization, AuthorizationServer},
12            check_response::HttpResponse,
13            CheckRequest, CheckResponse, DeniedHttpResponse, OkHttpResponse,
14        };
15    }
16
17    use pb::{
18        Address, CheckRequest, CheckResponse, DeniedHttpResponse, HeaderAppendAction, HeaderValue,
19        HeaderValueOption, HttpResponse, HttpStatus, HttpStatusCode, OkHttpResponse,
20        QueryParameter,
21    };
22    use std::collections::HashMap;
23
24    use crate::pb::google::{protobuf::Struct, rpc};
25
26    impl crate::sealed::Sealed for CheckRequest {}
27    impl crate::sealed::Sealed for CheckResponse {}
28    impl crate::sealed::Sealed for OkHttpResponse {}
29    impl crate::sealed::Sealed for DeniedHttpResponse {}
30    impl crate::sealed::Sealed for HttpResponse {}
31
32    /// Extension trait aiming to provide convenient methods to get useful
33    /// data from [`pb::CheckRequest`].
34    ///
35    /// This trait is sealed and not meant to be implemented outside of
36    /// `envoy-types`.
37    pub trait CheckRequestExt: crate::sealed::Sealed {
38        /// Returns a reference to the [`pb::CheckRequest`]'s source peer
39        /// [`pb::Address::SocketAddress`] inner value, if any.
40        ///
41        /// In cases where Envoy is acting as an Edge Proxy, this should match
42        /// the client's IP.
43        fn get_client_address(&self) -> Option<&String>;
44
45        /// Returns a reference to the inner (client) request's headers.
46        fn get_client_headers(&self) -> Option<&HashMap<String, String>>;
47    }
48
49    impl CheckRequestExt for CheckRequest {
50        fn get_client_address(&self) -> Option<&String> {
51            let peer = self.attributes.as_ref()?.source.as_ref()?;
52            let address = peer.address.as_ref()?.address.as_ref()?;
53            if let Address::SocketAddress(socket_address) = address {
54                return Some(&socket_address.address);
55            }
56            None
57        }
58
59        fn get_client_headers(&self) -> Option<&HashMap<String, String>> {
60            let request = self.attributes.as_ref()?.request.as_ref()?;
61            Some(&request.http.as_ref()?.headers)
62        }
63    }
64
65    /// Simple trait used to convert local and foreign types to
66    /// [`pb::HttpResponse`]. Implemented for [`pb::OkHttpResponse`],
67    /// [`pb::DeniedHttpResponse`], [`pb::HttpResponse`],
68    /// [`OkHttpResponseBuilder`] and [`DeniedHttpResponseBuilder`].
69    ///
70    /// This trait is sealed and not meant to be implemented outside of
71    /// `envoy-types`.
72    pub trait ToHttpResponse: crate::sealed::Sealed {
73        fn to_http_response(self) -> HttpResponse;
74    }
75
76    impl ToHttpResponse for OkHttpResponse {
77        fn to_http_response(self) -> HttpResponse {
78            HttpResponse::OkResponse(self)
79        }
80    }
81
82    impl ToHttpResponse for DeniedHttpResponse {
83        fn to_http_response(self) -> HttpResponse {
84            HttpResponse::DeniedResponse(self)
85        }
86    }
87
88    impl ToHttpResponse for HttpResponse {
89        fn to_http_response(self) -> HttpResponse {
90            self
91        }
92    }
93
94    /// Extension trait aiming to provide convenient associated fn's and methods
95    /// to create and edit [`pb::CheckResponse`].
96    ///
97    /// This trait is sealed and not meant to be implemented outside of
98    /// `envoy-types`.
99    pub trait CheckResponseExt: crate::sealed::Sealed {
100        /// Create a new, empty [`pb::CheckResponse`].
101        ///
102        /// Please note that if you return an empty [`pb::CheckResponse`], the
103        /// request will be denied, since there will not be an inner `Ok`
104        /// [`rpc::Status`].
105        fn new() -> Self;
106
107        /// Create a new [`pb::CheckResponse`] with the [`rpc::Status`]'s
108        /// `code` and `message` matching those of the `tonic::Status` provided.
109        ///
110        /// Please note that `tonic::Status`'s `details` will not be considered.
111        fn with_status(status: tonic::Status) -> Self;
112
113        /// Set the [`pb::CheckResponse`] inner [`rpc::Status`]'s `code` and
114        /// `message` as those of the `tonic::Status` provided.
115        ///
116        /// Please note that `tonic::Status`'s `details` will not be considered.
117        fn set_status(&mut self, status: tonic::Status) -> &mut Self;
118
119        /// Set the [`pb::CheckResponse`]'s `dynamic_metadata` field.
120        fn set_dynamic_metadata(&mut self, dynamic_metadata: Option<Struct>) -> &mut Self;
121
122        /// Set the [`pb::CheckResponse`]'s `http_response` field.
123        ///
124        /// Compatible with [`OkHttpResponseBuilder`],
125        /// [`DeniedHttpResponseBuilder`], or even [`pb::OkHttpResponse`],
126        /// [`pb::DeniedHttpResponse`] and [`pb::HttpResponse`].
127        fn set_http_response(&mut self, http_response: impl ToHttpResponse) -> &mut Self;
128    }
129
130    impl CheckResponseExt for CheckResponse {
131        fn new() -> Self {
132            CheckResponse::default()
133        }
134
135        fn with_status(status: tonic::Status) -> Self {
136            let status = Some(rpc::Status {
137                code: status.code().into(),
138                message: status.message().into(),
139                // `tonic::Status`'s details are not considered
140                details: Vec::new(),
141            });
142
143            CheckResponse {
144                status,
145                ..Default::default()
146            }
147        }
148
149        fn set_status(&mut self, status: tonic::Status) -> &mut Self {
150            self.status = Some(rpc::Status {
151                code: status.code().into(),
152                message: status.message().into(),
153                // `tonic::Status`'s details are not considered
154                details: Vec::new(),
155            });
156            self
157        }
158
159        fn set_dynamic_metadata(&mut self, dynamic_metadata: Option<Struct>) -> &mut Self {
160            self.dynamic_metadata = dynamic_metadata;
161            self
162        }
163
164        fn set_http_response(&mut self, http_response: impl ToHttpResponse) -> &mut Self {
165            self.http_response = Some(http_response.to_http_response());
166            self
167        }
168    }
169
170    fn push_header(
171        headers: &mut Vec<HeaderValueOption>,
172        key: impl Into<String>,
173        value: impl Into<String>,
174        append_action: Option<HeaderAppendAction>,
175        keep_empty_value: bool,
176    ) {
177        #[allow(deprecated)]
178        headers.push(HeaderValueOption {
179            header: Some(HeaderValue {
180                key: key.into(),
181                value: value.into(),
182                raw_value: Vec::new(), // Only one of `value` or `raw_value` can be set.
183            }),
184            append: None, // Deprecated field
185            append_action: append_action
186                .unwrap_or(HeaderAppendAction::AppendIfExistsOrAdd)
187                .into(),
188            keep_empty_value,
189        });
190    }
191
192    /// Provides convenient associated fn's and methods used to build a
193    /// [`pb::OkHttpResponse`], containing HTTP attributes for an OK response.
194    #[derive(Debug, Default)]
195    pub struct OkHttpResponseBuilder {
196        headers: Vec<HeaderValueOption>,
197        headers_to_remove: Vec<String>,
198        response_headers_to_add: Vec<HeaderValueOption>,
199        query_parameters_to_remove: Vec<String>,
200        query_parameters_to_set: Vec<QueryParameter>,
201    }
202
203    impl OkHttpResponseBuilder {
204        /// Creates a new, empty [`OkHttpResponseBuilder`].
205        pub fn new() -> Self {
206            OkHttpResponseBuilder::default()
207        }
208
209        /// Add, overwrite or append a HTTP header to the original request
210        /// before dispatching it upstream.
211        ///
212        /// The `append_action` field describes what action should be taken to
213        /// append/overwrite the given value for an existing header, or to only
214        /// add this header if it is not already present. Defaults to
215        /// [`pb::HeaderAppendAction::AppendIfExistsOrAdd`] if set as `None`.
216        ///
217        /// If `keep_empty_value` is set as `false`, custom headers with empty
218        /// values will be dropped. If set to `true`, they will be added.
219        pub fn add_header(
220            &mut self,
221            key: impl Into<String>,
222            value: impl Into<String>,
223            append_action: Option<HeaderAppendAction>,
224            keep_empty_value: bool,
225        ) -> &mut Self {
226            push_header(
227                &mut self.headers,
228                key,
229                value,
230                append_action,
231                keep_empty_value,
232            );
233            self
234        }
235
236        /// Remove a HTTP header from the original request before dispatching
237        /// it upstream.
238        ///
239        /// Useful to consume headers related to auth that should not be exposed
240        /// to downstream services.
241        ///
242        /// Envoy's pseudo headers (such as `:authority`, `:method`, `:path`
243        /// etc), as well as the header `Host`, will not be removed, since this
244        /// would make the request malformed.
245        pub fn remove_header(&mut self, header: impl Into<String>) -> &mut Self {
246            self.headers_to_remove.push(header.into());
247            self
248        }
249
250        /// Add a HTTP response header that will be sent to the downstream
251        /// client, in case of success.
252        ///
253        /// The `append_action` field describes what action should be taken to
254        /// append/overwrite the given value for an existing header, or to only
255        /// add this header if it is not already present. Defaults to
256        /// [`pb::HeaderAppendAction::AppendIfExistsOrAdd`] if set as `None`.
257        ///
258        /// If `keep_empty_value` is set as `false`, custom headers with empty
259        /// values will be dropped. If set to `true`, they will be added.
260        pub fn add_response_header(
261            &mut self,
262            key: impl Into<String>,
263            value: impl Into<String>,
264            append_action: Option<HeaderAppendAction>,
265            keep_empty_value: bool,
266        ) -> &mut Self {
267            push_header(
268                &mut self.response_headers_to_add,
269                key,
270                value,
271                append_action,
272                keep_empty_value,
273            );
274            self
275        }
276
277        /// Remove a query parameter from the original request before
278        /// dispatching it upstream.
279        ///
280        /// Please note that the parameter `key` is case-sensitive.
281        pub fn remove_query_parameter(&mut self, key: impl Into<String>) -> &mut Self {
282            self.query_parameters_to_remove.push(key.into());
283            self
284        }
285
286        /// Add or overwrite a query parameter of the original request before
287        /// dispatching it upstream.
288        ///
289        /// Please note that the parameter `key` is case-sensitive.
290        pub fn set_query_parameter(
291            &mut self,
292            key: impl Into<String>,
293            value: impl Into<String>,
294        ) -> &mut Self {
295            self.query_parameters_to_set.push(QueryParameter {
296                key: key.into(),
297                value: value.into(),
298            });
299            self
300        }
301
302        /// Get reference to `headers`.
303        pub fn get_headers(&self) -> &Vec<HeaderValueOption> {
304            &self.headers
305        }
306
307        /// Get reference to `headers_to_remove`.
308        pub fn get_headers_to_remove(&self) -> &Vec<String> {
309            &self.headers_to_remove
310        }
311
312        /// Get reference to `response_headers_to_add`.
313        pub fn get_response_headers_to_add(&self) -> &Vec<HeaderValueOption> {
314            &self.response_headers_to_add
315        }
316
317        /// Get reference to `query_parameters_to_remove`.
318        pub fn get_query_parameters_to_remove(&self) -> &Vec<String> {
319            &self.query_parameters_to_remove
320        }
321
322        /// Get reference to `query_parameters_to_set`.
323        pub fn get_query_parameters_to_set(&self) -> &Vec<QueryParameter> {
324            &self.query_parameters_to_set
325        }
326
327        /// Build a [`pb::OkHttpResponse`], consuming the
328        /// [`OkHttpResponseBuilder`].
329        pub fn build(self) -> OkHttpResponse {
330            #[allow(deprecated)]
331            OkHttpResponse {
332                headers: self.headers,
333                headers_to_remove: self.headers_to_remove,
334                dynamic_metadata: None, // Deprecated field
335                response_headers_to_add: self.response_headers_to_add,
336                query_parameters_to_remove: self.query_parameters_to_remove,
337                query_parameters_to_set: self.query_parameters_to_set,
338            }
339        }
340    }
341
342    impl From<OkHttpResponseBuilder> for OkHttpResponse {
343        fn from(val: OkHttpResponseBuilder) -> Self {
344            val.build()
345        }
346    }
347
348    impl crate::sealed::Sealed for OkHttpResponseBuilder {}
349
350    impl ToHttpResponse for OkHttpResponseBuilder {
351        fn to_http_response(self) -> HttpResponse {
352            self.build().to_http_response()
353        }
354    }
355
356    /// Provides convenient associated fn's and methods used to build a
357    /// [`pb::DeniedHttpResponse`], containing HTTP attributes for a
358    /// denied response.
359    #[derive(Debug, Default)]
360    pub struct DeniedHttpResponseBuilder {
361        status: Option<HttpStatus>,
362        headers: Vec<HeaderValueOption>,
363        body: String,
364    }
365
366    impl DeniedHttpResponseBuilder {
367        /// Creates a new, empty [`DeniedHttpResponseBuilder`].
368        pub fn new() -> Self {
369            DeniedHttpResponseBuilder::default()
370        }
371
372        /// Set the HTTP response status code that will be sent to the
373        /// downstream client.
374        ///
375        /// If not set, Envoy will send a `403 Forbidden` HTTP status code.
376        pub fn set_http_status(&mut self, http_status_code: HttpStatusCode) -> &mut Self {
377            self.status = Some(HttpStatus {
378                code: http_status_code.into(),
379            });
380            self
381        }
382
383        /// Add a HTTP response header that will be sent to the downstream
384        /// client.
385        ///
386        /// The `append_action` field describes what action should be taken to
387        /// append/overwrite the given value for an existing header, or to only
388        /// add this header if it is not already present. Defaults to
389        /// [`pb::HeaderAppendAction::AppendIfExistsOrAdd`] if set as `None`.
390        ///
391        /// If `keep_empty_value` is set as `false`, custom headers with empty
392        /// values will be dropped. If set to `true`, they will be added.
393        pub fn add_header(
394            &mut self,
395            key: impl Into<String>,
396            value: impl Into<String>,
397            append_action: Option<HeaderAppendAction>,
398            keep_empty_value: bool,
399        ) -> &mut Self {
400            push_header(
401                &mut self.headers,
402                key,
403                value,
404                append_action,
405                keep_empty_value,
406            );
407            self
408        }
409
410        /// Set the HTTP response body that will be sent to the downstream
411        /// client.
412        pub fn set_body(&mut self, body: impl Into<String>) -> &mut Self {
413            self.body = body.into();
414            self
415        }
416
417        /// Get reference to `status`.
418        pub fn get_http_status(&self) -> &Option<HttpStatus> {
419            &self.status
420        }
421
422        /// Get reference to `headers`.
423        pub fn get_headers(&self) -> &Vec<HeaderValueOption> {
424            &self.headers
425        }
426
427        /// Get reference to `body`.
428        pub fn get_body(&self) -> &String {
429            &self.body
430        }
431
432        /// Build a [`pb::DeniedHttpResponse`], consuming the
433        /// [`DeniedHttpResponseBuilder`].
434        pub fn build(self) -> DeniedHttpResponse {
435            DeniedHttpResponse {
436                status: self.status,
437                headers: self.headers,
438                body: self.body,
439            }
440        }
441    }
442
443    impl From<DeniedHttpResponseBuilder> for DeniedHttpResponse {
444        fn from(val: DeniedHttpResponseBuilder) -> Self {
445            val.build()
446        }
447    }
448
449    impl crate::sealed::Sealed for DeniedHttpResponseBuilder {}
450
451    impl ToHttpResponse for DeniedHttpResponseBuilder {
452        fn to_http_response(self) -> HttpResponse {
453            self.build().to_http_response()
454        }
455    }
456}