kiosk_app_manager.cc revision 7d4cd473f85ac64c3747c96c277f9e506a0d2246
1// Copyright 2013 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/app_mode/kiosk_app_manager.h"
6
7#include <map>
8#include <set>
9
10#include "base/bind.h"
11#include "base/chromeos/chromeos_version.h"
12#include "base/logging.h"
13#include "base/path_service.h"
14#include "base/prefs/pref_registry_simple.h"
15#include "base/prefs/pref_service.h"
16#include "base/stl_util.h"
17#include "chrome/browser/browser_process.h"
18#include "chrome/browser/chromeos/app_mode/kiosk_app_data.h"
19#include "chrome/browser/chromeos/app_mode/kiosk_app_manager.h"
20#include "chrome/browser/chromeos/app_mode/kiosk_app_manager_observer.h"
21#include "chrome/browser/chromeos/login/user_manager.h"
22#include "chrome/browser/chromeos/policy/device_local_account.h"
23#include "chrome/browser/chromeos/settings/cros_settings.h"
24#include "chrome/browser/chromeos/settings/cros_settings_names.h"
25#include "chrome/browser/chromeos/settings/owner_key_util.h"
26#include "chrome/browser/policy/browser_policy_connector.h"
27#include "chrome/browser/prefs/scoped_user_pref_update.h"
28#include "chrome/common/chrome_notification_types.h"
29#include "chrome/common/chrome_paths.h"
30#include "chromeos/cryptohome/async_method_caller.h"
31#include "chromeos/cryptohome/cryptohome_library.h"
32#include "content/public/browser/browser_thread.h"
33
34namespace chromeos {
35
36namespace {
37
38// Domain that is used for kiosk-app account IDs.
39const char kKioskAppAccountDomain[] = "kiosk-apps";
40
41std::string GenerateKioskAppAccountId(const std::string& app_id) {
42  return app_id + '@' + kKioskAppAccountDomain;
43}
44
45void OnRemoveAppCryptohomeComplete(const std::string& app,
46                                   bool success,
47                                   cryptohome::MountError return_code) {
48  if (!success) {
49    LOG(ERROR) << "Remove cryptohome for " << app
50        << " failed, return code: " << return_code;
51  }
52}
53
54// Check for presence of machine owner public key file.
55void CheckOwnerFilePresence(bool *present) {
56  scoped_refptr<OwnerKeyUtil> util = OwnerKeyUtil::Create();
57  *present = util->IsPublicKeyPresent();
58}
59
60}  // namespace
61
62// static
63const char KioskAppManager::kKioskDictionaryName[] = "kiosk";
64const char KioskAppManager::kKeyApps[] = "apps";
65const char KioskAppManager::kKeyAutoLoginState[] = "auto_login_state";
66const char KioskAppManager::kIconCacheDir[] = "kiosk";
67
68// static
69static base::LazyInstance<KioskAppManager> instance = LAZY_INSTANCE_INITIALIZER;
70KioskAppManager* KioskAppManager::Get() {
71  return instance.Pointer();
72}
73
74// static
75void KioskAppManager::Shutdown() {
76  if (instance == NULL)
77    return;
78
79  instance.Pointer()->CleanUp();
80}
81
82// static
83void KioskAppManager::RegisterPrefs(PrefRegistrySimple* registry) {
84  registry->RegisterDictionaryPref(kKioskDictionaryName);
85}
86
87KioskAppManager::App::App(const KioskAppData& data)
88    : app_id(data.app_id()),
89      user_id(data.user_id()),
90      name(data.name()),
91      icon(data.icon()),
92      is_loading(data.IsLoading()) {
93}
94
95KioskAppManager::App::App() : is_loading(false) {}
96KioskAppManager::App::~App() {}
97
98std::string KioskAppManager::GetAutoLaunchApp() const {
99  return auto_launch_app_id_;
100}
101
102void KioskAppManager::SetAutoLaunchApp(const std::string& app_id) {
103  SetAutoLoginState(AUTOLOGIN_REQUESTED);
104  // Clean first, so the proper change notifications are triggered even
105  // if we are only changing AutoLoginState here.
106  if (!auto_launch_app_id_.empty()) {
107    CrosSettings::Get()->SetString(kAccountsPrefDeviceLocalAccountAutoLoginId,
108                                   std::string());
109  }
110
111  CrosSettings::Get()->SetString(
112      kAccountsPrefDeviceLocalAccountAutoLoginId,
113      app_id.empty() ? std::string() : GenerateKioskAppAccountId(app_id));
114  CrosSettings::Get()->SetInteger(
115      kAccountsPrefDeviceLocalAccountAutoLoginDelay, 0);
116}
117
118void KioskAppManager::EnableConsumerModeKiosk(
119    const KioskAppManager::EnableKioskModeCallback& callback) {
120  g_browser_process->browser_policy_connector()->GetInstallAttributes()->
121      LockDevice(std::string(),  // user
122                 policy::DEVICE_MODE_CONSUMER_KIOSK,
123                 std::string(),  // device_id
124                 base::Bind(&KioskAppManager::OnLockDevice,
125                            base::Unretained(this),
126                            callback));
127}
128
129void KioskAppManager::GetConsumerKioskModeStatus(
130    const KioskAppManager::GetConsumerKioskModeStatusCallback& callback) {
131  g_browser_process->browser_policy_connector()->GetInstallAttributes()->
132      ReadImmutableAttributes(
133          base::Bind(&KioskAppManager::OnReadImmutableAttributes,
134                     base::Unretained(this),
135                     callback));
136}
137
138void KioskAppManager::OnLockDevice(
139    const KioskAppManager::EnableKioskModeCallback& callback,
140    policy::EnterpriseInstallAttributes::LockResult result) {
141  if (callback.is_null())
142    return;
143
144  callback.Run(result == policy::EnterpriseInstallAttributes::LOCK_SUCCESS);
145}
146
147void KioskAppManager::OnOwnerFileChecked(
148    const KioskAppManager::GetConsumerKioskModeStatusCallback& callback,
149    bool *owner_present) {
150  ownership_established_ = *owner_present;
151
152  if (callback.is_null())
153    return;
154
155  // If we have owner already established on the machine, don't let
156  // consumer kiosk to be enabled.
157  if (ownership_established_)
158    callback.Run(CONSUMER_KIOSK_MODE_DISABLED);
159  else
160    callback.Run(CONSUMER_KIOSK_MODE_CONFIGURABLE);
161}
162
163void KioskAppManager::OnReadImmutableAttributes(
164    const KioskAppManager::GetConsumerKioskModeStatusCallback& callback) {
165  if (callback.is_null())
166    return;
167
168  ConsumerKioskModeStatus status = CONSUMER_KIOSK_MODE_DISABLED;
169  policy::EnterpriseInstallAttributes* attributes =
170      g_browser_process->browser_policy_connector()->GetInstallAttributes();
171  switch (attributes->GetMode()) {
172    case policy::DEVICE_MODE_NOT_SET: {
173      if (!base::chromeos::IsRunningOnChromeOS()) {
174        status = CONSUMER_KIOSK_MODE_CONFIGURABLE;
175      } else if (!ownership_established_) {
176        bool* owner_present = new bool(false);
177        content::BrowserThread::PostBlockingPoolTaskAndReply(
178            FROM_HERE,
179            base::Bind(&CheckOwnerFilePresence,
180                       owner_present),
181            base::Bind(&KioskAppManager::OnOwnerFileChecked,
182                       base::Unretained(this),
183                       callback,
184                       base::Owned(owner_present)));
185        return;
186      }
187      break;
188    }
189    case policy::DEVICE_MODE_CONSUMER_KIOSK:
190      status = CONSUMER_KIOSK_MODE_ENABLED;
191      break;
192    default:
193      break;
194  }
195
196  callback.Run(status);
197}
198
199void KioskAppManager::SetEnableAutoLaunch(bool value) {
200  SetAutoLoginState(value ? AUTOLOGIN_APPROVED : AUTOLOGIN_REJECTED);
201}
202
203bool KioskAppManager::IsAutoLaunchRequested() const {
204  if (GetAutoLaunchApp().empty())
205    return false;
206
207  // Apps that were installed by the policy don't require machine owner
208  // consent through UI.
209  if (g_browser_process->browser_policy_connector()->IsEnterpriseManaged())
210    return false;
211
212  return GetAutoLoginState() == AUTOLOGIN_REQUESTED;
213}
214
215bool KioskAppManager::IsAutoLaunchEnabled() const {
216  if (GetAutoLaunchApp().empty())
217    return false;
218
219  // Apps that were installed by the policy don't require machine owner
220  // consent through UI.
221  if (g_browser_process->browser_policy_connector()->IsEnterpriseManaged())
222    return true;
223
224  return GetAutoLoginState() == AUTOLOGIN_APPROVED;
225}
226
227void KioskAppManager::AddApp(const std::string& app_id) {
228  std::vector<policy::DeviceLocalAccount> device_local_accounts =
229      policy::GetDeviceLocalAccounts(CrosSettings::Get());
230
231  // Don't insert the app if it's already in the list.
232  for (std::vector<policy::DeviceLocalAccount>::const_iterator
233           it = device_local_accounts.begin();
234       it != device_local_accounts.end(); ++it) {
235    if (it->type == policy::DeviceLocalAccount::TYPE_KIOSK_APP &&
236        it->kiosk_app_id == app_id) {
237      return;
238    }
239  }
240
241  // Add the new account.
242  device_local_accounts.push_back(policy::DeviceLocalAccount(
243      policy::DeviceLocalAccount::TYPE_KIOSK_APP,
244      GenerateKioskAppAccountId(app_id),
245      app_id,
246      std::string()));
247
248  policy::SetDeviceLocalAccounts(CrosSettings::Get(), device_local_accounts);
249}
250
251void KioskAppManager::RemoveApp(const std::string& app_id) {
252  std::vector<policy::DeviceLocalAccount> device_local_accounts =
253      policy::GetDeviceLocalAccounts(CrosSettings::Get());
254  if (device_local_accounts.empty())
255    return;
256
257  // Remove entries that match |app_id|.
258  for (std::vector<policy::DeviceLocalAccount>::iterator
259           it = device_local_accounts.begin();
260       it != device_local_accounts.end(); ++it) {
261    if (it->type == policy::DeviceLocalAccount::TYPE_KIOSK_APP &&
262        it->kiosk_app_id == app_id) {
263      device_local_accounts.erase(it);
264      break;
265    }
266  }
267
268  policy::SetDeviceLocalAccounts(CrosSettings::Get(), device_local_accounts);
269}
270
271void KioskAppManager::GetApps(Apps* apps) const {
272  apps->reserve(apps_.size());
273  for (size_t i = 0; i < apps_.size(); ++i)
274    apps->push_back(App(*apps_[i]));
275}
276
277bool KioskAppManager::GetApp(const std::string& app_id, App* app) const {
278  const KioskAppData* data = GetAppData(app_id);
279  if (!data)
280    return false;
281
282  *app = App(*data);
283  return true;
284}
285
286const base::RefCountedString* KioskAppManager::GetAppRawIcon(
287    const std::string& app_id) const {
288  const KioskAppData* data = GetAppData(app_id);
289  if (!data)
290    return NULL;
291
292  return data->raw_icon();
293}
294
295bool KioskAppManager::GetDisableBailoutShortcut() const {
296  bool enable;
297  if (CrosSettings::Get()->GetBoolean(
298          kAccountsPrefDeviceLocalAccountAutoLoginBailoutEnabled, &enable)) {
299    return !enable;
300  }
301
302  return false;
303}
304
305void KioskAppManager::AddObserver(KioskAppManagerObserver* observer) {
306  observers_.AddObserver(observer);
307}
308
309void KioskAppManager::RemoveObserver(KioskAppManagerObserver* observer) {
310  observers_.RemoveObserver(observer);
311}
312
313KioskAppManager::KioskAppManager() : ownership_established_(false) {
314  UpdateAppData();
315  CrosSettings::Get()->AddSettingsObserver(
316      kAccountsPrefDeviceLocalAccounts, this);
317  CrosSettings::Get()->AddSettingsObserver(
318      kAccountsPrefDeviceLocalAccountAutoLoginId, this);
319}
320
321KioskAppManager::~KioskAppManager() {}
322
323void KioskAppManager::CleanUp() {
324  CrosSettings::Get()->RemoveSettingsObserver(
325      kAccountsPrefDeviceLocalAccounts, this);
326  CrosSettings::Get()->RemoveSettingsObserver(
327      kAccountsPrefDeviceLocalAccountAutoLoginId, this);
328  apps_.clear();
329}
330
331const KioskAppData* KioskAppManager::GetAppData(
332    const std::string& app_id) const {
333  for (size_t i = 0; i < apps_.size(); ++i) {
334    const KioskAppData* data = apps_[i];
335    if (data->app_id() == app_id)
336      return data;
337  }
338
339  return NULL;
340}
341
342void KioskAppManager::UpdateAppData() {
343  // Gets app id to data mapping for existing apps.
344  std::map<std::string, KioskAppData*> old_apps;
345  for (size_t i = 0; i < apps_.size(); ++i)
346    old_apps[apps_[i]->app_id()] = apps_[i];
347  apps_.weak_clear();  // |old_apps| takes ownership
348
349  auto_launch_app_id_.clear();
350  std::string auto_login_account_id;
351  CrosSettings::Get()->GetString(kAccountsPrefDeviceLocalAccountAutoLoginId,
352                                 &auto_login_account_id);
353
354  // Re-populates |apps_| and reuses existing KioskAppData when possible.
355  const std::vector<policy::DeviceLocalAccount> device_local_accounts =
356      policy::GetDeviceLocalAccounts(CrosSettings::Get());
357  for (std::vector<policy::DeviceLocalAccount>::const_iterator
358           it = device_local_accounts.begin();
359       it != device_local_accounts.end(); ++it) {
360    if (it->type != policy::DeviceLocalAccount::TYPE_KIOSK_APP)
361      continue;
362
363    if (it->account_id == auto_login_account_id)
364      auto_launch_app_id_ = it->kiosk_app_id;
365
366    // TODO(mnissler): Support non-CWS update URLs.
367
368    std::map<std::string, KioskAppData*>::iterator old_it =
369        old_apps.find(it->kiosk_app_id);
370    if (old_it != old_apps.end()) {
371      apps_.push_back(old_it->second);
372      old_apps.erase(old_it);
373    } else {
374      KioskAppData* new_app =
375          new KioskAppData(this, it->kiosk_app_id, it->user_id);
376      apps_.push_back(new_app);  // Takes ownership of |new_app|.
377      new_app->Load();
378    }
379  }
380
381  // Clears cache and deletes the remaining old data.
382  for (std::map<std::string, KioskAppData*>::iterator it = old_apps.begin();
383       it != old_apps.end(); ++it) {
384    it->second->ClearCache();
385    cryptohome::AsyncMethodCaller::GetInstance()->AsyncRemove(
386        it->first, base::Bind(&OnRemoveAppCryptohomeComplete, it->first));
387  }
388  STLDeleteValues(&old_apps);
389
390  FOR_EACH_OBSERVER(KioskAppManagerObserver, observers_,
391                    OnKioskAppsSettingsChanged());
392}
393
394void KioskAppManager::Observe(int type,
395                              const content::NotificationSource& source,
396                              const content::NotificationDetails& details) {
397  DCHECK_EQ(chrome::NOTIFICATION_SYSTEM_SETTING_CHANGED, type);
398  UpdateAppData();
399}
400
401void KioskAppManager::GetKioskAppIconCacheDir(base::FilePath* cache_dir) {
402  base::FilePath user_data_dir;
403  CHECK(PathService::Get(chrome::DIR_USER_DATA, &user_data_dir));
404  *cache_dir = user_data_dir.AppendASCII(kIconCacheDir);
405}
406
407void KioskAppManager::OnKioskAppDataChanged(const std::string& app_id) {
408  FOR_EACH_OBSERVER(KioskAppManagerObserver,
409                    observers_,
410                    OnKioskAppDataChanged(app_id));
411}
412
413void KioskAppManager::OnKioskAppDataLoadFailure(const std::string& app_id) {
414  FOR_EACH_OBSERVER(KioskAppManagerObserver,
415                    observers_,
416                    OnKioskAppDataLoadFailure(app_id));
417  RemoveApp(app_id);
418}
419
420KioskAppManager::AutoLoginState KioskAppManager::GetAutoLoginState() const {
421  PrefService* prefs = g_browser_process->local_state();
422  const base::DictionaryValue* dict =
423      prefs->GetDictionary(KioskAppManager::kKioskDictionaryName);
424  int value;
425  if (!dict->GetInteger(kKeyAutoLoginState, &value))
426    return AUTOLOGIN_NONE;
427
428  return static_cast<AutoLoginState>(value);
429}
430
431void KioskAppManager::SetAutoLoginState(AutoLoginState state) {
432  PrefService* prefs = g_browser_process->local_state();
433  DictionaryPrefUpdate dict_update(prefs,
434                                   KioskAppManager::kKioskDictionaryName);
435  dict_update->SetInteger(kKeyAutoLoginState, state);
436  prefs->CommitPendingWrite();
437}
438
439}  // namespace chromeos
440