use crate::database::Database;
use crate::model::{DatabaseError, ProfileCreate, ProfileLogin, TokenContext};
use axum::http::{HeaderMap, HeaderValue};
use hcaptcha_no_wasm::Hcaptcha;
use databeam::DefaultReturn;
use axum::response::IntoResponse;
use axum::{
extract::{Query, State},
Json,
};
use axum_extra::extract::cookie::CookieJar;
pub async fn create_request(
jar: CookieJar,
headers: HeaderMap,
State(database): State<Database>,
Json(props): Json<ProfileCreate>,
) -> impl IntoResponse {
if let Some(_) = jar.get("__Secure-Token") {
return (
HeaderMap::new(),
serde_json::to_string(&DefaultReturn {
success: false,
message: DatabaseError::NotAllowed.to_string(),
payload: (),
})
.unwrap(),
);
}
let real_ip = if let Some(ref real_ip_header) = database.config.real_ip_header {
headers
.get(real_ip_header.to_owned())
.unwrap_or(&HeaderValue::from_static(""))
.to_str()
.unwrap_or("")
.to_string()
} else {
String::new()
};
if database.get_ipban_by_ip(real_ip.clone()).await.is_ok() {
return (
HeaderMap::new(),
serde_json::to_string(&DefaultReturn {
success: false,
message: DatabaseError::NotAllowed.to_string(),
payload: (),
})
.unwrap(),
);
}
let res = match database.create_profile(props, real_ip).await {
Ok(r) => r,
Err(e) => {
return (
HeaderMap::new(),
serde_json::to_string(&DefaultReturn {
success: false,
message: e.to_string(),
payload: (),
})
.unwrap(),
);
}
};
let mut headers = HeaderMap::new();
headers.insert(
"Set-Cookie",
format!(
"__Secure-Token={}; SameSite=Lax; Secure; Path=/; HostOnly=true; HttpOnly=true; Max-Age={}",
res,
60* 60 * 24 * 365
)
.parse()
.unwrap(),
);
(
headers,
serde_json::to_string(&DefaultReturn {
success: true,
message: res.clone(),
payload: (),
})
.unwrap(),
)
}
pub async fn login_request(
headers: HeaderMap,
State(database): State<Database>,
Json(props): Json<ProfileLogin>,
) -> impl IntoResponse {
if let Err(e) = props
.valid_response(&database.config.captcha.secret, None)
.await
{
return (
HeaderMap::new(),
serde_json::to_string(&DefaultReturn {
success: false,
message: e.to_string(),
payload: (),
})
.unwrap(),
);
}
let mut ua = match database
.get_profile_by_username(props.username.clone())
.await
{
Ok(ua) => ua,
Err(e) => {
return (
HeaderMap::new(),
serde_json::to_string(&DefaultReturn {
success: false,
message: e.to_string(),
payload: (),
})
.unwrap(),
)
}
};
let input_password = rainbeam_shared::hash::hash_salted(props.password.clone(), ua.salt);
if input_password != ua.password {
return (
HeaderMap::new(),
serde_json::to_string(&DefaultReturn {
success: false,
message: DatabaseError::NotAllowed.to_string(),
payload: (),
})
.unwrap(),
);
}
let real_ip = if let Some(ref real_ip_header) = database.config.real_ip_header {
headers
.get(real_ip_header.to_owned())
.unwrap_or(&HeaderValue::from_static(""))
.to_str()
.unwrap_or("")
.to_string()
} else {
String::new()
};
if database.get_ipban_by_ip(real_ip.clone()).await.is_ok() {
return (
HeaderMap::new(),
serde_json::to_string(&DefaultReturn {
success: false,
message: DatabaseError::NotAllowed.to_string(),
payload: (),
})
.unwrap(),
);
}
let token = databeam::utility::uuid();
let token_hashed = databeam::utility::hash(token.clone());
ua.tokens.push(token_hashed);
ua.ips.push(real_ip);
ua.token_context.push(TokenContext::default());
database
.update_profile_tokens(props.username.clone(), ua.tokens, ua.ips, ua.token_context)
.await
.unwrap();
let mut headers = HeaderMap::new();
headers.insert(
"Set-Cookie",
format!(
"__Secure-Token={}; SameSite=Lax; Secure; Path=/; HostOnly=true; HttpOnly=true; Max-Age={}",
token,
60* 60 * 24 * 365
)
.parse()
.unwrap(),
);
(
headers,
serde_json::to_string(&DefaultReturn {
success: true,
message: token,
payload: (),
})
.unwrap(),
)
}
#[derive(serde::Deserialize)]
pub struct CallbackQueryProps {
pub uid: String, }
pub async fn callback_request(Query(params): Query<CallbackQueryProps>) -> impl IntoResponse {
(
[
("Content-Type".to_string(), "text/html".to_string()),
(
"Set-Cookie".to_string(),
format!(
"__Secure-Token={}; SameSite=Lax; Secure; Path=/; HostOnly=true; HttpOnly=true; Max-Age={}",
params.uid,
60 * 60 * 24 * 365
),
),
],
"<head>
<meta http-equiv=\"Refresh\" content=\"0; URL=/\" />
</head>"
)
}
pub async fn logout_request(jar: CookieJar) -> impl IntoResponse {
if let Some(_) = jar.get("__Secure-Token") {
return (
[
("Content-Type".to_string(), "text/plain".to_string()),
(
"Set-Cookie".to_string(),
"__Secure-Token=refresh; SameSite=Strict; Secure; Path=/; HostOnly=true; HttpOnly=true; Max-Age=0".to_string(),
) ],
"You have been signed out. You can now close this tab.",
);
}
(
[
("Content-Type".to_string(), "text/plain".to_string()),
("Set-Cookie".to_string(), String::new()),
],
"Failed to sign out of account.",
)
}
pub async fn remove_tag(jar: CookieJar) -> impl IntoResponse {
if let Some(_) = jar.get("__Secure-Token") {
return (
[
("Content-Type".to_string(), "text/plain".to_string()),
(
"Set-Cookie2".to_string(),
"__Secure-Question-Tag=refresh; SameSite=Lax; Secure; Path=/; HostOnly=true; HttpOnly=true; Max-Age=0".to_string()
)
],
"You have been signed out. You can now close this tab.",
);
}
(
[
("Content-Type".to_string(), "text/plain".to_string()),
("Set-Cookie".to_string(), String::new()),
],
"Failed to remove tag.",
)
}