Device policy to allow powerwash

This is a device policy that specifies if the device is allowed to
powerwash, either from login screen with shortcut keys, or from settings
page.

The implementation of the effects on the settings page will be done in a
separate CL.

Bug: chromium:900369
Test: unit_tests --gtest_filter=DeviceSettingsProviderTest.DevicePowerwashAllowed
Change-Id: I3d70c18c6053dbe80d83213dcee543f0b2a96230
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1599619
Commit-Queue: Igor <igorcov@chromium.org>
Reviewed-by: Denis Kuznetsov <antrim@chromium.org>
Reviewed-by: Maksim Ivanov <emaxx@chromium.org>
Reviewed-by: Julian Pastarmov <pastarmovj@chromium.org>
Reviewed-by: Igor <igorcov@chromium.org>
Cr-Commit-Position: refs/heads/master@{#672062}
diff --git a/chrome/browser/chromeos/policy/device_policy_decoder_chromeos.cc b/chrome/browser/chromeos/policy/device_policy_decoder_chromeos.cc
index 6f01f1f..6ff2ed4 100644
--- a/chrome/browser/chromeos/policy/device_policy_decoder_chromeos.cc
+++ b/chrome/browser/chromeos/policy/device_policy_decoder_chromeos.cc
@@ -415,6 +415,18 @@
           nullptr);
     }
   }
+
+  if (policy.has_device_powerwash_allowed()) {
+    const em::DevicePowerwashAllowedProto& container(
+        policy.device_powerwash_allowed());
+    if (container.has_device_powerwash_allowed()) {
+      policies->Set(
+          key::kDevicePowerwashAllowed, POLICY_LEVEL_MANDATORY,
+          POLICY_SCOPE_MACHINE, POLICY_SOURCE_CLOUD,
+          std::make_unique<base::Value>(container.device_powerwash_allowed()),
+          nullptr);
+    }
+  }
 }
 
 void DecodeNetworkPolicies(const em::ChromeDeviceSettingsProto& policy,
diff --git a/chrome/browser/chromeos/settings/device_settings_provider.cc b/chrome/browser/chromeos/settings/device_settings_provider.cc
index dd152f6..1b0cd86 100644
--- a/chrome/browser/chromeos/settings/device_settings_provider.cc
+++ b/chrome/browser/chromeos/settings/device_settings_provider.cc
@@ -85,6 +85,7 @@
     kDeviceNativePrintersAccessMode,
     kDeviceNativePrintersBlacklist,
     kDeviceNativePrintersWhitelist,
+    kDevicePowerwashAllowed,
     kDeviceQuirksDownloadEnabled,
     kDeviceRebootOnUserSignout,
     kDeviceScheduledUpdateCheck,
@@ -809,6 +810,15 @@
         kDeviceSecondFactorAuthenticationMode,
         policy.device_second_factor_authentication().mode());
   }
+
+  if (policy.has_device_powerwash_allowed()) {
+    const em::DevicePowerwashAllowedProto& container(
+        policy.device_powerwash_allowed());
+    if (container.has_device_powerwash_allowed()) {
+      new_values_cache->SetBoolean(kDevicePowerwashAllowed,
+                                   container.device_powerwash_allowed());
+    }
+  }
 }
 
 void DecodeLogUploadPolicies(const em::ChromeDeviceSettingsProto& policy,
diff --git a/chrome/browser/chromeos/settings/device_settings_provider_unittest.cc b/chrome/browser/chromeos/settings/device_settings_provider_unittest.cc
index 93985de..52a31be 100644
--- a/chrome/browser/chromeos/settings/device_settings_provider_unittest.cc
+++ b/chrome/browser/chromeos/settings/device_settings_provider_unittest.cc
@@ -311,6 +311,13 @@
     BuildAndInstallDevicePolicy();
   }
 
+  void SetDevicePowerwashAllowed(bool device_powerwash_allowed) {
+    em::DevicePowerwashAllowedProto* proto =
+        device_policy_->payload().mutable_device_powerwash_allowed();
+    proto->set_device_powerwash_allowed(device_powerwash_allowed);
+    BuildAndInstallDevicePolicy();
+  }
+
   ScopedTestingLocalState local_state_;
 
   std::unique_ptr<DeviceSettingsProvider> provider_;
@@ -825,4 +832,15 @@
             *provider_->Get(kDeviceSecondFactorAuthenticationMode));
 }
 
