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/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