]> git.proxmox.com Git - proxmox-backup.git/blob - src/server/rest.rs
8ee3e77b17a22db451a389555e30db154ac19ca0
[proxmox-backup.git] / src / server / rest.rs
1 use std::collections::HashMap;
2 use std::future::Future;
3 use std::hash::BuildHasher;
4 use std::path::{Path, PathBuf};
5 use std::pin::Pin;
6 use std::sync::{Arc, Mutex};
7 use std::task::{Context, Poll};
8
9 use anyhow::{bail, format_err, Error};
10 use futures::future::{self, FutureExt, TryFutureExt};
11 use futures::stream::TryStreamExt;
12 use hyper::header::{self, HeaderMap};
13 use hyper::body::HttpBody;
14 use hyper::http::request::Parts;
15 use hyper::{Body, Request, Response, StatusCode};
16 use lazy_static::lazy_static;
17 use serde_json::{json, Value};
18 use tokio::fs::File;
19 use tokio::time::Instant;
20 use percent_encoding::percent_decode_str;
21 use url::form_urlencoded;
22 use regex::Regex;
23
24 use proxmox::http_err;
25 use proxmox::api::{
26 ApiHandler,
27 ApiMethod,
28 HttpError,
29 Permission,
30 RpcEnvironment,
31 RpcEnvironmentType,
32 check_api_permission,
33 };
34 use proxmox::api::schema::{
35 ObjectSchema,
36 parse_parameter_strings,
37 parse_simple_value,
38 verify_json_object,
39 };
40
41 use super::environment::RestEnvironment;
42 use super::formatter::*;
43 use super::ApiConfig;
44
45 use crate::auth_helpers::*;
46 use crate::api2::types::{Authid, Userid};
47 use crate::tools;
48 use crate::tools::FileLogger;
49 use crate::tools::ticket::Ticket;
50 use crate::config::cached_user_info::CachedUserInfo;
51
52 extern "C" { fn tzset(); }
53
54 pub struct RestServer {
55 pub api_config: Arc<ApiConfig>,
56 }
57
58 const MAX_URI_QUERY_LENGTH: usize = 3072;
59
60 impl RestServer {
61
62 pub fn new(api_config: ApiConfig) -> Self {
63 Self { api_config: Arc::new(api_config) }
64 }
65 }
66
67 impl tower_service::Service<&tokio_openssl::SslStream<tokio::net::TcpStream>> for RestServer {
68 type Response = ApiService;
69 type Error = Error;
70 type Future = Pin<Box<dyn Future<Output = Result<ApiService, Error>> + Send>>;
71
72 fn poll_ready(&mut self, _cx: &mut Context) -> Poll<Result<(), Self::Error>> {
73 Poll::Ready(Ok(()))
74 }
75
76 fn call(&mut self, ctx: &tokio_openssl::SslStream<tokio::net::TcpStream>) -> Self::Future {
77 match ctx.get_ref().peer_addr() {
78 Err(err) => {
79 future::err(format_err!("unable to get peer address - {}", err)).boxed()
80 }
81 Ok(peer) => {
82 future::ok(ApiService { peer, api_config: self.api_config.clone() }).boxed()
83 }
84 }
85 }
86 }
87
88 impl tower_service::Service<&tokio::net::TcpStream> for RestServer {
89 type Response = ApiService;
90 type Error = Error;
91 type Future = Pin<Box<dyn Future<Output = Result<ApiService, Error>> + Send>>;
92
93 fn poll_ready(&mut self, _cx: &mut Context) -> Poll<Result<(), Self::Error>> {
94 Poll::Ready(Ok(()))
95 }
96
97 fn call(&mut self, ctx: &tokio::net::TcpStream) -> Self::Future {
98 match ctx.peer_addr() {
99 Err(err) => {
100 future::err(format_err!("unable to get peer address - {}", err)).boxed()
101 }
102 Ok(peer) => {
103 future::ok(ApiService { peer, api_config: self.api_config.clone() }).boxed()
104 }
105 }
106 }
107 }
108
109 pub struct ApiService {
110 pub peer: std::net::SocketAddr,
111 pub api_config: Arc<ApiConfig>,
112 }
113
114 fn log_response(
115 logfile: Option<&Mutex<FileLogger>>,
116 peer: &std::net::SocketAddr,
117 method: hyper::Method,
118 path_query: &str,
119 resp: &Response<Body>,
120 user_agent: Option<String>,
121 ) {
122
123 if resp.extensions().get::<NoLogExtension>().is_some() { return; };
124
125 // we also log URL-to-long requests, so avoid message bigger than PIPE_BUF (4k on Linux)
126 // to profit from atomicty guarantees for O_APPEND opened logfiles
127 let path = &path_query[..MAX_URI_QUERY_LENGTH.min(path_query.len())];
128
129 let status = resp.status();
130
131 if !(status.is_success() || status.is_informational()) {
132 let reason = status.canonical_reason().unwrap_or("unknown reason");
133
134 let mut message = "request failed";
135 if let Some(data) = resp.extensions().get::<ErrorMessageExtension>() {
136 message = &data.0;
137 }
138
139 log::error!("{} {}: {} {}: [client {}] {}", method.as_str(), path, status.as_str(), reason, peer, message);
140 }
141 if let Some(logfile) = logfile {
142 let auth_id = match resp.extensions().get::<Authid>() {
143 Some(auth_id) => auth_id.to_string(),
144 None => "-".to_string(),
145 };
146 let now = proxmox::tools::time::epoch_i64();
147 // time format which apache/nginx use (by default), copied from pve-http-server
148 let datetime = proxmox::tools::time::strftime_local("%d/%m/%Y:%H:%M:%S %z", now)
149 .unwrap_or("-".into());
150
151 logfile
152 .lock()
153 .unwrap()
154 .log(format!(
155 "{} - {} [{}] \"{} {}\" {} {} {}",
156 peer.ip(),
157 auth_id,
158 datetime,
159 method.as_str(),
160 path,
161 status.as_str(),
162 resp.body().size_hint().lower(),
163 user_agent.unwrap_or("-".into()),
164 ));
165 }
166 }
167
168 fn get_proxied_peer(headers: &HeaderMap) -> Option<std::net::SocketAddr> {
169 lazy_static! {
170 static ref RE: Regex = Regex::new(r#"for="([^"]+)""#).unwrap();
171 }
172 let forwarded = headers.get(header::FORWARDED)?.to_str().ok()?;
173 let capture = RE.captures(&forwarded)?;
174 let rhost = capture.get(1)?.as_str();
175
176 rhost.parse().ok()
177 }
178
179 fn get_user_agent(headers: &HeaderMap) -> Option<String> {
180 let agent = headers.get(header::USER_AGENT)?.to_str();
181 agent.map(|s| {
182 let mut s = s.to_owned();
183 s.truncate(128);
184 s
185 }).ok()
186 }
187
188 impl tower_service::Service<Request<Body>> for ApiService {
189 type Response = Response<Body>;
190 type Error = Error;
191 type Future = Pin<Box<dyn Future<Output = Result<Response<Body>, Self::Error>> + Send>>;
192
193 fn poll_ready(&mut self, _cx: &mut Context) -> Poll<Result<(), Self::Error>> {
194 Poll::Ready(Ok(()))
195 }
196
197 fn call(&mut self, req: Request<Body>) -> Self::Future {
198 let path = req.uri().path_and_query().unwrap().as_str().to_owned();
199 let method = req.method().clone();
200 let user_agent = get_user_agent(req.headers());
201
202 let config = Arc::clone(&self.api_config);
203 let peer = match get_proxied_peer(req.headers()) {
204 Some(proxied_peer) => proxied_peer,
205 None => self.peer,
206 };
207 async move {
208 let response = match handle_request(Arc::clone(&config), req, &peer).await {
209 Ok(response) => response,
210 Err(err) => {
211 let (err, code) = match err.downcast_ref::<HttpError>() {
212 Some(apierr) => (apierr.message.clone(), apierr.code),
213 _ => (err.to_string(), StatusCode::BAD_REQUEST),
214 };
215 Response::builder().status(code).body(err.into())?
216 }
217 };
218 let logger = config.get_file_log();
219 log_response(logger, &peer, method, &path, &response, user_agent);
220 Ok(response)
221 }
222 .boxed()
223 }
224 }
225
226 fn parse_query_parameters<S: 'static + BuildHasher + Send>(
227 param_schema: &ObjectSchema,
228 form: &str, // x-www-form-urlencoded body data
229 parts: &Parts,
230 uri_param: &HashMap<String, String, S>,
231 ) -> Result<Value, Error> {
232
233 let mut param_list: Vec<(String, String)> = vec![];
234
235 if !form.is_empty() {
236 for (k, v) in form_urlencoded::parse(form.as_bytes()).into_owned() {
237 param_list.push((k, v));
238 }
239 }
240
241 if let Some(query_str) = parts.uri.query() {
242 for (k, v) in form_urlencoded::parse(query_str.as_bytes()).into_owned() {
243 if k == "_dc" { continue; } // skip extjs "disable cache" parameter
244 param_list.push((k, v));
245 }
246 }
247
248 for (k, v) in uri_param {
249 param_list.push((k.clone(), v.clone()));
250 }
251
252 let params = parse_parameter_strings(&param_list, param_schema, true)?;
253
254 Ok(params)
255 }
256
257 async fn get_request_parameters<S: 'static + BuildHasher + Send>(
258 param_schema: &ObjectSchema,
259 parts: Parts,
260 req_body: Body,
261 uri_param: HashMap<String, String, S>,
262 ) -> Result<Value, Error> {
263
264 let mut is_json = false;
265
266 if let Some(value) = parts.headers.get(header::CONTENT_TYPE) {
267 match value.to_str().map(|v| v.split(';').next()) {
268 Ok(Some("application/x-www-form-urlencoded")) => {
269 is_json = false;
270 }
271 Ok(Some("application/json")) => {
272 is_json = true;
273 }
274 _ => bail!("unsupported content type {:?}", value.to_str()),
275 }
276 }
277
278 let body = req_body
279 .map_err(|err| http_err!(BAD_REQUEST, "Promlems reading request body: {}", err))
280 .try_fold(Vec::new(), |mut acc, chunk| async move {
281 if acc.len() + chunk.len() < 64*1024 { //fimxe: max request body size?
282 acc.extend_from_slice(&*chunk);
283 Ok(acc)
284 } else {
285 Err(http_err!(BAD_REQUEST, "Request body too large"))
286 }
287 }).await?;
288
289 let utf8_data = std::str::from_utf8(&body)
290 .map_err(|err| format_err!("Request body not uft8: {}", err))?;
291
292 if is_json {
293 let mut params: Value = serde_json::from_str(utf8_data)?;
294 for (k, v) in uri_param {
295 if let Some((_optional, prop_schema)) = param_schema.lookup(&k) {
296 params[&k] = parse_simple_value(&v, prop_schema)?;
297 }
298 }
299 verify_json_object(&params, param_schema)?;
300 return Ok(params);
301 } else {
302 parse_query_parameters(param_schema, utf8_data, &parts, &uri_param)
303 }
304 }
305
306 struct NoLogExtension();
307
308 async fn proxy_protected_request(
309 info: &'static ApiMethod,
310 mut parts: Parts,
311 req_body: Body,
312 peer: &std::net::SocketAddr,
313 ) -> Result<Response<Body>, Error> {
314
315 let mut uri_parts = parts.uri.clone().into_parts();
316
317 uri_parts.scheme = Some(http::uri::Scheme::HTTP);
318 uri_parts.authority = Some(http::uri::Authority::from_static("127.0.0.1:82"));
319 let new_uri = http::Uri::from_parts(uri_parts).unwrap();
320
321 parts.uri = new_uri;
322
323 let mut request = Request::from_parts(parts, req_body);
324 request
325 .headers_mut()
326 .insert(header::FORWARDED, format!("for=\"{}\";", peer).parse().unwrap());
327
328 let reload_timezone = info.reload_timezone;
329
330 let resp = hyper::client::Client::new()
331 .request(request)
332 .map_err(Error::from)
333 .map_ok(|mut resp| {
334 resp.extensions_mut().insert(NoLogExtension());
335 resp
336 })
337 .await?;
338
339 if reload_timezone { unsafe { tzset(); } }
340
341 Ok(resp)
342 }
343
344 pub async fn handle_api_request<Env: RpcEnvironment, S: 'static + BuildHasher + Send>(
345 mut rpcenv: Env,
346 info: &'static ApiMethod,
347 formatter: &'static OutputFormatter,
348 parts: Parts,
349 req_body: Body,
350 uri_param: HashMap<String, String, S>,
351 ) -> Result<Response<Body>, Error> {
352
353 let delay_unauth_time = std::time::Instant::now() + std::time::Duration::from_millis(3000);
354
355 let result = match info.handler {
356 ApiHandler::AsyncHttp(handler) => {
357 let params = parse_query_parameters(info.parameters, "", &parts, &uri_param)?;
358 (handler)(parts, req_body, params, info, Box::new(rpcenv)).await
359 }
360 ApiHandler::Sync(handler) => {
361 let params = get_request_parameters(info.parameters, parts, req_body, uri_param).await?;
362 (handler)(params, info, &mut rpcenv)
363 .map(|data| (formatter.format_data)(data, &rpcenv))
364 }
365 ApiHandler::Async(handler) => {
366 let params = get_request_parameters(info.parameters, parts, req_body, uri_param).await?;
367 (handler)(params, info, &mut rpcenv)
368 .await
369 .map(|data| (formatter.format_data)(data, &rpcenv))
370 }
371 };
372
373 let resp = match result {
374 Ok(resp) => resp,
375 Err(err) => {
376 if let Some(httperr) = err.downcast_ref::<HttpError>() {
377 if httperr.code == StatusCode::UNAUTHORIZED {
378 tokio::time::delay_until(Instant::from_std(delay_unauth_time)).await;
379 }
380 }
381 (formatter.format_error)(err)
382 }
383 };
384
385 if info.reload_timezone { unsafe { tzset(); } }
386
387 Ok(resp)
388 }
389
390 fn get_index(
391 userid: Option<Userid>,
392 csrf_token: Option<String>,
393 language: Option<String>,
394 api: &Arc<ApiConfig>,
395 parts: Parts,
396 ) -> Response<Body> {
397
398 let nodename = proxmox::tools::nodename();
399 let user = userid.as_ref().map(|u| u.as_str()).unwrap_or("");
400
401 let csrf_token = csrf_token.unwrap_or_else(|| String::from(""));
402
403 let mut debug = false;
404 let mut template_file = "index";
405
406 if let Some(query_str) = parts.uri.query() {
407 for (k, v) in form_urlencoded::parse(query_str.as_bytes()).into_owned() {
408 if k == "debug" && v != "0" && v != "false" {
409 debug = true;
410 } else if k == "console" {
411 template_file = "console";
412 }
413 }
414 }
415
416 let mut lang = String::from("");
417 if let Some(language) = language {
418 if Path::new(&format!("/usr/share/pbs-i18n/pbs-lang-{}.js", language)).exists() {
419 lang = language;
420 }
421 }
422
423 let data = json!({
424 "NodeName": nodename,
425 "UserName": user,
426 "CSRFPreventionToken": csrf_token,
427 "language": lang,
428 "debug": debug,
429 });
430
431 let (ct, index) = match api.render_template(template_file, &data) {
432 Ok(index) => ("text/html", index),
433 Err(err) => {
434 ("text/plain", format!("Error rendering template: {}", err))
435 }
436 };
437
438 let mut resp = Response::builder()
439 .status(StatusCode::OK)
440 .header(header::CONTENT_TYPE, ct)
441 .body(index.into())
442 .unwrap();
443
444 if let Some(userid) = userid {
445 resp.extensions_mut().insert(Authid::from((userid, None)));
446 }
447
448 resp
449 }
450
451 fn extension_to_content_type(filename: &Path) -> (&'static str, bool) {
452
453 if let Some(ext) = filename.extension().and_then(|osstr| osstr.to_str()) {
454 return match ext {
455 "css" => ("text/css", false),
456 "html" => ("text/html", false),
457 "js" => ("application/javascript", false),
458 "json" => ("application/json", false),
459 "map" => ("application/json", false),
460 "png" => ("image/png", true),
461 "ico" => ("image/x-icon", true),
462 "gif" => ("image/gif", true),
463 "svg" => ("image/svg+xml", false),
464 "jar" => ("application/java-archive", true),
465 "woff" => ("application/font-woff", true),
466 "woff2" => ("application/font-woff2", true),
467 "ttf" => ("application/font-snft", true),
468 "pdf" => ("application/pdf", true),
469 "epub" => ("application/epub+zip", true),
470 "mp3" => ("audio/mpeg", true),
471 "oga" => ("audio/ogg", true),
472 "tgz" => ("application/x-compressed-tar", true),
473 _ => ("application/octet-stream", false),
474 };
475 }
476
477 ("application/octet-stream", false)
478 }
479
480 async fn simple_static_file_download(filename: PathBuf) -> Result<Response<Body>, Error> {
481
482 let (content_type, _nocomp) = extension_to_content_type(&filename);
483
484 use tokio::io::AsyncReadExt;
485
486 let mut file = File::open(filename)
487 .await
488 .map_err(|err| http_err!(BAD_REQUEST, "File open failed: {}", err))?;
489
490 let mut data: Vec<u8> = Vec::new();
491 file.read_to_end(&mut data)
492 .await
493 .map_err(|err| http_err!(BAD_REQUEST, "File read failed: {}", err))?;
494
495 let mut response = Response::new(data.into());
496 response.headers_mut().insert(
497 header::CONTENT_TYPE,
498 header::HeaderValue::from_static(content_type));
499 Ok(response)
500 }
501
502 async fn chuncked_static_file_download(filename: PathBuf) -> Result<Response<Body>, Error> {
503 let (content_type, _nocomp) = extension_to_content_type(&filename);
504
505 let file = File::open(filename)
506 .await
507 .map_err(|err| http_err!(BAD_REQUEST, "File open failed: {}", err))?;
508
509 let payload = tokio_util::codec::FramedRead::new(file, tokio_util::codec::BytesCodec::new())
510 .map_ok(|bytes| hyper::body::Bytes::from(bytes.freeze()));
511 let body = Body::wrap_stream(payload);
512
513 // fixme: set other headers ?
514 Ok(Response::builder()
515 .status(StatusCode::OK)
516 .header(header::CONTENT_TYPE, content_type)
517 .body(body)
518 .unwrap()
519 )
520 }
521
522 async fn handle_static_file_download(filename: PathBuf) -> Result<Response<Body>, Error> {
523
524 let metadata = tokio::fs::metadata(filename.clone())
525 .map_err(|err| http_err!(BAD_REQUEST, "File access problems: {}", err))
526 .await?;
527
528 if metadata.len() < 1024*32 {
529 simple_static_file_download(filename).await
530 } else {
531 chuncked_static_file_download(filename).await
532 }
533 }
534
535 fn extract_lang_header(headers: &http::HeaderMap) -> Option<String> {
536 if let Some(raw_cookie) = headers.get("COOKIE") {
537 if let Ok(cookie) = raw_cookie.to_str() {
538 return tools::extract_cookie(cookie, "PBSLangCookie");
539 }
540 }
541
542 None
543 }
544
545 struct UserAuthData{
546 ticket: String,
547 csrf_token: Option<String>,
548 }
549
550 enum AuthData {
551 User(UserAuthData),
552 ApiToken(String),
553 }
554
555 fn extract_auth_data(headers: &http::HeaderMap) -> Option<AuthData> {
556 if let Some(raw_cookie) = headers.get(header::COOKIE) {
557 if let Ok(cookie) = raw_cookie.to_str() {
558 if let Some(ticket) = tools::extract_cookie(cookie, "PBSAuthCookie") {
559 let csrf_token = match headers.get("CSRFPreventionToken").map(|v| v.to_str()) {
560 Some(Ok(v)) => Some(v.to_owned()),
561 _ => None,
562 };
563 return Some(AuthData::User(UserAuthData {
564 ticket,
565 csrf_token,
566 }));
567 }
568 }
569 }
570
571 match headers.get(header::AUTHORIZATION).map(|v| v.to_str()) {
572 Some(Ok(v)) if v.starts_with("PBSAPIToken ") => {
573 Some(AuthData::ApiToken(v["PBSAPIToken ".len()..].to_owned()))
574 },
575 _ => None,
576 }
577 }
578
579 fn check_auth(
580 method: &hyper::Method,
581 auth_data: &AuthData,
582 user_info: &CachedUserInfo,
583 ) -> Result<Authid, Error> {
584 match auth_data {
585 AuthData::User(user_auth_data) => {
586 let ticket = user_auth_data.ticket.clone();
587 let ticket_lifetime = tools::ticket::TICKET_LIFETIME;
588
589 let userid: Userid = Ticket::parse(&ticket)?
590 .verify_with_time_frame(public_auth_key(), "PBS", None, -300..ticket_lifetime)?;
591
592 let auth_id = Authid::from(userid.clone());
593 if !user_info.is_active_auth_id(&auth_id) {
594 bail!("user account disabled or expired.");
595 }
596
597 if method != hyper::Method::GET {
598 if let Some(csrf_token) = &user_auth_data.csrf_token {
599 verify_csrf_prevention_token(csrf_secret(), &userid, &csrf_token, -300, ticket_lifetime)?;
600 } else {
601 bail!("missing CSRF prevention token");
602 }
603 }
604
605 Ok(auth_id)
606 },
607 AuthData::ApiToken(api_token) => {
608 let mut parts = api_token.splitn(2, ':');
609 let tokenid = parts.next()
610 .ok_or_else(|| format_err!("failed to split API token header"))?;
611 let tokenid: Authid = tokenid.parse()?;
612
613 let tokensecret = parts.next()
614 .ok_or_else(|| format_err!("failed to split API token header"))?;
615 let tokensecret = percent_decode_str(tokensecret)
616 .decode_utf8()
617 .map_err(|_| format_err!("failed to decode API token header"))?;
618
619 crate::config::token_shadow::verify_secret(&tokenid, &tokensecret)?;
620
621 Ok(tokenid)
622 }
623 }
624 }
625
626 async fn handle_request(
627 api: Arc<ApiConfig>,
628 req: Request<Body>,
629 peer: &std::net::SocketAddr,
630 ) -> Result<Response<Body>, Error> {
631
632 let (parts, body) = req.into_parts();
633 let method = parts.method.clone();
634 let (path, components) = tools::normalize_uri_path(parts.uri.path())?;
635
636 let comp_len = components.len();
637
638 let query = parts.uri.query().unwrap_or_default();
639 if path.len() + query.len() > MAX_URI_QUERY_LENGTH {
640 return Ok(Response::builder()
641 .status(StatusCode::URI_TOO_LONG)
642 .body("".into())
643 .unwrap());
644 }
645
646 let env_type = api.env_type();
647 let mut rpcenv = RestEnvironment::new(env_type);
648
649 rpcenv.set_client_ip(Some(*peer));
650
651 let user_info = CachedUserInfo::new()?;
652
653 let delay_unauth_time = std::time::Instant::now() + std::time::Duration::from_millis(3000);
654 let access_forbidden_time = std::time::Instant::now() + std::time::Duration::from_millis(500);
655
656 if comp_len >= 1 && components[0] == "api2" {
657
658 if comp_len >= 2 {
659
660 let format = components[1];
661
662 let formatter = match format {
663 "json" => &JSON_FORMATTER,
664 "extjs" => &EXTJS_FORMATTER,
665 _ => bail!("Unsupported output format '{}'.", format),
666 };
667
668 let mut uri_param = HashMap::new();
669 let api_method = api.find_method(&components[2..], method.clone(), &mut uri_param);
670
671 let mut auth_required = true;
672 if let Some(api_method) = api_method {
673 if let Permission::World = *api_method.access.permission {
674 auth_required = false; // no auth for endpoints with World permission
675 }
676 }
677
678 if auth_required {
679 let auth_result = match extract_auth_data(&parts.headers) {
680 Some(auth_data) => check_auth(&method, &auth_data, &user_info),
681 None => Err(format_err!("no authentication credentials provided.")),
682 };
683 match auth_result {
684 Ok(authid) => rpcenv.set_auth_id(Some(authid.to_string())),
685 Err(err) => {
686 // always delay unauthorized calls by 3 seconds (from start of request)
687 let err = http_err!(UNAUTHORIZED, "authentication failed - {}", err);
688 tokio::time::delay_until(Instant::from_std(delay_unauth_time)).await;
689 return Ok((formatter.format_error)(err));
690 }
691 }
692 }
693
694 match api_method {
695 None => {
696 let err = http_err!(NOT_FOUND, "Path '{}' not found.", path);
697 return Ok((formatter.format_error)(err));
698 }
699 Some(api_method) => {
700 let auth_id = rpcenv.get_auth_id();
701 if !check_api_permission(api_method.access.permission, auth_id.as_deref(), &uri_param, user_info.as_ref()) {
702 let err = http_err!(FORBIDDEN, "permission check failed");
703 tokio::time::delay_until(Instant::from_std(access_forbidden_time)).await;
704 return Ok((formatter.format_error)(err));
705 }
706
707 let result = if api_method.protected && env_type == RpcEnvironmentType::PUBLIC {
708 proxy_protected_request(api_method, parts, body, peer).await
709 } else {
710 handle_api_request(rpcenv, api_method, formatter, parts, body, uri_param).await
711 };
712
713 let mut response = match result {
714 Ok(resp) => resp,
715 Err(err) => (formatter.format_error)(err),
716 };
717
718 if let Some(auth_id) = auth_id {
719 let auth_id: Authid = auth_id.parse()?;
720 response.extensions_mut().insert(auth_id);
721 }
722
723 return Ok(response);
724 }
725 }
726
727 }
728 } else {
729 // not Auth required for accessing files!
730
731 if method != hyper::Method::GET {
732 bail!("Unsupported HTTP method {}", method);
733 }
734
735 if comp_len == 0 {
736 let language = extract_lang_header(&parts.headers);
737 if let Some(auth_data) = extract_auth_data(&parts.headers) {
738 match check_auth(&method, &auth_data, &user_info) {
739 Ok(auth_id) if !auth_id.is_token() => {
740 let userid = auth_id.user();
741 let new_csrf_token = assemble_csrf_prevention_token(csrf_secret(), userid);
742 return Ok(get_index(Some(userid.clone()), Some(new_csrf_token), language, &api, parts));
743 },
744 _ => {
745 tokio::time::delay_until(Instant::from_std(delay_unauth_time)).await;
746 return Ok(get_index(None, None, language, &api, parts));
747 }
748 }
749 } else {
750 return Ok(get_index(None, None, language, &api, parts));
751 }
752 } else {
753 let filename = api.find_alias(&components);
754 return handle_static_file_download(filename).await;
755 }
756 }
757
758 Err(http_err!(NOT_FOUND, "Path '{}' not found.", path))
759 }