[Autotest] policy_ExtensionPolicy

Test verifies extension policies can be set and that policies with
sensitive values are obfuscated.

Also introduces method for reloading policies on the policy tab.

BUG=chromium:900238
TEST=this
CQ-DEPEND=CL:1308741

Change-Id: Ib8cdd37362819e74c740d01b654ae5f9c4b95f7a
Reviewed-on: https://chromium-review.googlesource.com/1329994
Commit-Ready: Max Timkovich <timkovich@chromium.org>
Tested-by: Max Timkovich <timkovich@chromium.org>
Reviewed-by: Max Timkovich <timkovich@chromium.org>
diff --git a/client/cros/enterprise/enterprise_policy_base.py b/client/cros/enterprise/enterprise_policy_base.py
index d3441b0..28dc82b 100755
--- a/client/cros/enterprise/enterprise_policy_base.py
+++ b/client/cros/enterprise/enterprise_policy_base.py
@@ -338,14 +338,27 @@
         return table_index
 
 
-    def verify_extension_stats(self, extension_policies):
+    def reload_policies(self):
+        """Force a policy fetch."""
+        policy_tab = self.navigate_to_url(self.CHROME_POLICY_PAGE)
+        reload_button = "document.querySelector('button#reload-policies')"
+        policy_tab.ExecuteJavaScript("%s.click()" % reload_button)
+        policy_tab.WaitForJavaScriptCondition("!%s.disabled" % reload_button,
+                                              timeout=1)
+        policy_tab.Close()
+
+
+    def verify_extension_stats(self, extension_policies, sensitive_fields=[]):
         """
         Verify the extension policies match what is on chrome://policy.
 
-        @params extension_policies: the dictionary of extension IDs mapping
+        @param extension_policies: the dictionary of extension IDs mapping
             to download_url and secure_hash.
+        @param sensitive_fields: list of fields that should have their value
+            censored.
         @raises error.TestError: if the shown values do not match what we are
             expecting.
+
         """
         policy_tab = self.navigate_to_url(self.CHROME_POLICY_PAGE)
 
@@ -362,6 +375,10 @@
                 expected_value = settings['Value']
                 value_shown = self._get_policy_stats_shown(
                         policy_tab, policy_name, table)['value']
+
+                if policy_name in sensitive_fields:
+                    expected_value = '********'
+
                 self._compare_values(policy_name, expected_value, value_shown)
 
         policy_tab.Close()
