Browse Source

update email function and forward.rs regarding new version of sncf

master
alpcentaur 3 years ago
parent
commit
501086e1cd
1 changed files with 127 additions and 166 deletions
  1. +127
    -166
      docker/build/deb-rust-sncf/forward.rs

+ 127
- 166
docker/build/deb-rust-sncf/forward.rs View File

@ -1,14 +1,14 @@
use actix_web::client::{Client, ClientRequest}; use actix_web::client::{Client, ClientRequest};
use actix_web::{http, web, HttpRequest, HttpResponse}; use actix_web::{http, web, HttpRequest, HttpResponse};
use actix_session::Session;
use askama::Template; use askama::Template;
use chrono::Utc; use chrono::Utc;
use regex::Regex;
use csrf::{AesGcmCsrfProtection, CsrfProtection};
use std::time::Duration; use std::time::Duration;
use url::Url; use url::Url;
use csrf::{AesGcmCsrfProtection, CsrfProtection};
use crate::config::get_csrf_key;
use crate::account::*; use crate::account::*;
use crate::config::get_csrf_key;
use crate::config::PAYLOAD_LIMIT; use crate::config::PAYLOAD_LIMIT;
use crate::config::PROXY_TIMEOUT; use crate::config::PROXY_TIMEOUT;
use crate::database::methods::InsertableForm; use crate::database::methods::InsertableForm;
@ -27,51 +27,41 @@ pub async fn forward(
client: web::Data<Client>, client: web::Data<Client>,
) -> Result<HttpResponse, TrainCrash> { ) -> Result<HttpResponse, TrainCrash> {
let route = req.uri().path(); let route = req.uri().path();
if route == "/link/email" {
let email_request_headers = &req;
let email_body = &body;
//let mut body = String::new();
let forged_emailheaders = format!(
"{:?}",
email_request_headers
);
let forged_emailbody = format!(
"{:?}",
email_body
);
//let body = email_response_body.escape_ascii().to_string();
/*email_response_body.read_to_string(&mut body)?;*/
// does not work, because body is in bytes format.
println!("da budy {}",forged_emailbody );
println!("da headers {}",forged_emailheaders);
use std::io::Write;
use std::fs::OpenOptions;
let mut f = OpenOptions::new()
.append(true)
.create(true) // Optionally create the file if it doesn't already exist
.open("/tmp/foo")
.expect("Unable to open file");
//let body_bytes = as_bytes!("{:?}", body);
f.write_all(forged_emailheaders.as_bytes()).expect("Unable to write data");
f.write_all(forged_emailbody.as_bytes()).expect("Unable to write data");
if route == "/link/email" {
//let email_body = &body;
//let mut body = String::new();
//let forged_emailbody = format!(
// "{:?}",
// email_body
// );
//let body = email_response_body.escape_ascii().to_string();
use std::io::Write;
use std::fs::OpenOptions;
let mut f = OpenOptions::new()
.append(true)
.create(true) // Optionally create the file if it doesn't already exist
.open("/var/tokmails/tuples.csv")
.expect("Unable to open file");
//f.write_all(forged_emailheaders.as_bytes()).expect("Unable to write data");
////f.write_all(forged_emailbody.as_bytes()).expect("Unable to write data");
f.write_all(&body).expect("Unable to write data");
}
}
// if check_route returns true, // if check_route returns true,
// the user supposedly tried to access a restricted page. // the user supposedly tried to access a restricted page.
// They get redirected to the main page. // They get redirected to the main page.
if check_route(route) {
if route.starts_with("/apps/files") {
// exception for /apps/files: always redirect to /apps/forms
debug(&format!("Files route blocked: {}", route));
return Ok(web_redir("/apps/forms").await.map_err(|e| {
eprintln!("error_redirect: {}", e);
crash(get_lang(&req), "error_redirect")
})?);
} else if check_route(route) {
debug(&format!("Restricted route blocked: {}", route)); debug(&format!("Restricted route blocked: {}", route));
return Ok(web_redir("/").await.map_err(|e| { return Ok(web_redir("/").await.map_err(|e| {
eprintln!("error_redirect: {}", e); eprintln!("error_redirect: {}", e);
@ -85,8 +75,8 @@ pub async fn forward(
// (prevents the user from sending some specific POST requests) // (prevents the user from sending some specific POST requests)
if check_request(route, &body) { if check_request(route, &body) {
debug(&format!( debug(&format!(
"Restricted request: {}",
String::from_utf8_lossy(&body)
"Restricted request: {}",
String::from_utf8_lossy(&body)
)); ));
return Err(crash(get_lang(&req), "error_dirtyhacker")); return Err(crash(get_lang(&req), "error_dirtyhacker"));
} }
@ -103,11 +93,12 @@ pub async fn forward(
// and basic-auth, because this feature is not needed. // and basic-auth, because this feature is not needed.
for (header_name, header_value) in res for (header_name, header_value) in res
.headers() .headers()
.iter()
.filter(|(h, _)| *h != "connection" && *h != "content-encoding")
.iter()
.filter(|(h, _)| *h != "connection" && *h != "content-encoding")
{ {
client_resp.header(header_name.clone(), header_value.clone()); client_resp.header(header_name.clone(), header_value.clone());
} }
// sparing the use of a mutable body when not needed // sparing the use of a mutable body when not needed
// For now, the body only needs to be modified when the route // For now, the body only needs to be modified when the route
// is "create a new form" route // is "create a new form" route
@ -123,8 +114,8 @@ pub async fn forward(
let form_id = check_new_form(&response_body); let form_id = check_new_form(&response_body);
if form_id > 0 { if form_id > 0 {
debug(&format!( debug(&format!(
"New form. Forging request to set isAnonymous for id {}",
form_id
"New form. Forging request to set isAnonymous for id {}",
form_id
)); ));
let forged_body = format!( let forged_body = format!(
@ -137,8 +128,8 @@ pub async fn forward(
&url, &url,
&client, &client,
) )
.set_header("content-length", forged_body.len())
.set_header("content-type", "application/json;charset=utf-8");
.set_header("content-length", forged_body.len())
.set_header("content-type", "application/json;charset=utf-8");
let res = update_req.send_body(forged_body).await.map_err(|e| { let res = update_req.send_body(forged_body).await.map_err(|e| {
eprintln!("error_forward_isanon: {}", e); eprintln!("error_forward_isanon: {}", e);
@ -161,8 +152,8 @@ pub async fn forward(
// check the response before returning it (unused) // check the response before returning it (unused)
/*if check_response(route, &response_body) { /*if check_response(route, &response_body) {
return Ok(web_redir("/"));
}*/
return Ok(web_redir("/"));
}*/
} }
#[derive(Deserialize)] #[derive(Deserialize)]
@ -177,17 +168,11 @@ pub struct CsrfToken {
pub async fn forward_login( pub async fn forward_login(
req: HttpRequest, req: HttpRequest,
s: Session,
params: web::Path<LoginToken>, params: web::Path<LoginToken>,
client: web::Data<Client>, client: web::Data<Client>,
dbpool: web::Data<DbPool>, dbpool: web::Data<DbPool>,
) -> Result<HttpResponse, TrainCrash> { ) -> Result<HttpResponse, TrainCrash> {
// if the user is already logged in, redirect to the Forms app
if is_logged_in(&req).is_some() {
return Ok(web_redir("/apps/forms").await.map_err(|e| {
eprintln!("error_redirect (1:/apps/forms/): {}", e);
crash(get_lang(&req), "error_redirect")
})?);
}
// check if the provided token seems valid. If not, early return. // check if the provided token seems valid. If not, early return.
if !check_token(&params.token) { if !check_token(&params.token) {
@ -201,6 +186,7 @@ pub async fn forward_login(
crash(get_lang(&req), "error_forwardlogin_db") crash(get_lang(&req), "error_forwardlogin_db")
})?; })?;
let moved_token = params.token.clone();
// check if the link exists in DB. if it does, update lastvisit_at. // check if the link exists in DB. if it does, update lastvisit_at.
let formdata = web::block(move || Form::get_from_token(&params.token, &conn)) let formdata = web::block(move || Form::get_from_token(&params.token, &conn))
.await .await
@ -208,109 +194,76 @@ pub async fn forward_login(
eprintln!("error_forwardlogin_db_get (diesel error): {}", e); eprintln!("error_forwardlogin_db_get (diesel error): {}", e);
crash(get_lang(&req), "error_forwardlogin_db_get") crash(get_lang(&req), "error_forwardlogin_db_get")
})? })?
.ok_or_else(|| {
debug("Token not found.");
crash(get_lang(&req), "error_forwardlogin_notfound")
})?;
.ok_or_else(|| {
debug("error: Token not found.");
crash(get_lang(&req), "error_forwardlogin_notfound")
})?;
// copy the token in cookies.
s.set("sncf_admin_token", &moved_token).map_err(|e| {
eprintln!("error_login_setcookie (in login): {}", e);
crash(get_lang(&req),"error_login_setcookie")
})?;
// if the user is already logged in, skip the login process
// we don't care if someone edits their cookies, Nextcloud will properly
// check them anyway
if let Some(nc_username) = is_logged_in(&req) {
if nc_username.contains(&format!("nc_username={}", formdata.nc_username)) {
return Ok(web_redir("/apps/forms").await.map_err(|e| {
eprintln!("error_redirect (1:/apps/forms/): {}", e);
crash(get_lang(&req), "error_redirect")
})?);
}
}
// else, try to log the user in with DB data, then redirect.
// try to log the user in with DB data, then redirect.
login(&client, &req, &formdata.nc_username, &formdata.nc_password).await login(&client, &req, &formdata.nc_username, &formdata.nc_password).await
} }
// creates a NC account using a random name and password. // creates a NC account using a random name and password.
// the account gets associated with a token in sqlite DB. // the account gets associated with a token in sqlite DB.
// POST /link route
pub async fn forward_register( pub async fn forward_register(
req: HttpRequest, req: HttpRequest,
s: Session,
csrf_post: web::Form<CsrfToken>, csrf_post: web::Form<CsrfToken>,
client: web::Data<Client>, client: web::Data<Client>,
dbpool: web::Data<DbPool>, dbpool: web::Data<DbPool>,
) -> Result<HttpResponse, TrainCrash> { ) -> Result<HttpResponse, TrainCrash> {
let lang = get_lang(&req); let lang = get_lang(&req);
// if the user is already logged in, redirect to the Forms app
if is_logged_in(&req).is_some() {
return Ok(web_redir("/apps/forms").await.map_err(|e| {
eprintln!("error_redirect (2:/apps/forms/): {}", e);
crash(get_lang(&req), "error_redirect")
})?);
}
// if the user has already generated an admin token, redirect too
if let Some(token) = has_admintoken(&req) {
lazy_static! {
static ref RE: Regex = Regex::new(r#"sncf_admin_token=(?P<token>[0-9A-Za-z_\-]*)"#)
.expect("Error while parsing the sncf_admin_token regex");
}
let admin_token = RE
.captures(&token)
.ok_or_else(|| {
eprintln!("error_forwardregister_tokenparse (no capture)");
crash(get_lang(&req), "error_forwardregister_tokenparse")
})?
.name("token")
.ok_or_else(|| {
eprintln!("error_forwardregister_tokenparse (no capture named token)");
crash(get_lang(&req), "error_forwardregister_tokenparse")
})?
.as_str();
// sanitize the token beforehand, cookies are unsafe
if check_token(&admin_token) {
return Ok(
web_redir(&format!("{}/admin/{}", CONFIG.sncf_url, &admin_token))
.await
.map_err(|e| {
eprintln!("error_redirect (admin): {}", e);
crash(get_lang(&req), "error_redirect")
})?,
);
} else {
debug("Incorrect admin token given in cookies.");
debug(&format!("Token: {:#?}", &admin_token));
return Err(crash(lang, "error_dirtyhacker"));
}
}
// do not check for existing admin tokens and force a new registration
// check if the csrf token is OK // check if the csrf token is OK
if let Some(cookie_token) = has_csrftoken(&req) {
lazy_static! {
static ref RE: Regex = Regex::new(r#"sncf_csrf_cookie=(?P<token>[0-9A-Za-z_\-]*)"#)
.expect("Error while parsing the sncf_csrf_cookie regex");
}
let cookie_csrf_token = RE
.captures(&cookie_token)
.ok_or_else(|| {
eprintln!("error_csrf_cookie: no capture");
crash(get_lang(&req), "error_csrf_cookie")
})?
.name("token")
.ok_or_else(|| {
eprintln!("error_csrf_cookie: no capture named token");
crash(get_lang(&req), "error_csrf_cookie")
})?
.as_str();
let raw_ctoken = base64::decode_config(cookie_csrf_token.as_bytes(), base64::URL_SAFE_NO_PAD).map_err(|e| {
eprintln!("error_csrf_cookie (base64): {}", e);
crash(get_lang(&req), "error_csrf_cookie")
})?;
let raw_token = base64::decode_config(csrf_post.csrf_token.as_bytes(), base64::URL_SAFE_NO_PAD).map_err(|e| {
eprintln!("error_csrf_token (base64): {}", e);
crash(get_lang(&req), "error_csrf_token")
})?;
let cookie_csrf_token = s.get::<String>("sncf_csrf_token").map_err(|e| {
eprintln!("error_csrf_cookie: {}", e);
crash(get_lang(&req), "error_csrf_cookie")
})?;
if let Some(cookie_token) = cookie_csrf_token {
let raw_ctoken =
base64::decode_config(cookie_token.as_bytes(), base64::URL_SAFE_NO_PAD).map_err(
|e| {
eprintln!("error_csrf_cookie (base64): {}", e);
crash(get_lang(&req), "error_csrf_cookie")
},
)?;
let raw_token =
base64::decode_config(csrf_post.csrf_token.as_bytes(), base64::URL_SAFE_NO_PAD)
.map_err(|e| {
eprintln!("error_csrf_token (base64): {}", e);
crash(get_lang(&req), "error_csrf_token")
})?;
let seed = AesGcmCsrfProtection::from_key(get_csrf_key()); let seed = AesGcmCsrfProtection::from_key(get_csrf_key());
let parsed_token = seed.parse_token(&raw_token).expect("token not parsed");
let parsed_cookie = seed.parse_cookie(&raw_ctoken).expect("cookie not parsed");
let parsed_token = seed.parse_token(&raw_token).expect("error: token not parsed");
let parsed_cookie = seed.parse_cookie(&raw_ctoken).expect("error: cookie not parsed");
if !seed.verify_token_pair(&parsed_token, &parsed_cookie) { if !seed.verify_token_pair(&parsed_token, &parsed_cookie) {
debug("warn: CSRF token doesn't match."); debug("warn: CSRF token doesn't match.");
return Err(crash(lang, "error_csrf_token")); return Err(crash(lang, "error_csrf_token"));
} }
}
else {
} else {
debug("warn: missing CSRF token."); debug("warn: missing CSRF token.");
return Err(crash(lang, "error_csrf_cookie")); return Err(crash(lang, "error_csrf_cookie"));
} }
@ -333,39 +286,41 @@ pub async fn forward_register(
let token_mv = token.clone(); let token_mv = token.clone();
// store the result in DB // store the result in DB
let form_result = web::block(move || Form::insert(
InsertableForm {
created_at: Utc::now().naive_utc(),
lastvisit_at: Utc::now().naive_utc(),
token: token_mv,
nc_username,
nc_password,
},
&conn,
))
let form_result = web::block(move || {
Form::insert(
InsertableForm {
created_at: Utc::now().naive_utc(),
lastvisit_at: Utc::now().naive_utc(),
token: token_mv,
nc_username,
nc_password,
},
&conn,
)
})
.await; .await;
if form_result.is_err() { if form_result.is_err() {
return Err(crash(lang, "error_forwardregister_db")); return Err(crash(lang, "error_forwardregister_db"));
} }
s.set("sncf_admin_token", &token).map_err(|e| {
eprintln!("error_login_setcookie (in register): {}", e);
crash(lang.clone(), "error_login_setcookie")
})?;
Ok(HttpResponse::Ok() Ok(HttpResponse::Ok()
.content_type("text/html") .content_type("text/html")
.set_header(
"Set-Cookie",
format!("sncf_admin_token={}; HttpOnly; SameSite=Strict", &token),
)
.body( .body(
TplLink { TplLink {
lang: &lang, lang: &lang,
admin_token: &token, admin_token: &token,
config: &CONFIG, config: &CONFIG,
}
.render()
.map_err(|e| {
eprintln!("error_tplrender (TplLink): {}", e);
crash(lang.clone(), "error_tplrender")
})?,
}
.render()
.map_err(|e| {
eprintln!("error_tplrender (TplLink): {}", e);
crash(lang.clone(), "error_tplrender")
})?,
) )
.await .await
.map_err(|e| { .map_err(|e| {
@ -406,22 +361,28 @@ fn web_redir(location: &str) -> HttpResponse {
.finish() .finish()
} }
pub async fn index(req: HttpRequest) -> Result<HttpResponse, TrainCrash> {
pub async fn index(req: HttpRequest, s: Session) -> Result<HttpResponse, TrainCrash> {
let seed = AesGcmCsrfProtection::from_key(get_csrf_key()); let seed = AesGcmCsrfProtection::from_key(get_csrf_key());
let (csrf_token, csrf_cookie) = seed.generate_token_pair(None, 43200)
let (csrf_token, csrf_cookie) = seed
.generate_token_pair(None, 43200)
.expect("couldn't generate token/cookie pair"); .expect("couldn't generate token/cookie pair");
s.set("sncf_csrf_token", &base64::encode_config(&csrf_cookie.value(), base64::URL_SAFE_NO_PAD)).map_err(|e| {
eprintln!("error_login_setcookie (in index): {}", e);
crash(get_lang(&req), "error_login_setcookie")
})?;
let cookie_admin_token = s.get::<String>("sncf_admin_token").map_err(|e| {
eprintln!("error_forwardregister_tokenparse (index): {}", e);
crash(get_lang(&req), "error_forwardregister_tokenparse")
})?;
Ok(HttpResponse::Ok() Ok(HttpResponse::Ok()
.content_type("text/html") .content_type("text/html")
.set_header(
"Set-Cookie",
format!("sncf_csrf_cookie={}; HttpOnly; SameSite=Strict",
base64::encode_config(&csrf_cookie.value(), base64::URL_SAFE_NO_PAD)))
.body( .body(
TplIndex { TplIndex {
lang: &get_lang(&req), lang: &get_lang(&req),
csrf_token: &base64::encode_config(&csrf_token.value(), base64::URL_SAFE_NO_PAD), csrf_token: &base64::encode_config(&csrf_token.value(), base64::URL_SAFE_NO_PAD),
sncf_admin_token: cookie_admin_token,
} }
.render() .render()
.map_err(|e| { .map_err(|e| {

Loading…
Cancel
Save