blob: 218497c0c813f7c9b8ee0e5a52c434d37b9d9ff0 [file] [log] [blame]
// Copyright 2015 The Chromium 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 "chrome/browser/chromeos/arc/arc_auth_service.h"
#include <utility>
#include "base/command_line.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "chrome/browser/chromeos/arc/arc_optin_uma.h"
#include "chrome/browser/chromeos/arc/arc_session_manager.h"
#include "chrome/browser/chromeos/arc/auth/arc_auth_code_fetcher.h"
#include "chrome/browser/chromeos/arc/auth/arc_background_auth_code_fetcher.h"
#include "chrome/browser/chromeos/arc/auth/arc_manual_auth_code_fetcher.h"
#include "chrome/browser/chromeos/arc/auth/arc_robot_auth_code_fetcher.h"
#include "chrome/browser/chromeos/arc/policy/arc_policy_util.h"
#include "chrome/browser/lifetime/application_lifetime.h"
#include "chromeos/chromeos_switches.h"
#include "components/arc/arc_bridge_service.h"
#include "components/arc/arc_features.h"
#include "content/public/browser/browser_thread.h"
namespace arc {
namespace {
ArcAuthService* g_arc_auth_service = nullptr;
constexpr uint32_t kMinVersionForOnAccountInfoReady = 5;
// Convers mojom::ArcSignInFailureReason into ProvisiningResult.
ProvisioningResult ConvertArcSignInFailureReasonToProvisioningResult(
mojom::ArcSignInFailureReason reason) {
using ArcSignInFailureReason = mojom::ArcSignInFailureReason;
#define MAP_PROVISIONING_RESULT(name) \
case ArcSignInFailureReason::name: \
return ProvisioningResult::name
switch (reason) {
MAP_PROVISIONING_RESULT(UNKNOWN_ERROR);
MAP_PROVISIONING_RESULT(MOJO_VERSION_MISMATCH);
MAP_PROVISIONING_RESULT(MOJO_CALL_TIMEOUT);
MAP_PROVISIONING_RESULT(DEVICE_CHECK_IN_FAILED);
MAP_PROVISIONING_RESULT(DEVICE_CHECK_IN_TIMEOUT);
MAP_PROVISIONING_RESULT(DEVICE_CHECK_IN_INTERNAL_ERROR);
MAP_PROVISIONING_RESULT(GMS_NETWORK_ERROR);
MAP_PROVISIONING_RESULT(GMS_SERVICE_UNAVAILABLE);
MAP_PROVISIONING_RESULT(GMS_BAD_AUTHENTICATION);
MAP_PROVISIONING_RESULT(GMS_SIGN_IN_FAILED);
MAP_PROVISIONING_RESULT(GMS_SIGN_IN_TIMEOUT);
MAP_PROVISIONING_RESULT(GMS_SIGN_IN_INTERNAL_ERROR);
MAP_PROVISIONING_RESULT(CLOUD_PROVISION_FLOW_FAILED);
MAP_PROVISIONING_RESULT(CLOUD_PROVISION_FLOW_TIMEOUT);
MAP_PROVISIONING_RESULT(CLOUD_PROVISION_FLOW_INTERNAL_ERROR);
}
#undef MAP_PROVISIONING_RESULT
NOTREACHED() << "unknown reason: " << static_cast<int>(reason);
return ProvisioningResult::UNKNOWN_ERROR;
}
mojom::ChromeAccountType GetAccountType() {
return ArcSessionManager::IsArcKioskMode()
? mojom::ChromeAccountType::ROBOT_ACCOUNT
: mojom::ChromeAccountType::USER_ACCOUNT;
}
} // namespace
// TODO(lhchavez): Get rid of this class once we can safely remove all the
// deprecated interfaces and only need to care about one type of callback.
class ArcAuthService::AccountInfoNotifier {
public:
explicit AccountInfoNotifier(
const GetAuthCodeDeprecatedCallback& auth_callback)
: callback_type_(CallbackType::AUTH_CODE),
auth_callback_(auth_callback) {}
explicit AccountInfoNotifier(
const GetAuthCodeAndAccountTypeDeprecatedCallback& auth_account_callback)
: callback_type_(CallbackType::AUTH_CODE_AND_ACCOUNT),
auth_account_callback_(auth_account_callback) {}
explicit AccountInfoNotifier(const AccountInfoCallback& account_info_callback)
: callback_type_(CallbackType::ACCOUNT_INFO),
account_info_callback_(account_info_callback) {}
void Notify(bool is_enforced,
const std::string& auth_code,
mojom::ChromeAccountType account_type,
bool is_managed) {
switch (callback_type_) {
case CallbackType::AUTH_CODE:
DCHECK(!auth_callback_.is_null());
auth_callback_.Run(auth_code, is_enforced);
break;
case CallbackType::AUTH_CODE_AND_ACCOUNT:
DCHECK(!auth_account_callback_.is_null());
auth_account_callback_.Run(auth_code, is_enforced, account_type);
break;
case CallbackType::ACCOUNT_INFO:
DCHECK(!account_info_callback_.is_null());
mojom::AccountInfoPtr account_info = mojom::AccountInfo::New();
if (!is_enforced) {
account_info->auth_code = base::nullopt;
} else {
account_info->auth_code = auth_code;
}
account_info->account_type = account_type;
account_info->is_managed = is_managed;
account_info_callback_.Run(std::move(account_info));
break;
}
}
private:
enum class CallbackType { AUTH_CODE, AUTH_CODE_AND_ACCOUNT, ACCOUNT_INFO };
const CallbackType callback_type_;
const GetAuthCodeDeprecatedCallback auth_callback_;
const GetAuthCodeAndAccountTypeDeprecatedCallback auth_account_callback_;
const AccountInfoCallback account_info_callback_;
};
ArcAuthService::ArcAuthService(ArcBridgeService* bridge_service)
: ArcService(bridge_service), binding_(this), weak_ptr_factory_(this) {
DCHECK(!g_arc_auth_service);
g_arc_auth_service = this;
arc_bridge_service()->auth()->AddObserver(this);
}
ArcAuthService::~ArcAuthService() {
arc_bridge_service()->auth()->RemoveObserver(this);
DCHECK_EQ(g_arc_auth_service, this);
g_arc_auth_service = nullptr;
}
// static
ArcAuthService* ArcAuthService::GetForTest() {
DCHECK(g_arc_auth_service);
return g_arc_auth_service;
}
void ArcAuthService::OnInstanceReady() {
auto* instance = arc_bridge_service()->auth()->GetInstanceForMethod("Init");
DCHECK(instance);
instance->Init(binding_.CreateInterfacePtrAndBind());
}
void ArcAuthService::OnInstanceClosed() {
fetcher_.reset();
notifier_.reset();
}
void ArcAuthService::OnSignInComplete() {
ArcSessionManager::Get()->OnProvisioningFinished(ProvisioningResult::SUCCESS);
}
void ArcAuthService::OnSignInFailed(mojom::ArcSignInFailureReason reason) {
ArcSessionManager::Get()->OnProvisioningFinished(
ConvertArcSignInFailureReasonToProvisioningResult(reason));
}
void ArcAuthService::RequestAccountInfo() {
RequestAccountInfoInternal(
base::MakeUnique<ArcAuthService::AccountInfoNotifier>(
base::Bind(&ArcAuthService::OnAccountInfoReady,
weak_ptr_factory_.GetWeakPtr())));
}
void ArcAuthService::OnAccountInfoReady(mojom::AccountInfoPtr account_info) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
auto* instance = arc_bridge_service()->auth()->GetInstanceForMethod(
"OnAccountInfoReady", kMinVersionForOnAccountInfoReady);
DCHECK(instance);
instance->OnAccountInfoReady(std::move(account_info));
}
void ArcAuthService::GetAuthCodeDeprecated0(
const GetAuthCodeDeprecated0Callback& callback) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
NOTREACHED() << "GetAuthCodeDeprecated0() should no longer be callable";
}
void ArcAuthService::GetAuthCodeDeprecated(
const GetAuthCodeDeprecatedCallback& callback) {
// For robot account we must use RequestAccountInfo because it allows
// to specify account type.
DCHECK(!ArcSessionManager::IsArcKioskMode());
RequestAccountInfoInternal(
base::MakeUnique<ArcAuthService::AccountInfoNotifier>(callback));
}
void ArcAuthService::GetAuthCodeAndAccountTypeDeprecated(
const GetAuthCodeAndAccountTypeDeprecatedCallback& callback) {
RequestAccountInfoInternal(
base::MakeUnique<ArcAuthService::AccountInfoNotifier>(callback));
}
void ArcAuthService::GetIsAccountManagedDeprecated(
const GetIsAccountManagedDeprecatedCallback& callback) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
callback.Run(
policy_util::IsAccountManaged(ArcSessionManager::Get()->profile()));
}
void ArcAuthService::RequestAccountInfoInternal(
std::unique_ptr<AccountInfoNotifier> notifier) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
// No other auth code-related operation may be in progress.
DCHECK(!notifier_);
DCHECK(!fetcher_);
if (ArcSessionManager::IsOptInVerificationDisabled()) {
notifier->Notify(
false /* = is_enforced */, std::string(), GetAccountType(),
policy_util::IsAccountManaged(ArcSessionManager::Get()->profile()));
return;
}
// Hereafter asynchronous operation. Remember the notifier.
notifier_ = std::move(notifier);
if (ArcSessionManager::IsArcKioskMode()) {
// In Kiosk mode, use Robot auth code fetching.
fetcher_ = base::MakeUnique<ArcRobotAuthCodeFetcher>();
} else if (base::FeatureList::IsEnabled(arc::kArcUseAuthEndpointFeature)) {
// Optionally retrieve auth code in silent mode.
fetcher_ = base::MakeUnique<ArcBackgroundAuthCodeFetcher>(
ArcSessionManager::Get()->profile(),
ArcSessionManager::Get()->auth_context());
} else {
// Otherwise, show LSO page and let user click "Sign in" button.
// Here, support_host should be available always. The case support_host is
// not created is when 1) IsOptInVerificationDisabled() is true or 2)
// IsArcKioskMode() is true. Both cases are handled above.
fetcher_ = base::MakeUnique<ArcManualAuthCodeFetcher>(
ArcSessionManager::Get()->auth_context(),
ArcSessionManager::Get()->support_host());
}
fetcher_->Fetch(base::Bind(&ArcAuthService::OnAuthCodeFetched,
weak_ptr_factory_.GetWeakPtr()));
}
void ArcAuthService::OnAuthCodeFetched(const std::string& auth_code) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
fetcher_.reset();
if (auth_code.empty()) {
ArcSessionManager::Get()->OnProvisioningFinished(
ProvisioningResult::CHROME_SERVER_COMMUNICATION_ERROR);
return;
}
notifier_->Notify(
!ArcSessionManager::IsOptInVerificationDisabled(), auth_code,
GetAccountType(),
policy_util::IsAccountManaged(ArcSessionManager::Get()->profile()));
notifier_.reset();
}
} // namespace arc