blob: b8e75af2b9b292557f31e36c5a6d97592fce854a [file] [log] [blame]
// Copyright 2019 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 account_common::{AccountManagerError, ResultExt};
use failure::format_err;
use fidl::endpoints::{create_endpoints, ClientEnd};
use fidl_fuchsia_auth::{AuthProviderMarker, Status};
use fidl_fuchsia_auth_account_internal::{AccountHandlerContextMarker, AccountHandlerContextProxy};
use futures::future::{ready as fready, FutureObj};
use token_manager::TokenManagerError;
/// A type capable of acquiring `AuthProvider` connections from components implementing the
/// `AuthProviderFactory` interface.
///
/// The functionality is delegated to the component that launched the AccountHandler, through
/// methods it implements on the `AccountHandlerContext` interface.
pub struct AuthProviderSupplier {
/// The `AccountHandlerContext` interface supplied by the component that launched
/// AccountHandler
account_handler_context: AccountHandlerContextProxy,
}
impl AuthProviderSupplier {
/// Creates a new `AuthProviderSupplier` from the supplied `AccountHandlerContext`.
pub fn new(
account_handler_client_end: ClientEnd<AccountHandlerContextMarker>,
) -> Result<Self, AccountManagerError> {
Ok(AuthProviderSupplier {
account_handler_context: account_handler_client_end
.into_proxy()
.account_manager_status(fidl_fuchsia_auth_account::Status::IoError)?,
})
}
}
impl token_manager::AuthProviderSupplier for AuthProviderSupplier {
/// Asynchronously creates an `AuthProvider` for the requested `auth_provider_type` and
/// returns the `ClientEnd` for communication with it.
fn get<'a>(
&'a self, auth_provider_type: &'a str,
) -> FutureObj<'a, Result<ClientEnd<AuthProviderMarker>, TokenManagerError>> {
let (client_end, server_end) = match create_endpoints() {
Ok((client_end, server_end)) => (client_end, server_end),
Err(err) => {
let tm_err = TokenManagerError::new(Status::UnknownError).with_cause(err);
return FutureObj::new(Box::new(fready(Err(tm_err))));
}
};
FutureObj::new(Box::new(
async move {
match await!(self
.account_handler_context
.get_auth_provider(auth_provider_type, server_end))
{
Ok(fidl_fuchsia_auth_account::Status::Ok) => Ok(client_end),
Ok(stat) => Err(
TokenManagerError::new(Status::AuthProviderServiceUnavailable)
.with_cause(format_err!("AccountHandlerContext returned {:?}", stat)),
),
Err(err) => Err(TokenManagerError::new(Status::UnknownError).with_cause(err)),
}
},
))
}
}
#[cfg(test)]
mod tests {
use super::*;
use fidl::endpoints::{create_endpoints, ClientEnd, ServerEnd};
use fidl_fuchsia_auth_account_internal::AccountHandlerContextRequest;
use fuchsia_async as fasync;
use fuchsia_zircon as zx;
use futures::TryStreamExt;
use token_manager::AuthProviderSupplier as AuthProviderSupplierTrait;
const TEST_AUTH_PROVIDER_TYPE: &str = "test_auth_provider";
/// Runs a asynchronous test on the supplied executor to contruct a new AuthProviderSupplier
/// and request TEST_AUTH_PROVIDER.
fn do_get_test(
mut executor: fasync::Executor, client_end: ClientEnd<AccountHandlerContextMarker>,
expected_error: Option<Status>,
) {
let auth_provider_supplier = AuthProviderSupplier::new(client_end).unwrap();
executor.run_singlethreaded(
async move {
let result = await!(auth_provider_supplier.get(TEST_AUTH_PROVIDER_TYPE));
match expected_error {
Some(status) => {
assert!(result.is_err());
assert_eq!(status, result.unwrap_err().status);
}
None => {
assert!(result.is_ok());
}
}
},
);
}
/// Spawns a trivial task to respond to the first AccountHandlerContextRequest with the
/// supplied status, only if the first request is a get request for TEST_AUTH_PROVIDER_TYPE
fn spawn_account_handler_context_server(
server_end: ServerEnd<AccountHandlerContextMarker>,
status: fidl_fuchsia_auth_account::Status,
) {
fasync::spawn(
async move {
let mut request_stream = server_end.into_stream().unwrap();
// Only respond to the first received message, only when its of the intended type.
if let Ok(Some(AccountHandlerContextRequest::GetAuthProvider {
auth_provider_type,
auth_provider: _,
responder,
})) = await!(request_stream.try_next())
{
if auth_provider_type == TEST_AUTH_PROVIDER_TYPE {
responder
.send(status)
.expect("Failed to send test response");
}
}
},
);
}
#[test]
fn test_new_valid() {
// Note: AuthProviderSupplier schedules async tasks hence an executor needs to be defined.
let _executor = fasync::Executor::new().expect("Failed to create executor");
let (client_end, _) = create_endpoints::<AccountHandlerContextMarker>().unwrap();
assert!(AuthProviderSupplier::new(client_end).is_ok());
}
#[test]
fn test_new_invalid() {
// Note: AuthProviderSupplier schedules async tasks hence an executor needs to be defined.
let _executor = fasync::Executor::new().expect("Failed to create executor");
let client_end = ClientEnd::from(zx::Handle::invalid());
assert!(AuthProviderSupplier::new(client_end).is_err());
}
#[test]
fn test_get_valid() {
let executor = fasync::Executor::new().expect("Failed to create executor");
let (client_end, server_end) = create_endpoints::<AccountHandlerContextMarker>().unwrap();
spawn_account_handler_context_server(server_end, fidl_fuchsia_auth_account::Status::Ok);
do_get_test(executor, client_end, None);
}
#[test]
fn test_get_invalid() {
let executor = fasync::Executor::new().expect("Failed to create executor");
let (client_end, server_end) = create_endpoints::<AccountHandlerContextMarker>().unwrap();
spawn_account_handler_context_server(
server_end,
fidl_fuchsia_auth_account::Status::NotFound,
);
do_get_test(
executor,
client_end,
Some(Status::AuthProviderServiceUnavailable),
);
}
}