Monotone Arbeit nervt!
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

376 lines
13 KiB

2 years ago
  1. use actix_web::client::{Client, ClientRequest};
  2. use actix_web::{http, web, HttpRequest, HttpResponse};
  3. use actix_session::Session;
  4. use askama::Template;
  5. use chrono::Utc;
  6. use csrf::{AesGcmCsrfProtection, CsrfProtection};
  7. use std::time::Duration;
  8. use url::Url;
  9. use crate::account::*;
  10. use crate::config::get_csrf_key;
  11. use crate::config::PAYLOAD_LIMIT;
  12. use crate::config::PROXY_TIMEOUT;
  13. use crate::database::methods::InsertableForm;
  14. use crate::database::structs::Form;
  15. use crate::debug;
  16. use crate::errors::{crash, TrainCrash};
  17. use crate::sniff::*;
  18. use crate::templates::*;
  19. use crate::DbPool;
  20. use crate::CONFIG;
  21. pub async fn forward(
  22. req: HttpRequest,
  23. body: web::Bytes,
  24. url: web::Data<Url>,
  25. client: web::Data<Client>,
  26. ) -> Result<HttpResponse, TrainCrash> {
  27. let route = req.uri().path();
  28. // if check_route returns true,
  29. // the user supposedly tried to access a restricted page.
  30. // They get redirected to the main page.
  31. if route.starts_with("/apps/files") {
  32. // exception for /apps/files: always redirect to /apps/forms
  33. debug(&format!("Files route blocked: {}", route));
  34. return Ok(web_redir("/apps/forms").await.map_err(|e| {
  35. eprintln!("error_redirect: {}", e);
  36. crash(get_lang(&req), "error_redirect")
  37. })?);
  38. } else if check_route(route) {
  39. debug(&format!("Restricted route blocked: {}", route));
  40. return Ok(web_redir("/").await.map_err(|e| {
  41. eprintln!("error_redirect: {}", e);
  42. crash(get_lang(&req), "error_redirect")
  43. })?);
  44. }
  45. let forwarded_req = forge_from(route, &req, &url, &client);
  46. // check the request before sending it
  47. // (prevents the user from sending some specific POST requests)
  48. if check_request(route, &body) {
  49. debug(&format!(
  50. "Restricted request: {}",
  51. String::from_utf8_lossy(&body)
  52. ));
  53. return Err(crash(get_lang(&req), "error_dirtyhacker"));
  54. }
  55. // send the request to the Nextcloud instance
  56. let mut res = forwarded_req.send_body(body).await.map_err(|e| {
  57. eprintln!("error_forward_resp: {}", e);
  58. crash(get_lang(&req), "error_forward_req")
  59. })?;
  60. let mut client_resp = HttpResponse::build(res.status());
  61. // remove connection as per the spec
  62. // and content-encoding since we have to decompress the traffic to edit it
  63. // and basic-auth, because this feature is not needed.
  64. for (header_name, header_value) in res
  65. .headers()
  66. .iter()
  67. .filter(|(h, _)| *h != "connection" && *h != "content-encoding")
  68. {
  69. client_resp.header(header_name.clone(), header_value.clone());
  70. }
  71. // sparing the use of a mutable body when not needed
  72. // For now, the body only needs to be modified when the route
  73. // is "create a new form" route
  74. if route == "/ocs/v2.php/apps/forms/api/v1/form" {
  75. // retreive the body from the request result
  76. let response_body = res.body().limit(PAYLOAD_LIMIT).await.map_err(|e| {
  77. eprintln!("error_forward_resp: {}", e);
  78. crash(get_lang(&req), "error_forward_resp")
  79. })?;
  80. // if a new form is created, automatically set some fields.
  81. // this is very hackish but it works! for now.
  82. let form_id = check_new_form(&response_body);
  83. if form_id > 0 {
  84. debug(&format!(
  85. "New form. Forging request to set isAnonymous for id {}",
  86. form_id
  87. ));
  88. let forged_body = format!(
  89. r#"{{"id":{},"keyValuePairs":{{"isAnonymous":true}}}}"#,
  90. form_id
  91. );
  92. let update_req = forge_from(
  93. "/ocs/v2.php/apps/forms/api/v1/form/update",
  94. &req,
  95. &url,
  96. &client,
  97. )
  98. .set_header("content-length", forged_body.len())
  99. .set_header("content-type", "application/json;charset=utf-8");
  100. let res = update_req.send_body(forged_body).await.map_err(|e| {
  101. eprintln!("error_forward_isanon: {}", e);
  102. crash(get_lang(&req), "error_forward_isanon")
  103. })?;
  104. debug(&format!("(new_form) Request returned {}", res.status()));
  105. }
  106. Ok(client_resp.body(response_body).await.map_err(|e| {
  107. eprintln!("error_forward_clientresp_newform: {}", e);
  108. crash(get_lang(&req), "error_forward_clientresp_newform")
  109. })?)
  110. } else {
  111. Ok(
  112. client_resp.body(res.body().limit(PAYLOAD_LIMIT).await.map_err(|e| {
  113. eprintln!("error_forward_clientresp_newform: {}", e);
  114. crash(get_lang(&req), "error_forward_clientresp_std")
  115. })?),
  116. )
  117. }
  118. // check the response before returning it (unused)
  119. /*if check_response(route, &response_body) {
  120. return Ok(web_redir("/"));
  121. }*/
  122. }
  123. #[derive(Deserialize)]
  124. pub struct LoginToken {
  125. pub token: String,
  126. }
  127. #[derive(Deserialize)]
  128. pub struct CsrfToken {
  129. pub csrf_token: String,
  130. }
  131. pub async fn forward_login(
  132. req: HttpRequest,
  133. s: Session,
  134. params: web::Path<LoginToken>,
  135. client: web::Data<Client>,
  136. dbpool: web::Data<DbPool>,
  137. ) -> Result<HttpResponse, TrainCrash> {
  138. // check if the provided token seems valid. If not, early return.
  139. if !check_token(&params.token) {
  140. debug("Incorrect admin token given in params.");
  141. debug(&format!("Token: {:#?}", params.token));
  142. return Err(crash(get_lang(&req), "error_dirtyhacker"));
  143. }
  144. let conn = dbpool.get().map_err(|e| {
  145. eprintln!("error_forwardlogin_db: {}", e);
  146. crash(get_lang(&req), "error_forwardlogin_db")
  147. })?;
  148. let moved_token = params.token.clone();
  149. // check if the link exists in DB. if it does, update lastvisit_at.
  150. let formdata = web::block(move || Form::get_from_token(&params.token, &conn))
  151. .await
  152. .map_err(|e| {
  153. eprintln!("error_forwardlogin_db_get (diesel error): {}", e);
  154. crash(get_lang(&req), "error_forwardlogin_db_get")
  155. })?
  156. .ok_or_else(|| {
  157. debug("error: Token not found.");
  158. crash(get_lang(&req), "error_forwardlogin_notfound")
  159. })?;
  160. // copy the token in cookies.
  161. s.set("sncf_admin_token", &moved_token).map_err(|e| {
  162. eprintln!("error_login_setcookie (in login): {}", e);
  163. crash(get_lang(&req),"error_login_setcookie")
  164. })?;
  165. // if the user is already logged in, skip the login process
  166. // we don't care if someone edits their cookies, Nextcloud will properly
  167. // check them anyway
  168. if let Some(nc_username) = is_logged_in(&req) {
  169. if nc_username.contains(&format!("nc_username={}", formdata.nc_username)) {
  170. return Ok(web_redir("/apps/forms").await.map_err(|e| {
  171. eprintln!("error_redirect (1:/apps/forms/): {}", e);
  172. crash(get_lang(&req), "error_redirect")
  173. })?);
  174. }
  175. }
  176. // try to log the user in with DB data, then redirect.
  177. login(&client, &req, &formdata.nc_username, &formdata.nc_password).await
  178. }
  179. // creates a NC account using a random name and password.
  180. // the account gets associated with a token in sqlite DB.
  181. // POST /link route
  182. pub async fn forward_register(
  183. req: HttpRequest,
  184. s: Session,
  185. csrf_post: web::Form<CsrfToken>,
  186. client: web::Data<Client>,
  187. dbpool: web::Data<DbPool>,
  188. ) -> Result<HttpResponse, TrainCrash> {
  189. let lang = get_lang(&req);
  190. // do not check for existing admin tokens and force a new registration
  191. // check if the csrf token is OK
  192. let cookie_csrf_token = s.get::<String>("sncf_csrf_token").map_err(|e| {
  193. eprintln!("error_csrf_cookie: {}", e);
  194. crash(get_lang(&req), "error_csrf_cookie")
  195. })?;
  196. if let Some(cookie_token) = cookie_csrf_token {
  197. let raw_ctoken =
  198. base64::decode_config(cookie_token.as_bytes(), base64::URL_SAFE_NO_PAD).map_err(
  199. |e| {
  200. eprintln!("error_csrf_cookie (base64): {}", e);
  201. crash(get_lang(&req), "error_csrf_cookie")
  202. },
  203. )?;
  204. let raw_token =
  205. base64::decode_config(csrf_post.csrf_token.as_bytes(), base64::URL_SAFE_NO_PAD)
  206. .map_err(|e| {
  207. eprintln!("error_csrf_token (base64): {}", e);
  208. crash(get_lang(&req), "error_csrf_token")
  209. })?;
  210. let seed = AesGcmCsrfProtection::from_key(get_csrf_key());
  211. let parsed_token = seed.parse_token(&raw_token).expect("error: token not parsed");
  212. let parsed_cookie = seed.parse_cookie(&raw_ctoken).expect("error: cookie not parsed");
  213. if !seed.verify_token_pair(&parsed_token, &parsed_cookie) {
  214. debug("warn: CSRF token doesn't match.");
  215. return Err(crash(lang, "error_csrf_token"));
  216. }
  217. } else {
  218. debug("warn: missing CSRF token.");
  219. return Err(crash(lang, "error_csrf_cookie"));
  220. }
  221. let nc_username = gen_name();
  222. println!("gen_name: {}", nc_username);
  223. let nc_password = gen_token(45);
  224. // attempts to create the account
  225. create_account(&client, &nc_username, &nc_password, lang.clone()).await?;
  226. debug(&format!("Created user {}", nc_username));
  227. let conn = dbpool.get().map_err(|e| {
  228. eprintln!("error_forwardregister_pool: {}", e);
  229. crash(lang.clone(), "error_forwardregister_pool")
  230. })?;
  231. let token = gen_token(45);
  232. let token_mv = token.clone();
  233. // store the result in DB
  234. let form_result = web::block(move || {
  235. Form::insert(
  236. InsertableForm {
  237. created_at: Utc::now().naive_utc(),
  238. lastvisit_at: Utc::now().naive_utc(),
  239. token: token_mv,
  240. nc_username,
  241. nc_password,
  242. },
  243. &conn,
  244. )
  245. })
  246. .await;
  247. if form_result.is_err() {
  248. return Err(crash(lang, "error_forwardregister_db"));
  249. }
  250. s.set("sncf_admin_token", &token).map_err(|e| {
  251. eprintln!("error_login_setcookie (in register): {}", e);
  252. crash(lang.clone(), "error_login_setcookie")
  253. })?;
  254. Ok(HttpResponse::Ok()
  255. .content_type("text/html")
  256. .body(
  257. TplLink {
  258. lang: &lang,
  259. admin_token: &token,
  260. config: &CONFIG,
  261. }
  262. .render()
  263. .map_err(|e| {
  264. eprintln!("error_tplrender (TplLink): {}", e);
  265. crash(lang.clone(), "error_tplrender")
  266. })?,
  267. )
  268. .await
  269. .map_err(|e| {
  270. eprintln!("error_tplrender_resp (TplLink): {}", e);
  271. crash(lang, "error_tplrender_resp")
  272. })?)
  273. }
  274. // create a new query destined to the nextcloud instance
  275. // needed to forward any query
  276. fn forge_from(
  277. route: &str,
  278. req: &HttpRequest,
  279. url: &web::Data<Url>,
  280. client: &web::Data<Client>,
  281. ) -> ClientRequest {
  282. let mut new_url = url.get_ref().clone();
  283. new_url.set_path(route);
  284. new_url.set_query(req.uri().query());
  285. // insert forwarded header if we can
  286. let mut forwarded_req = client
  287. .request_from(new_url.as_str(), req.head())
  288. .timeout(Duration::new(PROXY_TIMEOUT, 0));
  289. // attempt to remove basic-auth header
  290. forwarded_req.headers_mut().remove("authorization");
  291. if let Some(addr) = req.head().peer_addr {
  292. forwarded_req.header("x-forwarded-for", format!("{}", addr.ip()))
  293. } else {
  294. forwarded_req
  295. }
  296. }
  297. fn web_redir(location: &str) -> HttpResponse {
  298. HttpResponse::SeeOther()
  299. .header(http::header::LOCATION, location)
  300. .finish()
  301. }
  302. pub async fn index(req: HttpRequest, s: Session) -> Result<HttpResponse, TrainCrash> {
  303. let seed = AesGcmCsrfProtection::from_key(get_csrf_key());
  304. let (csrf_token, csrf_cookie) = seed
  305. .generate_token_pair(None, 43200)
  306. .expect("couldn't generate token/cookie pair");
  307. s.set("sncf_csrf_token", &base64::encode_config(&csrf_cookie.value(), base64::URL_SAFE_NO_PAD)).map_err(|e| {
  308. eprintln!("error_login_setcookie (in index): {}", e);
  309. crash(get_lang(&req), "error_login_setcookie")
  310. })?;
  311. let cookie_admin_token = s.get::<String>("sncf_admin_token").map_err(|e| {
  312. eprintln!("error_forwardregister_tokenparse (index): {}", e);
  313. crash(get_lang(&req), "error_forwardregister_tokenparse")
  314. })?;
  315. Ok(HttpResponse::Ok()
  316. .content_type("text/html")
  317. .body(
  318. TplIndex {
  319. lang: &get_lang(&req),
  320. csrf_token: &base64::encode_config(&csrf_token.value(), base64::URL_SAFE_NO_PAD),
  321. sncf_admin_token: cookie_admin_token,
  322. }
  323. .render()
  324. .map_err(|e| {
  325. eprintln!("error_tplrender (TplIndex): {}", e);
  326. crash(get_lang(&req), "error_tplrender")
  327. })?,
  328. )
  329. .await
  330. .map_err(|e| {
  331. eprintln!("error_tplrender_resp (TplIndex): {}", e);
  332. crash(get_lang(&req), "error_tplrender_resp")
  333. })?)
  334. }