axum_containerssh/server/
mod.rs1use 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
22pub fn new<I, A>(api_impl: I) -> Router
24where
25 I: AsRef<A> + Clone + Send + Sync + 'static,
26 A: Api + 'static,
27{
28 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#[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 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#[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 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#[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 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#[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 response.status(500).body(Body::empty())
417 },
418 };
419
420 resp.map_err(|e| { error!(error = ?e); StatusCode::INTERNAL_SERVER_ERROR })
421}
422