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