blob: ee04a55ccf7120ce1e20d6c1bec4d1f9b0c6871e [file] [log] [blame]
// Copyright 2020 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 anyhow::{format_err, Context as _};
use fidl::endpoints::{DiscoverableService, ServerEnd};
use fidl_fuchsia_identity_account::Error as ApiError;
use fidl_fuchsia_identity_authentication::StorageUnlockMechanismMarker;
use fuchsia_component::client::{launch, launcher, App};
use log::info;
use parking_lot::Mutex;
use std::sync::Arc;
/// A type capable of launching and connecting to a component that implements
/// one or more authentication mechanism protocols. Launching is performed on demand.
/// Currently, an AuthenticatorConnection provides a single default authentication
/// mechanism.
pub struct AuthenticatorConnection {
/// The URL that should be used to launch the component.
component_url: String,
/// The state needed to retain a connection, once one has been created.
connection_state: Mutex<Option<Arc<App>>>,
}
// TODO(fxbug.dev/427): Add test coverage through integration tests.
impl AuthenticatorConnection {
/// Creates a new `AuthenticatorConnection` from the supplied Fuchsia component url.
pub fn from_url(component_url: &str) -> Self {
AuthenticatorConnection {
component_url: component_url.to_string(),
connection_state: Mutex::new(None),
}
}
/// Returns the component_url supplied at construction
#[allow(dead_code)]
pub fn component_url(&self) -> &str {
&self.component_url
}
/// Returns an `App` for opening new authenticator connections. If a
/// component instance has previously been launched this is used, otherwise
/// a fresh instance is launched and its proxy is stored for future use.
fn get_app(&self) -> Result<Arc<App>, AccountManagerError> {
let mut connection_state_lock = self.connection_state.lock();
if let Some(connection_state) = &*connection_state_lock {
return Ok(Arc::clone(&connection_state));
}
info!("Launching Authenticator component: {}", self.component_url);
let launcher = launcher()
.context("Failed to start launcher")
.account_manager_error(ApiError::Unknown)?;
let app = launch(&launcher, self.component_url.clone(), None)
.context("Failed to launch Authenticator")
.account_manager_error(ApiError::NotFound)?;
let app_arc = Arc::new(app);
connection_state_lock.get_or_insert(Arc::clone(&app_arc));
Ok(app_arc)
}
/// Connects the supplied channel to the authenticator. If a component has previously been
/// launched this is used, otherwise a fresh component is launched.
pub fn connect<S>(&self, server_end: ServerEnd<S>) -> Result<(), AccountManagerError>
where
S: AuthenticatorService,
{
let app = self.get_app()?;
app.pass_to_service::<S>(server_end.into_channel()).map_err(|err| {
AccountManagerError::new(ApiError::Unknown)
.with_cause(format_err!("Getting auth mechanism failed with {:?}", err))
})
}
}
/// A marker trait for identifying services that may be provided by an authenticator.
pub trait AuthenticatorService: DiscoverableService {}
impl AuthenticatorService for StorageUnlockMechanismMarker {}