form_based_credentials_backend.py revision 1320f92c476a1ad9d19dba2a48c72b75566198e9
1424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)# Copyright 2013 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import logging
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
62a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)from telemetry.core import util
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
82a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class FormBasedCredentialsBackend(object):
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __init__(self):
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._logged_in = False
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  def IsAlreadyLoggedIn(self, tab):
141320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    return tab.EvaluateJavaScript(self.logged_in_javascript)
152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  @property
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def credentials_type(self):
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    raise NotImplementedError()
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  @property
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def url(self):
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    raise NotImplementedError()
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  @property
252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  def login_form_id(self):
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    raise NotImplementedError()
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  @property
291320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  def login_button_javascript(self):
301320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    """Some sites have custom JS to log in."""
311320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    return None
321320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
331320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  @property
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def login_input_id(self):
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    raise NotImplementedError()
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  @property
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def password_input_id(self):
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    raise NotImplementedError()
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
411320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  @property
421320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  def logged_in_javascript(self):
431320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    """Evaluates to true iff already logged in."""
441320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    raise NotImplementedError()
451320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  def IsLoggedIn(self):
472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return self._logged_in
482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  def _ResetLoggedInState(self):
502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    """Makes the backend think we're not logged in even though we are.
512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    Should only be used in unit tests to simulate --dont-override-profile.
522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    """
532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self._logged_in = False
542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
551320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  def _WaitForLoginState(self, action_runner):
561320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    """Waits until it can detect either the login form, or already logged in."""
571320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    condition = '(document.querySelector("#%s") !== null) || (%s)' % (
581320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        self.login_form_id, self.logged_in_javascript)
591320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    action_runner.WaitForJavaScriptCondition(condition, 60)
601320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
611320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  def _SubmitLoginFormAndWait(self, action_runner, tab, username, password):
621320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    """Submits the login form and waits for the navigation."""
631320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    tab.WaitForDocumentReadyStateToBeInteractiveOrBetter()
641320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    email_id = 'document.querySelector("#%s #%s").value = "%s"; ' % (
651320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        self.login_form_id, self.login_input_id, username)
661320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    password = 'document.querySelector("#%s #%s").value = "%s"; ' % (
671320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        self.login_form_id, self.password_input_id, password)
681320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    tab.ExecuteJavaScript(email_id)
691320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    tab.ExecuteJavaScript(password)
701320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    if self.login_button_javascript:
711320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      tab.ExecuteJavaScript(self.login_button_javascript)
721320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    else:
731320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      tab.ExecuteJavaScript(
741320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci          'document.getElementById("%s").submit();' % self.login_form_id)
751320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    # Wait for the form element to disappear as confirmation of the navigation.
761320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    action_runner.WaitForNavigate()
771320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
781320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
791320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  def LoginNeeded(self, tab, action_runner, config):
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Logs in to a test account.
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Raises:
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      RuntimeError: if could not get credential information.
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if self._logged_in:
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return True
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if 'username' not in config or 'password' not in config:
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      message = ('Credentials for "%s" must include username and password.' %
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 self.credentials_type)
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      raise RuntimeError(message)
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    logging.debug('Logging into %s account...' % self.credentials_type)
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
958bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    if 'url' in config:
968bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)      url = config['url']
978bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    else:
988bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)      url = self.url
998bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    try:
1018bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)      logging.info('Loading %s...', url)
1028bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)      tab.Navigate(url)
1031320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      self._WaitForLoginState(action_runner)
1042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if self.IsAlreadyLoggedIn(tab):
1062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        self._logged_in = True
1072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        return True
1082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1091320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      self._SubmitLoginFormAndWait(
1101320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci          action_runner, tab, config['username'], config['password'])
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self._logged_in = True
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return True
1142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    except util.TimeoutException:
1158bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)      logging.warning('Timed out while loading: %s', url)
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return False
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def LoginNoLongerNeeded(self, tab): # pylint: disable=W0613
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    assert self._logged_in
120