// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/account_manager_core/account_manager_util.h"
#include <optional>
#include "base/notreached.h"
#include "components/account_manager_core/account.h"
#include "components/account_manager_core/account_addition_options.h"
#include "components/account_manager_core/account_upsertion_result.h"
#include "google_apis/gaia/google_service_auth_error.h"
namespace account_manager {
namespace cm = crosapi::mojom;
namespace {
GoogleServiceAuthError::InvalidGaiaCredentialsReason
FromMojoInvalidGaiaCredentialsReason(
crosapi::mojom::GoogleServiceAuthError::InvalidGaiaCredentialsReason
mojo_reason) {
switch (mojo_reason) {
case cm::GoogleServiceAuthError::InvalidGaiaCredentialsReason::kUnknown:
return GoogleServiceAuthError::InvalidGaiaCredentialsReason::UNKNOWN;
case cm::GoogleServiceAuthError::InvalidGaiaCredentialsReason::
kCredentialsRejectedByServer:
return GoogleServiceAuthError::InvalidGaiaCredentialsReason::
CREDENTIALS_REJECTED_BY_SERVER;
case cm::GoogleServiceAuthError::InvalidGaiaCredentialsReason::
kCredentialsRejectedByClient:
return GoogleServiceAuthError::InvalidGaiaCredentialsReason::
CREDENTIALS_REJECTED_BY_CLIENT;
case cm::GoogleServiceAuthError::InvalidGaiaCredentialsReason::
kCredentialsMissing:
return GoogleServiceAuthError::InvalidGaiaCredentialsReason::
CREDENTIALS_MISSING;
default:
LOG(WARNING) << "Unknown "
"crosapi::mojom::GoogleServiceAuthError::"
"InvalidGaiaCredentialsReason: "
<< mojo_reason;
return GoogleServiceAuthError::InvalidGaiaCredentialsReason::UNKNOWN;
}
}
crosapi::mojom::GoogleServiceAuthError::InvalidGaiaCredentialsReason
ToMojoInvalidGaiaCredentialsReason(
GoogleServiceAuthError::InvalidGaiaCredentialsReason reason) {
switch (reason) {
case GoogleServiceAuthError::InvalidGaiaCredentialsReason::UNKNOWN:
return cm::GoogleServiceAuthError::InvalidGaiaCredentialsReason::kUnknown;
case GoogleServiceAuthError::InvalidGaiaCredentialsReason::
CREDENTIALS_REJECTED_BY_SERVER:
return cm::GoogleServiceAuthError::InvalidGaiaCredentialsReason::
kCredentialsRejectedByServer;
case GoogleServiceAuthError::InvalidGaiaCredentialsReason::
CREDENTIALS_REJECTED_BY_CLIENT:
return cm::GoogleServiceAuthError::InvalidGaiaCredentialsReason::
kCredentialsRejectedByClient;
case GoogleServiceAuthError::InvalidGaiaCredentialsReason::
CREDENTIALS_MISSING:
return cm::GoogleServiceAuthError::InvalidGaiaCredentialsReason::
kCredentialsMissing;
case GoogleServiceAuthError::InvalidGaiaCredentialsReason::NUM_REASONS:
NOTREACHED_IN_MIGRATION();
return cm::GoogleServiceAuthError::InvalidGaiaCredentialsReason::kUnknown;
}
}
crosapi::mojom::GoogleServiceAuthError::State ToMojoGoogleServiceAuthErrorState(
GoogleServiceAuthError::State state) {
switch (state) {
case GoogleServiceAuthError::State::NONE:
return cm::GoogleServiceAuthError::State::kNone;
case GoogleServiceAuthError::State::INVALID_GAIA_CREDENTIALS:
return cm::GoogleServiceAuthError::State::kInvalidGaiaCredentials;
case GoogleServiceAuthError::State::USER_NOT_SIGNED_UP:
return cm::GoogleServiceAuthError::State::kUserNotSignedUp;
case GoogleServiceAuthError::State::CONNECTION_FAILED:
return cm::GoogleServiceAuthError::State::kConnectionFailed;
case GoogleServiceAuthError::State::SERVICE_UNAVAILABLE:
return cm::GoogleServiceAuthError::State::kServiceUnavailable;
case GoogleServiceAuthError::State::REQUEST_CANCELED:
return cm::GoogleServiceAuthError::State::kRequestCanceled;
case GoogleServiceAuthError::State::UNEXPECTED_SERVICE_RESPONSE:
return cm::GoogleServiceAuthError::State::kUnexpectedServiceResponse;
case GoogleServiceAuthError::State::SERVICE_ERROR:
return cm::GoogleServiceAuthError::State::kServiceError;
case GoogleServiceAuthError::State::SCOPE_LIMITED_UNRECOVERABLE_ERROR:
return cm::GoogleServiceAuthError::State::kScopeLimitedUnrecoverableError;
case GoogleServiceAuthError::State::CHALLENGE_RESPONSE_REQUIRED:
return cm::GoogleServiceAuthError::State::kChallengeResponseRequired;
case GoogleServiceAuthError::State::NUM_STATES:
NOTREACHED_IN_MIGRATION();
return cm::GoogleServiceAuthError::State::kNone;
}
}
std::optional<account_manager::AccountUpsertionResult::Status>
FromMojoAccountAdditionStatus(
crosapi::mojom::AccountUpsertionResult::Status mojo_status) {
switch (mojo_status) {
case cm::AccountUpsertionResult::Status::kSuccess:
return account_manager::AccountUpsertionResult::Status::kSuccess;
case cm::AccountUpsertionResult::Status::kAlreadyInProgress:
return account_manager::AccountUpsertionResult::Status::
kAlreadyInProgress;
case cm::AccountUpsertionResult::Status::kCancelledByUser:
return account_manager::AccountUpsertionResult::Status::kCancelledByUser;
case cm::AccountUpsertionResult::Status::kNetworkError:
return account_manager::AccountUpsertionResult::Status::kNetworkError;
case cm::AccountUpsertionResult::Status::kUnexpectedResponse:
return account_manager::AccountUpsertionResult::Status::
kUnexpectedResponse;
case cm::AccountUpsertionResult::Status::kBlockedByPolicy:
return account_manager::AccountUpsertionResult::Status::kBlockedByPolicy;
default:
LOG(WARNING) << "Unknown crosapi::mojom::AccountUpsertionResult::Status: "
<< mojo_status;
return std::nullopt;
}
}
crosapi::mojom::AccountUpsertionResult::Status ToMojoAccountAdditionStatus(
account_manager::AccountUpsertionResult::Status status) {
switch (status) {
case account_manager::AccountUpsertionResult::Status::kSuccess:
return cm::AccountUpsertionResult::Status::kSuccess;
case account_manager::AccountUpsertionResult::Status::kAlreadyInProgress:
return cm::AccountUpsertionResult::Status::kAlreadyInProgress;
case account_manager::AccountUpsertionResult::Status::kCancelledByUser:
return cm::AccountUpsertionResult::Status::kCancelledByUser;
case account_manager::AccountUpsertionResult::Status::kNetworkError:
return cm::AccountUpsertionResult::Status::kNetworkError;
case account_manager::AccountUpsertionResult::Status::kUnexpectedResponse:
return cm::AccountUpsertionResult::Status::kUnexpectedResponse;
case account_manager::AccountUpsertionResult::Status::kBlockedByPolicy:
return cm::AccountUpsertionResult::Status::kBlockedByPolicy;
case account_manager::AccountUpsertionResult::Status::
kMojoRemoteDisconnected:
case account_manager::AccountUpsertionResult::Status::
kIncompatibleMojoVersions:
// `kMojoRemoteDisconnected` and `kIncompatibleMojoVersions` are generated
// entirely on the remote side when the receiver can't even be reached.
// They do not have any Mojo equivalent since they are never passed over
// the wire in the first place.
NOTREACHED_IN_MIGRATION()
<< "These statuses should not be passed over the wire";
// Return something to make the compiler happy. This should never happen
// in production.
return cm::AccountUpsertionResult::Status::kUnexpectedResponse;
}
}
} // namespace
std::optional<account_manager::Account> FromMojoAccount(
const crosapi::mojom::AccountPtr& mojom_account) {
if (mojom_account.is_null()) {
return std::nullopt;
}
const std::optional<account_manager::AccountKey> account_key =
FromMojoAccountKey(mojom_account->key);
if (!account_key.has_value())
return std::nullopt;
account_manager::Account account{account_key.value(),
mojom_account->raw_email};
return account;
}
crosapi::mojom::AccountPtr ToMojoAccount(
const account_manager::Account& account) {
crosapi::mojom::AccountPtr mojom_account = crosapi::mojom::Account::New();
mojom_account->key = ToMojoAccountKey(account.key);
mojom_account->raw_email = account.raw_email;
return mojom_account;
}
std::optional<account_manager::AccountKey> FromMojoAccountKey(
const crosapi::mojom::AccountKeyPtr& mojom_account_key) {
if (mojom_account_key.is_null()) {
return std::nullopt;
}
const std::optional<account_manager::AccountType> account_type =
FromMojoAccountType(mojom_account_key->account_type);
if (!account_type.has_value())
return std::nullopt;
if (mojom_account_key->id.empty())
return std::nullopt;
return account_manager::AccountKey(mojom_account_key->id,
account_type.value());
}
crosapi::mojom::AccountKeyPtr ToMojoAccountKey(
const account_manager::AccountKey& account_key) {
crosapi::mojom::AccountKeyPtr mojom_account_key =
crosapi::mojom::AccountKey::New();
mojom_account_key->id = account_key.id();
mojom_account_key->account_type =
ToMojoAccountType(account_key.account_type());
return mojom_account_key;
}
std::optional<account_manager::AccountType> FromMojoAccountType(
const crosapi::mojom::AccountType& account_type) {
switch (account_type) {
case crosapi::mojom::AccountType::kGaia:
static_assert(static_cast<int>(crosapi::mojom::AccountType::kGaia) ==
static_cast<int>(account_manager::AccountType::kGaia),
"Underlying enum values must match");
return account_manager::AccountType::kGaia;
case crosapi::mojom::AccountType::kActiveDirectory:
static_assert(
static_cast<int>(crosapi::mojom::AccountType::kActiveDirectory) ==
static_cast<int>(account_manager::AccountType::kActiveDirectory),
"Underlying enum values must match");
return account_manager::AccountType::kActiveDirectory;
default:
// Don't consider this as as error to preserve forwards compatibility with
// lacros.
LOG(WARNING) << "Unknown account type: " << account_type;
return std::nullopt;
}
}
crosapi::mojom::AccountType ToMojoAccountType(
const account_manager::AccountType& account_type) {
switch (account_type) {
case account_manager::AccountType::kGaia:
return crosapi::mojom::AccountType::kGaia;
case account_manager::AccountType::kActiveDirectory:
return crosapi::mojom::AccountType::kActiveDirectory;
}
}
std::optional<GoogleServiceAuthError> FromMojoGoogleServiceAuthError(
const crosapi::mojom::GoogleServiceAuthErrorPtr& mojo_error) {
if (mojo_error.is_null()) {
return std::nullopt;
}
switch (mojo_error->state) {
case cm::GoogleServiceAuthError::State::kNone:
return GoogleServiceAuthError::AuthErrorNone();
case cm::GoogleServiceAuthError::State::kInvalidGaiaCredentials:
return GoogleServiceAuthError::FromInvalidGaiaCredentialsReason(
FromMojoInvalidGaiaCredentialsReason(
mojo_error->invalid_gaia_credentials_reason));
case cm::GoogleServiceAuthError::State::kConnectionFailed:
return GoogleServiceAuthError::FromConnectionError(
mojo_error->network_error);
case cm::GoogleServiceAuthError::State::kServiceError:
return GoogleServiceAuthError::FromServiceError(
mojo_error->error_message);
case cm::GoogleServiceAuthError::State::kUnexpectedServiceResponse:
return GoogleServiceAuthError::FromUnexpectedServiceResponse(
mojo_error->error_message);
case cm::GoogleServiceAuthError::State::kUserNotSignedUp:
return GoogleServiceAuthError(
GoogleServiceAuthError::State::USER_NOT_SIGNED_UP);
case cm::GoogleServiceAuthError::State::kServiceUnavailable:
return GoogleServiceAuthError(
GoogleServiceAuthError::State::SERVICE_UNAVAILABLE);
case cm::GoogleServiceAuthError::State::kRequestCanceled:
return GoogleServiceAuthError(
GoogleServiceAuthError::State::REQUEST_CANCELED);
case cm::GoogleServiceAuthError::State::kScopeLimitedUnrecoverableError:
return GoogleServiceAuthError::FromScopeLimitedUnrecoverableError(
mojo_error->error_message);
case cm::GoogleServiceAuthError::State::kChallengeResponseRequired:
return GoogleServiceAuthError::FromTokenBindingChallenge(
mojo_error->token_binding_challenge.value_or(
"MISSING_CHALLENGE_FROM_CROSAPI_MOJOM"));
default:
LOG(WARNING) << "Unknown crosapi::mojom::GoogleServiceAuthError::State: "
<< mojo_error->state;
return std::nullopt;
}
}
crosapi::mojom::GoogleServiceAuthErrorPtr ToMojoGoogleServiceAuthError(
GoogleServiceAuthError error) {
crosapi::mojom::GoogleServiceAuthErrorPtr mojo_result =
crosapi::mojom::GoogleServiceAuthError::New();
mojo_result->error_message = error.error_message();
if (error.state() == GoogleServiceAuthError::State::CONNECTION_FAILED) {
mojo_result->network_error = error.network_error();
}
if (error.state() ==
GoogleServiceAuthError::State::INVALID_GAIA_CREDENTIALS) {
mojo_result->invalid_gaia_credentials_reason =
ToMojoInvalidGaiaCredentialsReason(
error.GetInvalidGaiaCredentialsReason());
}
if (error.state() ==
GoogleServiceAuthError::State::CHALLENGE_RESPONSE_REQUIRED) {
mojo_result->token_binding_challenge = error.GetTokenBindingChallenge();
}
mojo_result->state = ToMojoGoogleServiceAuthErrorState(error.state());
return mojo_result;
}
std::optional<account_manager::AccountUpsertionResult>
FromMojoAccountUpsertionResult(
const crosapi::mojom::AccountUpsertionResultPtr& mojo_result) {
if (mojo_result.is_null()) {
return std::nullopt;
}
std::optional<account_manager::AccountUpsertionResult::Status> status =
FromMojoAccountAdditionStatus(mojo_result->status);
if (!status.has_value())
return std::nullopt;
switch (status.value()) {
case account_manager::AccountUpsertionResult::Status::kSuccess: {
std::optional<account_manager::Account> account =
FromMojoAccount(mojo_result->account);
if (!account.has_value())
return std::nullopt;
return account_manager::AccountUpsertionResult::FromAccount(
account.value());
}
case account_manager::AccountUpsertionResult::Status::kNetworkError: {
std::optional<GoogleServiceAuthError> net_error =
FromMojoGoogleServiceAuthError(mojo_result->error);
if (!net_error.has_value())
return std::nullopt;
return account_manager::AccountUpsertionResult::FromError(
net_error.value());
}
case account_manager::AccountUpsertionResult::Status::kAlreadyInProgress:
case account_manager::AccountUpsertionResult::Status::kCancelledByUser:
case account_manager::AccountUpsertionResult::Status::kUnexpectedResponse:
case account_manager::AccountUpsertionResult::Status::
kMojoRemoteDisconnected:
case account_manager::AccountUpsertionResult::Status::
kIncompatibleMojoVersions:
return account_manager::AccountUpsertionResult::FromStatus(
status.value());
case account_manager::AccountUpsertionResult::Status::kBlockedByPolicy:
return account_manager::AccountUpsertionResult::FromStatus(
status.value());
}
}
crosapi::mojom::AccountUpsertionResultPtr ToMojoAccountUpsertionResult(
account_manager::AccountUpsertionResult result) {
crosapi::mojom::AccountUpsertionResultPtr mojo_result =
crosapi::mojom::AccountUpsertionResult::New();
mojo_result->status = ToMojoAccountAdditionStatus(result.status());
if (result.account().has_value()) {
mojo_result->account =
account_manager::ToMojoAccount(result.account().value());
}
if (result.error().state() != GoogleServiceAuthError::NONE) {
mojo_result->error = ToMojoGoogleServiceAuthError(result.error());
}
return mojo_result;
}
std::optional<account_manager::AccountAdditionOptions>
FromMojoAccountAdditionOptions(
const crosapi::mojom::AccountAdditionOptionsPtr& mojo_options) {
if (mojo_options.is_null()) {
return std::nullopt;
}
account_manager::AccountAdditionOptions result;
result.is_available_in_arc = mojo_options->is_available_in_arc;
result.show_arc_availability_picker =
mojo_options->show_arc_availability_picker;
return result;
}
} // namespace account_manager