|  | // Copyright 2020 The Chromium Authors | 
|  | // 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 "base/feature_list.h" | 
|  | #include "base/system/sys_info.h" | 
|  | #include "chrome/browser/ash/guest_os/virtual_machines/virtual_machines_util.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/ash/components/install_attributes/install_attributes.h" | 
|  | #include "chromeos/ash/components/settings/cros_settings_names.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::IsUserProfile(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 (!ash::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 VirtualMachines are allowed by policy. | 
|  | if (!virtual_machines::AreVirtualMachinesAllowedByPolicy()) { | 
|  | return PolicyConfigured::kErrorVirtualMachinesNotAllowed; | 
|  | } | 
|  |  | 
|  | // 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"; | 
|  | case PolicyConfigured::kErrorVirtualMachinesNotAllowed: | 
|  | return "No Virtual Machines are allowed on this device"; | 
|  | } | 
|  |  | 
|  | 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 |