blob: 143f985b288edee7c5d6f9e0aa2c18b7354c7741 [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use anyhow::{format_err, Error};
use fidl_fuchsia_auth::Status::{self as TokenManagerStatus, *};
use fidl_fuchsia_identity_account::Error as AccountApiError;
use fidl_fuchsia_identity_authentication::Error as AuthenticationApiError;
use thiserror::Error;
/// An extension trait to simplify conversion of results based on general errors to
/// AccountManagerErrors.
pub trait ResultExt<T, E> {
/// Wraps the error in a non-fatal `AccountManagerError` with the supplied `AccountApiError`.
fn account_manager_error(self, api_error: AccountApiError) -> Result<T, AccountManagerError>;
}
impl<T, E> ResultExt<T, E> for Result<T, E>
where
E: Into<Error> + Send + Sync + Sized,
{
fn account_manager_error(self, api_error: AccountApiError) -> Result<T, AccountManagerError> {
self.map_err(|err| AccountManagerError::new(api_error).with_cause(err))
}
}
/// An Error type for problems encountered in the account manager and account handler. Each error
/// contains the fuchsia.identity.account.Error that should be reported back to the client and an
/// indication of whether it is fatal.
#[derive(Debug, Error)]
#[error("AccountManager error, returning {:?}. ({:?})", api_error, cause)]
pub struct AccountManagerError {
/// The most appropriate `fuchsia.identity.account.Error` to describe this problem.
pub api_error: AccountApiError,
/// Whether this error should be considered fatal, i.e. whether it should
/// terminate processing of all requests on the current channel.
pub fatal: bool,
/// The root cause of this error, if available.
pub cause: Option<Error>,
}
impl AccountManagerError {
/// Constructs a new non-fatal error based on the supplied `fuchsia.identity.account.Error`.
pub fn new(api_error: AccountApiError) -> Self {
AccountManagerError { api_error, fatal: false, cause: None }
}
/// Sets a cause on the current error.
pub fn with_cause<T: Into<Error>>(mut self, cause: T) -> Self {
self.cause = Some(cause.into());
self
}
}
impl From<fidl::Error> for AccountManagerError {
fn from(error: fidl::Error) -> Self {
AccountManagerError::new(AccountApiError::Resource).with_cause(error)
}
}
impl From<AccountApiError> for AccountManagerError {
fn from(api_error: AccountApiError) -> Self {
AccountManagerError::new(api_error)
}
}
impl From<TokenManagerStatus> for AccountManagerError {
fn from(token_manager_status: TokenManagerStatus) -> Self {
AccountManagerError {
api_error: match token_manager_status {
Ok => AccountApiError::Internal, // Invalid conversion
InternalError => AccountApiError::Internal,
InvalidAuthContext => AccountApiError::InvalidRequest,
InvalidRequest => AccountApiError::InvalidRequest,
IoError => AccountApiError::Resource,
NetworkError => AccountApiError::Network,
AuthProviderServiceUnavailable
| AuthProviderServerError
| UserNotFound
| ReauthRequired
| UserCancelled
| UnknownError => AccountApiError::Unknown,
},
fatal: false,
cause: Some(format_err!("Token manager error: {:?}", token_manager_status)),
}
}
}
impl From<AuthenticationApiError> for AccountManagerError {
fn from(authentication_error: AuthenticationApiError) -> Self {
AccountManagerError {
api_error: match authentication_error {
AuthenticationApiError::Internal => AccountApiError::Internal,
AuthenticationApiError::InvalidAuthContext => AccountApiError::InvalidRequest,
AuthenticationApiError::InvalidRequest => AccountApiError::InvalidRequest,
AuthenticationApiError::Resource => AccountApiError::Resource,
AuthenticationApiError::UnsupportedOperation => AccountApiError::UnsupportedOperation,
AuthenticationApiError::InvalidDataFormat
| AuthenticationApiError::Aborted // TODO(dnordstrom): Needs a non-generic error
| AuthenticationApiError::Unknown => AccountApiError::Unknown,
},
fatal: false,
cause: Some(format_err!("Authenticator error: {:?}", authentication_error)),
}
}
}
impl Into<AccountApiError> for AccountManagerError {
fn into(self) -> AccountApiError {
self.api_error.clone()
}
}
#[cfg(test)]
mod tests {
use super::*;
use anyhow::format_err;
const TEST_API_ERROR: AccountApiError = AccountApiError::Unknown;
fn create_test_error() -> Error {
format_err!("Test error")
}
#[test]
fn test_new() {
let cause = format_err!("Example cause");
let cause_str = format!("{:?}", cause);
let error = AccountManagerError::new(TEST_API_ERROR).with_cause(cause);
assert_eq!(error.api_error, TEST_API_ERROR);
assert!(!error.fatal);
assert_eq!(format!("{:?}", error.cause.unwrap()), cause_str);
}
#[test]
fn test_from_fidl_error() {
let error: AccountManagerError = fidl::Error::UnexpectedSyncResponse.into();
assert_eq!(error.api_error, AccountApiError::Resource);
assert!(!error.fatal);
assert_eq!(
format!("{:?}", error.cause.unwrap().downcast::<fidl::Error>().unwrap()),
format!("{:?}", fidl::Error::UnexpectedSyncResponse),
);
}
#[test]
fn test_from_identity_error() {
let error: AccountManagerError = TEST_API_ERROR.into();
assert_eq!(error.api_error, TEST_API_ERROR);
assert!(!error.fatal);
assert!(error.cause.is_none());
}
#[test]
fn test_from_authentication_error() {
let error: AccountManagerError = AuthenticationApiError::InvalidAuthContext.into();
let expected_cause = format_err!("Authenticator error: InvalidAuthContext");
assert_eq!(error.api_error, AccountApiError::InvalidRequest);
assert!(!error.fatal);
assert_eq!(format!("{:?}", error.cause.unwrap()), format!("{:?}", expected_cause));
}
#[test]
fn test_to_identity_error() {
let manager_error = AccountManagerError::new(TEST_API_ERROR);
let error: AccountApiError = manager_error.into();
assert_eq!(error, TEST_API_ERROR);
}
#[test]
fn test_account_manager_error() {
let test_result: Result<(), Error> = Err(create_test_error());
let wrapped_result = test_result.account_manager_error(TEST_API_ERROR);
assert_eq!(wrapped_result.as_ref().unwrap_err().api_error, TEST_API_ERROR);
assert_eq!(
format!("{:?}", wrapped_result.unwrap_err().cause.unwrap()),
format!("{:?}", create_test_error())
);
}
}