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