blob: d96fb78df063504200b323b993c8184c3557a4b6 [file] [log] [blame]
// Copyright 2018 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 "chrome/browser/chromeos/printing/enterprise_printers_provider.h"
#include <vector>
#include "base/hash/md5.h"
#include "base/json/json_reader.h"
#include "chrome/browser/chromeos/printing/bulk_printers_calculator.h"
#include "chrome/browser/chromeos/printing/bulk_printers_calculator_factory.h"
#include "chrome/browser/chromeos/printing/calculators_policies_binder.h"
#include "chrome/browser/chromeos/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/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"
namespace chromeos {
namespace {
std::vector<std::string> ConvertToVector(const base::ListValue* list) {
std::vector<std::string> string_list;
if (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());
// Binds instances of BulkPrintersCalculator to policies.
policies_binder_ = CalculatorsPoliciesBinder::Create(settings, profile);
// Get instance of BulkPrintersCalculator for device policies.
device_printers_ = BulkPrintersCalculatorFactory::Get()->GetForDevice();
if (device_printers_) {
device_printers_->AddObserver(this);
RecalculateCompleteFlagForDevicePrinters();
}
// Get instance of BulkPrintersCalculator for user policies.
user_printers_ =
BulkPrintersCalculatorFactory::Get()->GetForProfile(profile);
if (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::kRecommendedNativePrinters,
&EnterprisePrintersProviderImpl::UpdateUserRecommendedPrinters);
}
~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 (sender == device_printers_.get()) {
RecalculateCompleteFlagForDevicePrinters();
} else {
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::kRecommendedNativePrinters);
for (const auto& printer_json : data) {
std::unique_ptr<base::DictionaryValue> printer_dictionary =
base::DictionaryValue::From(base::JSONReader::ReadDeprecated(
printer_json, base::JSON_ALLOW_TRAILING_COMMAS));
if (!printer_dictionary) {
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);
printer_dictionary->SetString(kPrinterId, id);
auto new_printer = RecommendedPrinterToPrinter(*printer_dictionary);
if (!new_printer) {
LOG(WARNING) << "Recommended printer is malformed.";
continue;
}
if (!recommended_printers_.insert({id, *new_printer}).second) {
// 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() {
user_printers_is_complete_ =
user_printers_->IsComplete() &&
(user_printers_->IsDataPolicySet() ||
!PolicyWithDataIsSet(policy::key::kNativePrintersBulkConfiguration));
}
void RecalculateCompleteFlagForDevicePrinters() {
device_printers_is_complete_ =
device_printers_->IsComplete() &&
(device_printers_->IsDataPolicySet() ||
!PolicyWithDataIsSet(policy::key::kDeviceNativePrinters));
}
void RecalculateCurrentPrintersList() {
complete_ = true;
printers_ = recommended_printers_;
if (device_printers_) {
complete_ = complete_ && device_printers_is_complete_;
const auto& printers = device_printers_->GetPrinters();
printers_.insert(printers.begin(), printers.end());
}
if (user_printers_) {
complete_ = complete_ && user_printers_is_complete_;
const auto& printers = user_printers_->GetPrinters();
printers_.insert(printers.begin(), printers.end());
}
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);
if (value && value->is_dict()) {
// policy is set and its value is a dictionary
return true;
}
return false;
}
// current partial results
std::unordered_map<std::string, Printer> recommended_printers_;
bool device_printers_is_complete_ = true;
bool user_printers_is_complete_ = true;
// current final results
bool complete_ = false;
std::unordered_map<std::string, 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> policies_binder_;
// Profile (user) settings.
Profile* profile_;
PrefChangeRegistrar pref_change_registrar_;
base::ObserverList<EnterprisePrintersProvider::Observer>::Unchecked
observers_;
SEQUENCE_CHECKER(sequence_checker_);
DISALLOW_COPY_AND_ASSIGN(EnterprisePrintersProviderImpl);
};
} // namespace
// static
void EnterprisePrintersProvider::RegisterProfilePrefs(
user_prefs::PrefRegistrySyncable* registry) {
registry->RegisterListPref(prefs::kRecommendedNativePrinters);
CalculatorsPoliciesBinder::RegisterProfilePrefs(registry);
}
// static
std::unique_ptr<EnterprisePrintersProvider> EnterprisePrintersProvider::Create(
CrosSettings* settings,
Profile* profile) {
return std::make_unique<EnterprisePrintersProviderImpl>(settings, profile);
}
} // namespace chromeos