blob: 81af0f4eb616829ffe1219aaebd74fcae0b33f2f [file] [log] [blame]
// Copyright (c) 2010 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/device_management_policy_cache.h"
#include <limits>
#include <string>
#include "base/file_util.h"
#include "base/logging.h"
#include "base/task.h"
#include "base/values.h"
#include "chrome/browser/browser_thread.h"
#include "chrome/browser/policy/proto/device_management_constants.h"
#include "chrome/browser/policy/proto/device_management_local.pb.h"
using google::protobuf::RepeatedField;
using google::protobuf::RepeatedPtrField;
namespace policy {
// Saves policy information to a file.
class PersistPolicyTask : public Task {
public:
PersistPolicyTask(const FilePath& path,
const em::DevicePolicyResponse* policy,
const base::Time& timestamp,
const bool is_device_unmanaged)
: path_(path),
policy_(policy),
timestamp_(timestamp),
is_device_unmanaged_(is_device_unmanaged) {}
private:
// Task override.
virtual void Run();
const FilePath path_;
scoped_ptr<const em::DevicePolicyResponse> policy_;
const base::Time timestamp_;
const bool is_device_unmanaged_;
};
void PersistPolicyTask::Run() {
std::string data;
em::CachedDevicePolicyResponse cached_policy;
if (policy_.get())
cached_policy.mutable_policy()->CopyFrom(*policy_);
if (is_device_unmanaged_)
cached_policy.set_unmanaged(true);
cached_policy.set_timestamp(timestamp_.ToInternalValue());
if (!cached_policy.SerializeToString(&data)) {
LOG(WARNING) << "Failed to serialize policy data";
return;
}
int size = data.size();
if (file_util::WriteFile(path_, data.c_str(), size) != size) {
LOG(WARNING) << "Failed to write " << path_.value();
return;
}
}
DeviceManagementPolicyCache::DeviceManagementPolicyCache(
const FilePath& backing_file_path)
: backing_file_path_(backing_file_path),
policy_(new DictionaryValue),
fresh_policy_(false),
is_device_unmanaged_(false) {
}
void DeviceManagementPolicyCache::LoadPolicyFromFile() {
if (!file_util::PathExists(backing_file_path_) || fresh_policy_)
return;
// Read the protobuf from the file.
std::string data;
if (!file_util::ReadFileToString(backing_file_path_, &data)) {
LOG(WARNING) << "Failed to read policy data from "
<< backing_file_path_.value();
return;
}
em::CachedDevicePolicyResponse cached_policy;
if (!cached_policy.ParseFromArray(data.c_str(), data.size())) {
LOG(WARNING) << "Failed to parse policy data read from "
<< backing_file_path_.value();
return;
}
// Reject files that claim to be from the future.
base::Time timestamp = base::Time::FromInternalValue(
cached_policy.timestamp());
if (timestamp > base::Time::NowFromSystemTime()) {
LOG(WARNING) << "Rejected policy data from " << backing_file_path_.value()
<< ", file is from the future.";
return;
}
is_device_unmanaged_ = cached_policy.unmanaged();
// Decode and swap in the new policy information.
scoped_ptr<DictionaryValue> value(DecodePolicy(cached_policy.policy()));
{
AutoLock lock(lock_);
if (!fresh_policy_)
policy_.reset(value.release());
last_policy_refresh_time_ = timestamp;
}
}
bool DeviceManagementPolicyCache::SetPolicy(
const em::DevicePolicyResponse& policy) {
is_device_unmanaged_ = false;
DictionaryValue* value = DeviceManagementPolicyCache::DecodePolicy(policy);
const bool new_policy_differs = !(value->Equals(policy_.get()));
base::Time now(base::Time::NowFromSystemTime());
{
AutoLock lock(lock_);
policy_.reset(value);
fresh_policy_ = true;
last_policy_refresh_time_ = now;
}
em::DevicePolicyResponse* policy_copy = new em::DevicePolicyResponse;
policy_copy->CopyFrom(policy);
BrowserThread::PostTask(
BrowserThread::FILE,
FROM_HERE,
new PersistPolicyTask(backing_file_path_, policy_copy, now, false));
return new_policy_differs;
}
DictionaryValue* DeviceManagementPolicyCache::GetPolicy() {
AutoLock lock(lock_);
return static_cast<DictionaryValue*>(policy_->DeepCopy());
}
void DeviceManagementPolicyCache::SetDeviceUnmanaged(bool is_device_unmanaged) {
if (is_device_unmanaged_ == is_device_unmanaged)
return;
is_device_unmanaged_ = is_device_unmanaged;
base::Time now(base::Time::NowFromSystemTime());
DictionaryValue* empty = new DictionaryValue();
{
AutoLock lock(lock_);
policy_.reset(empty);
last_policy_refresh_time_ = now;
}
BrowserThread::PostTask(
BrowserThread::FILE,
FROM_HERE,
new PersistPolicyTask(backing_file_path_,
(is_device_unmanaged ? NULL
: new em::DevicePolicyResponse()),
now,
is_device_unmanaged_));
}
// static
Value* DeviceManagementPolicyCache::DecodeIntegerValue(
google::protobuf::int64 value) {
if (value < std::numeric_limits<int>::min() ||
value > std::numeric_limits<int>::max()) {
LOG(WARNING) << "Integer value " << value
<< " out of numeric limits, ignoring.";
return NULL;
}
return Value::CreateIntegerValue(static_cast<int>(value));
}
// static
Value* DeviceManagementPolicyCache::DecodeValue(const em::GenericValue& value) {
if (!value.has_value_type())
return NULL;
switch (value.value_type()) {
case em::GenericValue::VALUE_TYPE_BOOL:
if (value.has_bool_value())
return Value::CreateBooleanValue(value.bool_value());
return NULL;
case em::GenericValue::VALUE_TYPE_INT64:
if (value.has_int64_value())
return DecodeIntegerValue(value.int64_value());
return NULL;
case em::GenericValue::VALUE_TYPE_STRING:
if (value.has_string_value())
return Value::CreateStringValue(value.string_value());
return NULL;
case em::GenericValue::VALUE_TYPE_DOUBLE:
if (value.has_double_value())
return Value::CreateRealValue(value.double_value());
return NULL;
case em::GenericValue::VALUE_TYPE_BYTES:
if (value.has_bytes_value()) {
std::string bytes = value.bytes_value();
return BinaryValue::CreateWithCopiedBuffer(bytes.c_str(), bytes.size());
}
return NULL;
case em::GenericValue::VALUE_TYPE_BOOL_ARRAY: {
ListValue* list = new ListValue;
RepeatedField<bool>::const_iterator i;
for (i = value.bool_array().begin(); i != value.bool_array().end(); ++i)
list->Append(Value::CreateBooleanValue(*i));
return list;
}
case em::GenericValue::VALUE_TYPE_INT64_ARRAY: {
ListValue* list = new ListValue;
RepeatedField<google::protobuf::int64>::const_iterator i;
for (i = value.int64_array().begin();
i != value.int64_array().end(); ++i) {
Value* int_value = DecodeIntegerValue(*i);
if (int_value)
list->Append(int_value);
}
return list;
}
case em::GenericValue::VALUE_TYPE_STRING_ARRAY: {
ListValue* list = new ListValue;
RepeatedPtrField<std::string>::const_iterator i;
for (i = value.string_array().begin();
i != value.string_array().end(); ++i)
list->Append(Value::CreateStringValue(*i));
return list;
}
case em::GenericValue::VALUE_TYPE_DOUBLE_ARRAY: {
ListValue* list = new ListValue;
RepeatedField<double>::const_iterator i;
for (i = value.double_array().begin();
i != value.double_array().end(); ++i)
list->Append(Value::CreateRealValue(*i));
return list;
}
default:
NOTREACHED() << "Unhandled value type";
}
return NULL;
}
// static
DictionaryValue* DeviceManagementPolicyCache::DecodePolicy(
const em::DevicePolicyResponse& policy) {
DictionaryValue* result = new DictionaryValue;
RepeatedPtrField<em::DevicePolicySetting>::const_iterator setting;
for (setting = policy.setting().begin();
setting != policy.setting().end();
++setting) {
// Wrong policy key? Skip.
if (setting->policy_key().compare(kChromeDevicePolicySettingKey) != 0)
continue;
// No policy value? Skip.
if (!setting->has_policy_value())
continue;
// Iterate through all the name-value pairs wrapped in |setting|.
const em::GenericSetting& policy_value(setting->policy_value());
RepeatedPtrField<em::GenericNamedValue>::const_iterator named_value;
for (named_value = policy_value.named_value().begin();
named_value != policy_value.named_value().end();
++named_value) {
if (named_value->has_value()) {
Value* decoded_value =
DeviceManagementPolicyCache::DecodeValue(named_value->value());
if (decoded_value)
result->Set(named_value->name(), decoded_value);
}
}
}
return result;
}
} // namespace policy