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
;
99 ProximityAuthWebUIHandler::ProximityAuthWebUIHandler(
100 ProximityAuthUIDelegate
* delegate
)
101 : delegate_(delegate
), weak_ptr_factory_(this) {
102 cryptauth_client_factory_
= delegate_
->CreateCryptAuthClientFactory();
105 ProximityAuthWebUIHandler::~ProximityAuthWebUIHandler() {
106 LogBuffer::GetInstance()->RemoveObserver(this);
107 if (enrollment_manager_
)
108 enrollment_manager_
->RemoveObserver(this);
111 void ProximityAuthWebUIHandler::RegisterMessages() {
112 web_ui()->RegisterMessageCallback(
113 "clearLogBuffer", base::Bind(&ProximityAuthWebUIHandler::ClearLogBuffer
,
114 base::Unretained(this)));
116 web_ui()->RegisterMessageCallback(
117 "getLogMessages", base::Bind(&ProximityAuthWebUIHandler::GetLogMessages
,
118 base::Unretained(this)));
120 web_ui()->RegisterMessageCallback(
121 "findEligibleUnlockDevices",
122 base::Bind(&ProximityAuthWebUIHandler::FindEligibleUnlockDevices
,
123 base::Unretained(this)));
125 web_ui()->RegisterMessageCallback(
126 "getEnrollmentState",
127 base::Bind(&ProximityAuthWebUIHandler::GetEnrollmentState
,
128 base::Unretained(this)));
130 web_ui()->RegisterMessageCallback(
131 "forceEnrollment", base::Bind(&ProximityAuthWebUIHandler::ForceEnrollment
,
132 base::Unretained(this)));
134 LogBuffer::GetInstance()->AddObserver(this);
135 InitEnrollmentManager();
138 void ProximityAuthWebUIHandler::OnLogMessageAdded(
139 const LogBuffer::LogMessage
& log_message
) {
140 scoped_ptr
<base::DictionaryValue
> dictionary
=
141 LogMessageToDictionary(log_message
);
142 web_ui()->CallJavascriptFunction("LogBufferInterface.onLogMessageAdded",
146 void ProximityAuthWebUIHandler::OnLogBufferCleared() {
147 web_ui()->CallJavascriptFunction("LogBufferInterface.onLogBufferCleared");
150 void ProximityAuthWebUIHandler::OnEnrollmentStarted() {
151 web_ui()->CallJavascriptFunction(
152 "SyncStateInterface.onEnrollmentStateChanged",
153 *GetEnrollmentStateDictionary());
156 void ProximityAuthWebUIHandler::OnEnrollmentFinished(bool success
) {
157 scoped_ptr
<base::DictionaryValue
> enrollment_state
=
158 GetEnrollmentStateDictionary();
159 PA_LOG(INFO
) << "Enrollment attempt completed with success=" << success
160 << ":\n" << *enrollment_state
;
161 web_ui()->CallJavascriptFunction(
162 "SyncStateInterface.onEnrollmentStateChanged", *enrollment_state
);
165 void ProximityAuthWebUIHandler::GetLogMessages(const base::ListValue
* args
) {
166 base::ListValue json_logs
;
167 for (const auto& log
: *LogBuffer::GetInstance()->logs()) {
168 json_logs
.Append(LogMessageToDictionary(log
).release());
170 web_ui()->CallJavascriptFunction("LogBufferInterface.onGotLogMessages",
174 void ProximityAuthWebUIHandler::ClearLogBuffer(const base::ListValue
* args
) {
175 // The OnLogBufferCleared() observer function will be called after the buffer
177 LogBuffer::GetInstance()->Clear();
180 void ProximityAuthWebUIHandler::FindEligibleUnlockDevices(
181 const base::ListValue
* args
) {
182 cryptauth_client_
= cryptauth_client_factory_
->CreateInstance();
184 cryptauth::FindEligibleUnlockDevicesRequest request
;
185 *(request
.mutable_device_classifier()) = delegate_
->GetDeviceClassifier();
186 cryptauth_client_
->FindEligibleUnlockDevices(
188 base::Bind(&ProximityAuthWebUIHandler::OnFoundEligibleUnlockDevices
,
189 weak_ptr_factory_
.GetWeakPtr()),
190 base::Bind(&ProximityAuthWebUIHandler::OnCryptAuthClientError
,
191 weak_ptr_factory_
.GetWeakPtr()));
194 void ProximityAuthWebUIHandler::ForceEnrollment(const base::ListValue
* args
) {
195 if (enrollment_manager_
) {
196 enrollment_manager_
->ForceEnrollmentNow(
197 cryptauth::INVOCATION_REASON_MANUAL
);
201 void ProximityAuthWebUIHandler::InitEnrollmentManager() {
202 #if defined(OS_CHROMEOS)
203 // TODO(tengs): We initialize a CryptAuthEnrollmentManager here for
204 // development and testing purposes until it is ready to be moved into Chrome.
205 // The public/private key pair has been generated and serialized in a previous
207 std::string user_public_key
;
209 "CAESRgohAD1lP_wgQ8XqVVwz4aK_89SqdvAQG5L_NZH5zXxwg5UbEiEAZFMlgCZ9h8OlyE4"
210 "QYKY5oiOBu0FmLSKeTAXEq2jnVJI=",
213 std::string user_private_key
;
215 "MIIBeQIBADCCAQMGByqGSM49AgEwgfcCAQEwLAYHKoZIzj0BAQIhAP____8AAAABAAAAAAA"
216 "AAAAAAAAA________________MFsEIP____8AAAABAAAAAAAAAAAAAAAA______________"
217 "_8BCBaxjXYqjqT57PrvVV2mIa8ZR0GsMxTsPY7zjw-J9JgSwMVAMSdNgiG5wSTamZ44ROdJ"
218 "reBn36QBEEEaxfR8uEsQkf4vOblY6RA8ncDfYEt6zOg9KE5RdiYwpZP40Li_hp_m47n60p8"
219 "D54WK84zV2sxXs7LtkBoN79R9QIhAP____8AAAAA__________-85vqtpxeehPO5ysL8YyV"
220 "RAgEBBG0wawIBAQQgKZ4Dsm5xe4p5U2XPGxjrG376ZWWIa9E6r0y1BdjIntyhRANCAAQ9ZT"
221 "_8IEPF6lVcM-Giv_PUqnbwEBuS_zWR-c18cIOVG2RTJYAmfYfDpchOEGCmOaIjgbtBZi0in"
225 // This serialized DeviceInfo proto was previously captured from a real
226 // CryptAuth enrollment, and is replayed here for testing purposes.
227 std::string serialized_device_info
;
229 "IkoIARJGCiEAX_ZjLSq73EVcrarX-7l7No7nSP86GEC322ocSZKqUKwSIQDbEDu9KN7AgLM"
230 "v_lzZZNui9zSOgXCeDpLhS2tgrYVXijoEbGlua0IFZW4tVVNKSggBEkYKIQBf9mMtKrvcRV"
231 "ytqtf7uXs2judI_zoYQLfbahxJkqpQrBIhANsQO70o3sCAsy_-XNlk26L3NI6BcJ4OkuFLa"
232 "2CthVeKam9Nb3ppbGxhLzUuMCAoWDExOyBDck9TIHg4Nl82NCA3MTM0LjAuMCkgQXBwbGVX"
233 "ZWJLaXQvNTM3LjM2IChLSFRNTCwgbGlrZSBHZWNrbykgQ2hyb21lLzQ1LjAuMjQyMi4wIFN"
234 "hZmFyaS81MzcuMzZwLYoBAzEuMJABAZoBIG1rYWVtaWdob2xlYmNnY2hsa2JhbmttaWhrbm"
235 "9qZWFrsAHDPuoBHEJLZEluZWxFZk05VG1adGV3eTRGb19RV1Vicz2AAgKyBqIBQVBBOTFiS"
236 "FZDdlJJNGJFSXppMmFXOTBlZ044eHFBYkhWYnJwSVFuMTk3bWltd3RWWTZYN0JEcEI4Szg3"
237 "RjRubkJnejdLX1BQV2xkcUtDRVhiZkFiMGwyN1VaQXgtVjBWbEE4WlFwdkhETmpHVlh4RlV"
238 "WRDFNY1AzNTgtYTZ3eHRpVG5LQnpMTEVIT1F6Ujdpb0lUMzRtWWY1VmNhbmhPZDh3ugYgs9"
239 "7-c7qNUzzLeEqVCDXb_EaJ8wC3iie_Lpid44iuAh3CPo0CCugBCiMIARACGgi5wHHa82avM"
240 "ioQ7y8xhiUBs7Um73ZC1vQlzzIBABLAAeCqGnWF7RwtnmdfIQJoEqXoXrH1qLw4yqUAA1TW"
241 "M1qxTepJOdDHrh54eiejobW0SKpHqTlZIyiK3ObHAPdfzFum1l640RFdFGZTTTksZFqfD9O"
242 "dftoi0pMrApob4gXj8Pv2g22ArX55BiH56TkTIcDcEE3KKnA_2G0INT1y_clZvZfDw1n0WP"
243 "0Xdg1PLLCOb46WfDWUhHvUk3GzUce8xyxsjOkiZUNh8yvhFXaP2wJgVKVWInf0inuofo9Za"
244 "7p44hIgHgKJIr_4fuVs9Ojf0KcMzxoJTbFUGg58jglUAKFfJBLKPpMBeWEyOS5pQUdTNFZ1"
245 "bF9JVWY4YTJDSmJNbXFqaWpYUFYzaVV5dmJXSVRrR3d1bFRaVUs3RGVZczJtT0h5ZkQ1NWR"
246 "HRXEtdnJTdVc4VEZ2Z1haa2xhVEZTN0dqM2xCVUktSHd5Z0h6bHZHX2NGLWtzQmw0dXdveG"
247 "VPWE1hRlJ3WGJHVUU1Tm9sLS1mdkRIcGVZVnJR",
248 &serialized_device_info
);
249 cryptauth::GcmDeviceInfo device_info
;
250 device_info
.ParseFromString(serialized_device_info
);
252 enrollment_manager_
.reset(new CryptAuthEnrollmentManager(
253 make_scoped_ptr(new base::DefaultClock()),
254 make_scoped_ptr(new CryptAuthEnrollerFactoryImpl(delegate_
)),
255 user_public_key
, user_private_key
, device_info
,
256 delegate_
->GetPrefService()));
257 enrollment_manager_
->AddObserver(this);
258 enrollment_manager_
->Start();
262 void ProximityAuthWebUIHandler::OnCryptAuthClientError(
263 const std::string
& error_message
) {
264 PA_LOG(WARNING
) << "CryptAuth request failed: " << error_message
;
265 base::StringValue
error_string(error_message
);
266 web_ui()->CallJavascriptFunction("CryptAuthInterface.onError", error_string
);
269 void ProximityAuthWebUIHandler::OnFoundEligibleUnlockDevices(
270 const cryptauth::FindEligibleUnlockDevicesResponse
& response
) {
271 base::ListValue eligible_devices
;
272 for (const auto& external_device
: response
.eligible_devices()) {
273 eligible_devices
.Append(ExternalDeviceInfoToDictionary(external_device
));
276 base::ListValue ineligible_devices
;
277 for (const auto& ineligible_device
: response
.ineligible_devices()) {
278 ineligible_devices
.Append(IneligibleDeviceToDictionary(ineligible_device
));
281 PA_LOG(INFO
) << "Found " << eligible_devices
.GetSize()
282 << " eligible devices and " << ineligible_devices
.GetSize()
283 << " ineligible devices.";
284 web_ui()->CallJavascriptFunction("CryptAuthInterface.onGotEligibleDevices",
285 eligible_devices
, ineligible_devices
);
288 void ProximityAuthWebUIHandler::GetEnrollmentState(
289 const base::ListValue
* args
) {
290 scoped_ptr
<base::DictionaryValue
> enrollment_state
=
291 GetEnrollmentStateDictionary();
292 PA_LOG(INFO
) << "Got Enrollment State: \n" << *enrollment_state
;
293 web_ui()->CallJavascriptFunction("SyncStateInterface.onGotEnrollmentState",
297 scoped_ptr
<base::DictionaryValue
>
298 ProximityAuthWebUIHandler::GetEnrollmentStateDictionary() {
299 scoped_ptr
<base::DictionaryValue
> enrollment_state(
300 new base::DictionaryValue());
302 if (!enrollment_manager_
)
303 return enrollment_state
;
305 enrollment_state
->SetDouble(
306 kSyncStateLastSuccessTime
,
307 enrollment_manager_
->GetLastEnrollmentTime().ToJsTime());
308 enrollment_state
->SetDouble(
309 kSyncStateNextRefreshTime
,
310 enrollment_manager_
->GetTimeToNextAttempt().InMillisecondsF());
311 enrollment_state
->SetBoolean(kSyncStateRecoveringFromFailure
,
312 enrollment_manager_
->IsRecoveringFromFailure());
313 enrollment_state
->SetBoolean(kSyncStateOperationInProgress
,
314 enrollment_manager_
->IsEnrollmentInProgress());
315 return enrollment_state
;
318 } // namespace proximity_auth