user_cros_settings_provider.cc revision 72a454cd3513ac24fbdd0e0cb9ad70b86a99b801
1// Copyright (c) 2010 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/user_cros_settings_provider.h"
6
7#include <map>
8#include <set>
9
10#include "base/hash_tables.h"
11#include "base/logging.h"
12#include "base/singleton.h"
13#include "base/string_util.h"
14#include "base/task.h"
15#include "base/values.h"
16#include "chrome/browser/browser_process.h"
17#include "chrome/browser/browser_thread.h"
18#include "chrome/browser/chromeos/cros/cros_library.h"
19#include "chrome/browser/chromeos/cros/login_library.h"
20#include "chrome/browser/chromeos/cros_settings.h"
21#include "chrome/browser/chromeos/cros_settings_names.h"
22#include "chrome/browser/chromeos/login/ownership_service.h"
23#include "chrome/browser/chromeos/login/user_manager.h"
24#include "chrome/browser/prefs/pref_service.h"
25#include "chrome/common/notification_observer.h"
26#include "chrome/common/notification_registrar.h"
27#include "chrome/common/notification_service.h"
28#include "chrome/common/notification_type.h"
29
30namespace chromeos {
31
32namespace {
33
34const char kTrueIncantation[] = "true";
35const char kFalseIncantation[] = "false";
36const char kTrustedSuffix[] = "/trusted";
37
38// For all our boolean settings following is applicable:
39// true is default permissive value and false is safe prohibitic value.
40const char* kBooleanSettings[] = {
41  kAccountsPrefAllowNewUser,
42  kAccountsPrefAllowGuest,
43  kAccountsPrefShowUserNamesOnSignIn
44};
45
46const char* kStringSettings[] = {
47  kDeviceOwner
48};
49
50const char* kListSettings[] = {
51  kAccountsPrefUsers
52};
53
54bool IsControlledBooleanSetting(const std::string& pref_path) {
55  return std::find(kBooleanSettings,
56                   kBooleanSettings + arraysize(kBooleanSettings),
57                   pref_path) !=
58      kBooleanSettings + arraysize(kBooleanSettings);
59}
60
61bool IsControlledStringSetting(const std::string& pref_path) {
62  return std::find(kStringSettings,
63                   kStringSettings + arraysize(kStringSettings),
64                   pref_path) !=
65      kStringSettings + arraysize(kStringSettings);
66}
67
68bool IsControlledListSetting(const std::string& pref_path) {
69  return std::find(kListSettings,
70                   kListSettings + arraysize(kListSettings),
71                   pref_path) !=
72      kListSettings + arraysize(kListSettings);
73}
74
75void RegisterSetting(PrefService* local_state, const std::string& pref_path) {
76  local_state->RegisterBooleanPref((pref_path + kTrustedSuffix).c_str(),
77                                   false);
78  if (IsControlledBooleanSetting(pref_path)) {
79    local_state->RegisterBooleanPref(pref_path.c_str(), true);
80  } else if (IsControlledStringSetting(pref_path)) {
81    local_state->RegisterStringPref(pref_path.c_str(), "");
82  } else {
83    DCHECK(IsControlledListSetting(pref_path));
84    local_state->RegisterListPref(pref_path.c_str());
85  }
86}
87
88Value* CreateSettingsBooleanValue(bool value, bool managed) {
89  DictionaryValue* dict = new DictionaryValue;
90  dict->Set("value", Value::CreateBooleanValue(value));
91  dict->Set("managed", Value::CreateBooleanValue(managed));
92  return dict;
93}
94
95enum UseValue {
96  USE_VALUE_SUPPLIED,
97  USE_VALUE_DEFAULT
98};
99
100void UpdateCacheBool(const std::string& name,
101                     bool value,
102                     UseValue use_value) {
103  PrefService* prefs = g_browser_process->local_state();
104  if (use_value == USE_VALUE_DEFAULT)
105    prefs->ClearPref(name.c_str());
106  else
107    prefs->SetBoolean(name.c_str(), value);
108  prefs->ScheduleSavePersistentPrefs();
109}
110
111void UpdateCacheString(const std::string& name,
112                       const std::string& value,
113                       UseValue use_value) {
114  PrefService* prefs = g_browser_process->local_state();
115  if (use_value == USE_VALUE_DEFAULT)
116    prefs->ClearPref(name.c_str());
117  else
118    prefs->SetString(name.c_str(), value);
119  prefs->ScheduleSavePersistentPrefs();
120}
121
122bool GetUserWhitelist(ListValue* user_list) {
123  PrefService* prefs = g_browser_process->local_state();
124  DCHECK(!prefs->IsManagedPreference(kAccountsPrefUsers));
125
126  std::vector<std::string> whitelist;
127  if (!CrosLibrary::Get()->EnsureLoaded() ||
128      !CrosLibrary::Get()->GetLoginLibrary()->EnumerateWhitelisted(
129          &whitelist)) {
130    LOG(WARNING) << "Failed to retrieve user whitelist.";
131    return false;
132  }
133
134  ListValue* cached_whitelist = prefs->GetMutableList(kAccountsPrefUsers);
135  cached_whitelist->Clear();
136
137  const UserManager::User& self = UserManager::Get()->logged_in_user();
138  bool is_owner = UserManager::Get()->current_user_is_owner();
139
140  for (size_t i = 0; i < whitelist.size(); ++i) {
141    const std::string& email = whitelist[i];
142
143    if (user_list) {
144      DictionaryValue* user = new DictionaryValue;
145      user->SetString("email", email);
146      user->SetString("name", "");
147      user->SetBoolean("owner", is_owner && email == self.email());
148      user_list->Append(user);
149    }
150
151    cached_whitelist->Append(Value::CreateStringValue(email));
152  }
153
154  prefs->ScheduleSavePersistentPrefs();
155  return true;
156}
157
158class UserCrosSettingsTrust : public SignedSettingsHelper::Callback,
159                              public NotificationObserver {
160 public:
161  static UserCrosSettingsTrust* GetInstance() {
162    return Singleton<UserCrosSettingsTrust>::get();
163  }
164
165  // Working horse for UserCrosSettingsProvider::RequestTrusted* family.
166  bool RequestTrustedEntity(const std::string& name) {
167    if (GetOwnershipStatus() == OWNERSHIP_NONE)
168      return true;
169    PrefService* prefs = g_browser_process->local_state();
170    if (prefs->IsManagedPreference(name.c_str()))
171      return true;
172    if (GetOwnershipStatus() == OWNERSHIP_TAKEN) {
173      DCHECK(g_browser_process);
174      PrefService* prefs = g_browser_process->local_state();
175      DCHECK(prefs);
176      if (prefs->GetBoolean((name + kTrustedSuffix).c_str()))
177        return true;
178    }
179    return false;
180  }
181
182  bool RequestTrustedEntity(const std::string& name, Task* callback) {
183    if (RequestTrustedEntity(name)) {
184      delete callback;
185      return true;
186    } else {
187      if (callback)
188        callbacks_[name].push_back(callback);
189      return false;
190    }
191  }
192
193  void Set(const std::string& path, Value* in_value) {
194    PrefService* prefs = g_browser_process->local_state();
195    DCHECK(!prefs->IsManagedPreference(path.c_str()));
196
197    if (!UserManager::Get()->current_user_is_owner()) {
198      LOG(WARNING) << "Changing settings from non-owner, setting=" << path;
199
200      // Revert UI change.
201      CrosSettings::Get()->FireObservers(path.c_str());
202      return;
203    }
204
205    if (IsControlledBooleanSetting(path)) {
206      bool bool_value = false;
207      if (in_value->GetAsBoolean(&bool_value)) {
208        std::string value = bool_value ? kTrueIncantation : kFalseIncantation;
209        SignedSettingsHelper::Get()->StartStorePropertyOp(path, value, this);
210        UpdateCacheBool(path, bool_value, USE_VALUE_SUPPLIED);
211
212        VLOG(1) << "Set cros setting " << path << "=" << value;
213      }
214    } else if (path == kDeviceOwner) {
215      VLOG(1) << "Setting owner is not supported. Please use "
216                 "'UpdateCachedOwner' instead.";
217    } else if (path == kAccountsPrefUsers) {
218      VLOG(1) << "Setting user whitelist is not implemented.  Please use "
219                 "whitelist/unwhitelist instead.";
220    } else {
221      LOG(WARNING) << "Try to set unhandled cros setting " << path;
222    }
223  }
224
225 private:
226  // Listed in upgrade order.
227  enum OwnershipStatus {
228    OWNERSHIP_UNKNOWN = 0,
229    OWNERSHIP_NONE,
230    OWNERSHIP_TAKEN
231  };
232
233  // Used to discriminate different sources of ownership status info.
234  enum OwnershipSource {
235    SOURCE_FETCH,  // Info comes from FetchOwnershipStatus method.
236    SOURCE_OBSERVE // Info comes from Observe method.
237  };
238
239  // upper bound for number of retries to fetch a signed setting.
240  static const int kNumRetriesLimit = 9;
241
242  UserCrosSettingsTrust() : ownership_status_(OWNERSHIP_UNKNOWN),
243                            retries_left_(kNumRetriesLimit) {
244    notification_registrar_.Add(this,
245                                NotificationType::OWNERSHIP_TAKEN,
246                                NotificationService::AllSources());
247    // Start getting ownership status.
248    BrowserThread::PostTask(
249        BrowserThread::FILE,
250        FROM_HERE,
251        NewRunnableMethod(this, &UserCrosSettingsTrust::FetchOwnershipStatus));
252  }
253
254  ~UserCrosSettingsTrust() {
255    if (BrowserThread::CurrentlyOn(BrowserThread::UI) &&
256        CrosLibrary::Get()->EnsureLoaded()) {
257      // Cancels all pending callbacks from us.
258      SignedSettingsHelper::Get()->CancelCallback(this);
259    }
260  }
261
262  void FetchOwnershipStatus() {
263    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
264    OwnershipStatus status =
265        OwnershipService::GetSharedInstance()->IsAlreadyOwned() ?
266        OWNERSHIP_TAKEN : OWNERSHIP_NONE;
267    BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
268        NewRunnableMethod(this, &UserCrosSettingsTrust::SetOwnershipStatus,
269            status, SOURCE_FETCH));
270  }
271
272  void SetOwnershipStatus(OwnershipStatus new_status,
273                          OwnershipSource source) {
274    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
275    DCHECK(new_status == OWNERSHIP_TAKEN || new_status == OWNERSHIP_NONE);
276    if (source == SOURCE_FETCH) {
277      DCHECK(ownership_status_ != OWNERSHIP_NONE);
278      if (ownership_status_ == OWNERSHIP_TAKEN) {
279        // OWNERSHIP_TAKEN notification was observed earlier.
280        return;
281      }
282    }
283    ownership_status_ = new_status;
284    if (source == SOURCE_FETCH) {
285      // Start prefetching Boolean and String preferences.
286      for (size_t i = 0; i < arraysize(kBooleanSettings); ++i)
287        StartFetchingSetting(kBooleanSettings[i]);
288      for (size_t i = 0; i < arraysize(kStringSettings); ++i)
289        StartFetchingSetting(kStringSettings[i]);
290    }
291  }
292
293  // Returns ownership status.
294  // Called on UI thread unlike OwnershipService::IsAlreadyOwned.
295  OwnershipStatus GetOwnershipStatus() {
296    return ownership_status_;
297  }
298
299  void StartFetchingSetting(const std::string& name) {
300    DCHECK(g_browser_process);
301    PrefService* prefs = g_browser_process->local_state();
302    if (!prefs)
303      return;
304    // Do not trust before fetching complete.
305    prefs->ClearPref((name + kTrustedSuffix).c_str());
306    prefs->ScheduleSavePersistentPrefs();
307    if (CrosLibrary::Get()->EnsureLoaded()) {
308      SignedSettingsHelper::Get()->StartRetrieveProperty(name, this);
309    }
310  }
311
312  // Implementation of SignedSettingsHelper::Callback.
313  virtual void OnRetrievePropertyCompleted(SignedSettings::ReturnCode code,
314                                           const std::string& name,
315                                           const std::string& value) {
316    if (!IsControlledBooleanSetting(name) && !IsControlledStringSetting(name)) {
317      NOTREACHED();
318      return;
319    }
320    DCHECK(GetOwnershipStatus() != OWNERSHIP_UNKNOWN);
321
322    PrefService* prefs = g_browser_process->local_state();
323    switch (code) {
324      case SignedSettings::SUCCESS:
325      case SignedSettings::NOT_FOUND:
326      case SignedSettings::KEY_UNAVAILABLE: {
327        bool fallback_to_default = (code == SignedSettings::NOT_FOUND) ||
328            (GetOwnershipStatus() == OWNERSHIP_NONE);
329        DCHECK(fallback_to_default || code == SignedSettings::SUCCESS);
330        if (fallback_to_default)
331          VLOG(1) << "Going default for cros setting " << name;
332        else
333          VLOG(1) << "Retrieved cros setting " << name << "=" << value;
334        if (IsControlledBooleanSetting(name)) {
335          // We assume our boolean settings are true before explicitly set.
336          UpdateCacheBool(name, (value == kTrueIncantation),
337              fallback_to_default ? USE_VALUE_DEFAULT : USE_VALUE_SUPPLIED);
338        } else if (IsControlledStringSetting(name)) {
339          UpdateCacheString(name, value,
340              fallback_to_default ? USE_VALUE_DEFAULT : USE_VALUE_SUPPLIED);
341        }
342        break;
343      }
344      case SignedSettings::OPERATION_FAILED:
345      default: {
346        DCHECK(code == SignedSettings::OPERATION_FAILED);
347        DCHECK(GetOwnershipStatus() == OWNERSHIP_TAKEN);
348        LOG(ERROR) << "On owned device: failed to retrieve cros "
349                      "setting, name=" << name;
350        if (retries_left_ > 0) {
351          retries_left_ -= 1;
352          StartFetchingSetting(name);
353          return;
354        }
355        LOG(ERROR) << "No retries left";
356        if (IsControlledBooleanSetting(name)) {
357          // For boolean settings we can just set safe (false) values
358          // and continue as trusted.
359          UpdateCacheBool(name, false, USE_VALUE_SUPPLIED);
360        } else {
361          prefs->ClearPref((name + kTrustedSuffix).c_str());
362          return;
363        }
364        break;
365      }
366    }
367    prefs->SetBoolean((name + kTrustedSuffix).c_str(), true);
368    {
369      DCHECK(GetOwnershipStatus() != OWNERSHIP_UNKNOWN);
370      std::vector<Task*>& callbacks_vector = callbacks_[name];
371      for (size_t i = 0; i < callbacks_vector.size(); ++i)
372        MessageLoop::current()->PostTask(FROM_HERE, callbacks_vector[i]);
373      callbacks_vector.clear();
374    }
375    if (code == SignedSettings::SUCCESS)
376      CrosSettings::Get()->FireObservers(name.c_str());
377  }
378
379  // Implementation of SignedSettingsHelper::Callback.
380  virtual void OnStorePropertyCompleted(SignedSettings::ReturnCode code,
381                                        const std::string& name,
382                                        const std::string& value) {
383    VLOG(1) << "Store cros setting " << name << "=" << value << ", code="
384            << code;
385
386    // Reload the setting if store op fails.
387    if (code != SignedSettings::SUCCESS)
388      SignedSettingsHelper::Get()->StartRetrieveProperty(name, this);
389  }
390
391  // Implementation of SignedSettingsHelper::Callback.
392  virtual void OnWhitelistCompleted(SignedSettings::ReturnCode code,
393                                    const std::string& email) {
394    VLOG(1) << "Add " << email << " to whitelist, code=" << code;
395
396    // Reload the whitelist on settings op failure.
397    if (code != SignedSettings::SUCCESS)
398      CrosSettings::Get()->FireObservers(kAccountsPrefUsers);
399  }
400
401  // Implementation of SignedSettingsHelper::Callback.
402  virtual void OnUnwhitelistCompleted(SignedSettings::ReturnCode code,
403                                      const std::string& email) {
404    VLOG(1) << "Remove " << email << " from whitelist, code=" << code;
405
406    // Reload the whitelist on settings op failure.
407    if (code != SignedSettings::SUCCESS)
408      CrosSettings::Get()->FireObservers(kAccountsPrefUsers);
409  }
410
411  // NotificationObserver implementation.
412  virtual void Observe(NotificationType type,
413                       const NotificationSource& source,
414                       const NotificationDetails& details) {
415    if (type.value == NotificationType::OWNERSHIP_TAKEN) {
416      SetOwnershipStatus(OWNERSHIP_TAKEN, SOURCE_OBSERVE);
417      notification_registrar_.RemoveAll();
418    } else {
419      NOTREACHED();
420    }
421  }
422
423  // Pending callbacks that need to be invoked after settings verification.
424  base::hash_map< std::string, std::vector< Task* > > callbacks_;
425
426  NotificationRegistrar notification_registrar_;
427  OwnershipStatus ownership_status_;
428
429  // In order to guard against occasional failure to fetch a property
430  // we allow for some number of retries.
431  int retries_left_;
432
433  friend class SignedSettingsHelper;
434  friend struct DefaultSingletonTraits<UserCrosSettingsTrust>;
435
436  DISALLOW_COPY_AND_ASSIGN(UserCrosSettingsTrust);
437};
438
439}  // namespace
440
441}  // namespace chromeos
442
443// We want to use NewRunnableMethod with this class but need to disable
444// reference counting since it is singleton.
445DISABLE_RUNNABLE_METHOD_REFCOUNT(chromeos::UserCrosSettingsTrust);
446
447namespace chromeos {
448
449UserCrosSettingsProvider::UserCrosSettingsProvider() {
450  // Trigger prefetching of settings.
451  UserCrosSettingsTrust::GetInstance();
452}
453
454// static
455void UserCrosSettingsProvider::RegisterPrefs(PrefService* local_state) {
456  for (size_t i = 0; i < arraysize(kBooleanSettings); ++i)
457    RegisterSetting(local_state, kBooleanSettings[i]);
458  for (size_t i = 0; i < arraysize(kStringSettings); ++i)
459    RegisterSetting(local_state, kStringSettings[i]);
460  for (size_t i = 0; i < arraysize(kListSettings); ++i)
461    RegisterSetting(local_state, kListSettings[i]);
462}
463
464bool UserCrosSettingsProvider::RequestTrustedAllowGuest(Task* callback) {
465  return UserCrosSettingsTrust::GetInstance()->RequestTrustedEntity(
466      kAccountsPrefAllowGuest, callback);
467}
468
469bool UserCrosSettingsProvider::RequestTrustedAllowNewUser(Task* callback) {
470  return UserCrosSettingsTrust::GetInstance()->RequestTrustedEntity(
471      kAccountsPrefAllowNewUser, callback);
472}
473
474bool UserCrosSettingsProvider::RequestTrustedShowUsersOnSignin(Task* callback) {
475  return UserCrosSettingsTrust::GetInstance()->RequestTrustedEntity(
476      kAccountsPrefShowUserNamesOnSignIn, callback);
477}
478
479bool UserCrosSettingsProvider::RequestTrustedOwner(Task* callback) {
480  return UserCrosSettingsTrust::GetInstance()->RequestTrustedEntity(
481      kDeviceOwner, callback);
482}
483
484// static
485bool UserCrosSettingsProvider::cached_allow_guest() {
486  // Trigger prefetching if singleton object still does not exist.
487  UserCrosSettingsTrust::GetInstance();
488  return g_browser_process->local_state()->GetBoolean(kAccountsPrefAllowGuest);
489}
490
491// static
492bool UserCrosSettingsProvider::cached_allow_new_user() {
493  // Trigger prefetching if singleton object still does not exist.
494  UserCrosSettingsTrust::GetInstance();
495  return g_browser_process->local_state()->GetBoolean(
496    kAccountsPrefAllowNewUser);
497}
498
499// static
500bool UserCrosSettingsProvider::cached_show_users_on_signin() {
501  // Trigger prefetching if singleton object still does not exist.
502  UserCrosSettingsTrust::GetInstance();
503  return g_browser_process->local_state()->GetBoolean(
504      kAccountsPrefShowUserNamesOnSignIn);
505}
506
507// static
508const ListValue* UserCrosSettingsProvider::cached_whitelist() {
509  PrefService* prefs = g_browser_process->local_state();
510  const ListValue* cached_users = prefs->GetList(kAccountsPrefUsers);
511  if (!prefs->IsManagedPreference(kAccountsPrefUsers)) {
512    if (cached_users == NULL) {
513      // Update whitelist cache.
514      GetUserWhitelist(NULL);
515      cached_users = prefs->GetList(kAccountsPrefUsers);
516    }
517  }
518  if (cached_users == NULL) {
519    NOTREACHED();
520    cached_users = new ListValue;
521  }
522  return cached_users;
523}
524
525// static
526std::string UserCrosSettingsProvider::cached_owner() {
527  // Trigger prefetching if singleton object still does not exist.
528  UserCrosSettingsTrust::GetInstance();
529  if (!g_browser_process || !g_browser_process->local_state())
530    return std::string();
531  return g_browser_process->local_state()->GetString(kDeviceOwner);
532}
533
534// static
535bool UserCrosSettingsProvider::IsEmailInCachedWhitelist(
536    const std::string& email) {
537  const ListValue* whitelist = cached_whitelist();
538  if (whitelist) {
539    StringValue email_value(email);
540    for (ListValue::const_iterator i(whitelist->begin());
541        i != whitelist->end(); ++i) {
542      if ((*i)->Equals(&email_value))
543        return true;
544    }
545  }
546  return false;
547}
548
549void UserCrosSettingsProvider::DoSet(const std::string& path,
550                                     Value* in_value) {
551  UserCrosSettingsTrust::GetInstance()->Set(path, in_value);
552}
553
554bool UserCrosSettingsProvider::Get(const std::string& path,
555                                   Value** out_value) const {
556  if (IsControlledBooleanSetting(path)) {
557    *out_value = CreateSettingsBooleanValue(
558        g_browser_process->local_state()->GetBoolean(path.c_str()),
559        !UserManager::Get()->current_user_is_owner());
560    return true;
561  } else if (path == kAccountsPrefUsers) {
562    ListValue* user_list = new ListValue;
563    GetUserWhitelist(user_list);
564    *out_value = user_list;
565    return true;
566  }
567
568  return false;
569}
570
571bool UserCrosSettingsProvider::HandlesSetting(const std::string& path) {
572  return ::StartsWithASCII(path, "cros.accounts.", true);
573}
574
575void UserCrosSettingsProvider::WhitelistUser(const std::string& email) {
576  SignedSettingsHelper::Get()->StartWhitelistOp(
577      email, true, UserCrosSettingsTrust::GetInstance());
578  PrefService* prefs = g_browser_process->local_state();
579  ListValue* cached_whitelist = prefs->GetMutableList(kAccountsPrefUsers);
580  cached_whitelist->Append(Value::CreateStringValue(email));
581  prefs->ScheduleSavePersistentPrefs();
582}
583
584void UserCrosSettingsProvider::UnwhitelistUser(const std::string& email) {
585  SignedSettingsHelper::Get()->StartWhitelistOp(
586      email, false, UserCrosSettingsTrust::GetInstance());
587
588  PrefService* prefs = g_browser_process->local_state();
589  ListValue* cached_whitelist = prefs->GetMutableList(kAccountsPrefUsers);
590  StringValue email_value(email);
591  if (cached_whitelist->Remove(email_value) != -1)
592    prefs->ScheduleSavePersistentPrefs();
593}
594
595// static
596void UserCrosSettingsProvider::UpdateCachedOwner(const std::string& email) {
597  UpdateCacheString(kDeviceOwner, email, USE_VALUE_SUPPLIED);
598}
599
600}  // namespace chromeos
601