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}