Support for Remora enrollment.

* Methods for enterprise enrollment of Remora devices.
* Methods to save TPM password and clear TPM.
* utility function to read credentials from a file.
* sample test that exercises enrollment.

BUG=chromium:342884
TEST=enterprise_RemoraRequisition

Change-Id: Ie872e9ba0702474d9f605c5520577cf4e0ad21d2
Reviewed-on: https://chromium-review.googlesource.com/199560
Tested-by: Achuith Bhandarkar <achuith@chromium.org>
Commit-Queue: Mattias Nissler <mnissler@chromium.org>
Reviewed-by: Achuith Bhandarkar <achuith@chromium.org>
Commit-Queue: Achuith Bhandarkar <achuith@chromium.org>
diff --git a/client/bin/site_utils.py b/client/bin/site_utils.py
index 33beabd..26ac5fe 100644
--- a/client/bin/site_utils.py
+++ b/client/bin/site_utils.py
@@ -349,6 +349,21 @@
     return str(uuid.uuid4()) + '@example.com'
 
 
+def get_signin_credentials(filepath):
+    """Returns user_id, password tuple from credentials file at filepath.
+
+    File must have one line of the format user_id:password
+
+    @param filepath: path of credentials file.
+    @return user_id, password tuple.
+    """
+    user_id, password = None, None
+    if os.path.isfile(filepath):
+        with open(filepath) as f:
+            user_id, password = f.read().rstrip().split(':')
+    return user_id, password
+
+
 def parse_cmd_output(command, run_method=utils.run):
     """Runs a command on a host object to retrieve host attributes.
 
diff --git a/client/common_lib/cros/enrollment.py b/client/common_lib/cros/enrollment.py
new file mode 100644
index 0000000..f4c8a35
--- /dev/null
+++ b/client/common_lib/cros/enrollment.py
@@ -0,0 +1,119 @@
+# Copyright 2014 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 logging, os
+
+from autotest_lib.client.bin import utils
+from autotest_lib.client.cros import cros_ui, cryptohome, ownership
+from telemetry.core import exceptions
+from telemetry.core.backends.chrome import cros_interface
+
+
+_PASSWD_FILE = '/var/tmp/tpm_passwd'
+
+
+def ClearTPM():
+    """Clears the TPM (if it is owned) using the password stored in
+    /var/tmp/tpm_passwd. Returns True if tpm was owned.
+
+    @return True if the TPM was owned - enrollment should not be attempted.
+    """
+    status = cryptohome.get_tpm_status()
+    if not status['Owned']:
+        logging.debug('TPM is not owned')
+        return False
+    password = status['Password']
+    if not password:
+        if not os.path.isfile(_PASSWD_FILE):
+            logging.warn('Password file %s doesn\'t exist, cannot clear TPM. '
+                         'You need to have the firmware clear the TPM, for '
+                         'instance using crossystem or by toggling the dev '
+                         'switch.', _PASSWD_FILE)
+            return True
+        with open(_PASSWD_FILE) as f:
+            password = f.read().rstrip()
+
+    if not password:
+        logging.warn('Password file %s empty, cannot clear TPM. '
+                     'You need to have the firmware clear the TPM, for '
+                     'instance using crossystem or by toggling the dev switch.',
+                     _PASSWD_FILE)
+        return True
+
+    cros_ui.stop()
+    res = utils.system_output('tpm_clear --pass ' + password)
+    logging.warn(repr(res))
+
+    cryptohome.remove_all_vaults()
+    ownership.clear_ownership_files_no_restart()
+    logging.warn('Please reboot the system')
+    return True
+
+
+def _SaveTPMPassword():
+    """Save TPM Password to /var/tpm/tpm_passwd.
+
+    During enrollment, the TPM password becomes visible - we capture it and
+    save it in to a local file, so we can clear the TPM at the end of the test.
+    """
+    password = utils.poll_for_condition(
+            lambda: cryptohome.get_tpm_status()['Password'],
+            sleep_interval=0.5, timeout=60)
+    if password:
+        with open(_PASSWD_FILE, 'w') as f:
+            f.write(password)
+    else:
+        logging.warn('Could not save TPM password')
+    logging.info('TPM Password: ' + password)
+    return password
+
+
+def _ExecuteOobeCmd(browser, cmd):
+    logging.info('Invoking ' + cmd)
+    oobe = browser.oobe
+    oobe.WaitForJavaScriptExpression('typeof Oobe !== \'undefined\'', 10)
+    oobe.ExecuteJavaScript(cmd)
+
+
+def SwitchToRemora(browser):
+    """Switch to Remora enrollment.
+
+    @param browser: telemetry browser object.
+    """
+    _cri = cros_interface.CrOSInterface()
+    pid = _cri.GetChromePid()
+    try:
+        # This will restart the browser.
+        _ExecuteOobeCmd(browser, 'Oobe.remoraRequisitionForTesting();')
+    except (exceptions.BrowserConnectionGoneException,
+            exceptions.TabCrashException):
+        pass
+    utils.poll_for_condition(lambda: pid != _cri.GetChromePid(), timeout=60)
+    utils.poll_for_condition(lambda: browser.oobe_exists, timeout=30)
+
+    _ExecuteOobeCmd(browser, 'Oobe.skipToLoginForTesting();')
+    _SaveTPMPassword()
+
+
+def FinishEnrollment(oobe):
+    """Wait for enrollment to finish and dismiss the last enrollment screen.
+
+    @param oobe: telemetry oobe object.
+    """
+    oobe.WaitForJavaScriptExpression(
+            "document.getElementById('oauth-enrollment').className."
+            "search('oauth-enroll-state-success') != -1", 30)
+    oobe.EvaluateJavaScript('Oobe.enterpriseEnrollmentDone();')
+
+
+def RemoraEnrollment(browser, user_id, password):
+    """Enterprise login for a Remora device.
+
+    @param browser: telemetry browser object.
+    @param user_id: login credentials user_id.
+    @param password: login credentials password.
+    """
+    SwitchToRemora(browser)
+    browser.oobe.NavigateGaiaLogin(user_id, password)
+    FinishEnrollment(browser.oobe)
diff --git a/client/site_tests/enterprise_RemoraRequisition/control b/client/site_tests/enterprise_RemoraRequisition/control
new file mode 100644
index 0000000..d40df8f
--- /dev/null
+++ b/client/site_tests/enterprise_RemoraRequisition/control
@@ -0,0 +1,20 @@
+# Copyright 2014 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 = "achuith"
+NAME = "enterprise_RemoraRequisition"
+TIME = "SHORT"
+TEST_CATEGORY = "Enterprise"
+TEST_CLASS = "enterprise"
+TEST_TYPE = "client"
+
+DOC = """
+This test enrolls a Chrome device as a Remora device.
+
+You need a credentials.txt file with user_id:password in this directory for
+this test to succeed. The credentials are used to enroll the device as a Remora
+device.
+"""
+
+job.run_test('enterprise_RemoraRequisition')
diff --git a/client/site_tests/enterprise_RemoraRequisition/enterprise_RemoraRequisition.py b/client/site_tests/enterprise_RemoraRequisition/enterprise_RemoraRequisition.py
new file mode 100644
index 0000000..ac5adf0
--- /dev/null
+++ b/client/site_tests/enterprise_RemoraRequisition/enterprise_RemoraRequisition.py
@@ -0,0 +1,29 @@
+# Copyright 2014 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 logging, os
+
+from autotest_lib.client.bin import test, utils
+from autotest_lib.client.common_lib.cros import chrome, enrollment
+
+
+class enterprise_RemoraRequisition(test.test):
+    """Enroll as a Remora device."""
+    version = 1
+
+    def run_once(self):
+        if enrollment.ClearTPM():
+            return
+
+        user_id, password = utils.get_signin_credentials(os.path.join(
+                os.path.dirname(os.path.realpath(__file__)), 'credentials.txt'))
+        if user_id and password:
+            with chrome.Chrome(auto_login=False) as cr:
+                enrollment.RemoraEnrollment(cr.browser, user_id, password)
+                # TODO(achuith): Additional logic to ensure the hangouts app is
+                # functioning correctly.
+
+            enrollment.ClearTPM()
+        else:
+            logging.warn('No credentials found - exiting test.')