blob: 57ff3fce16731f6dd325500ef13779cd6e8f3c7b [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 anyhow::format_err;
use async_trait::async_trait;
use fidl::endpoints::{create_endpoints, ClientEnd};
use fidl_fuchsia_auth::Status;
use fidl_fuchsia_identity_external::{OauthMarker, OauthOpenIdConnectMarker, OpenIdConnectMarker};
use fidl_fuchsia_identity_internal::AccountHandlerContextProxy;
use token_manager::TokenManagerError;
/// A type capable of acquiring `AuthProvider` connections from components that implement it.
///
/// 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 `AccountHandlerContextProxy`.
pub fn new(account_handler_context: AccountHandlerContextProxy) -> Self {
AuthProviderSupplier { account_handler_context }
}
}
#[async_trait]
impl token_manager::AuthProviderSupplier for AuthProviderSupplier {
async fn get_oauth(
&self,
auth_provider_type: &str,
) -> Result<ClientEnd<OauthMarker>, TokenManagerError> {
let (client_end, server_end) = create_endpoints()
.map_err(|err| TokenManagerError::new(Status::UnknownError).with_cause(err))?;
match self.account_handler_context.get_oauth(auth_provider_type, server_end).await {
Ok(Ok(())) => Ok(client_end),
Ok(Err(status)) => Err(TokenManagerError::new(Status::AuthProviderServiceUnavailable)
.with_cause(format_err!("AccountHandlerContext returned {:?}", status))),
Err(err) => Err(TokenManagerError::new(Status::UnknownError).with_cause(err)),
}
}
async fn get_open_id_connect(
&self,
auth_provider_type: &str,
) -> Result<ClientEnd<OpenIdConnectMarker>, TokenManagerError> {
let (client_end, server_end) = create_endpoints()
.map_err(|err| TokenManagerError::new(Status::UnknownError).with_cause(err))?;
match self.account_handler_context.get_open_id_connect(auth_provider_type, server_end).await
{
Ok(Ok(())) => Ok(client_end),
Ok(Err(status)) => Err(TokenManagerError::new(Status::AuthProviderServiceUnavailable)
.with_cause(format_err!("AccountHandlerContext returned {:?}", status))),
Err(err) => Err(TokenManagerError::new(Status::UnknownError).with_cause(err)),
}
}
async fn get_oauth_open_id_connect(
&self,
auth_provider_type: &str,
) -> Result<ClientEnd<OauthOpenIdConnectMarker>, TokenManagerError> {
let (client_end, server_end) = create_endpoints()
.map_err(|err| TokenManagerError::new(Status::UnknownError).with_cause(err))?;
match self
.account_handler_context
.get_oauth_open_id_connect(auth_provider_type, server_end)
.await
{
Ok(Ok(())) => Ok(client_end),
Ok(Err(status)) => Err(TokenManagerError::new(Status::AuthProviderServiceUnavailable)
.with_cause(format_err!("AccountHandlerContext returned {:?}", status))),
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_identity_internal::{
AccountHandlerContextMarker, AccountHandlerContextRequest,
};
use fuchsia_async as fasync;
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 proxy = client_end.into_proxy().unwrap();
let auth_provider_supplier = AuthProviderSupplier::new(proxy);
executor.run_singlethreaded(async move {
let result = auth_provider_supplier.get_oauth(TEST_AUTH_PROVIDER_TYPE).await;
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 result, only if the first request is a get request for TEST_AUTH_PROVIDER_TYPE
fn spawn_account_handler_context_server(
server_end: ServerEnd<AccountHandlerContextMarker>,
mut result: Result<(), fidl_fuchsia_identity_account::Error>,
) {
fasync::Task::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::GetOauth {
auth_provider_type,
oauth: _,
responder,
})) = request_stream.try_next().await
{
if auth_provider_type == TEST_AUTH_PROVIDER_TYPE {
responder.send(&mut result).expect("Failed to send test response");
}
}
})
.detach();
}
#[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, 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,
Err(fidl_fuchsia_identity_account::Error::NotFound),
);
do_get_test(executor, client_end, Some(Status::AuthProviderServiceUnavailable));
}
}