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