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