pairing_registry.cc revision ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16
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::DictionaryValue& pairing) { 61 std::string client_name, client_id; 62 double created_time_value; 63 if (pairing.GetDouble(kCreatedTimeKey, &created_time_value) && 64 pairing.GetString(kClientNameKey, &client_name) && 65 pairing.GetString(kClientIdKey, &client_id)) { 66 // The shared secret is optional. 67 std::string shared_secret; 68 pairing.GetString(kSharedSecretKey, &shared_secret); 69 base::Time created_time = base::Time::FromJsTime(created_time_value); 70 return Pairing(created_time, client_name, client_id, shared_secret); 71 } 72 73 LOG(ERROR) << "Failed to load pairing information: unexpected format."; 74 return Pairing(); 75} 76 77scoped_ptr<base::DictionaryValue> PairingRegistry::Pairing::ToValue() const { 78 scoped_ptr<base::DictionaryValue> pairing(new base::DictionaryValue()); 79 pairing->SetDouble(kCreatedTimeKey, created_time().ToJsTime()); 80 pairing->SetString(kClientNameKey, client_name()); 81 pairing->SetString(kClientIdKey, client_id()); 82 if (!shared_secret().empty()) 83 pairing->SetString(kSharedSecretKey, shared_secret()); 84 return pairing.Pass(); 85} 86 87bool PairingRegistry::Pairing::operator==(const Pairing& other) const { 88 return created_time_ == other.created_time_ && 89 client_id_ == other.client_id_ && 90 client_name_ == other.client_name_ && 91 shared_secret_ == other.shared_secret_; 92} 93 94bool PairingRegistry::Pairing::is_valid() const { 95 return !client_id_.empty() && !shared_secret_.empty(); 96} 97 98PairingRegistry::PairingRegistry( 99 scoped_refptr<base::SingleThreadTaskRunner> delegate_task_runner, 100 scoped_ptr<Delegate> delegate) 101 : caller_task_runner_(base::ThreadTaskRunnerHandle::Get()), 102 delegate_task_runner_(delegate_task_runner), 103 delegate_(delegate.Pass()) { 104 DCHECK(delegate_); 105} 106 107PairingRegistry::Pairing PairingRegistry::CreatePairing( 108 const std::string& client_name) { 109 DCHECK(caller_task_runner_->BelongsToCurrentThread()); 110 111 Pairing result = Pairing::Create(client_name); 112 AddPairing(result); 113 return result; 114} 115 116void PairingRegistry::GetPairing(const std::string& client_id, 117 const GetPairingCallback& callback) { 118 DCHECK(caller_task_runner_->BelongsToCurrentThread()); 119 120 GetPairingCallback wrapped_callback = base::Bind( 121 &PairingRegistry::InvokeGetPairingCallbackAndScheduleNext, 122 this, callback); 123 base::Closure request = base::Bind( 124 &PairingRegistry::DoLoad, this, client_id, wrapped_callback); 125 ServiceOrQueueRequest(request); 126} 127 128void PairingRegistry::GetAllPairings( 129 const GetAllPairingsCallback& callback) { 130 DCHECK(caller_task_runner_->BelongsToCurrentThread()); 131 132 GetAllPairingsCallback wrapped_callback = base::Bind( 133 &PairingRegistry::InvokeGetAllPairingsCallbackAndScheduleNext, 134 this, callback); 135 GetAllPairingsCallback sanitize_callback = base::Bind( 136 &PairingRegistry::SanitizePairings, 137 this, wrapped_callback); 138 base::Closure request = base::Bind( 139 &PairingRegistry::DoLoadAll, this, sanitize_callback); 140 ServiceOrQueueRequest(request); 141} 142 143void PairingRegistry::DeletePairing( 144 const std::string& client_id, const DoneCallback& callback) { 145 DCHECK(caller_task_runner_->BelongsToCurrentThread()); 146 147 DoneCallback wrapped_callback = base::Bind( 148 &PairingRegistry::InvokeDoneCallbackAndScheduleNext, 149 this, callback); 150 base::Closure request = base::Bind( 151 &PairingRegistry::DoDelete, this, client_id, wrapped_callback); 152 ServiceOrQueueRequest(request); 153} 154 155void PairingRegistry::ClearAllPairings( 156 const DoneCallback& callback) { 157 DCHECK(caller_task_runner_->BelongsToCurrentThread()); 158 159 DoneCallback wrapped_callback = base::Bind( 160 &PairingRegistry::InvokeDoneCallbackAndScheduleNext, 161 this, callback); 162 base::Closure request = base::Bind( 163 &PairingRegistry::DoDeleteAll, this, wrapped_callback); 164 ServiceOrQueueRequest(request); 165} 166 167PairingRegistry::~PairingRegistry() { 168} 169 170void PairingRegistry::PostTask( 171 const scoped_refptr<base::SingleThreadTaskRunner>& task_runner, 172 const tracked_objects::Location& from_here, 173 const base::Closure& task) { 174 task_runner->PostTask(from_here, task); 175} 176 177void PairingRegistry::AddPairing(const Pairing& pairing) { 178 DoneCallback wrapped_callback = base::Bind( 179 &PairingRegistry::InvokeDoneCallbackAndScheduleNext, 180 this, DoneCallback()); 181 base::Closure request = base::Bind( 182 &PairingRegistry::DoSave, this, pairing, wrapped_callback); 183 ServiceOrQueueRequest(request); 184} 185 186void PairingRegistry::DoLoadAll( 187 const protocol::PairingRegistry::GetAllPairingsCallback& callback) { 188 DCHECK(delegate_task_runner_->BelongsToCurrentThread()); 189 190 scoped_ptr<base::ListValue> pairings = delegate_->LoadAll(); 191 PostTask(caller_task_runner_, FROM_HERE, base::Bind(callback, 192 base::Passed(&pairings))); 193} 194 195void PairingRegistry::DoDeleteAll( 196 const protocol::PairingRegistry::DoneCallback& callback) { 197 DCHECK(delegate_task_runner_->BelongsToCurrentThread()); 198 199 bool success = delegate_->DeleteAll(); 200 PostTask(caller_task_runner_, FROM_HERE, base::Bind(callback, success)); 201} 202 203void PairingRegistry::DoLoad( 204 const std::string& client_id, 205 const protocol::PairingRegistry::GetPairingCallback& callback) { 206 DCHECK(delegate_task_runner_->BelongsToCurrentThread()); 207 208 Pairing pairing = delegate_->Load(client_id); 209 PostTask(caller_task_runner_, FROM_HERE, base::Bind(callback, pairing)); 210} 211 212void PairingRegistry::DoSave( 213 const protocol::PairingRegistry::Pairing& pairing, 214 const protocol::PairingRegistry::DoneCallback& callback) { 215 DCHECK(delegate_task_runner_->BelongsToCurrentThread()); 216 217 bool success = delegate_->Save(pairing); 218 PostTask(caller_task_runner_, FROM_HERE, base::Bind(callback, success)); 219} 220 221void PairingRegistry::DoDelete( 222 const std::string& client_id, 223 const protocol::PairingRegistry::DoneCallback& callback) { 224 DCHECK(delegate_task_runner_->BelongsToCurrentThread()); 225 226 bool success = delegate_->Delete(client_id); 227 PostTask(caller_task_runner_, FROM_HERE, base::Bind(callback, success)); 228} 229 230void PairingRegistry::InvokeDoneCallbackAndScheduleNext( 231 const DoneCallback& callback, bool success) { 232 // CreatePairing doesn't have a callback, so the callback can be null. 233 if (!callback.is_null()) 234 callback.Run(success); 235 236 pending_requests_.pop(); 237 ServiceNextRequest(); 238} 239 240void PairingRegistry::InvokeGetPairingCallbackAndScheduleNext( 241 const GetPairingCallback& callback, Pairing pairing) { 242 callback.Run(pairing); 243 pending_requests_.pop(); 244 ServiceNextRequest(); 245} 246 247void PairingRegistry::InvokeGetAllPairingsCallbackAndScheduleNext( 248 const GetAllPairingsCallback& callback, 249 scoped_ptr<base::ListValue> pairings) { 250 callback.Run(pairings.Pass()); 251 pending_requests_.pop(); 252 ServiceNextRequest(); 253} 254 255void PairingRegistry::SanitizePairings(const GetAllPairingsCallback& callback, 256 scoped_ptr<base::ListValue> pairings) { 257 DCHECK(caller_task_runner_->BelongsToCurrentThread()); 258 259 scoped_ptr<base::ListValue> sanitized_pairings(new base::ListValue()); 260 for (size_t i = 0; i < pairings->GetSize(); ++i) { 261 DictionaryValue* pairing_json; 262 if (!pairings->GetDictionary(i, &pairing_json)) { 263 LOG(WARNING) << "A pairing entry is not a dictionary."; 264 continue; 265 } 266 267 // Parse the pairing data. 268 Pairing pairing = Pairing::CreateFromValue(*pairing_json); 269 if (!pairing.is_valid()) { 270 LOG(WARNING) << "Could not parse a pairing entry."; 271 continue; 272 } 273 274 // Clear the shared secrect and append the pairing data to the list. 275 Pairing sanitized_pairing( 276 pairing.created_time(), 277 pairing.client_name(), 278 pairing.client_id(), 279 ""); 280 sanitized_pairings->Append(sanitized_pairing.ToValue().release()); 281 } 282 283 callback.Run(sanitized_pairings.Pass()); 284} 285 286void PairingRegistry::ServiceOrQueueRequest(const base::Closure& request) { 287 bool servicing_request = !pending_requests_.empty(); 288 pending_requests_.push(request); 289 if (!servicing_request) { 290 ServiceNextRequest(); 291 } 292} 293 294void PairingRegistry::ServiceNextRequest() { 295 if (pending_requests_.empty()) 296 return; 297 298 PostTask(delegate_task_runner_, FROM_HERE, pending_requests_.front()); 299} 300 301} // namespace protocol 302} // namespace remoting 303