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