From a36290987d391221b2a51cdcc08457bf8703a3ff Mon Sep 17 00:00:00 2001 From: Sebastian Pravda Date: Sat, 29 Oct 2022 22:52:46 +0200 Subject: [PATCH 1/9] feat: encrypted PIDN in db --- api/src/lib.rs | 6 +++--- api/src/requests.rs | 7 +++++++ core/src/mutation.rs | 14 +++++++++++--- core/src/services/candidate_service.rs | 6 +++--- 4 files changed, 24 insertions(+), 9 deletions(-) diff --git a/api/src/lib.rs b/api/src/lib.rs index 1a066a6..33136ec 100644 --- a/api/src/lib.rs +++ b/api/src/lib.rs @@ -5,7 +5,7 @@ use std::net::SocketAddr; use portfolio_core::error::ServiceError; use portfolio_core::services::candidate_service::CandidateService; -use requests::LoginRequest; +use requests::{LoginRequest, RegisterRequest}; use rocket::http::Status; use rocket::{Rocket, Build}; use rocket::serde::json::Json; @@ -35,13 +35,13 @@ fn custom_err_from_service_err(service_err: ServiceError) -> Custom { } #[post("/", data = "")] -async fn create(conn: Connection<'_, Db>, post_form: Json) -> Result> { +async fn create(conn: Connection<'_, Db>, post_form: Json) -> Result> { let db = conn.into_inner(); let form = post_form.into_inner(); let plain_text_password = random_8_char_string(); - Mutation::create_candidate(db, form, &plain_text_password) + Mutation::create_candidate(db, form.application_id, &plain_text_password, form.personal_id_number) .await .expect("Could not insert candidate"); diff --git a/api/src/requests.rs b/api/src/requests.rs index 9c86d49..032af26 100644 --- a/api/src/requests.rs +++ b/api/src/requests.rs @@ -6,4 +6,11 @@ use rocket::serde::{Serialize, Deserialize}; pub struct LoginRequest { pub application_id: i32, pub password: String, +} + +#[derive(Serialize, Deserialize)] +#[serde(crate = "rocket::serde")] +pub struct RegisterRequest { + pub application_id: i32, + pub personal_id_number: String, } \ No newline at end of file diff --git a/core/src/mutation.rs b/core/src/mutation.rs index 7e88f47..b83ee97 100644 --- a/core/src/mutation.rs +++ b/core/src/mutation.rs @@ -1,3 +1,5 @@ +use std::vec; + use chrono::{Utc, Duration}; use ::entity::{candidate, session}; use sea_orm::{*, prelude::Uuid}; @@ -8,17 +10,23 @@ pub struct Mutation; impl Mutation { pub async fn create_candidate( db: &DbConn, - form_data: candidate::Model, + application_id: i32, plain_text_password: &String, + personal_id_number: String, ) -> Result { // TODO: unwrap pro testing.. let hashed_password = hash_password(plain_text_password.to_string()).await.unwrap(); let (pubkey, priv_key_plain_text) = crypto::create_identity(); let encrypted_priv_key = crypto::encrypt_password(&priv_key_plain_text, &plain_text_password.to_string()).await.unwrap(); + let encrypted_personal_id_number = crypto::encrypt_password_with_recipients( + &personal_id_number, vec![&pubkey] + ).await.unwrap(); + candidate::ActiveModel { - application: Set(form_data.application), + application: Set(application_id), + personal_identification_number: Set(Some(encrypted_personal_id_number)), code: Set(hashed_password), public_key: Set(pubkey), private_key: Set(encrypted_priv_key), @@ -97,7 +105,7 @@ mod tests { let secret_message = "trnka".to_string(); - let candidate = Mutation::create_candidate(&db, form, &plain_text_password).await.unwrap(); + let candidate = Mutation::create_candidate(&db, form, &plain_text_password, "".to_string()).await.unwrap(); let encrypted_message = crypto::encrypt_password_with_recipients(&secret_message, vec![&candidate.public_key]).await.unwrap(); diff --git a/core/src/services/candidate_service.rs b/core/src/services/candidate_service.rs index 73486e7..04cd45a 100644 --- a/core/src/services/candidate_service.rs +++ b/core/src/services/candidate_service.rs @@ -121,7 +121,7 @@ mod tests { "application": 5555555, })).unwrap(); - let candidate = Mutation::create_candidate(&db, form, &SECRET.to_string()).await.unwrap(); + let candidate = Mutation::create_candidate(&db, form, &SECRET.to_string(), "".to_string()).await.unwrap(); assert_eq!(candidate.application, 5555555); assert_ne!(candidate.code, SECRET.to_string()); @@ -136,7 +136,7 @@ mod tests { "application": 5555555, })).unwrap(); - Mutation::create_candidate(&db, form, &"Tajny_kod".to_string()).await.unwrap(); + Mutation::create_candidate(&db, form, &"Tajny_kod".to_string(), "".to_string()).await.unwrap(); // correct password let session = CandidateService::new_session( @@ -163,7 +163,7 @@ mod tests { "application": 5555555, })).unwrap(); - let candidate_form = Mutation::create_candidate(&db, form, &"Tajny_kod".to_string()).await.unwrap(); + let candidate_form = Mutation::create_candidate(&db, form, &"Tajny_kod".to_string(), "".to_string()).await.unwrap(); // incorrect password assert!( From 2aa0330d8cfa0e8c0127f1aed1206723b7318a13 Mon Sep 17 00:00:00 2001 From: Sebastian Pravda Date: Sat, 29 Oct 2022 23:03:31 +0200 Subject: [PATCH 2/9] refactor: remove serde json from entities --- entity/Cargo.toml | 1 - entity/src/admin.rs | 2 ++ entity/src/candidate.rs | 11 +++-------- entity/src/mod.rs | 2 ++ entity/src/parent.rs | 2 ++ entity/src/prelude.rs | 2 ++ entity/src/session.rs | 2 ++ 7 files changed, 13 insertions(+), 9 deletions(-) diff --git a/entity/Cargo.toml b/entity/Cargo.toml index 1b3a166..af3e744 100644 --- a/entity/Cargo.toml +++ b/entity/Cargo.toml @@ -9,7 +9,6 @@ name = "entity" path = "src/lib.rs" [dependencies] -serde = { version = "^1.0", features = ["derive"] } chrono = "^0.4" [dependencies.sea-orm] diff --git a/entity/src/admin.rs b/entity/src/admin.rs index 5d48632..ccfab5a 100644 --- a/entity/src/admin.rs +++ b/entity/src/admin.rs @@ -1,3 +1,5 @@ +//! SeaORM Entity. Generated by sea-orm-codegen 0.9.3 + use sea_orm::entity::prelude::*; #[derive(Clone, Debug, PartialEq, DeriveEntityModel)] diff --git a/entity/src/candidate.rs b/entity/src/candidate.rs index 221a80e..75b42cd 100644 --- a/entity/src/candidate.rs +++ b/entity/src/candidate.rs @@ -1,13 +1,12 @@ +//! SeaORM Entity. Generated by sea-orm-codegen 0.9.3 + use sea_orm::entity::prelude::*; -use serde::{Deserialize, Serialize}; - -#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, DeriveEntityModel)] #[sea_orm(table_name = "candidate")] pub struct Model { #[sea_orm(primary_key, auto_increment = false)] pub application: i32, - #[serde(skip_deserializing, skip_serializing)] pub code: String, pub name: Option, pub surname: Option, @@ -23,13 +22,9 @@ pub struct Model { pub personal_identification_number: Option, #[sea_orm(column_type = "Text", nullable)] pub personal_identification_number_hash: Option, - #[serde(skip_deserializing, skip_serializing)] pub public_key: String, - #[serde(skip_deserializing, skip_serializing)] pub private_key: String, - #[serde(skip_deserializing, skip_serializing)] pub created_at: DateTime, - #[serde(skip_deserializing, skip_serializing)] pub updated_at: DateTime, } diff --git a/entity/src/mod.rs b/entity/src/mod.rs index ef5f522..63cf621 100644 --- a/entity/src/mod.rs +++ b/entity/src/mod.rs @@ -1,3 +1,5 @@ +//! SeaORM Entity. Generated by sea-orm-codegen 0.9.3 + pub mod prelude; pub mod admin; diff --git a/entity/src/parent.rs b/entity/src/parent.rs index 7e1b00c..4cf47cc 100644 --- a/entity/src/parent.rs +++ b/entity/src/parent.rs @@ -1,3 +1,5 @@ +//! SeaORM Entity. Generated by sea-orm-codegen 0.9.3 + use sea_orm::entity::prelude::*; #[derive(Clone, Debug, PartialEq, DeriveEntityModel)] diff --git a/entity/src/prelude.rs b/entity/src/prelude.rs index af8586b..73b608f 100644 --- a/entity/src/prelude.rs +++ b/entity/src/prelude.rs @@ -1,3 +1,5 @@ +//! SeaORM Entity. Generated by sea-orm-codegen 0.9.3 + pub use super::admin::Entity as Admin; pub use super::candidate::Entity as Candidate; pub use super::parent::Entity as Parent; diff --git a/entity/src/session.rs b/entity/src/session.rs index 99310c8..3fdde8c 100644 --- a/entity/src/session.rs +++ b/entity/src/session.rs @@ -1,3 +1,5 @@ +//! SeaORM Entity. Generated by sea-orm-codegen 0.9.3 + use sea_orm::entity::prelude::*; #[derive(Clone, Debug, PartialEq, DeriveEntityModel)] From 79ab4d0ed40e7c4a658006df8c052166422b1c3d Mon Sep 17 00:00:00 2001 From: Sebastian Pravda Date: Sun, 30 Oct 2022 13:06:07 +0100 Subject: [PATCH 3/9] fix: candidate tests json --- core/src/services/candidate_service.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/src/services/candidate_service.rs b/core/src/services/candidate_service.rs index 04cd45a..79140f5 100644 --- a/core/src/services/candidate_service.rs +++ b/core/src/services/candidate_service.rs @@ -118,7 +118,7 @@ mod tests { let db = get_memory_sqlite_connection().await; let form = serde_json::from_value(json!({ - "application": 5555555, + "application_id": 5555555, })).unwrap(); let candidate = Mutation::create_candidate(&db, form, &SECRET.to_string(), "".to_string()).await.unwrap(); @@ -133,7 +133,7 @@ mod tests { let db = &get_memory_sqlite_connection().await; let form = serde_json::from_value(json!({ - "application": 5555555, + "application_id": 5555555, })).unwrap(); Mutation::create_candidate(&db, form, &"Tajny_kod".to_string(), "".to_string()).await.unwrap(); @@ -160,7 +160,7 @@ mod tests { let db = &get_memory_sqlite_connection().await; let form = serde_json::from_value(json!({ - "application": 5555555, + "application_id": 5555555, })).unwrap(); let candidate_form = Mutation::create_candidate(&db, form, &"Tajny_kod".to_string(), "".to_string()).await.unwrap(); From d0825b80d530260feadb37f1da7406c0bc383453 Mon Sep 17 00:00:00 2001 From: Sebastian Pravda Date: Sun, 30 Oct 2022 13:06:45 +0100 Subject: [PATCH 4/9] feat: PIdN not null --- core/src/mutation.rs | 4 ++-- entity/src/candidate.rs | 2 +- migration/Cargo.toml | 4 +++- migration/src/m20221024_121621_create_candidate.rs | 2 +- migration/src/main.rs | 6 ++++++ 5 files changed, 13 insertions(+), 5 deletions(-) create mode 100644 migration/src/main.rs diff --git a/core/src/mutation.rs b/core/src/mutation.rs index b83ee97..c759322 100644 --- a/core/src/mutation.rs +++ b/core/src/mutation.rs @@ -26,7 +26,7 @@ impl Mutation { candidate::ActiveModel { application: Set(application_id), - personal_identification_number: Set(Some(encrypted_personal_id_number)), + personal_identification_number: Set(encrypted_personal_id_number), code: Set(hashed_password), public_key: Set(pubkey), private_key: Set(encrypted_priv_key), @@ -98,7 +98,7 @@ mod tests { let db = get_memory_sqlite_connection().await; let form = serde_json::from_value(json!({ - "application": 5555555, + "application_id": 5555555, })).unwrap(); let plain_text_password = "test".to_string(); diff --git a/entity/src/candidate.rs b/entity/src/candidate.rs index 75b42cd..9800dc2 100644 --- a/entity/src/candidate.rs +++ b/entity/src/candidate.rs @@ -19,7 +19,7 @@ pub struct Model { pub email: Option, pub sex: Option, pub study: Option, - pub personal_identification_number: Option, + pub personal_identification_number: String, #[sea_orm(column_type = "Text", nullable)] pub personal_identification_number_hash: Option, pub public_key: String, diff --git a/migration/Cargo.toml b/migration/Cargo.toml index 794151d..673784b 100644 --- a/migration/Cargo.toml +++ b/migration/Cargo.toml @@ -9,10 +9,12 @@ name = "migration" path = "src/lib.rs" [dependencies] +tokio = { version = "^1.21", features = ["macros"] } + serde = { version = "^1.0", features = ["derive"] } chrono = "^0.4" portfolio-entity = { path = "../entity" } [dependencies.sea-orm-migration] version = "^0.10" -features = [ "runtime-tokio-native-tls", "sqlx-postgres", "sqlx-sqlite"] +features = [ "runtime-tokio-native-tls", "sqlx-postgres", "sqlx-sqlite"] \ No newline at end of file diff --git a/migration/src/m20221024_121621_create_candidate.rs b/migration/src/m20221024_121621_create_candidate.rs index 929838b..5483c3a 100644 --- a/migration/src/m20221024_121621_create_candidate.rs +++ b/migration/src/m20221024_121621_create_candidate.rs @@ -30,7 +30,7 @@ impl MigrationTrait for Migration { .col(ColumnDef::new(Candidate::Email).string()) .col(ColumnDef::new(Candidate::Sex).string()) .col(ColumnDef::new(Candidate::Study).string()) - .col(ColumnDef::new(Candidate::PersonalIdentificationNumber).string()) + .col(ColumnDef::new(Candidate::PersonalIdentificationNumber).string().not_null()) .col(ColumnDef::new(Candidate::PersonalIdentificationNumberHash).text()) .col(ColumnDef::new(Candidate::PublicKey).string().not_null()) .col(ColumnDef::new(Candidate::PrivateKey).string().not_null()) diff --git a/migration/src/main.rs b/migration/src/main.rs new file mode 100644 index 0000000..85be8cc --- /dev/null +++ b/migration/src/main.rs @@ -0,0 +1,6 @@ +use sea_orm_migration::prelude::*; + +#[tokio::main] +async fn main() { + cli::run_cli(migration::Migrator).await; +} \ No newline at end of file From e2d373431a8ef36f948b7faf1cbcba86943fb21d Mon Sep 17 00:00:00 2001 From: Sebastian Pravda Date: Sun, 30 Oct 2022 13:09:37 +0100 Subject: [PATCH 5/9] style: remove comments --- entity/src/admin.rs | 2 -- entity/src/candidate.rs | 2 -- entity/src/mod.rs | 2 -- entity/src/parent.rs | 2 -- entity/src/prelude.rs | 2 -- entity/src/session.rs | 2 -- 6 files changed, 12 deletions(-) diff --git a/entity/src/admin.rs b/entity/src/admin.rs index ccfab5a..5d48632 100644 --- a/entity/src/admin.rs +++ b/entity/src/admin.rs @@ -1,5 +1,3 @@ -//! SeaORM Entity. Generated by sea-orm-codegen 0.9.3 - use sea_orm::entity::prelude::*; #[derive(Clone, Debug, PartialEq, DeriveEntityModel)] diff --git a/entity/src/candidate.rs b/entity/src/candidate.rs index 9800dc2..4861e7c 100644 --- a/entity/src/candidate.rs +++ b/entity/src/candidate.rs @@ -1,5 +1,3 @@ -//! SeaORM Entity. Generated by sea-orm-codegen 0.9.3 - use sea_orm::entity::prelude::*; #[derive(Clone, Debug, PartialEq, DeriveEntityModel)] diff --git a/entity/src/mod.rs b/entity/src/mod.rs index 63cf621..ef5f522 100644 --- a/entity/src/mod.rs +++ b/entity/src/mod.rs @@ -1,5 +1,3 @@ -//! SeaORM Entity. Generated by sea-orm-codegen 0.9.3 - pub mod prelude; pub mod admin; diff --git a/entity/src/parent.rs b/entity/src/parent.rs index 4cf47cc..7e1b00c 100644 --- a/entity/src/parent.rs +++ b/entity/src/parent.rs @@ -1,5 +1,3 @@ -//! SeaORM Entity. Generated by sea-orm-codegen 0.9.3 - use sea_orm::entity::prelude::*; #[derive(Clone, Debug, PartialEq, DeriveEntityModel)] diff --git a/entity/src/prelude.rs b/entity/src/prelude.rs index 73b608f..af8586b 100644 --- a/entity/src/prelude.rs +++ b/entity/src/prelude.rs @@ -1,5 +1,3 @@ -//! SeaORM Entity. Generated by sea-orm-codegen 0.9.3 - pub use super::admin::Entity as Admin; pub use super::candidate::Entity as Candidate; pub use super::parent::Entity as Parent; diff --git a/entity/src/session.rs b/entity/src/session.rs index 3fdde8c..99310c8 100644 --- a/entity/src/session.rs +++ b/entity/src/session.rs @@ -1,5 +1,3 @@ -//! SeaORM Entity. Generated by sea-orm-codegen 0.9.3 - use sea_orm::entity::prelude::*; #[derive(Clone, Debug, PartialEq, DeriveEntityModel)] From e09a347d49fdb93f444f1f69750542b37e9aaf5a Mon Sep 17 00:00:00 2001 From: Sebastian Pravda Date: Sun, 30 Oct 2022 13:13:46 +0100 Subject: [PATCH 6/9] fix: create_candidate parameters in tests --- core/src/mutation.rs | 6 +----- core/src/query.rs | 1 + core/src/services/candidate_service.rs | 19 +++---------------- 3 files changed, 5 insertions(+), 21 deletions(-) diff --git a/core/src/mutation.rs b/core/src/mutation.rs index c759322..c71bf1d 100644 --- a/core/src/mutation.rs +++ b/core/src/mutation.rs @@ -73,7 +73,6 @@ impl Mutation { #[cfg(test)] mod tests { use sea_orm::{Database, DbConn}; - use serde_json::json; use crate::{Mutation, crypto}; @@ -97,15 +96,12 @@ mod tests { async fn test_encrypt_decrypt_private_key_with_passphrase() { let db = get_memory_sqlite_connection().await; - let form = serde_json::from_value(json!({ - "application_id": 5555555, - })).unwrap(); let plain_text_password = "test".to_string(); let secret_message = "trnka".to_string(); - let candidate = Mutation::create_candidate(&db, form, &plain_text_password, "".to_string()).await.unwrap(); + let candidate = Mutation::create_candidate(&db, 5555555, &plain_text_password, "".to_string()).await.unwrap(); let encrypted_message = crypto::encrypt_password_with_recipients(&secret_message, vec![&candidate.public_key]).await.unwrap(); diff --git a/core/src/query.rs b/core/src/query.rs index 35f0883..0c0c440 100644 --- a/core/src/query.rs +++ b/core/src/query.rs @@ -50,6 +50,7 @@ mod tests { code: Set("test".to_string()), public_key: Set("test".to_string()), private_key: Set("test".to_string()), + personal_identification_number: Set("test".to_string()), created_at: Set(chrono::offset::Local::now().naive_local()), updated_at: Set(chrono::offset::Local::now().naive_local()), ..Default::default() diff --git a/core/src/services/candidate_service.rs b/core/src/services/candidate_service.rs index 79140f5..91bdd83 100644 --- a/core/src/services/candidate_service.rs +++ b/core/src/services/candidate_service.rs @@ -92,7 +92,6 @@ impl CandidateService { mod tests { use entity::candidate; use sea_orm::{DbConn, Database, sea_query::TableCreateStatement, DbBackend, Schema, ConnectionTrait, prelude::Uuid}; - use serde_json::json; use crate::{crypto, Mutation, services::candidate_service::CandidateService}; @@ -117,11 +116,7 @@ mod tests { let db = get_memory_sqlite_connection().await; - let form = serde_json::from_value(json!({ - "application_id": 5555555, - })).unwrap(); - - let candidate = Mutation::create_candidate(&db, form, &SECRET.to_string(), "".to_string()).await.unwrap(); + let candidate = Mutation::create_candidate(&db, 5555555, &SECRET.to_string(), "".to_string()).await.unwrap(); assert_eq!(candidate.application, 5555555); assert_ne!(candidate.code, SECRET.to_string()); @@ -132,11 +127,7 @@ mod tests { async fn test_candidate_session_correct_password() { let db = &get_memory_sqlite_connection().await; - let form = serde_json::from_value(json!({ - "application_id": 5555555, - })).unwrap(); - - Mutation::create_candidate(&db, form, &"Tajny_kod".to_string(), "".to_string()).await.unwrap(); + Mutation::create_candidate(&db, 5555555, &"Tajny_kod".to_string(), "".to_string()).await.unwrap(); // correct password let session = CandidateService::new_session( @@ -159,11 +150,7 @@ mod tests { async fn test_candidate_session_incorrect_password() { let db = &get_memory_sqlite_connection().await; - let form = serde_json::from_value(json!({ - "application_id": 5555555, - })).unwrap(); - - let candidate_form = Mutation::create_candidate(&db, form, &"Tajny_kod".to_string(), "".to_string()).await.unwrap(); + let candidate_form = Mutation::create_candidate(&db, 5555555, &"Tajny_kod".to_string(), "".to_string()).await.unwrap(); // incorrect password assert!( From 9f54417dccb3ba89d24f1c32e8cbd041ad74c585 Mon Sep 17 00:00:00 2001 From: Sebastian Pravda Date: Sun, 30 Oct 2022 13:14:30 +0100 Subject: [PATCH 7/9] chore: remove unused serde dependency --- core/Cargo.toml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/core/Cargo.toml b/core/Cargo.toml index 9d32d30..9856703 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -7,10 +7,6 @@ publish = false [dependencies] chrono = "^0.4" -# serialization & deserialization -serde = { version = "^1.0", features = ["derive"] } -serde_json = "^1.0" - portfolio-entity = { path = "../entity" } # env From 8b26c14e8cb292a7e7a8bb3d2fb76fee181c44fe Mon Sep 17 00:00:00 2001 From: Sebastian Pravda Date: Sun, 30 Oct 2022 13:40:08 +0100 Subject: [PATCH 8/9] refactor: use aes encrypt function --- core/src/mutation.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/mutation.rs b/core/src/mutation.rs index 735c100..f63b071 100644 --- a/core/src/mutation.rs +++ b/core/src/mutation.rs @@ -17,7 +17,7 @@ impl Mutation { // TODO: unwrap pro testing.. let hashed_password = hash_password(plain_text_password.to_string()).await.unwrap(); let (pubkey, priv_key_plain_text) = crypto::create_identity(); - let encrypted_priv_key = crypto::encrypt_password_age(&priv_key_plain_text, &plain_text_password.to_string()).await.unwrap(); + let encrypted_priv_key = crypto::encrypt_password(priv_key_plain_text, plain_text_password.to_string()).await.unwrap(); let encrypted_personal_id_number = crypto::encrypt_password_with_recipients( &personal_id_number, vec![&pubkey] From 0731700f1791a480f3b1acd356728ecb25254bcb Mon Sep 17 00:00:00 2001 From: Sebastian Pravda Date: Sun, 30 Oct 2022 13:46:09 +0100 Subject: [PATCH 9/9] fix: use aes decrypt in test --- core/src/mutation.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/mutation.rs b/core/src/mutation.rs index f63b071..ba914fa 100644 --- a/core/src/mutation.rs +++ b/core/src/mutation.rs @@ -105,7 +105,7 @@ mod tests { let encrypted_message = crypto::encrypt_password_with_recipients(&secret_message, vec![&candidate.public_key]).await.unwrap(); - let private_key_plain_text = crypto::decrypt_password_age(&candidate.private_key, &plain_text_password).await.unwrap(); + let private_key_plain_text = crypto::decrypt_password(candidate.private_key, plain_text_password).await.unwrap(); let decrypted_message = crypto::decrypt_password_with_private_key(&encrypted_message, &private_key_plain_text).await.unwrap();