// Copyright (c) 2011 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/policy/cloud_policy_cache_base.h"
#include <string>
#include "base/logging.h"
#include "base/values.h"
#include "chrome/browser/policy/configuration_policy_pref_store.h"
#include "chrome/browser/policy/policy_notifier.h"
namespace policy {
// A thin ConfigurationPolicyProvider implementation sitting on top of
// CloudPolicyCacheBase for hooking up with ConfigurationPolicyPrefStore.
class CloudPolicyCacheBase::CloudPolicyProvider
: public ConfigurationPolicyProvider {
public:
CloudPolicyProvider(const PolicyDefinitionList* policy_list,
CloudPolicyCacheBase* cache,
CloudPolicyCacheBase::PolicyLevel level)
: ConfigurationPolicyProvider(policy_list),
cache_(cache),
level_(level) {}
virtual ~CloudPolicyProvider() {}
virtual bool Provide(ConfigurationPolicyStoreInterface* store) {
if (level_ == POLICY_LEVEL_MANDATORY)
ApplyPolicyMap(&cache_->mandatory_policy_, store);
else if (level_ == POLICY_LEVEL_RECOMMENDED)
ApplyPolicyMap(&cache_->recommended_policy_, store);
return true;
}
virtual bool IsInitializationComplete() const {
return cache_->initialization_complete_;
}
virtual void AddObserver(ConfigurationPolicyProvider::Observer* observer) {
cache_->observer_list_.AddObserver(observer);
}
virtual void RemoveObserver(ConfigurationPolicyProvider::Observer* observer) {
cache_->observer_list_.RemoveObserver(observer);
}
private:
// The underlying policy cache.
CloudPolicyCacheBase* cache_;
// Policy level this provider will handle.
CloudPolicyCacheBase::PolicyLevel level_;
DISALLOW_COPY_AND_ASSIGN(CloudPolicyProvider);
};
CloudPolicyCacheBase::CloudPolicyCacheBase()
: notifier_(NULL),
initialization_complete_(false),
is_unmanaged_(false) {
public_key_version_.valid = false;
managed_policy_provider_.reset(
new CloudPolicyProvider(
ConfigurationPolicyPrefStore::GetChromePolicyDefinitionList(),
this,
POLICY_LEVEL_MANDATORY));
recommended_policy_provider_.reset(
new CloudPolicyProvider(
ConfigurationPolicyPrefStore::GetChromePolicyDefinitionList(),
this,
POLICY_LEVEL_RECOMMENDED));
}
CloudPolicyCacheBase::~CloudPolicyCacheBase() {
FOR_EACH_OBSERVER(ConfigurationPolicyProvider::Observer,
observer_list_, OnProviderGoingAway());
}
bool CloudPolicyCacheBase::GetPublicKeyVersion(int* version) {
if (public_key_version_.valid)
*version = public_key_version_.version;
return public_key_version_.valid;
}
bool CloudPolicyCacheBase::SetPolicyInternal(
const em::PolicyFetchResponse& policy,
base::Time* timestamp,
bool check_for_timestamp_validity) {
DCHECK(CalledOnValidThread());
bool initialization_was_not_complete = !initialization_complete_;
is_unmanaged_ = false;
PolicyMap mandatory_policy;
PolicyMap recommended_policy;
base::Time temp_timestamp;
PublicKeyVersion temp_public_key_version;
bool ok = DecodePolicyResponse(policy, &mandatory_policy, &recommended_policy,
&temp_timestamp, &temp_public_key_version);
if (!ok) {
LOG(WARNING) << "Decoding policy data failed.";
return false;
}
if (timestamp) {
*timestamp = temp_timestamp;
}
if (check_for_timestamp_validity &&
temp_timestamp > base::Time::NowFromSystemTime()) {
LOG(WARNING) << "Rejected policy data, file is from the future.";
return false;
}
public_key_version_.version = temp_public_key_version.version;
public_key_version_.valid = temp_public_key_version.valid;
const bool new_policy_differs =
!mandatory_policy_.Equals(mandatory_policy) ||
!recommended_policy_.Equals(recommended_policy);
mandatory_policy_.Swap(&mandatory_policy);
recommended_policy_.Swap(&recommended_policy);
initialization_complete_ = true;
if (new_policy_differs || initialization_was_not_complete) {
FOR_EACH_OBSERVER(ConfigurationPolicyProvider::Observer,
observer_list_, OnUpdatePolicy());
}
InformNotifier(CloudPolicySubsystem::SUCCESS,
CloudPolicySubsystem::NO_DETAILS);
return true;
}
void CloudPolicyCacheBase::SetUnmanagedInternal(const base::Time& timestamp) {
is_unmanaged_ = true;
initialization_complete_ = true;
public_key_version_.valid = false;
mandatory_policy_.Clear();
recommended_policy_.Clear();
last_policy_refresh_time_ = timestamp;
FOR_EACH_OBSERVER(ConfigurationPolicyProvider::Observer,
observer_list_, OnUpdatePolicy());
}
ConfigurationPolicyProvider* CloudPolicyCacheBase::GetManagedPolicyProvider() {
DCHECK(CalledOnValidThread());
return managed_policy_provider_.get();
}
ConfigurationPolicyProvider*
CloudPolicyCacheBase::GetRecommendedPolicyProvider() {
DCHECK(CalledOnValidThread());
return recommended_policy_provider_.get();
}
bool CloudPolicyCacheBase::DecodePolicyResponse(
const em::PolicyFetchResponse& policy_response,
PolicyMap* mandatory,
PolicyMap* recommended,
base::Time* timestamp,
PublicKeyVersion* public_key_version) {
std::string data = policy_response.policy_data();
em::PolicyData policy_data;
if (!policy_data.ParseFromString(data)) {
LOG(WARNING) << "Failed to parse PolicyData protobuf.";
return false;
}
if (timestamp) {
*timestamp = base::Time::UnixEpoch() +
base::TimeDelta::FromMilliseconds(policy_data.timestamp());
}
if (public_key_version) {
public_key_version->valid = policy_data.has_public_key_version();
if (public_key_version->valid)
public_key_version->version = policy_data.public_key_version();
}
return DecodePolicyData(policy_data, mandatory, recommended);
}
void CloudPolicyCacheBase::InformNotifier(
CloudPolicySubsystem::PolicySubsystemState state,
CloudPolicySubsystem::ErrorDetails error_details) {
// TODO(jkummerow): To obsolete this NULL-check, make all uses of
// UserPolicyCache explicitly set a notifier using |set_policy_notifier()|.
if (notifier_)
notifier_->Inform(state, error_details, PolicyNotifier::POLICY_CACHE);
}
} // namespace policy