chromium/chrome/browser/ash/policy/rsu/lookup_key_uploader_unittest.cc

// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/ash/policy/rsu/lookup_key_uploader.h"

#include <memory>
#include <string>

#include "base/run_loop.h"
#include "base/strings/string_number_conversions.h"
#include "base/test/gmock_callback_support.h"
#include "base/test/simple_test_clock.h"
#include "base/time/time.h"
#include "chrome/browser/ash/attestation/mock_enrollment_certificate_uploader.h"
#include "chrome/browser/ash/settings/device_settings_test_helper.h"
#include "chrome/common/pref_names.h"
#include "chromeos/ash/components/dbus/userdataauth/fake_cryptohome_misc_client.h"
#include "components/policy/core/common/cloud/mock_cloud_policy_store.h"
#include "components/policy/proto/device_management_backend.pb.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/testing_pref_service.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"

using CertificateStatus =
    ash::attestation::EnrollmentCertificateUploader::Status;
using ash::attestation::MockEnrollmentCertificateUploader;
using testing::_;
using testing::Invoke;

namespace policy {

namespace {
const char kValidRsuDeviceId[] = "123";
const char kValidRsuDeviceIdEncoded[] =
    "MTIz";  // base::Base64Encode(kValidRsuDeviceId, kValidRsuDeviceencoded)
}
class LookupKeyUploaderTest : public ash::DeviceSettingsTestBase {
 public:
  LookupKeyUploaderTest(const LookupKeyUploaderTest&) = delete;
  LookupKeyUploaderTest& operator=(const LookupKeyUploaderTest&) = delete;

 protected:
  LookupKeyUploaderTest() = default;

  void SetUp() override {
    ash::DeviceSettingsTestBase::SetUp();
    pref_service_.registry()->RegisterStringPref(
        prefs::kLastRsuDeviceIdUploaded, std::string());
    lookup_key_uploader_ = std::make_unique<LookupKeyUploader>(
        nullptr, &pref_service_, &certificate_uploader_);
    lookup_key_uploader_->SetClock(&clock_);
    // We initialize clock to imitate real time.
    clock_.Advance(base::Days(50));
  }

  void TearDown() override { ash::DeviceSettingsTestBase::TearDown(); }

  void ExpectSavedIdToBe(const std::string& key) {
    EXPECT_EQ(pref_service_.GetString(prefs::kLastRsuDeviceIdUploaded), key);
  }
  bool NeedsUpload() { return lookup_key_uploader_->needs_upload_; }

  void SetCryptohomeReplyTo(const std::string& rsu_device_id) {
    ash::FakeCryptohomeMiscClient::Get()->set_rsu_device_id(rsu_device_id);
  }

  void AdvanceTime() { clock_.Advance(lookup_key_uploader_->kRetryFrequency); }
  void Start() {
    lookup_key_uploader_->OnStoreLoaded(&policy_store_);
    base::RunLoop().RunUntilIdle();
  }

  TestingPrefServiceSimple pref_service_;
  base::SimpleTestClock clock_;
  MockEnrollmentCertificateUploader certificate_uploader_;
  std::unique_ptr<LookupKeyUploader> lookup_key_uploader_;
  MockCloudPolicyStore policy_store_;
};

TEST_F(LookupKeyUploaderTest, Uploads) {
  EXPECT_CALL(certificate_uploader_, ObtainAndUploadCertificate(_))
      .WillOnce(Invoke(
          [](base::OnceCallback<void(CertificateStatus status)> callback) {
            std::move(callback).Run(CertificateStatus::kSuccess);
          }));
  SetCryptohomeReplyTo(kValidRsuDeviceId);
  Start();
  ExpectSavedIdToBe(kValidRsuDeviceIdEncoded);
}

TEST_F(LookupKeyUploaderTest, ReuploadsOnFail) {
  SetCryptohomeReplyTo("");
  Start();
  EXPECT_CALL(certificate_uploader_, ObtainAndUploadCertificate(_)).Times(0);
  EXPECT_TRUE(NeedsUpload());
}

TEST_F(LookupKeyUploaderTest, DoesntUploadTwice) {
  pref_service_.SetString(prefs::kLastRsuDeviceIdUploaded,
                          kValidRsuDeviceIdEncoded);
  SetCryptohomeReplyTo(kValidRsuDeviceId);
  Start();
  EXPECT_CALL(certificate_uploader_, ObtainAndUploadCertificate(_)).Times(0);
  ExpectSavedIdToBe(kValidRsuDeviceIdEncoded);
  EXPECT_FALSE(NeedsUpload());
}

TEST_F(LookupKeyUploaderTest, DoesNotUploadVeryFrequently) {
  SetCryptohomeReplyTo("");
  Start();
  EXPECT_TRUE(NeedsUpload());  // Will ask for restart.

  // Next upload should not be executed -- because of the frequency limit.
  SetCryptohomeReplyTo(kValidRsuDeviceId);
  Start();
  ExpectSavedIdToBe("");
  EXPECT_TRUE(NeedsUpload());  // Will ask for restart.

  AdvanceTime();

  EXPECT_CALL(certificate_uploader_, ObtainAndUploadCertificate(_))
      .WillOnce(Invoke(
          [](base::OnceCallback<void(CertificateStatus status)> callback) {
            std::move(callback).Run(CertificateStatus::kSuccess);
          }));
  Start();
  ExpectSavedIdToBe(kValidRsuDeviceIdEncoded);
  EXPECT_FALSE(NeedsUpload());
}

TEST_F(LookupKeyUploaderTest, UploadsEvenWhenSubmittedBeforeIfForcedByPolicy) {
  EXPECT_CALL(certificate_uploader_, ObtainAndUploadCertificate(_))
      .Times(2)
      .WillRepeatedly(Invoke(
          [](base::OnceCallback<void(CertificateStatus status)> callback) {
            std::move(callback).Run(CertificateStatus::kSuccess);
          }));
  SetCryptohomeReplyTo(kValidRsuDeviceId);
  Start();
  ExpectSavedIdToBe(kValidRsuDeviceIdEncoded);
  EXPECT_FALSE(NeedsUpload());

  // We set the policy for obtaining RSU lookup key.
  auto policy_data = std::make_unique<enterprise_management::PolicyData>();
  policy_data->mutable_client_action_required()
      ->set_enrollment_certificate_needed(true);
  policy_store_.set_policy_data_for_testing(std::move(policy_data));

  // We expect the ObtainAndUploadCertificate to called twice.
  AdvanceTime();
  Start();
}

}  // namespace policy