diff --git a/Cargo.toml b/Cargo.toml index 3881ade..65e50d4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,9 +30,11 @@ serde_json = "1.0.108" [build-dependencies] tonic-build = "0.10.2" + [lints.clippy] complexity = "deny" correctness = "deny" perf = "deny" suspicious = "warn" enum_variant_names = "allow" +unwrap_used = "warn" diff --git a/src/api/auth.rs b/src/api/auth.rs index 3acccbc..528fe52 100644 --- a/src/api/auth.rs +++ b/src/api/auth.rs @@ -18,7 +18,7 @@ pub fn validation_interceptor(mut req: Request<()>) -> Result, Statu Ok(token_data) => { req.metadata_mut().insert( "uid", - metadata::MetadataValue::from_str(&token_data.claims.sub).unwrap(), + metadata::MetadataValue::from_str(&token_data.claims.sub).map_err(|err| Status::internal(err.to_string()))?, ); Ok(req) } @@ -260,7 +260,7 @@ impl RequestExt for Request { self.metadata().get("authorization").map(|token| { token .to_str() - .unwrap() + .expect("failed to parse token string")//TODO better error handling .trim_start_matches("Bearer ") .to_string() }) @@ -268,19 +268,21 @@ impl RequestExt for Request { /// Returns the token string slice from the request metadata. fn token_str(&self) -> Option<&str> { match self.metadata().get("authorization") { - Some(token) => Some(token.to_str().unwrap().trim_start_matches("Bearer ")), + //TODO better error handling + Some(token) => Some(token.to_str().expect("failed to parse token string").trim_start_matches("Bearer ")), None => None, } } /// Returns the uid from the request metadata. fn uid(&self) -> Option { - let uid = match self.metadata().get("uid").unwrap().to_str() { + //TODO better error handling + let uid = match self.metadata().get("uid").expect("failed to parse user id").to_str() { Ok(uid) => uid, Err(_) => return None, }; - - Some(uid.parse().unwrap()) + //TODO better error handling + Some(uid.parse().expect("failed to parse user id")) } } diff --git a/src/api/ecdar_api.rs b/src/api/ecdar_api.rs index 80e1366..88d43ff 100644 --- a/src/api/ecdar_api.rs +++ b/src/api/ecdar_api.rs @@ -19,7 +19,7 @@ use crate::database::{session_context::SessionContextTrait, user_context::UserCo use crate::entities::{access, in_use, model, query, session, user}; use chrono::{Duration, Utc}; use regex::Regex; -use sea_orm::SqlErr; +use sea_orm::{DbErr, SqlErr}; use serde_json; use std::sync::Arc; use tonic::{Code, Request, Response, Status}; @@ -30,7 +30,39 @@ const IN_USE_DURATION_MINUTES: i64 = 10; pub struct ConcreteEcdarApi { contexts: ContextCollection, } - +/// Maps a `DbErr` to a `Status` +// fn to_status(db_err: DbErr) -> Status { +// //TODO Probably a bad idea to return DbErr messages, oh well. +// match db_err.sql_err() { +// Some(serr) => match serr { +// SqlErr::UniqueConstraintViolation(mes) => return Status::new(Code::AlreadyExists, mes), +// SqlErr::ForeignKeyConstraintViolation(mes) => { +// return Status::new(Code::InvalidArgument, mes) +// } +// _ => unreachable!(), +// }, +// None => {} +// } +// match db_err { +// DbErr::ConnectionAcquire(err) => Status::from_error(Box::new(err)), +// DbErr::TryIntoErr { from, into, source } => todo!(), +// DbErr::Conn(err) => Status::new(Code::FailedPrecondition, err.to_string()), +// DbErr::Exec(err) => Status::new(Code::Internal, err.to_string()), +// DbErr::Query(err) => Status::new(Code::Internal, err.to_string()), +// DbErr::ConvertFromU64(mes) => todo!(), +// DbErr::UnpackInsertId => todo!(), +// DbErr::UpdateGetPrimaryKey => panic!("unknown error"), +// DbErr::RecordNotFound(mes) => Status::new(Code::NotFound, mes), +// DbErr::AttrNotSet(mes) => Status::new(Code::Internal, mes), +// DbErr::Custom(mes) => Status::new(Code::Unknown, mes), +// DbErr::Type(mes) => Status::new(Code::Internal, mes), +// DbErr::Json(mes) => Status::new(Code::InvalidArgument, mes), +// DbErr::Migration(mes) => todo!(), +// DbErr::RecordNotInserted => todo!(), +// DbErr::RecordNotUpdated => Status::new(Code::NotFound, "No record updated"), +// } +// // todo!() +// } /// Updates or creates a session in the database for a given user. /// /// @@ -52,13 +84,13 @@ pub async fn handle_session( access_token: access_token.clone(), refresh_token: refresh_token.clone(), updated_at: Default::default(), - user_id: uid.parse().unwrap(), + user_id: uid.parse().map_err(|err| Status::internal(format!("failed to parse user id (uid) ({err})")))?, }) .await .map_err(|err| Status::new(Code::Internal, err.to_string()))?; } else { let mut session = match session_context - .get_by_token(TokenType::RefreshToken, request.token_string().unwrap()) + .get_by_token(TokenType::RefreshToken, request.token_string().ok_or(Status::internal("failed to get token from request metadata"))?) .await { Ok(Some(session)) => session, @@ -84,13 +116,13 @@ pub async fn handle_session( fn is_valid_email(email: &str) -> bool { Regex::new(r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$") - .unwrap() + .expect("failed to compile regex") .is_match(email) } fn is_valid_username(username: &str) -> bool { Regex::new(r"^[a-zA-Z0-9_]{3,32}$") - .unwrap() + .expect("failed to compile regex") .is_match(username) } @@ -139,7 +171,7 @@ impl EcdarApi for ConcreteEcdarApi { let model = Model { id: model.id, name: model.name, - components_info: serde_json::from_value(model.components_info).unwrap(), + components_info: serde_json::from_value(model.components_info).map_err(|err| Status::internal(err.to_string()))?, owner_id: model.owner_id, }; @@ -156,7 +188,7 @@ impl EcdarApi for ConcreteEcdarApi { let session = self .contexts .session_context - .get_by_token(TokenType::AccessToken, request.token_string().unwrap()) + .get_by_token(TokenType::AccessToken, request.token_string().ok_or(Status::internal("failed to get token from request metadata"))?) .await .map_err(|err| Status::new(Code::Internal, err.to_string()))? .ok_or_else(|| { @@ -198,7 +230,7 @@ impl EcdarApi for ConcreteEcdarApi { model_id: query.model_id, query: query.string, result: match query.result { - Some(result) => serde_json::from_value(result).unwrap(), + Some(result) => serde_json::from_value(result).expect("failed to parse message"), //TODO better error handling None => "".to_owned(), }, outdated: query.outdated, @@ -222,7 +254,7 @@ impl EcdarApi for ConcreteEcdarApi { .ok_or(Status::internal("Could not get uid from request metadata"))?; let components_info = match message.clone().components_info { - Some(components_info) => serde_json::to_value(components_info).unwrap(), + Some(components_info) => serde_json::to_value(components_info).map_err(|err| Status::internal(err.to_string()))?, None => return Err(Status::invalid_argument("No components info provided")), }; @@ -268,10 +300,10 @@ impl EcdarApi for ConcreteEcdarApi { let session = self .contexts .session_context - .get_by_token(TokenType::AccessToken, request.token_string().unwrap()) + .get_by_token(TokenType::AccessToken, request.token_string().ok_or(Status::internal("Failed to get token from request metadata"))?) .await - .unwrap() - .unwrap(); + .map_err(|_err| Status::internal("failed to query database"))? //TODO better error message + .ok_or(Status::not_found("token not found"))?; let in_use = in_use::Model { model_id: model.clone().id, @@ -279,8 +311,11 @@ impl EcdarApi for ConcreteEcdarApi { latest_activity: Default::default(), }; - self.contexts.in_use_context.create(in_use).await.unwrap(); - self.contexts.access_context.create(access).await.unwrap(); + self.contexts.in_use_context.create(in_use).await + .map_err(|_err| Status::new(Code::Internal, "failed to create entity"))?; + + self.contexts.access_context.create(access).await + .map_err(|_err| Status::new(Code::Internal, "failed to create entity"))?; Ok(Response::new(CreateModelResponse { id: model.id })) } @@ -336,7 +371,7 @@ impl EcdarApi for ConcreteEcdarApi { let session = match self .contexts .session_context - .get_by_token(TokenType::AccessToken, request.token_string().unwrap()) + .get_by_token(TokenType::AccessToken, request.token_string().ok_or(Status::new(Code::Internal,"Failed to get token from request metadata"))?) //? better error message? .await { Ok(Some(session)) => session, @@ -383,7 +418,7 @@ impl EcdarApi for ConcreteEcdarApi { None => model.name, }, components_info: match message.clone().components_info { - Some(components_info) => serde_json::to_value(components_info).unwrap(), + Some(components_info) => serde_json::to_value(components_info).map_err(|err| Status::new(Code::Internal,err.to_string()))?, None => model.components_info, }, owner_id: match message.clone().owner_id { diff --git a/src/api/hashing_context.rs b/src/api/hashing_context.rs index a4cd7b5..6a9d166 100644 --- a/src/api/hashing_context.rs +++ b/src/api/hashing_context.rs @@ -8,11 +8,12 @@ pub trait HashingContextTrait: Send + Sync { pub struct HashingContext; impl HashingContextTrait for HashingContext { + //! Methods should not panic, but propogate their result to the caller fn hash_password(&self, password: String) -> String { - hash(password, DEFAULT_COST).unwrap() + hash(password, DEFAULT_COST).expect("failed to hash password") } fn verify_password(&self, password: String, hash: &str) -> bool { - verify(password, hash).unwrap() + verify(password, hash).expect("failed to verify password") } } diff --git a/src/api/reveaal_context.rs b/src/api/reveaal_context.rs index f0c391b..4e3e2df 100644 --- a/src/api/reveaal_context.rs +++ b/src/api/reveaal_context.rs @@ -15,7 +15,7 @@ pub struct ReveaalContext; impl ReveaalContext { async fn get_connection() -> EcdarBackendClient { let url = env::var("REVEAAL_ADDRESS").expect("Expected REVEAAL_ADDRESS to be set."); - EcdarBackendClient::connect(url).await.unwrap() + EcdarBackendClient::connect(url).await.expect("failed to connect to Ecdar backend") //? Perhaps the error handling should be more graceful } } @@ -25,43 +25,41 @@ impl EcdarBackend for ReveaalContext { &self, request: Request<()>, ) -> Result, Status> { - Ok(ReveaalContext::get_connection() + ReveaalContext::get_connection() .await .get_user_token(request) .await - .unwrap()) } async fn send_query( &self, request: Request, ) -> Result, Status> { - Ok(ReveaalContext::get_connection() + ReveaalContext::get_connection() .await .send_query(request) .await - .unwrap()) + } async fn start_simulation( &self, request: Request, ) -> Result, Status> { - Ok(ReveaalContext::get_connection() + ReveaalContext::get_connection() .await .start_simulation(request) .await - .unwrap()) + } async fn take_simulation_step( &self, request: Request, ) -> Result, Status> { - Ok(ReveaalContext::get_connection() + ReveaalContext::get_connection() .await .take_simulation_step(request) .await - .unwrap()) } } diff --git a/src/api/server.rs b/src/api/server.rs index edfdfb9..55a584d 100644 --- a/src/api/server.rs +++ b/src/api/server.rs @@ -20,7 +20,7 @@ pub async fn start_grpc_server( let addr = env::var("API_ADDRESS") .expect("Expected API_ADDRESS to be set.") .parse() - .unwrap(); + .expect("Failed to parse IP address"); println!("Starting grpc server on '{}'", addr); diff --git a/src/database/database_context.rs b/src/database/database_context.rs index f13d824..d95079b 100644 --- a/src/database/database_context.rs +++ b/src/database/database_context.rs @@ -36,7 +36,7 @@ impl PostgresDatabaseContext { #[async_trait] impl DatabaseContextTrait for PostgresDatabaseContext { async fn reset(&self) -> Result, DbErr> { - Migrator::fresh(&self.db_connection).await.unwrap(); + Migrator::fresh(&self.db_connection).await.expect("failed to connect to database"); Ok(Arc::new(PostgresDatabaseContext { db_connection: self.get_connection(), @@ -64,7 +64,7 @@ impl SQLiteDatabaseContext { #[async_trait] impl DatabaseContextTrait for SQLiteDatabaseContext { async fn reset(&self) -> Result, DbErr> { - Migrator::fresh(&self.db_connection).await.unwrap(); + Migrator::fresh(&self.db_connection).await.expect("failed to connect to database"); Ok(Arc::new(SQLiteDatabaseContext { db_connection: self.get_connection(), diff --git a/src/main.rs b/src/main.rs index 15bbcd4..9fb1a96 100644 --- a/src/main.rs +++ b/src/main.rs @@ -45,7 +45,7 @@ async fn main() -> Result<(), Box> { hashing_context: Arc::new(HashingContext), }; - start_grpc_server(contexts).await.unwrap(); + start_grpc_server(contexts).await.expect("failed to start grpc server"); Ok(()) }