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/files/file_util.h"
13 #include "base/logging.h"
14 #include "base/path_service.h"
15 #include "base/prefs/pref_registry_simple.h"
16 #include "base/prefs/pref_service.h"
17 #include "base/prefs/scoped_user_pref_update.h"
18 #include "base/stl_util.h"
19 #include "base/sys_info.h"
20 #include "chrome/browser/browser_process.h"
21 #include "chrome/browser/chromeos/app_mode/kiosk_app_data.h"
22 #include "chrome/browser/chromeos/app_mode/kiosk_app_external_loader.h"
23 #include "chrome/browser/chromeos/app_mode/kiosk_app_manager_observer.h"
24 #include "chrome/browser/chromeos/app_mode/kiosk_external_updater.h"
25 #include "chrome/browser/chromeos/ownership/owner_settings_service_chromeos_factory.h"
26 #include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
27 #include "chrome/browser/chromeos/policy/device_local_account.h"
28 #include "chrome/browser/chromeos/settings/cros_settings.h"
29 #include "chrome/browser/extensions/external_loader.h"
30 #include "chrome/browser/extensions/external_provider_impl.h"
31 #include "chrome/common/chrome_paths.h"
32 #include "chrome/common/extensions/extension_constants.h"
33 #include "chromeos/chromeos_paths.h"
34 #include "chromeos/cryptohome/async_method_caller.h"
35 #include "chromeos/settings/cros_settings_names.h"
36 #include "components/ownership/owner_key_util.h"
37 #include "content/public/browser/browser_thread.h"
43 // Domain that is used for kiosk-app account IDs.
44 const char kKioskAppAccountDomain[] = "kiosk-apps";
46 std::string GenerateKioskAppAccountId(const std::string& app_id) {
47 return app_id + '@' + kKioskAppAccountDomain;
50 void OnRemoveAppCryptohomeComplete(const std::string& app,
52 cryptohome::MountError return_code) {
54 LOG(ERROR) << "Remove cryptohome for " << app
55 << " failed, return code: " << return_code;
59 // Check for presence of machine owner public key file.
60 void CheckOwnerFilePresence(bool *present) {
61 scoped_refptr<ownership::OwnerKeyUtil> util =
62 OwnerSettingsServiceChromeOSFactory::GetInstance()->GetOwnerKeyUtil();
63 *present = util.get() && util->IsPublicKeyPresent();
66 scoped_refptr<base::SequencedTaskRunner> GetBackgroundTaskRunner() {
67 base::SequencedWorkerPool* pool = content::BrowserThread::GetBlockingPool();
69 return pool->GetSequencedTaskRunnerWithShutdownBehavior(
70 pool->GetSequenceToken(), base::SequencedWorkerPool::SKIP_ON_SHUTDOWN);
76 const char KioskAppManager::kKioskDictionaryName[] = "kiosk";
77 const char KioskAppManager::kKeyApps[] = "apps";
78 const char KioskAppManager::kKeyAutoLoginState[] = "auto_login_state";
79 const char KioskAppManager::kIconCacheDir[] = "kiosk/icon";
80 const char KioskAppManager::kCrxCacheDir[] = "kiosk/crx";
81 const char KioskAppManager::kCrxUnpackDir[] = "kiosk_unpack";
84 static base::LazyInstance<KioskAppManager> instance = LAZY_INSTANCE_INITIALIZER;
85 KioskAppManager* KioskAppManager::Get() {
86 return instance.Pointer();
90 void KioskAppManager::Shutdown() {
94 instance.Pointer()->CleanUp();
98 void KioskAppManager::RegisterPrefs(PrefRegistrySimple* registry) {
99 registry->RegisterDictionaryPref(kKioskDictionaryName);
102 KioskAppManager::App::App(const KioskAppData& data, bool is_extension_pending)
103 : app_id(data.app_id()),
104 user_id(data.user_id()),
107 is_loading(data.IsLoading() || is_extension_pending) {
110 KioskAppManager::App::App() : is_loading(false) {}
111 KioskAppManager::App::~App() {}
113 std::string KioskAppManager::GetAutoLaunchApp() const {
114 return auto_launch_app_id_;
117 void KioskAppManager::SetAutoLaunchApp(const std::string& app_id) {
118 SetAutoLoginState(AUTOLOGIN_REQUESTED);
119 // Clean first, so the proper change callbacks are triggered even
120 // if we are only changing AutoLoginState here.
121 if (!auto_launch_app_id_.empty()) {
122 CrosSettings::Get()->SetString(kAccountsPrefDeviceLocalAccountAutoLoginId,
126 CrosSettings::Get()->SetString(
127 kAccountsPrefDeviceLocalAccountAutoLoginId,
128 app_id.empty() ? std::string() : GenerateKioskAppAccountId(app_id));
129 CrosSettings::Get()->SetInteger(
130 kAccountsPrefDeviceLocalAccountAutoLoginDelay, 0);
133 void KioskAppManager::EnableConsumerKioskAutoLaunch(
134 const KioskAppManager::EnableKioskAutoLaunchCallback& callback) {
135 policy::BrowserPolicyConnectorChromeOS* connector =
136 g_browser_process->platform_part()->browser_policy_connector_chromeos();
137 connector->GetInstallAttributes()->LockDevice(
138 std::string(), // user
139 policy::DEVICE_MODE_CONSUMER_KIOSK_AUTOLAUNCH,
140 std::string(), // device_id
142 &KioskAppManager::OnLockDevice, base::Unretained(this), callback));
145 void KioskAppManager::GetConsumerKioskAutoLaunchStatus(
146 const KioskAppManager::GetConsumerKioskAutoLaunchStatusCallback& callback) {
147 policy::BrowserPolicyConnectorChromeOS* connector =
148 g_browser_process->platform_part()->browser_policy_connector_chromeos();
149 connector->GetInstallAttributes()->ReadImmutableAttributes(
150 base::Bind(&KioskAppManager::OnReadImmutableAttributes,
151 base::Unretained(this),
155 bool KioskAppManager::IsConsumerKioskDeviceWithAutoLaunch() {
156 policy::BrowserPolicyConnectorChromeOS* connector =
157 g_browser_process->platform_part()->browser_policy_connector_chromeos();
158 return connector->GetInstallAttributes() &&
159 connector->GetInstallAttributes()
160 ->IsConsumerKioskDeviceWithAutoLaunch();
163 void KioskAppManager::OnLockDevice(
164 const KioskAppManager::EnableKioskAutoLaunchCallback& callback,
165 policy::EnterpriseInstallAttributes::LockResult result) {
166 if (callback.is_null())
169 callback.Run(result == policy::EnterpriseInstallAttributes::LOCK_SUCCESS);
172 void KioskAppManager::OnOwnerFileChecked(
173 const KioskAppManager::GetConsumerKioskAutoLaunchStatusCallback& callback,
174 bool* owner_present) {
175 ownership_established_ = *owner_present;
177 if (callback.is_null())
180 // If we have owner already established on the machine, don't let
181 // consumer kiosk to be enabled.
182 if (ownership_established_)
183 callback.Run(CONSUMER_KIOSK_AUTO_LAUNCH_DISABLED);
185 callback.Run(CONSUMER_KIOSK_AUTO_LAUNCH_CONFIGURABLE);
188 void KioskAppManager::OnReadImmutableAttributes(
189 const KioskAppManager::GetConsumerKioskAutoLaunchStatusCallback&
191 if (callback.is_null())
194 ConsumerKioskAutoLaunchStatus status =
195 CONSUMER_KIOSK_AUTO_LAUNCH_DISABLED;
196 policy::BrowserPolicyConnectorChromeOS* connector =
197 g_browser_process->platform_part()->browser_policy_connector_chromeos();
198 policy::EnterpriseInstallAttributes* attributes =
199 connector->GetInstallAttributes();
200 switch (attributes->GetMode()) {
201 case policy::DEVICE_MODE_NOT_SET: {
202 if (!base::SysInfo::IsRunningOnChromeOS()) {
203 status = CONSUMER_KIOSK_AUTO_LAUNCH_CONFIGURABLE;
204 } else if (!ownership_established_) {
205 bool* owner_present = new bool(false);
206 content::BrowserThread::PostBlockingPoolTaskAndReply(
208 base::Bind(&CheckOwnerFilePresence,
210 base::Bind(&KioskAppManager::OnOwnerFileChecked,
211 base::Unretained(this),
213 base::Owned(owner_present)));
218 case policy::DEVICE_MODE_CONSUMER_KIOSK_AUTOLAUNCH:
219 status = CONSUMER_KIOSK_AUTO_LAUNCH_ENABLED;
225 callback.Run(status);
228 void KioskAppManager::SetEnableAutoLaunch(bool value) {
229 SetAutoLoginState(value ? AUTOLOGIN_APPROVED : AUTOLOGIN_REJECTED);
232 bool KioskAppManager::IsAutoLaunchRequested() const {
233 if (GetAutoLaunchApp().empty())
236 // Apps that were installed by the policy don't require machine owner
237 // consent through UI.
238 policy::BrowserPolicyConnectorChromeOS* connector =
239 g_browser_process->platform_part()->browser_policy_connector_chromeos();
240 if (connector->IsEnterpriseManaged())
243 return GetAutoLoginState() == AUTOLOGIN_REQUESTED;
246 bool KioskAppManager::IsAutoLaunchEnabled() const {
247 if (GetAutoLaunchApp().empty())
250 // Apps that were installed by the policy don't require machine owner
251 // consent through UI.
252 policy::BrowserPolicyConnectorChromeOS* connector =
253 g_browser_process->platform_part()->browser_policy_connector_chromeos();
254 if (connector->IsEnterpriseManaged())
257 return GetAutoLoginState() == AUTOLOGIN_APPROVED;
260 void KioskAppManager::AddApp(const std::string& app_id) {
261 std::vector<policy::DeviceLocalAccount> device_local_accounts =
262 policy::GetDeviceLocalAccounts(CrosSettings::Get());
264 // Don't insert the app if it's already in the list.
265 for (std::vector<policy::DeviceLocalAccount>::const_iterator
266 it = device_local_accounts.begin();
267 it != device_local_accounts.end(); ++it) {
268 if (it->type == policy::DeviceLocalAccount::TYPE_KIOSK_APP &&
269 it->kiosk_app_id == app_id) {
274 // Add the new account.
275 device_local_accounts.push_back(policy::DeviceLocalAccount(
276 policy::DeviceLocalAccount::TYPE_KIOSK_APP,
277 GenerateKioskAppAccountId(app_id),
280 policy::SetDeviceLocalAccounts(CrosSettings::Get(), device_local_accounts);
283 void KioskAppManager::RemoveApp(const std::string& app_id) {
284 // Resets auto launch app if it is the removed app.
285 if (auto_launch_app_id_ == app_id)
286 SetAutoLaunchApp(std::string());
288 std::vector<policy::DeviceLocalAccount> device_local_accounts =
289 policy::GetDeviceLocalAccounts(CrosSettings::Get());
290 if (device_local_accounts.empty())
293 // Remove entries that match |app_id|.
294 for (std::vector<policy::DeviceLocalAccount>::iterator
295 it = device_local_accounts.begin();
296 it != device_local_accounts.end(); ++it) {
297 if (it->type == policy::DeviceLocalAccount::TYPE_KIOSK_APP &&
298 it->kiosk_app_id == app_id) {
299 device_local_accounts.erase(it);
304 policy::SetDeviceLocalAccounts(CrosSettings::Get(), device_local_accounts);
307 void KioskAppManager::GetApps(Apps* apps) const {
309 apps->reserve(apps_.size());
310 for (size_t i = 0; i < apps_.size(); ++i) {
311 const KioskAppData& app_data = *apps_[i];
312 if (app_data.status() != KioskAppData::STATUS_ERROR)
314 app_data, external_cache_->IsExtensionPending(app_data.app_id())));
318 bool KioskAppManager::GetApp(const std::string& app_id, App* app) const {
319 const KioskAppData* data = GetAppData(app_id);
323 *app = App(*data, external_cache_->IsExtensionPending(app_id));
327 const base::RefCountedString* KioskAppManager::GetAppRawIcon(
328 const std::string& app_id) const {
329 const KioskAppData* data = GetAppData(app_id);
333 return data->raw_icon();
336 bool KioskAppManager::GetDisableBailoutShortcut() const {
338 if (CrosSettings::Get()->GetBoolean(
339 kAccountsPrefDeviceLocalAccountAutoLoginBailoutEnabled, &enable)) {
346 void KioskAppManager::ClearAppData(const std::string& app_id) {
347 KioskAppData* app_data = GetAppDataMutable(app_id);
351 app_data->ClearCache();
354 void KioskAppManager::UpdateAppDataFromProfile(
355 const std::string& app_id,
357 const extensions::Extension* app) {
358 KioskAppData* app_data = GetAppDataMutable(app_id);
362 app_data->LoadFromInstalledApp(profile, app);
365 void KioskAppManager::RetryFailedAppDataFetch() {
366 for (size_t i = 0; i < apps_.size(); ++i) {
367 if (apps_[i]->status() == KioskAppData::STATUS_ERROR)
372 bool KioskAppManager::HasCachedCrx(const std::string& app_id) const {
373 base::FilePath crx_path;
375 return GetCachedCrx(app_id, &crx_path, &version);
378 bool KioskAppManager::GetCachedCrx(const std::string& app_id,
379 base::FilePath* file_path,
380 std::string* version) const {
381 return external_cache_->GetExtension(app_id, file_path, version);
384 void KioskAppManager::AddObserver(KioskAppManagerObserver* observer) {
385 observers_.AddObserver(observer);
388 void KioskAppManager::RemoveObserver(KioskAppManagerObserver* observer) {
389 observers_.RemoveObserver(observer);
392 extensions::ExternalLoader* KioskAppManager::CreateExternalLoader() {
393 if (external_loader_created_) {
397 external_loader_created_ = true;
398 KioskAppExternalLoader* loader = new KioskAppExternalLoader();
399 external_loader_ = loader->AsWeakPtr();
404 void KioskAppManager::InstallFromCache(const std::string& id) {
405 const base::DictionaryValue* extension = NULL;
406 if (external_cache_->cached_extensions()->GetDictionary(id, &extension)) {
407 scoped_ptr<base::DictionaryValue> prefs(new base::DictionaryValue);
408 base::DictionaryValue* extension_copy = extension->DeepCopy();
409 prefs->Set(id, extension_copy);
410 external_loader_->SetCurrentAppExtensions(prefs.Pass());
412 LOG(ERROR) << "Can't find app in the cached externsions"
417 void KioskAppManager::UpdateExternalCache() {
421 void KioskAppManager::OnKioskAppCacheUpdated(const std::string& app_id) {
423 KioskAppManagerObserver, observers_, OnKioskAppCacheUpdated(app_id));
426 void KioskAppManager::OnKioskAppExternalUpdateComplete(bool success) {
427 FOR_EACH_OBSERVER(KioskAppManagerObserver,
429 OnKioskAppExternalUpdateComplete(success));
432 void KioskAppManager::PutValidatedExternalExtension(
433 const std::string& app_id,
434 const base::FilePath& crx_path,
435 const std::string& version,
436 const ExternalCache::PutExternalExtensionCallback& callback) {
437 external_cache_->PutExternalExtension(app_id, crx_path, version, callback);
440 KioskAppManager::KioskAppManager()
441 : ownership_established_(false), external_loader_created_(false) {
442 base::FilePath cache_dir;
443 GetCrxCacheDir(&cache_dir);
444 external_cache_.reset(
445 new ExternalCache(cache_dir,
446 g_browser_process->system_request_context(),
447 GetBackgroundTaskRunner(),
449 true /* always_check_updates */,
450 false /* wait_for_cache_initialization */));
452 local_accounts_subscription_ =
453 CrosSettings::Get()->AddSettingsObserver(
454 kAccountsPrefDeviceLocalAccounts,
455 base::Bind(&KioskAppManager::UpdateAppData, base::Unretained(this)));
456 local_account_auto_login_id_subscription_ =
457 CrosSettings::Get()->AddSettingsObserver(
458 kAccountsPrefDeviceLocalAccountAutoLoginId,
459 base::Bind(&KioskAppManager::UpdateAppData, base::Unretained(this)));
462 KioskAppManager::~KioskAppManager() {}
464 void KioskAppManager::MonitorKioskExternalUpdate() {
465 base::FilePath cache_dir;
466 GetCrxCacheDir(&cache_dir);
467 base::FilePath unpack_dir;
468 GetCrxUnpackDir(&unpack_dir);
469 usb_stick_updater_.reset(new KioskExternalUpdater(
470 GetBackgroundTaskRunner(), cache_dir, unpack_dir));
473 void KioskAppManager::CleanUp() {
474 local_accounts_subscription_.reset();
475 local_account_auto_login_id_subscription_.reset();
477 usb_stick_updater_.reset();
478 external_cache_.reset();
481 const KioskAppData* KioskAppManager::GetAppData(
482 const std::string& app_id) const {
483 for (size_t i = 0; i < apps_.size(); ++i) {
484 const KioskAppData* data = apps_[i];
485 if (data->app_id() == app_id)
492 KioskAppData* KioskAppManager::GetAppDataMutable(const std::string& app_id) {
493 return const_cast<KioskAppData*>(GetAppData(app_id));
496 void KioskAppManager::UpdateAppData() {
497 // Gets app id to data mapping for existing apps.
498 std::map<std::string, KioskAppData*> old_apps;
499 for (size_t i = 0; i < apps_.size(); ++i)
500 old_apps[apps_[i]->app_id()] = apps_[i];
501 apps_.weak_clear(); // |old_apps| takes ownership
503 auto_launch_app_id_.clear();
504 std::string auto_login_account_id;
505 CrosSettings::Get()->GetString(kAccountsPrefDeviceLocalAccountAutoLoginId,
506 &auto_login_account_id);
508 // Re-populates |apps_| and reuses existing KioskAppData when possible.
509 const std::vector<policy::DeviceLocalAccount> device_local_accounts =
510 policy::GetDeviceLocalAccounts(CrosSettings::Get());
511 for (std::vector<policy::DeviceLocalAccount>::const_iterator
512 it = device_local_accounts.begin();
513 it != device_local_accounts.end(); ++it) {
514 if (it->type != policy::DeviceLocalAccount::TYPE_KIOSK_APP)
517 if (it->account_id == auto_login_account_id)
518 auto_launch_app_id_ = it->kiosk_app_id;
520 // TODO(mnissler): Support non-CWS update URLs.
522 std::map<std::string, KioskAppData*>::iterator old_it =
523 old_apps.find(it->kiosk_app_id);
524 if (old_it != old_apps.end()) {
525 apps_.push_back(old_it->second);
526 old_apps.erase(old_it);
528 KioskAppData* new_app =
529 new KioskAppData(this, it->kiosk_app_id, it->user_id);
530 apps_.push_back(new_app); // Takes ownership of |new_app|.
535 // Clears cache and deletes the remaining old data.
536 std::vector<std::string> apps_to_remove;
537 for (std::map<std::string, KioskAppData*>::iterator it = old_apps.begin();
538 it != old_apps.end(); ++it) {
539 it->second->ClearCache();
540 cryptohome::AsyncMethodCaller::GetInstance()->AsyncRemove(
541 it->second->user_id(),
542 base::Bind(&OnRemoveAppCryptohomeComplete, it->first));
543 apps_to_remove.push_back(it->second->app_id());
545 STLDeleteValues(&old_apps);
546 external_cache_->RemoveExtensions(apps_to_remove);
548 // Request external_cache_ to download new apps and update the existing
550 scoped_ptr<base::DictionaryValue> prefs(new base::DictionaryValue);
551 for (size_t i = 0; i < apps_.size(); ++i)
552 prefs->Set(apps_[i]->app_id(), new base::DictionaryValue);
553 external_cache_->UpdateExtensionsList(prefs.Pass());
555 RetryFailedAppDataFetch();
557 FOR_EACH_OBSERVER(KioskAppManagerObserver, observers_,
558 OnKioskAppsSettingsChanged());
561 void KioskAppManager::GetKioskAppIconCacheDir(base::FilePath* cache_dir) {
562 base::FilePath user_data_dir;
563 CHECK(PathService::Get(chrome::DIR_USER_DATA, &user_data_dir));
564 *cache_dir = user_data_dir.AppendASCII(kIconCacheDir);
567 void KioskAppManager::OnKioskAppDataChanged(const std::string& app_id) {
568 FOR_EACH_OBSERVER(KioskAppManagerObserver,
570 OnKioskAppDataChanged(app_id));
573 void KioskAppManager::OnKioskAppDataLoadFailure(const std::string& app_id) {
574 FOR_EACH_OBSERVER(KioskAppManagerObserver,
576 OnKioskAppDataLoadFailure(app_id));
579 void KioskAppManager::OnExtensionListsUpdated(
580 const base::DictionaryValue* prefs) {
583 void KioskAppManager::OnExtensionLoadedInCache(const std::string& id) {
584 KioskAppData* app_data = GetAppDataMutable(id);
587 FOR_EACH_OBSERVER(KioskAppManagerObserver,
589 OnKioskExtensionLoadedInCache(id));
593 void KioskAppManager::OnExtensionDownloadFailed(
594 const std::string& id,
595 extensions::ExtensionDownloaderDelegate::Error error) {
596 KioskAppData* app_data = GetAppDataMutable(id);
599 FOR_EACH_OBSERVER(KioskAppManagerObserver,
601 OnKioskExtensionDownloadFailed(id));
604 KioskAppManager::AutoLoginState KioskAppManager::GetAutoLoginState() const {
605 PrefService* prefs = g_browser_process->local_state();
606 const base::DictionaryValue* dict =
607 prefs->GetDictionary(KioskAppManager::kKioskDictionaryName);
609 if (!dict->GetInteger(kKeyAutoLoginState, &value))
610 return AUTOLOGIN_NONE;
612 return static_cast<AutoLoginState>(value);
615 void KioskAppManager::SetAutoLoginState(AutoLoginState state) {
616 PrefService* prefs = g_browser_process->local_state();
617 DictionaryPrefUpdate dict_update(prefs,
618 KioskAppManager::kKioskDictionaryName);
619 dict_update->SetInteger(kKeyAutoLoginState, state);
620 prefs->CommitPendingWrite();
623 void KioskAppManager::GetCrxCacheDir(base::FilePath* cache_dir) {
624 base::FilePath user_data_dir;
625 CHECK(PathService::Get(chrome::DIR_USER_DATA, &user_data_dir));
626 *cache_dir = user_data_dir.AppendASCII(kCrxCacheDir);
629 void KioskAppManager::GetCrxUnpackDir(base::FilePath* unpack_dir) {
630 base::FilePath temp_dir;
631 base::GetTempDir(&temp_dir);
632 *unpack_dir = temp_dir.AppendASCII(kCrxUnpackDir);
635 } // namespace chromeos