1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/policy/policy_loader_mac.h"
8 #include "base/bind_helpers.h"
9 #include "base/callback.h"
10 #include "base/file_util.h"
11 #include "base/mac/foundation_util.h"
12 #include "base/mac/scoped_cftyperef.h"
13 #include "base/path_service.h"
14 #include "base/platform_file.h"
15 #include "base/sequenced_task_runner.h"
16 #include "base/strings/sys_string_conversions.h"
17 #include "base/values.h"
18 #include "chrome/browser/policy/external_data_fetcher.h"
19 #include "chrome/browser/policy/policy_bundle.h"
20 #include "chrome/browser/policy/policy_domain_descriptor.h"
21 #include "chrome/browser/policy/policy_load_status.h"
22 #include "chrome/browser/policy/policy_map.h"
23 #include "chrome/browser/policy/preferences_mac.h"
24 #include "components/policy/core/common/schema.h"
25 #include "policy/policy_constants.h"
27 using base::mac::CFCast;
28 using base::ScopedCFTypeRef;
34 // Callback function for CFDictionaryApplyFunction. |key| and |value| are an
35 // entry of the CFDictionary that should be converted into an equivalent entry
36 // in the DictionaryValue in |context|.
37 void DictionaryEntryToValue(const void* key, const void* value, void* context) {
38 if (CFStringRef cf_key = CFCast<CFStringRef>(key)) {
39 base::Value* converted =
40 PolicyLoaderMac::CreateValueFromProperty(
41 static_cast<CFPropertyListRef>(value));
43 const std::string string = base::SysCFStringRefToUTF8(cf_key);
44 static_cast<base::DictionaryValue *>(context)->Set(string, converted);
49 // Callback function for CFArrayApplyFunction. |value| is an entry of the
50 // CFArray that should be converted into an equivalent entry in the ListValue
52 void ArrayEntryToValue(const void* value, void* context) {
53 base::Value* converted =
54 PolicyLoaderMac::CreateValueFromProperty(
55 static_cast<CFPropertyListRef>(value));
57 static_cast<base::ListValue *>(context)->Append(converted);
62 PolicyLoaderMac::PolicyLoaderMac(
63 scoped_refptr<base::SequencedTaskRunner> task_runner,
64 const PolicyDefinitionList* policy_list,
65 const base::FilePath& managed_policy_path,
66 MacPreferences* preferences)
67 : AsyncPolicyLoader(task_runner),
68 policy_list_(policy_list),
69 preferences_(preferences),
70 managed_policy_path_(managed_policy_path) {}
72 PolicyLoaderMac::~PolicyLoaderMac() {}
74 void PolicyLoaderMac::InitOnBackgroundThread() {
75 if (!managed_policy_path_.empty()) {
77 managed_policy_path_, false,
78 base::Bind(&PolicyLoaderMac::OnFileUpdated, base::Unretained(this)));
82 scoped_ptr<PolicyBundle> PolicyLoaderMac::Load() {
83 preferences_->AppSynchronize(kCFPreferencesCurrentApplication);
84 scoped_ptr<PolicyBundle> bundle(new PolicyBundle());
86 // Load Chrome's policy.
87 // TODO(joaodasilva): use a schema for Chrome once it's generated and
88 // available from a PolicyDomainDescriptor.
89 PolicyMap& chrome_policy =
90 bundle->Get(PolicyNamespace(POLICY_DOMAIN_CHROME, std::string()));
92 PolicyLoadStatusSample status;
93 bool policy_present = false;
94 const PolicyDefinitionList::Entry* current;
95 for (current = policy_list_->begin; current != policy_list_->end; ++current) {
96 base::ScopedCFTypeRef<CFStringRef> name(
97 base::SysUTF8ToCFStringRef(current->name));
98 base::ScopedCFTypeRef<CFPropertyListRef> value(
99 preferences_->CopyAppValue(name, kCFPreferencesCurrentApplication));
102 policy_present = true;
104 preferences_->AppValueIsForced(name, kCFPreferencesCurrentApplication);
105 PolicyLevel level = forced ? POLICY_LEVEL_MANDATORY :
106 POLICY_LEVEL_RECOMMENDED;
107 // TODO(joaodasilva): figure the policy scope.
108 base::Value* policy = CreateValueFromProperty(value);
110 chrome_policy.Set(current->name, level, POLICY_SCOPE_USER, policy, NULL);
112 status.Add(POLICY_LOAD_STATUS_PARSE_ERROR);
116 status.Add(POLICY_LOAD_STATUS_NO_POLICY);
118 // Load policy for the registered components.
119 static const struct {
121 const char* domain_name;
122 } kSupportedDomains[] = {
123 { POLICY_DOMAIN_EXTENSIONS, "extensions" },
125 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kSupportedDomains); ++i) {
126 DescriptorMap::const_iterator it =
127 descriptor_map().find(kSupportedDomains[i].domain);
128 if (it != descriptor_map().end()) {
130 it->second, kSupportedDomains[i].domain_name, bundle.get());
134 return bundle.Pass();
137 base::Time PolicyLoaderMac::LastModificationTime() {
138 base::PlatformFileInfo file_info;
139 if (!file_util::GetFileInfo(managed_policy_path_, &file_info) ||
140 file_info.is_directory) {
144 return file_info.last_modified;
148 base::Value* PolicyLoaderMac::CreateValueFromProperty(
149 CFPropertyListRef property) {
150 if (CFCast<CFNullRef>(property))
151 return base::Value::CreateNullValue();
153 if (CFBooleanRef boolean = CFCast<CFBooleanRef>(property))
154 return base::Value::CreateBooleanValue(CFBooleanGetValue(boolean));
156 if (CFNumberRef number = CFCast<CFNumberRef>(property)) {
157 // CFNumberGetValue() converts values implicitly when the conversion is not
158 // lossy. Check the type before trying to convert.
159 if (CFNumberIsFloatType(number)) {
161 if (CFNumberGetValue(number, kCFNumberDoubleType, &double_value))
162 return base::Value::CreateDoubleValue(double_value);
165 if (CFNumberGetValue(number, kCFNumberIntType, &int_value))
166 return base::Value::CreateIntegerValue(int_value);
170 if (CFStringRef string = CFCast<CFStringRef>(property))
171 return base::Value::CreateStringValue(base::SysCFStringRefToUTF8(string));
173 if (CFDictionaryRef dict = CFCast<CFDictionaryRef>(property)) {
174 base::DictionaryValue* dict_value = new base::DictionaryValue();
175 CFDictionaryApplyFunction(dict, DictionaryEntryToValue, dict_value);
179 if (CFArrayRef array = CFCast<CFArrayRef>(property)) {
180 base::ListValue* list_value = new base::ListValue();
181 CFArrayApplyFunction(array,
182 CFRangeMake(0, CFArrayGetCount(array)),
191 void PolicyLoaderMac::LoadPolicyForDomain(
192 scoped_refptr<const PolicyDomainDescriptor> descriptor,
193 const std::string& domain_name,
194 PolicyBundle* bundle) {
195 std::string id_prefix(base::mac::BaseBundleID());
196 id_prefix.append(".").append(domain_name).append(".");
198 for (PolicyDomainDescriptor::SchemaMap::const_iterator it_schema =
199 descriptor->components().begin();
200 it_schema != descriptor->components().end(); ++it_schema) {
202 LoadPolicyForComponent(
203 id_prefix + it_schema->first, it_schema->second, &policy);
204 if (!policy.empty()) {
205 bundle->Get(PolicyNamespace(descriptor->domain(), it_schema->first))
211 void PolicyLoaderMac::LoadPolicyForComponent(
212 const std::string& bundle_id_string,
215 // TODO(joaodasilva): extensions may be registered in a PolicyDomainDescriptor
216 // without a schema, to allow a graceful update of the Legacy Browser Support
217 // extension on Windows. Remove this check once that support is removed.
221 base::ScopedCFTypeRef<CFStringRef> bundle_id(
222 base::SysUTF8ToCFStringRef(bundle_id_string));
223 preferences_->AppSynchronize(bundle_id);
225 for (Schema::Iterator it = schema.GetPropertiesIterator();
226 !it.IsAtEnd(); it.Advance()) {
227 base::ScopedCFTypeRef<CFStringRef> pref_name(
228 base::SysUTF8ToCFStringRef(it.key()));
229 base::ScopedCFTypeRef<CFPropertyListRef> value(
230 preferences_->CopyAppValue(pref_name, bundle_id));
234 preferences_->AppValueIsForced(pref_name, bundle_id);
235 PolicyLevel level = forced ? POLICY_LEVEL_MANDATORY :
236 POLICY_LEVEL_RECOMMENDED;
237 scoped_ptr<base::Value> policy_value(CreateValueFromProperty(value));
239 policy->Set(it.key(), level, POLICY_SCOPE_USER,
240 policy_value.release(), NULL);
245 void PolicyLoaderMac::OnFileUpdated(const base::FilePath& path, bool error) {
250 } // namespace policy