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