| Index: components/proximity_auth/e2e_test/setup_test.py
|
| diff --git a/components/proximity_auth/e2e_test/setup_test.py b/components/proximity_auth/e2e_test/setup_test.py
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..9c47d80cc2a91d747f5fea3cb5de5abd3e81b6ba
|
| --- /dev/null
|
| +++ b/components/proximity_auth/e2e_test/setup_test.py
|
| @@ -0,0 +1,158 @@
|
| +# Copyright 2015 The Chromium Authors. All rights reserved.
|
| +# Use of this source code is governed by a BSD-style license that can be
|
| +# found in the LICENSE file.
|
| +
|
| +""" Script that exercises the Smart Lock setup flow, testing that a nearby phone
|
| +can be found and used to unlock a Chromebook.
|
| +
|
| +Note: This script does not currently automate Android phones, so make sure that
|
| +a phone is properly configured and online before starting the test.
|
| +
|
| +Usage:
|
| + python setup_test.py --remote_address REMOTE_ADDRESS
|
| + --username USERNAME
|
| + --password PASSWORD
|
| + [--app_path APP_PATH]
|
| + [--ssh_port SSH_PORT]
|
| + If |--app_path| is provided, then a copy of the Smart Lock app on the local
|
| + machine will be used instead of the app on the ChromeOS device.
|
| +"""
|
| +
|
| +import argparse
|
| +import cros
|
| +import cryptauth
|
| +import logging
|
| +import os
|
| +import subprocess
|
| +import sys
|
| +import tempfile
|
| +
|
| +logger = logging.getLogger('proximity_auth.%s' % __name__)
|
| +
|
| +class SmartLockSetupError(Exception):
|
| + pass
|
| +
|
| +def pingable_address(address):
|
| + try:
|
| + subprocess.check_output(['ping', '-c', '1', '-W', '1', address])
|
| + except subprocess.CalledProcessError:
|
| + raise argparse.ArgumentError('%s cannot be reached.' % address)
|
| + return address
|
| +
|
| +def email(arg):
|
| + tokens = arg.lower().split('@')
|
| + if len(tokens) != 2 or '.' not in tokens[1]:
|
| + raise argparse.ArgumentError('%s is not a valid email address' % arg)
|
| + name, domain = tokens
|
| + if domain == 'gmail.com':
|
| + name = name.replace('.', '')
|
| + return '@'.join([name, domain])
|
| +
|
| +def directory(path):
|
| + if not os.path.isdir(path):
|
| + raise argparse.ArgumentError('%s is not a directory' % path)
|
| + return path
|
| +
|
| +def ParseArgs():
|
| + parser = argparse.ArgumentParser(prog='python setup_test.py')
|
| + parser.add_argument('--remote_address', required=True, type=pingable_address)
|
| + parser.add_argument('--username', required=True, type=email)
|
| + parser.add_argument('--password', required=True)
|
| + parser.add_argument('--ssh_port', type=int)
|
| + parser.add_argument('--app_path', type=directory)
|
| + args = parser.parse_args()
|
| + return args
|
| +
|
| +def CheckCryptAuthState(access_token):
|
| + cryptauth_client = cryptauth.CryptAuthClient(access_token)
|
| +
|
| + # Check if we can make CryptAuth requests.
|
| + if cryptauth_client.GetMyDevices() is None:
|
| + logger.error('Cannot reach CryptAuth on test machine.')
|
| + return False
|
| +
|
| + if cryptauth_client.GetUnlockKey() is not None:
|
| + logger.info('Smart Lock currently enabled, turning off on Cryptauth...')
|
| + if not cryptauth_client.ToggleEasyUnlock(False):
|
| + logger.error('ToggleEasyUnlock request failed.')
|
| + return False
|
| +
|
| + result = cryptauth_client.FindEligibleUnlockDevices()
|
| + if result is None:
|
| + logger.error('FindEligibleUnlockDevices request failed')
|
| + return False
|
| + eligibleDevices, _ = result
|
| + if len(eligibleDevices) == 0:
|
| + logger.warn('No eligible phones found, trying to ping phones...')
|
| + result = cryptauth_client.PingPhones()
|
| + if result is None or not len(result[0]):
|
| + logger.error('Pinging phones failed :(')
|
| + return False
|
| + else:
|
| + logger.info('Pinging phones succeeded!')
|
| + else:
|
| + logger.info('Found eligible device: %s' % (
|
| + eligibleDevices[0]['friendlyDeviceName']))
|
| + return True
|
| +
|
| +def _NavigateSetupDialog(chromeos, app):
|
| + logger.info('Scanning for nearby phones...')
|
| + btmon = chromeos.RunBtmon()
|
| + find_phone_success = app.FindPhone()
|
| + btmon.terminate()
|
| +
|
| + if not find_phone_success:
|
| + fd, filepath = tempfile.mkstemp(prefix='btmon-')
|
| + os.write(fd, btmon.stdout.read())
|
| + os.close(fd)
|
| + logger.info('Logs for btmon can be found at %s' % filepath)
|
| + raise SmartLockSetupError("Failed to find nearby phone.")
|
| +
|
| + logger.info('Phone found! Starting pairing...')
|
| + if not app.PairPhone():
|
| + raise SmartLockSetupError("Failed to pair with phone.")
|
| + logger.info('Pairing success! Starting trial run...')
|
| + app.StartTrialRun()
|
| +
|
| + logger.info('Unlocking for trial run...')
|
| + lock_screen = chromeos.GetAccountPickerScreen()
|
| + lock_screen.WaitForSmartLockState(
|
| + lock_screen.SmartLockState.AUTHENTICATED)
|
| + lock_screen.UnlockWithClick()
|
| +
|
| + logger.info('Trial run success! Dismissing app...')
|
| + app.DismissApp()
|
| +
|
| +def RunSetupTest(args):
|
| + logger.info('Starting test for %s at %s' % (
|
| + args.username, args.remote_address))
|
| + if args.app_path is not None:
|
| + logger.info('Replacing Smart Lock app with %s' % args.app_path)
|
| +
|
| + chromeos = cros.ChromeOS(
|
| + args.remote_address, args.username, args.password, ssh_port=args.ssh_port)
|
| + with chromeos.Start(local_app_path=args.app_path):
|
| + logger.info('Chrome initialized')
|
| +
|
| + # TODO(tengs): The access token is currently fetched from the Smart Lock
|
| + # app's background page. To be more robust, we should instead mint the
|
| + # access token ourselves.
|
| + if not CheckCryptAuthState(chromeos.cryptauth_access_token):
|
| + raise SmartLockSetupError('Failed to check CryptAuth state')
|
| +
|
| + logger.info('Opening Smart Lock settings...')
|
| + settings = chromeos.GetSmartLockSettings()
|
| + assert(not settings.is_smart_lock_enabled)
|
| + logger.info('Starting Smart Lock setup flow...')
|
| + app = settings.StartSetupAndReturnApp()
|
| +
|
| + _NavigateSetupDialog(chromeos, app)
|
| +
|
| +def main():
|
| + logging.basicConfig()
|
| + logging.getLogger('proximity_auth').setLevel(logging.INFO)
|
| + args = ParseArgs()
|
| + RunSetupTest(args)
|
| +
|
| +if __name__ == '__main__':
|
| + main()
|
|
|