Search on legal documents using Tensorflow and a web_actix web interface
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.

390 lines
13 KiB

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