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.
4
5 #include "components/policy/core/common/policy_loader_mac.h"
6
7 #include "base/bind.h"
8 #include "base/bind_helpers.h"
9 #include "base/callback.h"
10 #include "base/files/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/sequenced_task_runner.h"
15 #include "base/strings/sys_string_conversions.h"
16 #include "base/values.h"
17 #include "components/policy/core/common/external_data_fetcher.h"
18 #include "components/policy/core/common/mac_util.h"
19 #include "components/policy/core/common/policy_bundle.h"
20 #include "components/policy/core/common/policy_load_status.h"
21 #include "components/policy/core/common/policy_map.h"
22 #include "components/policy/core/common/preferences_mac.h"
23 #include "components/policy/core/common/schema.h"
24 #include "components/policy/core/common/schema_map.h"
25
26 using base::ScopedCFTypeRef;
27
28 namespace policy {
29
PolicyLoaderMac(scoped_refptr<base::SequencedTaskRunner> task_runner,const base::FilePath & managed_policy_path,MacPreferences * preferences)30 PolicyLoaderMac::PolicyLoaderMac(
31 scoped_refptr<base::SequencedTaskRunner> task_runner,
32 const base::FilePath& managed_policy_path,
33 MacPreferences* preferences)
34 : AsyncPolicyLoader(task_runner),
35 preferences_(preferences),
36 managed_policy_path_(managed_policy_path) {}
37
~PolicyLoaderMac()38 PolicyLoaderMac::~PolicyLoaderMac() {}
39
InitOnBackgroundThread()40 void PolicyLoaderMac::InitOnBackgroundThread() {
41 if (!managed_policy_path_.empty()) {
42 watcher_.Watch(
43 managed_policy_path_, false,
44 base::Bind(&PolicyLoaderMac::OnFileUpdated, base::Unretained(this)));
45 }
46 }
47
Load()48 scoped_ptr<PolicyBundle> PolicyLoaderMac::Load() {
49 preferences_->AppSynchronize(kCFPreferencesCurrentApplication);
50 scoped_ptr<PolicyBundle> bundle(new PolicyBundle());
51
52 // Load Chrome's policy.
53 PolicyMap& chrome_policy =
54 bundle->Get(PolicyNamespace(POLICY_DOMAIN_CHROME, std::string()));
55
56 PolicyLoadStatusSample status;
57 bool policy_present = false;
58 const Schema* schema =
59 schema_map()->GetSchema(PolicyNamespace(POLICY_DOMAIN_CHROME, ""));
60 for (Schema::Iterator it = schema->GetPropertiesIterator();
61 !it.IsAtEnd(); it.Advance()) {
62 base::ScopedCFTypeRef<CFStringRef> name(
63 base::SysUTF8ToCFStringRef(it.key()));
64 base::ScopedCFTypeRef<CFPropertyListRef> value(
65 preferences_->CopyAppValue(name, kCFPreferencesCurrentApplication));
66 if (!value.get())
67 continue;
68 policy_present = true;
69 bool forced =
70 preferences_->AppValueIsForced(name, kCFPreferencesCurrentApplication);
71 PolicyLevel level = forced ? POLICY_LEVEL_MANDATORY :
72 POLICY_LEVEL_RECOMMENDED;
73 // TODO(joaodasilva): figure the policy scope.
74 scoped_ptr<base::Value> policy = PropertyToValue(value);
75 if (policy) {
76 chrome_policy.Set(
77 it.key(), level, POLICY_SCOPE_USER, policy.release(), NULL);
78 } else {
79 status.Add(POLICY_LOAD_STATUS_PARSE_ERROR);
80 }
81 }
82
83 if (!policy_present)
84 status.Add(POLICY_LOAD_STATUS_NO_POLICY);
85
86 // Load policy for the registered components.
87 LoadPolicyForDomain(POLICY_DOMAIN_EXTENSIONS, "extensions", bundle.get());
88
89 return bundle.Pass();
90 }
91
LastModificationTime()92 base::Time PolicyLoaderMac::LastModificationTime() {
93 base::File::Info file_info;
94 if (!base::GetFileInfo(managed_policy_path_, &file_info) ||
95 file_info.is_directory) {
96 return base::Time();
97 }
98
99 return file_info.last_modified;
100 }
101
LoadPolicyForDomain(PolicyDomain domain,const std::string & domain_name,PolicyBundle * bundle)102 void PolicyLoaderMac::LoadPolicyForDomain(
103 PolicyDomain domain,
104 const std::string& domain_name,
105 PolicyBundle* bundle) {
106 std::string id_prefix(base::mac::BaseBundleID());
107 id_prefix.append(".").append(domain_name).append(".");
108
109 const ComponentMap* components = schema_map()->GetComponents(domain);
110 if (!components)
111 return;
112
113 for (ComponentMap::const_iterator it = components->begin();
114 it != components->end(); ++it) {
115 PolicyMap policy;
116 LoadPolicyForComponent(id_prefix + it->first, it->second, &policy);
117 if (!policy.empty())
118 bundle->Get(PolicyNamespace(domain, it->first)).Swap(&policy);
119 }
120 }
121
LoadPolicyForComponent(const std::string & bundle_id_string,const Schema & schema,PolicyMap * policy)122 void PolicyLoaderMac::LoadPolicyForComponent(
123 const std::string& bundle_id_string,
124 const Schema& schema,
125 PolicyMap* policy) {
126 // TODO(joaodasilva): Extensions may be registered in a ComponentMap
127 // without a schema, to allow a graceful update of the Legacy Browser Support
128 // extension on Windows. Remove this check once that support is removed.
129 if (!schema.valid())
130 return;
131
132 base::ScopedCFTypeRef<CFStringRef> bundle_id(
133 base::SysUTF8ToCFStringRef(bundle_id_string));
134 preferences_->AppSynchronize(bundle_id);
135
136 for (Schema::Iterator it = schema.GetPropertiesIterator();
137 !it.IsAtEnd(); it.Advance()) {
138 base::ScopedCFTypeRef<CFStringRef> pref_name(
139 base::SysUTF8ToCFStringRef(it.key()));
140 base::ScopedCFTypeRef<CFPropertyListRef> value(
141 preferences_->CopyAppValue(pref_name, bundle_id));
142 if (!value.get())
143 continue;
144 bool forced =
145 preferences_->AppValueIsForced(pref_name, bundle_id);
146 PolicyLevel level = forced ? POLICY_LEVEL_MANDATORY :
147 POLICY_LEVEL_RECOMMENDED;
148 scoped_ptr<base::Value> policy_value = PropertyToValue(value);
149 if (policy_value) {
150 policy->Set(it.key(), level, POLICY_SCOPE_USER,
151 policy_value.release(), NULL);
152 }
153 }
154 }
155
OnFileUpdated(const base::FilePath & path,bool error)156 void PolicyLoaderMac::OnFileUpdated(const base::FilePath& path, bool error) {
157 if (!error)
158 Reload(false);
159 }
160
161 } // namespace policy
162