1 // Copyright 2015 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.
5 #include "components/proximity_auth/webui/proximity_auth_webui_handler.h"
8 #include "base/i18n/time_formatting.h"
9 #include "base/prefs/pref_service.h"
10 #include "base/time/default_clock.h"
11 #include "base/values.h"
12 #include "components/proximity_auth/cryptauth/base64url.h"
13 #include "components/proximity_auth/cryptauth/cryptauth_enrollment_manager.h"
14 #include "components/proximity_auth/cryptauth/cryptauth_gcm_manager_impl.h"
15 #include "components/proximity_auth/cryptauth/proto/cryptauth_api.pb.h"
16 #include "components/proximity_auth/logging/logging.h"
17 #include "components/proximity_auth/webui/cryptauth_enroller_factory_impl.h"
18 #include "components/proximity_auth/webui/proximity_auth_ui_delegate.h"
19 #include "content/public/browser/web_ui.h"
21 namespace proximity_auth
{
25 // Keys in the JSON representation of a log message.
26 const char kLogMessageTextKey
[] = "text";
27 const char kLogMessageTimeKey
[] = "time";
28 const char kLogMessageFileKey
[] = "file";
29 const char kLogMessageLineKey
[] = "line";
30 const char kLogMessageSeverityKey
[] = "severity";
32 // Keys in the JSON representation of a SyncState object for enrollment or
34 const char kSyncStateLastSuccessTime
[] = "lastSuccessTime";
35 const char kSyncStateNextRefreshTime
[] = "nextRefreshTime";
36 const char kSyncStateRecoveringFromFailure
[] = "recoveringFromFailure";
37 const char kSyncStateOperationInProgress
[] = "operationInProgress";
39 // Converts |log_message| to a raw dictionary value used as a JSON argument to
40 // JavaScript functions.
41 scoped_ptr
<base::DictionaryValue
> LogMessageToDictionary(
42 const LogBuffer::LogMessage
& log_message
) {
43 scoped_ptr
<base::DictionaryValue
> dictionary(new base::DictionaryValue());
44 dictionary
->SetString(kLogMessageTextKey
, log_message
.text
);
45 dictionary
->SetString(
47 base::TimeFormatTimeOfDayWithMilliseconds(log_message
.time
));
48 dictionary
->SetString(kLogMessageFileKey
, log_message
.file
);
49 dictionary
->SetInteger(kLogMessageLineKey
, log_message
.line
);
50 dictionary
->SetInteger(kLogMessageSeverityKey
,
51 static_cast<int>(log_message
.severity
));
52 return dictionary
.Pass();
55 // Keys in the JSON representation of an ExternalDeviceInfo proto.
56 const char kExternalDevicePublicKey
[] = "publicKey";
57 const char kExternalDeviceFriendlyName
[] = "friendlyDeviceName";
58 const char kExternalDeviceUnlockKey
[] = "unlockKey";
59 const char kExternalDeviceConnectionStatus
[] = "connectionStatus";
61 // The possible values of the |kExternalDeviceConnectionStatus| field.
62 const char kExternalDeviceDisconnected
[] = "disconnected";
64 // Converts an ExternalDeviceInfo proto to a JSON dictionary used in JavaScript.
65 scoped_ptr
<base::DictionaryValue
> ExternalDeviceInfoToDictionary(
66 const cryptauth::ExternalDeviceInfo
& device_info
) {
67 std::string base64_public_key
;
68 Base64UrlEncode(device_info
.public_key(), &base64_public_key
);
70 scoped_ptr
<base::DictionaryValue
> dictionary(new base::DictionaryValue());
71 dictionary
->SetString(kExternalDevicePublicKey
, base64_public_key
);
72 dictionary
->SetString(kExternalDeviceFriendlyName
,
73 device_info
.friendly_device_name());
74 dictionary
->SetBoolean(kExternalDeviceUnlockKey
, device_info
.unlock_key());
75 dictionary
->SetString(kExternalDeviceConnectionStatus
,
76 kExternalDeviceDisconnected
);
77 return dictionary
.Pass();
80 // Keys in the JSON representation of an IneligibleDevice proto.
81 const char kIneligibleDeviceReasons
[] = "ineligibilityReasons";
83 // Converts an IneligibleDevice proto to a JSON dictionary used in JavaScript.
84 scoped_ptr
<base::DictionaryValue
> IneligibleDeviceToDictionary(
85 const cryptauth::IneligibleDevice
& ineligible_device
) {
86 scoped_ptr
<base::ListValue
> ineligibility_reasons(new base::ListValue());
87 for (const std::string
& reason
: ineligible_device
.reasons()) {
88 ineligibility_reasons
->AppendString(reason
);
91 scoped_ptr
<base::DictionaryValue
> device_dictionary
=
92 ExternalDeviceInfoToDictionary(ineligible_device
.device());
93 device_dictionary
->Set(kIneligibleDeviceReasons
,
94 ineligibility_reasons
.Pass());
95 return device_dictionary
;
98 // Creates a SyncState JSON object that can be passed to the WebUI.
99 scoped_ptr
<base::DictionaryValue
> CreateSyncStateDictionary(
100 double last_success_time
,
101 double next_refresh_time
,
102 bool is_recovering_from_failure
,
103 bool is_enrollment_in_progress
) {
104 scoped_ptr
<base::DictionaryValue
> sync_state(new base::DictionaryValue());
105 sync_state
->SetDouble(kSyncStateLastSuccessTime
, last_success_time
);
106 sync_state
->SetDouble(kSyncStateNextRefreshTime
, next_refresh_time
);
107 sync_state
->SetBoolean(kSyncStateRecoveringFromFailure
,
108 is_recovering_from_failure
);
109 sync_state
->SetBoolean(kSyncStateOperationInProgress
,
110 is_enrollment_in_progress
);
116 ProximityAuthWebUIHandler::ProximityAuthWebUIHandler(
117 ProximityAuthUIDelegate
* delegate
)
118 : delegate_(delegate
), weak_ptr_factory_(this) {
119 cryptauth_client_factory_
= delegate_
->CreateCryptAuthClientFactory();
122 ProximityAuthWebUIHandler::~ProximityAuthWebUIHandler() {
123 LogBuffer::GetInstance()->RemoveObserver(this);
124 if (enrollment_manager_
)
125 enrollment_manager_
->RemoveObserver(this);
128 void ProximityAuthWebUIHandler::RegisterMessages() {
129 web_ui()->RegisterMessageCallback(
130 "clearLogBuffer", base::Bind(&ProximityAuthWebUIHandler::ClearLogBuffer
,
131 base::Unretained(this)));
133 web_ui()->RegisterMessageCallback(
134 "getLogMessages", base::Bind(&ProximityAuthWebUIHandler::GetLogMessages
,
135 base::Unretained(this)));
137 web_ui()->RegisterMessageCallback(
138 "findEligibleUnlockDevices",
139 base::Bind(&ProximityAuthWebUIHandler::FindEligibleUnlockDevices
,
140 base::Unretained(this)));
142 web_ui()->RegisterMessageCallback(
143 "getSyncStates", base::Bind(&ProximityAuthWebUIHandler::GetSyncStates
,
144 base::Unretained(this)));
146 web_ui()->RegisterMessageCallback(
147 "forceEnrollment", base::Bind(&ProximityAuthWebUIHandler::ForceEnrollment
,
148 base::Unretained(this)));
150 web_ui()->RegisterMessageCallback(
151 "forceDeviceSync", base::Bind(&ProximityAuthWebUIHandler::ForceDeviceSync
,
152 base::Unretained(this)));
154 LogBuffer::GetInstance()->AddObserver(this);
157 InitEnrollmentManager();
161 void ProximityAuthWebUIHandler::OnLogMessageAdded(
162 const LogBuffer::LogMessage
& log_message
) {
163 scoped_ptr
<base::DictionaryValue
> dictionary
=
164 LogMessageToDictionary(log_message
);
165 web_ui()->CallJavascriptFunction("LogBufferInterface.onLogMessageAdded",
169 void ProximityAuthWebUIHandler::OnLogBufferCleared() {
170 web_ui()->CallJavascriptFunction("LogBufferInterface.onLogBufferCleared");
173 void ProximityAuthWebUIHandler::OnEnrollmentStarted() {
174 web_ui()->CallJavascriptFunction(
175 "SyncStateInterface.onEnrollmentStateChanged",
176 *GetEnrollmentStateDictionary());
179 void ProximityAuthWebUIHandler::OnEnrollmentFinished(bool success
) {
180 scoped_ptr
<base::DictionaryValue
> enrollment_state
=
181 GetEnrollmentStateDictionary();
182 PA_LOG(INFO
) << "Enrollment attempt completed with success=" << success
183 << ":\n" << *enrollment_state
;
184 web_ui()->CallJavascriptFunction(
185 "SyncStateInterface.onEnrollmentStateChanged", *enrollment_state
);
188 void ProximityAuthWebUIHandler::OnSyncStarted() {
189 web_ui()->CallJavascriptFunction(
190 "SyncStateInterface.onDeviceSyncStateChanged",
191 *GetDeviceSyncStateDictionary());
194 void ProximityAuthWebUIHandler::OnSyncFinished(
195 CryptAuthDeviceManager::SyncResult sync_result
,
196 CryptAuthDeviceManager::DeviceChangeResult device_change_result
) {
197 scoped_ptr
<base::DictionaryValue
> device_sync_state
=
198 GetDeviceSyncStateDictionary();
199 PA_LOG(INFO
) << "Device sync completed with result="
200 << static_cast<int>(sync_result
) << ":\n" << *device_sync_state
;
201 web_ui()->CallJavascriptFunction(
202 "SyncStateInterface.onDeviceSyncStateChanged", *device_sync_state
);
205 void ProximityAuthWebUIHandler::GetLogMessages(const base::ListValue
* args
) {
206 base::ListValue json_logs
;
207 for (const auto& log
: *LogBuffer::GetInstance()->logs()) {
208 json_logs
.Append(LogMessageToDictionary(log
).release());
210 web_ui()->CallJavascriptFunction("LogBufferInterface.onGotLogMessages",
214 void ProximityAuthWebUIHandler::ClearLogBuffer(const base::ListValue
* args
) {
215 // The OnLogBufferCleared() observer function will be called after the buffer
217 LogBuffer::GetInstance()->Clear();
220 void ProximityAuthWebUIHandler::FindEligibleUnlockDevices(
221 const base::ListValue
* args
) {
222 cryptauth_client_
= cryptauth_client_factory_
->CreateInstance();
224 cryptauth::FindEligibleUnlockDevicesRequest request
;
225 *(request
.mutable_device_classifier()) = delegate_
->GetDeviceClassifier();
226 cryptauth_client_
->FindEligibleUnlockDevices(
228 base::Bind(&ProximityAuthWebUIHandler::OnFoundEligibleUnlockDevices
,
229 weak_ptr_factory_
.GetWeakPtr()),
230 base::Bind(&ProximityAuthWebUIHandler::OnCryptAuthClientError
,
231 weak_ptr_factory_
.GetWeakPtr()));
234 void ProximityAuthWebUIHandler::ForceEnrollment(const base::ListValue
* args
) {
235 if (enrollment_manager_
) {
236 enrollment_manager_
->ForceEnrollmentNow(
237 cryptauth::INVOCATION_REASON_MANUAL
);
241 void ProximityAuthWebUIHandler::ForceDeviceSync(const base::ListValue
* args
) {
243 device_manager_
->ForceSyncNow(cryptauth::INVOCATION_REASON_MANUAL
);
246 void ProximityAuthWebUIHandler::InitGCMManager() {
247 gcm_manager_
.reset(new CryptAuthGCMManagerImpl(delegate_
->GetGCMDriver(),
248 delegate_
->GetPrefService()));
251 void ProximityAuthWebUIHandler::InitEnrollmentManager() {
252 #if defined(OS_CHROMEOS)
253 // TODO(tengs): We initialize a CryptAuthEnrollmentManager here for
254 // development and testing purposes until it is ready to be moved into Chrome.
255 // The public/private key pair has been generated and serialized in a previous
257 std::string user_public_key
;
259 "CAESRgohAD1lP_wgQ8XqVVwz4aK_89SqdvAQG5L_NZH5zXxwg5UbEiEAZFMlgCZ9h8OlyE4"
260 "QYKY5oiOBu0FmLSKeTAXEq2jnVJI=",
263 std::string user_private_key
;
265 "MIIBeQIBADCCAQMGByqGSM49AgEwgfcCAQEwLAYHKoZIzj0BAQIhAP____8AAAABAAAAAAA"
266 "AAAAAAAAA________________MFsEIP____8AAAABAAAAAAAAAAAAAAAA______________"
267 "_8BCBaxjXYqjqT57PrvVV2mIa8ZR0GsMxTsPY7zjw-J9JgSwMVAMSdNgiG5wSTamZ44ROdJ"
268 "reBn36QBEEEaxfR8uEsQkf4vOblY6RA8ncDfYEt6zOg9KE5RdiYwpZP40Li_hp_m47n60p8"
269 "D54WK84zV2sxXs7LtkBoN79R9QIhAP____8AAAAA__________-85vqtpxeehPO5ysL8YyV"
270 "RAgEBBG0wawIBAQQgKZ4Dsm5xe4p5U2XPGxjrG376ZWWIa9E6r0y1BdjIntyhRANCAAQ9ZT"
271 "_8IEPF6lVcM-Giv_PUqnbwEBuS_zWR-c18cIOVG2RTJYAmfYfDpchOEGCmOaIjgbtBZi0in"
275 // This serialized DeviceInfo proto was previously captured from a real
276 // CryptAuth enrollment, and is replayed here for testing purposes.
277 std::string serialized_device_info
;
279 "IkoIARJGCiEAX_ZjLSq73EVcrarX-7l7No7nSP86GEC322ocSZKqUKwSIQDbEDu9KN7AgLM"
280 "v_lzZZNui9zSOgXCeDpLhS2tgrYVXijoEbGlua0IFZW4tVVNKSggBEkYKIQBf9mMtKrvcRV"
281 "ytqtf7uXs2judI_zoYQLfbahxJkqpQrBIhANsQO70o3sCAsy_-XNlk26L3NI6BcJ4OkuFLa"
282 "2CthVeKam9Nb3ppbGxhLzUuMCAoWDExOyBDck9TIHg4Nl82NCA3MTM0LjAuMCkgQXBwbGVX"
283 "ZWJLaXQvNTM3LjM2IChLSFRNTCwgbGlrZSBHZWNrbykgQ2hyb21lLzQ1LjAuMjQyMi4wIFN"
284 "hZmFyaS81MzcuMzZwLYoBAzEuMJABAZoBIG1rYWVtaWdob2xlYmNnY2hsa2JhbmttaWhrbm"
285 "9qZWFrsAHDPuoBHEJLZEluZWxFZk05VG1adGV3eTRGb19RV1Vicz2AAgKyBqIBQVBBOTFiS"
286 "FZDdlJJNGJFSXppMmFXOTBlZ044eHFBYkhWYnJwSVFuMTk3bWltd3RWWTZYN0JEcEI4Szg3"
287 "RjRubkJnejdLX1BQV2xkcUtDRVhiZkFiMGwyN1VaQXgtVjBWbEE4WlFwdkhETmpHVlh4RlV"
288 "WRDFNY1AzNTgtYTZ3eHRpVG5LQnpMTEVIT1F6Ujdpb0lUMzRtWWY1VmNhbmhPZDh3ugYgs9"
289 "7-c7qNUzzLeEqVCDXb_EaJ8wC3iie_Lpid44iuAh3CPo0CCugBCiMIARACGgi5wHHa82avM"
290 "ioQ7y8xhiUBs7Um73ZC1vQlzzIBABLAAeCqGnWF7RwtnmdfIQJoEqXoXrH1qLw4yqUAA1TW"
291 "M1qxTepJOdDHrh54eiejobW0SKpHqTlZIyiK3ObHAPdfzFum1l640RFdFGZTTTksZFqfD9O"
292 "dftoi0pMrApob4gXj8Pv2g22ArX55BiH56TkTIcDcEE3KKnA_2G0INT1y_clZvZfDw1n0WP"
293 "0Xdg1PLLCOb46WfDWUhHvUk3GzUce8xyxsjOkiZUNh8yvhFXaP2wJgVKVWInf0inuofo9Za"
294 "7p44hIgHgKJIr_4fuVs9Ojf0KcMzxoJTbFUGg58jglUAKFfJBLKPpMBeWEyOS5pQUdTNFZ1"
295 "bF9JVWY4YTJDSmJNbXFqaWpYUFYzaVV5dmJXSVRrR3d1bFRaVUs3RGVZczJtT0h5ZkQ1NWR"
296 "HRXEtdnJTdVc4VEZ2Z1haa2xhVEZTN0dqM2xCVUktSHd5Z0h6bHZHX2NGLWtzQmw0dXdveG"
297 "VPWE1hRlJ3WGJHVUU1Tm9sLS1mdkRIcGVZVnJR",
298 &serialized_device_info
);
299 cryptauth::GcmDeviceInfo device_info
;
300 device_info
.ParseFromString(serialized_device_info
);
302 enrollment_manager_
.reset(new CryptAuthEnrollmentManager(
303 make_scoped_ptr(new base::DefaultClock()),
304 make_scoped_ptr(new CryptAuthEnrollerFactoryImpl(delegate_
)),
305 user_public_key
, user_private_key
, device_info
, gcm_manager_
.get(),
306 delegate_
->GetPrefService()));
307 enrollment_manager_
->AddObserver(this);
308 enrollment_manager_
->Start();
312 void ProximityAuthWebUIHandler::InitDeviceManager() {
313 // TODO(tengs): We initialize a CryptAuthDeviceManager here for
314 // development and testing purposes until it is ready to be moved into Chrome.
315 device_manager_
.reset(new CryptAuthDeviceManager(
316 make_scoped_ptr(new base::DefaultClock()),
317 delegate_
->CreateCryptAuthClientFactory(), gcm_manager_
.get(),
318 delegate_
->GetPrefService()));
319 device_manager_
->AddObserver(this);
320 device_manager_
->Start();
323 void ProximityAuthWebUIHandler::OnCryptAuthClientError(
324 const std::string
& error_message
) {
325 PA_LOG(WARNING
) << "CryptAuth request failed: " << error_message
;
326 base::StringValue
error_string(error_message
);
327 web_ui()->CallJavascriptFunction("CryptAuthInterface.onError", error_string
);
330 void ProximityAuthWebUIHandler::OnFoundEligibleUnlockDevices(
331 const cryptauth::FindEligibleUnlockDevicesResponse
& response
) {
332 base::ListValue eligible_devices
;
333 for (const auto& external_device
: response
.eligible_devices()) {
334 eligible_devices
.Append(ExternalDeviceInfoToDictionary(external_device
));
337 base::ListValue ineligible_devices
;
338 for (const auto& ineligible_device
: response
.ineligible_devices()) {
339 ineligible_devices
.Append(IneligibleDeviceToDictionary(ineligible_device
));
342 PA_LOG(INFO
) << "Found " << eligible_devices
.GetSize()
343 << " eligible devices and " << ineligible_devices
.GetSize()
344 << " ineligible devices.";
345 web_ui()->CallJavascriptFunction("CryptAuthInterface.onGotEligibleDevices",
346 eligible_devices
, ineligible_devices
);
349 void ProximityAuthWebUIHandler::GetSyncStates(const base::ListValue
* args
) {
350 scoped_ptr
<base::DictionaryValue
> enrollment_state
=
351 GetEnrollmentStateDictionary();
352 scoped_ptr
<base::DictionaryValue
> device_sync_state
=
353 GetDeviceSyncStateDictionary();
354 PA_LOG(INFO
) << "Enrollment State: \n" << *enrollment_state
355 << "Device Sync State: \n" << *device_sync_state
;
356 web_ui()->CallJavascriptFunction("SyncStateInterface.onGotSyncStates",
357 *enrollment_state
, *device_sync_state
);
360 scoped_ptr
<base::DictionaryValue
>
361 ProximityAuthWebUIHandler::GetEnrollmentStateDictionary() {
362 if (!enrollment_manager_
)
363 return make_scoped_ptr(new base::DictionaryValue());
365 return CreateSyncStateDictionary(
366 enrollment_manager_
->GetLastEnrollmentTime().ToJsTime(),
367 enrollment_manager_
->GetTimeToNextAttempt().InMillisecondsF(),
368 enrollment_manager_
->IsRecoveringFromFailure(),
369 enrollment_manager_
->IsEnrollmentInProgress());
372 scoped_ptr
<base::DictionaryValue
>
373 ProximityAuthWebUIHandler::GetDeviceSyncStateDictionary() {
374 if (!device_manager_
)
375 return make_scoped_ptr(new base::DictionaryValue());
377 return CreateSyncStateDictionary(
378 device_manager_
->GetLastSyncTime().ToJsTime(),
379 device_manager_
->GetTimeToNextAttempt().InMillisecondsF(),
380 device_manager_
->IsRecoveringFromFailure(),
381 device_manager_
->IsSyncInProgress());
384 } // namespace proximity_auth