OLD | NEW |
---|---|
(Empty) | |
1 // Copyright (c) 2011 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 "chrome/browser/chromeos/device_settings_provider.h" | |
6 | |
7 #include "base/base64.h" | |
8 #include "base/bind.h" | |
9 #include "base/bind_helpers.h" | |
10 #include "base/callback.h" | |
11 #include "base/hash_tables.h" | |
12 #include "base/logging.h" | |
13 #include "base/memory/singleton.h" | |
14 #include "base/string_util.h" | |
15 #include "base/values.h" | |
16 #include "chrome/browser/browser_process.h" | |
17 #include "chrome/browser/chromeos/cros/cros_library.h" | |
18 #include "chrome/browser/chromeos/cros/network_library.h" | |
19 #include "chrome/browser/chromeos/cros_settings.h" | |
20 #include "chrome/browser/chromeos/cros_settings_names.h" | |
21 #include "chrome/browser/chromeos/login/ownership_service.h" | |
22 #include "chrome/browser/chromeos/login/ownership_status_checker.h" | |
Mattias Nissler (ping if slow)
2011/12/02 12:13:37
not needed.
pastarmovj
2011/12/02 14:43:38
Done.
| |
23 #include "chrome/browser/chromeos/login/signed_settings_cache.h" | |
24 #include "chrome/browser/chromeos/login/user_manager.h" | |
25 #include "chrome/browser/policy/proto/chrome_device_policy.pb.h" | |
26 #include "chrome/browser/policy/proto/device_management_backend.pb.h" | |
27 #include "chrome/browser/prefs/pref_service.h" | |
28 #include "chrome/browser/prefs/pref_value_map.h" | |
29 #include "chrome/browser/prefs/scoped_user_pref_update.h" | |
30 #include "chrome/browser/ui/options/options_util.h" | |
31 #include "chrome/common/chrome_notification_types.h" | |
32 #include "chrome/installer/util/google_update_settings.h" | |
33 #include "content/public/browser/browser_thread.h" | |
34 #include "content/public/browser/notification_service.h" | |
35 | |
36 using content::BrowserThread; | |
37 using google::protobuf::RepeatedPtrField; | |
38 | |
39 namespace chromeos { | |
40 | |
41 namespace { | |
42 | |
43 const char* kBooleanSettings[] = { | |
44 kAccountsPrefAllowNewUser, | |
45 kAccountsPrefAllowGuest, | |
46 kAccountsPrefShowUserNamesOnSignIn, | |
47 kSignedDataRoamingEnabled, | |
48 kStatsReportingPref | |
49 }; | |
50 | |
51 const char* kStringSettings[] = { | |
52 kDeviceOwner, | |
53 kReleaseChannel, | |
54 kSettingProxyEverywhere | |
55 }; | |
56 | |
57 const char* kListSettings[] = { | |
58 kAccountsPrefUsers | |
59 }; | |
60 | |
61 // Upper bound for number of retries to fetch a signed setting. | |
62 static const int kNumRetriesLimit = 9; | |
63 | |
64 bool IsControlledBooleanSetting(const std::string& pref_path) { | |
65 const char** end = kBooleanSettings + arraysize(kBooleanSettings); | |
66 return std::find(kBooleanSettings, end, pref_path) != end; | |
67 } | |
68 | |
69 bool IsControlledStringSetting(const std::string& pref_path) { | |
70 const char** end = kStringSettings + arraysize(kStringSettings); | |
71 return std::find(kStringSettings, end, pref_path) != end; | |
72 } | |
73 | |
74 bool IsControlledListSetting(const std::string& pref_path) { | |
75 const char** end = kListSettings + arraysize(kListSettings); | |
76 return std::find(kListSettings, end, pref_path) != end; | |
77 } | |
78 | |
79 bool IsControlledSetting(const std::string& pref_path) { | |
80 return (IsControlledBooleanSetting(pref_path) || | |
81 IsControlledStringSetting(pref_path) || | |
82 IsControlledListSetting(pref_path)); | |
83 } | |
84 | |
85 bool HasOldMetricsFile() { | |
86 // TODO(pastarmovj): Remove this once migration is not needed anymore. | |
87 // If the value is not set we should try to migrate legacy consent file. | |
88 // Loading consent file state causes us to do blocking IO on UI thread. | |
89 // Temporarily allow it until we fix http://crbug.com/62626 | |
90 base::ThreadRestrictions::ScopedAllowIO allow_io; | |
91 return GoogleUpdateSettings::GetCollectStatsConsent(); | |
92 } | |
93 | |
94 } // namespace | |
95 | |
96 DeviceSettingsProvider::DeviceSettingsProvider() | |
97 : ownership_status_(OwnershipService::GetSharedInstance()->GetStatus(true)), | |
98 migration_helper_(new SignedSettingsMigrationHelper()), | |
99 retries_left_(kNumRetriesLimit), | |
100 trusted_(false) { | |
101 // Register for notification when ownership is taken so that we can update | |
102 // the |ownership_status_| and reload if needed. | |
103 registrar_.Add(this, chrome::NOTIFICATION_OWNER_KEY_FETCH_ATTEMPT_SUCCEEDED, | |
104 content::NotificationService::AllSources()); | |
105 // Make sure we have at least the cache data immediately. | |
106 RetrieveCachedData(); | |
107 // Start prefetching preferences. | |
108 Reload(); | |
109 } | |
110 | |
111 DeviceSettingsProvider::~DeviceSettingsProvider() { | |
112 } | |
113 | |
114 void DeviceSettingsProvider::Reload() { | |
115 // While fetching we can't trust the cache anymore. | |
116 trusted_ = false; | |
117 if (ownership_status_ == OwnershipService::OWNERSHIP_NONE) { | |
118 RetrieveCachedData(); | |
119 } else { | |
120 // Retrieve the real data. | |
121 SignedSettingsHelper::Get()->StartRetrievePolicyOp( | |
122 base::Bind(&DeviceSettingsProvider::OnRetrievePolicyCompleted, | |
123 base::Unretained(this))); | |
124 } | |
125 } | |
126 | |
127 void DeviceSettingsProvider::DoSet(const std::string& path, | |
128 const base::Value& in_value) { | |
129 if (!UserManager::Get()->current_user_is_owner() && | |
130 ownership_status_ != OwnershipService::OWNERSHIP_NONE) { | |
131 LOG(WARNING) << "Changing settings from non-owner, setting=" << path; | |
132 | |
133 // Revert UI change. | |
134 CrosSettings::Get()->FireObservers(path.c_str()); | |
135 return; | |
136 } | |
137 | |
138 if (IsControlledSetting(path)) | |
139 SetInPolicy(path, in_value); | |
140 else | |
141 NOTREACHED() << "Try to set unhandled cros setting " << path; | |
142 } | |
143 | |
144 void DeviceSettingsProvider::Observe( | |
145 int type, | |
146 const content::NotificationSource& source, | |
147 const content::NotificationDetails& details) { | |
148 if (type == chrome::NOTIFICATION_OWNER_KEY_FETCH_ATTEMPT_SUCCEEDED && | |
149 UserManager::Get()->current_user_is_owner()) { | |
150 // Reload the initial policy blob, apply settings from temp storage, | |
151 // and write back the blob. | |
152 ownership_status_ = OwnershipService::OWNERSHIP_TAKEN; | |
153 Reload(); | |
154 } | |
155 } | |
156 | |
157 const em::PolicyData DeviceSettingsProvider::get_policy() const { | |
158 return policy_; | |
159 } | |
160 | |
161 void DeviceSettingsProvider::RetrieveCachedData() { | |
162 // If there is no owner yet, this function will pull the policy cache from the | |
163 // temp storage and use that instead. | |
164 em::PolicyData policy; | |
165 if (!signed_settings_cache::Retrieve(&policy, | |
166 g_browser_process->local_state())) { | |
167 VLOG(1) << "Can't retrieve temp store possibly not created yet."; | |
168 // Prepare empty data for the case we don't have temp cache yet. | |
169 policy.set_policy_type(kDevicePolicyType); | |
170 em::ChromeDeviceSettingsProto pol; | |
171 policy.set_policy_value(pol.SerializeAsString()); | |
172 } | |
173 | |
174 policy_ = policy; | |
175 UpdateValuesCache(); | |
176 } | |
177 | |
178 void DeviceSettingsProvider::SetInPolicy(const std::string& prop, | |
179 const base::Value& value) { | |
180 if (prop == kDeviceOwner) { | |
181 // Just store it in the memory cache no trusted checks no persisting. | |
Mattias Nissler (ping if slow)
2011/12/02 12:13:37
no comma no sentence no grammar? :)
pastarmovj
2011/12/02 14:43:38
Done,
| |
182 std::string owner; | |
183 if (value.GetAsString(&owner)) { | |
184 policy_.set_username(owner); | |
185 values_cache_.SetValue(prop, value.DeepCopy()); | |
186 CrosSettings::Get()->FireObservers(prop.c_str()); | |
187 // We can't trust this value anymore until we reload the real username. | |
188 trusted_ = false; | |
189 } else { | |
190 NOTREACHED(); | |
191 } | |
192 return; | |
193 } | |
194 | |
195 if (!RequestTrustedEntity()) { | |
196 // Otherwise we should first reload and apply on top of that. | |
197 SignedSettingsHelper::Get()->StartRetrievePolicyOp( | |
198 base::Bind(&DeviceSettingsProvider::FinishSetInPolicy, | |
199 base::Unretained(this), | |
200 prop, base::Owned(value.DeepCopy()))); | |
201 return; | |
202 } | |
203 | |
204 trusted_ = false; | |
205 em::PolicyData data = get_policy(); | |
206 em::ChromeDeviceSettingsProto pol; | |
207 pol.ParseFromString(data.policy_value()); | |
208 if (prop == kAccountsPrefAllowNewUser) { | |
209 em::AllowNewUsersProto* allow = pol.mutable_allow_new_users(); | |
210 bool allow_value; | |
211 if (value.GetAsBoolean(&allow_value)) | |
212 allow->set_allow_new_users(allow_value); | |
213 else | |
214 NOTREACHED(); | |
215 } else if (prop == kAccountsPrefAllowGuest) { | |
216 em::GuestModeEnabledProto* guest = pol.mutable_guest_mode_enabled(); | |
217 bool guest_value; | |
218 if (value.GetAsBoolean(&guest_value)) | |
219 guest->set_guest_mode_enabled(guest_value); | |
220 else | |
221 NOTREACHED(); | |
222 } else if (prop == kAccountsPrefShowUserNamesOnSignIn) { | |
223 em::ShowUserNamesOnSigninProto* show = pol.mutable_show_user_names(); | |
224 bool show_value; | |
225 if (value.GetAsBoolean(&show_value)) | |
226 show->set_show_user_names(show_value); | |
227 else | |
228 NOTREACHED(); | |
229 } else if (prop == kSignedDataRoamingEnabled) { | |
230 em::DataRoamingEnabledProto* roam = pol.mutable_data_roaming_enabled(); | |
231 bool roaming_value = false; | |
232 if (value.GetAsBoolean(&roaming_value)) | |
233 roam->set_data_roaming_enabled(roaming_value); | |
234 else | |
235 NOTREACHED(); | |
236 ApplyRoamingSetting(roaming_value); | |
237 } else if (prop == kSettingProxyEverywhere) { | |
238 // TODO(cmasone): NOTIMPLEMENTED() once http://crosbug.com/13052 is fixed. | |
239 std::string proxy_value; | |
240 if (value.GetAsString(&proxy_value)) { | |
241 bool success = | |
242 pol.mutable_device_proxy_settings()->ParseFromString(proxy_value); | |
243 DCHECK(success); | |
244 } else { | |
245 NOTREACHED(); | |
246 } | |
247 } else if (prop == kReleaseChannel) { | |
248 em::ReleaseChannelProto* release_channel = pol.mutable_release_channel(); | |
249 std::string channel_value; | |
250 if (value.GetAsString(&channel_value)) | |
251 release_channel->set_release_channel(channel_value); | |
252 else | |
253 NOTREACHED(); | |
254 } else if (prop == kStatsReportingPref) { | |
255 em::MetricsEnabledProto* metrics = pol.mutable_metrics_enabled(); | |
256 bool metrics_value = false; | |
257 if (value.GetAsBoolean(&metrics_value)) | |
258 metrics->set_metrics_enabled(metrics_value); | |
259 else | |
260 NOTREACHED(); | |
261 ApplyMetricsSetting(false, metrics_value); | |
262 } else if (prop == kAccountsPrefUsers) { | |
263 em::UserWhitelistProto* whitelist_proto = pol.mutable_user_whitelist(); | |
264 whitelist_proto->clear_user_whitelist(); | |
265 const base::ListValue& users = static_cast<const base::ListValue&>(value); | |
266 for (base::ListValue::const_iterator i = users.begin(); | |
267 i != users.end(); ++i) { | |
268 std::string email; | |
269 if ((*i)->GetAsString(&email)) | |
270 whitelist_proto->add_user_whitelist(email.c_str()); | |
271 } | |
272 } else { | |
273 NOTREACHED(); | |
274 } | |
275 data.set_policy_value(pol.SerializeAsString()); | |
276 // Set the cache to the updated value. | |
277 policy_ = data; | |
278 UpdateValuesCache(); | |
279 CrosSettings::Get()->FireObservers(prop.c_str()); | |
280 | |
281 if (!signed_settings_cache::Store(data, g_browser_process->local_state())) | |
282 LOG(ERROR) << "Couldn't store to the temp storage."; | |
283 | |
284 if (ownership_status_ == OwnershipService::OWNERSHIP_TAKEN) { | |
285 em::PolicyFetchResponse policy_envelope; | |
286 policy_envelope.set_policy_data(policy_.SerializeAsString()); | |
287 SignedSettingsHelper::Get()->StartStorePolicyOp( | |
288 policy_envelope, | |
289 base::Bind(&DeviceSettingsProvider::OnStorePolicyCompleted, | |
290 base::Unretained(this))); | |
291 } | |
292 } | |
293 | |
294 void DeviceSettingsProvider::FinishSetInPolicy( | |
295 const std::string& prop, | |
296 const base::Value* value, | |
297 SignedSettings::ReturnCode code, | |
298 const em::PolicyFetchResponse& policy) { | |
299 if (code != SignedSettings::SUCCESS) { | |
300 LOG(ERROR) << "Can't serialize to policy error code: " << code; | |
301 Reload(); | |
302 return; | |
303 } | |
304 SetInPolicy(prop, *value); | |
305 } | |
306 | |
307 void DeviceSettingsProvider::UpdateValuesCache() { | |
308 const em::PolicyData data = get_policy(); | |
309 values_cache_.Clear(); | |
310 | |
311 if (data.has_username() && !data.has_request_token()) | |
312 values_cache_.SetString(kDeviceOwner, data.username()); | |
313 | |
314 em::ChromeDeviceSettingsProto pol; | |
315 pol.ParseFromString(data.policy_value()); | |
316 | |
317 // For all our boolean settings the following is applicable: | |
318 // true is default permissive value and false is safe prohibitive value. | |
319 // Exception: kSignedDataRoamingEnabled which has default value of false. | |
320 if (pol.has_allow_new_users() && | |
321 pol.allow_new_users().has_allow_new_users() && | |
322 pol.allow_new_users().allow_new_users()) { | |
323 // New users allowed, user_whitelist() ignored. | |
324 values_cache_.SetBoolean(kAccountsPrefAllowNewUser, true); | |
325 } else if (!pol.has_user_whitelist()) { | |
326 // If we have the allow_new_users bool, and it is true, we honor that above. | |
327 // In all other cases (don't have it, have it and it is set to false, etc), | |
328 // We will honor the user_whitelist() if it is there and populated. | |
329 // Otherwise we default to allowing new users. | |
330 values_cache_.SetBoolean(kAccountsPrefAllowNewUser, true); | |
331 } else { | |
332 values_cache_.SetBoolean(kAccountsPrefAllowNewUser, | |
333 pol.user_whitelist().user_whitelist_size() == 0); | |
334 } | |
335 | |
336 values_cache_.SetBoolean( | |
337 kAccountsPrefAllowGuest, | |
338 !pol.has_guest_mode_enabled() || | |
339 !pol.guest_mode_enabled().has_guest_mode_enabled() || | |
340 pol.guest_mode_enabled().guest_mode_enabled()); | |
341 | |
342 values_cache_.SetBoolean( | |
343 kAccountsPrefShowUserNamesOnSignIn, | |
344 !pol.has_show_user_names() || | |
345 !pol.show_user_names().has_show_user_names() || | |
346 pol.show_user_names().show_user_names()); | |
347 | |
348 values_cache_.SetBoolean( | |
349 kSignedDataRoamingEnabled, | |
350 pol.has_data_roaming_enabled() && | |
351 pol.data_roaming_enabled().has_data_roaming_enabled() && | |
352 pol.data_roaming_enabled().data_roaming_enabled()); | |
353 | |
354 // TODO(cmasone): NOTIMPLEMENTED() once http://crosbug.com/13052 is fixed. | |
355 std::string serialized; | |
356 if (pol.has_device_proxy_settings() && | |
357 pol.device_proxy_settings().SerializeToString(&serialized)) { | |
358 values_cache_.SetString(kSettingProxyEverywhere, serialized); | |
359 } | |
360 | |
361 if (!pol.has_release_channel() || | |
362 !pol.release_channel().has_release_channel()) { | |
363 // Default to an invalid channel (will be ignored). | |
364 values_cache_.SetString(kReleaseChannel, ""); | |
365 } else { | |
366 values_cache_.SetString(kReleaseChannel, | |
367 pol.release_channel().release_channel()); | |
368 } | |
369 | |
370 if (pol.has_metrics_enabled()) { | |
371 values_cache_.SetBoolean(kStatsReportingPref, | |
372 pol.metrics_enabled().metrics_enabled()); | |
373 } else { | |
374 values_cache_.SetBoolean(kStatsReportingPref, HasOldMetricsFile()); | |
375 } | |
376 | |
377 base::ListValue* list = new base::ListValue(); | |
378 const em::UserWhitelistProto& whitelist_proto = pol.user_whitelist(); | |
379 const RepeatedPtrField<std::string>& whitelist = | |
380 whitelist_proto.user_whitelist(); | |
381 for (RepeatedPtrField<std::string>::const_iterator it = whitelist.begin(); | |
382 it != whitelist.end(); ++it) { | |
383 list->Append(base::Value::CreateStringValue(*it)); | |
384 } | |
385 values_cache_.SetValue(kAccountsPrefUsers, list); | |
386 } | |
387 | |
388 void DeviceSettingsProvider::ApplyMetricsSetting(bool use_file, | |
389 bool new_value) const { | |
390 // TODO(pastarmovj): Remove this once migration is not needed anymore. | |
391 // If the value is not set we should try to migrate legacy consent file. | |
392 if (use_file) { | |
393 new_value = HasOldMetricsFile(); | |
394 // Make sure the values will get eventually written to the policy file. | |
395 migration_helper_->AddMigrationValue( | |
396 kStatsReportingPref, base::Value::CreateBooleanValue(new_value)); | |
397 migration_helper_->MigrateValues(); | |
398 LOG(INFO) << "No metrics policy set will revert to checking " | |
399 << "consent file which is " | |
400 << (new_value ? "on." : "off."); | |
401 } | |
402 VLOG(1) << "Metrics policy is being set to : " << new_value | |
403 << "(use file : " << use_file << ")"; | |
404 // TODO(pastarmovj): Remove this once we don't need to regenerate the | |
405 // consent file for the GUID anymore. | |
406 OptionsUtil::ResolveMetricsReportingEnabled(new_value); | |
407 } | |
408 | |
409 void DeviceSettingsProvider::ApplyRoamingSetting(bool new_value) const { | |
410 NetworkLibrary* cros = CrosLibrary::Get()->GetNetworkLibrary(); | |
411 const NetworkDevice* cellular = cros->FindCellularDevice(); | |
412 if (cellular) { | |
413 bool device_value = cellular->data_roaming_allowed(); | |
414 if (!device_value && cros->IsCellularAlwaysInRoaming()) { | |
415 // If operator requires roaming always enabled, ignore supplied value | |
416 // and set data roaming allowed in true always. | |
417 cros->SetCellularDataRoamingAllowed(true); | |
418 } else if (device_value != new_value) { | |
419 cros->SetCellularDataRoamingAllowed(new_value); | |
420 } | |
421 } | |
422 } | |
423 | |
424 void DeviceSettingsProvider::ApplySideEffects() const { | |
425 const em::PolicyData data = get_policy(); | |
426 em::ChromeDeviceSettingsProto pol; | |
427 pol.ParseFromString(data.policy_value()); | |
428 // First migrate metrics settings as needed. | |
429 if (pol.has_metrics_enabled()) | |
430 ApplyMetricsSetting(false, pol.metrics_enabled().metrics_enabled()); | |
431 else | |
432 ApplyMetricsSetting(true, false); | |
433 // Next set the roaming setting as needed. | |
434 ApplyRoamingSetting(pol.has_data_roaming_enabled() ? | |
435 pol.data_roaming_enabled().data_roaming_enabled() : false); | |
436 } | |
437 | |
438 const base::Value* DeviceSettingsProvider::Get(const std::string& path) const { | |
439 if (IsControlledSetting(path)) { | |
440 const base::Value* value; | |
441 if (values_cache_.GetValue(path, &value)) | |
442 return value; | |
443 } else { | |
444 NOTREACHED() << "Trying to get non cros setting."; | |
445 } | |
446 | |
447 return NULL; | |
448 } | |
449 | |
450 bool DeviceSettingsProvider::GetTrusted(const std::string& path, | |
451 const base::Closure& callback) { | |
452 if (!IsControlledSetting(path)) { | |
453 NOTREACHED(); | |
454 return true; | |
455 } | |
456 | |
457 if (RequestTrustedEntity()) { | |
458 return true; | |
459 } else { | |
460 if (!callback.is_null()) | |
461 callbacks_.push_back(callback); | |
462 return false; | |
463 } | |
464 } | |
465 | |
466 bool DeviceSettingsProvider::HandlesSetting(const std::string& path) const { | |
467 return IsControlledSetting(path); | |
468 } | |
469 | |
470 bool DeviceSettingsProvider::RequestTrustedEntity() { | |
471 if (ownership_status_ == OwnershipService::OWNERSHIP_NONE) | |
472 return true; | |
473 return trusted_; | |
474 } | |
475 | |
476 void DeviceSettingsProvider::OnStorePolicyCompleted( | |
477 SignedSettings::ReturnCode code) { | |
478 // In any case reload the policy cache to now. | |
479 if (code != SignedSettings::SUCCESS) | |
480 Reload(); | |
481 else | |
482 trusted_ = true; | |
483 } | |
484 | |
485 void DeviceSettingsProvider::OnRetrievePolicyCompleted( | |
486 SignedSettings::ReturnCode code, | |
487 const em::PolicyFetchResponse& policy) { | |
488 switch (code) { | |
489 case SignedSettings::SUCCESS: { | |
490 DCHECK(policy.has_policy_data()); | |
491 policy_.ParseFromString(policy.policy_data()); | |
492 signed_settings_cache::Store(get_policy(), | |
493 g_browser_process->local_state()); | |
494 UpdateValuesCache(); | |
495 trusted_ = true; | |
496 for (size_t i = 0; i < callbacks_.size(); ++i) | |
497 callbacks_[i].Run(); | |
498 callbacks_.clear(); | |
499 // TODO(pastarmovj): Make those side effects responsibility of the | |
500 // respective subsystems. | |
501 ApplySideEffects(); | |
502 break; | |
503 } | |
504 case SignedSettings::NOT_FOUND: | |
505 case SignedSettings::KEY_UNAVAILABLE: { | |
506 if (ownership_status_ != OwnershipService::OWNERSHIP_TAKEN) | |
507 NOTREACHED() << "No policies present yet, will use the temp storage."; | |
508 break; | |
509 } | |
510 case SignedSettings::BAD_SIGNATURE: | |
511 case SignedSettings::OPERATION_FAILED: { | |
512 LOG(ERROR) << "Failed to retrieve cros policies. Reason:" << code; | |
513 if (retries_left_ > 0) { | |
514 retries_left_ -= 1; | |
515 Reload(); | |
516 return; | |
517 } | |
518 LOG(ERROR) << "No retries left"; | |
519 break; | |
520 } | |
521 } | |
522 } | |
523 | |
524 } // namespace chromeos | |
OLD | NEW |