use askama::Template;
use bytes::Bytes;
use ordinary_auth::AuthClient;
use ordinary_types::{Kind, flexbuffer_reader_to_json, json_to_flexbuffer_vec};
use scramblr::*;
use serde::Serialize;
use std::error::Error;
use wasm_bindgen::prelude::*;
mod filters {
#[askama::filter_fn]
pub fn md_to_html(s: &str, _: &dyn askama::Values) -> askama::Result<String> {
Ok(markdown::to_html_with_options(
s,
&markdown::Options {
parse: markdown::ParseOptions {
constructs: markdown::Constructs {
math_flow: true,
math_text: true,
..markdown::Constructs::gfm()
},
..markdown::ParseOptions::gfm()
},
compile: markdown::CompileOptions {
..markdown::CompileOptions::gfm()
},
},
)
.expect("failed to parse markdown"))
}
#[askama::filter_fn]
pub fn timestamp_s_to_rfc_2822(
timestamp_s: &i64,
_: &dyn askama::Values,
) -> askama::Result<String> {
if let Some(date_time) = chrono::DateTime::from_timestamp(*timestamp_s, 0) {
Ok(date_time.to_rfc2822())
} else {
Err(askama::Error::Custom(
"failed to get timestamp".to_string().into(),
))
}
}
fn inner_timestamp_s_to_formatted(timestamp_s: &i64, format: &str) -> askama::Result<String> {
if let Some(date_time) = chrono::DateTime::from_timestamp(*timestamp_s, 0) {
let formatted = date_time.format(format);
let mut string = String::new();
if formatted.write_to(&mut string).is_ok() {
Ok(string)
} else {
Err(askama::Error::Custom(
"failed to write formatted to string".to_string().into(),
))
}
} else {
Err(askama::Error::Custom(
"failed to get timestamp".to_string().into(),
))
}
}
#[askama::filter_fn]
pub fn timestamp_s_to_formatted(
timestamp_s: &i64,
_: &dyn askama::Values,
format: &str,
) -> askama::Result<String> {
inner_timestamp_s_to_formatted(timestamp_s, format)
}
#[askama::filter_fn]
pub fn uuid_str(bytes: &[u8; 16], _: &dyn askama::Values) -> askama::Result<String> {
Ok(uuid::Uuid::from_bytes(*bytes).to_string())
}
#[askama::filter_fn]
pub fn uuid_to_formatted(
bytes: &[u8; 16],
_: &dyn askama::Values,
format: &str,
) -> askama::Result<String> {
let uuid = uuid::Uuid::from_bytes(*bytes);
match uuid.get_timestamp() {
Some(ts) => inner_timestamp_s_to_formatted(&(ts.to_unix().0 as i64), format),
None => Ok(uuid.to_string()),
}
}
}
#[wasm_bindgen]
pub struct RegistrationStartReq {
client_state: Vec<u8>,
request: Vec<u8>,
}
#[wasm_bindgen]
impl RegistrationStartReq {
#[wasm_bindgen(getter)]
pub fn client_state(&self) -> Vec<u8> {
self.client_state.clone()
}
#[wasm_bindgen(getter)]
pub fn request(&self) -> Vec<u8> {
self.request.clone()
}
}
#[wasm_bindgen]
pub fn registration_start_req(
account: String,
password: Vec<u8>,
invite_token: Option<Vec<u8>>,
) -> Result<RegistrationStartReq, String> {
match AuthClient::registration_start_req(
account.as_bytes(),
&password,
invite_token.map(|t| Bytes::copy_from_slice(&t[..])),
) {
Ok((client_state, request)) => Ok(RegistrationStartReq {
client_state,
request: request.to_vec(),
}),
Err(err) => Err(err.to_string()),
}
}
#[wasm_bindgen]
pub struct RegistrationFinishReq {
private_key: Vec<u8>,
request: Vec<u8>,
}
#[wasm_bindgen]
impl RegistrationFinishReq {
#[wasm_bindgen(getter)]
pub fn private_key(&self) -> Vec<u8> {
self.private_key.clone()
}
#[wasm_bindgen(getter)]
pub fn request(&self) -> Vec<u8> {
self.request.clone()
}
}
#[wasm_bindgen]
pub fn registration_finish_req(
account: String,
password: Vec<u8>,
client_state: Vec<u8>,
server_message: Vec<u8>,
) -> Result<RegistrationFinishReq, String> {
match AuthClient::registration_finish_req(
account.as_bytes(),
&password,
&client_state,
&server_message,
) {
Ok((private_key, request)) => Ok(RegistrationFinishReq {
private_key: private_key.to_vec(),
request: request.to_vec(),
}),
Err(err) => Err(err.to_string()),
}
}
#[wasm_bindgen]
pub fn decrypt_totp_mfa(
response: Vec<u8>,
private_key: Vec<u8>,
app_name: String,
account: String,
) -> Result<String, String> {
let private_key: [u8; 32] = match private_key.try_into() {
Ok(key) => key,
Err(_) => return Err("private key is not 32 bytes".into()),
};
match AuthClient::decrypt_totp_mfa_to_qr_svg(
&Bytes::from(response),
private_key,
app_name,
account,
) {
Ok((qr_code, recovery_codes)) => Ok(format!("{recovery_codes}__{qr_code}")),
Err(err) => Err(err.to_string()),
}
}
#[wasm_bindgen]
pub struct LoginStartReq {
client_state: Vec<u8>,
request: Vec<u8>,
}
#[wasm_bindgen]
impl LoginStartReq {
#[wasm_bindgen(getter)]
pub fn client_state(&self) -> Vec<u8> {
self.client_state.clone()
}
#[wasm_bindgen(getter)]
pub fn request(&self) -> Vec<u8> {
self.request.clone()
}
}
#[wasm_bindgen]
pub fn login_start_req(account: String, password: Vec<u8>) -> Result<LoginStartReq, String> {
match AuthClient::login_start_req(account.as_bytes(), &password) {
Ok((client_state, request)) => Ok(LoginStartReq {
client_state,
request: request.to_vec(),
}),
Err(err) => Err(err.to_string()),
}
}
#[wasm_bindgen]
pub struct LoginFinishReq {
session_key: Vec<u8>,
request: Vec<u8>,
}
#[wasm_bindgen]
impl LoginFinishReq {
#[wasm_bindgen(getter)]
pub fn session_key(&self) -> Vec<u8> {
self.session_key.clone()
}
#[wasm_bindgen(getter)]
pub fn request(&self) -> Vec<u8> {
self.request.clone()
}
}
#[wasm_bindgen]
pub fn login_finish_req(
account: String,
password: Vec<u8>,
mfa_code: Vec<u8>,
client_state: Vec<u8>,
server_message: Vec<u8>,
client_verifier: Vec<u8>,
) -> Result<LoginFinishReq, String> {
match AuthClient::login_finish_req(
account.as_bytes(),
&password,
&mfa_code,
&client_state,
&server_message,
Some(&client_verifier),
) {
Ok((request, session_key)) => Ok(LoginFinishReq {
session_key,
request: request.to_vec(),
}),
Err(err) => Err(err.to_string()),
}
}
#[wasm_bindgen]
pub fn decrypt_token(response: Vec<u8>, session_key: Vec<u8>) -> Result<Vec<u8>, String> {
match AuthClient::decrypt_token(&Bytes::from(response), &session_key) {
Ok(token) => Ok(token.to_vec()),
Err(err) => Err(err.to_string()),
}
}
#[wasm_bindgen]
pub fn access_get_req(refresh_token: Vec<u8>) -> Result<Vec<u8>, String> {
Ok(AuthClient::access_get_req(&refresh_token).to_vec())
}
fn invoke_req_from_json_and_kind_str(
json_str: &str,
kind_str: &str,
) -> Result<Vec<u8>, Box<dyn Error>> {
let kind: Kind = serde_json::from_str(kind_str)?;
let value: serde_json::Value = serde_json::from_str(json_str)?;
let mut builder = flexbuffers::Builder::new(&flexbuffers::BuilderOptions::SHARE_NONE);
let mut builder_vec = builder.start_vector();
json_to_flexbuffer_vec(&kind, &value, &mut builder_vec)?;
builder_vec.end_vector();
Ok(builder.view().to_vec())
}
#[wasm_bindgen]
pub fn invoke_req(json_str: String, kind_str: String) -> Result<Vec<u8>, String> {
match invoke_req_from_json_and_kind_str(&json_str, &kind_str) {
Ok(val) => Ok(val),
Err(err) => Err(err.to_string()),
}
}
fn invoke_res_from_kind_str(kind_str: &str, payload: Vec<u8>) -> Result<String, Box<dyn Error>> {
let kind: Kind = serde_json::from_str(kind_str)?;
let root = flexbuffers::Reader::get_root(&payload[..])?;
let json_res = flexbuffer_reader_to_json(&kind, &root)?;
Ok(json_res.to_string())
}
#[wasm_bindgen]
pub fn invoke_res(kind_str: String, payload: Vec<u8>) -> Result<String, String> {
match invoke_res_from_kind_str(&kind_str, payload) {
Ok(val) => Ok(val),
Err(err) => Err(err.to_string()),
}
}