| // Copyright 2020 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/ash/plugin_vm/plugin_vm_features.h" |
| |
| #include "ash/components/settings/cros_settings_names.h" |
| #include "base/feature_list.h" |
| #include "base/system/sys_info.h" |
| #include "chrome/browser/ash/plugin_vm/plugin_vm_pref_names.h" |
| #include "chrome/browser/ash/plugin_vm/plugin_vm_util.h" |
| #include "chrome/browser/ash/profiles/profile_helper.h" |
| #include "chrome/browser/ash/settings/cros_settings.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/common/chrome_features.h" |
| #include "chromeos/tpm/install_attributes.h" |
| #include "components/prefs/pref_service.h" |
| #include "components/user_manager/user.h" |
| |
| namespace plugin_vm { |
| |
| namespace { |
| |
| using ProfileSupported = PluginVmFeatures::ProfileSupported; |
| using PolicyConfigured = PluginVmFeatures::PolicyConfigured; |
| |
| ProfileSupported CheckProfileSupported(const Profile* profile) { |
| if (!profile) { |
| VLOG(1) << "profile == nullptr"; |
| return ProfileSupported::kErrorNotSupported; |
| } |
| |
| if (!ash::ProfileHelper::IsPrimaryProfile(profile)) { |
| return ProfileSupported::kErrorNonPrimary; |
| } |
| |
| if (profile->IsChild()) { |
| return ProfileSupported::kErrorChildAccount; |
| } |
| |
| if (profile->IsOffTheRecord()) { |
| return ProfileSupported::kErrorOffTheRecord; |
| } |
| |
| if (ash::ProfileHelper::IsEphemeralUserProfile(profile)) { |
| return ProfileSupported::kErrorEphemeral; |
| } |
| |
| if (!ash::ProfileHelper::IsRegularProfile(profile)) { |
| VLOG(1) << "non-regular profile is not supported"; |
| // If this happens, the profile is for something like the sign in screen or |
| // lock screen. Return a generic error code because the user will not be |
| // able to see the error code/message anyway. |
| return ProfileSupported::kErrorNotSupported; |
| } |
| |
| return ProfileSupported::kOk; |
| } |
| |
| PolicyConfigured CheckPolicyConfigured(const Profile* profile) { |
| // Bypass other checks when a fake policy is set, or running linux-chromeos. |
| if (FakeLicenseKeyIsSet() || !base::SysInfo::IsRunningOnChromeOS()) { |
| return PolicyConfigured::kOk; |
| } |
| |
| if (profile == nullptr) { |
| VLOG(1) << "Profile is null"; |
| return PolicyConfigured::kErrorUnableToCheckPolicy; |
| } |
| |
| // Check that the device is enterprise enrolled. |
| if (!chromeos::InstallAttributes::Get()->IsEnterpriseManaged()) { |
| return PolicyConfigured::kErrorNotEnterpriseEnrolled; |
| } |
| |
| // Check that the user is affiliated. |
| const user_manager::User* const user = |
| ash::ProfileHelper::Get()->GetUserByProfile(profile); |
| if (user == nullptr || !user->IsAffiliated()) { |
| return PolicyConfigured::kErrorUserNotAffiliated; |
| } |
| |
| // Check that PluginVm is allowed to run by policy. |
| bool plugin_vm_allowed_for_device; |
| if (!ash::CrosSettings::Get()->GetBoolean(ash::kPluginVmAllowed, |
| &plugin_vm_allowed_for_device)) { |
| return PolicyConfigured::kErrorUnableToCheckDevicePolicy; |
| } |
| |
| if (!plugin_vm_allowed_for_device) { |
| return PolicyConfigured::kErrorNotAllowedByDevicePolicy; |
| } |
| |
| bool plugin_vm_allowed_for_user = |
| profile->GetPrefs()->GetBoolean(plugin_vm::prefs::kPluginVmAllowed); |
| if (!plugin_vm_allowed_for_user) { |
| return PolicyConfigured::kErrorNotAllowedByUserPolicy; |
| } |
| |
| if (GetPluginVmUserIdForProfile(profile).empty()) { |
| return PolicyConfigured::kErrorLicenseNotSetUp; |
| } |
| |
| return PolicyConfigured::kOk; |
| } |
| |
| } // namespace |
| |
| bool PluginVmFeatures::IsAllowedDiagnostics::IsOk() const { |
| return device_supported && profile_supported == ProfileSupported::kOk && |
| policy_configured == PolicyConfigured::kOk; |
| } |
| |
| std::string PluginVmFeatures::IsAllowedDiagnostics::GetTopError() const { |
| if (!device_supported) { |
| return "Parallels Desktop is not supported on this device"; |
| } |
| |
| switch (profile_supported) { |
| case ProfileSupported::kOk: |
| break; |
| case ProfileSupported::kErrorNonPrimary: |
| return "Parallels Desktop is only allowed in primary user sessions"; |
| case ProfileSupported::kErrorChildAccount: |
| return "Child accounts are not supported"; |
| case ProfileSupported::kErrorOffTheRecord: |
| return "Guest profiles are not supported"; |
| case ProfileSupported::kErrorEphemeral: |
| return "Ephemeral user profiles are not supported"; |
| case ProfileSupported::kErrorNotSupported: |
| return "This user session is not allowed to run Parallels Desktop"; |
| } |
| |
| switch (policy_configured) { |
| case PolicyConfigured::kOk: |
| break; |
| case PolicyConfigured::kErrorUnableToCheckPolicy: |
| return "Unable to check policy"; |
| case PolicyConfigured::kErrorNotEnterpriseEnrolled: |
| return "This is not an enterprise-managed device"; |
| case PolicyConfigured::kErrorUserNotAffiliated: |
| return "This user is not affiliated with the organization"; |
| case PolicyConfigured::kErrorUnableToCheckDevicePolicy: |
| return "Unable to determine if device-level policy allows running VMs"; |
| case PolicyConfigured::kErrorNotAllowedByDevicePolicy: |
| return "VMs are disallowed by policy on this device"; |
| case PolicyConfigured::kErrorNotAllowedByUserPolicy: |
| return "VMs are disallowed by policy"; |
| case PolicyConfigured::kErrorLicenseNotSetUp: |
| return "License for the product is not set up in policy"; |
| } |
| |
| return ""; |
| } |
| |
| static PluginVmFeatures* g_plugin_vm_features = nullptr; |
| |
| PluginVmFeatures* PluginVmFeatures::Get() { |
| if (!g_plugin_vm_features) { |
| g_plugin_vm_features = new PluginVmFeatures(); |
| } |
| return g_plugin_vm_features; |
| } |
| |
| void PluginVmFeatures::SetForTesting(PluginVmFeatures* features) { |
| g_plugin_vm_features = features; |
| } |
| |
| PluginVmFeatures::PluginVmFeatures() = default; |
| |
| PluginVmFeatures::~PluginVmFeatures() = default; |
| |
| // For PluginVm to be allowed: |
| // * PluginVm feature should be enabled. |
| // * Profile should be eligible. |
| // * 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. |
| // * PluginVmUserId policy is set. |
| PluginVmFeatures::IsAllowedDiagnostics |
| PluginVmFeatures::GetIsAllowedDiagnostics(const Profile* profile) { |
| auto diagnostics = IsAllowedDiagnostics{ |
| /*device_supported=*/base::FeatureList::IsEnabled(features::kPluginVm), |
| /*profile_supported=*/CheckProfileSupported(profile), |
| /*policy_configured=*/CheckPolicyConfigured(profile), |
| }; |
| if (!diagnostics.IsOk()) { |
| VLOG(1) << diagnostics.GetTopError(); |
| } |
| |
| return diagnostics; |
| } |
| |
| bool PluginVmFeatures::IsAllowed(const Profile* profile, std::string* reason) { |
| auto diagnostics = GetIsAllowedDiagnostics(profile); |
| if (!diagnostics.IsOk()) { |
| if (reason) { |
| *reason = diagnostics.GetTopError(); |
| DCHECK(!reason->empty()); |
| } |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool PluginVmFeatures::IsConfigured(const Profile* profile) { |
| return profile->GetPrefs()->GetBoolean( |
| plugin_vm::prefs::kPluginVmImageExists); |
| } |
| |
| bool PluginVmFeatures::IsEnabled(const Profile* profile) { |
| return PluginVmFeatures::Get()->IsAllowed(profile) && |
| PluginVmFeatures::Get()->IsConfigured(profile); |
| } |
| |
| } // namespace plugin_vm |