pairing_registry.cc revision a36e5920737c6adbddd3e43b760e5de8431db6e0
1// Copyright 2013 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/protocol/pairing_registry.h" 6 7#include "base/base64.h" 8#include "base/bind.h" 9#include "base/guid.h" 10#include "base/json/json_string_value_serializer.h" 11#include "base/location.h" 12#include "base/single_thread_task_runner.h" 13#include "base/strings/string_number_conversions.h" 14#include "base/thread_task_runner_handle.h" 15#include "base/values.h" 16#include "crypto/random.h" 17 18namespace remoting { 19namespace protocol { 20 21// How many bytes of random data to use for the shared secret. 22const int kKeySize = 16; 23 24const char PairingRegistry::kCreatedTimeKey[] = "createdTime"; 25const char PairingRegistry::kClientIdKey[] = "clientId"; 26const char PairingRegistry::kClientNameKey[] = "clientName"; 27const char PairingRegistry::kSharedSecretKey[] = "sharedSecret"; 28 29PairingRegistry::Pairing::Pairing() { 30} 31 32PairingRegistry::Pairing::Pairing(const base::Time& created_time, 33 const std::string& client_name, 34 const std::string& client_id, 35 const std::string& shared_secret) 36 : created_time_(created_time), 37 client_name_(client_name), 38 client_id_(client_id), 39 shared_secret_(shared_secret) { 40} 41 42PairingRegistry::Pairing::~Pairing() { 43} 44 45PairingRegistry::Pairing PairingRegistry::Pairing::Create( 46 const std::string& client_name) { 47 base::Time created_time = base::Time::Now(); 48 std::string client_id = base::GenerateGUID(); 49 std::string shared_secret; 50 char buffer[kKeySize]; 51 crypto::RandBytes(buffer, arraysize(buffer)); 52 if (!base::Base64Encode(base::StringPiece(buffer, arraysize(buffer)), 53 &shared_secret)) { 54 LOG(FATAL) << "Base64Encode failed."; 55 } 56 return Pairing(created_time, client_name, client_id, shared_secret); 57} 58 59PairingRegistry::Pairing PairingRegistry::Pairing::CreateFromValue( 60 const base::Value& pairing_json) { 61 const base::DictionaryValue* pairing = NULL; 62 if (!pairing_json.GetAsDictionary(&pairing)) { 63 LOG(ERROR) << "Failed to load pairing information: not a dictionary."; 64 return Pairing(); 65 } 66 67 std::string client_name, client_id; 68 double created_time_value; 69 if (pairing->GetDouble(kCreatedTimeKey, &created_time_value) && 70 pairing->GetString(kClientNameKey, &client_name) && 71 pairing->GetString(kClientIdKey, &client_id)) { 72 // The shared secret is optional. 73 std::string shared_secret; 74 pairing->GetString(kSharedSecretKey, &shared_secret); 75 base::Time created_time = base::Time::FromJsTime(created_time_value); 76 return Pairing(created_time, client_name, client_id, shared_secret); 77 } 78 79 LOG(ERROR) << "Failed to load pairing information: unexpected format."; 80 return Pairing(); 81} 82 83scoped_ptr<base::Value> PairingRegistry::Pairing::ToValue() const { 84 scoped_ptr<base::DictionaryValue> pairing(new base::DictionaryValue()); 85 pairing->SetDouble(kCreatedTimeKey, created_time().ToJsTime()); 86 pairing->SetString(kClientNameKey, client_name()); 87 pairing->SetString(kClientIdKey, client_id()); 88 if (!shared_secret().empty()) 89 pairing->SetString(kSharedSecretKey, shared_secret()); 90 return pairing.PassAs<base::Value>(); 91} 92 93bool PairingRegistry::Pairing::operator==(const Pairing& other) const { 94 return created_time_ == other.created_time_ && 95 client_id_ == other.client_id_ && 96 client_name_ == other.client_name_ && 97 shared_secret_ == other.shared_secret_; 98} 99 100bool PairingRegistry::Pairing::is_valid() const { 101 return !client_id_.empty() && !shared_secret_.empty(); 102} 103 104PairingRegistry::PairingRegistry( 105 scoped_refptr<base::SingleThreadTaskRunner> delegate_task_runner, 106 scoped_ptr<Delegate> delegate) 107 : caller_task_runner_(base::ThreadTaskRunnerHandle::Get()), 108 delegate_task_runner_(delegate_task_runner), 109 delegate_(delegate.Pass()) { 110 DCHECK(delegate_); 111} 112 113PairingRegistry::Pairing PairingRegistry::CreatePairing( 114 const std::string& client_name) { 115 DCHECK(caller_task_runner_->BelongsToCurrentThread()); 116 117 Pairing result = Pairing::Create(client_name); 118 AddPairing(result); 119 return result; 120} 121 122void PairingRegistry::GetPairing(const std::string& client_id, 123 const GetPairingCallback& callback) { 124 DCHECK(caller_task_runner_->BelongsToCurrentThread()); 125 126 GetPairingCallback wrapped_callback = base::Bind( 127 &PairingRegistry::InvokeGetPairingCallbackAndScheduleNext, 128 this, callback); 129 base::Closure request = base::Bind( 130 &PairingRegistry::DoLoad, this, client_id, wrapped_callback); 131 ServiceOrQueueRequest(request); 132} 133 134void PairingRegistry::GetAllPairings( 135 const GetAllPairingsCallback& callback) { 136 DCHECK(caller_task_runner_->BelongsToCurrentThread()); 137 138 GetAllPairingsCallback wrapped_callback = base::Bind( 139 &PairingRegistry::InvokeGetAllPairingsCallbackAndScheduleNext, 140 this, callback); 141 GetAllPairingsCallback sanitize_callback = base::Bind( 142 &PairingRegistry::SanitizePairings, 143 this, wrapped_callback); 144 base::Closure request = base::Bind( 145 &PairingRegistry::DoLoadAll, this, sanitize_callback); 146 ServiceOrQueueRequest(request); 147} 148 149void PairingRegistry::DeletePairing( 150 const std::string& client_id, const DoneCallback& callback) { 151 DCHECK(caller_task_runner_->BelongsToCurrentThread()); 152 153 DoneCallback wrapped_callback = base::Bind( 154 &PairingRegistry::InvokeDoneCallbackAndScheduleNext, 155 this, callback); 156 base::Closure request = base::Bind( 157 &PairingRegistry::DoDelete, this, client_id, wrapped_callback); 158 ServiceOrQueueRequest(request); 159} 160 161void PairingRegistry::ClearAllPairings( 162 const DoneCallback& callback) { 163 DCHECK(caller_task_runner_->BelongsToCurrentThread()); 164 165 DoneCallback wrapped_callback = base::Bind( 166 &PairingRegistry::InvokeDoneCallbackAndScheduleNext, 167 this, callback); 168 base::Closure request = base::Bind( 169 &PairingRegistry::DoDeleteAll, this, wrapped_callback); 170 ServiceOrQueueRequest(request); 171} 172 173PairingRegistry::~PairingRegistry() { 174} 175 176void PairingRegistry::PostTask( 177 const scoped_refptr<base::SingleThreadTaskRunner>& task_runner, 178 const tracked_objects::Location& from_here, 179 const base::Closure& task) { 180 task_runner->PostTask(from_here, task); 181} 182 183void PairingRegistry::AddPairing(const Pairing& pairing) { 184 DoneCallback wrapped_callback = base::Bind( 185 &PairingRegistry::InvokeDoneCallbackAndScheduleNext, 186 this, DoneCallback()); 187 base::Closure request = base::Bind( 188 &PairingRegistry::DoSave, this, pairing, wrapped_callback); 189 ServiceOrQueueRequest(request); 190} 191 192void PairingRegistry::DoLoadAll( 193 const protocol::PairingRegistry::GetAllPairingsCallback& callback) { 194 DCHECK(delegate_task_runner_->BelongsToCurrentThread()); 195 196 scoped_ptr<base::ListValue> pairings = delegate_->LoadAll(); 197 PostTask(caller_task_runner_, FROM_HERE, base::Bind(callback, 198 base::Passed(&pairings))); 199} 200 201void PairingRegistry::DoDeleteAll( 202 const protocol::PairingRegistry::DoneCallback& callback) { 203 DCHECK(delegate_task_runner_->BelongsToCurrentThread()); 204 205 bool success = delegate_->DeleteAll(); 206 PostTask(caller_task_runner_, FROM_HERE, base::Bind(callback, success)); 207} 208 209void PairingRegistry::DoLoad( 210 const std::string& client_id, 211 const protocol::PairingRegistry::GetPairingCallback& callback) { 212 DCHECK(delegate_task_runner_->BelongsToCurrentThread()); 213 214 Pairing pairing = delegate_->Load(client_id); 215 PostTask(caller_task_runner_, FROM_HERE, base::Bind(callback, pairing)); 216} 217 218void PairingRegistry::DoSave( 219 const protocol::PairingRegistry::Pairing& pairing, 220 const protocol::PairingRegistry::DoneCallback& callback) { 221 DCHECK(delegate_task_runner_->BelongsToCurrentThread()); 222 223 bool success = delegate_->Save(pairing); 224 PostTask(caller_task_runner_, FROM_HERE, base::Bind(callback, success)); 225} 226 227void PairingRegistry::DoDelete( 228 const std::string& client_id, 229 const protocol::PairingRegistry::DoneCallback& callback) { 230 DCHECK(delegate_task_runner_->BelongsToCurrentThread()); 231 232 bool success = delegate_->Delete(client_id); 233 PostTask(caller_task_runner_, FROM_HERE, base::Bind(callback, success)); 234} 235 236void PairingRegistry::InvokeDoneCallbackAndScheduleNext( 237 const DoneCallback& callback, bool success) { 238 // CreatePairing doesn't have a callback, so the callback can be null. 239 if (!callback.is_null()) 240 callback.Run(success); 241 242 pending_requests_.pop(); 243 ServiceNextRequest(); 244} 245 246void PairingRegistry::InvokeGetPairingCallbackAndScheduleNext( 247 const GetPairingCallback& callback, Pairing pairing) { 248 callback.Run(pairing); 249 pending_requests_.pop(); 250 ServiceNextRequest(); 251} 252 253void PairingRegistry::InvokeGetAllPairingsCallbackAndScheduleNext( 254 const GetAllPairingsCallback& callback, 255 scoped_ptr<base::ListValue> pairings) { 256 callback.Run(pairings.Pass()); 257 pending_requests_.pop(); 258 ServiceNextRequest(); 259} 260 261void PairingRegistry::SanitizePairings(const GetAllPairingsCallback& callback, 262 scoped_ptr<base::ListValue> pairings) { 263 DCHECK(caller_task_runner_->BelongsToCurrentThread()); 264 265 scoped_ptr<base::ListValue> sanitized_pairings(new base::ListValue()); 266 for (size_t i = 0; i < pairings->GetSize(); ++i) { 267 DictionaryValue* pairing_json; 268 if (!pairings->GetDictionary(i, &pairing_json)) { 269 LOG(WARNING) << "A pairing entry is not a dictionary."; 270 continue; 271 } 272 273 // Parse the pairing data. 274 Pairing pairing = Pairing::CreateFromValue(*pairing_json); 275 if (!pairing.is_valid()) { 276 LOG(WARNING) << "Could not parse a pairing entry."; 277 continue; 278 } 279 280 // Clear the shared secrect and append the pairing data to the list. 281 Pairing sanitized_pairing( 282 pairing.created_time(), 283 pairing.client_name(), 284 pairing.client_id(), 285 ""); 286 sanitized_pairings->Append(sanitized_pairing.ToValue().release()); 287 } 288 289 callback.Run(sanitized_pairings.Pass()); 290} 291 292void PairingRegistry::ServiceOrQueueRequest(const base::Closure& request) { 293 bool servicing_request = !pending_requests_.empty(); 294 pending_requests_.push(request); 295 if (!servicing_request) { 296 ServiceNextRequest(); 297 } 298} 299 300void PairingRegistry::ServiceNextRequest() { 301 if (pending_requests_.empty()) 302 return; 303 304 PostTask(delegate_task_runner_, FROM_HERE, pending_requests_.front()); 305} 306 307} // namespace protocol 308} // namespace remoting 309