| // Copyright 2024 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/enterprise_companion/dm_client.h" |
| |
| #include <stdint.h> |
| |
| #include <algorithm> |
| #include <memory> |
| #include <ostream> |
| #include <queue> |
| #include <string> |
| #include <utility> |
| |
| #include "base/check.h" |
| #include "base/files/file_path.h" |
| #include "base/files/file_util.h" |
| #include "base/functional/bind.h" |
| #include "base/functional/callback.h" |
| #include "base/functional/callback_helpers.h" |
| #include "base/logging.h" |
| #include "base/memory/scoped_refptr.h" |
| #include "base/memory/weak_ptr.h" |
| #include "base/sequence_checker.h" |
| #include "base/strings/strcat.h" |
| #include "base/strings/string_util.h" |
| #include "base/system/sys_info.h" |
| #include "base/task/sequenced_task_runner.h" |
| #include "base/time/time.h" |
| #include "base/timer/timer.h" |
| #include "chrome/enterprise_companion/constants.h" |
| #include "chrome/enterprise_companion/device_management_storage/dm_storage.h" |
| #include "chrome/enterprise_companion/enterprise_companion_branding.h" |
| #include "chrome/enterprise_companion/enterprise_companion_status.h" |
| #include "chrome/enterprise_companion/enterprise_companion_version.h" |
| #include "chrome/enterprise_companion/event_logger.h" |
| #include "chrome/enterprise_companion/global_constants.h" |
| #include "chrome/enterprise_companion/installer_paths.h" |
| #include "chrome/enterprise_companion/proto/enterprise_companion_event.pb.h" |
| #include "components/policy/core/common/cloud/client_data_delegate.h" |
| #include "components/policy/core/common/cloud/cloud_policy_client.h" |
| #include "components/policy/core/common/cloud/cloud_policy_constants.h" |
| #include "components/policy/core/common/cloud/cloud_policy_util.h" |
| #include "components/policy/core/common/cloud/cloud_policy_validator.h" |
| #include "components/policy/core/common/cloud/device_management_service.h" |
| #include "components/policy/core/common/cloud/policy_value_validator.h" |
| #include "components/policy/core/common/policy_types.h" |
| #include "components/policy/proto/device_management_backend.pb.h" |
| #include "net/base/net_errors.h" |
| #include "net/http/http_util.h" |
| #include "services/network/public/cpp/shared_url_loader_factory.h" |
| #include "third_party/abseil-cpp/absl/strings/str_format.h" |
| |
| namespace enterprise_companion { |
| |
| namespace { |
| |
| class FailedEnrollmentTokenServiceImpl : public FailedEnrollmentTokenService { |
| public: |
| FailedEnrollmentTokenServiceImpl() |
| : failed_enrollment_token_path_(GetFailedEnrollmentTokenPath()) {} |
| ~FailedEnrollmentTokenServiceImpl() override = default; |
| |
| bool StoreFailedEnrollmentToken(const std::string& token) override { |
| return !failed_enrollment_token_path_.empty() && |
| device_management_storage::WriteContentToGlobalReadableFile( |
| failed_enrollment_token_path_, token); |
| } |
| |
| bool DeleteFailedEnrollmentToken() override { |
| return !failed_enrollment_token_path_.empty() && |
| base::DeleteFile(failed_enrollment_token_path_); |
| } |
| |
| std::string GetFailedEnrollmentToken() const override { |
| std::string token; |
| if (failed_enrollment_token_path_.empty() || |
| !base::ReadFileToString(failed_enrollment_token_path_, &token)) { |
| return {}; |
| } |
| return std::string(base::TrimWhitespaceASCII(token, base::TRIM_ALL)); |
| } |
| |
| private: |
| static base::FilePath GetFailedEnrollmentTokenPath() { |
| std::optional<base::FilePath> install_dir = GetInstallDirectory(); |
| return install_dir ? install_dir->Append(FILE_PATH_LITERAL( |
| "CloudManagementFailedEnrollmentToken")) |
| : base::FilePath(); |
| } |
| |
| const base::FilePath failed_enrollment_token_path_; |
| }; |
| |
| std::ostream& operator<<(std::ostream& os, |
| const policy::CloudPolicyClient::Result& result) { |
| if (result.IsSuccess()) { |
| os << "Success"; |
| } else if (result.IsClientNotRegisteredError()) { |
| os << "Client not registered"; |
| } else if (result.IsDMServerError()) { |
| os << EnterpriseCompanionStatus::FromDeviceManagementStatus( |
| result.GetDMServerError()) |
| .description(); |
| } else if (result.GetNetError() != net::OK) { |
| os << "Network error: " << net::ErrorToString(result.GetNetError()); |
| } |
| return os; |
| } |
| |
| // Convert a CloudPolicyClient::ResponseMap to a DMPolicyMap by dropping the |
| // "settings entity ID" from the key and serializing the fetch response. |
| device_management_storage::DMPolicyMap ToDMPolicyMap( |
| const policy::CloudPolicyClient::ResponseMap& in) { |
| device_management_storage::DMPolicyMap out; |
| std::ranges::transform( |
| in, std::inserter(out, out.end()), |
| [](const std::pair<policy::PolicyTypeToFetch, |
| enterprise_management::PolicyFetchResponse>& |
| response) { |
| return std::make_pair(response.first.policy_type(), |
| response.second.SerializeAsString()); |
| }); |
| return out; |
| } |
| |
| class DMConfiguration : public policy::DeviceManagementService::Configuration { |
| public: |
| std::string GetDMServerUrl() const override { |
| return GetGlobalConstants()->DeviceManagementServerURL().spec(); |
| } |
| std::string GetAgentParameter() const override { |
| return base::StrCat( |
| {PRODUCT_FULLNAME_STRING, " ", kEnterpriseCompanionVersion}); |
| } |
| std::string GetPlatformParameter() const override { |
| int32_t major = 0; |
| int32_t minor = 0; |
| int32_t bugfix = 0; |
| base::SysInfo::OperatingSystemVersionNumbers(&major, &minor, &bugfix); |
| return absl::StrFormat( |
| "%s|%s|%d.%d.%d", base::SysInfo::OperatingSystemName(), |
| base::SysInfo::OperatingSystemArchitecture(), major, minor, bugfix); |
| } |
| std::string GetRealtimeReportingServerUrl() const override { |
| return GetGlobalConstants()->DeviceManagementRealtimeReportingURL().spec(); |
| } |
| std::string GetEncryptedReportingServerUrl() const override { |
| return GetGlobalConstants()->DeviceManagementEncryptedReportingURL().spec(); |
| } |
| }; |
| |
| class ClientDataDelegate : public policy::ClientDataDelegate { |
| public: |
| void FillRegisterBrowserRequest( |
| enterprise_management::RegisterBrowserRequest* request, |
| base::OnceClosure callback) const override { |
| request->set_os_platform(policy::GetOSPlatform()); |
| request->set_os_version(policy::GetOSVersion()); |
| request->set_allocated_browser_device_identifier( |
| policy::GetBrowserDeviceIdentifier().release()); |
| std::move(callback).Run(); |
| } |
| }; |
| |
| class FetchedPolicyValidator final : public policy::CloudPolicyValidatorBase { |
| public: |
| explicit FetchedPolicyValidator( |
| std::unique_ptr<enterprise_management::PolicyFetchResponse> |
| policy_response) |
| : policy::CloudPolicyValidatorBase(std::move(policy_response), |
| /*background_task_runner=*/nullptr) {} |
| FetchedPolicyValidator(const FetchedPolicyValidator&) = delete; |
| FetchedPolicyValidator& operator=(const FetchedPolicyValidator&) = delete; |
| |
| private: |
| // Overrides for CloudPolicyValidatorBase. |
| Status CheckPayload() override { |
| // The payload is valid so long as at least one policy is present. |
| return (policy_data() && policy_data()->has_policy_value()) |
| ? VALIDATION_OK |
| : VALIDATION_POLICY_PARSE_ERROR; |
| } |
| |
| Status CheckValues() override { |
| // The enterprise companion is agnostic to the type of payload. Hence, the |
| // values are not verified. |
| return VALIDATION_OK; |
| } |
| }; |
| |
| // `BufferedDMClient` sequences calls to another DMClient ensuring that no |
| // operations overlap. This is useful as the CloudPolicyClient backing |
| // DMClientImpl does not provide any means to discriminate the origin of |
| // overlapping calls nor does it make any guarantees of the behavior of such |
| // calls. |
| class BufferedDMClient : public DMClient { |
| public: |
| explicit BufferedDMClient(std::unique_ptr<DMClient> client) |
| : client_(std::move(client)) {} |
| |
| ~BufferedDMClient() override { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| VLOG_IF(1, !tasks_.empty()) |
| << "BufferedDMClient destroyed while " << tasks_.size() |
| << " tasks were pending. Callbacks will be dropped."; |
| VLOG_IF(1, task_running_) << "BufferedDMClient destroyed while a task was " |
| "in-flight; its callback will be dropped."; |
| } |
| |
| void RegisterPolicyAgent(scoped_refptr<EnterpriseCompanionEventLogger> logger, |
| StatusCallback callback) override { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| Enqueue(base::BindOnce(&BufferedDMClient::DoRegisterPolicyAgent, |
| weak_ptr_factory_.GetWeakPtr(), logger, |
| std::move(callback))); |
| } |
| |
| void FetchPolicies(policy::PolicyFetchReason reason, |
| scoped_refptr<EnterpriseCompanionEventLogger> logger, |
| StatusCallback callback) override { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| Enqueue(base::BindOnce(&BufferedDMClient::DoFetchPolicies, |
| weak_ptr_factory_.GetWeakPtr(), reason, logger, |
| std::move(callback))); |
| } |
| |
| private: |
| void DoRegisterPolicyAgent( |
| scoped_refptr<EnterpriseCompanionEventLogger> logger, |
| StatusCallback callback) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| client_->RegisterPolicyAgent( |
| logger, base::BindOnce(&BufferedDMClient::OnTaskComplete, |
| base::Unretained(this), std::move(callback))); |
| } |
| |
| void DoFetchPolicies(policy::PolicyFetchReason reason, |
| scoped_refptr<EnterpriseCompanionEventLogger> logger, |
| StatusCallback callback) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| client_->FetchPolicies( |
| reason, logger, |
| base::BindOnce(&BufferedDMClient::OnTaskComplete, |
| base::Unretained(this), std::move(callback))); |
| } |
| |
| void OnTaskComplete(StatusCallback callback, |
| const EnterpriseCompanionStatus& status) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| base::SequencedTaskRunner::GetCurrentDefault()->PostTask( |
| FROM_HERE, base::BindOnce(std::move(callback), status)); |
| task_running_ = false; |
| ScheduleNextTask(); |
| } |
| |
| // Adds a task to the queue. Tasks should reference the BufferedDMClient by |
| // weak_ptr as they are posted. |
| void Enqueue(base::OnceClosure task) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| tasks_.push(std::move(task)); |
| ScheduleNextTask(); |
| } |
| |
| void ScheduleNextTask() { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| if (task_running_ || tasks_.empty()) { |
| return; |
| } |
| task_running_ = true; |
| base::SequencedTaskRunner::GetCurrentDefault()->PostTask( |
| FROM_HERE, std::move(tasks_.front())); |
| tasks_.pop(); |
| } |
| |
| SEQUENCE_CHECKER(sequence_checker_); |
| std::unique_ptr<DMClient> client_; |
| std::queue<base::OnceClosure> tasks_; |
| bool task_running_ = false; |
| base::WeakPtrFactory<BufferedDMClient> weak_ptr_factory_{this}; |
| }; |
| |
| // Interface to a CloudPolicyClient which interacts with the device management |
| // server. May perform blocking IO. |
| class DMClientImpl : public DMClient, policy::CloudPolicyClient::Observer { |
| public: |
| explicit DMClientImpl( |
| std::unique_ptr<policy::DeviceManagementService::Configuration> config, |
| CloudPolicyClientProvider cloud_policy_client_provider, |
| scoped_refptr<device_management_storage::DMStorage> dm_storage, |
| PolicyFetchResponseValidator policy_fetch_response_validator, |
| base::TimeDelta task_timeout, |
| std::unique_ptr<FailedEnrollmentTokenService> |
| failed_enrollment_token_service) |
| : task_timeout_(task_timeout), |
| dm_service_(std::move(config)), |
| cloud_policy_client_( |
| std::move(cloud_policy_client_provider).Run(&dm_service_)), |
| dm_storage_(dm_storage), |
| policy_fetch_response_validator_(policy_fetch_response_validator), |
| failed_enrollment_token_service_( |
| std::move(failed_enrollment_token_service)) { |
| dm_service_.ScheduleInitialization(0); |
| cloud_policy_client_->AddObserver(this); |
| cloud_policy_client_->AddPolicyTypeToFetch( |
| policy::dm_protocol::kGoogleUpdateMachineLevelAppsPolicyType, |
| /*settings_entity_id=*/""); |
| if (!dm_storage->GetDmToken().empty()) { |
| if (net::HttpUtil::IsValidHeaderValue(dm_storage->GetDmToken())) { |
| cloud_policy_client_->SetupRegistration(dm_storage_->GetDmToken(), |
| dm_storage_->GetDeviceID(), |
| /*user_affiliation_ids=*/{}); |
| } else { |
| VLOG(1) << "The stored DM token is malformed. The device will be " |
| "considered not registered."; |
| } |
| } |
| UpdateCachedPolicyInfo(); |
| } |
| |
| ~DMClientImpl() override { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| cloud_policy_client_->RemoveObserver(this); |
| VLOG_IF(1, pending_callback_) |
| << "DMClient destroyed while task in-flight. Callback will be dropped"; |
| } |
| |
| // Overrides for DMClient. |
| void RegisterPolicyAgent( |
| scoped_refptr<EnterpriseCompanionEventLogger> event_logger, |
| StatusCallback callback) override { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| CHECK(!pending_callback_) << "DMClientImpl calls may not overlap"; |
| |
| if (ShouldSkipRegistration()) { |
| std::move(callback).Run(EnterpriseCompanionStatus::Success()); |
| return; |
| } |
| |
| if (IsEnrollmentBlocked()) { |
| std::move(callback).Run( |
| EnterpriseCompanionStatus(ApplicationError::kEnrollmentBlocked)); |
| return; |
| } |
| |
| // Wrap the callback with event logging to ensure that precondition errors |
| // are logged. |
| callback = base::BindPostTaskToCurrentDefault(base::BindOnce( |
| &EnterpriseCompanionEventLogger::LogRegisterPolicyAgentEvent, |
| event_logger, base::Time::Now(), std::move(callback))); |
| |
| if (!net::HttpUtil::IsValidHeaderValue(dm_storage_->GetEnrollmentToken())) { |
| VLOG(1) << "The stored enrollment token is malformed."; |
| std::move(callback).Run( |
| EnterpriseCompanionStatus(ApplicationError::kInvalidEnrollmentToken)); |
| return; |
| } |
| |
| dm_storage_->RemoveAllPolicies(); |
| SetPendingCallback(std::move(callback)); |
| cloud_policy_client_->RegisterPolicyAgentWithEnrollmentToken( |
| dm_storage_->GetEnrollmentToken(), dm_storage_->GetDeviceID(), |
| client_data_delegate_); |
| } |
| |
| void FetchPolicies(policy::PolicyFetchReason reason, |
| scoped_refptr<EnterpriseCompanionEventLogger> event_logger, |
| StatusCallback callback) override { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| CHECK(!pending_callback_) << "DMClientImpl calls may not overlap"; |
| // Wrap the callback with event logging early to ensure that precondition |
| // errors are logged. |
| callback = base::BindPostTaskToCurrentDefault( |
| base::BindOnce(&EnterpriseCompanionEventLogger::LogPolicyFetchEvent, |
| event_logger, base::Time::Now(), std::move(callback))); |
| |
| if (!cloud_policy_client_->is_registered()) { |
| VLOG(1) << "Failed to fetch policies: client is not registered"; |
| std::move(callback).Run(EnterpriseCompanionStatus( |
| ApplicationError::kRegistrationPreconditionFailed)); |
| return; |
| } |
| |
| if (!dm_storage_->CanPersistPolicies()) { |
| VLOG(1) << "Failed to fetch policies: policies cannot be persisted."; |
| std::move(callback).Run(EnterpriseCompanionStatus( |
| ApplicationError::kPolicyPersistenceImpossible)); |
| return; |
| } |
| |
| SetPendingCallback(std::move(callback)); |
| UpdateCachedPolicyInfo(); |
| cloud_policy_client_->FetchPolicy(reason); |
| } |
| |
| // Overrides for policy::CloudPolicyClient::Observer. |
| void OnPolicyFetched(policy::CloudPolicyClient*) override { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| VLOG(1) << __func__; |
| |
| StatusCallback callback = |
| pending_callback_ ? std::move(pending_callback_) : base::DoNothing(); |
| |
| if (cloud_policy_client_->last_dm_status() != |
| policy::DeviceManagementStatus::DM_STATUS_SUCCESS) { |
| std::move(callback).Run( |
| EnterpriseCompanionStatus::FromDeviceManagementStatus( |
| cloud_policy_client_->last_dm_status())); |
| return; |
| } |
| |
| policy::CloudPolicyClient::ResponseMap responses = |
| cloud_policy_client_->last_policy_fetch_responses(); |
| FetchedPolicyValidator::ValidationResult validation_result = |
| ValidatePolicyFetchResponses(responses); |
| if (validation_result.status != FetchedPolicyValidator::VALIDATION_OK) { |
| VLOG(1) << "Clearing policy cache due to fetched policy validation error"; |
| dm_storage_->RemoveAllPolicies(); |
| cloud_policy_client_->UploadPolicyValidationReport( |
| validation_result.status, validation_result.value_validation_issues, |
| policy::ValidationAction::kStore, |
| policy::dm_protocol::kGoogleUpdateMachineLevelAppsPolicyType, |
| validation_result.policy_token, |
| base::BindOnce( |
| [](policy::CloudPolicyClient::Result upload_report_result) { |
| VLOG_IF(1, !upload_report_result.IsSuccess()) |
| << "Failed to upload policy validation report: " |
| << upload_report_result; |
| }) |
| .Then(base::BindOnce( |
| std::move(callback), |
| EnterpriseCompanionStatus::FromCloudPolicyValidationResult( |
| validation_result.status)))); |
| return; |
| } |
| |
| if (!dm_storage_->PersistPolicies(ToDMPolicyMap(std::move(responses)))) { |
| std::move(callback).Run(EnterpriseCompanionStatus( |
| ApplicationError::kPolicyPersistenceFailed)); |
| return; |
| } |
| |
| std::move(callback).Run(EnterpriseCompanionStatus::Success()); |
| } |
| |
| void OnRegistrationStateChanged(policy::CloudPolicyClient*) override { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| VLOG(1) << __func__; |
| if (cloud_policy_client_->is_registered()) { |
| dm_storage_->StoreDmToken(cloud_policy_client_->dm_token()); |
| const bool failed_enrollment_token_deleted = |
| failed_enrollment_token_service_->DeleteFailedEnrollmentToken(); |
| LOG_IF(ERROR, !failed_enrollment_token_deleted) |
| << "Failed to delete the rejected enrollment token."; |
| } |
| if (pending_callback_) { |
| std::move(pending_callback_) |
| .Run(EnterpriseCompanionStatus::FromDeviceManagementStatus( |
| cloud_policy_client_->last_dm_status())); |
| } |
| } |
| |
| void OnClientError(policy::CloudPolicyClient*) override { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| VLOG(1) << __func__; |
| if (cloud_policy_client_->last_dm_status() == |
| policy::DM_STATUS_SERVICE_DEVICE_NEEDS_RESET) { |
| VLOG(1) << "DMServer requests deregister via DMToken deletion."; |
| LOG_IF(ERROR, !dm_storage_->DeleteDMToken()) |
| << "Could not deregister: Failed to delete the DMToken."; |
| } else if (cloud_policy_client_->last_dm_status() == |
| policy::DM_STATUS_SERVICE_DEVICE_NOT_FOUND) { |
| VLOG(1) << "DMServer requests deregister via DMToken invalidation."; |
| LOG_IF(ERROR, !dm_storage_->InvalidateDMToken()) |
| << "Could not deregister: Failed to invalidate the DMToken."; |
| } else if (!dm_storage_->IsValidDMToken() && |
| cloud_policy_client_->last_dm_status() == |
| policy::DM_STATUS_SERVICE_MANAGEMENT_TOKEN_INVALID) { |
| VLOG(1) << "DMServer rejected the enrollment token as invalid. " |
| "Blocking future registration attempts that reuse this token."; |
| std::string failed_token = dm_storage_->GetEnrollmentToken(); |
| if (!failed_token.empty()) { |
| const bool failed_enrollment_token_stored = |
| failed_enrollment_token_service_->StoreFailedEnrollmentToken( |
| failed_token); |
| LOG_IF(ERROR, !failed_enrollment_token_stored) |
| << "Failed to store the rejected enrollment token."; |
| } |
| } |
| if (pending_callback_) { |
| std::move(pending_callback_) |
| .Run(EnterpriseCompanionStatus::FromDeviceManagementStatus( |
| cloud_policy_client_->last_dm_status())); |
| } |
| } |
| |
| private: |
| bool ShouldSkipRegistration() { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| if (dm_storage_->GetEnrollmentToken().empty()) { |
| VLOG(1) << "Registration skipped: device not managed."; |
| return true; |
| } else if (cloud_policy_client_->is_registered()) { |
| VLOG(1) << "Registration skipped: device already registered."; |
| return true; |
| } |
| return false; |
| } |
| |
| bool IsEnrollmentBlocked() const { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| std::string failed_token = |
| failed_enrollment_token_service_->GetFailedEnrollmentToken(); |
| if (failed_token.empty()) { |
| return false; |
| } |
| std::string current_token = dm_storage_->GetEnrollmentToken(); |
| if (current_token.empty() || current_token != failed_token) { |
| return false; |
| } |
| VLOG(1) << "Registration attempt blocked. The active enrollment token has " |
| "previously failed verification."; |
| return true; |
| } |
| |
| // Validates all of the fetched policies. |
| FetchedPolicyValidator::ValidationResult ValidatePolicyFetchResponses( |
| const policy::CloudPolicyClient::ResponseMap& responses) { |
| for (const auto& [key, response] : responses) { |
| std::unique_ptr<FetchedPolicyValidator::ValidationResult> |
| validation_result = policy_fetch_response_validator_.Run( |
| dm_storage_->GetDmToken(), dm_storage_->GetDeviceID(), |
| cached_policy_info_->public_key(), |
| cached_policy_info_->timestamp(), response); |
| CHECK(validation_result) << "Policy validation result cannot be null"; |
| if (validation_result->status != FetchedPolicyValidator::VALIDATION_OK) { |
| VLOG(1) << "Policy validation failed for " << key.policy_type() |
| << " response: " |
| << FetchedPolicyValidator::StatusToString( |
| validation_result->status); |
| return *validation_result; |
| } |
| } |
| return FetchedPolicyValidator::ValidationResult(); |
| } |
| |
| // Update the cached policy information, configuring the CloudPolicyClient |
| // with the results. |
| void UpdateCachedPolicyInfo() { |
| cached_policy_info_ = dm_storage_->GetCachedPolicyInfo(); |
| if (cached_policy_info_->has_key_version()) { |
| cloud_policy_client_->set_public_key_version( |
| cached_policy_info_->key_version()); |
| } else { |
| cloud_policy_client_->clear_public_key_version(); |
| } |
| } |
| |
| void HandleTaskTimeout() { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| // If the callback has already been responded to, the task did not time out. |
| if (pending_callback_) { |
| VLOG(1) << "DMClient task timed out before CloudPolicyClient response"; |
| std::move(pending_callback_) |
| .Run(EnterpriseCompanionStatus( |
| ApplicationError::kCloudPolicyClientTimeout)); |
| } |
| } |
| |
| void SetPendingCallback(StatusCallback callback) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| task_timer_.Start(FROM_HERE, task_timeout_, |
| base::BindOnce(&DMClientImpl::HandleTaskTimeout, |
| base::Unretained(this))); |
| pending_callback_ = std::move(callback); |
| } |
| |
| SEQUENCE_CHECKER(sequence_checker_); |
| const base::TimeDelta task_timeout_; |
| policy::DeviceManagementService dm_service_; |
| std::unique_ptr<policy::CloudPolicyClient> cloud_policy_client_; |
| scoped_refptr<device_management_storage::DMStorage> dm_storage_; |
| PolicyFetchResponseValidator policy_fetch_response_validator_; |
| ClientDataDelegate client_data_delegate_; |
| StatusCallback pending_callback_; |
| base::OneShotTimer task_timer_; |
| std::unique_ptr<device_management_storage::CachedPolicyInfo> |
| cached_policy_info_; |
| std::unique_ptr<FailedEnrollmentTokenService> |
| failed_enrollment_token_service_; |
| }; |
| |
| } // namespace |
| |
| CloudPolicyClientProvider GetDefaultCloudPolicyClientProvider( |
| scoped_refptr<network::SharedURLLoaderFactory> shared_url_loader_factory) { |
| return base::BindOnce( |
| [](scoped_refptr<network::SharedURLLoaderFactory> |
| shared_url_loader_factory, |
| policy::DeviceManagementService* dm_service) { |
| return std::make_unique<policy::CloudPolicyClient>( |
| dm_service, shared_url_loader_factory); |
| }, |
| std::move(shared_url_loader_factory)); |
| } |
| |
| PolicyFetchResponseValidator GetDefaultPolicyFetchResponseValidator() { |
| return base::BindRepeating([](const std::string& dm_token, |
| const std::string& device_id, |
| const std::string& cached_policy_public_key, |
| int64_t cached_policy_timestamp, |
| const enterprise_management:: |
| PolicyFetchResponse& response) { |
| FetchedPolicyValidator validator( |
| std::make_unique<enterprise_management::PolicyFetchResponse>(response)); |
| validator.ValidateDMToken( |
| dm_token, |
| FetchedPolicyValidator::ValidateDMTokenOption::DM_TOKEN_REQUIRED); |
| validator.ValidateDeviceId( |
| device_id, |
| FetchedPolicyValidator::ValidateDeviceIdOption::DEVICE_ID_REQUIRED); |
| validator.ValidateTimestamp( |
| base::Time::FromMillisecondsSinceUnixEpoch(cached_policy_timestamp), |
| FetchedPolicyValidator::ValidateTimestampOption::TIMESTAMP_VALIDATED); |
| if (cached_policy_public_key.empty()) { |
| validator.ValidateInitialKey(""); |
| } else { |
| validator.ValidateSignatureAllowingRotation(cached_policy_public_key, ""); |
| } |
| validator.ValidatePayload(); |
| validator.RunValidation(); |
| return validator.GetValidationResult(); |
| }); |
| } |
| |
| std::unique_ptr<policy::DeviceManagementService::Configuration> |
| CreateDeviceManagementServiceConfig() { |
| return std::make_unique<DMConfiguration>(); |
| } |
| |
| std::unique_ptr<FailedEnrollmentTokenService> |
| GetDefaultFailedEnrollmentTokenService() { |
| return std::make_unique<FailedEnrollmentTokenServiceImpl>(); |
| } |
| |
| std::unique_ptr<DMClient> CreateDMClient( |
| CloudPolicyClientProvider cloud_policy_client_provider, |
| scoped_refptr<device_management_storage::DMStorage> dm_storage, |
| PolicyFetchResponseValidator policy_fetch_response_validator, |
| std::unique_ptr<policy::DeviceManagementService::Configuration> config, |
| base::TimeDelta task_timeout, |
| std::unique_ptr<FailedEnrollmentTokenService> |
| failed_enrollment_token_service) { |
| return std::make_unique<BufferedDMClient>(std::make_unique<DMClientImpl>( |
| std::move(config), std::move(cloud_policy_client_provider), dm_storage, |
| policy_fetch_response_validator, task_timeout, |
| std::move(failed_enrollment_token_service))); |
| } |
| |
| } // namespace enterprise_companion |