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