| // Copyright 2015 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "components/proximity_auth/webui/proximity_auth_webui_handler.h" |
| |
| #include <algorithm> |
| #include <memory> |
| #include <utility> |
| |
| #include "base/base64url.h" |
| #include "base/bind.h" |
| #include "base/i18n/time_formatting.h" |
| #include "base/threading/thread_task_runner_handle.h" |
| #include "base/time/default_clock.h" |
| #include "base/time/default_tick_clock.h" |
| #include "base/values.h" |
| #include "components/cryptauth/cryptauth_enrollment_manager.h" |
| #include "components/cryptauth/proto/cryptauth_api.pb.h" |
| #include "components/cryptauth/remote_device.h" |
| #include "components/cryptauth/remote_device_loader.h" |
| #include "components/cryptauth/secure_context.h" |
| #include "components/cryptauth/secure_message_delegate.h" |
| #include "components/prefs/pref_service.h" |
| #include "components/proximity_auth/logging/logging.h" |
| #include "components/proximity_auth/messenger.h" |
| #include "components/proximity_auth/remote_device_life_cycle_impl.h" |
| #include "components/proximity_auth/remote_status_update.h" |
| #include "components/proximity_auth/webui/reachable_phone_flow.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "content/public/browser/web_ui.h" |
| #include "device/bluetooth/bluetooth_uuid.h" |
| |
| namespace proximity_auth { |
| |
| namespace { |
| |
| // Keys in the JSON representation of a log message. |
| const char kLogMessageTextKey[] = "text"; |
| const char kLogMessageTimeKey[] = "time"; |
| const char kLogMessageFileKey[] = "file"; |
| const char kLogMessageLineKey[] = "line"; |
| const char kLogMessageSeverityKey[] = "severity"; |
| |
| // Keys in the JSON representation of a SyncState object for enrollment or |
| // device sync. |
| const char kSyncStateLastSuccessTime[] = "lastSuccessTime"; |
| const char kSyncStateNextRefreshTime[] = "nextRefreshTime"; |
| const char kSyncStateRecoveringFromFailure[] = "recoveringFromFailure"; |
| const char kSyncStateOperationInProgress[] = "operationInProgress"; |
| |
| // Converts |log_message| to a raw dictionary value used as a JSON argument to |
| // JavaScript functions. |
| std::unique_ptr<base::DictionaryValue> LogMessageToDictionary( |
| const LogBuffer::LogMessage& log_message) { |
| std::unique_ptr<base::DictionaryValue> dictionary( |
| new base::DictionaryValue()); |
| dictionary->SetString(kLogMessageTextKey, log_message.text); |
| dictionary->SetString( |
| kLogMessageTimeKey, |
| base::TimeFormatTimeOfDayWithMilliseconds(log_message.time)); |
| dictionary->SetString(kLogMessageFileKey, log_message.file); |
| dictionary->SetInteger(kLogMessageLineKey, log_message.line); |
| dictionary->SetInteger(kLogMessageSeverityKey, |
| static_cast<int>(log_message.severity)); |
| return dictionary; |
| } |
| |
| // Keys in the JSON representation of an ExternalDeviceInfo proto. |
| const char kExternalDevicePublicKey[] = "publicKey"; |
| const char kExternalDevicePublicKeyTruncated[] = "publicKeyTruncated"; |
| const char kExternalDeviceFriendlyName[] = "friendlyDeviceName"; |
| const char kExternalDeviceBluetoothAddress[] = "bluetoothAddress"; |
| const char kExternalDeviceUnlockKey[] = "unlockKey"; |
| const char kExternalDeviceMobileHotspot[] = "hasMobileHotspot"; |
| const char kExternalDeviceIsArcPlusPlusEnrollment[] = "isArcPlusPlusEnrollment"; |
| const char kExternalDeviceIsPixelPhone[] = "isPixelPhone"; |
| const char kExternalDeviceConnectionStatus[] = "connectionStatus"; |
| const char kExternalDeviceRemoteState[] = "remoteState"; |
| |
| // The possible values of the |kExternalDeviceConnectionStatus| field. |
| const char kExternalDeviceConnected[] = "connected"; |
| const char kExternalDeviceDisconnected[] = "disconnected"; |
| const char kExternalDeviceConnecting[] = "connecting"; |
| |
| // Keys in the JSON representation of an IneligibleDevice proto. |
| const char kIneligibleDeviceReasons[] = "ineligibilityReasons"; |
| |
| // Creates a SyncState JSON object that can be passed to the WebUI. |
| std::unique_ptr<base::DictionaryValue> CreateSyncStateDictionary( |
| double last_success_time, |
| double next_refresh_time, |
| bool is_recovering_from_failure, |
| bool is_enrollment_in_progress) { |
| std::unique_ptr<base::DictionaryValue> sync_state( |
| new base::DictionaryValue()); |
| sync_state->SetDouble(kSyncStateLastSuccessTime, last_success_time); |
| sync_state->SetDouble(kSyncStateNextRefreshTime, next_refresh_time); |
| sync_state->SetBoolean(kSyncStateRecoveringFromFailure, |
| is_recovering_from_failure); |
| sync_state->SetBoolean(kSyncStateOperationInProgress, |
| is_enrollment_in_progress); |
| return sync_state; |
| } |
| |
| } // namespace |
| |
| ProximityAuthWebUIHandler::ProximityAuthWebUIHandler( |
| ProximityAuthClient* proximity_auth_client) |
| : proximity_auth_client_(proximity_auth_client), |
| web_contents_initialized_(false), |
| weak_ptr_factory_(this) { |
| cryptauth_client_factory_ = |
| proximity_auth_client_->CreateCryptAuthClientFactory(); |
| } |
| |
| ProximityAuthWebUIHandler::~ProximityAuthWebUIHandler() { |
| LogBuffer::GetInstance()->RemoveObserver(this); |
| cryptauth::CryptAuthDeviceManager* device_manager = |
| proximity_auth_client_->GetCryptAuthDeviceManager(); |
| if (device_manager) |
| device_manager->RemoveObserver(this); |
| } |
| |
| void ProximityAuthWebUIHandler::RegisterMessages() { |
| web_ui()->RegisterMessageCallback( |
| "onWebContentsInitialized", |
| base::BindRepeating(&ProximityAuthWebUIHandler::OnWebContentsInitialized, |
| base::Unretained(this))); |
| |
| web_ui()->RegisterMessageCallback( |
| "clearLogBuffer", |
| base::BindRepeating(&ProximityAuthWebUIHandler::ClearLogBuffer, |
| base::Unretained(this))); |
| |
| web_ui()->RegisterMessageCallback( |
| "getLogMessages", |
| base::BindRepeating(&ProximityAuthWebUIHandler::GetLogMessages, |
| base::Unretained(this))); |
| |
| web_ui()->RegisterMessageCallback( |
| "toggleUnlockKey", |
| base::BindRepeating(&ProximityAuthWebUIHandler::ToggleUnlockKey, |
| base::Unretained(this))); |
| |
| web_ui()->RegisterMessageCallback( |
| "findEligibleUnlockDevices", |
| base::BindRepeating(&ProximityAuthWebUIHandler::FindEligibleUnlockDevices, |
| base::Unretained(this))); |
| |
| web_ui()->RegisterMessageCallback( |
| "findReachableDevices", |
| base::BindRepeating(&ProximityAuthWebUIHandler::FindReachableDevices, |
| base::Unretained(this))); |
| |
| web_ui()->RegisterMessageCallback( |
| "getLocalState", |
| base::BindRepeating(&ProximityAuthWebUIHandler::GetLocalState, |
| base::Unretained(this))); |
| |
| web_ui()->RegisterMessageCallback( |
| "forceEnrollment", |
| base::BindRepeating(&ProximityAuthWebUIHandler::ForceEnrollment, |
| base::Unretained(this))); |
| |
| web_ui()->RegisterMessageCallback( |
| "forceDeviceSync", |
| base::BindRepeating(&ProximityAuthWebUIHandler::ForceDeviceSync, |
| base::Unretained(this))); |
| |
| web_ui()->RegisterMessageCallback( |
| "toggleConnection", |
| base::BindRepeating(&ProximityAuthWebUIHandler::ToggleConnection, |
| base::Unretained(this))); |
| } |
| |
| void ProximityAuthWebUIHandler::OnLogMessageAdded( |
| const LogBuffer::LogMessage& log_message) { |
| std::unique_ptr<base::DictionaryValue> dictionary = |
| LogMessageToDictionary(log_message); |
| web_ui()->CallJavascriptFunctionUnsafe("LogBufferInterface.onLogMessageAdded", |
| *dictionary); |
| } |
| |
| void ProximityAuthWebUIHandler::OnLogBufferCleared() { |
| web_ui()->CallJavascriptFunctionUnsafe( |
| "LogBufferInterface.onLogBufferCleared"); |
| } |
| |
| void ProximityAuthWebUIHandler::OnEnrollmentStarted() { |
| web_ui()->CallJavascriptFunctionUnsafe( |
| "LocalStateInterface.onEnrollmentStateChanged", |
| *GetEnrollmentStateDictionary()); |
| } |
| |
| void ProximityAuthWebUIHandler::OnEnrollmentFinished(bool success) { |
| std::unique_ptr<base::DictionaryValue> enrollment_state = |
| GetEnrollmentStateDictionary(); |
| PA_LOG(INFO) << "Enrollment attempt completed with success=" << success |
| << ":\n" << *enrollment_state; |
| web_ui()->CallJavascriptFunctionUnsafe( |
| "LocalStateInterface.onEnrollmentStateChanged", *enrollment_state); |
| } |
| |
| void ProximityAuthWebUIHandler::OnSyncStarted() { |
| web_ui()->CallJavascriptFunctionUnsafe( |
| "LocalStateInterface.onDeviceSyncStateChanged", |
| *GetDeviceSyncStateDictionary()); |
| } |
| |
| void ProximityAuthWebUIHandler::OnSyncFinished( |
| cryptauth::CryptAuthDeviceManager::SyncResult sync_result, |
| cryptauth::CryptAuthDeviceManager::DeviceChangeResult |
| device_change_result) { |
| std::unique_ptr<base::DictionaryValue> device_sync_state = |
| GetDeviceSyncStateDictionary(); |
| PA_LOG(INFO) << "Device sync completed with result=" |
| << static_cast<int>(sync_result) << ":\n" << *device_sync_state; |
| web_ui()->CallJavascriptFunctionUnsafe( |
| "LocalStateInterface.onDeviceSyncStateChanged", *device_sync_state); |
| |
| if (device_change_result == |
| cryptauth::CryptAuthDeviceManager::DeviceChangeResult::CHANGED) { |
| std::unique_ptr<base::ListValue> synced_devices = GetRemoteDevicesList(); |
| PA_LOG(INFO) << "New unlock keys obtained after device sync:\n" |
| << *synced_devices; |
| web_ui()->CallJavascriptFunctionUnsafe( |
| "LocalStateInterface.onRemoteDevicesChanged", *synced_devices); |
| } |
| } |
| |
| void ProximityAuthWebUIHandler::OnWebContentsInitialized( |
| const base::ListValue* args) { |
| if (!web_contents_initialized_) { |
| cryptauth::CryptAuthEnrollmentManager* enrollment_manager = |
| proximity_auth_client_->GetCryptAuthEnrollmentManager(); |
| if (enrollment_manager) |
| enrollment_manager->AddObserver(this); |
| |
| cryptauth::CryptAuthDeviceManager* device_manager = |
| proximity_auth_client_->GetCryptAuthDeviceManager(); |
| if (device_manager) |
| device_manager->AddObserver(this); |
| |
| LogBuffer::GetInstance()->AddObserver(this); |
| |
| web_contents_initialized_ = true; |
| } |
| } |
| |
| void ProximityAuthWebUIHandler::GetLogMessages(const base::ListValue* args) { |
| base::ListValue json_logs; |
| for (const auto& log : *LogBuffer::GetInstance()->logs()) { |
| json_logs.Append(LogMessageToDictionary(log)); |
| } |
| web_ui()->CallJavascriptFunctionUnsafe("LogBufferInterface.onGotLogMessages", |
| json_logs); |
| } |
| |
| void ProximityAuthWebUIHandler::ClearLogBuffer(const base::ListValue* args) { |
| // The OnLogBufferCleared() observer function will be called after the buffer |
| // is cleared. |
| LogBuffer::GetInstance()->Clear(); |
| } |
| |
| void ProximityAuthWebUIHandler::ToggleUnlockKey(const base::ListValue* args) { |
| std::string public_key_b64, public_key; |
| bool make_unlock_key; |
| if (args->GetSize() != 2 || !args->GetString(0, &public_key_b64) || |
| !args->GetBoolean(1, &make_unlock_key) || |
| !base::Base64UrlDecode(public_key_b64, |
| base::Base64UrlDecodePolicy::REQUIRE_PADDING, |
| &public_key)) { |
| PA_LOG(ERROR) << "Invalid arguments to toggleUnlockKey"; |
| return; |
| } |
| |
| cryptauth::ToggleEasyUnlockRequest request; |
| request.set_enable(make_unlock_key); |
| request.set_public_key(public_key); |
| *(request.mutable_device_classifier()) = |
| proximity_auth_client_->GetDeviceClassifier(); |
| |
| PA_LOG(INFO) << "Toggling unlock key:\n" |
| << " public_key: " << public_key_b64 << "\n" |
| << " make_unlock_key: " << make_unlock_key; |
| cryptauth_client_ = cryptauth_client_factory_->CreateInstance(); |
| cryptauth_client_->ToggleEasyUnlock( |
| request, base::Bind(&ProximityAuthWebUIHandler::OnEasyUnlockToggled, |
| weak_ptr_factory_.GetWeakPtr()), |
| base::Bind(&ProximityAuthWebUIHandler::OnCryptAuthClientError, |
| weak_ptr_factory_.GetWeakPtr())); |
| } |
| |
| void ProximityAuthWebUIHandler::FindEligibleUnlockDevices( |
| const base::ListValue* args) { |
| cryptauth_client_ = cryptauth_client_factory_->CreateInstance(); |
| |
| cryptauth::FindEligibleUnlockDevicesRequest request; |
| *(request.mutable_device_classifier()) = |
| proximity_auth_client_->GetDeviceClassifier(); |
| cryptauth_client_->FindEligibleUnlockDevices( |
| request, |
| base::Bind(&ProximityAuthWebUIHandler::OnFoundEligibleUnlockDevices, |
| weak_ptr_factory_.GetWeakPtr()), |
| base::Bind(&ProximityAuthWebUIHandler::OnCryptAuthClientError, |
| weak_ptr_factory_.GetWeakPtr())); |
| } |
| |
| void ProximityAuthWebUIHandler::FindReachableDevices( |
| const base::ListValue* args) { |
| if (reachable_phone_flow_) { |
| PA_LOG(INFO) << "Waiting for existing ReachablePhoneFlow to finish."; |
| return; |
| } |
| |
| reachable_phone_flow_.reset( |
| new ReachablePhoneFlow(cryptauth_client_factory_.get())); |
| reachable_phone_flow_->Run( |
| base::Bind(&ProximityAuthWebUIHandler::OnReachablePhonesFound, |
| weak_ptr_factory_.GetWeakPtr())); |
| } |
| |
| void ProximityAuthWebUIHandler::ForceEnrollment(const base::ListValue* args) { |
| cryptauth::CryptAuthEnrollmentManager* enrollment_manager = |
| proximity_auth_client_->GetCryptAuthEnrollmentManager(); |
| if (enrollment_manager) { |
| enrollment_manager->ForceEnrollmentNow(cryptauth::INVOCATION_REASON_MANUAL); |
| } |
| } |
| |
| void ProximityAuthWebUIHandler::ForceDeviceSync(const base::ListValue* args) { |
| cryptauth::CryptAuthDeviceManager* device_manager = |
| proximity_auth_client_->GetCryptAuthDeviceManager(); |
| if (device_manager) |
| device_manager->ForceSyncNow(cryptauth::INVOCATION_REASON_MANUAL); |
| } |
| |
| void ProximityAuthWebUIHandler::ToggleConnection(const base::ListValue* args) { |
| cryptauth::CryptAuthEnrollmentManager* enrollment_manager = |
| proximity_auth_client_->GetCryptAuthEnrollmentManager(); |
| cryptauth::CryptAuthDeviceManager* device_manager = |
| proximity_auth_client_->GetCryptAuthDeviceManager(); |
| if (!enrollment_manager || !device_manager) |
| return; |
| |
| std::string b64_public_key; |
| std::string public_key; |
| if (!enrollment_manager || !device_manager || !args->GetSize() || |
| !args->GetString(0, &b64_public_key) || |
| !base::Base64UrlDecode(b64_public_key, |
| base::Base64UrlDecodePolicy::REQUIRE_PADDING, |
| &public_key)) { |
| return; |
| } |
| |
| for (const auto& unlock_key : device_manager->GetUnlockKeys()) { |
| if (unlock_key.public_key() == public_key) { |
| if (life_cycle_ && selected_remote_device_.public_key == public_key) { |
| CleanUpRemoteDeviceLifeCycle(); |
| return; |
| } |
| |
| // TODO(sacomoto): Pass an instance of ProximityAuthPrefManager. This is |
| // used to get the address of BLE devices. |
| remote_device_loader_.reset(new cryptauth::RemoteDeviceLoader( |
| std::vector<cryptauth::ExternalDeviceInfo>(1, unlock_key), |
| proximity_auth_client_->GetAccountId(), |
| enrollment_manager->GetUserPrivateKey(), |
| proximity_auth_client_->CreateSecureMessageDelegate())); |
| remote_device_loader_->Load( |
| true /* should_load_beacon_seeds */, |
| base::Bind(&ProximityAuthWebUIHandler::OnRemoteDevicesLoaded, |
| weak_ptr_factory_.GetWeakPtr())); |
| return; |
| } |
| } |
| |
| PA_LOG(ERROR) << "Unlock key (" << b64_public_key << ") not found"; |
| } |
| |
| void ProximityAuthWebUIHandler::OnCryptAuthClientError( |
| const std::string& error_message) { |
| PA_LOG(WARNING) << "CryptAuth request failed: " << error_message; |
| base::Value error_string(error_message); |
| web_ui()->CallJavascriptFunctionUnsafe("CryptAuthInterface.onError", |
| error_string); |
| } |
| |
| void ProximityAuthWebUIHandler::OnEasyUnlockToggled( |
| const cryptauth::ToggleEasyUnlockResponse& response) { |
| web_ui()->CallJavascriptFunctionUnsafe( |
| "CryptAuthInterface.onUnlockKeyToggled"); |
| // TODO(tengs): Update the local state to reflect the toggle. |
| } |
| |
| void ProximityAuthWebUIHandler::OnFoundEligibleUnlockDevices( |
| const cryptauth::FindEligibleUnlockDevicesResponse& response) { |
| base::ListValue eligible_devices; |
| for (const auto& external_device : response.eligible_devices()) { |
| eligible_devices.Append(ExternalDeviceInfoToDictionary(external_device)); |
| } |
| |
| base::ListValue ineligible_devices; |
| for (const auto& ineligible_device : response.ineligible_devices()) { |
| ineligible_devices.Append(IneligibleDeviceToDictionary(ineligible_device)); |
| } |
| |
| PA_LOG(INFO) << "Found " << eligible_devices.GetSize() |
| << " eligible devices and " << ineligible_devices.GetSize() |
| << " ineligible devices."; |
| web_ui()->CallJavascriptFunctionUnsafe( |
| "CryptAuthInterface.onGotEligibleDevices", eligible_devices, |
| ineligible_devices); |
| } |
| |
| void ProximityAuthWebUIHandler::OnReachablePhonesFound( |
| const std::vector<cryptauth::ExternalDeviceInfo>& reachable_phones) { |
| reachable_phone_flow_.reset(); |
| base::ListValue device_list; |
| for (const auto& external_device : reachable_phones) { |
| device_list.Append(ExternalDeviceInfoToDictionary(external_device)); |
| } |
| web_ui()->CallJavascriptFunctionUnsafe( |
| "CryptAuthInterface.onGotReachableDevices", device_list); |
| } |
| |
| void ProximityAuthWebUIHandler::GetLocalState(const base::ListValue* args) { |
| std::unique_ptr<base::Value> truncated_local_device_id = |
| GetTruncatedLocalDeviceId(); |
| std::unique_ptr<base::DictionaryValue> enrollment_state = |
| GetEnrollmentStateDictionary(); |
| std::unique_ptr<base::DictionaryValue> device_sync_state = |
| GetDeviceSyncStateDictionary(); |
| std::unique_ptr<base::ListValue> synced_devices = GetRemoteDevicesList(); |
| |
| PA_LOG(INFO) << "==== Got Local State ====\n" |
| << "Device ID (truncated): " << *truncated_local_device_id |
| << "\nEnrollment State: \n" |
| << *enrollment_state << "Device Sync State: \n" |
| << *device_sync_state << "Unlock Keys: \n" |
| << *synced_devices; |
| web_ui()->CallJavascriptFunctionUnsafe( |
| "LocalStateInterface.onGotLocalState", *truncated_local_device_id, |
| *enrollment_state, *device_sync_state, *synced_devices); |
| } |
| |
| std::unique_ptr<base::Value> |
| ProximityAuthWebUIHandler::GetTruncatedLocalDeviceId() { |
| std::string local_public_key = |
| proximity_auth_client_->GetLocalDevicePublicKey(); |
| |
| std::string device_id; |
| base::Base64UrlEncode(local_public_key, |
| base::Base64UrlEncodePolicy::INCLUDE_PADDING, |
| &device_id); |
| |
| return std::make_unique<base::Value>( |
| cryptauth::RemoteDevice::TruncateDeviceIdForLogs(device_id)); |
| } |
| |
| std::unique_ptr<base::DictionaryValue> |
| ProximityAuthWebUIHandler::GetEnrollmentStateDictionary() { |
| cryptauth::CryptAuthEnrollmentManager* enrollment_manager = |
| proximity_auth_client_->GetCryptAuthEnrollmentManager(); |
| if (!enrollment_manager) |
| return std::make_unique<base::DictionaryValue>(); |
| |
| return CreateSyncStateDictionary( |
| enrollment_manager->GetLastEnrollmentTime().ToJsTime(), |
| enrollment_manager->GetTimeToNextAttempt().InMillisecondsF(), |
| enrollment_manager->IsRecoveringFromFailure(), |
| enrollment_manager->IsEnrollmentInProgress()); |
| } |
| |
| std::unique_ptr<base::DictionaryValue> |
| ProximityAuthWebUIHandler::GetDeviceSyncStateDictionary() { |
| cryptauth::CryptAuthDeviceManager* device_manager = |
| proximity_auth_client_->GetCryptAuthDeviceManager(); |
| if (!device_manager) |
| return std::make_unique<base::DictionaryValue>(); |
| |
| return CreateSyncStateDictionary( |
| device_manager->GetLastSyncTime().ToJsTime(), |
| device_manager->GetTimeToNextAttempt().InMillisecondsF(), |
| device_manager->IsRecoveringFromFailure(), |
| device_manager->IsSyncInProgress()); |
| } |
| |
| std::unique_ptr<base::ListValue> |
| ProximityAuthWebUIHandler::GetRemoteDevicesList() { |
| std::unique_ptr<base::ListValue> unlock_keys(new base::ListValue()); |
| cryptauth::CryptAuthDeviceManager* device_manager = |
| proximity_auth_client_->GetCryptAuthDeviceManager(); |
| if (!device_manager) |
| return unlock_keys; |
| |
| for (const auto& unlock_key : device_manager->GetSyncedDevices()) { |
| unlock_keys->Append(ExternalDeviceInfoToDictionary(unlock_key)); |
| } |
| |
| return unlock_keys; |
| } |
| |
| void ProximityAuthWebUIHandler::OnRemoteDevicesLoaded( |
| const std::vector<cryptauth::RemoteDevice>& remote_devices) { |
| if (remote_devices[0].persistent_symmetric_key.empty()) { |
| PA_LOG(ERROR) << "Failed to derive PSK."; |
| return; |
| } |
| |
| selected_remote_device_ = remote_devices[0]; |
| life_cycle_.reset(new RemoteDeviceLifeCycleImpl(selected_remote_device_, |
| proximity_auth_client_)); |
| life_cycle_->AddObserver(this); |
| life_cycle_->Start(); |
| } |
| |
| std::unique_ptr<base::DictionaryValue> |
| ProximityAuthWebUIHandler::ExternalDeviceInfoToDictionary( |
| const cryptauth::ExternalDeviceInfo& device_info) { |
| std::string base64_public_key; |
| base::Base64UrlEncode(device_info.public_key(), |
| base::Base64UrlEncodePolicy::INCLUDE_PADDING, |
| &base64_public_key); |
| |
| // Set the fields in the ExternalDeviceInfo proto. |
| std::unique_ptr<base::DictionaryValue> dictionary( |
| new base::DictionaryValue()); |
| dictionary->SetString(kExternalDevicePublicKey, base64_public_key); |
| dictionary->SetString( |
| kExternalDevicePublicKeyTruncated, |
| cryptauth::RemoteDevice::TruncateDeviceIdForLogs(base64_public_key)); |
| dictionary->SetString(kExternalDeviceFriendlyName, |
| device_info.friendly_device_name()); |
| dictionary->SetString(kExternalDeviceBluetoothAddress, |
| device_info.bluetooth_address()); |
| dictionary->SetBoolean(kExternalDeviceUnlockKey, device_info.unlock_key()); |
| dictionary->SetBoolean(kExternalDeviceMobileHotspot, |
| device_info.mobile_hotspot_supported()); |
| dictionary->SetBoolean(kExternalDeviceIsArcPlusPlusEnrollment, |
| device_info.arc_plus_plus()); |
| dictionary->SetBoolean(kExternalDeviceIsPixelPhone, |
| device_info.pixel_phone()); |
| dictionary->SetString(kExternalDeviceConnectionStatus, |
| kExternalDeviceDisconnected); |
| |
| cryptauth::CryptAuthDeviceManager* device_manager = |
| proximity_auth_client_->GetCryptAuthDeviceManager(); |
| if (!device_manager) |
| return dictionary; |
| |
| // If |device_info| is a known unlock key, then combine the proto data with |
| // the corresponding local device data (e.g. connection status and remote |
| // status updates). |
| std::string public_key = device_info.public_key(); |
| std::vector<cryptauth::ExternalDeviceInfo> unlock_keys = |
| device_manager->GetUnlockKeys(); |
| auto iterator = std::find_if( |
| unlock_keys.begin(), unlock_keys.end(), |
| [&public_key](const cryptauth::ExternalDeviceInfo& unlock_key) { |
| return unlock_key.public_key() == public_key; |
| }); |
| |
| if (iterator == unlock_keys.end() || |
| selected_remote_device_.public_key != device_info.public_key()) |
| return dictionary; |
| |
| // Fill in the current Bluetooth connection status. |
| std::string connection_status = kExternalDeviceDisconnected; |
| if (life_cycle_ && |
| life_cycle_->GetState() == |
| RemoteDeviceLifeCycle::State::SECURE_CHANNEL_ESTABLISHED) { |
| connection_status = kExternalDeviceConnected; |
| } else if (life_cycle_) { |
| connection_status = kExternalDeviceConnecting; |
| } |
| dictionary->SetString(kExternalDeviceConnectionStatus, connection_status); |
| |
| // Fill the remote status dictionary. |
| if (last_remote_status_update_) { |
| std::unique_ptr<base::DictionaryValue> status_dictionary( |
| new base::DictionaryValue()); |
| status_dictionary->SetInteger("userPresent", |
| last_remote_status_update_->user_presence); |
| status_dictionary->SetInteger( |
| "secureScreenLock", |
| last_remote_status_update_->secure_screen_lock_state); |
| status_dictionary->SetInteger( |
| "trustAgent", last_remote_status_update_->trust_agent_state); |
| dictionary->Set(kExternalDeviceRemoteState, std::move(status_dictionary)); |
| } |
| |
| return dictionary; |
| } |
| |
| std::unique_ptr<base::DictionaryValue> |
| ProximityAuthWebUIHandler::IneligibleDeviceToDictionary( |
| const cryptauth::IneligibleDevice& ineligible_device) { |
| std::unique_ptr<base::ListValue> ineligibility_reasons(new base::ListValue()); |
| for (const std::string& reason : ineligible_device.reasons()) { |
| ineligibility_reasons->AppendString(reason); |
| } |
| |
| std::unique_ptr<base::DictionaryValue> device_dictionary = |
| ExternalDeviceInfoToDictionary(ineligible_device.device()); |
| device_dictionary->Set(kIneligibleDeviceReasons, |
| std::move(ineligibility_reasons)); |
| return device_dictionary; |
| } |
| |
| void ProximityAuthWebUIHandler::CleanUpRemoteDeviceLifeCycle() { |
| PA_LOG(INFO) << "Cleaning up connection to " << selected_remote_device_.name |
| << " [" << selected_remote_device_.bluetooth_address << "]"; |
| life_cycle_.reset(); |
| selected_remote_device_ = cryptauth::RemoteDevice(); |
| last_remote_status_update_.reset(); |
| web_ui()->CallJavascriptFunctionUnsafe( |
| "LocalStateInterface.onRemoteDevicesChanged", *GetRemoteDevicesList()); |
| } |
| |
| void ProximityAuthWebUIHandler::OnLifeCycleStateChanged( |
| RemoteDeviceLifeCycle::State old_state, |
| RemoteDeviceLifeCycle::State new_state) { |
| // Do not re-attempt to find a connection after the first one fails--just |
| // abort. |
| if ((old_state != RemoteDeviceLifeCycle::State::STOPPED && |
| new_state == RemoteDeviceLifeCycle::State::FINDING_CONNECTION) || |
| new_state == RemoteDeviceLifeCycle::State::AUTHENTICATION_FAILED) { |
| // Clean up the life cycle asynchronously, because we are currently in the |
| // call stack of |life_cycle_|. |
| base::ThreadTaskRunnerHandle::Get()->PostTask( |
| FROM_HERE, |
| base::Bind(&ProximityAuthWebUIHandler::CleanUpRemoteDeviceLifeCycle, |
| weak_ptr_factory_.GetWeakPtr())); |
| } else if (new_state == |
| RemoteDeviceLifeCycle::State::SECURE_CHANNEL_ESTABLISHED) { |
| life_cycle_->GetMessenger()->AddObserver(this); |
| } |
| |
| web_ui()->CallJavascriptFunctionUnsafe( |
| "LocalStateInterface.onRemoteDevicesChanged", *GetRemoteDevicesList()); |
| } |
| |
| void ProximityAuthWebUIHandler::OnRemoteStatusUpdate( |
| const RemoteStatusUpdate& status_update) { |
| PA_LOG(INFO) << "Remote status update:" |
| << "\n user_presence: " |
| << static_cast<int>(status_update.user_presence) |
| << "\n secure_screen_lock_state: " |
| << static_cast<int>(status_update.secure_screen_lock_state) |
| << "\n trust_agent_state: " |
| << static_cast<int>(status_update.trust_agent_state); |
| |
| last_remote_status_update_.reset(new RemoteStatusUpdate(status_update)); |
| std::unique_ptr<base::ListValue> synced_devices = GetRemoteDevicesList(); |
| web_ui()->CallJavascriptFunctionUnsafe( |
| "LocalStateInterface.onRemoteDevicesChanged", *synced_devices); |
| } |
| |
| } // namespace proximity_auth |