1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "remoting/host/setup/host_starter.h" 6 7 #include "base/bind.h" 8 #include "base/guid.h" 9 #include "base/location.h" 10 #include "base/thread_task_runner_handle.h" 11 #include "base/values.h" 12 #include "google_apis/google_api_keys.h" 13 #include "remoting/host/pin_hash.h" 14 #include "remoting/host/setup/oauth_helper.h" 15 16 namespace { 17 const int kMaxGetTokensRetries = 3; 18 } // namespace 19 20 namespace remoting { 21 22 HostStarter::HostStarter( 23 scoped_ptr<gaia::GaiaOAuthClient> oauth_client, 24 scoped_ptr<remoting::ServiceClient> service_client, 25 scoped_refptr<remoting::DaemonController> daemon_controller) 26 : oauth_client_(oauth_client.Pass()), 27 service_client_(service_client.Pass()), 28 daemon_controller_(daemon_controller), 29 consent_to_data_collection_(false), 30 unregistering_host_(false), 31 weak_ptr_factory_(this) { 32 weak_ptr_ = weak_ptr_factory_.GetWeakPtr(); 33 main_task_runner_ = base::ThreadTaskRunnerHandle::Get(); 34 } 35 36 HostStarter::~HostStarter() { 37 } 38 39 scoped_ptr<HostStarter> HostStarter::Create( 40 const std::string& chromoting_hosts_url, 41 net::URLRequestContextGetter* url_request_context_getter) { 42 scoped_ptr<gaia::GaiaOAuthClient> oauth_client( 43 new gaia::GaiaOAuthClient(url_request_context_getter)); 44 scoped_ptr<remoting::ServiceClient> service_client( 45 new remoting::ServiceClient( 46 chromoting_hosts_url, url_request_context_getter)); 47 scoped_refptr<remoting::DaemonController> daemon_controller( 48 remoting::DaemonController::Create()); 49 return scoped_ptr<HostStarter>( 50 new HostStarter(oauth_client.Pass(), service_client.Pass(), 51 daemon_controller)); 52 } 53 54 void HostStarter::StartHost( 55 const std::string& host_name, 56 const std::string& host_pin, 57 bool consent_to_data_collection, 58 const std::string& auth_code, 59 const std::string& redirect_url, 60 CompletionCallback on_done) { 61 DCHECK(main_task_runner_->BelongsToCurrentThread()); 62 DCHECK(on_done_.is_null()); 63 64 host_name_ = host_name; 65 host_pin_ = host_pin; 66 consent_to_data_collection_ = consent_to_data_collection; 67 on_done_ = on_done; 68 oauth_client_info_.client_id = 69 google_apis::GetOAuth2ClientID(google_apis::CLIENT_REMOTING); 70 oauth_client_info_.client_secret = 71 google_apis::GetOAuth2ClientSecret(google_apis::CLIENT_REMOTING); 72 oauth_client_info_.redirect_uri = redirect_url; 73 // Map the authorization code to refresh and access tokens. 74 oauth_client_->GetTokensFromAuthCode(oauth_client_info_, auth_code, 75 kMaxGetTokensRetries, this); 76 } 77 78 void HostStarter::OnGetTokensResponse( 79 const std::string& refresh_token, 80 const std::string& access_token, 81 int expires_in_seconds) { 82 if (!main_task_runner_->BelongsToCurrentThread()) { 83 main_task_runner_->PostTask(FROM_HERE, base::Bind( 84 &HostStarter::OnGetTokensResponse, weak_ptr_, 85 refresh_token, access_token, expires_in_seconds)); 86 return; 87 } 88 refresh_token_ = refresh_token; 89 access_token_ = access_token; 90 // Get the email corresponding to the access token. 91 oauth_client_->GetUserEmail(access_token_, 1, this); 92 } 93 94 void HostStarter::OnRefreshTokenResponse( 95 const std::string& access_token, 96 int expires_in_seconds) { 97 // We never request a refresh token, so this call is not expected. 98 NOTREACHED(); 99 } 100 101 // This function is called twice: once with the host owner credentials, and once 102 // with the service account credentials. 103 void HostStarter::OnGetUserEmailResponse(const std::string& user_email) { 104 if (!main_task_runner_->BelongsToCurrentThread()) { 105 main_task_runner_->PostTask(FROM_HERE, base::Bind( 106 &HostStarter::OnGetUserEmailResponse, weak_ptr_, user_email)); 107 return; 108 } 109 110 if (host_owner_.empty()) { 111 // This is the first callback, with the host owner credentials. Store the 112 // owner's email, and register the host. 113 host_owner_ = user_email; 114 host_id_ = base::GenerateGUID(); 115 key_pair_ = RsaKeyPair::Generate(); 116 117 std::string host_client_id; 118 host_client_id = google_apis::GetOAuth2ClientID( 119 google_apis::CLIENT_REMOTING_HOST); 120 121 service_client_->RegisterHost( 122 host_id_, host_name_, key_pair_->GetPublicKey(), host_client_id, 123 access_token_, this); 124 } else { 125 // This is the second callback, with the service account credentials. 126 // This email is the service account's email, used to login to XMPP. 127 xmpp_login_ = user_email; 128 StartHostProcess(); 129 } 130 } 131 132 void HostStarter::OnHostRegistered(const std::string& authorization_code) { 133 if (!main_task_runner_->BelongsToCurrentThread()) { 134 main_task_runner_->PostTask(FROM_HERE, base::Bind( 135 &HostStarter::OnHostRegistered, weak_ptr_, authorization_code)); 136 return; 137 } 138 139 if (authorization_code.empty()) { 140 // No service account code, start the host with the owner's credentials. 141 xmpp_login_ = host_owner_; 142 StartHostProcess(); 143 return; 144 } 145 146 // Received a service account authorization code, update oauth_client_info_ 147 // to use the service account client keys, and get service account tokens. 148 oauth_client_info_.client_id = 149 google_apis::GetOAuth2ClientID( 150 google_apis::CLIENT_REMOTING_HOST); 151 oauth_client_info_.client_secret = 152 google_apis::GetOAuth2ClientSecret( 153 google_apis::CLIENT_REMOTING_HOST); 154 oauth_client_info_.redirect_uri = "oob"; 155 oauth_client_->GetTokensFromAuthCode( 156 oauth_client_info_, authorization_code, kMaxGetTokensRetries, this); 157 } 158 159 void HostStarter::StartHostProcess() { 160 // Start the host. 161 std::string host_secret_hash = remoting::MakeHostPinHash(host_id_, host_pin_); 162 scoped_ptr<base::DictionaryValue> config(new base::DictionaryValue()); 163 if (host_owner_ != xmpp_login_) { 164 config->SetString("host_owner", host_owner_); 165 } 166 config->SetString("xmpp_login", xmpp_login_); 167 config->SetString("oauth_refresh_token", refresh_token_); 168 config->SetString("host_id", host_id_); 169 config->SetString("host_name", host_name_); 170 config->SetString("private_key", key_pair_->ToString()); 171 config->SetString("host_secret_hash", host_secret_hash); 172 daemon_controller_->SetConfigAndStart( 173 config.Pass(), consent_to_data_collection_, 174 base::Bind(&HostStarter::OnHostStarted, base::Unretained(this))); 175 } 176 177 void HostStarter::OnHostStarted(DaemonController::AsyncResult result) { 178 if (!main_task_runner_->BelongsToCurrentThread()) { 179 main_task_runner_->PostTask(FROM_HERE, base::Bind( 180 &HostStarter::OnHostStarted, weak_ptr_, result)); 181 return; 182 } 183 if (result != DaemonController::RESULT_OK) { 184 unregistering_host_ = true; 185 service_client_->UnregisterHost(host_id_, access_token_, this); 186 return; 187 } 188 CompletionCallback cb = on_done_; 189 on_done_.Reset(); 190 cb.Run(START_COMPLETE); 191 } 192 193 void HostStarter::OnOAuthError() { 194 if (!main_task_runner_->BelongsToCurrentThread()) { 195 main_task_runner_->PostTask(FROM_HERE, base::Bind( 196 &HostStarter::OnOAuthError, weak_ptr_)); 197 return; 198 } 199 CompletionCallback cb = on_done_; 200 on_done_.Reset(); 201 if (unregistering_host_) { 202 LOG(ERROR) << "OAuth error occurred when unregistering host."; 203 cb.Run(START_ERROR); 204 } else { 205 cb.Run(OAUTH_ERROR); 206 } 207 } 208 209 void HostStarter::OnNetworkError(int response_code) { 210 if (!main_task_runner_->BelongsToCurrentThread()) { 211 main_task_runner_->PostTask(FROM_HERE, base::Bind( 212 &HostStarter::OnNetworkError, weak_ptr_, response_code)); 213 return; 214 } 215 CompletionCallback cb = on_done_; 216 on_done_.Reset(); 217 if (unregistering_host_) { 218 LOG(ERROR) << "Network error occurred when unregistering host."; 219 cb.Run(START_ERROR); 220 } else { 221 cb.Run(NETWORK_ERROR); 222 } 223 } 224 225 void HostStarter::OnHostUnregistered() { 226 if (!main_task_runner_->BelongsToCurrentThread()) { 227 main_task_runner_->PostTask(FROM_HERE, base::Bind( 228 &HostStarter::OnHostUnregistered, weak_ptr_)); 229 return; 230 } 231 CompletionCallback cb = on_done_; 232 on_done_.Reset(); 233 cb.Run(START_ERROR); 234 } 235 236 } // namespace remoting 237