blob: 28675413a8faf29e98f6032c5f7a67b65221e00a [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/auth/arc_auth_service.h"
#include <utility>
#include "base/command_line.h"
#include "base/memory/singleton.h"
#include "base/time/time.h"
#include "chrome/browser/chromeos/arc/arc_optin_uma.h"
#include "chrome/browser/chromeos/arc/arc_session_manager.h"
#include "chrome/browser/chromeos/arc/arc_util.h"
#include "chrome/browser/chromeos/arc/auth/arc_background_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 "chrome/browser/profiles/profile.h"
#include "chromeos/chromeos_switches.h"
#include "components/arc/arc_bridge_service.h"
#include "components/arc/arc_browser_context_keyed_service_factory_base.h"
#include "components/arc/arc_features.h"
#include "components/arc/arc_prefs.h"
#include "components/arc/arc_service_manager.h"
#include "components/arc/arc_util.h"
#include "components/prefs/pref_service.h"
#include "components/user_manager/user_manager.h"
#include "content/public/browser/browser_thread.h"
namespace arc {
namespace {
// Singleton factory for ArcAuthService.
class ArcAuthServiceFactory
: public internal::ArcBrowserContextKeyedServiceFactoryBase<
ArcAuthService,
ArcAuthServiceFactory> {
public:
// Factory name used by ArcBrowserContextKeyedServiceFactoryBase.
static constexpr const char* kName = "ArcAuthServiceFactory";
static ArcAuthServiceFactory* GetInstance() {
return base::Singleton<ArcAuthServiceFactory>::get();
}
private:
friend struct base::DefaultSingletonTraits<ArcAuthServiceFactory>;
ArcAuthServiceFactory() = default;
~ArcAuthServiceFactory() override = default;
};
// Convers mojom::ArcSignInStatus into ProvisiningResult.
ProvisioningResult ConvertArcSignInStatusToProvisioningResult(
mojom::ArcSignInStatus reason) {
using ArcSignInStatus = mojom::ArcSignInStatus;
#define MAP_PROVISIONING_RESULT(name) \
case ArcSignInStatus::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);
MAP_PROVISIONING_RESULT(NO_NETWORK_CONNECTION);
MAP_PROVISIONING_RESULT(CHROME_SERVER_COMMUNICATION_ERROR);
MAP_PROVISIONING_RESULT(ARC_DISABLED);
MAP_PROVISIONING_RESULT(SUCCESS);
MAP_PROVISIONING_RESULT(SUCCESS_ALREADY_PROVISIONED);
}
#undef MAP_PROVISIONING_RESULT
NOTREACHED() << "unknown reason: " << static_cast<int>(reason);
return ProvisioningResult::UNKNOWN_ERROR;
}
mojom::ChromeAccountType GetAccountType() {
return IsRobotAccountMode() ? mojom::ChromeAccountType::ROBOT_ACCOUNT
: mojom::ChromeAccountType::USER_ACCOUNT;
}
mojom::AccountInfoPtr CreateAccountInfo(bool is_enforced,
const std::string& auth_info,
const std::string& account_name,
mojom::ChromeAccountType account_type,
bool is_managed) {
mojom::AccountInfoPtr account_info = mojom::AccountInfo::New();
account_info->account_name = account_name;
if (account_type == mojom::ChromeAccountType::ACTIVE_DIRECTORY_ACCOUNT) {
account_info->enrollment_token = auth_info;
} else {
if (!is_enforced)
account_info->auth_code = base::nullopt;
else
account_info->auth_code = auth_info;
}
account_info->account_type = account_type;
account_info->is_managed = is_managed;
return account_info;
}
} // namespace
// static
const char ArcAuthService::kArcServiceName[] = "arc::ArcAuthService";
// static
ArcAuthService* ArcAuthService::GetForBrowserContext(
content::BrowserContext* context) {
return ArcAuthServiceFactory::GetForBrowserContext(context);
}
ArcAuthService::ArcAuthService(content::BrowserContext* browser_context,
ArcBridgeService* arc_bridge_service)
: profile_(Profile::FromBrowserContext(browser_context)),
arc_bridge_service_(arc_bridge_service),
weak_ptr_factory_(this) {
arc_bridge_service_->auth()->SetHost(this);
arc_bridge_service_->auth()->AddObserver(this);
}
ArcAuthService::~ArcAuthService() {
arc_bridge_service_->auth()->RemoveObserver(this);
arc_bridge_service_->auth()->SetHost(nullptr);
}
void ArcAuthService::OnConnectionClosed() {
fetcher_.reset();
}
void ArcAuthService::OnAuthorizationComplete(mojom::ArcSignInStatus status,
bool initial_signin) {
if (!initial_signin) {
// Note, UMA for initial signin is updated from ArcSessionManager.
DCHECK_NE(mojom::ArcSignInStatus::SUCCESS_ALREADY_PROVISIONED, status);
UpdateReauthorizationResultUMA(
ConvertArcSignInStatusToProvisioningResult(status), profile_);
return;
}
ArcSessionManager::Get()->OnProvisioningFinished(
ConvertArcSignInStatusToProvisioningResult(status));
}
void ArcAuthService::OnSignInCompleteDeprecated() {
OnAuthorizationComplete(mojom::ArcSignInStatus::SUCCESS,
true /* initial_signin */);
}
void ArcAuthService::OnSignInFailedDeprecated(mojom::ArcSignInStatus reason) {
DCHECK_NE(mojom::ArcSignInStatus::SUCCESS, reason);
OnAuthorizationComplete(reason, true /* initial_signin */);
}
void ArcAuthService::ReportMetrics(mojom::MetricsType metrics_type,
int32_t value) {
switch (metrics_type) {
case mojom::MetricsType::NETWORK_WAITING_TIME_MILLISECONDS:
UpdateAuthTiming("ArcAuth.NetworkWaitTime",
base::TimeDelta::FromMilliseconds(value));
break;
case mojom::MetricsType::CHECKIN_ATTEMPTS:
UpdateAuthCheckinAttempts(value);
break;
case mojom::MetricsType::CHECKIN_TIME_MILLISECONDS:
UpdateAuthTiming("ArcAuth.CheckinTime",
base::TimeDelta::FromMilliseconds(value));
break;
case mojom::MetricsType::SIGNIN_TIME_MILLISECONDS:
UpdateAuthTiming("ArcAuth.SignInTime",
base::TimeDelta::FromMilliseconds(value));
break;
case mojom::MetricsType::ACCOUNT_CHECK_MILLISECONDS:
UpdateAuthTiming("ArcAuth.AccountCheckTime",
base::TimeDelta::FromMilliseconds(value));
break;
}
}
void ArcAuthService::ReportAccountCheckStatus(
mojom::AccountCheckStatus status) {
UpdateAuthAccountCheckStatus(status);
}
void ArcAuthService::OnAccountInfoReady(mojom::AccountInfoPtr account_info,
mojom::ArcSignInStatus status) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
auto* instance = ARC_GET_INSTANCE_FOR_METHOD(arc_bridge_service_->auth(),
OnAccountInfoReady);
DCHECK(instance);
instance->OnAccountInfoReady(std::move(account_info), status);
}
void ArcAuthService::RequestAccountInfo(bool initial_signin) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
// No other auth code-related operation may be in progress.
DCHECK(!fetcher_);
if (IsArcOptInVerificationDisabled()) {
OnAccountInfoReady(
CreateAccountInfo(false /* is_enforced */,
std::string() /* auth_info */,
std::string() /* auth_name */, GetAccountType(),
policy_util::IsAccountManaged(profile_)),
mojom::ArcSignInStatus::SUCCESS);
return;
}
if (IsActiveDirectoryUserForProfile(profile_)) {
// For Active Directory enrolled devices, we get an enrollment token for a
// managed Google Play account from DMServer.
auto enrollment_token_fetcher =
std::make_unique<ArcActiveDirectoryEnrollmentTokenFetcher>(
ArcSessionManager::Get()->support_host());
enrollment_token_fetcher->Fetch(
base::BindOnce(&ArcAuthService::OnEnrollmentTokenFetched,
weak_ptr_factory_.GetWeakPtr()));
fetcher_ = std::move(enrollment_token_fetcher);
return;
}
// For non-AD enrolled devices an auth code is fetched.
std::unique_ptr<ArcAuthCodeFetcher> auth_code_fetcher;
if (IsRobotAccountMode()) {
// In Kiosk and public session mode, use Robot auth code fetching.
auth_code_fetcher = std::make_unique<ArcRobotAuthCodeFetcher>();
} else {
// Optionally retrieve auth code in silent mode.
auth_code_fetcher = std::make_unique<ArcBackgroundAuthCodeFetcher>(
profile_, ArcSessionManager::Get()->auth_context(), initial_signin);
}
auth_code_fetcher->Fetch(base::Bind(&ArcAuthService::OnAuthCodeFetched,
weak_ptr_factory_.GetWeakPtr()));
fetcher_ = std::move(auth_code_fetcher);
}
void ArcAuthService::OnEnrollmentTokenFetched(
ArcActiveDirectoryEnrollmentTokenFetcher::Status status,
const std::string& enrollment_token,
const std::string& user_id) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
fetcher_.reset();
switch (status) {
case ArcActiveDirectoryEnrollmentTokenFetcher::Status::SUCCESS: {
// Save user_id to the user profile.
profile_->GetPrefs()->SetString(prefs::kArcActiveDirectoryPlayUserId,
user_id);
// Send enrollment token to ARC.
OnAccountInfoReady(
CreateAccountInfo(true /* is_enforced */, enrollment_token,
std::string() /* account_name */,
mojom::ChromeAccountType::ACTIVE_DIRECTORY_ACCOUNT,
true),
mojom::ArcSignInStatus::SUCCESS);
break;
}
case ArcActiveDirectoryEnrollmentTokenFetcher::Status::FAILURE: {
// Send error to ARC.
OnAccountInfoReady(
nullptr, mojom::ArcSignInStatus::CHROME_SERVER_COMMUNICATION_ERROR);
break;
}
case ArcActiveDirectoryEnrollmentTokenFetcher::Status::ARC_DISABLED: {
// Send error to ARC.
OnAccountInfoReady(nullptr, mojom::ArcSignInStatus::ARC_DISABLED);
break;
}
}
}
void ArcAuthService::OnAuthCodeFetched(bool success,
const std::string& auth_code) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
fetcher_.reset();
if (success) {
OnAccountInfoReady(
CreateAccountInfo(
!IsArcOptInVerificationDisabled(), auth_code,
ArcSessionManager::Get()->auth_context()->full_account_id(),
GetAccountType(), policy_util::IsAccountManaged(profile_)),
mojom::ArcSignInStatus::SUCCESS);
} else {
// Send error to ARC.
OnAccountInfoReady(
nullptr, mojom::ArcSignInStatus::CHROME_SERVER_COMMUNICATION_ERROR);
}
}
} // namespace arc