+TEST_F(DeviceSettingsProviderTest, DevicePowerwashAllowed) {
+  // Policy should not be set by default
+  VerifyPolicyValue(kDevicePowerwashAllowed, nullptr);
+
+  SetDevicePowerwashAllowed(true);
+  EXPECT_EQ(base::Value(true), *provider_->Get(kDevicePowerwashAllowed));
+
+  SetDevicePowerwashAllowed(false);
+  EXPECT_EQ(base::Value(false), *provider_->Get(kDevicePowerwashAllowed));
+}
+
 }  // namespace chromeos
diff --git a/chrome/browser/ui/webui/chromeos/login/core_oobe_handler.cc b/chrome/browser/ui/webui/chromeos/login/core_oobe_handler.cc
index cd1d375..ef95db9 100644
--- a/chrome/browser/ui/webui/chromeos/login/core_oobe_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/core_oobe_handler.cc
@@ -407,12 +407,25 @@
 }
 
 void CoreOobeHandler::HandleToggleResetScreen() {
-  // Powerwash is generally not available on enterprise devices. First, check
-  // the common case of a correctly enrolled device.
+  // TODO(igorcov): Move this logic in a static method in wizard_controller,
+  // passing as parameter a callback(bool). Here, call the newly created method
+  // and pass as a callback a simple function that will call LaunchResetScreen
+  // if the input bool parameter is true.
+  // Check the common case of a correctly enrolled device.
   if (g_browser_process->platform_part()
           ->browser_policy_connector_chromeos()
           ->IsEnterpriseManaged()) {
-    // Powerwash is only available if allowed by the admin specifically for the
+    // Admin can explicitly allow to powerwash. If the policy is not loaded yet,
+    // we consider by default that the device is not allowed to powerwash.
+    bool is_powerwash_allowed = false;
+    CrosSettings::Get()->GetBoolean(kDevicePowerwashAllowed,
+                                    &is_powerwash_allowed);
+    if (is_powerwash_allowed) {
+      LaunchResetScreen();
+      return;
+    }
+
+    // Check if powerwash is only allowed by the admin specifically for the
     // purpose of installing a TPM firmware update.
     tpm_firmware_update::GetAvailableUpdateModes(
         base::BindOnce([](const std::set<tpm_firmware_update::Mode>& modes) {
@@ -437,9 +450,7 @@
   // and thus pending for enterprise management. These should not be allowed to
   // powerwash either. Note that taking consumer device ownership has the side
   // effect of dropping the FRE requirement if it was previously in effect.
-  const AutoEnrollmentController::FRERequirement requirement =
-      AutoEnrollmentController::GetFRERequirement();
-  if (requirement !=
+  if (AutoEnrollmentController::GetFRERequirement() !=
       AutoEnrollmentController::FRERequirement::kExplicitlyRequired) {
     LaunchResetScreen();
   }
diff --git a/chrome/test/data/policy/policy_test_cases.json b/chrome/test/data/policy/policy_test_cases.json
index fc9e5c3..cd351bb 100644
--- a/chrome/test/data/policy/policy_test_cases.json
+++ b/chrome/test/data/policy/policy_test_cases.json
@@ -4653,6 +4653,17 @@
     ]
   },
 
+  "DevicePowerwashAllowed": {
+    "os": ["chromeos"],
+    "test_policy": { "DevicePowerwashAllowed": true },
+    "pref_mappings": [
+      {
+        "pref": "cros.device.device_powerwash_allowed",
+        "local_state": true
+      }
+    ]
+  },
+
   "----- Chrome Frame policies -------------------------------------------": {},
 
   "ChromeFrameRendererSettings": {
diff --git a/chromeos/settings/cros_settings_names.cc b/chromeos/settings/cros_settings_names.cc
index 9fe24df..928655a 100644
--- a/chromeos/settings/cros_settings_names.cc
+++ b/chromeos/settings/cros_settings_names.cc
@@ -388,4 +388,6 @@
 const char kDeviceSecondFactorAuthenticationMode[] =
     "cros.device.device_second_factor_authentication_mode";
 
+// A boolean pref specifying if the device is allowed to powerwash.
+const char kDevicePowerwashAllowed[] = "cros.device.device_powerwash_allowed";
 }  // namespace chromeos
diff --git a/chromeos/settings/cros_settings_names.h b/chromeos/settings/cros_settings_names.h
index b1676b604..1031971 100644
--- a/chromeos/settings/cros_settings_names.h
+++ b/chromeos/settings/cros_settings_names.h
@@ -225,6 +225,9 @@
 COMPONENT_EXPORT(CHROMEOS_SETTINGS)
 extern const char kDeviceSecondFactorAuthenticationMode[];
 
+COMPONENT_EXPORT(CHROMEOS_SETTINGS)
+extern const char kDevicePowerwashAllowed[];
+
 }  // namespace chromeos
 
 #endif  // CHROMEOS_SETTINGS_CROS_SETTINGS_NAMES_H_
diff --git a/components/policy/proto/chrome_device_policy.proto b/components/policy/proto/chrome_device_policy.proto
index 5812693..a71b482 100644
--- a/components/policy/proto/chrome_device_policy.proto
+++ b/components/policy/proto/chrome_device_policy.proto
@@ -1278,6 +1278,12 @@
   optional string device_webusb_allow_devices_for_urls = 1;
 }
 
+// Settings that control if the device is allowed to powerwash.
+message DevicePowerwashAllowedProto {
+  // Determines if powerwash is allowed on the device.
+  optional bool device_powerwash_allowed = 1;
+}
+
 message ChromeDeviceSettingsProto {
   reserved 61;
   optional DevicePolicyRefreshRateProto device_policy_refresh_rate = 1;
@@ -1391,4 +1397,5 @@
   optional DeviceScheduledUpdateCheckProto device_scheduled_update_check = 89;
   optional DeviceWebUsbAllowDevicesForUrlsProto
       device_webusb_allow_devices_for_urls = 90;
+  optional DevicePowerwashAllowedProto device_powerwash_allowed = 91;
 }
diff --git a/components/policy/resources/policy_templates.json b/components/policy/resources/policy_templates.json
index d65ddbf..a9f38de 100644
--- a/components/policy/resources/policy_templates.json
+++ b/components/policy/resources/policy_templates.json
@@ -16123,6 +16123,28 @@
 
           If this policy is disabled or not set, the browser window is allowed to launch. Note that the browser window might not launch due to other policies or command-line flags.''',
     },
+    {
+      'id': 571,
+      'name': 'DevicePowerwashAllowed',
+      'type': 'main',
+      'schema': {
+        'type': 'boolean',
+      },
+      'device_only': True,
+      'example_value': True,
+      'features': {
+        'dynamic_refresh': True,
+        'per_profile': False,
+      },
+      'supported_on': ['chrome_os:77-'],
+      'caption': '''Allow the device to request powerwash''',
+      'tags': [],
+      'desc': '''
+      This policy when set to False, does not allow the device to trigger powerwash.
+      When set to True, it allows the device to trigger powerwash.
+      If left unset, it defaults to False, meaning it doesn't allow the device to powerwash.
+      ''',
+    },
   ],
 
   'messages': {
@@ -16470,7 +16492,8 @@
     'DeviceBatteryChargeCustomStartCharging': 'device_battery_charge_mode.custom_charge_start',
     'DeviceBatteryChargeCustomStopCharging': 'device_battery_charge_mode.custom_charge_stop',
     'DeviceScheduledUpdateCheck': 'device_scheduled_update_check.device_scheduled_update_check_settings',
-    'DeviceWebUsbAllowDevicesForUrls': 'device_webusb_allow_devices_for_urls.device_webusb_allow_devices_for_urls'
+    'DeviceWebUsbAllowDevicesForUrls': 'device_webusb_allow_devices_for_urls.device_webusb_allow_devices_for_urls',
+    'DevicePowerwashAllowed': 'device_powerwash_allowed.device_powerwash_allowed'
   },
   'policy_atomic_group_definitions': [
     {
@@ -16911,6 +16934,6 @@
   ],
   'placeholders': [],
   'deleted_policy_ids': [412, 546, 562],
-  'highest_id_currently_used': 570,
+  'highest_id_currently_used': 571,
   'highest_atomic_group_id_currently_used': 37
 }
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 00e2353..fda207f 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -17321,6 +17321,7 @@
   <int value="568" label="StartupBrowserWindowLaunchSuppressed"/>
   <int value="569" label="DeviceWebUsbAllowDevicesForUrls"/>
   <int value="570" label="UserFeedbackAllowed"/>
+  <int value="571" label="DevicePowerwashAllowed"/>
 </enum>
 
 <enum name="EnterprisePolicyInvalidations">