diff --git a/client/cros/enterprise/extension_policy.json b/client/cros/enterprise/extension_policy.json
new file mode 100644
index 0000000..55db47e
--- /dev/null
+++ b/client/cros/enterprise/extension_policy.json
@@ -0,0 +1,20 @@
+{
+    "VisibleStringPolicy": {
+        "Value": "notsecret"
+    },
+    "SensitiveStringPolicy": {
+        "Value": "secret"
+    },
+    "VisibleDictPolicy": {
+      "Value": {
+        "some_bool": true,
+        "some_string": "notsecret"
+      }
+    },
+    "SensitiveDictPolicy": {
+        "Value": {
+            "some_bool": true,
+            "some_string": "secret"
+        }
+    }
+}
diff --git a/client/site_tests/policy_ExtensionPolicy/control b/client/site_tests/policy_ExtensionPolicy/control
new file mode 100644
index 0000000..c363c79
--- /dev/null
+++ b/client/site_tests/policy_ExtensionPolicy/control
@@ -0,0 +1,25 @@
+# Copyright 2018 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+AUTHOR = 'timkovich'
+NAME = 'policy_ExtensionPolicy'
+ATTRIBUTES = 'suite:ent-nightly, suite:policy'
+TIME = 'SHORT'
+TEST_CATEGORY = 'General'
+TEST_CLASS = 'enterprise'
+TEST_TYPE = 'client'
+
+DOC = '''
+policy_ExtensionPolicy verifies that changes to extension policies can be set
+and are reflected correctly in chrome://policy. Fields marked sensitive should
+have their value obfuscated.
+
+This test will only work when using the fake DM server.
+
+'''
+
+args_dict = utils.args_to_dict(args)
+
+job.run_test('policy_ExtensionPolicy', **args_dict)
+
diff --git a/client/site_tests/policy_ExtensionPolicy/policy_ExtensionPolicy.py b/client/site_tests/policy_ExtensionPolicy/policy_ExtensionPolicy.py
new file mode 100644
index 0000000..8293ab1
--- /dev/null
+++ b/client/site_tests/policy_ExtensionPolicy/policy_ExtensionPolicy.py
@@ -0,0 +1,75 @@
+# Copyright 2018 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import hashlib
+import logging
+import os
+
+from autotest_lib.client.common_lib import error
+from autotest_lib.client.cros.enterprise import enterprise_policy_base
+
+
+class policy_ExtensionPolicy(enterprise_policy_base.EnterprisePolicyTest):
+    version = 1
+
+
+    def initialize(self, **kwargs):
+        """
+        Start webserver and set the extension policy file's path and checksum.
+
+        """
+        super(policy_ExtensionPolicy, self).initialize(**kwargs)
+        self.start_webserver()
+
+        # Location of the extension policy on the server.
+        POLICY_FILE = 'extension_policy.json'
+        policy_path = os.path.join(self.enterprise_dir, POLICY_FILE)
+        self.EXTENSION_POLICY_URL = '%s/%s' % (self.WEB_HOST, POLICY_FILE)
+        self.CHECKSUM = self.sha256sum(policy_path)
+
+
+    def sha256sum(self, filepath):
+        """
+        Generate the SHA256 checksum of |filepath|.
+
+        @param filepath: Path to file.
+
+        @returns: SHA256 checksum as a hex string.
+
+        """
+        with open(filepath, 'rb') as f:
+            return hashlib.sha256(f.read()).hexdigest()
+
+
+    def run_once(self):
+        """
+        Setup and run the test configured for the specified test case.
+
+        """
+        extension_path = os.path.join(os.path.dirname(__file__),
+                                      'policy_test_extension')
+
+        self.setup_case(extension_paths=[extension_path])
+
+        # The extension ID is required for setting the extension policy. But
+        # the extension ID is assigned randomly, so we need to force install
+        # the policy test extension first and then read its ID.
+        extension_id = self.cr.get_extension(extension_path).extension_id
+        extension_policies = {
+            extension_id: {
+                'download_url': self.EXTENSION_POLICY_URL,
+                'secure_hash': self.CHECKSUM
+            }
+        }
+
+        if self.dms_is_fake:
+            # Update the server policies with the extension policies.
+            self.fake_dm_server.setup_policy(self._make_json_blob(
+                extension_policies=extension_policies))
+            self.reload_policies()
+
+        # Ensure fields marked sensitive are censored in the policy tab.
+        sensitive_fields = ['SensitiveStringPolicy', 'SensitiveDictPolicy']
+        self.verify_extension_stats(extension_policies,
+                                    sensitive_fields=sensitive_fields)
diff --git a/client/site_tests/policy_ExtensionPolicy/policy_test_extension/background.js b/client/site_tests/policy_ExtensionPolicy/policy_test_extension/background.js
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/client/site_tests/policy_ExtensionPolicy/policy_test_extension/background.js
diff --git a/client/site_tests/policy_ExtensionPolicy/policy_test_extension/manifest.json b/client/site_tests/policy_ExtensionPolicy/policy_test_extension/manifest.json
new file mode 100644
index 0000000..f9685dd
--- /dev/null
+++ b/client/site_tests/policy_ExtensionPolicy/policy_test_extension/manifest.json
@@ -0,0 +1,20 @@
+{
+  "manifest_version": 2,
+
+  "name": "Policy Extension",
+  "description": "Dummy extension for testing extension policies",
+  "version": "1.0",
+
+  "background": {
+      "scripts": ["background.js"],
+      "persistent": false
+  },
+
+  "storage": {
+      "managed_schema": "schema.json"
+  },
+
+  "permissions": [
+      "storage"
+  ]
+}
diff --git a/client/site_tests/policy_ExtensionPolicy/policy_test_extension/schema.json b/client/site_tests/policy_ExtensionPolicy/policy_test_extension/schema.json
new file mode 100644
index 0000000..096356c
--- /dev/null
+++ b/client/site_tests/policy_ExtensionPolicy/policy_test_extension/schema.json
@@ -0,0 +1,27 @@
+{
+  "type": "object",
+  "properties": {
+    "VisibleStringPolicy": {
+      "type": "string"
+    },
+    "SensitiveStringPolicy": {
+      "type": "string",
+      "sensitiveValue": true
+    },
+    "VisibleDictPolicy": {
+      "type": "object",
+      "properties": {
+        "some_bool": { "type": "boolean" },
+        "some_string": { "type": "string" }
+      }
+    },
+    "SensitiveDictPolicy": {
+      "type": "object",
+      "properties": {
+        "some_bool": { "type": "boolean" },
+        "some_string": { "type": "string" }
+      },
+      "sensitiveValue": true
+    }
+  }
+}