Skip to content

Commit d601caf

Browse files
committed
setup end point
1 parent 966cd8f commit d601caf

9 files changed

Lines changed: 117 additions & 25 deletions

File tree

.env

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ AVORED_DATABASE_FOLDER_NAME=rocksdb://data/avored.db
55

66
PORT=50051
77

8-
AVORED_PASSWORD_SALT=pvumfkuyowpvqnk7j2n23g99ilnxw0znziw6vo6dp9me967j
8+
AVORED_PASSWORD_SALT=rO0ABXNyABdqYXZhLnV0aWwLSGVsbG8gV29ybGQgSEVMTE8gV09STEQgSGVsbG8gV29ybGQgSEVMTE8gV09STEQgl
99

1010
AVORED_JWT_SECRET=oikcvnmlmgumgxfhccfpsjhpazbvfazdojemqherghvseoedu
1111
AVORED_JWT_EXPIRED_IN=60m

Cargo.lock

Lines changed: 12 additions & 19 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ serde = { version = "1.0", features = ["derive"] }
1616
async-trait = "0.1"
1717
serde_json = "1.0"
1818

19+
# Additional dependencies not in workspace
20+
argon2 = "0.5.3"
21+
dotenvy = "0.15.7"
22+
1923

2024
## Optional
2125
console_error_panic_hook = { version = "0.1", optional = true }

src/adapters/grpc_api/misc_api.rs

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1+
12
#[cfg(feature = "ssr")]
23
use tonic::{Request, Response, Status};
3-
4+
use crate::infra::setup::get_env;
45
use crate::{
5-
application::use_cases::misc_use_case::MiscUseCase, domain::models::admin_user::StorableAdminUser, infra::grpc::misc::{
6+
application::{extensions::string_extension::StringExtension, use_cases::misc_use_case::MiscUseCase}, domain::models::admin_user::StorableAdminUser, infra::grpc::misc::{
67
HealthCheckRequest, HealthCheckResponse, SetupRequest, SetupResponse, misc_server::Misc
78
}
89
};
@@ -49,11 +50,17 @@ impl Misc for MiscGrpcApi {
4950
Err(e) => return Err(Status::invalid_argument(e.to_string())),
5051
};
5152

53+
54+
let password_salt = get_env("AVORED_PASSWORD_SALT")?;
55+
56+
println!("Pass salt: {}", password_salt);
57+
58+
let password_hash = storable_admin_user.password_hash.get_password_hash(&password_salt)?;
59+
5260
storable_admin_user.logged_in_user = "ApplicationSetupProcess".to_string();
5361
storable_admin_user.is_super_admin = true;
62+
storable_admin_user.password_hash = password_hash;
5463

55-
// @todo hash the password
56-
5764
println!("Got a setup request: {:?}", storable_admin_user);
5865

5966
// Think of implementing a generic result handler

src/application/extensions/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pub mod string_extension;
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// Usually we don't postfix the extension,
2+
// but naming only string might conflict so we use it.
3+
4+
use argon2::{Argon2, PasswordHasher, password_hash::SaltString};
5+
6+
/// `string_extension`
7+
pub trait StringExtension {
8+
/// gets the password hash using the provided salt
9+
fn get_password_hash(&self, password_salt: &str) -> crate::error::Result<String>;
10+
}
11+
12+
impl StringExtension for String {
13+
fn get_password_hash(&self, password_salt: &str) -> crate::error::Result<String> {
14+
15+
let password = self.as_bytes();
16+
let salt = SaltString::from_b64(password_salt)?;
17+
18+
let argon2 = Argon2::default();
19+
let password_hash = argon2.hash_password(password, &salt)?.to_string();
20+
21+
Ok(Self::from(password_hash))
22+
}
23+
}

src/application/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
pub mod use_cases;
2+
pub mod extensions;

src/error.rs

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
use leptos::config::errors::LeptosConfigError;
1+
use leptos::{config::errors::LeptosConfigError};
2+
use tonic::Status;
23
use tracing::error;
34

45
/// This is custom Result type for the application.
@@ -8,6 +9,10 @@ pub type Result<T> = core::result::Result<T, Error>;
89
#[derive(Debug, Clone)]
910
pub enum Error {
1011
Generic(String),
12+
13+
/// Error when the password encryption has some issue.
14+
Argon2(Box<argon2::password_hash::Error>),
15+
1116
}
1217

1318
impl core::fmt::Display for Error {
@@ -30,6 +35,40 @@ impl From<surrealdb::Error> for Error {
3035
}
3136
}
3237

38+
impl From<argon2::password_hash::Error> for Error {
39+
fn from(actual_error: argon2::password_hash::Error) -> Self {
40+
error!("argon2 password hash error: {actual_error:?}");
41+
Self::Argon2(Box::new(actual_error))
42+
}
43+
}
44+
45+
impl From<dotenvy::Error> for Error {
46+
fn from(val: dotenvy::Error) -> Self {
47+
error!("there is an issue with loading env file: {val:?}");
48+
Self::Generic("there is an issue with loading env file".to_string())
49+
}
50+
}
51+
52+
impl From<Error> for Status {
53+
fn from(val: Error) -> Self {
54+
match val {
55+
// Error::InvalidArgument(error_response) => Self::invalid_argument(error_response),
56+
// Error::Unauthorizeed(resource_name) => {
57+
// let error_message = format!("unauthorized: you do not have access to access this ({resource_name}) resource");
58+
// Self::permission_denied(error_message)
59+
// }
60+
// Error::Unauthenticated(error_message) => {
61+
// Self::unauthenticated(error_message)
62+
// },
63+
Error::Argon2(boxed_error) => {
64+
Self::internal(format!("Argon2 error: {boxed_error:?}"))
65+
},
66+
_ => Self::invalid_argument("500 Internal server error")
67+
}
68+
}
69+
}
70+
71+
3372
impl From<std::io::Error> for Error {
3473
fn from(error: std::io::Error) -> Self {
3574
error!("IO error: {error:?}");

src/infra/setup.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,12 @@ use std::sync::Arc;
88
use tracing_subscriber::layer::SubscriberExt;
99
use tracing_subscriber::util::SubscriberInitExt;
1010
use tracing_subscriber::{filter, Layer};
11+
use crate::error::Error;
1112

1213
pub async fn init_app_state() -> Result<AppState> {
1314
init_log();
15+
16+
load_env()?;
1417
// let db = init_database().await?;
1518
// rust_i18n::i18n!("resources/locales");
1619

@@ -35,6 +38,27 @@ impl FromRef<AppState> for LeptosOptions {
3538
}
3639
}
3740

41+
42+
fn load_env() -> Result<()>{
43+
dotenvy::dotenv()?;
44+
45+
println!("here");
46+
match get_env("APP_ENV")?.as_str() {
47+
"prod" => dotenvy::from_filename_override(".env.prod")?,
48+
"stag" => dotenvy::from_filename_override(".env.stag")?,
49+
"test" => dotenvy::from_filename_override(".env.test")?,
50+
"dev" => dotenvy::from_filename_override(".env.dev")?,
51+
// as if it won't match any we load dev as default
52+
_ => dotenvy::from_filename_override(".env")?,
53+
};
54+
55+
Ok(())
56+
}
57+
58+
pub fn get_env(name: &'static str) -> Result<String> {
59+
env::var(name).map_err(|_| Error::Generic(format!("environment variable {} is not set", name)))
60+
}
61+
3862
fn init_log() {
3963
let stdout_log = tracing_subscriber::fmt::layer().pretty();
4064

0 commit comments

Comments
 (0)