use std::{
error::Error,
path::Path,
sync::Arc,
time::{Duration, SystemTime},
};
use bytes::{BufMut, BytesMut};
use cbwaw::{Auth, AuthClient, OsRng};
use ed25519_dalek::{SigningKey, ed25519::signature::SignerMut};
use ordinary_config::{AuthConfig, InviteConfig, InviteMode};
use saferlmdb::EnvBuilder;
use sha2::{Digest, Sha256};
#[test]
fn integration() -> Result<(), Box<dyn Error>> {
let store_dir = Path::new(".ordinary").join("store");
if std::fs::read_dir(&store_dir).is_ok() {
std::fs::remove_dir_all(&store_dir)?;
}
std::fs::create_dir_all(&store_dir)?;
let env = Arc::new(unsafe {
let mut env_builder = EnvBuilder::new()?;
env_builder.set_maxreaders(126)?;
env_builder.set_mapsize(16384 * 64 * 10)?;
env_builder.set_maxdbs(13)?;
env_builder.open(
store_dir.to_str().expect("store dir is not str"),
&saferlmdb::open::Flags::empty(),
0o600,
)?
});
let auth = Arc::new(Auth::new(
"example.com".into(),
None,
[0u8; 32],
env.clone(),
)?);
let (state, req) = AuthClient::registration_start_req(b"account", b"password", None)?;
let res = auth.registration_start(req, None, None)?;
let (private_key, req) =
AuthClient::registration_finish_req(b"account", b"password", &state, &res)?;
let (res, ..) = auth.registration_finish(req, None)?;
let (mfa_code, recovery_codes) = AuthClient::decrypt_totp_mfa_code(
&res,
private_key,
"example.com".into(),
"account".into(),
)?;
let mut recovery_code1 = String::new();
let mut recovery_code2 = String::new();
for (i, c) in recovery_codes.chars().enumerate() {
if i < 11 {
recovery_code1 = format!("{recovery_code1}{c}");
} else if i < 22 {
recovery_code2 = format!("{recovery_code2}{c}");
} else {
break;
}
}
let (state, req) = AuthClient::login_start_req(b"account", b"password")?;
let res = auth.login_start(req)?;
let mut signing_key: SigningKey = SigningKey::generate(&mut OsRng);
let verifying_key = signing_key.verifying_key();
let mut mfa_input = b"example.com".to_vec();
mfa_input.extend_from_slice(b"account");
mfa_input.extend_from_slice(mfa_code.as_bytes());
let mut hasher = Sha256::new();
hasher.update(&mfa_input);
let mfa_hash = hasher.finalize().to_vec();
let (req, session_key) = AuthClient::login_finish_req(
b"account",
b"password",
&mfa_hash,
&state,
&res,
Some(verifying_key.as_bytes()),
)?;
let (res, _, _) = auth.login_finish(req, true)?;
let mut refresh_token: BytesMut = AuthClient::decrypt_token(&res, &session_key)?.into();
let exp = SystemTime::now()
.checked_add(Duration::from_secs(5))
.expect("time to work")
.duration_since(SystemTime::UNIX_EPOCH)?
.as_secs() as u32;
refresh_token.put_u32(exp);
let signature = signing_key.sign(&refresh_token[..]);
refresh_token.put(&signature.to_bytes()[..]);
let mut access_token: BytesMut = auth.access_get(&refresh_token.into())?.into();
let exp = SystemTime::now()
.checked_add(Duration::from_secs(3))
.expect("time to work")
.duration_since(SystemTime::UNIX_EPOCH)?
.as_secs() as u32;
access_token.put_u32(exp);
let signature = signing_key.sign(&access_token[..]);
access_token.put(&signature.to_bytes()[..]);
let (_account, claims) = auth.verify_access_token(&access_token[..])?;
assert_eq!(claims.idx(0).as_vector().idx(0).as_blob().0.len(), 16);
let (state, req) = AuthClient::login_start_req(b"account", b"password")?;
let res = auth.login_start(req)?;
let (req, session_key) =
AuthClient::login_finish_req(b"account", b"password", &mfa_hash, &state, &res, None)?;
let (res, _, _) = auth.login_finish(req, true)?;
let refresh_token = AuthClient::decrypt_token(&res, &session_key)?;
let access_token = auth.access_get(&refresh_token)?;
let (_account, claims) = auth.verify_access_token(&access_token[..])?;
assert_eq!(claims.idx(0).as_vector().idx(0).as_blob().0.len(), 16);
let (state, req) = AuthClient::reset_password_login_start_req(b"account", b"password")?;
let res = auth.reset_password_login_start(req)?;
let (req, session_key) = AuthClient::reset_password_login_finish_req(
b"account",
b"password",
&mfa_hash,
&state,
&res,
None,
)?;
let res = auth.reset_password_login_finish(req)?;
let password_reset_token = AuthClient::decrypt_token(&res, &session_key)?;
let (state, req) = AuthClient::password_reset_registration_start_req(b"account", b"password2")?;
let res = auth.reset_password_registration_start(req, &password_reset_token)?;
let req =
AuthClient::password_reset_registration_finish_req(b"account", b"password2", &state, &res)?;
auth.reset_password_registration_finish(req, &password_reset_token)?;
let (state, req) = AuthClient::login_start_req(b"account", b"password2")?;
let res = auth.login_start(req)?;
let (req, _) =
AuthClient::login_finish_req(b"account", b"password2", &mfa_hash, &state, &res, None)?;
auth.login_finish(req, true)?;
let mut hasher = Sha256::new();
hasher.update(recovery_code1.as_bytes());
let hashed_recovery_code1 = hasher.finalize().to_vec();
let (state, req) =
AuthClient::forgot_password_start_req(b"account", b"password3", &hashed_recovery_code1)?;
let res = auth.forgot_password_start(req)?;
let req = AuthClient::forgot_password_finish_req(
b"account",
b"password3",
&state,
&res,
recovery_code1.as_bytes(),
)?;
auth.forgot_password_finish(req)?;
let (state, req) = AuthClient::login_start_req(b"account", b"password3")?;
let res = auth.login_start(req)?;
let (req, _) =
AuthClient::login_finish_req(b"account", b"password3", &mfa_hash, &state, &res, None)?;
auth.login_finish(req, true)?;
let (state, req) = AuthClient::reset_totp_mfa_start_req(b"account", b"password3")?;
let res = auth.reset_totp_mfa_start(req)?;
let (req, session_key) =
AuthClient::reset_totp_mfa_finish_req(b"account", b"password3", &mfa_hash, &state, &res)?;
let res = auth.reset_totp_mfa_finish(req)?;
let mfa_code2 = AuthClient::decrypt_reset_totp_mfa_code(
&res,
&session_key,
"example.com".into(),
"account".into(),
)?;
let mut mfa_input2 = b"example.com".to_vec();
mfa_input2.extend_from_slice(b"account");
mfa_input2.extend_from_slice(mfa_code2.as_bytes());
let mut hasher2 = Sha256::new();
hasher2.update(&mfa_input2);
let mfa_hash2 = hasher2.finalize().to_vec();
let (state, req) = AuthClient::login_start_req(b"account", b"password3")?;
let res = auth.login_start(req)?;
let (req, _) =
AuthClient::login_finish_req(b"account", b"password3", &mfa_hash2, &state, &res, None)?;
auth.login_finish(req, true)?;
let mut hasher = Sha256::new();
hasher.update(recovery_code2.as_bytes());
let hashed_recovery_code2 = hasher.finalize().to_vec();
let (state, req) =
AuthClient::lost_totp_mfa_start_req(b"account", b"password3", &hashed_recovery_code2)?;
let res = auth.lost_totp_mfa_start(req)?;
let (req, session_key) = AuthClient::lost_totp_mfa_finish_req(
b"account",
b"password3",
&state,
&res,
recovery_code2.as_bytes(),
)?;
let res = auth.lost_totp_mfa_finish(req)?;
let mfa_code3 = AuthClient::decrypt_lost_totp_mfa_code(
&res,
&session_key,
"example.com".into(),
"account".into(),
)?;
let mut mfa_input3 = b"example.com".to_vec();
mfa_input3.extend_from_slice(b"account");
mfa_input3.extend_from_slice(mfa_code3.as_bytes());
let mut hasher3 = Sha256::new();
hasher3.update(&mfa_input3);
let mfa_hash3 = hasher3.finalize().to_vec();
let (state, req) = AuthClient::login_start_req(b"account", b"password3")?;
let res = auth.login_start(req)?;
let (req, _) =
AuthClient::login_finish_req(b"account", b"password3", &mfa_hash3, &state, &res, None)?;
auth.login_finish(req, true)?;
let (state, req) = AuthClient::reset_recovery_codes_start_req(b"account", b"password3")?;
let res = auth.reset_recovery_codes_start(req)?;
let (req, session_key) = AuthClient::reset_recovery_codes_finish_req(
b"account",
b"password3",
&mfa_hash3,
&state,
&res,
)?;
let res = auth.reset_recovery_codes_finish(req)?;
let recovery_codes = AuthClient::decrypt_reset_recovery_codes(&res, &session_key)?;
let mut recovery_code3 = String::new();
for (i, c) in recovery_codes.chars().enumerate() {
if i < 11 {
recovery_code3 = format!("{recovery_code3}{c}");
} else {
break;
}
}
let mut hasher = Sha256::new();
hasher.update(recovery_code3.as_bytes());
let hashed_recovery_code3 = hasher.finalize().to_vec();
let (state, req) =
AuthClient::forgot_password_start_req(b"account", b"password4", &hashed_recovery_code3)?;
let res = auth.forgot_password_start(req)?;
let req = AuthClient::forgot_password_finish_req(
b"account",
b"password4",
&state,
&res,
recovery_code3.as_bytes(),
)?;
auth.forgot_password_finish(req)?;
let (state, req) = AuthClient::delete_account_start_req(b"account", b"password4")?;
let res = auth.delete_account_start(req)?;
let req =
AuthClient::delete_account_finish_req(b"account", b"password4", &mfa_hash3, &state, &res)?;
auth.delete_account_finish(req)?;
drop(auth);
drop(env);
let env = Arc::new(unsafe {
let mut env_builder = EnvBuilder::new()?;
env_builder.set_maxreaders(126)?;
env_builder.set_mapsize(16384 * 64 * 10)?;
env_builder.set_maxdbs(13)?;
env_builder.open(
store_dir.to_str().expect("store dir is not str"),
&saferlmdb::open::Flags::empty(),
0o600,
)?
});
let auth = Arc::new(Auth::new(
"example.com".into(),
Some(AuthConfig {
invite: Some(InviteConfig {
mode: InviteMode::Viral,
..Default::default()
}),
..Default::default()
}),
[0u8; 32],
env.clone(),
)?);
let invite_code = auth.api_invite_get("api.example.com", "root", None)?;
let (state, req) =
AuthClient::registration_start_req(b"account", b"password", Some(invite_code))?;
let res = auth.registration_start(req, None, None)?;
let (private_key, req) =
AuthClient::registration_finish_req(b"account", b"password", &state, &res)?;
let (res, ..) = auth.registration_finish(req, None)?;
AuthClient::decrypt_totp_mfa_code(&res, private_key, "example.com".into(), "account".into())?;
Ok(())
}