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/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" 34 35 namespace chromeos { 36 37 namespace { 38 39 // Domain that is used for kiosk-app account IDs. 40 const char kKioskAppAccountDomain[] = "kiosk-apps"; 41 42 std::string GenerateKioskAppAccountId(const std::string& app_id) { 43 return app_id + '@' + kKioskAppAccountDomain; 44 } 45 46 void OnRemoveAppCryptohomeComplete(const std::string& app, 47 bool success, 48 cryptohome::MountError return_code) { 49 if (!success) { 50 LOG(ERROR) << "Remove cryptohome for " << app 51 << " failed, return code: " << return_code; 52 } 53 } 54 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(); 59 } 60 61 scoped_refptr<base::SequencedTaskRunner> GetBackgroundTaskRunner() { 62 base::SequencedWorkerPool* pool = content::BrowserThread::GetBlockingPool(); 63 CHECK(pool); 64 return pool->GetSequencedTaskRunnerWithShutdownBehavior( 65 pool->GetSequenceToken(), base::SequencedWorkerPool::SKIP_ON_SHUTDOWN); 66 } 67 68 } // namespace 69 70 // static 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"; 76 77 // static 78 static base::LazyInstance<KioskAppManager> instance = LAZY_INSTANCE_INITIALIZER; 79 KioskAppManager* KioskAppManager::Get() { 80 return instance.Pointer(); 81 } 82 83 // static 84 void KioskAppManager::Shutdown() { 85 if (instance == NULL) 86 return; 87 88 instance.Pointer()->CleanUp(); 89 } 90 91 // static 92 void KioskAppManager::RegisterPrefs(PrefRegistrySimple* registry) { 93 registry->RegisterDictionaryPref(kKioskDictionaryName); 94 } 95 96 KioskAppManager::App::App(const KioskAppData& data, bool is_extension_pending) 97 : app_id(data.app_id()), 98 user_id(data.user_id()), 99 name(data.name()), 100 icon(data.icon()), 101 is_loading(data.IsLoading() || is_extension_pending) { 102 } 103 104 KioskAppManager::App::App() : is_loading(false) {} 105 KioskAppManager::App::~App() {} 106 107 std::string KioskAppManager::GetAutoLaunchApp() const { 108 return auto_launch_app_id_; 109 } 110 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, 117 std::string()); 118 } 119 120 CrosSettings::Get()->SetString( 121 kAccountsPrefDeviceLocalAccountAutoLoginId, 122 app_id.empty() ? std::string() : GenerateKioskAppAccountId(app_id)); 123 CrosSettings::Get()->SetInteger( 124 kAccountsPrefDeviceLocalAccountAutoLoginDelay, 0); 125 } 126 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 135 base::Bind( 136 &KioskAppManager::OnLockDevice, base::Unretained(this), callback)); 137 } 138 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), 146 callback)); 147 } 148 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(); 155 } 156 157 void KioskAppManager::OnLockDevice( 158 const KioskAppManager::EnableKioskAutoLaunchCallback& callback, 159 policy::EnterpriseInstallAttributes::LockResult result) { 160 if (callback.is_null()) 161 return; 162 163 callback.Run(result == policy::EnterpriseInstallAttributes::LOCK_SUCCESS); 164 } 165 166 void KioskAppManager::OnOwnerFileChecked( 167 const KioskAppManager::GetConsumerKioskAutoLaunchStatusCallback& callback, 168 bool* owner_present) { 169 ownership_established_ = *owner_present; 170 171 if (callback.is_null()) 172 return; 173 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); 178 else 179 callback.Run(CONSUMER_KIOSK_AUTO_LAUNCH_CONFIGURABLE); 180 } 181 182 void KioskAppManager::OnReadImmutableAttributes( 183 const KioskAppManager::GetConsumerKioskAutoLaunchStatusCallback& 184 callback) { 185 if (callback.is_null()) 186 return; 187 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( 201 FROM_HERE, 202 base::Bind(&CheckOwnerFilePresence, 203 owner_present), 204 base::Bind(&KioskAppManager::OnOwnerFileChecked, 205 base::Unretained(this), 206 callback, 207 base::Owned(owner_present))); 208 return; 209 } 210 break; 211 } 212 case policy::DEVICE_MODE_CONSUMER_KIOSK_AUTOLAUNCH: 213 status = CONSUMER_KIOSK_AUTO_LAUNCH_ENABLED; 214 break; 215 default: 216 break; 217 } 218 219 callback.Run(status); 220 } 221 222 void KioskAppManager::SetEnableAutoLaunch(bool value) { 223 SetAutoLoginState(value ? AUTOLOGIN_APPROVED : AUTOLOGIN_REJECTED); 224 } 225 226 bool KioskAppManager::IsAutoLaunchRequested() const { 227 if (GetAutoLaunchApp().empty()) 228 return false; 229 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()) 235 return false; 236 237 return GetAutoLoginState() == AUTOLOGIN_REQUESTED; 238 } 239 240 bool KioskAppManager::IsAutoLaunchEnabled() const { 241 if (GetAutoLaunchApp().empty()) 242 return false; 243 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()) 249 return true; 250 251 return GetAutoLoginState() == AUTOLOGIN_APPROVED; 252 } 253 254 void KioskAppManager::AddApp(const std::string& app_id) { 255 std::vector<policy::DeviceLocalAccount> device_local_accounts = 256 policy::GetDeviceLocalAccounts(CrosSettings::Get()); 257 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) { 264 return; 265 } 266 } 267 268 // Add the new account. 269 device_local_accounts.push_back(policy::DeviceLocalAccount( 270 policy::DeviceLocalAccount::TYPE_KIOSK_APP, 271 GenerateKioskAppAccountId(app_id), 272 app_id)); 273 274 policy::SetDeviceLocalAccounts(CrosSettings::Get(), device_local_accounts); 275 } 276 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()); 281 282 std::vector<policy::DeviceLocalAccount> device_local_accounts = 283 policy::GetDeviceLocalAccounts(CrosSettings::Get()); 284 if (device_local_accounts.empty()) 285 return; 286 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); 294 break; 295 } 296 } 297 298 policy::SetDeviceLocalAccounts(CrosSettings::Get(), device_local_accounts); 299 } 300 301 void KioskAppManager::GetApps(Apps* apps) const { 302 apps->clear(); 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) 307 apps->push_back(App( 308 app_data, external_cache_->IsExtensionPending(app_data.app_id()))); 309 } 310 } 311 312 bool KioskAppManager::GetApp(const std::string& app_id, App* app) const { 313 const KioskAppData* data = GetAppData(app_id); 314 if (!data) 315 return false; 316 317 *app = App(*data, external_cache_->IsExtensionPending(app_id)); 318 return true; 319 } 320 321 const base::RefCountedString* KioskAppManager::GetAppRawIcon( 322 const std::string& app_id) const { 323 const KioskAppData* data = GetAppData(app_id); 324 if (!data) 325 return NULL; 326 327 return data->raw_icon(); 328 } 329 330 bool KioskAppManager::GetDisableBailoutShortcut() const { 331 bool enable; 332 if (CrosSettings::Get()->GetBoolean( 333 kAccountsPrefDeviceLocalAccountAutoLoginBailoutEnabled, &enable)) { 334 return !enable; 335 } 336 337 return false; 338 } 339 340 void KioskAppManager::ClearAppData(const std::string& app_id) { 341 KioskAppData* app_data = GetAppDataMutable(app_id); 342 if (!app_data) 343 return; 344 345 app_data->ClearCache(); 346 } 347 348 void KioskAppManager::UpdateAppDataFromProfile( 349 const std::string& app_id, 350 Profile* profile, 351 const extensions::Extension* app) { 352 KioskAppData* app_data = GetAppDataMutable(app_id); 353 if (!app_data) 354 return; 355 356 app_data->LoadFromInstalledApp(profile, app); 357 } 358 359 void KioskAppManager::RetryFailedAppDataFetch() { 360 for (size_t i = 0; i < apps_.size(); ++i) { 361 if (apps_[i]->status() == KioskAppData::STATUS_ERROR) 362 apps_[i]->Load(); 363 } 364 } 365 366 void KioskAppManager::AddObserver(KioskAppManagerObserver* observer) { 367 observers_.AddObserver(observer); 368 } 369 370 void KioskAppManager::RemoveObserver(KioskAppManagerObserver* observer) { 371 observers_.RemoveObserver(observer); 372 } 373 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(), 381 this, 382 true /* always_check_updates */, 383 false /* wait_for_cache_initialization */)); 384 385 UpdateAppData(); 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))); 394 } 395 396 KioskAppManager::~KioskAppManager() {} 397 398 void KioskAppManager::CleanUp() { 399 local_accounts_subscription_.reset(); 400 local_account_auto_login_id_subscription_.reset(); 401 apps_.clear(); 402 external_cache_.reset(); 403 } 404 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) 410 return data; 411 } 412 413 return NULL; 414 } 415 416 KioskAppData* KioskAppManager::GetAppDataMutable(const std::string& app_id) { 417 return const_cast<KioskAppData*>(GetAppData(app_id)); 418 } 419 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 426 427 auto_launch_app_id_.clear(); 428 std::string auto_login_account_id; 429 CrosSettings::Get()->GetString(kAccountsPrefDeviceLocalAccountAutoLoginId, 430 &auto_login_account_id); 431 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) 439 continue; 440 441 if (it->account_id == auto_login_account_id) 442 auto_launch_app_id_ = it->kiosk_app_id; 443 444 // TODO(mnissler): Support non-CWS update URLs. 445 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); 451 } else { 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|. 455 new_app->Load(); 456 } 457 } 458 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()); 468 } 469 STLDeleteValues(&old_apps); 470 external_cache_->RemoveExtensions(apps_to_remove); 471 472 // Request external_cache_ to download new apps and update the existing 473 // apps. 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()); 478 479 RetryFailedAppDataFetch(); 480 481 FOR_EACH_OBSERVER(KioskAppManagerObserver, observers_, 482 OnKioskAppsSettingsChanged()); 483 } 484 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); 489 } 490 491 void KioskAppManager::OnKioskAppDataChanged(const std::string& app_id) { 492 FOR_EACH_OBSERVER(KioskAppManagerObserver, 493 observers_, 494 OnKioskAppDataChanged(app_id)); 495 } 496 497 void KioskAppManager::OnKioskAppDataLoadFailure(const std::string& app_id) { 498 FOR_EACH_OBSERVER(KioskAppManagerObserver, 499 observers_, 500 OnKioskAppDataLoadFailure(app_id)); 501 } 502 503 void KioskAppManager::OnExtensionListsUpdated( 504 const base::DictionaryValue* prefs) { 505 } 506 507 void KioskAppManager::OnExtensionLoadedInCache(const std::string& id) { 508 KioskAppData* app_data = GetAppDataMutable(id); 509 if (!app_data) 510 return; 511 OnKioskAppDataChanged(id); 512 } 513 514 void KioskAppManager::OnExtensionDownloadFailed( 515 const std::string& id, 516 extensions::ExtensionDownloaderDelegate::Error error) { 517 KioskAppData* app_data = GetAppDataMutable(id); 518 if (!app_data) 519 return; 520 OnKioskAppDataLoadFailure(id); 521 } 522 523 KioskAppManager::AutoLoginState KioskAppManager::GetAutoLoginState() const { 524 PrefService* prefs = g_browser_process->local_state(); 525 const base::DictionaryValue* dict = 526 prefs->GetDictionary(KioskAppManager::kKioskDictionaryName); 527 int value; 528 if (!dict->GetInteger(kKeyAutoLoginState, &value)) 529 return AUTOLOGIN_NONE; 530 531 return static_cast<AutoLoginState>(value); 532 } 533 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(); 540 } 541 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); 546 } 547 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); 552 } 553 554 } // namespace chromeos 555