| // Copyright 2018 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/printing/enterprise_printers_provider.h" |
| |
| #include <iterator> |
| #include <unordered_map> |
| #include <utility> |
| #include <vector> |
| |
| #include "base/hash/md5.h" |
| #include "base/json/json_reader.h" |
| #include "base/logging.h" |
| #include "base/memory/raw_ptr.h" |
| #include "base/ranges/algorithm.h" |
| #include "chrome/browser/ash/printing/bulk_printers_calculator.h" |
| #include "chrome/browser/ash/printing/bulk_printers_calculator_factory.h" |
| #include "chrome/browser/ash/printing/calculators_policies_binder.h" |
| #include "chrome/browser/ash/profiles/profile_helper.h" |
| #include "chrome/browser/ash/settings/cros_settings.h" |
| #include "chrome/browser/policy/profile_policy_connector.h" |
| #include "chrome/common/pref_names.h" |
| #include "chromeos/printing/printer_configuration.h" |
| #include "chromeos/printing/printer_translator.h" |
| #include "components/device_event_log/device_event_log.h" |
| #include "components/policy/core/common/policy_service.h" |
| #include "components/policy/policy_constants.h" |
| #include "components/pref_registry/pref_registry_syncable.h" |
| #include "components/prefs/pref_change_registrar.h" |
| #include "components/prefs/pref_service.h" |
| #include "components/user_manager/user.h" |
| |
| namespace ash { |
| |
| namespace { |
| |
| std::vector<std::string> ConvertToVector(const base::Value::List& list) { |
| std::vector<std::string> string_list; |
| for (const base::Value& value : list) { |
| if (value.is_string()) { |
| string_list.push_back(value.GetString()); |
| } |
| } |
| return string_list; |
| } |
| |
| class EnterprisePrintersProviderImpl : public EnterprisePrintersProvider, |
| public BulkPrintersCalculator::Observer { |
| public: |
| EnterprisePrintersProviderImpl(CrosSettings* settings, Profile* profile) |
| : profile_(profile) { |
| // initialization of pref_change_registrar |
| pref_change_registrar_.Init(profile->GetPrefs()); |
| |
| auto* factory = BulkPrintersCalculatorFactory::Get(); |
| if (!factory) { |
| DVLOG(1) << "Factory is null. Policies are unbound. This is only " |
| "expected in unit tests"; |
| return; |
| } |
| |
| // Get instance of BulkPrintersCalculator for device policies. |
| device_printers_ = factory->GetForDevice(); |
| if (device_printers_) { |
| devices_binder_ = |
| CalculatorsPoliciesBinder::DeviceBinder(settings, device_printers_); |
| device_printers_->AddObserver(this); |
| RecalculateCompleteFlagForDevicePrinters(); |
| } |
| |
| // Calculate account_id_ and get instance of BulkPrintersCalculator for user |
| // policies. |
| const user_manager::User* user = |
| ProfileHelper::Get()->GetUserByProfile(profile); |
| if (user) { |
| account_id_ = user->GetAccountId(); |
| user_printers_ = factory->GetForAccountId(account_id_); |
| // Binds instances of BulkPrintersCalculator to policies. |
| profile_binder_ = CalculatorsPoliciesBinder::UserBinder( |
| profile->GetPrefs(), user_printers_); |
| user_printers_->AddObserver(this); |
| RecalculateCompleteFlagForUserPrinters(); |
| } |
| |
| // Binds policy with recommended printers (deprecated). This method calls |
| // indirectly RecalculateCurrentPrintersList() that prepares the first |
| // version of final list of printers. |
| BindPref(prefs::kRecommendedPrinters, |
| &EnterprisePrintersProviderImpl::UpdateUserRecommendedPrinters); |
| } |
| |
| EnterprisePrintersProviderImpl(const EnterprisePrintersProviderImpl&) = |
| delete; |
| EnterprisePrintersProviderImpl& operator=( |
| const EnterprisePrintersProviderImpl&) = delete; |
| |
| ~EnterprisePrintersProviderImpl() override { |
| if (device_printers_) |
| device_printers_->RemoveObserver(this); |
| if (user_printers_) { |
| user_printers_->RemoveObserver(this); |
| } |
| } |
| |
| void AddObserver(EnterprisePrintersProvider::Observer* observer) override { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| observers_.AddObserver(observer); |
| observer->OnPrintersChanged(complete_, printers_); |
| } |
| |
| void RemoveObserver(EnterprisePrintersProvider::Observer* observer) override { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| observers_.RemoveObserver(observer); |
| } |
| |
| // BulkPrintersCalculator::Observer implementation |
| void OnPrintersChanged(const BulkPrintersCalculator* sender) override { |
| if (device_printers_ && sender == device_printers_.get()) { |
| RecalculateCompleteFlagForDevicePrinters(); |
| } else if (user_printers_ && sender == user_printers_.get()) { |
| RecalculateCompleteFlagForUserPrinters(); |
| } |
| RecalculateCurrentPrintersList(); |
| } |
| |
| private: |
| // This method process value from the deprecated policy with recommended |
| // printers. It is called when value of the policy changes. |
| void UpdateUserRecommendedPrinters() { |
| recommended_printers_.clear(); |
| std::vector<std::string> data = FromPrefs(prefs::kRecommendedPrinters); |
| for (const auto& printer_json : data) { |
| std::optional<base::Value> printer_value = base::JSONReader::Read( |
| printer_json, base::JSON_ALLOW_TRAILING_COMMAS); |
| if (!printer_value.has_value() || !printer_value.value().is_dict()) { |
| LOG(WARNING) << "Ignoring invalid printer. Invalid JSON object: " |
| << printer_json; |
| continue; |
| } |
| |
| // Policy printers don't have id's but the ids only need to be locally |
| // unique so we'll hash the record. This will not collide with the |
| // UUIDs generated for user entries. |
| std::string id = base::MD5String(printer_json); |
| base::Value::Dict& printer_dictionary = printer_value.value().GetDict(); |
| printer_dictionary.Set(chromeos::kPrinterId, id); |
| |
| auto new_printer = |
| chromeos::RecommendedPrinterToPrinter(printer_dictionary); |
| if (!new_printer) { |
| LOG(WARNING) << "Recommended printer is malformed."; |
| continue; |
| } |
| |
| bool inserted = recommended_printers_.insert({id, *new_printer}).second; |
| if (!inserted) { |
| // Printer is already in the list. |
| LOG(WARNING) << "Duplicate printer ignored: " << id; |
| continue; |
| } |
| } |
| RecalculateCurrentPrintersList(); |
| } |
| |
| // These three methods calculate resultant list of printers and complete flag. |
| |
| void RecalculateCompleteFlagForUserPrinters() { |
| if (!user_printers_) { |
| user_printers_is_complete_ = true; |
| return; |
| } |
| |
| user_printers_is_complete_ = |
| user_printers_->IsComplete() && |
| (user_printers_->IsDataPolicySet() || |
| !PolicyWithDataIsSet(policy::key::kPrintersBulkConfiguration)); |
| } |
| |
| void RecalculateCompleteFlagForDevicePrinters() { |
| if (!device_printers_) { |
| device_printers_is_complete_ = true; |
| return; |
| } |
| |
| device_printers_is_complete_ = |
| device_printers_->IsComplete() && |
| (device_printers_->IsDataPolicySet() || |
| !PolicyWithDataIsSet(policy::key::kDevicePrinters)); |
| } |
| |
| void RecalculateCurrentPrintersList() { |
| complete_ = true; |
| |
| // Enterprise printers from user policy, device policy, as well as printers |
| // from the legacy `Printers` policy. |
| std::unordered_map<std::string, chromeos::Printer> all_printers = |
| recommended_printers_; |
| |
| if (device_printers_) { |
| complete_ = complete_ && device_printers_is_complete_; |
| std::unordered_map<std::string, chromeos::Printer> printers = |
| device_printers_->GetPrinters(); |
| PRINTER_LOG(DEBUG) |
| << "EnterprisePrintersProvider::RecalculateCurrentPrintersList()" |
| << "-device-printers: complete=" << device_printers_is_complete_ |
| << " count=" << printers.size(); |
| |
| all_printers.merge(std::move(printers)); |
| } |
| if (user_printers_) { |
| complete_ = complete_ && user_printers_is_complete_; |
| std::unordered_map<std::string, chromeos::Printer> printers = |
| user_printers_->GetPrinters(); |
| PRINTER_LOG(DEBUG) |
| << "EnterprisePrintersProvider::RecalculateCurrentPrintersList()" |
| << "-user-printers: complete=" << user_printers_is_complete_ |
| << " count=" << printers.size(); |
| all_printers.merge(std::move(printers)); |
| } |
| |
| // Update `printers_` with the recalculated result. |
| printers_.clear(); |
| printers_.reserve(all_printers.size()); |
| base::ranges::transform(all_printers, std::back_inserter(printers_), |
| [](const auto& p) { return p.second; }); |
| |
| for (auto& observer : observers_) { |
| observer.OnPrintersChanged(complete_, printers_); |
| } |
| } |
| |
| typedef void (EnterprisePrintersProviderImpl::*SimpleMethod)(); |
| |
| // Binds given user policy to given method and calls this method once. |
| void BindPref(const char* policy_name, SimpleMethod method_to_call) { |
| pref_change_registrar_.Add( |
| policy_name, |
| base::BindRepeating(method_to_call, base::Unretained(this))); |
| (this->*method_to_call)(); |
| } |
| |
| // Extracts the list of strings named |policy_name| from user policies. |
| std::vector<std::string> FromPrefs(const std::string& policy_name) { |
| return ConvertToVector(profile_->GetPrefs()->GetList(policy_name)); |
| } |
| |
| // Checks if given policy is set and if it is a dictionary |
| bool PolicyWithDataIsSet(const char* policy_name) { |
| policy::ProfilePolicyConnector* policy_connector = |
| profile_->GetProfilePolicyConnector(); |
| if (!policy_connector) { |
| // something is wrong |
| return false; |
| } |
| const policy::PolicyNamespace policy_namespace = |
| policy::PolicyNamespace(policy::PolicyDomain::POLICY_DOMAIN_CHROME, ""); |
| const policy::PolicyMap& policy_map = |
| policy_connector->policy_service()->GetPolicies(policy_namespace); |
| const base::Value* value = |
| policy_map.GetValue(policy_name, base::Value::Type::DICT); |
| return value != nullptr; |
| } |
| |
| // current partial results |
| std::unordered_map<std::string, chromeos::Printer> recommended_printers_; |
| bool device_printers_is_complete_ = true; |
| bool user_printers_is_complete_ = true; |
| |
| // current final results |
| bool complete_ = false; |
| std::vector<chromeos::Printer> printers_; |
| |
| // Calculators for bulk printers from device and user policies. Unowned. |
| base::WeakPtr<BulkPrintersCalculator> device_printers_; |
| base::WeakPtr<BulkPrintersCalculator> user_printers_; |
| |
| // Policies binder (bridge between policies and calculators). Owned. |
| std::unique_ptr<CalculatorsPoliciesBinder> devices_binder_; |
| std::unique_ptr<CalculatorsPoliciesBinder> profile_binder_; |
| |
| // Profile (user) settings. |
| raw_ptr<Profile> profile_; |
| AccountId account_id_; |
| PrefChangeRegistrar pref_change_registrar_; |
| |
| base::ObserverList<EnterprisePrintersProvider::Observer>::Unchecked |
| observers_; |
| SEQUENCE_CHECKER(sequence_checker_); |
| }; |
| |
| } // namespace |
| |
| // static |
| void EnterprisePrintersProvider::RegisterProfilePrefs( |
| user_prefs::PrefRegistrySyncable* registry) { |
| registry->RegisterListPref(prefs::kRecommendedPrinters); |
| CalculatorsPoliciesBinder::RegisterProfilePrefs(registry); |
| } |
| |
| // static |
| std::unique_ptr<EnterprisePrintersProvider> EnterprisePrintersProvider::Create( |
| CrosSettings* settings, |
| Profile* profile) { |
| return std::make_unique<EnterprisePrintersProviderImpl>(settings, profile); |
| } |
| |
| } // namespace ash |