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.
5 #include "chrome/browser/chromeos/app_mode/kiosk_app_manager.h"
10 #include "base/bind.h"
11 #include "base/files/file_path.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/prefs/scoped_user_pref_update.h"
17 #include "base/stl_util.h"
18 #include "base/sys_info.h"
19 #include "chrome/browser/browser_process.h"
20 #include "chrome/browser/chromeos/app_mode/kiosk_app_data.h"
21 #include "chrome/browser/chromeos/app_mode/kiosk_app_manager_observer.h"
22 #include "chrome/browser/chromeos/login/users/user_manager.h"
23 #include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
24 #include "chrome/browser/chromeos/policy/device_local_account.h"
25 #include "chrome/browser/chromeos/settings/cros_settings.h"
26 #include "chrome/browser/chromeos/settings/owner_key_util.h"
27 #include "chrome/browser/extensions/external_provider_impl.h"
28 #include "chrome/common/chrome_paths.h"
29 #include "chrome/common/extensions/extension_constants.h"
30 #include "chromeos/chromeos_paths.h"
31 #include "chromeos/cryptohome/async_method_caller.h"
32 #include "chromeos/settings/cros_settings_names.h"
33 #include "content/public/browser/browser_thread.h"
39 // Domain that is used for kiosk-app account IDs.
40 const char kKioskAppAccountDomain[] = "kiosk-apps";
42 std::string GenerateKioskAppAccountId(const std::string& app_id) {
43 return app_id + '@' + kKioskAppAccountDomain;
46 void OnRemoveAppCryptohomeComplete(const std::string& app,
48 cryptohome::MountError return_code) {
50 LOG(ERROR) << "Remove cryptohome for " << app
51 << " failed, return code: " << return_code;
55 // Check for presence of machine owner public key file.
56 void CheckOwnerFilePresence(bool *present) {
57 scoped_refptr<OwnerKeyUtil> util = OwnerKeyUtil::Create();
58 *present = util->IsPublicKeyPresent();
61 scoped_refptr<base::SequencedTaskRunner> GetBackgroundTaskRunner() {
62 base::SequencedWorkerPool* pool = content::BrowserThread::GetBlockingPool();
64 return pool->GetSequencedTaskRunnerWithShutdownBehavior(
65 pool->GetSequenceToken(), base::SequencedWorkerPool::SKIP_ON_SHUTDOWN);
71 const char KioskAppManager::kKioskDictionaryName[] = "kiosk";
72 const char KioskAppManager::kKeyApps[] = "apps";
73 const char KioskAppManager::kKeyAutoLoginState[] = "auto_login_state";
74 const char KioskAppManager::kIconCacheDir[] = "kiosk/icon";
75 const char KioskAppManager::kCrxCacheDir[] = "kiosk/crx";
78 static base::LazyInstance<KioskAppManager> instance = LAZY_INSTANCE_INITIALIZER;
79 KioskAppManager* KioskAppManager::Get() {
80 return instance.Pointer();
84 void KioskAppManager::Shutdown() {
88 instance.Pointer()->CleanUp();
92 void KioskAppManager::RegisterPrefs(PrefRegistrySimple* registry) {
93 registry->RegisterDictionaryPref(kKioskDictionaryName);
96 KioskAppManager::App::App(const KioskAppData& data, bool is_extension_pending)
97 : app_id(data.app_id()),
98 user_id(data.user_id()),
101 is_loading(data.IsLoading() || is_extension_pending) {
104 KioskAppManager::App::App() : is_loading(false) {}
105 KioskAppManager::App::~App() {}
107 std::string KioskAppManager::GetAutoLaunchApp() const {
108 return auto_launch_app_id_;
111 void KioskAppManager::SetAutoLaunchApp(const std::string& app_id) {
112 SetAutoLoginState(AUTOLOGIN_REQUESTED);
113 // Clean first, so the proper change callbacks are triggered even
114 // if we are only changing AutoLoginState here.
115 if (!auto_launch_app_id_.empty()) {
116 CrosSettings::Get()->SetString(kAccountsPrefDeviceLocalAccountAutoLoginId,
120 CrosSettings::Get()->SetString(
121 kAccountsPrefDeviceLocalAccountAutoLoginId,
122 app_id.empty() ? std::string() : GenerateKioskAppAccountId(app_id));
123 CrosSettings::Get()->SetInteger(
124 kAccountsPrefDeviceLocalAccountAutoLoginDelay, 0);
127 void KioskAppManager::EnableConsumerKioskAutoLaunch(
128 const KioskAppManager::EnableKioskAutoLaunchCallback& callback) {
129 policy::BrowserPolicyConnectorChromeOS* connector =
130 g_browser_process->platform_part()->browser_policy_connector_chromeos();
131 connector->GetInstallAttributes()->LockDevice(
132 std::string(), // user
133 policy::DEVICE_MODE_CONSUMER_KIOSK_AUTOLAUNCH,
134 std::string(), // device_id
136 &KioskAppManager::OnLockDevice, base::Unretained(this), callback));
139 void KioskAppManager::GetConsumerKioskAutoLaunchStatus(
140 const KioskAppManager::GetConsumerKioskAutoLaunchStatusCallback& callback) {
141 policy::BrowserPolicyConnectorChromeOS* connector =
142 g_browser_process->platform_part()->browser_policy_connector_chromeos();
143 connector->GetInstallAttributes()->ReadImmutableAttributes(
144 base::Bind(&KioskAppManager::OnReadImmutableAttributes,
145 base::Unretained(this),
149 bool KioskAppManager::IsConsumerKioskDeviceWithAutoLaunch() {
150 policy::BrowserPolicyConnectorChromeOS* connector =
151 g_browser_process->platform_part()->browser_policy_connector_chromeos();
152 return connector->GetInstallAttributes() &&
153 connector->GetInstallAttributes()
154 ->IsConsumerKioskDeviceWithAutoLaunch();
157 void KioskAppManager::OnLockDevice(
158 const KioskAppManager::EnableKioskAutoLaunchCallback& callback,
159 policy::EnterpriseInstallAttributes::LockResult result) {
160 if (callback.is_null())
163 callback.Run(result == policy::EnterpriseInstallAttributes::LOCK_SUCCESS);
166 void KioskAppManager::OnOwnerFileChecked(
167 const KioskAppManager::GetConsumerKioskAutoLaunchStatusCallback& callback,
168 bool* owner_present) {
169 ownership_established_ = *owner_present;
171 if (callback.is_null())
174 // If we have owner already established on the machine, don't let
175 // consumer kiosk to be enabled.
176 if (ownership_established_)
177 callback.Run(CONSUMER_KIOSK_AUTO_LAUNCH_DISABLED);
179 callback.Run(CONSUMER_KIOSK_AUTO_LAUNCH_CONFIGURABLE);
182 void KioskAppManager::OnReadImmutableAttributes(
183 const KioskAppManager::GetConsumerKioskAutoLaunchStatusCallback&
185 if (callback.is_null())
188 ConsumerKioskAutoLaunchStatus status =
189 CONSUMER_KIOSK_AUTO_LAUNCH_DISABLED;
190 policy::BrowserPolicyConnectorChromeOS* connector =
191 g_browser_process->platform_part()->browser_policy_connector_chromeos();
192 policy::EnterpriseInstallAttributes* attributes =
193 connector->GetInstallAttributes();
194 switch (attributes->GetMode()) {
195 case policy::DEVICE_MODE_NOT_SET: {
196 if (!base::SysInfo::IsRunningOnChromeOS()) {
197 status = CONSUMER_KIOSK_AUTO_LAUNCH_CONFIGURABLE;
198 } else if (!ownership_established_) {
199 bool* owner_present = new bool(false);
200 content::BrowserThread::PostBlockingPoolTaskAndReply(
202 base::Bind(&CheckOwnerFilePresence,
204 base::Bind(&KioskAppManager::OnOwnerFileChecked,
205 base::Unretained(this),
207 base::Owned(owner_present)));
212 case policy::DEVICE_MODE_CONSUMER_KIOSK_AUTOLAUNCH:
213 status = CONSUMER_KIOSK_AUTO_LAUNCH_ENABLED;
219 callback.Run(status);
222 void KioskAppManager::SetEnableAutoLaunch(bool value) {
223 SetAutoLoginState(value ? AUTOLOGIN_APPROVED : AUTOLOGIN_REJECTED);
226 bool KioskAppManager::IsAutoLaunchRequested() const {
227 if (GetAutoLaunchApp().empty())
230 // Apps that were installed by the policy don't require machine owner
231 // consent through UI.
232 policy::BrowserPolicyConnectorChromeOS* connector =
233 g_browser_process->platform_part()->browser_policy_connector_chromeos();
234 if (connector->IsEnterpriseManaged())
237 return GetAutoLoginState() == AUTOLOGIN_REQUESTED;
240 bool KioskAppManager::IsAutoLaunchEnabled() const {
241 if (GetAutoLaunchApp().empty())
244 // Apps that were installed by the policy don't require machine owner
245 // consent through UI.
246 policy::BrowserPolicyConnectorChromeOS* connector =
247 g_browser_process->platform_part()->browser_policy_connector_chromeos();
248 if (connector->IsEnterpriseManaged())
251 return GetAutoLoginState() == AUTOLOGIN_APPROVED;
254 void KioskAppManager::AddApp(const std::string& app_id) {
255 std::vector<policy::DeviceLocalAccount> device_local_accounts =
256 policy::GetDeviceLocalAccounts(CrosSettings::Get());
258 // Don't insert the app if it's already in the list.
259 for (std::vector<policy::DeviceLocalAccount>::const_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) {
268 // Add the new account.
269 device_local_accounts.push_back(policy::DeviceLocalAccount(
270 policy::DeviceLocalAccount::TYPE_KIOSK_APP,
271 GenerateKioskAppAccountId(app_id),
274 policy::SetDeviceLocalAccounts(CrosSettings::Get(), device_local_accounts);
277 void KioskAppManager::RemoveApp(const std::string& app_id) {
278 // Resets auto launch app if it is the removed app.
279 if (auto_launch_app_id_ == app_id)
280 SetAutoLaunchApp(std::string());
282 std::vector<policy::DeviceLocalAccount> device_local_accounts =
283 policy::GetDeviceLocalAccounts(CrosSettings::Get());
284 if (device_local_accounts.empty())
287 // Remove entries that match |app_id|.
288 for (std::vector<policy::DeviceLocalAccount>::iterator
289 it = device_local_accounts.begin();
290 it != device_local_accounts.end(); ++it) {
291 if (it->type == policy::DeviceLocalAccount::TYPE_KIOSK_APP &&
292 it->kiosk_app_id == app_id) {
293 device_local_accounts.erase(it);
298 policy::SetDeviceLocalAccounts(CrosSettings::Get(), device_local_accounts);
301 void KioskAppManager::GetApps(Apps* apps) const {
303 apps->reserve(apps_.size());
304 for (size_t i = 0; i < apps_.size(); ++i) {
305 const KioskAppData& app_data = *apps_[i];
306 if (app_data.status() != KioskAppData::STATUS_ERROR)
308 app_data, external_cache_->IsExtensionPending(app_data.app_id())));
312 bool KioskAppManager::GetApp(const std::string& app_id, App* app) const {
313 const KioskAppData* data = GetAppData(app_id);
317 *app = App(*data, external_cache_->IsExtensionPending(app_id));
321 const base::RefCountedString* KioskAppManager::GetAppRawIcon(
322 const std::string& app_id) const {
323 const KioskAppData* data = GetAppData(app_id);
327 return data->raw_icon();
330 bool KioskAppManager::GetDisableBailoutShortcut() const {
332 if (CrosSettings::Get()->GetBoolean(
333 kAccountsPrefDeviceLocalAccountAutoLoginBailoutEnabled, &enable)) {
340 void KioskAppManager::ClearAppData(const std::string& app_id) {
341 KioskAppData* app_data = GetAppDataMutable(app_id);
345 app_data->ClearCache();
348 void KioskAppManager::UpdateAppDataFromProfile(
349 const std::string& app_id,
351 const extensions::Extension* app) {
352 KioskAppData* app_data = GetAppDataMutable(app_id);
356 app_data->LoadFromInstalledApp(profile, app);
359 void KioskAppManager::RetryFailedAppDataFetch() {
360 for (size_t i = 0; i < apps_.size(); ++i) {
361 if (apps_[i]->status() == KioskAppData::STATUS_ERROR)
366 void KioskAppManager::AddObserver(KioskAppManagerObserver* observer) {
367 observers_.AddObserver(observer);
370 void KioskAppManager::RemoveObserver(KioskAppManagerObserver* observer) {
371 observers_.RemoveObserver(observer);
374 KioskAppManager::KioskAppManager() : ownership_established_(false) {
375 base::FilePath cache_dir;
376 GetCrxCacheDir(&cache_dir);
377 external_cache_.reset(
378 new ExternalCache(cache_dir,
379 g_browser_process->system_request_context(),
380 GetBackgroundTaskRunner(),
382 true /* always_check_updates */,
383 false /* wait_for_cache_initialization */));
386 local_accounts_subscription_ =
387 CrosSettings::Get()->AddSettingsObserver(
388 kAccountsPrefDeviceLocalAccounts,
389 base::Bind(&KioskAppManager::UpdateAppData, base::Unretained(this)));
390 local_account_auto_login_id_subscription_ =
391 CrosSettings::Get()->AddSettingsObserver(
392 kAccountsPrefDeviceLocalAccountAutoLoginId,
393 base::Bind(&KioskAppManager::UpdateAppData, base::Unretained(this)));
396 KioskAppManager::~KioskAppManager() {}
398 void KioskAppManager::CleanUp() {
399 local_accounts_subscription_.reset();
400 local_account_auto_login_id_subscription_.reset();
402 external_cache_.reset();
405 const KioskAppData* KioskAppManager::GetAppData(
406 const std::string& app_id) const {
407 for (size_t i = 0; i < apps_.size(); ++i) {
408 const KioskAppData* data = apps_[i];
409 if (data->app_id() == app_id)
416 KioskAppData* KioskAppManager::GetAppDataMutable(const std::string& app_id) {
417 return const_cast<KioskAppData*>(GetAppData(app_id));
420 void KioskAppManager::UpdateAppData() {
421 // Gets app id to data mapping for existing apps.
422 std::map<std::string, KioskAppData*> old_apps;
423 for (size_t i = 0; i < apps_.size(); ++i)
424 old_apps[apps_[i]->app_id()] = apps_[i];
425 apps_.weak_clear(); // |old_apps| takes ownership
427 auto_launch_app_id_.clear();
428 std::string auto_login_account_id;
429 CrosSettings::Get()->GetString(kAccountsPrefDeviceLocalAccountAutoLoginId,
430 &auto_login_account_id);
432 // Re-populates |apps_| and reuses existing KioskAppData when possible.
433 const std::vector<policy::DeviceLocalAccount> device_local_accounts =
434 policy::GetDeviceLocalAccounts(CrosSettings::Get());
435 for (std::vector<policy::DeviceLocalAccount>::const_iterator
436 it = device_local_accounts.begin();
437 it != device_local_accounts.end(); ++it) {
438 if (it->type != policy::DeviceLocalAccount::TYPE_KIOSK_APP)
441 if (it->account_id == auto_login_account_id)
442 auto_launch_app_id_ = it->kiosk_app_id;
444 // TODO(mnissler): Support non-CWS update URLs.
446 std::map<std::string, KioskAppData*>::iterator old_it =
447 old_apps.find(it->kiosk_app_id);
448 if (old_it != old_apps.end()) {
449 apps_.push_back(old_it->second);
450 old_apps.erase(old_it);
452 KioskAppData* new_app =
453 new KioskAppData(this, it->kiosk_app_id, it->user_id);
454 apps_.push_back(new_app); // Takes ownership of |new_app|.
459 // Clears cache and deletes the remaining old data.
460 std::vector<std::string> apps_to_remove;
461 for (std::map<std::string, KioskAppData*>::iterator it = old_apps.begin();
462 it != old_apps.end(); ++it) {
463 it->second->ClearCache();
464 cryptohome::AsyncMethodCaller::GetInstance()->AsyncRemove(
465 it->second->user_id(),
466 base::Bind(&OnRemoveAppCryptohomeComplete, it->first));
467 apps_to_remove.push_back(it->second->app_id());
469 STLDeleteValues(&old_apps);
470 external_cache_->RemoveExtensions(apps_to_remove);
472 // Request external_cache_ to download new apps and update the existing
474 scoped_ptr<base::DictionaryValue> prefs(new base::DictionaryValue);
475 for (size_t i = 0; i < apps_.size(); ++i)
476 prefs->Set(apps_[i]->app_id(), new base::DictionaryValue);
477 external_cache_->UpdateExtensionsList(prefs.Pass());
479 RetryFailedAppDataFetch();
481 FOR_EACH_OBSERVER(KioskAppManagerObserver, observers_,
482 OnKioskAppsSettingsChanged());
485 void KioskAppManager::GetKioskAppIconCacheDir(base::FilePath* cache_dir) {
486 base::FilePath user_data_dir;
487 CHECK(PathService::Get(chrome::DIR_USER_DATA, &user_data_dir));
488 *cache_dir = user_data_dir.AppendASCII(kIconCacheDir);
491 void KioskAppManager::OnKioskAppDataChanged(const std::string& app_id) {
492 FOR_EACH_OBSERVER(KioskAppManagerObserver,
494 OnKioskAppDataChanged(app_id));
497 void KioskAppManager::OnKioskAppDataLoadFailure(const std::string& app_id) {
498 FOR_EACH_OBSERVER(KioskAppManagerObserver,
500 OnKioskAppDataLoadFailure(app_id));
503 void KioskAppManager::OnExtensionListsUpdated(
504 const base::DictionaryValue* prefs) {
507 void KioskAppManager::OnExtensionLoadedInCache(const std::string& id) {
508 KioskAppData* app_data = GetAppDataMutable(id);
511 OnKioskAppDataChanged(id);
514 void KioskAppManager::OnExtensionDownloadFailed(
515 const std::string& id,
516 extensions::ExtensionDownloaderDelegate::Error error) {
517 KioskAppData* app_data = GetAppDataMutable(id);
520 OnKioskAppDataLoadFailure(id);
523 KioskAppManager::AutoLoginState KioskAppManager::GetAutoLoginState() const {
524 PrefService* prefs = g_browser_process->local_state();
525 const base::DictionaryValue* dict =
526 prefs->GetDictionary(KioskAppManager::kKioskDictionaryName);
528 if (!dict->GetInteger(kKeyAutoLoginState, &value))
529 return AUTOLOGIN_NONE;
531 return static_cast<AutoLoginState>(value);
534 void KioskAppManager::SetAutoLoginState(AutoLoginState state) {
535 PrefService* prefs = g_browser_process->local_state();
536 DictionaryPrefUpdate dict_update(prefs,
537 KioskAppManager::kKioskDictionaryName);
538 dict_update->SetInteger(kKeyAutoLoginState, state);
539 prefs->CommitPendingWrite();
542 void KioskAppManager::GetCrxCacheDir(base::FilePath* cache_dir) {
543 base::FilePath user_data_dir;
544 CHECK(PathService::Get(chrome::DIR_USER_DATA, &user_data_dir));
545 *cache_dir = user_data_dir.AppendASCII(kCrxCacheDir);
548 bool KioskAppManager::GetCachedCrx(const std::string& app_id,
549 base::FilePath* file_path,
550 std::string* version) {
551 return external_cache_->GetExtension(app_id, file_path, version);
554 } // namespace chromeos