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