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/proto/cryptauth_api.pb.h"
15 #include "components/proximity_auth/logging/logging.h"
16 #include "components/proximity_auth/webui/cryptauth_enroller_factory_impl.h"
17 #include "components/proximity_auth/webui/proximity_auth_ui_delegate.h"
18 #include "content/public/browser/web_ui.h"
20 namespace proximity_auth
{
24 // Keys in the JSON representation of a log message.
25 const char kLogMessageTextKey
[] = "text";
26 const char kLogMessageTimeKey
[] = "time";
27 const char kLogMessageFileKey
[] = "file";
28 const char kLogMessageLineKey
[] = "line";
29 const char kLogMessageSeverityKey
[] = "severity";
31 // Keys in the JSON representation of a SyncState object for enrollment or
33 const char kSyncStateLastSuccessTime
[] = "lastSuccessTime";
34 const char kSyncStateNextRefreshTime
[] = "nextRefreshTime";
35 const char kSyncStateRecoveringFromFailure
[] = "recoveringFromFailure";
36 const char kSyncStateOperationInProgress
[] = "operationInProgress";
38 // Converts |log_message| to a raw dictionary value used as a JSON argument to
39 // JavaScript functions.
40 scoped_ptr
<base::DictionaryValue
> LogMessageToDictionary(
41 const LogBuffer::LogMessage
& log_message
) {
42 scoped_ptr
<base::DictionaryValue
> dictionary(new base::DictionaryValue());
43 dictionary
->SetString(kLogMessageTextKey
, log_message
.text
);
44 dictionary
->SetString(
46 base::TimeFormatTimeOfDayWithMilliseconds(log_message
.time
));
47 dictionary
->SetString(kLogMessageFileKey
, log_message
.file
);
48 dictionary
->SetInteger(kLogMessageLineKey
, log_message
.line
);
49 dictionary
->SetInteger(kLogMessageSeverityKey
,
50 static_cast<int>(log_message
.severity
));
51 return dictionary
.Pass();
54 // Keys in the JSON representation of an ExternalDeviceInfo proto.
55 const char kExternalDevicePublicKey
[] = "publicKey";
56 const char kExternalDeviceFriendlyName
[] = "friendlyDeviceName";
57 const char kExternalDeviceUnlockKey
[] = "unlockKey";
58 const char kExternalDeviceConnectionStatus
[] = "connectionStatus";
60 // The possible values of the |kExternalDeviceConnectionStatus| field.
61 const char kExternalDeviceDisconnected
[] = "disconnected";
63 // Converts an ExternalDeviceInfo proto to a JSON dictionary used in JavaScript.
64 scoped_ptr
<base::DictionaryValue
> ExternalDeviceInfoToDictionary(
65 const cryptauth::ExternalDeviceInfo
& device_info
) {
66 std::string base64_public_key
;
67 Base64UrlEncode(device_info
.public_key(), &base64_public_key
);
69 scoped_ptr
<base::DictionaryValue
> dictionary(new base::DictionaryValue());
70 dictionary
->SetString(kExternalDevicePublicKey
, base64_public_key
);
71 dictionary
->SetString(kExternalDeviceFriendlyName
,
72 device_info
.friendly_device_name());
73 dictionary
->SetBoolean(kExternalDeviceUnlockKey
, device_info
.unlock_key());
74 dictionary
->SetString(kExternalDeviceConnectionStatus
,
75 kExternalDeviceDisconnected
);
76 return dictionary
.Pass();
79 // Keys in the JSON representation of an IneligibleDevice proto.
80 const char kIneligibleDeviceReasons
[] = "ineligibilityReasons";
82 // Converts an IneligibleDevice proto to a JSON dictionary used in JavaScript.
83 scoped_ptr
<base::DictionaryValue
> IneligibleDeviceToDictionary(
84 const cryptauth::IneligibleDevice
& ineligible_device
) {
85 scoped_ptr
<base::ListValue
> ineligibility_reasons(new base::ListValue());
86 for (const std::string
& reason
: ineligible_device
.reasons()) {
87 ineligibility_reasons
->AppendString(reason
);
90 scoped_ptr
<base::DictionaryValue
> device_dictionary
=
91 ExternalDeviceInfoToDictionary(ineligible_device
.device());
92 device_dictionary
->Set(kIneligibleDeviceReasons
,
93 ineligibility_reasons
.Pass());
94 return device_dictionary
;
97 // Creates a SyncState JSON object that can be passed to the WebUI.
98 scoped_ptr
<base::DictionaryValue
> CreateSyncStateDictionary(
99 double last_success_time
,
100 double next_refresh_time
,
101 bool is_recovering_from_failure
,
102 bool is_enrollment_in_progress
) {
103 scoped_ptr
<base::DictionaryValue
> sync_state(new base::DictionaryValue());
104 sync_state
->SetDouble(kSyncStateLastSuccessTime
, last_success_time
);
105 sync_state
->SetDouble(kSyncStateNextRefreshTime
, next_refresh_time
);
106 sync_state
->SetBoolean(kSyncStateRecoveringFromFailure
,
107 is_recovering_from_failure
);
108 sync_state
->SetBoolean(kSyncStateOperationInProgress
,
109 is_enrollment_in_progress
);
115 ProximityAuthWebUIHandler::ProximityAuthWebUIHandler(
116 ProximityAuthUIDelegate
* delegate
)
117 : delegate_(delegate
), weak_ptr_factory_(this) {
118 cryptauth_client_factory_
= delegate_
->CreateCryptAuthClientFactory();
121 ProximityAuthWebUIHandler::~ProximityAuthWebUIHandler() {
122 LogBuffer::GetInstance()->RemoveObserver(this);
123 if (enrollment_manager_
)
124 enrollment_manager_
->RemoveObserver(this);
127 void ProximityAuthWebUIHandler::RegisterMessages() {
128 web_ui()->RegisterMessageCallback(
129 "clearLogBuffer", base::Bind(&ProximityAuthWebUIHandler::ClearLogBuffer
,
130 base::Unretained(this)));
132 web_ui()->RegisterMessageCallback(
133 "getLogMessages", base::Bind(&ProximityAuthWebUIHandler::GetLogMessages
,
134 base::Unretained(this)));
136 web_ui()->RegisterMessageCallback(
137 "findEligibleUnlockDevices",
138 base::Bind(&ProximityAuthWebUIHandler::FindEligibleUnlockDevices
,
139 base::Unretained(this)));
141 web_ui()->RegisterMessageCallback(
142 "getSyncStates", base::Bind(&ProximityAuthWebUIHandler::GetSyncStates
,
143 base::Unretained(this)));
145 web_ui()->RegisterMessageCallback(
146 "forceEnrollment", base::Bind(&ProximityAuthWebUIHandler::ForceEnrollment
,
147 base::Unretained(this)));
149 web_ui()->RegisterMessageCallback(
150 "forceDeviceSync", base::Bind(&ProximityAuthWebUIHandler::ForceDeviceSync
,
151 base::Unretained(this)));
153 LogBuffer::GetInstance()->AddObserver(this);
154 InitEnrollmentManager();
158 void ProximityAuthWebUIHandler::OnLogMessageAdded(
159 const LogBuffer::LogMessage
& log_message
) {
160 scoped_ptr
<base::DictionaryValue
> dictionary
=
161 LogMessageToDictionary(log_message
);
162 web_ui()->CallJavascriptFunction("LogBufferInterface.onLogMessageAdded",
166 void ProximityAuthWebUIHandler::OnLogBufferCleared() {
167 web_ui()->CallJavascriptFunction("LogBufferInterface.onLogBufferCleared");
170 void ProximityAuthWebUIHandler::OnEnrollmentStarted() {
171 web_ui()->CallJavascriptFunction(
172 "SyncStateInterface.onEnrollmentStateChanged",
173 *GetEnrollmentStateDictionary());
176 void ProximityAuthWebUIHandler::OnEnrollmentFinished(bool success
) {
177 scoped_ptr
<base::DictionaryValue
> enrollment_state
=
178 GetEnrollmentStateDictionary();
179 PA_LOG(INFO
) << "Enrollment attempt completed with success=" << success
180 << ":\n" << *enrollment_state
;
181 web_ui()->CallJavascriptFunction(
182 "SyncStateInterface.onEnrollmentStateChanged", *enrollment_state
);
185 void ProximityAuthWebUIHandler::OnSyncStarted() {
186 web_ui()->CallJavascriptFunction(
187 "SyncStateInterface.onDeviceSyncStateChanged",
188 *GetDeviceSyncStateDictionary());
191 void ProximityAuthWebUIHandler::OnSyncFinished(
192 CryptAuthDeviceManager::SyncResult sync_result
,
193 CryptAuthDeviceManager::DeviceChangeResult device_change_result
) {
194 scoped_ptr
<base::DictionaryValue
> device_sync_state
=
195 GetDeviceSyncStateDictionary();
196 PA_LOG(INFO
) << "Device sync completed with result="
197 << static_cast<int>(sync_result
) << ":\n" << *device_sync_state
;
198 web_ui()->CallJavascriptFunction(
199 "SyncStateInterface.onDeviceSyncStateChanged", *device_sync_state
);
202 void ProximityAuthWebUIHandler::GetLogMessages(const base::ListValue
* args
) {
203 base::ListValue json_logs
;
204 for (const auto& log
: *LogBuffer::GetInstance()->logs()) {
205 json_logs
.Append(LogMessageToDictionary(log
).release());
207 web_ui()->CallJavascriptFunction("LogBufferInterface.onGotLogMessages",
211 void ProximityAuthWebUIHandler::ClearLogBuffer(const base::ListValue
* args
) {
212 // The OnLogBufferCleared() observer function will be called after the buffer
214 LogBuffer::GetInstance()->Clear();
217 void ProximityAuthWebUIHandler::FindEligibleUnlockDevices(
218 const base::ListValue
* args
) {
219 cryptauth_client_
= cryptauth_client_factory_
->CreateInstance();
221 cryptauth::FindEligibleUnlockDevicesRequest request
;
222 *(request
.mutable_device_classifier()) = delegate_
->GetDeviceClassifier();
223 cryptauth_client_
->FindEligibleUnlockDevices(
225 base::Bind(&ProximityAuthWebUIHandler::OnFoundEligibleUnlockDevices
,
226 weak_ptr_factory_
.GetWeakPtr()),
227 base::Bind(&ProximityAuthWebUIHandler::OnCryptAuthClientError
,
228 weak_ptr_factory_
.GetWeakPtr()));
231 void ProximityAuthWebUIHandler::ForceEnrollment(const base::ListValue
* args
) {
232 if (enrollment_manager_
) {
233 enrollment_manager_
->ForceEnrollmentNow(
234 cryptauth::INVOCATION_REASON_MANUAL
);
238 void ProximityAuthWebUIHandler::ForceDeviceSync(const base::ListValue
* args
) {
240 device_manager_
->ForceSyncNow(cryptauth::INVOCATION_REASON_MANUAL
);
243 void ProximityAuthWebUIHandler::InitEnrollmentManager() {
244 #if defined(OS_CHROMEOS)
245 // TODO(tengs): We initialize a CryptAuthEnrollmentManager here for
246 // development and testing purposes until it is ready to be moved into Chrome.
247 // The public/private key pair has been generated and serialized in a previous
249 std::string user_public_key
;
251 "CAESRgohAD1lP_wgQ8XqVVwz4aK_89SqdvAQG5L_NZH5zXxwg5UbEiEAZFMlgCZ9h8OlyE4"
252 "QYKY5oiOBu0FmLSKeTAXEq2jnVJI=",
255 std::string user_private_key
;
257 "MIIBeQIBADCCAQMGByqGSM49AgEwgfcCAQEwLAYHKoZIzj0BAQIhAP____8AAAABAAAAAAA"
258 "AAAAAAAAA________________MFsEIP____8AAAABAAAAAAAAAAAAAAAA______________"
259 "_8BCBaxjXYqjqT57PrvVV2mIa8ZR0GsMxTsPY7zjw-J9JgSwMVAMSdNgiG5wSTamZ44ROdJ"
260 "reBn36QBEEEaxfR8uEsQkf4vOblY6RA8ncDfYEt6zOg9KE5RdiYwpZP40Li_hp_m47n60p8"
261 "D54WK84zV2sxXs7LtkBoN79R9QIhAP____8AAAAA__________-85vqtpxeehPO5ysL8YyV"
262 "RAgEBBG0wawIBAQQgKZ4Dsm5xe4p5U2XPGxjrG376ZWWIa9E6r0y1BdjIntyhRANCAAQ9ZT"
263 "_8IEPF6lVcM-Giv_PUqnbwEBuS_zWR-c18cIOVG2RTJYAmfYfDpchOEGCmOaIjgbtBZi0in"
267 // This serialized DeviceInfo proto was previously captured from a real
268 // CryptAuth enrollment, and is replayed here for testing purposes.
269 std::string serialized_device_info
;
271 "IkoIARJGCiEAX_ZjLSq73EVcrarX-7l7No7nSP86GEC322ocSZKqUKwSIQDbEDu9KN7AgLM"
272 "v_lzZZNui9zSOgXCeDpLhS2tgrYVXijoEbGlua0IFZW4tVVNKSggBEkYKIQBf9mMtKrvcRV"
273 "ytqtf7uXs2judI_zoYQLfbahxJkqpQrBIhANsQO70o3sCAsy_-XNlk26L3NI6BcJ4OkuFLa"
274 "2CthVeKam9Nb3ppbGxhLzUuMCAoWDExOyBDck9TIHg4Nl82NCA3MTM0LjAuMCkgQXBwbGVX"
275 "ZWJLaXQvNTM3LjM2IChLSFRNTCwgbGlrZSBHZWNrbykgQ2hyb21lLzQ1LjAuMjQyMi4wIFN"
276 "hZmFyaS81MzcuMzZwLYoBAzEuMJABAZoBIG1rYWVtaWdob2xlYmNnY2hsa2JhbmttaWhrbm"
277 "9qZWFrsAHDPuoBHEJLZEluZWxFZk05VG1adGV3eTRGb19RV1Vicz2AAgKyBqIBQVBBOTFiS"
278 "FZDdlJJNGJFSXppMmFXOTBlZ044eHFBYkhWYnJwSVFuMTk3bWltd3RWWTZYN0JEcEI4Szg3"
279 "RjRubkJnejdLX1BQV2xkcUtDRVhiZkFiMGwyN1VaQXgtVjBWbEE4WlFwdkhETmpHVlh4RlV"
280 "WRDFNY1AzNTgtYTZ3eHRpVG5LQnpMTEVIT1F6Ujdpb0lUMzRtWWY1VmNhbmhPZDh3ugYgs9"
281 "7-c7qNUzzLeEqVCDXb_EaJ8wC3iie_Lpid44iuAh3CPo0CCugBCiMIARACGgi5wHHa82avM"
282 "ioQ7y8xhiUBs7Um73ZC1vQlzzIBABLAAeCqGnWF7RwtnmdfIQJoEqXoXrH1qLw4yqUAA1TW"
283 "M1qxTepJOdDHrh54eiejobW0SKpHqTlZIyiK3ObHAPdfzFum1l640RFdFGZTTTksZFqfD9O"
284 "dftoi0pMrApob4gXj8Pv2g22ArX55BiH56TkTIcDcEE3KKnA_2G0INT1y_clZvZfDw1n0WP"
285 "0Xdg1PLLCOb46WfDWUhHvUk3GzUce8xyxsjOkiZUNh8yvhFXaP2wJgVKVWInf0inuofo9Za"
286 "7p44hIgHgKJIr_4fuVs9Ojf0KcMzxoJTbFUGg58jglUAKFfJBLKPpMBeWEyOS5pQUdTNFZ1"
287 "bF9JVWY4YTJDSmJNbXFqaWpYUFYzaVV5dmJXSVRrR3d1bFRaVUs3RGVZczJtT0h5ZkQ1NWR"
288 "HRXEtdnJTdVc4VEZ2Z1haa2xhVEZTN0dqM2xCVUktSHd5Z0h6bHZHX2NGLWtzQmw0dXdveG"
289 "VPWE1hRlJ3WGJHVUU1Tm9sLS1mdkRIcGVZVnJR",
290 &serialized_device_info
);
291 cryptauth::GcmDeviceInfo device_info
;
292 device_info
.ParseFromString(serialized_device_info
);
294 enrollment_manager_
.reset(new CryptAuthEnrollmentManager(
295 make_scoped_ptr(new base::DefaultClock()),
296 make_scoped_ptr(new CryptAuthEnrollerFactoryImpl(delegate_
)),
297 user_public_key
, user_private_key
, device_info
,
298 delegate_
->GetPrefService()));
299 enrollment_manager_
->AddObserver(this);
300 enrollment_manager_
->Start();
304 void ProximityAuthWebUIHandler::InitDeviceManager() {
305 // TODO(tengs): We initialize a CryptAuthDeviceManager here for
306 // development and testing purposes until it is ready to be moved into Chrome.
307 device_manager_
.reset(new CryptAuthDeviceManager(
308 make_scoped_ptr(new base::DefaultClock()),
309 delegate_
->CreateCryptAuthClientFactory(), delegate_
->GetPrefService()));
310 device_manager_
->AddObserver(this);
311 device_manager_
->Start();
314 void ProximityAuthWebUIHandler::OnCryptAuthClientError(
315 const std::string
& error_message
) {
316 PA_LOG(WARNING
) << "CryptAuth request failed: " << error_message
;
317 base::StringValue
error_string(error_message
);
318 web_ui()->CallJavascriptFunction("CryptAuthInterface.onError", error_string
);
321 void ProximityAuthWebUIHandler::OnFoundEligibleUnlockDevices(
322 const cryptauth::FindEligibleUnlockDevicesResponse
& response
) {
323 base::ListValue eligible_devices
;
324 for (const auto& external_device
: response
.eligible_devices()) {
325 eligible_devices
.Append(ExternalDeviceInfoToDictionary(external_device
));
328 base::ListValue ineligible_devices
;
329 for (const auto& ineligible_device
: response
.ineligible_devices()) {
330 ineligible_devices
.Append(IneligibleDeviceToDictionary(ineligible_device
));
333 PA_LOG(INFO
) << "Found " << eligible_devices
.GetSize()
334 << " eligible devices and " << ineligible_devices
.GetSize()
335 << " ineligible devices.";
336 web_ui()->CallJavascriptFunction("CryptAuthInterface.onGotEligibleDevices",
337 eligible_devices
, ineligible_devices
);
340 void ProximityAuthWebUIHandler::GetSyncStates(const base::ListValue
* args
) {
341 scoped_ptr
<base::DictionaryValue
> enrollment_state
=
342 GetEnrollmentStateDictionary();
343 scoped_ptr
<base::DictionaryValue
> device_sync_state
=
344 GetDeviceSyncStateDictionary();
345 PA_LOG(INFO
) << "Enrollment State: \n" << *enrollment_state
346 << "Device Sync State: \n" << *device_sync_state
;
347 web_ui()->CallJavascriptFunction("SyncStateInterface.onGotSyncStates",
348 *enrollment_state
, *device_sync_state
);
351 scoped_ptr
<base::DictionaryValue
>
352 ProximityAuthWebUIHandler::GetEnrollmentStateDictionary() {
353 if (!enrollment_manager_
)
354 return make_scoped_ptr(new base::DictionaryValue());
356 return CreateSyncStateDictionary(
357 enrollment_manager_
->GetLastEnrollmentTime().ToJsTime(),
358 enrollment_manager_
->GetTimeToNextAttempt().InMillisecondsF(),
359 enrollment_manager_
->IsRecoveringFromFailure(),
360 enrollment_manager_
->IsEnrollmentInProgress());
363 scoped_ptr
<base::DictionaryValue
>
364 ProximityAuthWebUIHandler::GetDeviceSyncStateDictionary() {
365 if (!device_manager_
)
366 return make_scoped_ptr(new base::DictionaryValue());
368 return CreateSyncStateDictionary(
369 device_manager_
->GetLastSyncTime().ToJsTime(),
370 device_manager_
->GetTimeToNextAttempt().InMillisecondsF(),
371 device_manager_
->IsRecoveringFromFailure(),
372 device_manager_
->IsSyncInProgress());
375 } // namespace proximity_auth