1# Copyright 2013 The Chromium Authors. All rights reserved. 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4import logging 5 6from telemetry.core import exceptions 7 8 9class FormBasedCredentialsBackend(object): 10 def __init__(self): 11 self._logged_in = False 12 13 def IsAlreadyLoggedIn(self, tab): 14 return tab.EvaluateJavaScript(self.logged_in_javascript) 15 16 @property 17 def credentials_type(self): 18 raise NotImplementedError() 19 20 @property 21 def url(self): 22 raise NotImplementedError() 23 24 @property 25 def login_form_id(self): 26 raise NotImplementedError() 27 28 @property 29 def login_button_javascript(self): 30 """Some sites have custom JS to log in.""" 31 return None 32 33 @property 34 def login_input_id(self): 35 raise NotImplementedError() 36 37 @property 38 def password_input_id(self): 39 raise NotImplementedError() 40 41 @property 42 def logged_in_javascript(self): 43 """Evaluates to true iff already logged in.""" 44 raise NotImplementedError() 45 46 def IsLoggedIn(self): 47 return self._logged_in 48 49 def _ResetLoggedInState(self): 50 """Makes the backend think we're not logged in even though we are. 51 Should only be used in unit tests to simulate --dont-override-profile. 52 """ 53 self._logged_in = False 54 55 def _WaitForLoginState(self, action_runner): 56 """Waits until it can detect either the login form, or already logged in.""" 57 condition = '(document.querySelector("#%s") !== null) || (%s)' % ( 58 self.login_form_id, self.logged_in_javascript) 59 action_runner.WaitForJavaScriptCondition(condition, 60) 60 61 def _SubmitLoginFormAndWait(self, action_runner, tab, username, password): 62 """Submits the login form and waits for the navigation.""" 63 tab.WaitForDocumentReadyStateToBeInteractiveOrBetter() 64 email_id = 'document.querySelector("#%s #%s").value = "%s"; ' % ( 65 self.login_form_id, self.login_input_id, username) 66 password = 'document.querySelector("#%s #%s").value = "%s"; ' % ( 67 self.login_form_id, self.password_input_id, password) 68 tab.ExecuteJavaScript(email_id) 69 tab.ExecuteJavaScript(password) 70 if self.login_button_javascript: 71 tab.ExecuteJavaScript(self.login_button_javascript) 72 else: 73 tab.ExecuteJavaScript( 74 'document.getElementById("%s").submit();' % self.login_form_id) 75 # Wait for the form element to disappear as confirmation of the navigation. 76 action_runner.WaitForNavigate() 77 78 79 def LoginNeeded(self, tab, action_runner, config): 80 """Logs in to a test account. 81 82 Raises: 83 RuntimeError: if could not get credential information. 84 """ 85 if self._logged_in: 86 return True 87 88 if 'username' not in config or 'password' not in config: 89 message = ('Credentials for "%s" must include username and password.' % 90 self.credentials_type) 91 raise RuntimeError(message) 92 93 logging.debug('Logging into %s account...' % self.credentials_type) 94 95 if 'url' in config: 96 url = config['url'] 97 else: 98 url = self.url 99 100 try: 101 logging.info('Loading %s...', url) 102 tab.Navigate(url) 103 self._WaitForLoginState(action_runner) 104 105 if self.IsAlreadyLoggedIn(tab): 106 self._logged_in = True 107 return True 108 109 self._SubmitLoginFormAndWait( 110 action_runner, tab, config['username'], config['password']) 111 112 self._logged_in = True 113 return True 114 except exceptions.TimeoutException: 115 logging.warning('Timed out while loading: %s', url) 116 return False 117 118 def LoginNoLongerNeeded(self, tab): # pylint: disable=unused-argument 119 assert self._logged_in 120