diff --git a/src/api/ecdar_api.rs b/src/api/ecdar_api.rs index 81916ca..f78a59f 100644 --- a/src/api/ecdar_api.rs +++ b/src/api/ecdar_api.rs @@ -1,7 +1,7 @@ +use crate::api::hashing_context::HashingContextTrait; use crate::api::server::server::get_auth_token_request::user_credentials; use crate::entities::access; use crate::entities::session; -use bcrypt::hash; use regex::Regex; use sea_orm::SqlErr; use std::sync::Arc; @@ -38,10 +38,9 @@ pub struct ConcreteEcdarApi { session_context: Arc, user_context: Arc, reveaal_context: Arc, + hashing_context: Arc, } -const HASH_COST: u32 = 12; - /// Updates or creates a session in the database for a given user. /// /// @@ -131,6 +130,7 @@ impl ConcreteEcdarApi { session_context: Arc, user_context: Arc, reveaal_context: Arc, + hashing_context: Arc, ) -> Self { ConcreteEcdarApi { access_context, @@ -140,6 +140,7 @@ impl ConcreteEcdarApi { session_context, user_context, reveaal_context, + hashing_context, } } } @@ -256,7 +257,7 @@ impl EcdarApi for ConcreteEcdarApi { // Record to be inserted in database let new_user = user::Model { - id: Default::default(), + id: uid, username: match message.clone().username { Some(username) => { if is_valid_username(username.as_str()) { @@ -278,7 +279,7 @@ impl EcdarApi for ConcreteEcdarApi { None => user.email, }, password: match message.clone().password { - Some(password) => hash(password, HASH_COST).unwrap(), + Some(password) => self.hashing_context.hash_password(password), None => user.password, }, }; @@ -384,7 +385,7 @@ impl EcdarApi for ConcreteEcdarApi { async fn get_auth_find_user_helper( user_context: Arc, user_credentials: UserCredentials, -) -> Result { +) -> Result { if let Some(user) = user_credentials.user { match user { user_credentials::User::Username(username) => Ok(user_context @@ -485,7 +486,7 @@ impl EcdarApiAuth for ConcreteEcdarApi { return Err(Status::new(Code::InvalidArgument, "Invalid email")); } - let user = UserEntity { + let user = user::Model { id: Default::default(), username: message.clone().username, password: message.clone().password, diff --git a/src/api/hashing_context.rs b/src/api/hashing_context.rs new file mode 100644 index 0000000..a4cd7b5 --- /dev/null +++ b/src/api/hashing_context.rs @@ -0,0 +1,18 @@ +use bcrypt::{hash, verify, DEFAULT_COST}; + +pub trait HashingContextTrait: Send + Sync { + fn hash_password(&self, password: String) -> String; + fn verify_password(&self, password: String, hash: &str) -> bool; +} + +pub struct HashingContext; + +impl HashingContextTrait for HashingContext { + fn hash_password(&self, password: String) -> String { + hash(password, DEFAULT_COST).unwrap() + } + + fn verify_password(&self, password: String, hash: &str) -> bool { + verify(password, hash).unwrap() + } +} diff --git a/src/api/mod.rs b/src/api/mod.rs index eae1c43..40d3555 100644 --- a/src/api/mod.rs +++ b/src/api/mod.rs @@ -1,4 +1,5 @@ pub mod auth; pub mod ecdar_api; +pub mod hashing_context; pub mod reveaal_context; pub mod server; diff --git a/src/api/server.rs b/src/api/server.rs index 4923a2a..17eb7f0 100644 --- a/src/api/server.rs +++ b/src/api/server.rs @@ -5,6 +5,7 @@ use tonic::transport::Server; use crate::api::auth; use crate::api::ecdar_api::ConcreteEcdarApi; +use crate::api::hashing_context::HashingContextTrait; use crate::api::server::server::ecdar_api_auth_server::EcdarApiAuthServer; use crate::api::server::server::ecdar_api_server::EcdarApiServer; use crate::api::server::server::ecdar_backend_server::{EcdarBackend, EcdarBackendServer}; @@ -27,6 +28,7 @@ pub async fn start_grpc_server( session_context: Arc, user_context: Arc, reveaal_context: Arc, + hashing_context: Arc, ) -> Result<(), Box> { // defining address for our service let addr = env::var("API_ADDRESS") @@ -44,6 +46,7 @@ pub async fn start_grpc_server( session_context, user_context, reveaal_context, + hashing_context, ); // adding services to our server. diff --git a/src/main.rs b/src/main.rs index 189f531..e2a9694 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,6 +3,7 @@ mod database; mod entities; mod tests; +use crate::api::hashing_context::HashingContext; use crate::api::reveaal_context::ReveaalContext; use crate::database::access_context::AccessContext; use crate::database::database_context::{PostgresDatabaseContext, SQLiteDatabaseContext}; @@ -39,6 +40,7 @@ async fn main() -> Result<(), Box> { let session_context = Arc::new(SessionContext::new(db_context.clone())); let in_use_context = Arc::new(InUseContext::new(db_context.clone())); let reveaal_context = Arc::new(ReveaalContext); + let hashing_context = Arc::new(HashingContext); start_grpc_server( access_context, @@ -48,6 +50,7 @@ async fn main() -> Result<(), Box> { session_context, user_context, reveaal_context, + hashing_context, ) .await .unwrap(); diff --git a/src/tests/api/helpers.rs b/src/tests/api/helpers.rs index fa90745..e652e5e 100644 --- a/src/tests/api/helpers.rs +++ b/src/tests/api/helpers.rs @@ -1,6 +1,7 @@ #![cfg(test)] use crate::api::ecdar_api::ConcreteEcdarApi; +use crate::api::hashing_context::HashingContextTrait; use crate::api::server::server::ecdar_backend_server::EcdarBackend; use crate::api::server::server::{ QueryRequest, QueryResponse, SimulationStartRequest, SimulationStepRequest, @@ -29,6 +30,7 @@ pub fn get_mock_concrete_ecdar_api(mock_services: MockServices) -> ConcreteEcdar Arc::new(mock_services.session_context_mock), Arc::new(mock_services.user_context_mock), Arc::new(mock_services.reveaal_context_mock), + Arc::new(mock_services.hashing_context_mock), ) } @@ -41,6 +43,7 @@ pub fn get_mock_services() -> MockServices { session_context_mock: MockSessionContext::new(), user_context_mock: MockUserContext::new(), reveaal_context_mock: MockReveaalContext::new(), + hashing_context_mock: MockHashingContext::new(), } } @@ -52,6 +55,7 @@ pub struct MockServices { pub(crate) session_context_mock: MockSessionContext, pub(crate) user_context_mock: MockUserContext, pub(crate) reveaal_context_mock: MockReveaalContext, + pub(crate) hashing_context_mock: MockHashingContext, } mock! { @@ -153,3 +157,11 @@ mock! { async fn take_simulation_step(&self, request: Request) -> Result, Status>; } } + +mock! { + pub HashingContext {} + impl HashingContextTrait for HashingContext { + fn hash_password(&self, password: String) -> String; + fn verify_password(&self, password: String, hash: &str) -> bool; + } +} diff --git a/src/tests/api/user_logic.rs b/src/tests/api/user_logic.rs index 82ea804..0052ea7 100644 --- a/src/tests/api/user_logic.rs +++ b/src/tests/api/user_logic.rs @@ -214,21 +214,20 @@ async fn test_create_user_valid_request_returns_ok() { #[tokio::test] async fn update_user_returns_ok() { - //todo!(update user implementation should be changed to populate unchanged fields with existing values) let mut mock_services = get_mock_services(); let old_user = user::Model { id: 1, - email: "newuser@example.com".to_string(), + email: "olduser@example.com".to_string(), username: "old_username".to_string(), password: "StrongPassword123".to_string(), }; - let user = user::Model { + let new_user = user::Model { id: 1, email: "newuser@example.com".to_string(), username: "new_username".to_string(), - password: "StrongPassword123".to_string(), + password: "g76df2gd7hd837g8hjd8723hd8gd823d82d3".to_string(), }; mock_services @@ -237,11 +236,17 @@ async fn update_user_returns_ok() { .with(predicate::eq(1)) .returning(move |_| Ok(Some(old_user.clone()))); + mock_services + .hashing_context_mock + .expect_hash_password() + .with(predicate::eq("StrongPassword123".to_string())) + .returning(move |_| "g76df2gd7hd837g8hjd8723hd8gd823d82d3".to_string()); + mock_services .user_context_mock .expect_update() - .with(predicate::eq(user.clone())) - .returning(move |_| Ok(user.clone())); + .with(predicate::eq(new_user.clone())) + .returning(move |_| Ok(new_user.clone())); let api = get_mock_concrete_ecdar_api(mock_services); @@ -264,17 +269,10 @@ async fn update_user_returns_ok() { async fn update_user_non_existant_user_returns_err() { let mut mock_services = get_mock_services(); - let user = user::Model { - id: 1, - email: "new_test@test".to_string(), - username: "new_test_user".to_string(), - password: "new_test_pass".to_string(), - }; - mock_services .user_context_mock - .expect_update() - .with(predicate::eq(user.clone())) + .expect_get_by_id() + .with(predicate::eq(1)) .returning(move |_| Err(DbErr::RecordNotFound("".to_string()))); let api = get_mock_concrete_ecdar_api(mock_services); @@ -289,7 +287,7 @@ async fn update_user_non_existant_user_returns_err() { .metadata_mut() .insert("uid", metadata::MetadataValue::from_str("1").unwrap()); - let update_user_response = api.update_user(update_user_request).await; + let res = api.update_user(update_user_request).await; - assert!(update_user_response.is_err()) + assert_eq!(res.unwrap_err().code(), Code::Internal); }