Kyle Horimoto | a7554261 | 2018-04-18 00:33:39 | [diff] [blame] | 1 | // Copyright 2017 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 "components/cryptauth/software_feature_manager_impl.h" |
| 6 | |
| 7 | #include "base/bind.h" |
| 8 | #include "base/macros.h" |
| 9 | #include "components/cryptauth/mock_cryptauth_client.h" |
| 10 | #include "components/cryptauth/remote_device.h" |
| 11 | #include "components/cryptauth/remote_device_test_util.h" |
| 12 | #include "testing/gmock/include/gmock/gmock.h" |
| 13 | #include "testing/gtest/include/gtest/gtest.h" |
| 14 | |
| 15 | using testing::_; |
| 16 | using testing::Invoke; |
| 17 | |
| 18 | namespace cryptauth { |
| 19 | |
| 20 | namespace { |
| 21 | |
| 22 | const char kSuccessResult[] = "success"; |
| 23 | const char kErrorSetSoftwareFeatureState[] = "setSoftwareFeatureStateError"; |
| 24 | const char kErrorFindEligibleDevices[] = "findEligibleDevicesError"; |
| 25 | |
| 26 | std::vector<cryptauth::ExternalDeviceInfo> |
| 27 | CreateExternalDeviceInfosForRemoteDevices( |
| 28 | const std::vector<cryptauth::RemoteDevice> remote_devices) { |
| 29 | std::vector<cryptauth::ExternalDeviceInfo> device_infos; |
| 30 | for (const auto& remote_device : remote_devices) { |
| 31 | // Add an ExternalDeviceInfo with the same public key as the RemoteDevice. |
| 32 | cryptauth::ExternalDeviceInfo info; |
| 33 | info.set_public_key(remote_device.public_key); |
| 34 | device_infos.push_back(info); |
| 35 | } |
| 36 | return device_infos; |
| 37 | } |
| 38 | |
| 39 | } // namespace |
| 40 | |
| 41 | class CryptAuthSoftwareFeatureManagerImplTest |
| 42 | : public testing::Test, |
| 43 | public MockCryptAuthClientFactory::Observer { |
| 44 | public: |
| 45 | CryptAuthSoftwareFeatureManagerImplTest() |
| 46 | : all_test_external_device_infos_( |
| 47 | CreateExternalDeviceInfosForRemoteDevices( |
| 48 | cryptauth::GenerateTestRemoteDevices(5))), |
| 49 | test_eligible_external_devices_infos_( |
| 50 | {all_test_external_device_infos_[0], |
| 51 | all_test_external_device_infos_[1], |
| 52 | all_test_external_device_infos_[2]}), |
| 53 | test_ineligible_external_devices_infos_( |
| 54 | {all_test_external_device_infos_[3], |
| 55 | all_test_external_device_infos_[4]}) {} |
| 56 | |
| 57 | void SetUp() override { |
| 58 | mock_cryptauth_client_factory_ = |
| 59 | std::make_unique<MockCryptAuthClientFactory>( |
| 60 | MockCryptAuthClientFactory::MockType::MAKE_NICE_MOCKS); |
| 61 | mock_cryptauth_client_factory_->AddObserver(this); |
| 62 | software_feature_manager_ = |
| 63 | SoftwareFeatureManagerImpl::Factory::NewInstance( |
| 64 | mock_cryptauth_client_factory_.get()); |
| 65 | } |
| 66 | |
| 67 | void TearDown() override { |
| 68 | mock_cryptauth_client_factory_->RemoveObserver(this); |
| 69 | } |
| 70 | |
| 71 | void OnCryptAuthClientCreated(MockCryptAuthClient* client) override { |
| 72 | ON_CALL(*client, ToggleEasyUnlock(_, _, _)) |
| 73 | .WillByDefault(Invoke( |
| 74 | this, |
| 75 | &CryptAuthSoftwareFeatureManagerImplTest::MockToggleEasyUnlock)); |
| 76 | ON_CALL(*client, FindEligibleUnlockDevices(_, _, _)) |
| 77 | .WillByDefault(Invoke(this, &CryptAuthSoftwareFeatureManagerImplTest:: |
| 78 | MockFindEligibleUnlockDevices)); |
| 79 | } |
| 80 | |
| 81 | // Mock CryptAuthClient::ToggleEasyUnlock() implementation. |
| 82 | void MockToggleEasyUnlock( |
| 83 | const ToggleEasyUnlockRequest& request, |
| 84 | const CryptAuthClient::ToggleEasyUnlockCallback& callback, |
| 85 | const CryptAuthClient::ErrorCallback& error_callback) { |
| 86 | last_toggle_request_ = request; |
| 87 | toggle_easy_unlock_callback_ = callback; |
| 88 | error_callback_ = error_callback; |
| 89 | error_code_ = kErrorSetSoftwareFeatureState; |
| 90 | } |
| 91 | |
| 92 | // Mock CryptAuthClient::FindEligibleUnlockDevices() implementation. |
| 93 | void MockFindEligibleUnlockDevices( |
| 94 | const FindEligibleUnlockDevicesRequest& request, |
| 95 | const CryptAuthClient::FindEligibleUnlockDevicesCallback& callback, |
| 96 | const CryptAuthClient::ErrorCallback& error_callback) { |
| 97 | last_find_request_ = request; |
| 98 | find_eligible_unlock_devices_callback_ = callback; |
| 99 | error_callback_ = error_callback; |
| 100 | error_code_ = kErrorFindEligibleDevices; |
| 101 | } |
| 102 | |
| 103 | FindEligibleUnlockDevicesResponse CreateFindEligibleUnlockDevicesResponse() { |
| 104 | FindEligibleUnlockDevicesResponse find_eligible_unlock_devices_response; |
| 105 | for (const auto& device_info : test_eligible_external_devices_infos_) { |
| 106 | find_eligible_unlock_devices_response.add_eligible_devices()->CopyFrom( |
| 107 | device_info); |
| 108 | } |
| 109 | for (const auto& device_info : test_ineligible_external_devices_infos_) { |
| 110 | find_eligible_unlock_devices_response.add_ineligible_devices() |
| 111 | ->mutable_device() |
| 112 | ->CopyFrom(device_info); |
| 113 | } |
| 114 | return find_eligible_unlock_devices_response; |
| 115 | } |
| 116 | |
| 117 | void VerifyDeviceEligibility() { |
| 118 | // Ensure that resulting devices are not empty. Otherwise, following for |
| 119 | // loop checks will succeed on empty resulting devices. |
| 120 | EXPECT_TRUE(result_eligible_devices_.size() > 0); |
| 121 | EXPECT_TRUE(result_ineligible_devices_.size() > 0); |
| 122 | for (const auto& device_info : result_eligible_devices_) { |
| 123 | EXPECT_TRUE( |
| 124 | std::find_if( |
| 125 | test_eligible_external_devices_infos_.begin(), |
| 126 | test_eligible_external_devices_infos_.end(), |
| 127 | [&device_info](const cryptauth::ExternalDeviceInfo& device) { |
| 128 | return device.public_key() == device_info.public_key(); |
| 129 | }) != test_eligible_external_devices_infos_.end()); |
| 130 | } |
| 131 | for (const auto& ineligible_device : result_ineligible_devices_) { |
| 132 | EXPECT_TRUE( |
| 133 | std::find_if(test_ineligible_external_devices_infos_.begin(), |
| 134 | test_ineligible_external_devices_infos_.end(), |
| 135 | [&ineligible_device]( |
| 136 | const cryptauth::ExternalDeviceInfo& device) { |
| 137 | return device.public_key() == |
| 138 | ineligible_device.device().public_key(); |
| 139 | }) != test_ineligible_external_devices_infos_.end()); |
| 140 | } |
| 141 | result_eligible_devices_.clear(); |
| 142 | result_ineligible_devices_.clear(); |
| 143 | } |
| 144 | |
| 145 | void SetSoftwareFeatureState(SoftwareFeature feature, |
| 146 | const ExternalDeviceInfo& device_info, |
| 147 | bool enabled, |
| 148 | bool is_exclusive = false) { |
| 149 | software_feature_manager_->SetSoftwareFeatureState( |
| 150 | device_info.public_key(), feature, enabled, |
| 151 | base::Bind( |
| 152 | &CryptAuthSoftwareFeatureManagerImplTest::OnSoftwareFeatureStateSet, |
| 153 | base::Unretained(this)), |
| 154 | base::Bind(&CryptAuthSoftwareFeatureManagerImplTest::OnError, |
| 155 | base::Unretained(this)), |
| 156 | is_exclusive); |
| 157 | } |
| 158 | |
| 159 | void FindEligibleDevices(SoftwareFeature feature) { |
| 160 | software_feature_manager_->FindEligibleDevices( |
| 161 | feature, |
| 162 | base::Bind( |
| 163 | &CryptAuthSoftwareFeatureManagerImplTest::OnEligibleDevicesFound, |
| 164 | base::Unretained(this)), |
| 165 | base::Bind(&CryptAuthSoftwareFeatureManagerImplTest::OnError, |
| 166 | base::Unretained(this))); |
| 167 | } |
| 168 | |
| 169 | void OnSoftwareFeatureStateSet() { result_ = kSuccessResult; } |
| 170 | |
| 171 | void OnEligibleDevicesFound( |
| 172 | const std::vector<ExternalDeviceInfo>& eligible_devices, |
| 173 | const std::vector<IneligibleDevice>& ineligible_devices) { |
| 174 | result_ = kSuccessResult; |
| 175 | result_eligible_devices_ = eligible_devices; |
| 176 | result_ineligible_devices_ = ineligible_devices; |
| 177 | } |
| 178 | |
| 179 | void OnError(const std::string& error_message) { result_ = error_message; } |
| 180 | |
| 181 | void InvokeSetSoftwareFeatureCallback() { |
| 182 | CryptAuthClient::ToggleEasyUnlockCallback success_callback = |
| 183 | toggle_easy_unlock_callback_; |
| 184 | ASSERT_TRUE(!success_callback.is_null()); |
| 185 | toggle_easy_unlock_callback_.Reset(); |
| 186 | success_callback.Run(ToggleEasyUnlockResponse()); |
| 187 | } |
| 188 | |
| 189 | void InvokeFindEligibleDevicesCallback( |
| 190 | const FindEligibleUnlockDevicesResponse& retrieved_devices_response) { |
| 191 | CryptAuthClient::FindEligibleUnlockDevicesCallback success_callback = |
| 192 | find_eligible_unlock_devices_callback_; |
| 193 | ASSERT_TRUE(!success_callback.is_null()); |
| 194 | find_eligible_unlock_devices_callback_.Reset(); |
| 195 | success_callback.Run(retrieved_devices_response); |
| 196 | } |
| 197 | |
| 198 | void InvokeErrorCallback() { |
| 199 | CryptAuthClient::ErrorCallback error_callback = error_callback_; |
| 200 | ASSERT_TRUE(!error_callback.is_null()); |
| 201 | error_callback_.Reset(); |
| 202 | error_callback.Run(error_code_); |
| 203 | } |
| 204 | |
| 205 | std::string GetResultAndReset() { |
| 206 | std::string result; |
| 207 | result.swap(result_); |
| 208 | return result; |
| 209 | } |
| 210 | |
| 211 | const std::vector<cryptauth::ExternalDeviceInfo> |
| 212 | all_test_external_device_infos_; |
| 213 | const std::vector<ExternalDeviceInfo> test_eligible_external_devices_infos_; |
| 214 | const std::vector<ExternalDeviceInfo> test_ineligible_external_devices_infos_; |
| 215 | |
| 216 | std::unique_ptr<MockCryptAuthClientFactory> mock_cryptauth_client_factory_; |
| 217 | std::unique_ptr<cryptauth::SoftwareFeatureManager> software_feature_manager_; |
| 218 | |
| 219 | CryptAuthClient::ErrorCallback error_callback_; |
| 220 | |
| 221 | // Set when a CryptAuthClient function returns. If empty, no callback has been |
| 222 | // invoked. |
| 223 | std::string result_; |
| 224 | |
| 225 | // The code passed to the error callback; varies depending on what |
| 226 | // CryptAuthClient function is invoked. |
| 227 | std::string error_code_; |
| 228 | |
| 229 | // For SetSoftwareFeatureState() tests. |
| 230 | ToggleEasyUnlockRequest last_toggle_request_; |
| 231 | CryptAuthClient::ToggleEasyUnlockCallback toggle_easy_unlock_callback_; |
| 232 | |
| 233 | // For FindEligibleDevices() tests. |
| 234 | FindEligibleUnlockDevicesRequest last_find_request_; |
| 235 | CryptAuthClient::FindEligibleUnlockDevicesCallback |
| 236 | find_eligible_unlock_devices_callback_; |
| 237 | std::vector<ExternalDeviceInfo> result_eligible_devices_; |
| 238 | std::vector<IneligibleDevice> result_ineligible_devices_; |
| 239 | |
| 240 | private: |
| 241 | DISALLOW_COPY_AND_ASSIGN(CryptAuthSoftwareFeatureManagerImplTest); |
| 242 | }; |
| 243 | |
| 244 | TEST_F(CryptAuthSoftwareFeatureManagerImplTest, TestOrderUponMultipleRequests) { |
| 245 | SetSoftwareFeatureState(SoftwareFeature::BETTER_TOGETHER_HOST, |
| 246 | test_eligible_external_devices_infos_[0], |
| 247 | true /* enable */); |
| 248 | FindEligibleDevices(SoftwareFeature::BETTER_TOGETHER_HOST); |
| 249 | SetSoftwareFeatureState(SoftwareFeature::BETTER_TOGETHER_CLIENT, |
| 250 | test_eligible_external_devices_infos_[1], |
| 251 | false /* enable */); |
| 252 | FindEligibleDevices(SoftwareFeature::BETTER_TOGETHER_CLIENT); |
| 253 | |
| 254 | EXPECT_EQ(SoftwareFeature::BETTER_TOGETHER_HOST, |
| 255 | last_toggle_request_.feature()); |
| 256 | EXPECT_EQ(true, last_toggle_request_.enable()); |
| 257 | EXPECT_EQ(false, last_toggle_request_.is_exclusive()); |
| 258 | InvokeSetSoftwareFeatureCallback(); |
| 259 | EXPECT_EQ(kSuccessResult, GetResultAndReset()); |
| 260 | |
| 261 | EXPECT_EQ(SoftwareFeature::BETTER_TOGETHER_HOST, |
| 262 | last_find_request_.feature()); |
| 263 | InvokeFindEligibleDevicesCallback(CreateFindEligibleUnlockDevicesResponse()); |
| 264 | EXPECT_EQ(kSuccessResult, GetResultAndReset()); |
| 265 | VerifyDeviceEligibility(); |
| 266 | |
| 267 | EXPECT_EQ(SoftwareFeature::BETTER_TOGETHER_CLIENT, |
| 268 | last_toggle_request_.feature()); |
| 269 | EXPECT_EQ(false, last_toggle_request_.enable()); |
| 270 | EXPECT_EQ(false, last_toggle_request_.is_exclusive()); |
| 271 | InvokeSetSoftwareFeatureCallback(); |
| 272 | EXPECT_EQ(kSuccessResult, GetResultAndReset()); |
| 273 | |
| 274 | EXPECT_EQ(SoftwareFeature::BETTER_TOGETHER_CLIENT, |
| 275 | last_find_request_.feature()); |
| 276 | InvokeFindEligibleDevicesCallback(CreateFindEligibleUnlockDevicesResponse()); |
| 277 | EXPECT_EQ(kSuccessResult, GetResultAndReset()); |
| 278 | VerifyDeviceEligibility(); |
| 279 | } |
| 280 | |
| 281 | TEST_F(CryptAuthSoftwareFeatureManagerImplTest, |
| 282 | TestMultipleSetUnlocksRequests) { |
| 283 | SetSoftwareFeatureState(SoftwareFeature::BETTER_TOGETHER_HOST, |
| 284 | test_eligible_external_devices_infos_[0], |
| 285 | true /* enable */); |
| 286 | SetSoftwareFeatureState(SoftwareFeature::BETTER_TOGETHER_CLIENT, |
| 287 | test_eligible_external_devices_infos_[1], |
| 288 | false /* enable */); |
| 289 | SetSoftwareFeatureState(SoftwareFeature::BETTER_TOGETHER_HOST, |
| 290 | test_eligible_external_devices_infos_[2], |
| 291 | true /* enable */); |
| 292 | |
| 293 | EXPECT_EQ(SoftwareFeature::BETTER_TOGETHER_HOST, |
| 294 | last_toggle_request_.feature()); |
| 295 | EXPECT_EQ(true, last_toggle_request_.enable()); |
| 296 | EXPECT_EQ(false, last_toggle_request_.is_exclusive()); |
| 297 | InvokeErrorCallback(); |
| 298 | EXPECT_EQ(kErrorSetSoftwareFeatureState, GetResultAndReset()); |
| 299 | |
| 300 | EXPECT_EQ(SoftwareFeature::BETTER_TOGETHER_CLIENT, |
| 301 | last_toggle_request_.feature()); |
| 302 | EXPECT_EQ(false, last_toggle_request_.enable()); |
| 303 | EXPECT_EQ(false, last_toggle_request_.is_exclusive()); |
| 304 | InvokeSetSoftwareFeatureCallback(); |
| 305 | EXPECT_EQ(kSuccessResult, GetResultAndReset()); |
| 306 | |
| 307 | EXPECT_EQ(SoftwareFeature::BETTER_TOGETHER_HOST, |
| 308 | last_toggle_request_.feature()); |
| 309 | EXPECT_EQ(true, last_toggle_request_.enable()); |
| 310 | EXPECT_EQ(false, last_toggle_request_.is_exclusive()); |
| 311 | InvokeSetSoftwareFeatureCallback(); |
| 312 | EXPECT_EQ(kSuccessResult, GetResultAndReset()); |
| 313 | } |
| 314 | |
| 315 | TEST_F(CryptAuthSoftwareFeatureManagerImplTest, |
| 316 | TestMultipleFindEligibleForUnlockDevicesRequests) { |
| 317 | FindEligibleDevices(SoftwareFeature::BETTER_TOGETHER_HOST); |
| 318 | FindEligibleDevices(SoftwareFeature::BETTER_TOGETHER_CLIENT); |
| 319 | FindEligibleDevices(SoftwareFeature::BETTER_TOGETHER_HOST); |
| 320 | |
| 321 | EXPECT_EQ(SoftwareFeature::BETTER_TOGETHER_HOST, |
| 322 | last_find_request_.feature()); |
| 323 | InvokeFindEligibleDevicesCallback(CreateFindEligibleUnlockDevicesResponse()); |
| 324 | EXPECT_EQ(kSuccessResult, GetResultAndReset()); |
| 325 | VerifyDeviceEligibility(); |
| 326 | |
| 327 | EXPECT_EQ(SoftwareFeature::BETTER_TOGETHER_CLIENT, |
| 328 | last_find_request_.feature()); |
| 329 | InvokeErrorCallback(); |
| 330 | EXPECT_EQ(kErrorFindEligibleDevices, GetResultAndReset()); |
| 331 | |
| 332 | EXPECT_EQ(SoftwareFeature::BETTER_TOGETHER_HOST, |
| 333 | last_find_request_.feature()); |
| 334 | InvokeFindEligibleDevicesCallback(CreateFindEligibleUnlockDevicesResponse()); |
| 335 | EXPECT_EQ(kSuccessResult, GetResultAndReset()); |
| 336 | VerifyDeviceEligibility(); |
| 337 | } |
| 338 | |
| 339 | TEST_F(CryptAuthSoftwareFeatureManagerImplTest, TestOrderViaMultipleErrors) { |
| 340 | SetSoftwareFeatureState(SoftwareFeature::BETTER_TOGETHER_HOST, |
| 341 | test_eligible_external_devices_infos_[0], |
| 342 | true /* enable */); |
| 343 | FindEligibleDevices(SoftwareFeature::BETTER_TOGETHER_HOST); |
| 344 | |
| 345 | EXPECT_EQ(SoftwareFeature::BETTER_TOGETHER_HOST, |
| 346 | last_toggle_request_.feature()); |
| 347 | InvokeErrorCallback(); |
| 348 | EXPECT_EQ(kErrorSetSoftwareFeatureState, GetResultAndReset()); |
| 349 | |
| 350 | EXPECT_EQ(SoftwareFeature::BETTER_TOGETHER_HOST, |
| 351 | last_find_request_.feature()); |
| 352 | InvokeErrorCallback(); |
| 353 | EXPECT_EQ(kErrorFindEligibleDevices, GetResultAndReset()); |
| 354 | } |
| 355 | |
| 356 | TEST_F(CryptAuthSoftwareFeatureManagerImplTest, TestIsExclusive) { |
| 357 | SetSoftwareFeatureState(SoftwareFeature::BETTER_TOGETHER_HOST, |
| 358 | test_eligible_external_devices_infos_[0], |
| 359 | true /* enable */, true /* is_exclusive */); |
| 360 | |
| 361 | EXPECT_EQ(SoftwareFeature::BETTER_TOGETHER_HOST, |
| 362 | last_toggle_request_.feature()); |
| 363 | EXPECT_EQ(true, last_toggle_request_.enable()); |
| 364 | EXPECT_EQ(true, last_toggle_request_.is_exclusive()); |
| 365 | InvokeErrorCallback(); |
| 366 | EXPECT_EQ(kErrorSetSoftwareFeatureState, GetResultAndReset()); |
| 367 | } |
| 368 | |
| 369 | TEST_F(CryptAuthSoftwareFeatureManagerImplTest, TestEasyUnlockSpecialCase) { |
| 370 | SetSoftwareFeatureState(SoftwareFeature::EASY_UNLOCK_HOST, |
| 371 | test_eligible_external_devices_infos_[0], |
| 372 | false /* enable */); |
| 373 | |
| 374 | EXPECT_EQ(SoftwareFeature::EASY_UNLOCK_HOST, last_toggle_request_.feature()); |
| 375 | EXPECT_EQ(false, last_toggle_request_.enable()); |
| 376 | // apply_to_all() should be false when disabling EasyUnlock host capabilities. |
| 377 | EXPECT_EQ(true, last_toggle_request_.apply_to_all()); |
| 378 | InvokeErrorCallback(); |
| 379 | EXPECT_EQ(kErrorSetSoftwareFeatureState, GetResultAndReset()); |
| 380 | } |
| 381 | |
| 382 | } // namespace cryptauth |