| // Copyright 2018 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/chromeos/plugin_vm/plugin_vm_util.h" |
| |
| #include <string> |
| #include <utility> |
| |
| #include "base/bind.h" |
| #include "base/files/file_util.h" |
| #include "base/observer_list.h" |
| #include "base/task/post_task.h" |
| #include "base/task/thread_pool.h" |
| #include "chrome/browser/chromeos/plugin_vm/plugin_vm_drive_image_download_service.h" |
| #include "chrome/browser/chromeos/plugin_vm/plugin_vm_manager.h" |
| #include "chrome/browser/chromeos/plugin_vm/plugin_vm_manager_factory.h" |
| #include "chrome/browser/chromeos/plugin_vm/plugin_vm_pref_names.h" |
| #include "chrome/browser/chromeos/profiles/profile_helper.h" |
| #include "chrome/browser/chromeos/settings/cros_settings.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/profiles/profile_manager.h" |
| #include "chrome/browser/ui/ash/launcher/chrome_launcher_controller.h" |
| #include "chrome/common/chrome_features.h" |
| #include "chromeos/dbus/dlcservice/dlcservice_client.h" |
| #include "chromeos/tpm/install_attributes.h" |
| #include "components/exo/shell_surface_util.h" |
| #include "components/prefs/pref_service.h" |
| #include "components/prefs/scoped_user_pref_update.h" |
| #include "google_apis/drive/drive_api_error_codes.h" |
| |
| namespace plugin_vm { |
| |
| namespace { |
| |
| static std::string& GetFakeLicenseKey() { |
| static base::NoDestructor<std::string> license_key; |
| return *license_key; |
| } |
| |
| static base::CallbackList<void(void)>& GetFakeLicenceKeyListeners() { |
| static base::NoDestructor<base::CallbackList<void(void)>> instance; |
| return *instance; |
| } |
| |
| static std::string& GetFakeUserId() { |
| static base::NoDestructor<std::string> user_id; |
| return *user_id; |
| } |
| |
| } // namespace |
| |
| // For PluginVm to be allowed: |
| // * Profile should be eligible. |
| // * PluginVm feature should be enabled. |
| // * Device should be enterprise enrolled: |
| // * User should be affiliated. |
| // * PluginVmAllowed device policy should be set to true. |
| // * UserPluginVmAllowed user policy should be set to true. |
| // * At least one of the following should be set: |
| // * PluginVmLicenseKey policy. |
| // * PluginVmUserId policy. |
| bool IsPluginVmAllowedForProfile(const Profile* profile) { |
| // Check that the profile is eligible. |
| if (!profile || profile->IsChild() || profile->IsLegacySupervised() || |
| profile->IsOffTheRecord() || |
| chromeos::ProfileHelper::IsEphemeralUserProfile(profile) || |
| chromeos::ProfileHelper::IsLockScreenAppProfile(profile) || |
| !chromeos::ProfileHelper::IsPrimaryProfile(profile)) { |
| return false; |
| } |
| |
| // Check that PluginVm feature is enabled. |
| if (!base::FeatureList::IsEnabled(features::kPluginVm)) |
| return false; |
| |
| // Bypass other checks when a fake policy is set |
| if (FakeLicenseKeyIsSet()) |
| return true; |
| |
| // Check that the device is enterprise enrolled. |
| if (!chromeos::InstallAttributes::Get()->IsEnterpriseManaged()) |
| return false; |
| |
| // Check that the user is affiliated. |
| const user_manager::User* const user = |
| chromeos::ProfileHelper::Get()->GetUserByProfile(profile); |
| if (user == nullptr || !user->IsAffiliated()) |
| return false; |
| |
| // Check that PluginVm is allowed to run by policy. |
| bool plugin_vm_allowed_for_device; |
| if (!chromeos::CrosSettings::Get()->GetBoolean( |
| chromeos::kPluginVmAllowed, &plugin_vm_allowed_for_device)) { |
| return false; |
| } |
| bool plugin_vm_allowed_for_user = |
| profile->GetPrefs()->GetBoolean(plugin_vm::prefs::kPluginVmAllowed); |
| if (!plugin_vm_allowed_for_device || !plugin_vm_allowed_for_user) |
| return false; |
| |
| if (GetPluginVmLicenseKey().empty() && |
| GetPluginVmUserIdForProfile(profile).empty()) |
| return false; |
| |
| return true; |
| } |
| |
| bool IsPluginVmConfigured(const Profile* profile) { |
| return profile->GetPrefs()->GetBoolean( |
| plugin_vm::prefs::kPluginVmImageExists); |
| } |
| |
| bool IsPluginVmEnabled(const Profile* profile) { |
| return IsPluginVmAllowedForProfile(profile) && IsPluginVmConfigured(profile); |
| } |
| |
| bool IsPluginVmRunning(Profile* profile) { |
| return plugin_vm::PluginVmManagerFactory::GetForProfile(profile) |
| ->vm_state() == |
| vm_tools::plugin_dispatcher::VmState::VM_STATE_RUNNING && |
| ChromeLauncherController::instance()->IsOpen( |
| ash::ShelfID(kPluginVmAppId)); |
| } |
| |
| bool IsPluginVmWindow(const aura::Window* window) { |
| const std::string* app_id = exo::GetShellApplicationId(window); |
| if (!app_id) |
| return false; |
| return *app_id == "org.chromium.plugin_vm_ui"; |
| } |
| |
| std::string GetPluginVmLicenseKey() { |
| if (FakeLicenseKeyIsSet()) |
| return GetFakeLicenseKey(); |
| std::string plugin_vm_license_key; |
| if (!chromeos::CrosSettings::Get()->GetString(chromeos::kPluginVmLicenseKey, |
| &plugin_vm_license_key)) { |
| return std::string(); |
| } |
| return plugin_vm_license_key; |
| } |
| |
| std::string GetPluginVmUserIdForProfile(const Profile* profile) { |
| DCHECK(profile); |
| return profile->GetPrefs()->GetString(plugin_vm::prefs::kPluginVmUserId); |
| } |
| |
| void SetFakePluginVmPolicy(Profile* profile, |
| const std::string& image_url, |
| const std::string& image_hash, |
| const std::string& license_key) { |
| DictionaryPrefUpdate update(profile->GetPrefs(), |
| plugin_vm::prefs::kPluginVmImage); |
| base::DictionaryValue* dict = update.Get(); |
| dict->SetPath("url", base::Value(image_url)); |
| dict->SetPath("hash", base::Value(image_hash)); |
| |
| GetFakeLicenseKey() = license_key; |
| |
| GetFakeLicenceKeyListeners().Notify(); |
| GetFakeUserId() = "FAKE_USER_ID"; |
| } |
| |
| bool FakeLicenseKeyIsSet() { |
| return !GetFakeLicenseKey().empty(); |
| } |
| |
| bool FakeUserIdIsSet() { |
| return !GetFakeUserId().empty(); |
| } |
| |
| void RemoveDriveDownloadDirectoryIfExists() { |
| auto log_file_deletion_if_failed = [](bool success) { |
| if (!success) { |
| LOG(ERROR) << "PluginVM failed to delete download directory"; |
| } |
| }; |
| |
| base::ThreadPool::PostTaskAndReplyWithResult( |
| FROM_HERE, {base::MayBlock(), base::TaskPriority::BEST_EFFORT}, |
| base::BindOnce(&base::DeleteFileRecursively, |
| base::FilePath(kPluginVmDriveDownloadDirectory)), |
| base::BindOnce(std::move(log_file_deletion_if_failed))); |
| } |
| |
| // TODO(muhamedp): Update if a different url format is ultimately chosen. |
| bool IsDriveUrl(const GURL& url) { |
| const std::string url_base = "https://drive.google.com/open"; |
| const std::string& spec = url.spec(); |
| return spec.find(url_base) == 0 && spec.find("id=") < (spec.length() - 3); |
| } |
| |
| // TODO(muhamedp): Update if a different url format is ultimately chosen. |
| std::string GetIdFromDriveUrl(const GURL& url) { |
| const std::string& spec = url.spec(); |
| |
| const size_t id_start = spec.find("id=") + 3; |
| // In case there are other GET parameters after the id. |
| const size_t first_ampersand = spec.find('&', id_start); |
| |
| if (first_ampersand == std::string::npos) |
| return spec.substr(id_start); |
| else |
| return spec.substr(id_start, first_ampersand - id_start); |
| } |
| |
| PluginVmPolicySubscription::PluginVmPolicySubscription( |
| Profile* profile, |
| base::RepeatingCallback<void(bool)> callback) |
| : profile_(profile), callback_(callback) { |
| DCHECK(chromeos::CrosSettings::IsInitialized()); |
| chromeos::CrosSettings* cros_settings = chromeos::CrosSettings::Get(); |
| // Subscriptions are automatically removed when this object is destroyed. |
| pref_change_registrar_ = std::make_unique<PrefChangeRegistrar>(); |
| pref_change_registrar_->Init(profile->GetPrefs()); |
| pref_change_registrar_->Add( |
| plugin_vm::prefs::kPluginVmAllowed, |
| base::BindRepeating(&PluginVmPolicySubscription::OnPolicyChanged, |
| base::Unretained(this))); |
| pref_change_registrar_->Add( |
| plugin_vm::prefs::kPluginVmUserId, |
| base::BindRepeating(&PluginVmPolicySubscription::OnPolicyChanged, |
| base::Unretained(this))); |
| device_allowed_subscription_ = cros_settings->AddSettingsObserver( |
| chromeos::kPluginVmAllowed, |
| base::BindRepeating(&PluginVmPolicySubscription::OnPolicyChanged, |
| base::Unretained(this))); |
| license_subscription_ = cros_settings->AddSettingsObserver( |
| chromeos::kPluginVmLicenseKey, |
| base::BindRepeating(&PluginVmPolicySubscription::OnPolicyChanged, |
| base::Unretained(this))); |
| fake_license_subscription_ = GetFakeLicenceKeyListeners().Add( |
| base::BindRepeating(&PluginVmPolicySubscription::OnPolicyChanged, |
| base::Unretained(this))); |
| |
| is_allowed_ = IsPluginVmAllowedForProfile(profile); |
| } |
| |
| void PluginVmPolicySubscription::OnPolicyChanged() { |
| bool allowed = IsPluginVmAllowedForProfile(profile_); |
| if (allowed != is_allowed_) { |
| is_allowed_ = allowed; |
| callback_.Run(allowed); |
| } |
| } |
| |
| PluginVmPolicySubscription::~PluginVmPolicySubscription() = default; |
| |
| } // namespace plugin_vm |