diff --git a/Cargo.lock b/Cargo.lock index 171b7a2..18bc0f6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -809,12 +809,14 @@ dependencies = [ name = "giterated-stack" version = "0.1.0" dependencies = [ + "anyhow", "async-trait", "bincode", "futures-util", "giterated-models", "serde", "serde_json", + "thiserror", "tokio", "tracing", ] diff --git a/giterated-daemon/src/authorization.rs b/giterated-daemon/src/authorization.rs index 1bb1fe5..4ed564c 100644 --- a/giterated-daemon/src/authorization.rs +++ b/giterated-daemon/src/authorization.rs @@ -14,6 +14,11 @@ use giterated_models::user::User; use giterated_models::value::GetValueTyped; use giterated_models::{object::ObjectRequest, settings::SetSetting, value::GiteratedObjectValue}; + +#[derive(Debug, thiserror::Error)] +#[error("unauthorized")] +pub struct UnauthorizedError; + #[async_trait::async_trait] pub trait AuthorizedOperation { /// Authorizes the operation, returning whether the operation was @@ -23,7 +28,7 @@ pub trait AuthorizedOperation { authenticating_user: Option<&User>, object: &O, state: &mut S, - ) -> Result>; + ) -> Result>; } #[async_trait::async_trait] @@ -33,7 +38,7 @@ impl AuthorizedOperation for SetSetting { _authenticating_user: Option<&User>, _object: &User, _state: &mut ConnectionState, - ) -> Result> { + ) -> Result> { // TODO Ok(true) } @@ -46,7 +51,7 @@ impl AuthorizedOperation for SetSetting { _authenticating_user: Option<&User>, _object: &Repository, _state: &mut ConnectionState, - ) -> Result> { + ) -> Result> { // TODO Ok(true) } @@ -61,7 +66,7 @@ impl _authenticating_user: Option<&User>, _object: &Repository, _state: &mut ConnectionState, - ) -> Result> { + ) -> Result> { // TODO Ok(true) } @@ -74,7 +79,7 @@ impl AuthorizedOperation for ObjectRequest { _authenticating_user: Option<&User>, _object: &Repository, _state: &mut ConnectionState, - ) -> Result> { + ) -> Result> { // TODO Ok(true) } @@ -87,7 +92,7 @@ impl AuthorizedOperation for RepositoryFileInspectR _authenticating_user: Option<&User>, _object: &Repository, _state: &mut ConnectionState, - ) -> Result> { + ) -> Result> { // TODO Ok(true) } @@ -100,7 +105,7 @@ impl AuthorizedOperation for RepositoryIssuesReques _authenticating_user: Option<&User>, _object: &Repository, _state: &mut ConnectionState, - ) -> Result> { + ) -> Result> { // TODO Ok(true) } @@ -113,7 +118,7 @@ impl AuthorizedOperation for RepositoryIssueLabelsR _authenticating_user: Option<&User>, _object: &Repository, _state: &mut ConnectionState, - ) -> Result> { + ) -> Result> { // TODO Ok(true) } @@ -126,7 +131,7 @@ impl AuthorizedOperation for RepositoryIssuesCountR _authenticating_user: Option<&User>, _object: &Repository, _state: &mut ConnectionState, - ) -> Result> { + ) -> Result> { // TODO Ok(true) } diff --git a/giterated-daemon/src/connection/wrapper.rs b/giterated-daemon/src/connection/wrapper.rs index 3691a7b..b24a03a 100644 --- a/giterated-daemon/src/connection/wrapper.rs +++ b/giterated-daemon/src/connection/wrapper.rs @@ -170,9 +170,12 @@ pub async fn connection_wrapper( operation_state.instance = None; if let Err(OperationError::Internal(internal_error)) = &result { - error!("An internal error has occured: {}", internal_error); + error!("An internal error has occurred:\n{:?}", internal_error); } + // Map error to the network variant + let result = result.map_err(|e| e.into_network()); + let mut socket = connection_state.socket.lock().await; let _ = socket .send(Message::Binary(bincode::serialize(&result).unwrap())) diff --git a/giterated-daemon/src/database_backend/handler.rs b/giterated-daemon/src/database_backend/handler.rs index 7a366a1..a66a84d 100644 --- a/giterated-daemon/src/database_backend/handler.rs +++ b/giterated-daemon/src/database_backend/handler.rs @@ -1,9 +1,11 @@ -use std::{error::Error, sync::Arc}; +use std::sync::Arc; use futures_util::{future::LocalBoxFuture, FutureExt}; use giterated_models::{ authenticated::UserAuthenticationToken, - error::{GetValueError, InstanceError, OperationError, RepositoryError, UserError}, + error::{ + GetValueError, InstanceError, IntoInternalError, OperationError, RepositoryError, UserError, + }, instance::{ AuthenticationTokenRequest, Instance, RegisterAccountRequest, RepositoryCreateRequest, }, @@ -39,7 +41,7 @@ pub fn user_get_repositories( let repositories_response = user_backend .repositories_for_user(&requester, &object) .await - .map_err(|e| OperationError::Internal(e.to_string()))?; + .as_internal_error()?; drop(user_backend); let mut repositories_backend = state.repository_backend.lock().await; @@ -49,7 +51,7 @@ pub fn user_get_repositories( if repositories_backend .exists(&requester, &repository.repository) .await - .map_err(|e| OperationError::Internal(e.to_string()))? + .as_internal_error()? { repositories.push(repository); } @@ -72,7 +74,7 @@ pub fn user_get_value( let value = user_backend .get_value(&object, &operation.value_name) .await - .map_err(|e| OperationError::Internal(e.to_string()))?; + .as_internal_error()?; Ok(value) } @@ -91,7 +93,7 @@ pub fn user_get_setting( let value = user_backend .get_setting(&object, &operation.setting_name) .await - .map_err(|e| OperationError::Internal(e.to_string()))?; + .as_internal_error()?; Ok(value.0) } @@ -125,7 +127,7 @@ pub fn repository_info( }, ) .await - .map_err(|err| OperationError::Internal(format!("{:?}", err)))?; + .as_internal_error()?; let statistics = repository_backend .repository_get_statistics( @@ -136,7 +138,7 @@ pub fn repository_info( }, ) .await - .map_err(|err| OperationError::Internal(format!("{:?}", err)))?; + .as_internal_error()?; drop(repository_backend); let info = RepositoryView { @@ -146,11 +148,11 @@ pub fn repository_info( visibility: object .get::(&operation_state) .await - .map_err(|e| OperationError::Internal(format!("{:?}: {}", e.source(), e)))?, + .as_internal_error()?, default_branch: object .get::(&operation_state) .await - .map_err(|e| OperationError::Internal(format!("{:?}: {}", e.source(), e)))?, + .as_internal_error()?, // TODO: Can't be a simple get function, this needs to be returned alongside the tree as this differs depending on the rev and path. latest_commit: object.get::(&operation_state).await.ok(), stats: statistics, @@ -187,7 +189,7 @@ pub fn repository_get_statistics( &RepositoryStatisticsRequest { rev: operation.rev }, ) .await - .map_err(|err| OperationError::Internal(format!("{:?}", err)))?; + .as_internal_error()?; drop(repository_backend); Ok(statistics) @@ -215,7 +217,7 @@ pub fn repository_get_branches( let branches = repository_backend .repository_get_branches(&requester, object.object(), &operation) .await - .map_err(|err| OperationError::Internal(format!("{:?}", err)))?; + .as_internal_error()?; drop(repository_backend); Ok(branches) @@ -248,7 +250,7 @@ pub fn repository_file_from_id( &RepositoryFileFromIdRequest(operation.0), ) .await - .map_err(|err| OperationError::Internal(format!("{:?}", err)))?; + .as_internal_error()?; drop(repository_backend); Ok(file) @@ -283,7 +285,7 @@ pub fn repository_file_from_path( }, ) .await - .map_err(|err| OperationError::Internal(format!("{:?}", err)))?; + .as_internal_error()?; drop(repository_backend); Ok(file) @@ -318,7 +320,7 @@ pub fn repository_last_commit_of_file( }, ) .await - .map_err(|err| OperationError::Internal(format!("{:?}", err)))?; + .as_internal_error()?; drop(repository_backend); Ok(commit) @@ -350,7 +352,7 @@ pub fn repository_commit_by_id( &RepositoryCommitFromIdRequest(operation.0), ) .await - .map_err(|err| OperationError::Internal(format!("{:?}", err)))?; + .as_internal_error()?; drop(repository_backend); Ok(commit) @@ -378,7 +380,7 @@ pub fn repository_diff( let diff = repository_backend .repository_diff(&requester, object.object(), &operation) .await - .map_err(|err| OperationError::Internal(format!("{:?}", err)))?; + .as_internal_error()?; drop(repository_backend); Ok(diff) @@ -406,7 +408,7 @@ pub fn repository_diff_patch( let patch = repository_backend .repository_diff_patch(&requester, object.object(), &operation) .await - .map_err(|err| OperationError::Internal(format!("{:?}", err)))?; + .as_internal_error()?; drop(repository_backend); Ok(patch) @@ -434,7 +436,7 @@ pub fn repository_commit_before( let file = repository_backend .repository_commit_before(&requester, object.object(), &operation) .await - .map_err(|err| OperationError::Internal(format!("{:?}", err)))?; + .as_internal_error()?; drop(repository_backend); Ok(file) @@ -454,9 +456,7 @@ pub fn repository_get_value( let value = repository_backend .get_value(&object, &operation.value_name) .await - .map_err(|e| { - OperationError::Internal(format!("error getting value: {}", e.to_string())) - })?; + .as_internal_error()?; Ok(value) } @@ -475,7 +475,7 @@ pub fn repository_get_setting( let value = repository_backend .get_setting(&object, &operation.setting_name) .await - .map_err(|e| OperationError::Internal(e.to_string()))?; + .as_internal_error()?; Ok(value.0) } @@ -493,10 +493,7 @@ pub fn instance_authentication_request( async move { let mut backend = state.user_backend.lock().await; - backend - .login(&object, operation) - .await - .map_err(|e| OperationError::Internal(e.to_string())) + backend.login(&object, operation).await.as_internal_error() } .boxed_local() } @@ -511,10 +508,7 @@ pub fn instance_registration_request( async move { let mut backend = state.user_backend.lock().await; - backend - .register(operation) - .await - .map_err(|e| OperationError::Internal(e.to_string())) + backend.register(operation).await.as_internal_error() } .boxed_local() } @@ -533,7 +527,7 @@ pub fn instance_create_repository_request( backend .create_repository(&requester, &operation) .await - .map_err(|e| OperationError::Internal(e.to_string())) + .as_internal_error() } .boxed_local() } @@ -552,10 +546,9 @@ pub fn user_get_value_display_name( let raw_value = backend .get_value(&object, &operation.value_name) .await - .map_err(|e| OperationError::Internal(e.to_string()))?; + .as_internal_error()?; - Ok(serde_json::from_value(raw_value.into_inner()) - .map_err(|e| OperationError::Internal(e.to_string()))?) + Ok(serde_json::from_value(raw_value.into_inner()).as_internal_error()?) } .boxed_local() } @@ -573,10 +566,9 @@ pub fn user_get_value_bio( let raw_value = backend .get_value(&object, &operation.value_name) .await - .map_err(|e| OperationError::Internal(e.to_string()))?; + .as_internal_error()?; - Ok(serde_json::from_value(raw_value.into_inner()) - .map_err(|e| OperationError::Internal(e.to_string()))?) + Ok(serde_json::from_value(raw_value.into_inner()).as_internal_error()?) } .boxed_local() } @@ -594,10 +586,9 @@ pub fn repository_get_value_description( let raw_value = backend .get_value(&object, &operation.value_name) .await - .map_err(|e| OperationError::Internal(e.to_string()))?; + .as_internal_error()?; - Ok(serde_json::from_value(raw_value.into_inner()) - .map_err(|e| OperationError::Internal(e.to_string()))?) + Ok(serde_json::from_value(raw_value.into_inner()).as_internal_error()?) } .boxed_local() } @@ -615,10 +606,9 @@ pub fn repository_get_value_visibility( let raw_value = backend .get_value(&object, &operation.value_name) .await - .map_err(|e| OperationError::Internal(e.to_string()))?; + .as_internal_error()?; - Ok(serde_json::from_value(raw_value.into_inner()) - .map_err(|e| OperationError::Internal(e.to_string()))?) + Ok(serde_json::from_value(raw_value.into_inner()).as_internal_error()?) } .boxed_local() } @@ -636,10 +626,9 @@ pub fn repository_get_default_branch( let raw_value = backend .get_value(&object, &operation.value_name) .await - .map_err(|e| OperationError::Internal(e.to_string()))?; + .as_internal_error()?; - Ok(serde_json::from_value(raw_value.into_inner()) - .map_err(|e| OperationError::Internal(e.to_string()))?) + Ok(serde_json::from_value(raw_value.into_inner()).as_internal_error()?) } .boxed_local() } @@ -657,10 +646,9 @@ pub fn repository_get_latest_commit( let raw_value = backend .get_value(&object, &operation.value_name) .await - .map_err(|e| OperationError::Internal(e.to_string()))?; + .as_internal_error()?; - Ok(serde_json::from_value(raw_value.into_inner()) - .map_err(|e| OperationError::Internal(e.to_string()))?) + Ok(serde_json::from_value(raw_value.into_inner()).as_internal_error()?) } .boxed_local() } diff --git a/giterated-models/src/error.rs b/giterated-models/src/error.rs index 9015c5d..b056171 100644 --- a/giterated-models/src/error.rs +++ b/giterated-models/src/error.rs @@ -1,3 +1,5 @@ +use std::fmt::Display; + use serde::{Deserialize, Serialize}; #[derive(Debug, thiserror::Error, Deserialize, Serialize)] @@ -20,12 +22,76 @@ pub enum GetValueError { InvalidObject, } -#[derive(Serialize, Deserialize, Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error)] +#[error("unauthorized")] +pub struct UnauthorizedError; + +#[derive(Debug, thiserror::Error)] pub enum OperationError { - #[error("the operation was handled but an error occured")] + #[error("the operation was handled but an error occurred")] + Operation(#[from] B), + #[error("an internal error occurred: {0:?}")] + Internal(anyhow::Error), + #[error("the operation was unhandled or unrecognized")] + Unhandled, +} + +pub trait IntoInternalError: Sized { + fn as_internal_error(self) -> Result>; + + fn as_internal_error_with_context(self, context: C) -> Result> + where + C: Display + Send + Sync + 'static, + { + let internal_error = self.as_internal_error::(); + + match internal_error { + Ok(success) => Ok(success), + Err(OperationError::Internal(internal)) => { + Err(OperationError::Internal(internal.context(context))) + } + _ => unreachable!(), + } + } +} + +impl, T> IntoInternalError for Result { + fn as_internal_error(self) -> Result> { + match self { + Ok(success) => Ok(success), + Err(err) => Err(OperationError::Internal(err.into())), + } + } +} + +impl OperationError { + pub fn into_network(self) -> NetworkOperationError { + match self { + OperationError::Operation(operation_error) => { + NetworkOperationError::Operation(operation_error) + } + OperationError::Internal(_) => NetworkOperationError::Internal, + OperationError::Unhandled => NetworkOperationError::Unhandled, + } + } +} + +#[derive(Debug, thiserror::Error)] +#[error("an extractor failed with an error {0}")] +pub struct ExtractorError>(#[from] pub E); + +impl> IntoInternalError for ExtractorError { + fn as_internal_error(self) -> Result> { + todo!() + } +} + +#[derive(Serialize, Deserialize, Debug, thiserror::Error)] +pub enum NetworkOperationError { + #[error("the operation was handled but an error occurred")] Operation(#[from] B), - #[error("an internal error occured: {0}")] - Internal(String), + #[error("internal error")] + Internal, #[error("the operation was unhandled or unrecognized")] Unhandled, } diff --git a/giterated-stack/Cargo.toml b/giterated-stack/Cargo.toml index 7fcbe96..f4c4068 100644 --- a/giterated-stack/Cargo.toml +++ b/giterated-stack/Cargo.toml @@ -13,4 +13,6 @@ serde_json = "1.0" bincode = "1.3" futures-util = "0.3" tracing = "0.1" -tokio = { version = "1.32", features = [ "full" ] } \ No newline at end of file +tokio = { version = "1.32", features = [ "full" ] } +anyhow = "1" +thiserror = "1" \ No newline at end of file diff --git a/giterated-stack/src/handler.rs b/giterated-stack/src/handler.rs index df1fa11..113a7d8 100644 --- a/giterated-stack/src/handler.rs +++ b/giterated-stack/src/handler.rs @@ -1,9 +1,10 @@ use std::{any::Any, collections::HashMap, sync::Arc}; +use anyhow::Context; use futures_util::FutureExt; use giterated_models::{ authenticated::AuthenticatedPayload, - error::{GetValueError, OperationError}, + error::{GetValueError, IntoInternalError, OperationError}, instance::Instance, message::GiteratedMessage, object::{ @@ -451,9 +452,28 @@ impl GiteratedStack { // Special case let operation: GetValue = serde_json::from_slice(&message.payload.0).unwrap(); - return self - .network_get_value(object, object_type.clone(), operation, operation_state) + let result = self + .network_get_value( + object, + object_type.clone(), + operation.clone(), + operation_state, + ) .await; + + // In the case of internal errors, attach context + let result = result.map_err(|err| match err { + OperationError::Operation(operation) => OperationError::Operation(operation), + OperationError::Internal(internal) => { + OperationError::Internal(internal.context(format!( + "{}::get_value::<{}> outcome", + object_type, operation.value_name + ))) + } + OperationError::Unhandled => OperationError::Unhandled, + }); + + return result; } else if message.operation == "get_setting" { let operation: GetSetting = serde_json::from_slice(&message.payload.0).unwrap(); let setting_meta = self @@ -478,7 +498,12 @@ impl GiteratedStack { OperationError::Operation(serde_json::to_vec(&failure).unwrap()) } - OperationError::Internal(internal) => OperationError::Internal(internal), + OperationError::Internal(internal) => { + OperationError::Internal(internal.context(format!( + "{}::get_setting::<{}> handler outcome", + object_type, setting_meta.name + ))) + } OperationError::Unhandled => OperationError::Unhandled, }), }; @@ -508,8 +533,11 @@ impl GiteratedStack { .get(&target) .ok_or_else(|| OperationError::Unhandled)?; - let operation = (meta.deserialize)(&message.payload.0) - .map_err(|e| OperationError::Internal(e.to_string()))?; + let operation = + (meta.deserialize)(&message.payload.0).as_internal_error_with_context(format!( + "deserializing operation {}::{}", + target.object_name, target.operation_name + ))?; trace!( "Deserialized operation for network message {}::<{}>", @@ -534,14 +562,17 @@ impl GiteratedStack { // Deserialize the raw result for the network match raw_result { - Ok(success) => Ok((meta.serialize_success)(success) - .map_err(|e| OperationError::Internal(e.to_string()))?), + Ok(success) => Ok((meta.serialize_success)(success).as_internal_error()?), Err(err) => Err(match err { - OperationError::Operation(failure) => OperationError::Operation( - (meta.serialize_error)(failure) - .map_err(|e| OperationError::Internal(e.to_string()))?, - ), - OperationError::Internal(internal) => OperationError::Internal(internal), + OperationError::Operation(failure) => { + OperationError::Operation((meta.serialize_error)(failure).as_internal_error()?) + } + OperationError::Internal(internal) => { + OperationError::Internal(internal.context(format!( + "operation {}::{} handler outcome", + target.object_name, target.operation_name + ))) + } OperationError::Unhandled => OperationError::Unhandled, }), } @@ -580,8 +611,7 @@ impl GiteratedStack { { Ok(success) => { // Serialize success, which is the value type itself - let serialized = (value_meta.serialize)(success) - .map_err(|e| OperationError::Internal(e.to_string()))?; + let serialized = (value_meta.serialize)(success).as_internal_error()?; Ok(serialized) } @@ -590,10 +620,7 @@ impl GiteratedStack { // Failure is sourced from GetValue operation, but this is hardcoded for now let failure: GetValueError = *failure.downcast().unwrap(); - OperationError::Operation( - serde_json::to_vec(&failure) - .map_err(|e| OperationError::Internal(e.to_string()))?, - ) + OperationError::Operation(serde_json::to_vec(&failure).as_internal_error()?) } OperationError::Internal(internal) => OperationError::Internal(internal), OperationError::Unhandled => OperationError::Unhandled, @@ -693,7 +720,13 @@ impl ObjectBackend for Arc { OperationError::Operation(failure) => { OperationError::Operation(*failure.downcast::().unwrap()) } - OperationError::Internal(internal) => OperationError::Internal(internal), + OperationError::Internal(internal) => { + OperationError::Internal(internal.context(format!( + "{}::get_value::<{}> handler outcome", + O::object_name(), + value_name + ))) + } OperationError::Unhandled => OperationError::Unhandled, }), }; @@ -702,6 +735,7 @@ impl ObjectBackend for Arc { return Err(OperationError::Unhandled); } else if operation.is::() { let get_setting: Box = operation.downcast().unwrap(); + let setting_name = get_setting.setting_name.clone(); let raw_result = self .get_setting( @@ -725,7 +759,13 @@ impl ObjectBackend for Arc { // We know this is the right type OperationError::Operation(*failure.downcast().unwrap()) } - OperationError::Internal(internal) => OperationError::Internal(internal), + OperationError::Internal(internal) => { + OperationError::Internal(internal.context(format!( + "{}::get_setting::<{}> handler outcome", + O::object_name(), + setting_name + ))) + } OperationError::Unhandled => OperationError::Unhandled, }), }; @@ -773,7 +813,12 @@ impl ObjectBackend for Arc { match raw_result { Ok(result) => Ok(*result.downcast::().unwrap()), Err(err) => Err(match err { - OperationError::Internal(internal) => OperationError::Internal(internal), + OperationError::Internal(internal) => { + OperationError::Internal(internal.context(format!( + "operation {}::{} handler outcome", + operation_type.object_name, operation_type.operation_name + ))) + } OperationError::Operation(boxed_error) => { OperationError::Operation(*boxed_error.downcast::().unwrap()) } diff --git a/giterated-stack/src/lib.rs b/giterated-stack/src/lib.rs index 2ba5444..5f2a99c 100644 --- a/giterated-stack/src/lib.rs +++ b/giterated-stack/src/lib.rs @@ -5,12 +5,12 @@ pub use meta::*; pub mod state; pub mod update; -use std::{any::Any, future::Future, ops::Deref, pin::Pin, sync::Arc}; +use std::{any::Any, convert::Infallible, future::Future, ops::Deref, pin::Pin, sync::Arc}; use core::fmt::Debug; use futures_util::FutureExt; use giterated_models::{ - error::OperationError, + error::{ExtractorError, IntoInternalError, OperationError, UnauthorizedError}, instance::{ AuthenticationTokenRequest, Instance, RegisterAccountRequest, RepositoryCreateRequest, }, @@ -22,7 +22,6 @@ use giterated_models::{ user::User, value::GetValue, }; -use serde::{de::DeserializeOwned, Serialize}; #[derive(Clone, Debug, Hash, Eq, PartialEq)] struct ObjectOperationPair { @@ -110,6 +109,7 @@ where >::Failure: Send, S: Send + Sync + Clone + 'static, O1: FromOperationState, + ExtractorError<>::Error>: Into, { fn operation_name(&self) -> &str { D::operation_name() @@ -128,7 +128,7 @@ where ) -> Result> { let o1 = O1::from_state(object, &operation, operation_state) .await - .map_err(|e| OperationError::Internal(e.to_string()))?; + .as_internal_error()?; self.clone()(object, operation, state, o1).await } } @@ -152,7 +152,9 @@ where >::Failure: Send, S: Send + Sync + Clone + 'static, O1: FromOperationState, + ExtractorError<>::Error>: Into, O2: FromOperationState, + ExtractorError<>::Error>: Into, { fn operation_name(&self) -> &str { D::operation_name() @@ -171,10 +173,10 @@ where ) -> Result> { let o1 = O1::from_state(object, &operation, operation_state) .await - .map_err(|e| OperationError::Internal(e.to_string()))?; + .as_internal_error()?; let o2 = O2::from_state(object, &operation, operation_state) .await - .map_err(|e| OperationError::Internal(e.to_string()))?; + .as_internal_error()?; self.clone()(object, operation, state, o1, o2).await } } @@ -199,8 +201,11 @@ where >::Failure: Send, S: Send + Sync + Clone + 'static, O1: FromOperationState, + ExtractorError<>::Error>: Into, O2: FromOperationState, + ExtractorError<>::Error>: Into, O3: FromOperationState, + ExtractorError<>::Error>: Into, { fn operation_name(&self) -> &str { D::operation_name() @@ -219,13 +224,13 @@ where ) -> Result> { let o1 = O1::from_state(object, &operation, operation_state) .await - .map_err(|e| OperationError::Internal(e.to_string()))?; + .as_internal_error()?; let o2 = O2::from_state(object, &operation, operation_state) .await - .map_err(|e| OperationError::Internal(e.to_string()))?; + .as_internal_error()?; let o3 = O3::from_state(object, &operation, operation_state) .await - .map_err(|e| OperationError::Internal(e.to_string()))?; + .as_internal_error()?; self.clone()(object, operation, state, o1, o2, o3).await } } @@ -315,26 +320,26 @@ impl OperationWrapper { #[async_trait::async_trait(?Send)] pub trait FromOperationState>: Sized + Clone { - type Error: Serialize + DeserializeOwned; + type Error: Into; async fn from_state( object: &O, operation: &D, state: &StackOperationState, - ) -> Result>; + ) -> Result>; } #[async_trait::async_trait(?Send)] impl> FromOperationState for Arc { - type Error = (); + type Error = Infallible; async fn from_state( _object: &O, _operation: &D, state: &StackOperationState, - ) -> Result> { + ) -> Result> { Ok(state.runtime.clone()) } } @@ -343,32 +348,36 @@ impl> FromOperationState impl> FromOperationState for StackOperationState { - type Error = (); + type Error = Infallible; async fn from_state( _object: &O, _operation: &D, state: &StackOperationState, - ) -> Result> { + ) -> Result> { Ok(state.clone()) } } +#[derive(Debug, thiserror::Error)] +#[error("missing value")] +pub struct MissingValue; + #[async_trait::async_trait(?Send)] impl + Send + Sync> FromOperationState for AuthenticatedUser { - type Error = (); + type Error = MissingValue; async fn from_state( _object: &O, _operation: &D, state: &StackOperationState, - ) -> Result> { + ) -> Result> { state .user .clone() - .ok_or_else(|| OperationError::Operation(())) + .ok_or_else(|| ExtractorError(MissingValue)) } } @@ -376,17 +385,17 @@ impl + Send + Sync> FromOperationSt impl + Send + Sync> FromOperationState for AuthenticatedInstance { - type Error = (); + type Error = MissingValue; async fn from_state( _object: &O, _operation: &D, state: &StackOperationState, - ) -> Result> { + ) -> Result> { state .instance .clone() - .ok_or_else(|| OperationError::Operation(())) + .ok_or_else(|| ExtractorError(MissingValue)) } } @@ -397,13 +406,13 @@ impl< D: GiteratedOperation + Send + Sync, > FromOperationState for Option { - type Error = (); + type Error = Infallible; async fn from_state( object: &O, operation: &D, state: &StackOperationState, - ) -> Result, OperationError<()>> { + ) -> Result, ExtractorError> { Ok(T::from_state(object, operation, state).await.ok()) } } @@ -416,20 +425,24 @@ pub struct AuthorizedInstance(AuthenticatedInstance); #[async_trait::async_trait(?Send)] pub trait AuthorizedOperation: GiteratedOperation { + type Error: Into; + async fn authorize( &self, authorize_for: &O, state: &StackOperationState, - ) -> Result>; + ) -> Result>; } #[async_trait::async_trait(?Send)] impl AuthorizedOperation for GetValue { + type Error = anyhow::Error; + async fn authorize( &self, authorize_for: &O, operation_state: &StackOperationState, - ) -> Result> { + ) -> Result> { Ok(operation_state .runtime .get_object::(&authorize_for.to_string(), operation_state) @@ -440,15 +453,14 @@ impl AuthorizedOperation #[async_trait::async_trait(?Send)] impl AuthorizedOperation for SetSetting { + type Error = MissingValue; + async fn authorize( &self, authorize_for: &User, operation_state: &StackOperationState, - ) -> Result> { - let authenticated_user = operation_state - .user - .as_ref() - .ok_or_else(|| OperationError::Operation(()))?; + ) -> Result> { + let authenticated_user = operation_state.user.as_ref().ok_or_else(|| MissingValue)?; Ok(authorize_for == authenticated_user.deref()) } @@ -456,15 +468,14 @@ impl AuthorizedOperation for SetSetting { #[async_trait::async_trait(?Send)] impl AuthorizedOperation for GetSetting { + type Error = MissingValue; + async fn authorize( &self, authorize_for: &User, operation_state: &StackOperationState, - ) -> Result> { - let authenticated_user = operation_state - .user - .as_ref() - .ok_or_else(|| OperationError::Operation(()))?; + ) -> Result> { + let authenticated_user = operation_state.user.as_ref().ok_or_else(|| MissingValue)?; Ok(authorize_for == authenticated_user.deref()) } @@ -472,26 +483,28 @@ impl AuthorizedOperation for GetSetting { #[async_trait::async_trait(?Send)] impl AuthorizedOperation for SetSetting { + type Error = anyhow::Error; + async fn authorize( &self, authorize_for: &Repository, operation_state: &StackOperationState, - ) -> Result> { + ) -> Result> { let authenticated_user = operation_state .user .as_ref() - .ok_or_else(|| OperationError::Operation(()))?; + .ok_or_else(|| anyhow::Error::from(MissingValue))?; let mut object = operation_state .runtime .get_object::(&authorize_for.to_string(), operation_state) .await - .map_err(|e| OperationError::Internal(e.to_string()))?; + .map_err(|err| anyhow::Error::from(err))?; let access_list = object .get_setting::(operation_state) .await - .map_err(|e| OperationError::Internal(e.to_string()))?; + .map_err(|err| anyhow::Error::from(err))?; if access_list .0 @@ -508,26 +521,28 @@ impl AuthorizedOperation for SetSetting { #[async_trait::async_trait(?Send)] impl AuthorizedOperation for GetSetting { + type Error = anyhow::Error; + async fn authorize( &self, authorize_for: &Repository, operation_state: &StackOperationState, - ) -> Result> { + ) -> Result> { let authenticated_user = operation_state .user .as_ref() - .ok_or_else(|| OperationError::Operation(()))?; + .ok_or_else(|| anyhow::Error::from(MissingValue))?; let mut object = operation_state .runtime .get_object::(&authorize_for.to_string(), operation_state) .await - .map_err(|e| OperationError::Internal(e.to_string()))?; + .map_err(|err| anyhow::Error::from(err))?; let access_list = object .get_setting::(operation_state) .await - .map_err(|e| OperationError::Internal(e.to_string()))?; + .map_err(|err| anyhow::Error::from(err))?; if access_list .0 @@ -544,11 +559,13 @@ impl AuthorizedOperation for GetSetting { #[async_trait::async_trait(?Send)] impl AuthorizedOperation for RegisterAccountRequest { + type Error = Infallible; + async fn authorize( &self, authorize_for: &Instance, state: &StackOperationState, - ) -> Result> { + ) -> Result> { if state.our_instance == *authorize_for { Ok(true) } else { @@ -559,11 +576,13 @@ impl AuthorizedOperation for RegisterAccountRequest { #[async_trait::async_trait(?Send)] impl AuthorizedOperation for AuthenticationTokenRequest { + type Error = Infallible; + async fn authorize( &self, authorize_for: &Instance, state: &StackOperationState, - ) -> Result> { + ) -> Result> { if state.our_instance == *authorize_for { Ok(true) } else { @@ -574,11 +593,13 @@ impl AuthorizedOperation for AuthenticationTokenRequest { #[async_trait::async_trait(?Send)] impl AuthorizedOperation for RepositoryCreateRequest { + type Error = Infallible; + async fn authorize( &self, authorize_for: &Instance, state: &StackOperationState, - ) -> Result> { + ) -> Result> { if state.our_instance == *authorize_for { Ok(true) } else { @@ -589,20 +610,23 @@ impl AuthorizedOperation for RepositoryCreateRequest { #[async_trait::async_trait(?Send)] impl + Send + Sync> FromOperationState for AuthorizedUser { - type Error = (); + type Error = UnauthorizedError; async fn from_state( object: &User, operation: &A, state: &StackOperationState, - ) -> Result> { - let authenticated = AuthenticatedUser::from_state(object, operation, state).await?; + ) -> Result> { + // TODO + let authenticated = AuthenticatedUser::from_state(object, operation, state) + .await + .map_err(|_| ExtractorError(UnauthorizedError))?; match operation.authorize(object, state).await { Ok(authorized) => { assert!(authorized); } - Err(err) => return Err(OperationError::Internal(err.to_string())), + Err(_err) => return Err(ExtractorError(UnauthorizedError)), }; Ok(AuthorizedUser(authenticated)) @@ -613,20 +637,23 @@ impl + Send + Sync> FromOperationState for impl + Send + Sync> FromOperationState for AuthorizedInstance { - type Error = (); + type Error = UnauthorizedError; async fn from_state( object: &Instance, operation: &A, state: &StackOperationState, - ) -> Result> { - let authenticated = AuthenticatedInstance::from_state(object, operation, state).await?; + ) -> Result> { + //TODO + let authenticated = AuthenticatedInstance::from_state(object, operation, state) + .await + .map_err(|_| ExtractorError(UnauthorizedError))?; match operation.authorize(object, state).await { Ok(authorized) => { assert!(authorized); } - Err(err) => return Err(OperationError::Internal(err.to_string())), + Err(_err) => return Err(ExtractorError(UnauthorizedError)), }; Ok(AuthorizedInstance(authenticated))