blob: aba0ba67a73b1a15555f03323cef30adf7b6654a [file] [log] [blame]
// Copyright (c) 2012 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 "remoting/host/setup/host_starter.h"
#include <memory>
#include <utility>
#include "base/bind.h"
#include "base/guid.h"
#include "base/location.h"
#include "base/memory/ptr_util.h"
#include "base/strings/string_util.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/values.h"
#include "google_apis/google_api_keys.h"
#include "remoting/host/pin_hash.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
namespace {
const int kMaxGetTokensRetries = 3;
} // namespace
namespace remoting {
HostStarter::HostStarter(
std::unique_ptr<gaia::GaiaOAuthClient> oauth_client,
std::unique_ptr<remoting::ServiceClient> service_client,
scoped_refptr<remoting::DaemonController> daemon_controller,
std::unique_ptr<remoting::HostStopper> host_stopper)
: oauth_client_(std::move(oauth_client)),
service_client_(std::move(service_client)),
daemon_controller_(daemon_controller),
host_stopper_(std::move(host_stopper)),
consent_to_data_collection_(false),
unregistering_host_(false) {
weak_ptr_ = weak_ptr_factory_.GetWeakPtr();
main_task_runner_ = base::ThreadTaskRunnerHandle::Get();
}
HostStarter::~HostStarter() = default;
std::unique_ptr<HostStarter> HostStarter::Create(
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory) {
auto controller = remoting::DaemonController::Create();
return base::WrapUnique(new HostStarter(
std::make_unique<gaia::GaiaOAuthClient>(url_loader_factory),
std::make_unique<remoting::ServiceClient>(url_loader_factory), controller,
std::make_unique<remoting::HostStopper>(
std::make_unique<remoting::ServiceClient>(url_loader_factory),
controller)));
}
void HostStarter::StartHost(const std::string& host_id,
const std::string& host_name,
const std::string& host_pin,
const std::string& host_owner,
bool consent_to_data_collection,
const std::string& auth_code,
const std::string& redirect_url,
CompletionCallback on_done) {
DCHECK(main_task_runner_->BelongsToCurrentThread());
DCHECK(!on_done_);
host_id_ = host_id;
host_name_ = host_name;
host_pin_ = host_pin;
host_owner_ = host_owner;
consent_to_data_collection_ = consent_to_data_collection;
on_done_ = std::move(on_done);
oauth_client_info_.client_id =
google_apis::GetOAuth2ClientID(google_apis::CLIENT_REMOTING);
oauth_client_info_.client_secret =
google_apis::GetOAuth2ClientSecret(google_apis::CLIENT_REMOTING);
oauth_client_info_.redirect_uri = redirect_url;
// Map the authorization code to refresh and access tokens.
DCHECK_EQ(pending_get_tokens_, GET_TOKENS_NONE);
pending_get_tokens_ = GET_TOKENS_DIRECTORY;
oauth_client_->GetTokensFromAuthCode(oauth_client_info_, auth_code,
kMaxGetTokensRetries, this);
}
void HostStarter::OnGetTokensResponse(const std::string& refresh_token,
const std::string& access_token,
int expires_in_seconds) {
if (!main_task_runner_->BelongsToCurrentThread()) {
main_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&HostStarter::OnGetTokensResponse, weak_ptr_,
refresh_token, access_token, expires_in_seconds));
return;
}
if (pending_get_tokens_ == GET_TOKENS_DIRECTORY) {
directory_access_token_ = access_token;
} else if (pending_get_tokens_ == GET_TOKENS_HOST) {
host_refresh_token_ = refresh_token;
host_access_token_ = access_token;
} else {
NOTREACHED();
}
pending_get_tokens_ = GET_TOKENS_NONE;
// Get the email corresponding to the access token.
oauth_client_->GetUserEmail(access_token, 1, this);
}
void HostStarter::OnRefreshTokenResponse(const std::string& access_token,
int expires_in_seconds) {
// We never request a refresh token, so this call is not expected.
NOTREACHED();
}
// This function is called twice: once with the host owner credentials, and once
// with the service account credentials.
void HostStarter::OnGetUserEmailResponse(const std::string& user_email) {
if (!main_task_runner_->BelongsToCurrentThread()) {
main_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&HostStarter::OnGetUserEmailResponse,
weak_ptr_, user_email));
return;
}
if (!auth_code_exchanged_) {
// This is the first callback, with the host owner credentials.
auth_code_exchanged_ = true;
// If a host owner was not provided via the command line, then we just use
// the user_email for the account which generated the auth_code.
// Otherwise we want to verify user_email matches the host_owner provided.
// Note that the auth_code has been exchanged at this point so the user
// can't just re-run the command with the same nonce and a different
// host_owner to get the command to succeed.
if (host_owner_.empty()) {
host_owner_ = user_email;
} else if (!base::EqualsCaseInsensitiveASCII(host_owner_, user_email)) {
LOG(ERROR) << "User email from auth_code (" << user_email << ") does not "
<< "match the host owner provided (" << host_owner_ << ")";
std::move(on_done_).Run(OAUTH_ERROR);
return;
}
// If the host is already running, stop it; then register a new host with
// with the directory.
host_stopper_->StopLocalHost(
directory_access_token_,
base::BindOnce(&HostStarter::OnLocalHostStopped,
base::Unretained(this)));
} else {
// This is the second callback, with the service account credentials.
// This email is the service account's email, used to login to XMPP.
xmpp_login_ = user_email;
StartHostProcess();
}
}
void HostStarter::OnHostRegistered(const std::string& authorization_code) {
if (!main_task_runner_->BelongsToCurrentThread()) {
main_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&HostStarter::OnHostRegistered, weak_ptr_,
authorization_code));
return;
}
if (authorization_code.empty()) {
NOTREACHED() << "No authorization code returned by the Directory.";
std::move(on_done_).Run(START_ERROR);
return;
}
// Received a service account authorization code, update oauth_client_info_
// to use the service account client keys, and get service account tokens.
DCHECK_EQ(pending_get_tokens_, GET_TOKENS_NONE);
pending_get_tokens_ = GET_TOKENS_HOST;
oauth_client_info_.client_id =
google_apis::GetOAuth2ClientID(google_apis::CLIENT_REMOTING_HOST);
oauth_client_info_.client_secret =
google_apis::GetOAuth2ClientSecret(google_apis::CLIENT_REMOTING_HOST);
oauth_client_info_.redirect_uri = "oob";
oauth_client_->GetTokensFromAuthCode(oauth_client_info_, authorization_code,
kMaxGetTokensRetries, this);
}
void HostStarter::StartHostProcess() {
// Start the host.
std::string host_secret_hash = remoting::MakeHostPinHash(host_id_, host_pin_);
std::unique_ptr<base::DictionaryValue> config(new base::DictionaryValue());
config->SetString("host_owner", host_owner_);
config->SetString("xmpp_login", xmpp_login_);
config->SetString("oauth_refresh_token", host_refresh_token_);
config->SetString("host_id", host_id_);
config->SetString("host_name", host_name_);
config->SetString("private_key", key_pair_->ToString());
config->SetString("host_secret_hash", host_secret_hash);
daemon_controller_->SetConfigAndStart(
std::move(config), consent_to_data_collection_,
base::BindOnce(&HostStarter::OnHostStarted, base::Unretained(this)));
}
void HostStarter::OnLocalHostStopped() {
if (host_id_.empty())
host_id_ = base::GenerateGUID();
key_pair_ = RsaKeyPair::Generate();
std::string host_client_id;
host_client_id =
google_apis::GetOAuth2ClientID(google_apis::CLIENT_REMOTING_HOST);
service_client_->RegisterHost(host_id_, host_name_, key_pair_->GetPublicKey(),
host_client_id, directory_access_token_, this);
}
void HostStarter::OnHostStarted(DaemonController::AsyncResult result) {
if (!main_task_runner_->BelongsToCurrentThread()) {
main_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&HostStarter::OnHostStarted, weak_ptr_, result));
return;
}
if (result != DaemonController::RESULT_OK) {
unregistering_host_ = true;
service_client_->UnregisterHost(host_id_, directory_access_token_, this);
return;
}
std::move(on_done_).Run(START_COMPLETE);
}
void HostStarter::OnOAuthError() {
if (!main_task_runner_->BelongsToCurrentThread()) {
main_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&HostStarter::OnOAuthError, weak_ptr_));
return;
}
pending_get_tokens_ = GET_TOKENS_NONE;
if (unregistering_host_) {
LOG(ERROR) << "OAuth error occurred when unregistering host.";
}
std::move(on_done_).Run(unregistering_host_ ? START_ERROR : OAUTH_ERROR);
}
void HostStarter::OnNetworkError(int response_code) {
if (!main_task_runner_->BelongsToCurrentThread()) {
main_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&HostStarter::OnNetworkError, weak_ptr_, response_code));
return;
}
pending_get_tokens_ = GET_TOKENS_NONE;
if (unregistering_host_) {
LOG(ERROR) << "Network error occurred when unregistering host.";
}
std::move(on_done_).Run(unregistering_host_ ? START_ERROR : NETWORK_ERROR);
}
void HostStarter::OnHostUnregistered() {
if (!main_task_runner_->BelongsToCurrentThread()) {
main_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&HostStarter::OnHostUnregistered, weak_ptr_));
return;
}
std::move(on_done_).Run(START_ERROR);
}
} // namespace remoting