| // Copyright (c) 2012 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/policy_loader_mac.h" |
| |
| #include "base/bind.h" |
| #include "base/bind_helpers.h" |
| #include "base/callback.h" |
| #include "base/file_util.h" |
| #include "base/mac/foundation_util.h" |
| #include "base/mac/scoped_cftyperef.h" |
| #include "base/path_service.h" |
| #include "base/platform_file.h" |
| #include "base/sequenced_task_runner.h" |
| #include "base/strings/sys_string_conversions.h" |
| #include "base/values.h" |
| #include "chrome/browser/policy/external_data_fetcher.h" |
| #include "chrome/browser/policy/policy_bundle.h" |
| #include "chrome/browser/policy/policy_domain_descriptor.h" |
| #include "chrome/browser/policy/policy_load_status.h" |
| #include "chrome/browser/policy/policy_map.h" |
| #include "chrome/browser/policy/preferences_mac.h" |
| #include "components/policy/core/common/schema.h" |
| #include "policy/policy_constants.h" |
| |
| using base::mac::CFCast; |
| using base::ScopedCFTypeRef; |
| |
| namespace policy { |
| |
| namespace { |
| |
| // Callback function for CFDictionaryApplyFunction. |key| and |value| are an |
| // entry of the CFDictionary that should be converted into an equivalent entry |
| // in the DictionaryValue in |context|. |
| void DictionaryEntryToValue(const void* key, const void* value, void* context) { |
| if (CFStringRef cf_key = CFCast<CFStringRef>(key)) { |
| base::Value* converted = |
| PolicyLoaderMac::CreateValueFromProperty( |
| static_cast<CFPropertyListRef>(value)); |
| if (converted) { |
| const std::string string = base::SysCFStringRefToUTF8(cf_key); |
| static_cast<base::DictionaryValue *>(context)->Set(string, converted); |
| } |
| } |
| } |
| |
| // Callback function for CFArrayApplyFunction. |value| is an entry of the |
| // CFArray that should be converted into an equivalent entry in the ListValue |
| // in |context|. |
| void ArrayEntryToValue(const void* value, void* context) { |
| base::Value* converted = |
| PolicyLoaderMac::CreateValueFromProperty( |
| static_cast<CFPropertyListRef>(value)); |
| if (converted) |
| static_cast<base::ListValue *>(context)->Append(converted); |
| } |
| |
| } // namespace |
| |
| PolicyLoaderMac::PolicyLoaderMac( |
| scoped_refptr<base::SequencedTaskRunner> task_runner, |
| const PolicyDefinitionList* policy_list, |
| const base::FilePath& managed_policy_path, |
| MacPreferences* preferences) |
| : AsyncPolicyLoader(task_runner), |
| policy_list_(policy_list), |
| preferences_(preferences), |
| managed_policy_path_(managed_policy_path) {} |
| |
| PolicyLoaderMac::~PolicyLoaderMac() {} |
| |
| void PolicyLoaderMac::InitOnBackgroundThread() { |
| if (!managed_policy_path_.empty()) { |
| watcher_.Watch( |
| managed_policy_path_, false, |
| base::Bind(&PolicyLoaderMac::OnFileUpdated, base::Unretained(this))); |
| } |
| } |
| |
| scoped_ptr<PolicyBundle> PolicyLoaderMac::Load() { |
| preferences_->AppSynchronize(kCFPreferencesCurrentApplication); |
| scoped_ptr<PolicyBundle> bundle(new PolicyBundle()); |
| |
| // Load Chrome's policy. |
| // TODO(joaodasilva): use a schema for Chrome once it's generated and |
| // available from a PolicyDomainDescriptor. |
| PolicyMap& chrome_policy = |
| bundle->Get(PolicyNamespace(POLICY_DOMAIN_CHROME, std::string())); |
| |
| PolicyLoadStatusSample status; |
| bool policy_present = false; |
| const PolicyDefinitionList::Entry* current; |
| for (current = policy_list_->begin; current != policy_list_->end; ++current) { |
| base::ScopedCFTypeRef<CFStringRef> name( |
| base::SysUTF8ToCFStringRef(current->name)); |
| base::ScopedCFTypeRef<CFPropertyListRef> value( |
| preferences_->CopyAppValue(name, kCFPreferencesCurrentApplication)); |
| if (!value.get()) |
| continue; |
| policy_present = true; |
| bool forced = |
| preferences_->AppValueIsForced(name, kCFPreferencesCurrentApplication); |
| PolicyLevel level = forced ? POLICY_LEVEL_MANDATORY : |
| POLICY_LEVEL_RECOMMENDED; |
| // TODO(joaodasilva): figure the policy scope. |
| base::Value* policy = CreateValueFromProperty(value); |
| if (policy) |
| chrome_policy.Set(current->name, level, POLICY_SCOPE_USER, policy, NULL); |
| else |
| status.Add(POLICY_LOAD_STATUS_PARSE_ERROR); |
| } |
| |
| if (!policy_present) |
| status.Add(POLICY_LOAD_STATUS_NO_POLICY); |
| |
| // Load policy for the registered components. |
| static const struct { |
| PolicyDomain domain; |
| const char* domain_name; |
| } kSupportedDomains[] = { |
| { POLICY_DOMAIN_EXTENSIONS, "extensions" }, |
| }; |
| for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kSupportedDomains); ++i) { |
| DescriptorMap::const_iterator it = |
| descriptor_map().find(kSupportedDomains[i].domain); |
| if (it != descriptor_map().end()) { |
| LoadPolicyForDomain( |
| it->second, kSupportedDomains[i].domain_name, bundle.get()); |
| } |
| } |
| |
| return bundle.Pass(); |
| } |
| |
| base::Time PolicyLoaderMac::LastModificationTime() { |
| base::PlatformFileInfo file_info; |
| if (!file_util::GetFileInfo(managed_policy_path_, &file_info) || |
| file_info.is_directory) { |
| return base::Time(); |
| } |
| |
| return file_info.last_modified; |
| } |
| |
| // static |
| base::Value* PolicyLoaderMac::CreateValueFromProperty( |
| CFPropertyListRef property) { |
| if (CFCast<CFNullRef>(property)) |
| return base::Value::CreateNullValue(); |
| |
| if (CFBooleanRef boolean = CFCast<CFBooleanRef>(property)) |
| return base::Value::CreateBooleanValue(CFBooleanGetValue(boolean)); |
| |
| if (CFNumberRef number = CFCast<CFNumberRef>(property)) { |
| // CFNumberGetValue() converts values implicitly when the conversion is not |
| // lossy. Check the type before trying to convert. |
| if (CFNumberIsFloatType(number)) { |
| double double_value; |
| if (CFNumberGetValue(number, kCFNumberDoubleType, &double_value)) |
| return base::Value::CreateDoubleValue(double_value); |
| } else { |
| int int_value; |
| if (CFNumberGetValue(number, kCFNumberIntType, &int_value)) |
| return base::Value::CreateIntegerValue(int_value); |
| } |
| } |
| |
| if (CFStringRef string = CFCast<CFStringRef>(property)) |
| return base::Value::CreateStringValue(base::SysCFStringRefToUTF8(string)); |
| |
| if (CFDictionaryRef dict = CFCast<CFDictionaryRef>(property)) { |
| base::DictionaryValue* dict_value = new base::DictionaryValue(); |
| CFDictionaryApplyFunction(dict, DictionaryEntryToValue, dict_value); |
| return dict_value; |
| } |
| |
| if (CFArrayRef array = CFCast<CFArrayRef>(property)) { |
| base::ListValue* list_value = new base::ListValue(); |
| CFArrayApplyFunction(array, |
| CFRangeMake(0, CFArrayGetCount(array)), |
| ArrayEntryToValue, |
| list_value); |
| return list_value; |
| } |
| |
| return NULL; |
| } |
| |
| void PolicyLoaderMac::LoadPolicyForDomain( |
| scoped_refptr<const PolicyDomainDescriptor> descriptor, |
| const std::string& domain_name, |
| PolicyBundle* bundle) { |
| std::string id_prefix(base::mac::BaseBundleID()); |
| id_prefix.append(".").append(domain_name).append("."); |
| |
| for (PolicyDomainDescriptor::SchemaMap::const_iterator it_schema = |
| descriptor->components().begin(); |
| it_schema != descriptor->components().end(); ++it_schema) { |
| PolicyMap policy; |
| LoadPolicyForComponent( |
| id_prefix + it_schema->first, it_schema->second, &policy); |
| if (!policy.empty()) { |
| bundle->Get(PolicyNamespace(descriptor->domain(), it_schema->first)) |
| .Swap(&policy); |
| } |
| } |
| } |
| |
| void PolicyLoaderMac::LoadPolicyForComponent( |
| const std::string& bundle_id_string, |
| Schema schema, |
| PolicyMap* policy) { |
| // TODO(joaodasilva): extensions may be registered in a PolicyDomainDescriptor |
| // without a schema, to allow a graceful update of the Legacy Browser Support |
| // extension on Windows. Remove this check once that support is removed. |
| if (!schema.valid()) |
| return; |
| |
| base::ScopedCFTypeRef<CFStringRef> bundle_id( |
| base::SysUTF8ToCFStringRef(bundle_id_string)); |
| preferences_->AppSynchronize(bundle_id); |
| |
| for (Schema::Iterator it = schema.GetPropertiesIterator(); |
| !it.IsAtEnd(); it.Advance()) { |
| base::ScopedCFTypeRef<CFStringRef> pref_name( |
| base::SysUTF8ToCFStringRef(it.key())); |
| base::ScopedCFTypeRef<CFPropertyListRef> value( |
| preferences_->CopyAppValue(pref_name, bundle_id)); |
| if (!value.get()) |
| continue; |
| bool forced = |
| preferences_->AppValueIsForced(pref_name, bundle_id); |
| PolicyLevel level = forced ? POLICY_LEVEL_MANDATORY : |
| POLICY_LEVEL_RECOMMENDED; |
| scoped_ptr<base::Value> policy_value(CreateValueFromProperty(value)); |
| if (policy_value) { |
| policy->Set(it.key(), level, POLICY_SCOPE_USER, |
| policy_value.release(), NULL); |
| } |
| } |
| } |
| |
| void PolicyLoaderMac::OnFileUpdated(const base::FilePath& path, bool error) { |
| if (!error) |
| Reload(false); |
| } |
| |
| } // namespace policy |