blob: 00727ce527467dcbe525b1c4a36f775c8b4b3c0c [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.
#include "peridot/bin/basemgr/session_user_provider_impl.h"
#include <lib/fit/function.h>
#include <zircon/status.h>
#include <utility>
#include "peridot/lib/fidl/clone.h"
#include "peridot/lib/fidl/json_xdr.h"
namespace modular {
namespace {
// Url of the application launching token manager
constexpr char kSessionUserProviderAppUrl[] = "session_user_provider_url";
// Dev auth provider configuration
constexpr char kDevAuthProviderType[] = "dev";
constexpr char kDevAuthProviderUrl[] =
"fuchsia-pkg://fuchsia.com/dev_auth_provider#meta/"
"dev_auth_provider.cmx";
// Google auth provider configuration
constexpr char kGoogleAuthProviderType[] = "google";
constexpr char kGoogleAuthProviderUrl[] =
"fuchsia-pkg://fuchsia.com/google_auth_provider#meta/"
"google_auth_provider.cmx";
std::string GetRandomId() {
uint32_t random_number = 0;
zx_cprng_draw(&random_number, sizeof random_number);
return std::to_string(random_number);
}
// Returns the corresponding |auth_provider_type| string that maps to
// |fuchsia::modular::auth::IdentityProvider| value.
// TODO(ukode): Convert enum |fuchsia::modular::auth::IdentityProvider| to
// fidl::String datatype to make it consistent in the future.
std::string MapIdentityProviderToAuthProviderType(
const fuchsia::modular::auth::IdentityProvider idp) {
switch (idp) {
case fuchsia::modular::auth::IdentityProvider::DEV:
return kDevAuthProviderType;
case fuchsia::modular::auth::IdentityProvider::GOOGLE:
return kGoogleAuthProviderType;
}
FXL_DCHECK(false) << "Unrecognized IDP.";
}
// Returns a list of supported auth provider configurations that includes the
// type, startup parameters and the url of the auth provider component.
// TODO(ukode): This list will be derived from a config package in the future.
std::vector<fuchsia::auth::AuthProviderConfig> GetAuthProviderConfigs() {
fuchsia::auth::AuthProviderConfig dev_auth_provider_config;
dev_auth_provider_config.auth_provider_type = kDevAuthProviderType;
dev_auth_provider_config.url = kDevAuthProviderUrl;
fuchsia::auth::AuthProviderConfig google_auth_provider_config;
google_auth_provider_config.auth_provider_type = kGoogleAuthProviderType;
google_auth_provider_config.url = kGoogleAuthProviderUrl;
std::vector<fuchsia::auth::AuthProviderConfig> auth_provider_configs;
auth_provider_configs.push_back(std::move(google_auth_provider_config));
auth_provider_configs.push_back(std::move(dev_auth_provider_config));
return auth_provider_configs;
}
} // namespace
SessionUserProviderImpl::SessionUserProviderImpl(
fuchsia::auth::account::AccountManager* const account_manager,
fuchsia::auth::TokenManagerFactory* const token_manager_factory,
fuchsia::auth::AuthenticationContextProviderPtr
authentication_context_provider,
OnInitializeCallback on_initialize, OnLoginCallback on_login)
: account_manager_(account_manager),
token_manager_factory_(token_manager_factory),
authentication_context_provider_(
std::move(authentication_context_provider)),
authentication_context_provider_binding_(this),
account_listener_binding_(this),
on_initialize_(std::move(on_initialize)),
on_login_(std::move(on_login)) {
FXL_CHECK(account_manager_);
FXL_CHECK(token_manager_factory_);
FXL_CHECK(authentication_context_provider_);
FXL_CHECK(on_initialize_);
FXL_CHECK(on_login_);
authentication_context_provider_binding_.set_error_handler(
[this](zx_status_t status) {
FXL_LOG(WARNING) << "AuthenticationContextProvider disconnected.";
authentication_context_provider_binding_.Unbind();
});
// Register SessionUserProvider as an AccountListener. All added accounts will
// be logged in to a session.
account_listener_binding_.set_error_handler([](zx_status_t status) {
FXL_LOG(FATAL) << "AccountListener disconnected with status: "
<< zx_status_get_string(status);
});
fuchsia::auth::account::AccountListenerOptions options;
options.initial_state = true;
options.add_account = true;
account_manager_->RegisterAccountListener(
account_listener_binding_.NewBinding(), std::move(options),
[](fuchsia::auth::account::Status status) {
if (status == fuchsia::auth::account::Status::OK) {
FXL_LOG(INFO) << "AccountListener registered.";
} else {
FXL_LOG(FATAL) << "AccountListener registration failed with status: "
<< (uint32_t)status;
}
});
}
void SessionUserProviderImpl::Connect(
fidl::InterfaceRequest<fuchsia::modular::UserProvider> request) {
bindings_.AddBinding(this, std::move(request));
}
void SessionUserProviderImpl::AddUser(
fuchsia::modular::auth::IdentityProvider identity_provider,
AddUserCallback callback) {
account_manager_->ProvisionFromAuthProvider(
authentication_context_provider_binding_.NewBinding(),
MapIdentityProviderToAuthProviderType(identity_provider),
[callback = std::move(callback)](
fuchsia::auth::account::Status status,
std::unique_ptr<fuchsia::auth::account::LocalAccountId>
account_id) mutable {
if (status != fuchsia::auth::account::Status::OK || !account_id) {
FXL_LOG(ERROR) << "Failed to provision new account from wuth "
"provider with status: "
<< (uint32_t)status;
callback(nullptr,
"Failed to provision new account from auth provider.");
return;
}
// To interface with BaseShells that haven't migrated to use
// AccountManager, we give them the string account_id to call back
// Login with.
auto account = fuchsia::modular::auth::Account::New();
account->id = std::to_string(account_id->id);
callback(std::move(account), "");
});
}
void SessionUserProviderImpl::Login(fuchsia::modular::UserLoginParams params) {
Login2(fuchsia::modular::UserLoginParams2{
.account_id = std::move(params.account_id),
});
}
void SessionUserProviderImpl::Login2(fuchsia::modular::UserLoginParams2 params) {
bool login_as_guest = params.account_id.is_null() || params.account_id == "";
if (login_as_guest) {
FXL_LOG(INFO) << "fuchsia::modular::UserProvider::Login() Login as guest";
// If requested, login as guest by creating token managers with token
// manager factory.
auto account_id = GetRandomId();
// Instead of passing token_manager_factory all the way to agents and
// runners with all auth provider configurations, send two
// |fuchsia::auth::TokenManager| handles, one for ledger and one for
// agents for the given user account |account_id|.
fuchsia::auth::TokenManagerPtr ledger_token_manager =
CreateTokenManager(account_id);
fuchsia::auth::TokenManagerPtr agent_token_manager =
CreateTokenManager(account_id);
on_login_(/* account= */ nullptr, std::move(ledger_token_manager),
std::move(agent_token_manager));
} else {
FXL_LOG(INFO) << "fuchsia::modular::UserProvider::Login() Login as "
"authenticated user";
fuchsia::auth::account::LocalAccountId account_id;
account_id.id = std::atoll(params.account_id->c_str());
fuchsia::auth::account::AccountPtr account;
account_manager_->GetAccount(
account_id, authentication_context_provider_binding_.NewBinding(),
account.NewRequest(), [](fuchsia::auth::account::Status status) {
FXL_LOG(INFO) << "Got account with status: " << (uint32_t)status;
});
fuchsia::auth::account::PersonaPtr persona;
account->GetDefaultPersona(
persona.NewRequest(),
[](fuchsia::auth::account::Status status,
std::unique_ptr<fuchsia::auth::account::LocalPersonaId>) {
FXL_LOG(INFO) << "Got default persona with status: "
<< (uint32_t)status;
});
fuchsia::auth::TokenManagerPtr ledger_token_manager;
persona->GetTokenManager(
kSessionUserProviderAppUrl, ledger_token_manager.NewRequest(),
[](fuchsia::auth::account::Status status) {
FXL_LOG(INFO) << "Got token manager with status: "
<< (uint32_t)status;
});
fuchsia::auth::TokenManagerPtr agent_token_manager;
persona->GetTokenManager(
kSessionUserProviderAppUrl, agent_token_manager.NewRequest(),
[](fuchsia::auth::account::Status status) {
FXL_LOG(INFO) << "Got token manager with status: "
<< (uint32_t)status;
});
auto account_deprecated = fuchsia::modular::auth::Account::New();
account_deprecated->id = params.account_id->c_str();
// Save the newly added user as a joined persona.
struct JoinedPersona joined_persona {
.account = std::move(account), .persona = std::move(persona),
};
joined_personas_.emplace_back(std::move(joined_persona));
on_login_(std::move(account_deprecated), std::move(ledger_token_manager),
std::move(agent_token_manager));
}
}
void SessionUserProviderImpl::RemoveAllUsers(fit::function<void()> callback) {
joined_personas_.clear();
account_manager_->GetAccountIds(
[this, callback = std::move(callback)](
std::vector<fuchsia::auth::account::LocalAccountId>
account_ids) mutable {
if (account_ids.empty()) {
callback();
return;
}
// We only expect there to be one account at most.
account_manager_->RemoveAccount(
account_ids.at(0),
[callback = std::move(callback)](fuchsia::auth::account::Status) {
callback();
});
});
}
void SessionUserProviderImpl::RemoveUser(std::string account_id,
RemoveUserCallback callback) {
FXL_LOG(INFO) << "RemoveUser() is not implemented yet.";
callback("");
}
void SessionUserProviderImpl::PreviousUsers(PreviousUsersCallback callback) {
FXL_LOG(INFO) << "PreviousUsers() is not implemented yet";
fidl::VectorPtr<::fuchsia::modular::auth::Account> users;
callback(std::move(users));
}
void SessionUserProviderImpl::GetAuthenticationUIContext(
fidl::InterfaceRequest<fuchsia::auth::AuthenticationUIContext> request) {
authentication_context_provider_->GetAuthenticationUIContext(
std::move(request));
}
fuchsia::auth::TokenManagerPtr SessionUserProviderImpl::CreateTokenManager(
std::string account_id) {
FXL_CHECK(token_manager_factory_);
fuchsia::auth::TokenManagerPtr token_mgr;
token_manager_factory_->GetTokenManager(
account_id, kSessionUserProviderAppUrl, GetAuthProviderConfigs(),
authentication_context_provider_binding_.NewBinding(),
token_mgr.NewRequest());
token_mgr.set_error_handler([account_id](zx_status_t status) {
FXL_LOG(INFO) << "Token Manager for account:" << account_id
<< " disconnected";
});
return token_mgr;
}
void SessionUserProviderImpl::OnInitialize(
std::vector<fuchsia::auth::account::AccountAuthState>,
OnInitializeCallback callback) {
callback();
on_initialize_();
}
void SessionUserProviderImpl::OnAccountAdded(
fuchsia::auth::account::LocalAccountId account_id,
OnAccountAddedCallback callback) {
// TODO(MF-311): Get rid of this once clients of UserProvider interface start
// using AccountManager.
fuchsia::modular::UserLoginParams params;
params.account_id = std::to_string(account_id.id);
// Base shell may also call Login with the newly added account, but the Login
// flow should be resilient to multiple invocations.
Login(std::move(params));
callback();
}
void SessionUserProviderImpl::OnAccountRemoved(
fuchsia::auth::account::LocalAccountId, OnAccountRemovedCallback callback) {
callback();
}
void SessionUserProviderImpl::OnAuthStateChanged(
fuchsia::auth::account::AccountAuthState,
OnAuthStateChangedCallback callback) {
callback();
}
} // namespace modular