blob: 83e300a96d0f53708ccec9c3561c59492841ee06 [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 crate::authenticator_connection::{AuthenticatorConnection, AuthenticatorService};
use anyhow::Error;
use fidl::endpoints::ServerEnd;
use fidl_fuchsia_auth::AuthProviderConfig;
use fidl_fuchsia_identity_account::Error as ApiError;
use fidl_fuchsia_identity_internal::{
AccountHandlerContextRequest, AccountHandlerContextRequestStream,
};
use futures::prelude::*;
use std::collections::HashMap;
use token_manager::{AuthProviderConnection, AuthProviderService};
/// A type that can respond to`AccountHandlerContext` requests from the AccountHandler components
/// that we launch. These requests provide contextual and service information to the
/// AccountHandlers, such as connections to components implementing the `AuthProvider`
/// protocol.
pub struct AccountHandlerContext {
/// A map from auth_provider_type to an `AuthProviderConnection` used to launch the associated
/// component.
auth_provider_connections: HashMap<String, AuthProviderConnection>,
/// A map from an auth_mechanism_id to an `AuthenticatorConnection` used to launch the
/// associated component.
auth_mechanism_connections: HashMap<String, AuthenticatorConnection>,
}
impl AccountHandlerContext {
/// Creates a new `AccountHandlerContext` from the supplied vector of `AuthProviderConfig`
/// objects.
///
/// `auth_provider_configs` A list of config objects that are used to create the connections.
/// `authenticators` A list of component urls representing authenticators implementing the
/// any authentication protocols.
pub fn new(
auth_provider_configs: &[AuthProviderConfig],
auth_mechanism_ids: &[String],
) -> AccountHandlerContext {
AccountHandlerContext {
auth_provider_connections: auth_provider_configs
.iter()
.map(|apc| {
(apc.auth_provider_type.clone(), AuthProviderConnection::from_config_ref(apc))
})
.collect(),
auth_mechanism_connections: auth_mechanism_ids
.iter()
.map(|auth_mechanism_id| {
// For now, auth mechanism ids are Fuchsia component URLs.
// We use that fact to construct their authenticators.
let conn = AuthenticatorConnection::from_url(auth_mechanism_id);
(auth_mechanism_id.to_string(), conn)
})
.collect(),
}
}
/// Asynchronously handles the supplied stream of `AccountHandlerContextRequest` messages.
pub async fn handle_requests_from_stream(
&self,
mut stream: AccountHandlerContextRequestStream,
) -> Result<(), Error> {
while let Some(req) = stream.try_next().await? {
self.handle_request(req).await?;
}
Ok(())
}
/// Asynchronously handles a single `AccountHandlerContextRequest`.
async fn handle_request(&self, req: AccountHandlerContextRequest) -> Result<(), fidl::Error> {
match req {
AccountHandlerContextRequest::GetOauth { auth_provider_type, oauth, responder } => {
responder.send(&mut self.connect_to_auth_provider(&auth_provider_type, oauth).await)
}
AccountHandlerContextRequest::GetOpenIdConnect {
auth_provider_type,
open_id_connect,
responder,
} => responder.send(
&mut self.connect_to_auth_provider(&auth_provider_type, open_id_connect).await,
),
AccountHandlerContextRequest::GetOauthOpenIdConnect {
auth_provider_type,
oauth_open_id_connect,
responder,
} => responder.send(
&mut self
.connect_to_auth_provider(&auth_provider_type, oauth_open_id_connect)
.await,
),
AccountHandlerContextRequest::GetStorageUnlockAuthMechanism {
auth_mechanism_id,
storage_unlock_mechanism,
responder,
} => responder.send(
&mut self
.connect_to_authenticator(&auth_mechanism_id, storage_unlock_mechanism)
.await,
),
}
}
async fn connect_to_auth_provider<'a, S>(
&'a self,
auth_provider_type: &'a str,
server_end: ServerEnd<S>,
) -> Result<(), fidl_fuchsia_identity_account::Error>
where
S: AuthProviderService,
{
match self.auth_provider_connections.get(auth_provider_type) {
Some(apc) => apc.connect::<S>(server_end).map_err(|_| ApiError::Unknown),
None => Err(ApiError::NotFound),
}
}
async fn connect_to_authenticator<'a, S>(
&'a self,
auth_mechanism_id: &'a str,
server_end: ServerEnd<S>,
) -> Result<(), ApiError>
where
S: AuthenticatorService,
{
match self.auth_mechanism_connections.get(auth_mechanism_id) {
Some(ac) => ac.connect::<S>(server_end).map_err(|_| ApiError::Unknown),
None => Err(ApiError::NotFound),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
const DUMMY_AUTHENTICATOR_URL_1: &str = "fuchsia-pkg://fuchsia.com/dummy_auth_1#meta/auth.cmx";
const DUMMY_AUTHENTICATOR_URL_2: &str = "fuchsia-pkg://fuchsia.com/dummy_auth_2#meta/auth.cmx";
/// Note: Most AccountHandlerContext methods launch instances of an AuthProvider or an
/// authenticator.
/// Since it is currently not convenient to mock out this component launching in Rust, we rely on the
/// hermetic component test to provide coverage for these areas and only cover the in-process
/// behavior with this unit-test.
#[test]
fn init_with_auth_provider_configs() {
let dummy_configs = vec![
AuthProviderConfig {
auth_provider_type: "dummy_1".to_string(),
url: "fuchsia-pkg://fuchsia.com/dummy_ap_1#meta/ap.cmx".to_string(),
params: Some(vec!["test_arg_1".to_string()]),
},
AuthProviderConfig {
auth_provider_type: "dummy_2".to_string(),
url: "fuchsia-pkg://fuchsia.com/dummy_ap_2#meta/ap.cmx".to_string(),
params: None,
},
];
let dummy_config_1 = &dummy_configs[0];
let dummy_config_2 = &dummy_configs[1];
let test_object = AccountHandlerContext::new(&dummy_configs, &[]);
let test_connection_1 =
test_object.auth_provider_connections.get(&dummy_config_1.auth_provider_type).unwrap();
let test_connection_2 =
test_object.auth_provider_connections.get(&dummy_config_2.auth_provider_type).unwrap();
assert_eq!(test_connection_1.component_url(), dummy_config_1.url);
assert_eq!(test_connection_2.component_url(), dummy_config_2.url);
assert!(test_object.auth_provider_connections.get("bad url").is_none());
}
#[test]
fn init_with_authenticators() {
let test_object = AccountHandlerContext::new(
&[],
&[DUMMY_AUTHENTICATOR_URL_1.to_string(), DUMMY_AUTHENTICATOR_URL_2.to_string()],
);
let test_connection_1 =
test_object.auth_mechanism_connections.get(DUMMY_AUTHENTICATOR_URL_1).unwrap();
let test_connection_2 =
test_object.auth_mechanism_connections.get(DUMMY_AUTHENTICATOR_URL_2).unwrap();
assert_eq!(test_connection_1.component_url(), DUMMY_AUTHENTICATOR_URL_1);
assert_eq!(test_connection_2.component_url(), DUMMY_AUTHENTICATOR_URL_2);
assert!(test_object.auth_mechanism_connections.get("bad url").is_none());
}
}