axum_containerssh/server/
mod.rs

1use std::collections::HashMap;
2
3use axum::{body::Body, extract::*, response::Response, routing::*};
4use axum_extra::extract::{CookieJar, Multipart};
5use bytes::Bytes;
6use http::{header::CONTENT_TYPE, HeaderMap, HeaderName, HeaderValue, Method, StatusCode};
7use tracing::error;
8use validator::{Validate, ValidationErrors};
9
10use crate::{header, types::*};
11
12#[allow(unused_imports)]
13use crate::models;
14
15use crate::{Api,
16     AuthPasswordResponse,
17     AuthPubKeyResponse,
18     AuthzResponse,
19     GetUserConfigurationResponse
20};
21
22/// Setup API Server.
23pub fn new<I, A>(api_impl: I) -> Router
24where
25    I: AsRef<A> + Clone + Send + Sync + 'static,
26    A: Api + 'static,
27{
28    // build our application with a route
29    Router::new()
30        .route("/authz",
31            post(authz::<I, A>)
32        )
33        .route("/config",
34            post(get_user_configuration::<I, A>)
35        )
36        .route("/password",
37            post(auth_password::<I, A>)
38        )
39        .route("/pubkey",
40            post(auth_pub_key::<I, A>)
41        )
42        .with_state(api_impl)
43}
44
45    #[derive(validator::Validate)]
46    #[allow(dead_code)]
47    struct AuthPasswordBodyValidator<'a> {
48            #[validate]
49          body: &'a models::PasswordAuthRequest,
50    }
51
52
53#[tracing::instrument(skip_all)]
54fn auth_password_validation(
55        body: models::PasswordAuthRequest,
56) -> std::result::Result<(
57        models::PasswordAuthRequest,
58), ValidationErrors>
59{
60              let b = AuthPasswordBodyValidator { body: &body };
61              b.validate()?;
62
63Ok((
64    body,
65))
66}
67
68/// AuthPassword - POST /password
69#[tracing::instrument(skip_all)]
70async fn auth_password<I, A>(
71  method: Method,
72  host: Host,
73  cookies: CookieJar,
74 State(api_impl): State<I>,
75          Json(body): Json<models::PasswordAuthRequest>,
76) -> Result<Response, StatusCode>
77where 
78    I: AsRef<A> + Send + Sync,
79    A: Api,
80{
81
82      #[allow(clippy::redundant_closure)]
83      let validation = tokio::task::spawn_blocking(move || 
84    auth_password_validation(
85          body,
86    )
87  ).await.unwrap();
88
89  let Ok((
90      body,
91  )) = validation else {
92    return Response::builder()
93            .status(StatusCode::BAD_REQUEST)
94            .body(Body::from(validation.unwrap_err().to_string()))
95            .map_err(|_| StatusCode::BAD_REQUEST); 
96  };
97
98  let result = api_impl.as_ref().auth_password(
99      method,
100      host,
101      cookies,
102              body,
103  ).await;
104
105  let mut response = Response::builder();
106
107  let resp = match result {
108                                            Ok(rsp) => match rsp {
109                                                AuthPasswordResponse::Status200_ResponseIsTheFullHTTPAuthenticationResponse
110                                                    (body)
111                                                => {
112
113                                                  let mut response = response.status(200);
114                                                  {
115                                                    let mut response_headers = response.headers_mut().unwrap();
116                                                    response_headers.insert(
117                                                        CONTENT_TYPE,
118                                                        HeaderValue::from_str("application/json").map_err(|e| { error!(error = ?e); StatusCode::INTERNAL_SERVER_ERROR })?);
119                                                  }
120
121                                                  let body_content =  tokio::task::spawn_blocking(move ||
122                                                      serde_json::to_vec(&body).map_err(|e| {
123                                                        error!(error = ?e);
124                                                        StatusCode::INTERNAL_SERVER_ERROR
125                                                      })).await.unwrap()?;
126                                                  response.body(Body::from(body_content))
127                                                },
128                                            },
129                                            Err(_) => {
130                                                // Application code returned an error. This should not happen, as the implementation should
131                                                // return a valid response.
132                                                response.status(500).body(Body::empty())
133                                            },
134                                        };
135
136                                        resp.map_err(|e| { error!(error = ?e); StatusCode::INTERNAL_SERVER_ERROR })
137}
138
139    #[derive(validator::Validate)]
140    #[allow(dead_code)]
141    struct AuthPubKeyBodyValidator<'a> {
142            #[validate]
143          body: &'a models::PublicKeyAuthRequest,
144    }
145
146
147#[tracing::instrument(skip_all)]
148fn auth_pub_key_validation(
149        body: models::PublicKeyAuthRequest,
150) -> std::result::Result<(
151        models::PublicKeyAuthRequest,
152), ValidationErrors>
153{
154              let b = AuthPubKeyBodyValidator { body: &body };
155              b.validate()?;
156
157Ok((
158    body,
159))
160}
161
162/// AuthPubKey - POST /pubkey
163#[tracing::instrument(skip_all)]
164async fn auth_pub_key<I, A>(
165  method: Method,
166  host: Host,
167  cookies: CookieJar,
168 State(api_impl): State<I>,
169          Json(body): Json<models::PublicKeyAuthRequest>,
170) -> Result<Response, StatusCode>
171where 
172    I: AsRef<A> + Send + Sync,
173    A: Api,
174{
175
176      #[allow(clippy::redundant_closure)]
177      let validation = tokio::task::spawn_blocking(move || 
178    auth_pub_key_validation(
179          body,
180    )
181  ).await.unwrap();
182
183  let Ok((
184      body,
185  )) = validation else {
186    return Response::builder()
187            .status(StatusCode::BAD_REQUEST)
188            .body(Body::from(validation.unwrap_err().to_string()))
189            .map_err(|_| StatusCode::BAD_REQUEST); 
190  };
191
192  let result = api_impl.as_ref().auth_pub_key(
193      method,
194      host,
195      cookies,
196              body,
197  ).await;
198
199  let mut response = Response::builder();
200
201  let resp = match result {
202                                            Ok(rsp) => match rsp {
203                                                AuthPubKeyResponse::Status200_ResponseIsTheFullHTTPAuthenticationResponse
204                                                    (body)
205                                                => {
206
207                                                  let mut response = response.status(200);
208                                                  {
209                                                    let mut response_headers = response.headers_mut().unwrap();
210                                                    response_headers.insert(
211                                                        CONTENT_TYPE,
212                                                        HeaderValue::from_str("application/json").map_err(|e| { error!(error = ?e); StatusCode::INTERNAL_SERVER_ERROR })?);
213                                                  }
214
215                                                  let body_content =  tokio::task::spawn_blocking(move ||
216                                                      serde_json::to_vec(&body).map_err(|e| {
217                                                        error!(error = ?e);
218                                                        StatusCode::INTERNAL_SERVER_ERROR
219                                                      })).await.unwrap()?;
220                                                  response.body(Body::from(body_content))
221                                                },
222                                            },
223                                            Err(_) => {
224                                                // Application code returned an error. This should not happen, as the implementation should
225                                                // return a valid response.
226                                                response.status(500).body(Body::empty())
227                                            },
228                                        };
229
230                                        resp.map_err(|e| { error!(error = ?e); StatusCode::INTERNAL_SERVER_ERROR })
231}
232
233    #[derive(validator::Validate)]
234    #[allow(dead_code)]
235    struct AuthzBodyValidator<'a> {
236            #[validate]
237          body: &'a models::AuthorizationRequest,
238    }
239
240
241#[tracing::instrument(skip_all)]
242fn authz_validation(
243        body: models::AuthorizationRequest,
244) -> std::result::Result<(
245        models::AuthorizationRequest,
246), ValidationErrors>
247{
248              let b = AuthzBodyValidator { body: &body };
249              b.validate()?;
250
251Ok((
252    body,
253))
254}
255
256/// Authz - POST /authz
257#[tracing::instrument(skip_all)]
258async fn authz<I, A>(
259  method: Method,
260  host: Host,
261  cookies: CookieJar,
262 State(api_impl): State<I>,
263          Json(body): Json<models::AuthorizationRequest>,
264) -> Result<Response, StatusCode>
265where 
266    I: AsRef<A> + Send + Sync,
267    A: Api,
268{
269
270      #[allow(clippy::redundant_closure)]
271      let validation = tokio::task::spawn_blocking(move || 
272    authz_validation(
273          body,
274    )
275  ).await.unwrap();
276
277  let Ok((
278      body,
279  )) = validation else {
280    return Response::builder()
281            .status(StatusCode::BAD_REQUEST)
282            .body(Body::from(validation.unwrap_err().to_string()))
283            .map_err(|_| StatusCode::BAD_REQUEST); 
284  };
285
286  let result = api_impl.as_ref().authz(
287      method,
288      host,
289      cookies,
290              body,
291  ).await;
292
293  let mut response = Response::builder();
294
295  let resp = match result {
296                                            Ok(rsp) => match rsp {
297                                                AuthzResponse::Status200_ResponseIsTheFullHTTPAuthenticationResponse
298                                                    (body)
299                                                => {
300
301                                                  let mut response = response.status(200);
302                                                  {
303                                                    let mut response_headers = response.headers_mut().unwrap();
304                                                    response_headers.insert(
305                                                        CONTENT_TYPE,
306                                                        HeaderValue::from_str("application/json").map_err(|e| { error!(error = ?e); StatusCode::INTERNAL_SERVER_ERROR })?);
307                                                  }
308
309                                                  let body_content =  tokio::task::spawn_blocking(move ||
310                                                      serde_json::to_vec(&body).map_err(|e| {
311                                                        error!(error = ?e);
312                                                        StatusCode::INTERNAL_SERVER_ERROR
313                                                      })).await.unwrap()?;
314                                                  response.body(Body::from(body_content))
315                                                },
316                                            },
317                                            Err(_) => {
318                                                // Application code returned an error. This should not happen, as the implementation should
319                                                // return a valid response.
320                                                response.status(500).body(Body::empty())
321                                            },
322                                        };
323
324                                        resp.map_err(|e| { error!(error = ?e); StatusCode::INTERNAL_SERVER_ERROR })
325}
326
327    #[derive(validator::Validate)]
328    #[allow(dead_code)]
329    struct GetUserConfigurationBodyValidator<'a> {
330            #[validate]
331          body: &'a models::ConfigRequest,
332    }
333
334
335#[tracing::instrument(skip_all)]
336fn get_user_configuration_validation(
337        body: Option<models::ConfigRequest>,
338) -> std::result::Result<(
339        Option<models::ConfigRequest>,
340), ValidationErrors>
341{
342            if let Some(body) = &body {
343              let b = GetUserConfigurationBodyValidator { body };
344              b.validate()?;
345            }
346
347Ok((
348    body,
349))
350}
351
352/// GetUserConfiguration - POST /config
353#[tracing::instrument(skip_all)]
354async fn get_user_configuration<I, A>(
355  method: Method,
356  host: Host,
357  cookies: CookieJar,
358 State(api_impl): State<I>,
359          Json(body): Json<Option<models::ConfigRequest>>,
360) -> Result<Response, StatusCode>
361where 
362    I: AsRef<A> + Send + Sync,
363    A: Api,
364{
365
366      #[allow(clippy::redundant_closure)]
367      let validation = tokio::task::spawn_blocking(move || 
368    get_user_configuration_validation(
369          body,
370    )
371  ).await.unwrap();
372
373  let Ok((
374      body,
375  )) = validation else {
376    return Response::builder()
377            .status(StatusCode::BAD_REQUEST)
378            .body(Body::from(validation.unwrap_err().to_string()))
379            .map_err(|_| StatusCode::BAD_REQUEST); 
380  };
381
382  let result = api_impl.as_ref().get_user_configuration(
383      method,
384      host,
385      cookies,
386              body,
387  ).await;
388
389  let mut response = Response::builder();
390
391  let resp = match result {
392                                            Ok(rsp) => match rsp {
393                                                GetUserConfigurationResponse::Status200_ResponseIsTheEntireResponseFromTheConfigServer
394                                                    (body)
395                                                => {
396
397                                                  let mut response = response.status(200);
398                                                  {
399                                                    let mut response_headers = response.headers_mut().unwrap();
400                                                    response_headers.insert(
401                                                        CONTENT_TYPE,
402                                                        HeaderValue::from_str("application/json").map_err(|e| { error!(error = ?e); StatusCode::INTERNAL_SERVER_ERROR })?);
403                                                  }
404
405                                                  let body_content =  tokio::task::spawn_blocking(move ||
406                                                      serde_json::to_vec(&body).map_err(|e| {
407                                                        error!(error = ?e);
408                                                        StatusCode::INTERNAL_SERVER_ERROR
409                                                      })).await.unwrap()?;
410                                                  response.body(Body::from(body_content))
411                                                },
412                                            },
413                                            Err(_) => {
414                                                // Application code returned an error. This should not happen, as the implementation should
415                                                // return a valid response.
416                                                response.status(500).body(Body::empty())
417                                            },
418                                        };
419
420                                        resp.map_err(|e| { error!(error = ?e); StatusCode::INTERNAL_SERVER_ERROR })
421}
422